Merge remote-tracking branch 'origin/topic/jsiwek/983'

Closes #983.

* origin/topic/jsiwek/983:
  Add named constructor examples to docs.
  Allow named vector constructors. Addresses #983.
  Allow named table constructors.  Addresses #983.
  Improve set constructor argument coercion.
  Allow named set constructors. Addresses #983.
  Allow named record constructors. Addresses #983.
This commit is contained in:
Robin Sommer 2013-06-02 17:41:33 -07:00
commit d3d14e10cf
15 changed files with 465 additions and 40 deletions

13
NEWS
View file

@ -46,6 +46,19 @@ New Functionality
have changed their signatures to work with opaques types rather have changed their signatures to work with opaques types rather
than global state as it was before. than global state as it was before.
- The scripting language now supports a constructing sets, tables,
vectors, and records by name:
type MyRecordType: record {
c: count;
s: string &optional;
};
global r: MyRecordType = record($c = 7);
type MySet: set[MyRec];
global s = MySet([$c=1], [$c=2]);
- Strings now support the subscript operator to extract individual - Strings now support the subscript operator to extract individual
characters and substrings (e.g., s[4], s[1,5]). The index expression characters and substrings (e.g., s[4], s[1,5]). The index expression
can take up to two indices for the start and end index of the can take up to two indices for the start and end index of the

@ -1 +1 @@
Subproject commit 3389de4a6045451f66b6cd52074c746ec9be551e Subproject commit 0eca32b35d16a4d387f41976ab46360ee6ecaed8

View file

@ -246,6 +246,31 @@ The Bro scripting language supports the following built-in types.
[5] = "five", [5] = "five",
}; };
A table constructor (equivalent to above example) can also be used
to create a table:
.. code:: bro
global t2: table[count] of string = table(
[11] = "eleven",
[5] = "five"
);
Table constructors can also be explicitly named by a type, which is
useful for when a more complex index type could otherwise be
ambiguous:
.. code:: bro
type MyRec: record {
a: count &optional;
b: count;
};
type MyTable: table[MyRec] of string;
global t3 = MyTable([[$b=5]] = "b5", [[$b=7]] = "b7");
Accessing table elements if provided by enclosing values within square Accessing table elements if provided by enclosing values within square
brackets (``[]``), for example: brackets (``[]``), for example:
@ -308,6 +333,28 @@ The Bro scripting language supports the following built-in types.
The types are explicitly shown in the example above, but they could The types are explicitly shown in the example above, but they could
have been left to type inference. have been left to type inference.
A set constructor (equivalent to above example) can also be used to
create a set:
.. code:: bro
global s3: set[port] = set(21/tcp, 23/tcp, 80/tcp, 443/tcp);
Set constructors can also be explicitly named by a type, which is
useful for when a more complex index type could otherwise be
ambiguous:
.. code:: bro
type MyRec: record {
a: count &optional;
b: count;
};
type MySet: set[MyRec];
global s4 = MySet([$b=1], [$b=2]);
Set membership is tested with ``in``: Set membership is tested with ``in``:
.. code:: bro .. code:: bro
@ -349,6 +396,21 @@ The Bro scripting language supports the following built-in types.
global v: vector of string = vector("one", "two", "three"); global v: vector of string = vector("one", "two", "three");
Vector constructors can also be explicitly named by a type, which
is useful for when a more complex yield type could otherwise be
ambiguous.
.. code:: bro
type MyRec: record {
a: count &optional;
b: count;
};
type MyVec: vector of MyRec;
global v2 = MyVec([$b=1], [$b=2], [$b=3]);
Adding an element to a vector involves accessing/assigning it: Adding an element to a vector involves accessing/assigning it:
.. code:: bro .. code:: bro
@ -402,6 +464,19 @@ The Bro scripting language supports the following built-in types.
if ( r?$s ) if ( r?$s )
... ...
Records can also be created using a constructor syntax:
.. code:: bro
global r2: MyRecordType = record($c = 7);
And the constructor can be explicitly named by type, too, which
is arguably more readable code:
.. code:: bro
global r3 = MyRecordType($c = 42);
.. bro:type:: opaque .. bro:type:: opaque
A data type whose actual representation/implementation is A data type whose actual representation/implementation is

View file

@ -3320,12 +3320,20 @@ bool HasFieldExpr::DoUnserialize(UnserialInfo* info)
return UNSERIALIZE(&not_used) && UNSERIALIZE_STR(&field_name, 0) && UNSERIALIZE(&field); return UNSERIALIZE(&not_used) && UNSERIALIZE_STR(&field_name, 0) && UNSERIALIZE(&field);
} }
RecordConstructorExpr::RecordConstructorExpr(ListExpr* constructor_list) RecordConstructorExpr::RecordConstructorExpr(ListExpr* constructor_list,
BroType* arg_type)
: UnaryExpr(EXPR_RECORD_CONSTRUCTOR, constructor_list) : UnaryExpr(EXPR_RECORD_CONSTRUCTOR, constructor_list)
{ {
if ( IsError() ) if ( IsError() )
return; return;
if ( arg_type && arg_type->Tag() != TYPE_RECORD )
{
Error("bad record constructor type", arg_type);
SetError();
return;
}
// Spin through the list, which should be comprised of // Spin through the list, which should be comprised of
// either record's or record-field-assign, and build up a // either record's or record-field-assign, and build up a
// record type to associate with this constructor. // record type to associate with this constructor.
@ -3365,7 +3373,17 @@ RecordConstructorExpr::RecordConstructorExpr(ListExpr* constructor_list)
} }
} }
SetType(new RecordType(record_types)); ctor_type = new RecordType(record_types);
if ( arg_type )
SetType(arg_type->Ref());
else
SetType(ctor_type->Ref());
}
RecordConstructorExpr::~RecordConstructorExpr()
{
Unref(ctor_type);
} }
Val* RecordConstructorExpr::InitVal(const BroType* t, Val* aggr) const Val* RecordConstructorExpr::InitVal(const BroType* t, Val* aggr) const
@ -3391,7 +3409,7 @@ Val* RecordConstructorExpr::InitVal(const BroType* t, Val* aggr) const
Val* RecordConstructorExpr::Fold(Val* v) const Val* RecordConstructorExpr::Fold(Val* v) const
{ {
ListVal* lv = v->AsListVal(); ListVal* lv = v->AsListVal();
RecordType* rt = type->AsRecordType(); RecordType* rt = ctor_type->AsRecordType();
if ( lv->Length() != rt->NumFields() ) if ( lv->Length() != rt->NumFields() )
Internal("inconsistency evaluating record constructor"); Internal("inconsistency evaluating record constructor");
@ -3401,6 +3419,19 @@ Val* RecordConstructorExpr::Fold(Val* v) const
for ( int i = 0; i < lv->Length(); ++i ) for ( int i = 0; i < lv->Length(); ++i )
rv->Assign(i, lv->Index(i)->Ref()); rv->Assign(i, lv->Index(i)->Ref());
if ( ! same_type(rt, type) )
{
RecordVal* new_val = rv->CoerceTo(type->AsRecordType());
if ( new_val )
{
Unref(rv);
rv = new_val;
}
else
Internal("record constructor coercion failed");
}
return rv; return rv;
} }
@ -3416,37 +3447,89 @@ IMPLEMENT_SERIAL(RecordConstructorExpr, SER_RECORD_CONSTRUCTOR_EXPR);
bool RecordConstructorExpr::DoSerialize(SerialInfo* info) const bool RecordConstructorExpr::DoSerialize(SerialInfo* info) const
{ {
DO_SERIALIZE(SER_RECORD_CONSTRUCTOR_EXPR, UnaryExpr); DO_SERIALIZE(SER_RECORD_CONSTRUCTOR_EXPR, UnaryExpr);
SERIALIZE_OPTIONAL(ctor_type);
return true; return true;
} }
bool RecordConstructorExpr::DoUnserialize(UnserialInfo* info) bool RecordConstructorExpr::DoUnserialize(UnserialInfo* info)
{ {
DO_UNSERIALIZE(UnaryExpr); DO_UNSERIALIZE(UnaryExpr);
BroType* t = 0;
UNSERIALIZE_OPTIONAL(t, RecordType::Unserialize(info));
ctor_type = t->AsRecordType();
return true; return true;
} }
TableConstructorExpr::TableConstructorExpr(ListExpr* constructor_list, TableConstructorExpr::TableConstructorExpr(ListExpr* constructor_list,
attr_list* arg_attrs) attr_list* arg_attrs, BroType* arg_type)
: UnaryExpr(EXPR_TABLE_CONSTRUCTOR, constructor_list) : UnaryExpr(EXPR_TABLE_CONSTRUCTOR, constructor_list)
{ {
if ( IsError() ) if ( IsError() )
return; return;
if ( constructor_list->Exprs().length() == 0 ) if ( arg_type )
SetType(new TableType(new TypeList(base_type(TYPE_ANY)), 0)); {
if ( ! arg_type->IsTable() )
{
Error("bad table constructor type", arg_type);
SetError();
return;
}
SetType(arg_type->Ref());
}
else else
{ {
SetType(init_type(constructor_list)); if ( constructor_list->Exprs().length() == 0 )
SetType(new TableType(new TypeList(base_type(TYPE_ANY)), 0));
else
{
SetType(init_type(constructor_list));
if ( ! type ) if ( ! type )
SetError(); SetError();
else if ( type->Tag() != TYPE_TABLE || else if ( type->Tag() != TYPE_TABLE ||
type->AsTableType()->IsSet() ) type->AsTableType()->IsSet() )
SetError("values in table(...) constructor do not specify a table"); SetError("values in table(...) constructor do not specify a table");
}
} }
attrs = arg_attrs ? new Attributes(arg_attrs, type, false) : 0; attrs = arg_attrs ? new Attributes(arg_attrs, type, false) : 0;
type_list* indices = type->AsTableType()->Indices()->Types();
const expr_list& cle = constructor_list->Exprs();
// check and promote all index expressions in ctor list
loop_over_list(cle, i)
{
if ( cle[i]->Tag() != EXPR_ASSIGN )
continue;
Expr* idx_expr = cle[i]->AsAssignExpr()->Op1();
if ( idx_expr->Tag() != EXPR_LIST )
continue;
expr_list& idx_exprs = idx_expr->AsListExpr()->Exprs();
if ( idx_exprs.length() != indices->length() )
continue;
loop_over_list(idx_exprs, j)
{
Expr* idx = idx_exprs[j];
if ( check_and_promote_expr(idx, (*indices)[j]) )
{
if ( idx != idx_exprs[j] )
idx_exprs.replace(j, idx);
continue;
}
ExprError("inconsistent types in table constructor");
}
}
} }
Val* TableConstructorExpr::Eval(Frame* f) const Val* TableConstructorExpr::Eval(Frame* f) const
@ -3502,16 +3585,30 @@ bool TableConstructorExpr::DoUnserialize(UnserialInfo* info)
} }
SetConstructorExpr::SetConstructorExpr(ListExpr* constructor_list, SetConstructorExpr::SetConstructorExpr(ListExpr* constructor_list,
attr_list* arg_attrs) attr_list* arg_attrs, BroType* arg_type)
: UnaryExpr(EXPR_SET_CONSTRUCTOR, constructor_list) : UnaryExpr(EXPR_SET_CONSTRUCTOR, constructor_list)
{ {
if ( IsError() ) if ( IsError() )
return; return;
if ( constructor_list->Exprs().length() == 0 ) if ( arg_type )
SetType(new ::SetType(new TypeList(base_type(TYPE_ANY)), 0)); {
if ( ! arg_type->IsSet() )
{
Error("bad set constructor type", arg_type);
SetError();
return;
}
SetType(arg_type->Ref());
}
else else
SetType(init_type(constructor_list)); {
if ( constructor_list->Exprs().length() == 0 )
SetType(new ::SetType(new TypeList(base_type(TYPE_ANY)), 0));
else
SetType(init_type(constructor_list));
}
if ( ! type ) if ( ! type )
SetError(); SetError();
@ -3520,6 +3617,37 @@ SetConstructorExpr::SetConstructorExpr(ListExpr* constructor_list,
SetError("values in set(...) constructor do not specify a set"); SetError("values in set(...) constructor do not specify a set");
attrs = arg_attrs ? new Attributes(arg_attrs, type, false) : 0; attrs = arg_attrs ? new Attributes(arg_attrs, type, false) : 0;
type_list* indices = type->AsTableType()->Indices()->Types();
expr_list& cle = constructor_list->Exprs();
if ( indices->length() == 1 )
{
if ( ! check_and_promote_exprs_to_type(constructor_list,
(*indices)[0]) )
ExprError("inconsistent type in set constructor");
}
else if ( indices->length() > 1 )
{
// Check/promote each expression in composite index.
loop_over_list(cle, i)
{
Expr* ce = cle[i];
ListExpr* le = ce->AsListExpr();
if ( ce->Tag() == EXPR_LIST &&
check_and_promote_exprs(le, type->AsTableType()->Indices()) )
{
if ( le != cle[i] )
cle.replace(i, le);
continue;
}
ExprError("inconsistent types in set constructor");
}
}
} }
Val* SetConstructorExpr::Eval(Frame* f) const Val* SetConstructorExpr::Eval(Frame* f) const
@ -3590,31 +3718,50 @@ bool SetConstructorExpr::DoUnserialize(UnserialInfo* info)
return true; return true;
} }
VectorConstructorExpr::VectorConstructorExpr(ListExpr* constructor_list) VectorConstructorExpr::VectorConstructorExpr(ListExpr* constructor_list,
BroType* arg_type)
: UnaryExpr(EXPR_VECTOR_CONSTRUCTOR, constructor_list) : UnaryExpr(EXPR_VECTOR_CONSTRUCTOR, constructor_list)
{ {
if ( IsError() ) if ( IsError() )
return; return;
if ( constructor_list->Exprs().length() == 0 ) if ( arg_type )
{ {
// vector(). if ( arg_type->Tag() != TYPE_VECTOR )
SetType(new ::VectorType(base_type(TYPE_ANY))); {
return; Error("bad vector constructor type", arg_type);
} SetError();
return;
}
BroType* t = merge_type_list(constructor_list); SetType(arg_type->Ref());
if ( t )
{
SetType(new VectorType(t->Ref()));
if ( ! check_and_promote_exprs_to_type(constructor_list, t) )
ExprError("inconsistent types in vector constructor");
Unref(t);
} }
else else
SetError(); {
if ( constructor_list->Exprs().length() == 0 )
{
// vector().
SetType(new ::VectorType(base_type(TYPE_ANY)));
return;
}
BroType* t = merge_type_list(constructor_list);
if ( t )
{
SetType(new VectorType(t->Ref()));
Unref(t);
}
else
{
SetError();
return;
}
}
if ( ! check_and_promote_exprs_to_type(constructor_list,
type->AsVectorType()->YieldType()) )
ExprError("inconsistent types in vector constructor");
} }
Val* VectorConstructorExpr::Eval(Frame* f) const Val* VectorConstructorExpr::Eval(Frame* f) const

View file

@ -57,6 +57,8 @@ extern const char* expr_name(BroExprTag t);
class Stmt; class Stmt;
class Frame; class Frame;
class ListExpr; class ListExpr;
class NameExpr;
class AssignExpr;
class CallExpr; class CallExpr;
class EventExpr; class EventExpr;
@ -159,12 +161,37 @@ public:
CHECK_TAG(tag, EXPR_LIST, "ExprVal::AsListExpr", expr_name) CHECK_TAG(tag, EXPR_LIST, "ExprVal::AsListExpr", expr_name)
return (const ListExpr*) this; return (const ListExpr*) this;
} }
ListExpr* AsListExpr() ListExpr* AsListExpr()
{ {
CHECK_TAG(tag, EXPR_LIST, "ExprVal::AsListExpr", expr_name) CHECK_TAG(tag, EXPR_LIST, "ExprVal::AsListExpr", expr_name)
return (ListExpr*) this; return (ListExpr*) this;
} }
const NameExpr* AsNameExpr() const
{
CHECK_TAG(tag, EXPR_NAME, "ExprVal::AsNameExpr", expr_name)
return (const NameExpr*) this;
}
NameExpr* AsNameExpr()
{
CHECK_TAG(tag, EXPR_NAME, "ExprVal::AsNameExpr", expr_name)
return (NameExpr*) this;
}
const AssignExpr* AsAssignExpr() const
{
CHECK_TAG(tag, EXPR_ASSIGN, "ExprVal::AsAssignExpr", expr_name)
return (const AssignExpr*) this;
}
AssignExpr* AsAssignExpr()
{
CHECK_TAG(tag, EXPR_ASSIGN, "ExprVal::AsAssignExpr", expr_name)
return (AssignExpr*) this;
}
void Describe(ODesc* d) const; void Describe(ODesc* d) const;
bool Serialize(SerialInfo* info) const; bool Serialize(SerialInfo* info) const;
@ -729,7 +756,8 @@ protected:
class RecordConstructorExpr : public UnaryExpr { class RecordConstructorExpr : public UnaryExpr {
public: public:
RecordConstructorExpr(ListExpr* constructor_list); RecordConstructorExpr(ListExpr* constructor_list, BroType* arg_type = 0);
~RecordConstructorExpr();
protected: protected:
friend class Expr; friend class Expr;
@ -741,11 +769,14 @@ protected:
void ExprDescribe(ODesc* d) const; void ExprDescribe(ODesc* d) const;
DECLARE_SERIAL(RecordConstructorExpr); DECLARE_SERIAL(RecordConstructorExpr);
RecordType* ctor_type; // type inferred from the ctor expression list args
}; };
class TableConstructorExpr : public UnaryExpr { class TableConstructorExpr : public UnaryExpr {
public: public:
TableConstructorExpr(ListExpr* constructor_list, attr_list* attrs); TableConstructorExpr(ListExpr* constructor_list, attr_list* attrs,
BroType* arg_type = 0);
~TableConstructorExpr() { Unref(attrs); } ~TableConstructorExpr() { Unref(attrs); }
Attributes* Attrs() { return attrs; } Attributes* Attrs() { return attrs; }
@ -767,7 +798,8 @@ protected:
class SetConstructorExpr : public UnaryExpr { class SetConstructorExpr : public UnaryExpr {
public: public:
SetConstructorExpr(ListExpr* constructor_list, attr_list* attrs); SetConstructorExpr(ListExpr* constructor_list, attr_list* attrs,
BroType* arg_type = 0);
~SetConstructorExpr() { Unref(attrs); } ~SetConstructorExpr() { Unref(attrs); }
Attributes* Attrs() { return attrs; } Attributes* Attrs() { return attrs; }
@ -789,7 +821,7 @@ protected:
class VectorConstructorExpr : public UnaryExpr { class VectorConstructorExpr : public UnaryExpr {
public: public:
VectorConstructorExpr(ListExpr* constructor_list); VectorConstructorExpr(ListExpr* constructor_list, BroType* arg_type = 0);
Val* Eval(Frame* f) const; Val* Eval(Frame* f) const;

View file

@ -217,6 +217,11 @@ public:
return tag == TYPE_TABLE && (YieldType() == 0); return tag == TYPE_TABLE && (YieldType() == 0);
} }
int IsTable() const
{
return tag == TYPE_TABLE && (YieldType() != 0);
}
BroType* Ref() { ::Ref(this); return this; } BroType* Ref() { ::Ref(this); return this; }
virtual void Describe(ODesc* d) const; virtual void Describe(ODesc* d) const;

View file

@ -522,10 +522,52 @@ expr:
$$ = new VectorConstructorExpr($3); $$ = new VectorConstructorExpr($3);
} }
| expr '(' opt_expr_list ')' | expr '('
{ {
set_location(@1, @4); if ( $1->Tag() == EXPR_NAME && $1->Type()->IsTable() )
$$ = new CallExpr($1, $3, in_hook > 0); ++in_init;
}
opt_expr_list
{
if ( $1->Tag() == EXPR_NAME && $1->Type()->IsTable() )
--in_init;
}
')'
{
set_location(@1, @6);
BroType* ctor_type = 0;
if ( $1->Tag() == EXPR_NAME &&
(ctor_type = $1->AsNameExpr()->Id()->AsType()) )
{
switch ( ctor_type->Tag() ) {
case TYPE_RECORD:
$$ = new RecordConstructorExpr($4, ctor_type);
break;
case TYPE_TABLE:
if ( ctor_type->IsTable() )
$$ = new TableConstructorExpr($4, 0, ctor_type);
else
$$ = new SetConstructorExpr($4, 0, ctor_type);
break;
case TYPE_VECTOR:
$$ = new VectorConstructorExpr($4, ctor_type);
break;
default:
$1->Error("constructor type not implemented");
YYERROR;
}
}
else
$$ = new CallExpr($1, $4, in_hook > 0);
} }
| TOK_HOOK { ++in_hook; } expr | TOK_HOOK { ++in_hook; } expr

View file

@ -0,0 +1,2 @@
[min=<uninitialized>, max=2]
[min=7, max=42]

View file

@ -0,0 +1,13 @@
{
1,
5,
3
}
{
[min=<uninitialized>, max=5],
[min=<uninitialized>, max=2]
}
{
[test, 1] ,
[cool, 2]
}

View file

@ -0,0 +1,19 @@
{
[1] = one,
[5] = five,
[3] = three
}
{
[[min=<uninitialized>, max=5]] = max5,
[[min=<uninitialized>, max=2]] = max2
}
{
[test, 1] = test1,
[cool, 2] = cool2
}
{
[two] = 2.0,
[one] = 1.0,
[three] = 3.0
}
0

View file

@ -0,0 +1,3 @@
[one, two, three]
[1.0, 2.0, 3.0]
[[min=<uninitialized>, max=1], [min=<uninitialized>, max=2], [min=<uninitialized>, max=3]]

View file

@ -0,0 +1,12 @@
# @TEST-EXEC: bro -b %INPUT >out
# @TEST-EXEC: btest-diff out
type MyRec: record {
min: count &optional;
max: count;
};
local myrec: MyRec = MyRec($max=2);
print myrec;
myrec = MyRec($min=7, $max=42);
print myrec;

View file

@ -0,0 +1,19 @@
# @TEST-EXEC: bro -b %INPUT >out
# @TEST-EXEC: btest-diff out
type MyRec: record {
min: count &optional;
max: count;
};
type FooSet: set[count];
type FooSetRec: set[MyRec];
type FooSetComp: set[string, count];
global myset: FooSet = FooSet(1, 5, 3);
global mysetrec: FooSetRec = FooSetRec([$max=5], [$max=2]);
global mysetcomp: FooSetComp = FooSetComp(["test", 1], ["cool", 2]);
print myset;
print mysetrec;
print mysetcomp;

View file

@ -0,0 +1,24 @@
# @TEST-EXEC: bro -b %INPUT >out
# @TEST-EXEC: btest-diff out
type MyRec: record {
min: count &optional;
max: count;
};
type FooTable: table[count] of string;
type FooTableRec: table[MyRec] of string;
type FooTableComp: table[string, count] of string;
type FooTableY: table[string] of double;
global mytable: FooTable = FooTable([1] = "one", [5] = "five", [3] = "three");
global mytablerec: FooTableRec = FooTableRec([[$max=5]] = "max5", [[$max=2]] = "max2");
global mytablecomp: FooTableComp = FooTableComp(["test", 1] = "test1", ["cool",
2] = "cool2");
global mytabley: FooTableY = FooTableY(["one"] = 1, ["two"] = 2, ["three"] = 3) &default=0;
print mytable;
print mytablerec;
print mytablecomp;
print mytabley;
print mytabley["test"];

View file

@ -0,0 +1,19 @@
# @TEST-EXEC: bro -b %INPUT >out
# @TEST-EXEC: btest-diff out
type MyRec: record {
min: count &optional;
max: count;
};
type FooVector: vector of string;
type FooVectorD: vector of double;
type FooVectorRec: vector of MyRec;
global myvec: FooVector = FooVector("one", "two", "three");
global myvecd: FooVectorD = FooVectorD(1, 2, 3);
global myvecrec: FooVectorRec = FooVectorRec([$max=1], [$max=2], [$max=3]);
print myvec;
print myvecd;
print myvecrec;