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
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
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

@ -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",
};
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
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
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``:
.. 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");
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:
.. code:: bro
@ -402,6 +464,19 @@ The Bro scripting language supports the following built-in types.
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
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);
}
RecordConstructorExpr::RecordConstructorExpr(ListExpr* constructor_list)
RecordConstructorExpr::RecordConstructorExpr(ListExpr* constructor_list,
BroType* arg_type)
: UnaryExpr(EXPR_RECORD_CONSTRUCTOR, constructor_list)
{
if ( IsError() )
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
// either record's or record-field-assign, and build up a
// 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
@ -3391,7 +3409,7 @@ Val* RecordConstructorExpr::InitVal(const BroType* t, Val* aggr) const
Val* RecordConstructorExpr::Fold(Val* v) const
{
ListVal* lv = v->AsListVal();
RecordType* rt = type->AsRecordType();
RecordType* rt = ctor_type->AsRecordType();
if ( lv->Length() != rt->NumFields() )
Internal("inconsistency evaluating record constructor");
@ -3401,6 +3419,19 @@ Val* RecordConstructorExpr::Fold(Val* v) const
for ( int i = 0; i < lv->Length(); ++i )
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;
}
@ -3416,22 +3447,39 @@ IMPLEMENT_SERIAL(RecordConstructorExpr, SER_RECORD_CONSTRUCTOR_EXPR);
bool RecordConstructorExpr::DoSerialize(SerialInfo* info) const
{
DO_SERIALIZE(SER_RECORD_CONSTRUCTOR_EXPR, UnaryExpr);
SERIALIZE_OPTIONAL(ctor_type);
return true;
}
bool RecordConstructorExpr::DoUnserialize(UnserialInfo* info)
{
DO_UNSERIALIZE(UnaryExpr);
BroType* t = 0;
UNSERIALIZE_OPTIONAL(t, RecordType::Unserialize(info));
ctor_type = t->AsRecordType();
return true;
}
TableConstructorExpr::TableConstructorExpr(ListExpr* constructor_list,
attr_list* arg_attrs)
attr_list* arg_attrs, BroType* arg_type)
: UnaryExpr(EXPR_TABLE_CONSTRUCTOR, constructor_list)
{
if ( IsError() )
return;
if ( arg_type )
{
if ( ! arg_type->IsTable() )
{
Error("bad table constructor type", arg_type);
SetError();
return;
}
SetType(arg_type->Ref());
}
else
{
if ( constructor_list->Exprs().length() == 0 )
SetType(new TableType(new TypeList(base_type(TYPE_ANY)), 0));
else
@ -3445,8 +3493,43 @@ TableConstructorExpr::TableConstructorExpr(ListExpr* constructor_list,
type->AsTableType()->IsSet() )
SetError("values in table(...) constructor do not specify a table");
}
}
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
@ -3502,16 +3585,30 @@ bool TableConstructorExpr::DoUnserialize(UnserialInfo* info)
}
SetConstructorExpr::SetConstructorExpr(ListExpr* constructor_list,
attr_list* arg_attrs)
attr_list* arg_attrs, BroType* arg_type)
: UnaryExpr(EXPR_SET_CONSTRUCTOR, constructor_list)
{
if ( IsError() )
return;
if ( arg_type )
{
if ( ! arg_type->IsSet() )
{
Error("bad set constructor type", arg_type);
SetError();
return;
}
SetType(arg_type->Ref());
}
else
{
if ( constructor_list->Exprs().length() == 0 )
SetType(new ::SetType(new TypeList(base_type(TYPE_ANY)), 0));
else
SetType(init_type(constructor_list));
}
if ( ! type )
SetError();
@ -3520,6 +3617,37 @@ SetConstructorExpr::SetConstructorExpr(ListExpr* constructor_list,
SetError("values in set(...) constructor do not specify a set");
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
@ -3590,12 +3718,26 @@ bool SetConstructorExpr::DoUnserialize(UnserialInfo* info)
return true;
}
VectorConstructorExpr::VectorConstructorExpr(ListExpr* constructor_list)
VectorConstructorExpr::VectorConstructorExpr(ListExpr* constructor_list,
BroType* arg_type)
: UnaryExpr(EXPR_VECTOR_CONSTRUCTOR, constructor_list)
{
if ( IsError() )
return;
if ( arg_type )
{
if ( arg_type->Tag() != TYPE_VECTOR )
{
Error("bad vector constructor type", arg_type);
SetError();
return;
}
SetType(arg_type->Ref());
}
else
{
if ( constructor_list->Exprs().length() == 0 )
{
// vector().
@ -3604,17 +3746,22 @@ VectorConstructorExpr::VectorConstructorExpr(ListExpr* constructor_list)
}
BroType* t = merge_type_list(constructor_list);
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
{
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

View file

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

View file

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

View file

@ -522,10 +522,52 @@ expr:
$$ = new VectorConstructorExpr($3);
}
| expr '(' opt_expr_list ')'
| expr '('
{
set_location(@1, @4);
$$ = new CallExpr($1, $3, in_hook > 0);
if ( $1->Tag() == EXPR_NAME && $1->Type()->IsTable() )
++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

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;