Add key-value for loop

This commit is contained in:
ZekeMedley 2019-03-14 09:46:16 -07:00
parent a36ac12e88
commit 1f7924754e
7 changed files with 145 additions and 6 deletions

View file

@ -1421,12 +1421,48 @@ ForStmt::ForStmt(id_list* arg_loop_vars, Expr* loop_expr)
e->Error("target to iterate over must be a table, set, vector, or string"); e->Error("target to iterate over must be a table, set, vector, or string");
} }
ForStmt::ForStmt(id_list* arg_loop_vars, Expr* loop_expr, ID* val_var)
: ForStmt(arg_loop_vars, loop_expr)
{
value_var = val_var;
// Valdate that key-value for loop is being used on a table
if ( e->Type()->Tag() == TYPE_TABLE )
{
// Type of values table holds
BroType* yield_type = e->Type()->AsTableType()->YieldType();
// Verify value_vars type if its already been defined
if ( value_var->Type() )
{
if ( ! same_type(value_var->Type(), yield_type) )
{
value_var->Type()->Error("type clash in iteration", yield_type);
}
}
else
{
delete add_local(value_var,
yield_type->Ref(), INIT_NONE,
0, 0, VAR_REGULAR);
}
}
else
{
e->Error("key value for loops only support iteration over tables");
}
}
ForStmt::~ForStmt() ForStmt::~ForStmt()
{ {
loop_over_list(*loop_vars, i) loop_over_list(*loop_vars, i)
Unref((*loop_vars)[i]); Unref((*loop_vars)[i]);
delete loop_vars; delete loop_vars;
if (value_var)
{
Unref(value_var);
}
Unref(body); Unref(body);
} }
@ -1443,12 +1479,18 @@ Val* ForStmt::DoExec(Frame* f, Val* v, stmt_flow_type& flow) const
return 0; return 0;
HashKey* k; HashKey* k;
TableEntryVal* current_tev;
IterCookie* c = loop_vals->InitForIteration(); IterCookie* c = loop_vals->InitForIteration();
while ( loop_vals->NextEntry(k, c) ) while ( (current_tev = loop_vals->NextEntry(k, c)) )
{ {
ListVal* ind_lv = tv->RecoverIndex(k); ListVal* ind_lv = tv->RecoverIndex(k);
delete k; delete k;
if (value_var)
{
f->SetElement(value_var->Offset(), current_tev->Value()->Ref());
}
for ( int i = 0; i < ind_lv->Length(); i++ ) for ( int i = 0; i < ind_lv->Length(); i++ )
f->SetElement((*loop_vars)[i]->Offset(), ind_lv->Index(i)->Ref()); f->SetElement((*loop_vars)[i]->Offset(), ind_lv->Index(i)->Ref());
Unref(ind_lv); Unref(ind_lv);

View file

@ -337,6 +337,8 @@ protected:
class ForStmt : public ExprStmt { class ForStmt : public ExprStmt {
public: public:
ForStmt(id_list* loop_vars, Expr* loop_expr); ForStmt(id_list* loop_vars, Expr* loop_expr);
// Special constructor for key value for loop.
ForStmt(id_list* loop_vars, Expr* loop_expr, ID* val_var);
~ForStmt() override; ~ForStmt() override;
void AddBody(Stmt* arg_body) { body = arg_body; } void AddBody(Stmt* arg_body) { body = arg_body; }
@ -361,6 +363,9 @@ protected:
id_list* loop_vars; id_list* loop_vars;
Stmt* body; Stmt* body;
// Stores the value variable being used for a key value for loop.
// Always set to nullptr unless special constructor is called.
ID* value_var = nullptr;
}; };
class NextStmt : public Stmt { class NextStmt : public Stmt {

View file

@ -1592,7 +1592,7 @@ for_head:
if ( loop_var ) if ( loop_var )
{ {
if ( loop_var->IsGlobal() ) if ( loop_var->IsGlobal() )
loop_var->Error("global used in for loop"); loop_var->Error("global variable used in for loop");
} }
else else
@ -1606,7 +1606,60 @@ for_head:
} }
| |
TOK_FOR '(' '[' local_id_list ']' TOK_IN expr ')' TOK_FOR '(' '[' local_id_list ']' TOK_IN expr ')'
{ $$ = new ForStmt($4, $7); } {
$$ = new ForStmt($4, $7);
}
|
TOK_FOR '(' TOK_ID ',' TOK_ID TOK_IN expr ')'
{
set_location(@1, @8);
const char* module = current_module.c_str();
// Check for previous definitions of key and
// value variables.
ID* key_var = lookup_ID($3, module);
ID* val_var = lookup_ID($5, module);
// Validate previous definitions as needed.
if ( key_var ) {
if ( key_var->IsGlobal() ) {
key_var->Error("global variable used in for loop");
}
} else {
key_var = install_ID($3, module, false, false);
}
if ( val_var ) {
if ( val_var->IsGlobal() ) {
val_var->Error("global variable used in for loop");
}
} else {
val_var = install_ID($5, module, false, false);
}
id_list* loop_vars = new id_list;
loop_vars->append(key_var);
$$ = new ForStmt(loop_vars, $7, val_var);
}
|
TOK_FOR '(' '[' local_id_list ']' ',' TOK_ID TOK_IN expr ')'
{
set_location(@1, @10);
const char* module = current_module.c_str();
// Validate value variable
ID* val_var = lookup_ID($7, module);
if ( val_var ) {
if ( val_var->IsGlobal() ) {
val_var->Error("global variable used in for loop");
}
} else {
val_var = install_ID($7, module, false, false);
}
$$ = new ForStmt($4, $9, val_var);
}
; ;
local_id_list: local_id_list:

View file

@ -1,3 +1,4 @@
for loop (PASS) for loop (PASS)
for loop with break (PASS) for loop with break (PASS)
for loop with next (PASS) for loop with next (PASS)
keys that are tuples (PASS)

View file

@ -0,0 +1,4 @@
1, hello
55, goodbye
goodbye, world, 55
hello, world, 1

View file

@ -40,5 +40,16 @@ event bro_init()
test_case("Error: this should not happen", F); test_case("Error: this should not happen", F);
} }
test_case("for loop with next", ct == 3 ); test_case("for loop with next", ct == 3 );
}
# Test keys that are tuples
local t: table[count, count] of string = table();
t[1, 2] = "hi";
local s1: string = "";
for ( [i, j] in t )
s1 = fmt("%d %d %s", i, j, t[i,j]);
test_case("keys that are tuples", s1 == "1 2 hi");
# Tests for key value for loop are in key-value-for.bro
}

View file

@ -0,0 +1,23 @@
# @TEST-EXEC: bro -b %INPUT >out
# @TEST-EXEC: btest-diff out
event bro_init() {
# Test single keys
local t: table[count] of string = table();
t[1] = "hello";
t[55] = "goodbye";
for (key, value in t){
print key, value;
}
# Test multiple keys
local tkk: table[string, string] of count = table();
tkk["hello", "world"] = 1;
tkk["goodbye", "world"] = 55;
for ([k1, k2], val in tkk) {
print k1, k2, val;
}
}