mirror of
https://github.com/zeek/zeek.git
synced 2025-10-04 15:48:19 +00:00
Add 'while' statement to Bro language.
This commit is contained in:
parent
212368b245
commit
062baefde0
9 changed files with 328 additions and 1 deletions
|
@ -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)
|
||||||
|
|
122
src/Stmt.cc
122
src/Stmt.cc
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
29
src/Stmt.h
29
src/Stmt.h
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
12
testing/btest/Baseline/language.while/out
Normal file
12
testing/btest/Baseline/language.while/out
Normal 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]
|
80
testing/btest/core/leaks/while.bro
Normal file
80
testing/btest/core/leaks/while.bro
Normal 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();
|
||||||
|
}
|
77
testing/btest/language/while.bro
Normal file
77
testing/btest/language/while.bro
Normal 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();
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue