#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)
+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 (non-frame) block scopes inside filter/function scope
+ * - Each scope knows number of vars in that scope
+ * - Offset is therefore a sum of 'slots' up to filter/function 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->block)
+ {
+ 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
* through left ptr. The first item in a list also contains a pointer
checker = f_new_inst(FI_EQ, expr, getter);
setter->next = checker;
-
+
return assert_done(setter, start, end);
}
CF_DECLS
+CF_KEYWORDS_EXCLUSIVE(IN)
CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
ACCEPT, REJECT, ERROR,
INT, BOOL, IP, TYPE, PREFIX, RD, PAIR, QUAD, EC, LC,
- SET, STRING, BGPMASK, BGPPATH, CLIST, ECLIST, LCLIST,
+ SET, STRING, BYTESTRING, BGPMASK, BGPPATH, CLIST, ECLIST, LCLIST,
IF, THEN, ELSE, CASE,
+ FOR, DO,
TRUE, FALSE, RT, RO, UNKNOWN, GENERIC,
- FROM, GW, NET, MASK, PROTO, SOURCE, SCOPE, DEST, IFNAME, IFINDEX, WEIGHT, GW_MPLS,
+ FROM, GW, NET, MASK, PROTO, SOURCE, SCOPE, DEST, IFNAME, IFINDEX, WEIGHT, GW_MPLS, ONLINK,
PREFERENCE,
ROA_CHECK, ASN, SRC, DST,
IS_V4, IS_V6,
LEN, MAXLEN,
+ DATA, DATA1, DATA2,
DEFINED,
ADD, DELETE, CONTAINS, RESET,
PREPEND, FIRST, LAST, LAST_NONAGGREGATED, MATCH,
+ MIN, MAX,
EMPTY,
FILTER, WHERE, EVAL, ATTRIBUTE,
- BT_ASSERT, BT_TEST_SUITE, BT_CHECK_ASSIGN, BT_TEST_SAME, FORMAT, STACKS)
+ FROM_HEX,
+ BT_ASSERT, BT_TEST_SUITE, BT_CHECK_ASSIGN, BT_TEST_SAME, FORMAT)
%nonassoc THEN
%nonassoc ELSE
%type <xp> cmds_int cmd_prep
-%type <x> term block cmd cmds constant constructor print_list var_list function_call symbol_value bgp_path_expr bgp_path bgp_path_tail
+%type <x> term term_bs cmd cmd_var cmds cmds_scoped constant constructor print_list var var_list function_call symbol_value bgp_path_expr bgp_path bgp_path_tail
%type <fda> dynamic_attr
%type <fsa> static_attr
%type <f> filter where_filter
%type <fl> filter_body function_body
%type <flv> lvalue
-%type <i> type function_args function_vars
+%type <i> type function_vars
+%type <fa> function_argsn function_args
%type <ecs> ec_kind
-%type <fret> break_command
+%type <fret> break_command
%type <i32> cnum
%type <e> pair_item ec_item lc_item set_item switch_item set_items switch_items switch_body
%type <trie> fprefix_set
%type <v> set_atom switch_atom fipa
%type <px> fprefix
%type <t> get_cf_position
+%type <s> for_var
CF_GRAMMAR
-conf: FILTER STACKS expr expr ';' {
- new_config->filter_vstk = $3;
- new_config->filter_estk = $4;
- }
- ;
-
conf: filter_def ;
filter_def:
FILTER symbol { $2 = cf_define_symbol($2, SYM_FILTER, filter, NULL); cf_push_scope( $2 ); }
conf: filter_eval ;
filter_eval:
- EVAL term { f_eval_int(f_linearize($2)); }
+ EVAL term { cf_eval_int($2); }
;
conf: custom_attr ;
| EC { $$ = T_EC; }
| LC { $$ = T_LC; }
| STRING { $$ = T_STRING; }
+ | BYTESTRING { $$ = T_BYTESTRING; }
| BGPMASK { $$ = T_PATH_MASK; }
| BGPPATH { $$ = T_PATH; }
| CLIST { $$ = T_CLIST; }
;
function_argsn:
- /* EMPTY */
+ /* EMPTY */ { $$ = NULL; }
| function_argsn type symbol ';' {
if ($3->scope->slots >= 0xfe) cf_error("Too many declarations, at most 255 allowed");
- cf_define_symbol($3, SYM_VARIABLE | $2, offset, $3->scope->slots++);
+ $$ = cfg_alloc(sizeof(struct f_arg));
+ $$->arg = cf_define_symbol($3, SYM_VARIABLE | $2, offset, sym_->scope->slots++);
+ $$->next = $1;
}
;
function_args:
- '(' ')' { $$ = 0; }
+ '(' ')' { $$ = NULL; }
| '(' function_argsn type symbol ')' {
- cf_define_symbol($4, SYM_VARIABLE | $3, offset, $4->scope->slots++);
- $$ = $4->scope->slots;
+ $$ = cfg_alloc(sizeof(struct f_arg));
+ $$->arg = cf_define_symbol($4, SYM_VARIABLE | $3, offset, sym_->scope->slots++);
+ $$->next = $2;
}
;
function_vars:
/* EMPTY */ { $$ = 0; }
| function_vars type symbol ';' {
- cf_define_symbol($3, SYM_VARIABLE | $2, offset, $3->scope->slots++);
+ cf_define_symbol($3, SYM_VARIABLE | $2, offset, f_new_var(sym_->scope));
$$ = $1 + 1;
}
;
cf_assert_symbol($1, SYM_FILTER);
$$ = $1->filter;
}
- | filter_body {
+ | { cf_push_scope(NULL); } filter_body {
struct filter *f = cfg_alloc(sizeof(struct filter));
- *f = (struct filter) { .root = $1 };
+ *f = (struct filter) { .root = $2 };
$$ = f;
+
+ cf_pop_scope();
}
;
function_body:
function_vars '{' cmds '}' {
- $$ = f_linearize($3);
+ $$ = f_linearize($3, 0);
$$->vars = $1;
}
;
conf: function_def ;
function_def:
- FUNCTION symbol { DBG( "Beginning of function %s\n", $2->name );
+ FUNCTION symbol {
+ DBG( "Beginning of function %s\n", $2->name );
$2 = cf_define_symbol($2, SYM_FUNCTION, function, NULL);
cf_push_scope($2);
- } function_args function_body {
- DBG("Definition of function %s with %u args and %u local vars.\n", $2->name, $4, $5->vars);
- $5->args = $4;
- $2->function = $5;
+ } function_args {
+ /* Make dummy f_line for storing function prototype */
+ struct f_line *dummy = cfg_allocz(sizeof(struct f_line));
+ $2->function = dummy;
+
+ /* Revert the args */
+ while ($4) {
+ struct f_arg *tmp = $4;
+ $4 = $4->next;
+
+ tmp->next = dummy->arg_list;
+ dummy->arg_list = tmp;
+ dummy->args++;
+ }
+ } function_body {
+ $6->args = $2->function->args;
+ $6->arg_list = $2->function->arg_list;
+ $2->function = $6;
cf_pop_scope();
}
;
| 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;
if ($1)
while ($$.end->next)
}
;
-block:
- cmd {
- $$=$1;
- }
- | '{' cmds '}' {
- $$=$2;
- }
- ;
-
/*
* Complex types, their bison value is struct f_val
*/
| VPN_RD { $$.type = T_RD; $$.val.ec = $1; }
| ENUM { $$.type = pair_a($1); $$.val.i = pair_b($1); }
| '(' term ')' {
- if (f_eval(f_linearize($2), cfg_mem, &($$)) > F_RETURN) cf_error("Runtime error");
+ $$ = cf_eval($2, T_VOID);
if (!f_valid_set_type($$.type)) cf_error("Set-incompatible type");
}
| CF_SYM_KNOWN {
switch_atom:
NUM { $$.type = T_INT; $$.val.i = $1; }
- | '(' term ')' { $$.type = T_INT; $$.val.i = f_eval_int(f_linearize($2)); }
+ | '(' term ')' { $$ = cf_eval($2, T_INT); }
| fipa { $$ = $1; }
| ENUM { $$.type = pair_a($1); $$.val.i = pair_b($1); }
;
cnum:
- term { $$ = f_eval_int(f_linearize($1)); }
+ term { $$ = cf_eval_int($1); }
pair_item:
'(' cnum ',' cnum ')' { $$ = f_new_pair_item($2, $2, $4, $4); }
;
switch_body: /* EMPTY */ { $$ = NULL; }
- | switch_body switch_items ':' cmds {
+ | switch_body switch_items ':' cmds_scoped {
/* Fill data fields */
struct f_tree *t;
- struct f_line *line = f_linearize($4);
for (t = $2; t; t = t->left)
- t->data = line;
+ t->data = $4;
$$ = f_merge_items($1, $2);
}
- | switch_body ELSECOL cmds {
+ | switch_body ELSECOL cmds_scoped {
struct f_tree *t = f_new_tree();
t->from.type = t->to.type = T_VOID;
t->right = t;
- t->data = f_linearize($3);
+ t->data = $3;
$$ = f_merge_items($1, t);
}
;
bgp_path_tail:
NUM bgp_path_tail { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_PATH_MASK_ITEM, .val.pmi = { .asn = $1, .kind = PM_ASN, }, }); $$->next = $2; }
| NUM DDOT NUM bgp_path_tail { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_PATH_MASK_ITEM, .val.pmi = { .from = $1, .to = $3, .kind = PM_ASN_RANGE }, }); $$->next = $4; }
+ | '[' ']' bgp_path_tail { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_PATH_MASK_ITEM, .val.pmi = { .set = NULL, .kind = PM_ASN_SET }, }); $$->next = $3; }
| '[' set_items ']' bgp_path_tail {
if ($2->from.type != T_INT) cf_error("Only integer sets allowed in path mask");
$$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_PATH_MASK_ITEM, .val.pmi = { .set = build_tree($2), .kind = PM_ASN_SET }, }); $$->next = $4;
;
constant:
- NUM { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_INT, .val.i = $1, }); }
- | TRUE { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_BOOL, .val.i = 1, }); }
- | FALSE { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_BOOL, .val.i = 0, }); }
- | TEXT { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_STRING, .val.s = $1, }); }
- | fipa { $$ = f_new_inst(FI_CONSTANT, $1); }
- | VPN_RD { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_RD, .val.ec = $1, }); }
- | net_ { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_NET, .val.net = $1, }); }
+ NUM { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_INT, .val.i = $1, }); }
+ | TRUE { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_BOOL, .val.i = 1, }); }
+ | FALSE { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_BOOL, .val.i = 0, }); }
+ | TEXT { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_STRING, .val.s = $1, }); }
+ | BYTETEXT { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_BYTESTRING, .val.bs = $1, }); }
+ | fipa { $$ = f_new_inst(FI_CONSTANT, $1); }
+ | VPN_RD { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_RD, .val.ec = $1, }); }
+ | net_ { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_NET, .val.net = $1, }); }
+ | '[' ']' { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_SET, .val.t = NULL, }); }
| '[' set_items ']' {
DBG( "We've got a set here..." );
$$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_SET, .val.t = build_tree($2), });
| var_list ',' term { $$ = $3; $$->next = $1; }
function_call:
- CF_SYM_KNOWN '(' var_list ')' {
+ CF_SYM_KNOWN '(' var_list ')'
+ {
if ($1->class != SYM_FUNCTION)
cf_error("You can't call something which is not a function. Really.");
- struct f_inst *fc = f_new_inst(FI_CALL, $1);
- uint args = 0;
+ /* Revert the var_list */
+ struct f_inst *args = NULL;
while ($3) {
- args++;
- struct f_inst *tmp = $3->next;
- $3->next = fc;
+ struct f_inst *tmp = $3;
+ $3 = $3->next;
- fc = $3;
- $3 = tmp;
+ tmp->next = args;
+ args = tmp;
}
- if (args != $1->function->args)
- cf_error("Function call '%s' got %u arguments, need %u arguments.",
- $1->name, args, $1->function->args);
-
- $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_VOID });
- $$->next = fc;
+ $$ = f_new_inst(FI_CALL, args, $1);
}
;
| WEIGHT { $$ = f_new_static_attr(T_INT, SA_WEIGHT, 0); }
| PREFERENCE { $$ = f_new_static_attr(T_INT, SA_PREF, 0); }
| GW_MPLS { $$ = f_new_static_attr(T_INT, SA_GW_MPLS, 0); }
+ | ONLINK { $$ = f_new_static_attr(T_BOOL, SA_ONLINK, 0); }
;
term:
| term '.' RD { $$ = f_new_inst(FI_ROUTE_DISTINGUISHER, $1); }
| term '.' LEN { $$ = f_new_inst(FI_LENGTH, $1); }
| term '.' MAXLEN { $$ = f_new_inst(FI_ROA_MAXLEN, $1); }
- | term '.' ASN { $$ = f_new_inst(FI_ROA_ASN, $1); }
+ | term '.' ASN { $$ = f_new_inst(FI_ASN, $1); }
| term '.' SRC { $$ = f_new_inst(FI_NET_SRC, $1); }
| term '.' DST { $$ = f_new_inst(FI_NET_DST, $1); }
| term '.' MASK '(' term ')' { $$ = f_new_inst(FI_IP_MASK, $1, $5); }
| term '.' FIRST { $$ = f_new_inst(FI_AS_PATH_FIRST, $1); }
| term '.' LAST { $$ = f_new_inst(FI_AS_PATH_LAST, $1); }
| term '.' LAST_NONAGGREGATED { $$ = f_new_inst(FI_AS_PATH_LAST_NAG, $1); }
+ | term '.' DATA { $$ = f_new_inst(FI_PAIR_DATA, $1); }
+ | term '.' DATA1 { $$ = f_new_inst(FI_LC_DATA1, $1); }
+ | term '.' DATA2 { $$ = f_new_inst(FI_LC_DATA2, $1); }
+ | term '.' MIN { $$ = f_new_inst(FI_MIN, $1); }
+ | term '.' MAX { $$ = f_new_inst(FI_MAX, $1); }
/* Communities */
/* This causes one shift/reduce conflict
/* | term '.' LEN { $$->code = P('P','l'); } */
+ | term_bs
| function_call
;
+term_bs:
+ FROM_HEX '(' term ')' { $$ = f_new_inst(FI_FROM_HEX, $3); }
+ ;
+
break_command:
ACCEPT { $$ = F_ACCEPT; }
| REJECT { $$ = F_REJECT; }
}
;
+var:
+ type symbol '=' term ';' {
+ struct symbol *sym = cf_define_symbol($2, SYM_VARIABLE | $1, offset, f_new_var(sym_->scope));
+ $$ = f_new_inst(FI_VAR_INIT, $4, sym);
+ }
+ | type symbol ';' {
+ struct symbol *sym = cf_define_symbol($2, SYM_VARIABLE | $1, offset, f_new_var(sym_->scope));
+ $$ = f_new_inst(FI_VAR_INIT0, sym);
+ }
+ ;
+
+for_var:
+ type symbol { $$ = cf_define_symbol($2, SYM_VARIABLE | $1, offset, f_new_var(sym_->scope)); }
+ | CF_SYM_KNOWN { $$ = $1; cf_assert_symbol($1, SYM_VARIABLE); }
+ ;
+
cmd:
- IF term THEN block {
+ '{' cmds_scoped '}' {
+ $$ = $2;
+ }
+ | IF term THEN cmd {
$$ = f_new_inst(FI_CONDITION, $2, $4, NULL);
}
- | IF term THEN block ELSE block {
+ | IF term THEN cmd ELSE cmd {
$$ = f_new_inst(FI_CONDITION, $2, $4, $6);
}
+ | FOR {
+ /* Reserve space for walk data on stack */
+ cf_push_block_scope();
+ conf_this_scope->slots += 2;
+ } for_var IN
+ /* Parse term in the parent scope */
+ { conf_this_scope->active = 0; } term { conf_this_scope->active = 1; }
+ DO cmd {
+ cf_pop_block_scope();
+ $$ = f_new_inst(FI_FOR_INIT, $6, $3);
+ $$->next = f_new_inst(FI_FOR_NEXT, $3, $9);
+ }
| CF_SYM_KNOWN '=' term ';' {
switch ($1->class) {
case SYM_VARIABLE_RANGE:
| PRINTN print_list ';' {
$$ = 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 '}' {
- $$ = f_new_inst(FI_SWITCH, $2, build_tree($4));
+ $$ = f_new_inst(FI_SWITCH, $2, $4);
}
| dynamic_attr '.' EMPTY ';' { $$ = f_generate_empty($1); }