mirror of
https://github.com/zeek/zeek.git
synced 2025-10-04 23:58:20 +00:00
Merge remote-tracking branch 'origin/topic/jsiwek/while'
Added documentation to statement reference. * origin/topic/jsiwek/while: Add 'while' statement to Bro language. BIT-1315 #merged
This commit is contained in:
commit
abcb8e7c95
13 changed files with 374 additions and 4 deletions
4
CHANGES
4
CHANGES
|
@ -1,4 +1,8 @@
|
||||||
|
|
||||||
|
2.3-426 | 2015-02-20 12:45:51 -0800
|
||||||
|
|
||||||
|
* Add 'while' statement to Bro language. Really. (Jon Siwek)
|
||||||
|
|
||||||
2.3-424 | 2015-02-20 12:39:10 -0800
|
2.3-424 | 2015-02-20 12:39:10 -0800
|
||||||
|
|
||||||
* Add the ability to remove surrounding braces from the JSON
|
* Add the ability to remove surrounding braces from the JSON
|
||||||
|
|
8
NEWS
8
NEWS
|
@ -31,6 +31,14 @@ New Functionality
|
||||||
- Bro's file analysis now supports reassembly of files that are not
|
- Bro's file analysis now supports reassembly of files that are not
|
||||||
transferred/seen sequentially.
|
transferred/seen sequentially.
|
||||||
|
|
||||||
|
- Bro's scripting language now has a ``while`` statement::
|
||||||
|
|
||||||
|
while ( i < 5 )
|
||||||
|
print ++i;
|
||||||
|
|
||||||
|
``next`` and ``break`` can be used inside the loop's body just like
|
||||||
|
with ``for`` loops.
|
||||||
|
|
||||||
Changed Functionality
|
Changed Functionality
|
||||||
---------------------
|
---------------------
|
||||||
|
|
||||||
|
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
||||||
2.3-424
|
2.3-426
|
||||||
|
|
|
@ -45,8 +45,11 @@ Statements
|
||||||
| | file |
|
| | file |
|
||||||
+----------------------------+------------------------+
|
+----------------------------+------------------------+
|
||||||
| :bro:keyword:`for`, | Loop over each |
|
| :bro:keyword:`for`, | Loop over each |
|
||||||
| :bro:keyword:`next`, | element in a container |
|
| :bro:keyword:`while`, | element in a container |
|
||||||
| :bro:keyword:`break` | object |
|
| :bro:keyword:`next`, | object (``for``), or |
|
||||||
|
| :bro:keyword:`break` | as long as a condition |
|
||||||
|
| | evaluates to true |
|
||||||
|
| | (``while``). |
|
||||||
+----------------------------+------------------------+
|
+----------------------------+------------------------+
|
||||||
| :bro:keyword:`if` | Evaluate boolean |
|
| :bro:keyword:`if` | Evaluate boolean |
|
||||||
| | expression and if true,|
|
| | expression and if true,|
|
||||||
|
@ -563,6 +566,36 @@ Here are the statements that the Bro scripting language supports.
|
||||||
See the :bro:keyword:`return` statement for an explanation of how to
|
See the :bro:keyword:`return` statement for an explanation of how to
|
||||||
create an asynchronous function in a Bro script.
|
create an asynchronous function in a Bro script.
|
||||||
|
|
||||||
|
.. bro:keyword:: while
|
||||||
|
|
||||||
|
A "while" loop iterates over a body statement as long a given
|
||||||
|
condition remains true.
|
||||||
|
|
||||||
|
A :bro:keyword:`break` statement can be used at any time to immediately
|
||||||
|
terminate the "while" loop, and a :bro:keyword:`next` statement can be
|
||||||
|
used to skip to the next loop iteration.
|
||||||
|
|
||||||
|
Example::
|
||||||
|
|
||||||
|
local i = 0;
|
||||||
|
|
||||||
|
while ( i < 5 )
|
||||||
|
print ++i;
|
||||||
|
|
||||||
|
while ( some_cond() )
|
||||||
|
{
|
||||||
|
local finish_up = F;
|
||||||
|
|
||||||
|
if ( skip_ahead() )
|
||||||
|
next;
|
||||||
|
|
||||||
|
[...]
|
||||||
|
|
||||||
|
if ( finish_up )
|
||||||
|
break;
|
||||||
|
|
||||||
|
[...]
|
||||||
|
}
|
||||||
|
|
||||||
.. _compound statement:
|
.. _compound statement:
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
27
src/Stmt.h
27
src/Stmt.h
|
@ -310,6 +310,33 @@ 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;
|
||||||
|
|
||||||
|
WhileStmt()
|
||||||
|
{ loop_condition = 0; body = 0; }
|
||||||
|
|
||||||
|
Val* Exec(Frame* f, stmt_flow_type& flow) const;
|
||||||
|
Stmt* Simplify();
|
||||||
|
|
||||||
|
DECLARE_SERIAL(WhileStmt);
|
||||||
|
|
||||||
|
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