Filter: Implement mixed declarations of local variables
Allow variable declarations mixed with code, also in nested blocks with proper scoping, and with variable initializers. E.g: function fn(int a) { int b; int c = 10; if a > 20 then { b = 30; int d = c * 2; print a, b, c, d; } string s = "Hello"; }
This commit is contained in:
parent
a2527ee53d
commit
1ac8e11bba
4 changed files with 116 additions and 37 deletions
|
@ -1260,8 +1260,8 @@ this:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
filter not_too_far
|
filter not_too_far
|
||||||
int var;
|
|
||||||
{
|
{
|
||||||
|
int var;
|
||||||
if defined( rip_metric ) then
|
if defined( rip_metric ) then
|
||||||
var = rip_metric;
|
var = rip_metric;
|
||||||
else {
|
else {
|
||||||
|
@ -1290,9 +1290,9 @@ local variables. Recursion is not allowed. Function definitions look like this:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
function name ()
|
function name ()
|
||||||
int local_variable;
|
|
||||||
{
|
{
|
||||||
local_variable = 5;
|
int local_variable;
|
||||||
|
int another_variable = 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
function with_parameters (int parameter)
|
function with_parameters (int parameter)
|
||||||
|
@ -1301,16 +1301,19 @@ function with_parameters (int parameter)
|
||||||
}
|
}
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
<p>Unlike in C, variables are declared after the <cf/function/ line, but before
|
<p>Like in C programming language, variables are declared inside function body,
|
||||||
the first <cf/{/. You can't declare variables in nested blocks. Functions are
|
either at the beginning, or mixed with other statements. Declarations may
|
||||||
called like in C: <cf>name(); with_parameters(5);</cf>. Function may return
|
contain initialization. You can also declare variables in nested blocks, such
|
||||||
values using the <cf>return <m/[expr]/</cf> command. Returning a value exits
|
variables have scope restricted to such block. There is a deprecated syntax to
|
||||||
from current function (this is similar to C).
|
declare variables after the <cf/function/ line, but before the first <cf/{/.
|
||||||
|
Functions are called like in C: <cf>name(); with_parameters(5);</cf>. Function
|
||||||
|
may return values using the <cf>return <m/[expr]/</cf> command. Returning a
|
||||||
|
value exits from current function (this is similar to C).
|
||||||
|
|
||||||
<p>Filters are defined in a way similar to functions except they can't have
|
<p>Filters are defined in a way similar to functions except they cannot have
|
||||||
explicit parameters. They get a route table entry as an implicit parameter, it
|
explicit parameters. They get a route table entry as an implicit parameter, it
|
||||||
is also passed automatically to any functions called. The filter must terminate
|
is also passed automatically to any functions called. The filter must terminate
|
||||||
with either <cf/accept/ or <cf/reject/ statement. If there's a runtime error in
|
with either <cf/accept/ or <cf/reject/ statement. If there is a runtime error in
|
||||||
filter, the route is rejected.
|
filter, the route is rejected.
|
||||||
|
|
||||||
<p>A nice trick to debug filters is to use <cf>show route filter <m/name/</cf>
|
<p>A nice trick to debug filters is to use <cf>show route filter <m/name/</cf>
|
||||||
|
|
|
@ -22,6 +22,36 @@ static inline u32 pair_b(u32 p) { return p & 0xFFFF; }
|
||||||
#define f_generate_complex(fi_code, da, arg) \
|
#define f_generate_complex(fi_code, da, arg) \
|
||||||
f_new_inst(FI_EA_SET, f_new_inst(fi_code, f_new_inst(FI_EA_GET, da), arg), da)
|
f_new_inst(FI_EA_SET, f_new_inst(fi_code, f_new_inst(FI_EA_GET, da), arg), da)
|
||||||
|
|
||||||
|
static int
|
||||||
|
f_new_var(struct sym_scope *s)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* - A variable is an offset on vstack from vbase.
|
||||||
|
* - Vbase is set on filter start / function call.
|
||||||
|
* - Scopes contain anonymous scopes (blocks) inside filter/function scope
|
||||||
|
* - Each scope knows number of vars in that scope
|
||||||
|
* - Offset is therefore a sum of 'slots' up to named scope
|
||||||
|
* - New variables are added on top of vstk, so intermediate values cannot
|
||||||
|
* be there during FI_VAR_INIT. I.e. no 'var' inside 'term'.
|
||||||
|
* - Also, each f_line must always have its scope, otherwise a variable may
|
||||||
|
* be defined but not initialized if relevant f_line is not executed.
|
||||||
|
*/
|
||||||
|
|
||||||
|
int offset = s->slots++;
|
||||||
|
|
||||||
|
while (!s->name)
|
||||||
|
{
|
||||||
|
s = s->next;
|
||||||
|
ASSERT(s);
|
||||||
|
offset += s->slots;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (offset >= 0xff)
|
||||||
|
cf_error("Too many variables, at most 255 allowed");
|
||||||
|
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Sets and their items are during parsing handled as lists, linked
|
* Sets and their items are during parsing handled as lists, linked
|
||||||
* through left ptr. The first item in a list also contains a pointer
|
* through left ptr. The first item in a list also contains a pointer
|
||||||
|
@ -296,7 +326,7 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
|
||||||
%nonassoc ELSE
|
%nonassoc ELSE
|
||||||
|
|
||||||
%type <xp> cmds_int cmd_prep
|
%type <xp> cmds_int cmd_prep
|
||||||
%type <x> term cmd cmds constant constructor print_list var_list function_call symbol_value bgp_path_expr bgp_path bgp_path_tail
|
%type <x> term cmd cmd_var cmds cmds_scoped constant constructor print_list var var_init var_list function_call symbol_value bgp_path_expr bgp_path bgp_path_tail
|
||||||
%type <fda> dynamic_attr
|
%type <fda> dynamic_attr
|
||||||
%type <fsa> static_attr
|
%type <fsa> static_attr
|
||||||
%type <f> filter where_filter
|
%type <f> filter where_filter
|
||||||
|
@ -425,7 +455,7 @@ function_args:
|
||||||
function_vars:
|
function_vars:
|
||||||
/* EMPTY */ { $$ = 0; }
|
/* EMPTY */ { $$ = 0; }
|
||||||
| function_vars type symbol ';' {
|
| function_vars type symbol ';' {
|
||||||
cf_define_symbol($3, SYM_VARIABLE | $2, offset, sym_->scope->slots++);
|
cf_define_symbol($3, SYM_VARIABLE | $2, offset, f_new_var(sym_->scope));
|
||||||
$$ = $1 + 1;
|
$$ = $1 + 1;
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
@ -492,7 +522,11 @@ cmds: /* EMPTY */ { $$ = NULL; }
|
||||||
| cmds_int { $$ = $1.begin; }
|
| cmds_int { $$ = $1.begin; }
|
||||||
;
|
;
|
||||||
|
|
||||||
cmd_prep: cmd {
|
cmds_scoped: { cf_push_soft_scope(); } cmds { cf_pop_soft_scope(); $$ = $2; } ;
|
||||||
|
|
||||||
|
cmd_var: var | cmd ;
|
||||||
|
|
||||||
|
cmd_prep: cmd_var {
|
||||||
$$.begin = $$.end = $1;
|
$$.begin = $$.end = $1;
|
||||||
if ($1)
|
if ($1)
|
||||||
while ($$.end->next)
|
while ($$.end->next)
|
||||||
|
@ -639,7 +673,7 @@ fprefix_set:
|
||||||
;
|
;
|
||||||
|
|
||||||
switch_body: /* EMPTY */ { $$ = NULL; }
|
switch_body: /* EMPTY */ { $$ = NULL; }
|
||||||
| switch_body switch_items ':' cmds {
|
| switch_body switch_items ':' cmds_scoped {
|
||||||
/* Fill data fields */
|
/* Fill data fields */
|
||||||
struct f_tree *t;
|
struct f_tree *t;
|
||||||
struct f_line *line = f_linearize($4, 0);
|
struct f_line *line = f_linearize($4, 0);
|
||||||
|
@ -647,7 +681,7 @@ switch_body: /* EMPTY */ { $$ = NULL; }
|
||||||
t->data = line;
|
t->data = line;
|
||||||
$$ = f_merge_items($1, $2);
|
$$ = f_merge_items($1, $2);
|
||||||
}
|
}
|
||||||
| switch_body ELSECOL cmds {
|
| switch_body ELSECOL cmds_scoped {
|
||||||
struct f_tree *t = f_new_tree();
|
struct f_tree *t = f_new_tree();
|
||||||
t->from.type = t->to.type = T_VOID;
|
t->from.type = t->to.type = T_VOID;
|
||||||
t->right = t;
|
t->right = t;
|
||||||
|
@ -854,8 +888,19 @@ print_list: /* EMPTY */ { $$ = NULL; }
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
|
var_init:
|
||||||
|
/* empty */ { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { }); }
|
||||||
|
| '=' term { $$ = $2; }
|
||||||
|
;
|
||||||
|
|
||||||
|
var:
|
||||||
|
type symbol var_init ';' {
|
||||||
|
struct symbol *sym = cf_define_symbol($2, SYM_VARIABLE | $1, offset, f_new_var(sym_->scope));
|
||||||
|
$$ = f_new_inst(FI_VAR_INIT, $3, sym);
|
||||||
|
}
|
||||||
|
|
||||||
cmd:
|
cmd:
|
||||||
'{' cmds '}' {
|
'{' cmds_scoped '}' {
|
||||||
$$ = $2;
|
$$ = $2;
|
||||||
}
|
}
|
||||||
| IF term THEN cmd {
|
| IF term THEN cmd {
|
||||||
|
@ -912,7 +957,7 @@ cmd:
|
||||||
| PRINTN print_list ';' {
|
| PRINTN print_list ';' {
|
||||||
$$ = f_new_inst(FI_PRINT, $2);
|
$$ = f_new_inst(FI_PRINT, $2);
|
||||||
}
|
}
|
||||||
| function_call ';' { $$ = f_new_inst(FI_DROP_RESULT, $1); }
|
| function_call ';' { $$ = f_new_inst(FI_DROP_RESULT, $1); }
|
||||||
| CASE term '{' switch_body '}' {
|
| CASE term '{' switch_body '}' {
|
||||||
$$ = f_new_inst(FI_SWITCH, $2, build_tree($4));
|
$$ = f_new_inst(FI_SWITCH, $2, build_tree($4));
|
||||||
}
|
}
|
||||||
|
|
|
@ -501,6 +501,18 @@
|
||||||
RESULT(T_BOOL, i, ipa_is_ip4(v1.val.ip));
|
RESULT(T_BOOL, i, ipa_is_ip4(v1.val.ip));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
INST(FI_VAR_INIT, 1, 0) {
|
||||||
|
NEVER_CONSTANT;
|
||||||
|
ARG_ANY(1);
|
||||||
|
SYMBOL;
|
||||||
|
ARG_TYPE(1, sym->class & 0xff);
|
||||||
|
|
||||||
|
/* New variable is always the last on stack */
|
||||||
|
uint pos = curline.vbase + sym->offset;
|
||||||
|
fstk->vstk[pos] = v1;
|
||||||
|
fstk->vcnt = pos + 1;
|
||||||
|
}
|
||||||
|
|
||||||
/* Set to indirect value prepared in v1 */
|
/* Set to indirect value prepared in v1 */
|
||||||
INST(FI_VAR_SET, 1, 0) {
|
INST(FI_VAR_SET, 1, 0) {
|
||||||
NEVER_CONSTANT;
|
NEVER_CONSTANT;
|
||||||
|
|
|
@ -44,9 +44,8 @@ bt_test_same(onef, twof, 0);
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function t_bool()
|
function t_bool()
|
||||||
bool b;
|
|
||||||
{
|
{
|
||||||
b = true;
|
bool b = true;
|
||||||
bt_assert(b);
|
bt_assert(b);
|
||||||
bt_assert(!!b);
|
bt_assert(!!b);
|
||||||
|
|
||||||
|
@ -82,12 +81,11 @@ define xyzzy = (120+10);
|
||||||
define '1a-a1' = (xyzzy-100);
|
define '1a-a1' = (xyzzy-100);
|
||||||
|
|
||||||
function t_int()
|
function t_int()
|
||||||
int i;
|
|
||||||
{
|
{
|
||||||
bt_assert(xyzzy = 130);
|
bt_assert(xyzzy = 130);
|
||||||
bt_assert('1a-a1' = 30);
|
bt_assert('1a-a1' = 30);
|
||||||
|
|
||||||
i = four;
|
int i = four;
|
||||||
i = 12*100 + 60/2 + i;
|
i = 12*100 + 60/2 + i;
|
||||||
i = (i + 0);
|
i = (i + 0);
|
||||||
bt_assert(i = 1234);
|
bt_assert(i = 1234);
|
||||||
|
@ -128,9 +126,8 @@ define is2 = [(17+2), 17, 15, 11, 8, 5, 3, 2];
|
||||||
define is3 = [5, 17, 2, 11, 8, 15, 3, 19];
|
define is3 = [5, 17, 2, 11, 8, 15, 3, 19];
|
||||||
|
|
||||||
function t_int_set()
|
function t_int_set()
|
||||||
int set is;
|
|
||||||
{
|
{
|
||||||
is = [];
|
int set is = [];
|
||||||
bt_assert(is = []);
|
bt_assert(is = []);
|
||||||
bt_assert(0 !~ is);
|
bt_assert(0 !~ is);
|
||||||
|
|
||||||
|
@ -190,9 +187,8 @@ bt_test_suite(t_int_set, "Testing sets of integers");
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function t_string()
|
function t_string()
|
||||||
string st;
|
|
||||||
{
|
{
|
||||||
st = "Hello";
|
string st = "Hello";
|
||||||
bt_assert(format(st) = "Hello");
|
bt_assert(format(st) = "Hello");
|
||||||
bt_assert(st ~ "Hell*");
|
bt_assert(st ~ "Hell*");
|
||||||
bt_assert(st ~ "?ello");
|
bt_assert(st ~ "?ello");
|
||||||
|
@ -217,9 +213,8 @@ function 'mkpair-a'(int a)
|
||||||
}
|
}
|
||||||
|
|
||||||
function t_pair()
|
function t_pair()
|
||||||
pair pp;
|
|
||||||
{
|
{
|
||||||
pp = (1, 2);
|
pair pp = (1, 2);
|
||||||
bt_assert(format(pp) = "(1,2)");
|
bt_assert(format(pp) = "(1,2)");
|
||||||
bt_assert((1,2) = pp);
|
bt_assert((1,2) = pp);
|
||||||
bt_assert((1,1+1) = pp);
|
bt_assert((1,1+1) = pp);
|
||||||
|
@ -240,11 +235,9 @@ bt_test_suite(t_pair, "Testing pairs");
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function t_pair_set()
|
function t_pair_set()
|
||||||
pair pp;
|
|
||||||
pair set ps;
|
|
||||||
{
|
{
|
||||||
pp = (1, 2);
|
pair pp = (1, 2);
|
||||||
ps = [];
|
pair set ps = [];
|
||||||
bt_assert(pp !~ ps);
|
bt_assert(pp !~ ps);
|
||||||
|
|
||||||
ps = [(1,(one+one)), (3,4)..(4,8), (5,*), (6,3..6)];
|
ps = [(1,(one+one)), (3,4)..(4,8), (5,*), (6,3..6)];
|
||||||
|
@ -1290,6 +1283,34 @@ function fifteen()
|
||||||
return 15;
|
return 15;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function local_vars(int j)
|
||||||
|
{
|
||||||
|
int k = 10;
|
||||||
|
bt_assert(j = 5 && k = 10);
|
||||||
|
{
|
||||||
|
int j = 15;
|
||||||
|
k = 20;
|
||||||
|
bt_assert(j = 15 && k = 20);
|
||||||
|
}
|
||||||
|
bt_assert(j = 5 && k = 20);
|
||||||
|
|
||||||
|
if j < 10 then
|
||||||
|
{
|
||||||
|
int j = 25;
|
||||||
|
string k = "hello";
|
||||||
|
bt_assert(j = 25 && k = "hello");
|
||||||
|
}
|
||||||
|
bt_assert(j = 5 && k = 20);
|
||||||
|
|
||||||
|
int m = 100;
|
||||||
|
{
|
||||||
|
j = 35;
|
||||||
|
int k = 40;
|
||||||
|
bt_assert(j = 35 && k = 40 && m = 100);
|
||||||
|
}
|
||||||
|
bt_assert(j = 35 && k = 20 && m = 100);
|
||||||
|
}
|
||||||
|
|
||||||
function factorial(int x)
|
function factorial(int x)
|
||||||
{
|
{
|
||||||
if x = 0 then return 0;
|
if x = 0 then return 0;
|
||||||
|
@ -1312,21 +1333,18 @@ function hanoi_init(int a; int b)
|
||||||
}
|
}
|
||||||
|
|
||||||
function hanoi_solve(int n; bgppath h_src; bgppath h_dst; bgppath h_aux; bool x; bool y)
|
function hanoi_solve(int n; bgppath h_src; bgppath h_dst; bgppath h_aux; bool x; bool y)
|
||||||
bgppath tmp1;
|
|
||||||
bgppath tmp2;
|
|
||||||
int v;
|
|
||||||
{
|
{
|
||||||
# x -> return src or dst
|
# x -> return src or dst
|
||||||
# y -> print state
|
# y -> print state
|
||||||
|
|
||||||
if n = 0 then { if x then return h_src; else return h_dst; }
|
if n = 0 then { if x then return h_src; else return h_dst; }
|
||||||
|
|
||||||
tmp1 = hanoi_solve(n - 1, h_src, h_aux, h_dst, true, y);
|
bgppath tmp1 = hanoi_solve(n - 1, h_src, h_aux, h_dst, true, y);
|
||||||
tmp2 = hanoi_solve(n - 1, h_src, h_aux, h_dst, false, false);
|
bgppath tmp2 = hanoi_solve(n - 1, h_src, h_aux, h_dst, false, false);
|
||||||
h_src = tmp1;
|
h_src = tmp1;
|
||||||
h_aux = tmp2;
|
h_aux = tmp2;
|
||||||
|
|
||||||
v = h_src.first;
|
int v = h_src.first;
|
||||||
# bt_assert(h_dst = +empty+ || v < h_dst.first);
|
# bt_assert(h_dst = +empty+ || v < h_dst.first);
|
||||||
h_src = delete(h_src, v);
|
h_src = delete(h_src, v);
|
||||||
h_dst = prepend(h_dst, v);
|
h_dst = prepend(h_dst, v);
|
||||||
|
@ -1355,6 +1373,7 @@ bgppath h_src;
|
||||||
bt_assert(callme(4, 4) = 16);
|
bt_assert(callme(4, 4) = 16);
|
||||||
bt_assert(callme(7, 2) = 14);
|
bt_assert(callme(7, 2) = 14);
|
||||||
bt_assert(callmeagain(1, 2, 3) = 6);
|
bt_assert(callmeagain(1, 2, 3) = 6);
|
||||||
|
local_vars(5);
|
||||||
|
|
||||||
bt_assert(factorial(5) = 120);
|
bt_assert(factorial(5) = 120);
|
||||||
bt_assert(factorial(10) = 3628800);
|
bt_assert(factorial(10) = 3628800);
|
||||||
|
|
Loading…
Reference in a new issue