Add 'while' statement to Bro language.

This commit is contained in:
Jon Siwek 2015-02-13 11:24:32 -06:00
parent 212368b245
commit 062baefde0
9 changed files with 328 additions and 1 deletions

View file

@ -181,6 +181,7 @@ SERIAL_STMT(INIT_STMT, 17)
SERIAL_STMT(NULL_STMT, 18) SERIAL_STMT(NULL_STMT, 18)
SERIAL_STMT(WHEN_STMT, 19) SERIAL_STMT(WHEN_STMT, 19)
SERIAL_STMT(FALLTHROUGH_STMT, 20) SERIAL_STMT(FALLTHROUGH_STMT, 20)
SERIAL_STMT(WHILE_STMT, 21)
#define SERIAL_TYPE(name, val) SERIAL_CONST(name, val, BRO_TYPE) #define SERIAL_TYPE(name, val) SERIAL_CONST(name, val, BRO_TYPE)
SERIAL_TYPE(BRO_TYPE, 1) SERIAL_TYPE(BRO_TYPE, 1)

View file

@ -23,7 +23,7 @@ const char* stmt_name(BroStmtTag t)
"print", "event", "expr", "if", "when", "switch", "print", "event", "expr", "if", "when", "switch",
"for", "next", "break", "return", "add", "delete", "for", "next", "break", "return", "add", "delete",
"list", "bodylist", "list", "bodylist",
"<init>", "fallthrough", "<init>", "fallthrough", "while",
"null", "null",
}; };
@ -1127,6 +1127,126 @@ bool EventStmt::DoUnserialize(UnserialInfo* info)
return event_expr != 0; return event_expr != 0;
} }
WhileStmt::WhileStmt(Expr* arg_loop_condition, Stmt* arg_body)
: loop_condition(arg_loop_condition), body(arg_body)
{
if ( ! loop_condition->IsError() &&
! IsBool(loop_condition->Type()->Tag()) )
loop_condition->Error("while conditional must be boolean");
}
WhileStmt::~WhileStmt()
{
Unref(loop_condition);
Unref(body);
}
int WhileStmt::IsPure() const
{
return loop_condition->IsPure() && body->IsPure();
}
void WhileStmt::Describe(ODesc* d) const
{
Stmt::Describe(d);
if ( d->IsReadable() )
d->Add("(");
loop_condition->Describe(d);
if ( d->IsReadable() )
d->Add(")");
d->SP();
d->PushIndent();
body->AccessStats(d);
body->Describe(d);
d->PopIndent();
}
TraversalCode WhileStmt::Traverse(TraversalCallback* cb) const
{
TraversalCode tc = cb->PreStmt(this);
HANDLE_TC_STMT_PRE(tc);
tc = loop_condition->Traverse(cb);
HANDLE_TC_STMT_PRE(tc);
tc = body->Traverse(cb);
HANDLE_TC_STMT_PRE(tc);
tc = cb->PostStmt(this);
HANDLE_TC_STMT_POST(tc);
}
Val* WhileStmt::Exec(Frame* f, stmt_flow_type& flow) const
{
RegisterAccess();
flow = FLOW_NEXT;
Val* rval = 0;
for ( ; ; )
{
Val* cond = loop_condition->Eval(f);
if ( ! cond )
break;
bool cont = cond->AsBool();
Unref(cond);
if ( ! cont )
break;
flow = FLOW_NEXT;
rval = body->Exec(f, flow);
if ( flow == FLOW_BREAK || flow == FLOW_RETURN )
break;
}
if ( flow == FLOW_LOOP || flow == FLOW_BREAK )
flow = FLOW_NEXT;
return rval;
}
Stmt* WhileStmt::Simplify()
{
loop_condition = simplify_expr(loop_condition, SIMPLIFY_GENERAL);
if ( loop_condition->IsConst() && loop_condition->IsZero() )
return new NullStmt();
body = simplify_stmt(body);
return this;
}
IMPLEMENT_SERIAL(WhileStmt, SER_WHILE_STMT);
bool WhileStmt::DoSerialize(SerialInfo* info) const
{
DO_SERIALIZE(SER_WHILE_STMT, Stmt);
if ( ! loop_condition->Serialize(info) )
return false;
return body->Serialize(info);
}
bool WhileStmt::DoUnserialize(UnserialInfo* info)
{
DO_UNSERIALIZE(Stmt);
loop_condition = Expr::Unserialize(info);
if ( ! loop_condition )
return false;
body = Stmt::Unserialize(info);
return body != 0;
}
ForStmt::ForStmt(id_list* arg_loop_vars, Expr* loop_expr) ForStmt::ForStmt(id_list* arg_loop_vars, Expr* loop_expr)
: ExprStmt(STMT_FOR, loop_expr) : ExprStmt(STMT_FOR, loop_expr)
{ {

View file

@ -310,6 +310,35 @@ protected:
EventExpr* event_expr; EventExpr* event_expr;
}; };
class WhileStmt : public Stmt {
public:
WhileStmt(Expr* loop_condition, Stmt* body);
~WhileStmt();
int IsPure() const;
void Describe(ODesc* d) const;
TraversalCode Traverse(TraversalCallback* cb) const;
protected:
friend class Stmt;
DECLARE_SERIAL(WhileStmt);
WhileStmt()
{ loop_condition = 0; body = 0; }
Val* Exec(Frame* f, stmt_flow_type& flow) const;
Stmt* Simplify();
Expr* loop_condition;
Stmt* body;
};
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);

View file

@ -17,6 +17,7 @@ typedef enum {
STMT_LIST, STMT_EVENT_BODY_LIST, STMT_LIST, STMT_EVENT_BODY_LIST,
STMT_INIT, STMT_INIT,
STMT_FALLTHROUGH, STMT_FALLTHROUGH,
STMT_WHILE,
STMT_NULL STMT_NULL
#define NUM_STMTS (int(STMT_NULL) + 1) #define NUM_STMTS (int(STMT_NULL) + 1)
} BroStmtTag; } BroStmtTag;

View file

@ -16,6 +16,7 @@
%token TOK_REMOVE_FROM TOK_RETURN TOK_SCHEDULE TOK_SET %token TOK_REMOVE_FROM TOK_RETURN TOK_SCHEDULE TOK_SET
%token TOK_STRING TOK_SUBNET TOK_SWITCH TOK_TABLE %token TOK_STRING TOK_SUBNET TOK_SWITCH TOK_TABLE
%token TOK_TIME TOK_TIMEOUT TOK_TIMER TOK_TYPE TOK_UNION TOK_VECTOR TOK_WHEN %token TOK_TIME TOK_TIMEOUT TOK_TIMER TOK_TYPE TOK_UNION TOK_VECTOR TOK_WHEN
%token TOK_WHILE
%token TOK_ATTR_ADD_FUNC TOK_ATTR_ENCRYPT TOK_ATTR_DEFAULT %token TOK_ATTR_ADD_FUNC TOK_ATTR_ENCRYPT TOK_ATTR_DEFAULT
%token TOK_ATTR_OPTIONAL TOK_ATTR_REDEF TOK_ATTR_ROTATE_INTERVAL %token TOK_ATTR_OPTIONAL TOK_ATTR_REDEF TOK_ATTR_ROTATE_INTERVAL
@ -1340,6 +1341,11 @@ stmt:
$1->AsForStmt()->AddBody($2); $1->AsForStmt()->AddBody($2);
} }
| TOK_WHILE '(' expr ')' stmt
{
$$ = new WhileStmt($3, $5);
}
| TOK_NEXT ';' opt_no_test | TOK_NEXT ';' opt_no_test
{ {
set_location(@1, @2); set_location(@1, @2);

View file

@ -221,6 +221,7 @@ export return TOK_EXPORT;
fallthrough return TOK_FALLTHROUGH; fallthrough return TOK_FALLTHROUGH;
file return TOK_FILE; file return TOK_FILE;
for return TOK_FOR; for return TOK_FOR;
while return TOK_WHILE;
function return TOK_FUNCTION; function return TOK_FUNCTION;
global return TOK_GLOBAL; global return TOK_GLOBAL;
"?$" return TOK_HAS_FIELD; "?$" return TOK_HAS_FIELD;

View file

@ -0,0 +1,12 @@
10
s
ss
sss
{
7,
1,
9,
5,
3
}
[number 0, number 1, number 2, number 3, number 4, number 5, number 6, number 7, number 8, number 9, number 10, number 11, number 12]

View file

@ -0,0 +1,80 @@
# @TEST-GROUP: leaks
# @TEST-REQUIRES: bro --help 2>&1 | grep -q mem-leaks
# @TEST-EXEC: HEAP_CHECK_DUMP_DIRECTORY=. HEAPCHECK=local btest-bg-run bro bro -m -b -r $TRACES/http/get.trace %INPUT
# @TEST-EXEC: btest-bg-wait 30
function test_noop()
{
while ( F )
print "noooooooooo";
}
function test_it()
{
local i = 0;
while ( i < 10 )
++i;
print i;
}
function test_break()
{
local s = "";
while ( T )
{
s += "s";
print s;
if ( s == "sss" )
break;
}
}
function test_next()
{
local s: set[count];
local i = 0;
while ( 9 !in s )
{
++i;
if ( i % 2 == 0 )
next;
add s[i];
}
print s;
}
function test_return(): vector of string
{
local i = 0;
local rval: vector of string;
while ( T )
{
rval[i] = fmt("number %d", i);
++i;
if ( i == 13 )
return rval;
}
rval[0] = "noooo";
return rval;
}
event new_connection(c: connection)
{
test_noop();
test_it();
test_break();
test_next();
print test_return();
}

View file

@ -0,0 +1,77 @@
# @TEST-EXEC: bro -b %INPUT >out
# @TEST-EXEC: btest-diff out
function test_noop()
{
while ( F )
print "noooooooooo";
}
function test_it()
{
local i = 0;
while ( i < 10 )
++i;
print i;
}
function test_break()
{
local s = "";
while ( T )
{
s += "s";
print s;
if ( s == "sss" )
break;
}
}
function test_next()
{
local s: set[count];
local i = 0;
while ( 9 !in s )
{
++i;
if ( i % 2 == 0 )
next;
add s[i];
}
print s;
}
function test_return(): vector of string
{
local i = 0;
local rval: vector of string;
while ( T )
{
rval[i] = fmt("number %d", i);
++i;
if ( i == 13 )
return rval;
}
rval[0] = "noooo";
return rval;
}
event bro_init()
{
test_noop();
test_it();
test_break();
test_next();
print test_return();
}