]> git.ipfire.org Git - thirdparty/bird.git/blame - filter/config.Y
Filter: Fix bug in variable shadowing
[thirdparty/bird.git] / filter / config.Y
CommitLineData
b9d70dc8
PM
1/*
2 * BIRD - filters
3 *
1c20608e 4 * Copyright 1998--2000 Pavel Machek
b9d70dc8
PM
5 *
6 * Can be freely distributed and used under the terms of the GNU GPL.
c9f8c1a8 7 *
1877dab2 8 FIXME: priority of ! should be lower
b9d70dc8
PM
9 */
10
11CF_HDR
12
8bdb05ed 13#include "filter/f-inst.h"
4f082dfa 14#include "filter/data.h"
8bdb05ed 15
2edb31b0
MM
16CF_DEFINES
17
b8cc390e
OZ
18static inline u32 pair(u32 a, u32 b) { return (a << 16) | b; }
19static inline u32 pair_a(u32 p) { return p >> 16; }
20static inline u32 pair_b(u32 p) { return p & 0xFFFF; }
21
a84b8b6e
MM
22#define f_generate_complex(fi_code, da, arg) \
23 f_new_inst(FI_EA_SET, f_new_inst(fi_code, f_new_inst(FI_EA_GET, da), arg), da)
24
b8cc390e
OZ
25/*
26 * Sets and their items are during parsing handled as lists, linked
27 * through left ptr. The first item in a list also contains a pointer
28 * to the last item in a list (right ptr). For convenience, even items
29 * are handled as one-item lists. Lists are merged by f_merge_items().
30 */
b2f00837
OZ
31static int
32f_valid_set_type(int type)
33{
34 switch (type)
35 {
36 case T_INT:
37 case T_PAIR:
38 case T_QUAD:
39 case T_ENUM:
40 case T_IP:
41 case T_EC:
66dbdbd9 42 case T_LC:
83715aa8 43 case T_RD:
b2f00837
OZ
44 return 1;
45
46 default:
47 return 0;
48 }
49}
b8cc390e
OZ
50
51static inline struct f_tree *
52f_new_item(struct f_val from, struct f_val to)
92a72a4c 53{
b8cc390e
OZ
54 struct f_tree *t = f_new_tree();
55 t->right = t;
56 t->from = from;
57 t->to = to;
58 return t;
59}
92a72a4c 60
b8cc390e
OZ
61static inline struct f_tree *
62f_merge_items(struct f_tree *a, struct f_tree *b)
63{
64 if (!a) return b;
65 a->right->left = b;
66 a->right = b->right;
67 b->right = NULL;
68 return a;
69}
92a72a4c 70
b8cc390e
OZ
71static inline struct f_tree *
72f_new_pair_item(int fa, int ta, int fb, int tb)
73{
60566c5c
OZ
74 check_u16(fa);
75 check_u16(ta);
76 check_u16(fb);
77 check_u16(tb);
78
79 if ((ta < fa) || (tb < fb))
80 cf_error( "From value cannot be higher that To value in pair sets");
81
b8cc390e
OZ
82 struct f_tree *t = f_new_tree();
83 t->right = t;
84 t->from.type = t->to.type = T_PAIR;
85 t->from.val.i = pair(fa, fb);
86 t->to.val.i = pair(ta, tb);
87 return t;
92a72a4c
OZ
88}
89
b8cc390e
OZ
90static inline struct f_tree *
91f_new_pair_set(int fa, int ta, int fb, int tb)
4fc36f39 92{
60566c5c
OZ
93 check_u16(fa);
94 check_u16(ta);
95 check_u16(fb);
96 check_u16(tb);
c454872f 97
b8cc390e
OZ
98 if ((ta < fa) || (tb < fb))
99 cf_error( "From value cannot be higher that To value in pair sets");
c454872f 100
60566c5c
OZ
101 struct f_tree *lst = NULL;
102 int i;
103
b8cc390e
OZ
104 for (i = fa; i <= ta; i++)
105 lst = f_merge_items(lst, f_new_pair_item(i, i, fb, tb));
106
107 return lst;
4fc36f39
OF
108}
109
60566c5c 110#define CC_ALL 0xFFFF
42a0c054 111#define EC_ALL 0xFFFFFFFF
60566c5c 112#define LC_ALL 0xFFFFFFFF
42a0c054
OZ
113
114static struct f_tree *
115f_new_ec_item(u32 kind, u32 ipv4_used, u32 key, u32 vf, u32 vt)
116{
117 u64 fm, to;
118
e46128fb 119 if ((kind != EC_GENERIC) && (ipv4_used || (key >= 0x10000))) {
42a0c054
OZ
120 check_u16(vf);
121 if (vt == EC_ALL)
122 vt = 0xFFFF;
123 else
124 check_u16(vt);
125 }
126
127 if (kind == EC_GENERIC) {
128 fm = ec_generic(key, vf);
129 to = ec_generic(key, vt);
130 }
131 else if (ipv4_used) {
132 fm = ec_ip4(kind, key, vf);
133 to = ec_ip4(kind, key, vt);
134 }
135 else if (key < 0x10000) {
136 fm = ec_as2(kind, key, vf);
137 to = ec_as2(kind, key, vt);
138 }
139 else {
140 fm = ec_as4(kind, key, vf);
141 to = ec_as4(kind, key, vt);
142 }
143
144 struct f_tree *t = f_new_tree();
145 t->right = t;
146 t->from.type = t->to.type = T_EC;
147 t->from.val.ec = fm;
148 t->to.val.ec = to;
149 return t;
150}
151
60566c5c
OZ
152static struct f_tree *
153f_new_lc_item(u32 f1, u32 t1, u32 f2, u32 t2, u32 f3, u32 t3)
154{
155 struct f_tree *t = f_new_tree();
156 t->right = t;
157 t->from.type = t->to.type = T_LC;
158 t->from.val.lc = (lcomm) {f1, f2, f3};
159 t->to.val.lc = (lcomm) {t1, t2, t3};
160 return t;
161}
162
42a0c054 163static inline struct f_inst *
5a14df39 164f_generate_empty(struct f_dynamic_attr dyn)
5e173e9f 165{
9b46748d 166 struct f_val empty;
42a0c054 167
5a14df39 168 switch (dyn.type & EAF_TYPE_MASK) {
42a0c054 169 case EAF_TYPE_AS_PATH:
9b46748d 170 empty = f_const_empty_path;
42a0c054
OZ
171 break;
172 case EAF_TYPE_INT_SET:
9b46748d 173 empty = f_const_empty_clist;
42a0c054
OZ
174 break;
175 case EAF_TYPE_EC_SET:
9b46748d 176 empty = f_const_empty_eclist;
42a0c054 177 break;
66dbdbd9 178 case EAF_TYPE_LC_SET:
9b46748d 179 empty = f_const_empty_lclist;
66dbdbd9 180 break;
42a0c054
OZ
181 default:
182 cf_error("Can't empty that attribute");
183 }
184
a84b8b6e 185 return f_new_inst(FI_EA_SET, f_new_inst(FI_CONSTANT, empty), dyn);
42a0c054
OZ
186}
187
9b0a0ba9
OZ
188/*
189 * Remove all new lines and doubled whitespaces
190 * and convert all tabulators to spaces
191 * and return a copy of string
192 */
193char *
194assert_copy_expr(const char *start, size_t len)
195{
196 /* XXX: Allocates maybe a little more memory than we really finally need */
197 char *str = cfg_alloc(len + 1);
198
199 char *dst = str;
200 const char *src = start - 1;
201 const char *end = start + len;
202 while (++src < end)
203 {
204 if (*src == '\n')
205 continue;
206
207 /* Skip doubled whitespaces */
208 if (src != start)
209 {
210 const char *prev = src - 1;
211 if ((*src == ' ' || *src == '\t') && (*prev == ' ' || *prev == '\t'))
212 continue;
213 }
214
215 if (*src == '\t')
216 *dst = ' ';
217 else
218 *dst = *src;
219
220 dst++;
221 }
222 *dst = '\0';
223
224 return str;
225}
226
227/*
228 * assert_done - create f_instruction of bt_assert
229 * @expr: expression in bt_assert()
230 * @start: pointer to first char of test expression
231 * @end: pointer to the last char of test expression
232 */
233static struct f_inst *
234assert_done(struct f_inst *expr, const char *start, const char *end)
235{
9b46748d
MM
236 return f_new_inst(FI_ASSERT, expr,
237 (end >= start) ?
238 assert_copy_expr(start, end - start + 1)
239 : "???");
9b0a0ba9 240}
42a0c054 241
c0e958e0
MM
242static struct f_inst *
243assert_assign(struct f_lval *lval, struct f_inst *expr, const char *start, const char *end)
244{
a84b8b6e 245 struct f_inst *setter, *getter, *checker;
c0e958e0
MM
246 switch (lval->type) {
247 case F_LVAL_VARIABLE:
a84b8b6e 248 setter = f_new_inst(FI_VAR_SET, expr, lval->sym);
96d757c1 249 getter = f_new_inst(FI_VAR_GET, lval->sym);
c0e958e0
MM
250 break;
251 case F_LVAL_PREFERENCE:
a84b8b6e 252 setter = f_new_inst(FI_PREF_SET, expr);
c0e958e0
MM
253 getter = f_new_inst(FI_PREF_GET);
254 break;
255 case F_LVAL_SA:
a84b8b6e 256 setter = f_new_inst(FI_RTA_SET, expr, lval->sa);
c0e958e0
MM
257 getter = f_new_inst(FI_RTA_GET, lval->sa);
258 break;
259 case F_LVAL_EA:
a84b8b6e 260 setter = f_new_inst(FI_EA_SET, expr, lval->da);
c0e958e0
MM
261 getter = f_new_inst(FI_EA_GET, lval->da);
262 break;
a84b8b6e
MM
263 default:
264 bug("Unknown lval type");
c0e958e0
MM
265 }
266
a84b8b6e 267 checker = f_new_inst(FI_EQ, expr, getter);
4f082dfa 268 setter->next = checker;
a84b8b6e 269
c0e958e0
MM
270 return assert_done(setter, start, end);
271}
272
b9d70dc8
PM
273CF_DECLS
274
e4a73dbf 275CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
82bfee76 276 ACCEPT, REJECT, ERROR,
8c9986d3 277 INT, BOOL, IP, TYPE, PREFIX, RD, PAIR, QUAD, EC, LC,
66dbdbd9 278 SET, STRING, BGPMASK, BGPPATH, CLIST, ECLIST, LCLIST,
7db7b7db 279 IF, THEN, ELSE, CASE,
42a0c054 280 TRUE, FALSE, RT, RO, UNKNOWN, GENERIC,
e5468d16 281 FROM, GW, NET, MASK, PROTO, SOURCE, SCOPE, DEST, IFNAME, IFINDEX, WEIGHT, GW_MPLS,
a5fc5958 282 PREFERENCE,
ff2ca10c 283 ROA_CHECK, ASN, SRC, DST,
61e501da 284 IS_V4, IS_V6,
e58f8c28 285 LEN, MAXLEN,
a2a268da 286 DATA, DATA1, DATA2,
f4536657 287 DEFINED,
7f77e250 288 ADD, DELETE, CONTAINS, RESET,
9c9cc35c 289 PREPEND, FIRST, LAST, LAST_NONAGGREGATED, MATCH,
0e1fd7ea 290 MIN, MAX,
afc54517 291 EMPTY,
265419a3 292 FILTER, WHERE, EVAL, ATTRIBUTE,
132529ce 293 BT_ASSERT, BT_TEST_SUITE, BT_CHECK_ASSIGN, BT_TEST_SAME, FORMAT)
b9d70dc8 294
f4536657 295%nonassoc THEN
4ed8718a 296%nonassoc ELSE
f4536657 297
dfe63ed8 298%type <xp> cmds_int cmd_prep
0206c070 299%type <x> term block cmd cmds constant constructor print_list var_list function_call symbol_value bgp_path_expr bgp_path bgp_path_tail
5a14df39
MJM
300%type <fda> dynamic_attr
301%type <fsa> static_attr
0b39b1cb 302%type <f> filter where_filter
96d757c1 303%type <fl> filter_body function_body
c0e958e0 304%type <flv> lvalue
93d6096c
OZ
305%type <i> type function_vars
306%type <fa> function_argsn function_args
9b46748d 307%type <ecs> ec_kind
93d6096c 308%type <fret> break_command
60566c5c
OZ
309%type <i32> cnum
310%type <e> pair_item ec_item lc_item set_item switch_item set_items switch_items switch_body
b1a597e0 311%type <trie> fprefix_set
5e173e9f
JMM
312%type <v> set_atom switch_atom fipa
313%type <px> fprefix
9b0a0ba9 314%type <t> get_cf_position
b9d70dc8
PM
315
316CF_GRAMMAR
317
f851f0d7 318conf: filter_def ;
e0f2e42f 319filter_def:
2de1e206 320 FILTER symbol { $2 = cf_define_symbol($2, SYM_FILTER, filter, NULL); cf_push_scope( $2 ); }
b2b7bbfc 321 filter_body {
0b39b1cb 322 struct filter *f = cfg_alloc(sizeof(struct filter));
f249d0b8 323 *f = (struct filter) { .sym = $2, .root = $4 };
0b39b1cb
MM
324 $2->filter = f;
325
ae3e1af2 326 cf_pop_scope();
b9d70dc8
PM
327 }
328 ;
329
f851f0d7 330conf: filter_eval ;
1c20608e 331filter_eval:
23e3b1e6 332 EVAL term { f_eval_int(f_linearize($2)); }
1c20608e
MM
333 ;
334
265419a3 335conf: custom_attr ;
2de1e206 336custom_attr: ATTRIBUTE type symbol ';' {
0b39b1cb 337 cf_define_symbol($3, SYM_ATTRIBUTE, attribute, ca_lookup(new_config->pool, $3->name, $2)->fda);
265419a3
MM
338};
339
f851f0d7 340conf: bt_test_suite ;
9b0a0ba9 341bt_test_suite:
9eef9c64
MM
342 BT_TEST_SUITE '(' CF_SYM_KNOWN ',' text ')' {
343 cf_assert_symbol($3, SYM_FUNCTION);
132529ce 344 struct f_bt_test_suite *t = cfg_allocz(sizeof(struct f_bt_test_suite));
0b39b1cb 345 t->fn = $3->function;
9b0a0ba9
OZ
346 t->fn_name = $3->name;
347 t->dsc = $5;
348
349 add_tail(&new_config->tests, &t->n);
350 }
351 ;
352
132529ce
MM
353conf: bt_test_same ;
354bt_test_same:
9eef9c64
MM
355 BT_TEST_SAME '(' CF_SYM_KNOWN ',' CF_SYM_KNOWN ',' NUM ')' {
356 cf_assert_symbol($3, SYM_FUNCTION);
357 cf_assert_symbol($5, SYM_FUNCTION);
132529ce 358 struct f_bt_test_suite *t = cfg_allocz(sizeof(struct f_bt_test_suite));
0b39b1cb
MM
359 t->fn = $3->function;
360 t->cmp = $5->function;
132529ce
MM
361 t->result = $7;
362 t->fn_name = $3->name;
363 t->dsc = $5->name;
364 add_tail(&new_config->tests, &t->n);
365 }
366 ;
367
ba921648
PM
368type:
369 INT { $$ = T_INT; }
370 | BOOL { $$ = T_BOOL; }
371 | IP { $$ = T_IP; }
8c9986d3 372 | RD { $$ = T_RD; }
5e173e9f 373 | PREFIX { $$ = T_NET; }
ba921648 374 | PAIR { $$ = T_PAIR; }
126683fe 375 | QUAD { $$ = T_QUAD; }
42a0c054 376 | EC { $$ = T_EC; }
66dbdbd9 377 | LC { $$ = T_LC; }
ba921648 378 | STRING { $$ = T_STRING; }
10a53608
PM
379 | BGPMASK { $$ = T_PATH_MASK; }
380 | BGPPATH { $$ = T_PATH; }
381 | CLIST { $$ = T_CLIST; }
42a0c054 382 | ECLIST { $$ = T_ECLIST; }
66dbdbd9 383 | LCLIST { $$ = T_LCLIST; }
c8cafc8e 384 | type SET {
ba921648 385 switch ($1) {
b1a597e0 386 case T_INT:
b1a597e0 387 case T_PAIR:
126683fe 388 case T_QUAD:
42a0c054 389 case T_EC:
66dbdbd9 390 case T_LC:
83715aa8 391 case T_RD:
126683fe 392 case T_IP:
b1a597e0
OZ
393 $$ = T_SET;
394 break;
395
5e173e9f 396 case T_NET:
b1a597e0
OZ
397 $$ = T_PREFIX_SET;
398 break;
399
ba921648 400 default:
a5a947d4 401 cf_error( "You can't create sets of this type." );
ba921648 402 }
b1a597e0 403 }
ba921648
PM
404 ;
405
c29d73a0 406function_argsn:
93d6096c 407 /* EMPTY */ { $$ = NULL; }
2de1e206 408 | function_argsn type symbol ';' {
c29d73a0 409 if ($3->scope->slots >= 0xfe) cf_error("Too many declarations, at most 255 allowed");
93d6096c 410 $$ = cfg_alloc(sizeof(struct f_arg));
1e6acf34 411 $$->arg = cf_define_symbol($3, SYM_VARIABLE | $2, offset, sym_->scope->slots++);
93d6096c 412 $$->next = $1;
c29d73a0 413 }
6dc7a0cb
PM
414 ;
415
c29d73a0 416function_args:
93d6096c 417 '(' ')' { $$ = NULL; }
2de1e206 418 | '(' function_argsn type symbol ')' {
93d6096c 419 $$ = cfg_alloc(sizeof(struct f_arg));
1e6acf34 420 $$->arg = cf_define_symbol($4, SYM_VARIABLE | $3, offset, sym_->scope->slots++);
93d6096c 421 $$->next = $2;
e0f2e42f
MM
422 }
423 ;
424
c29d73a0
MM
425function_vars:
426 /* EMPTY */ { $$ = 0; }
2de1e206 427 | function_vars type symbol ';' {
1e6acf34 428 cf_define_symbol($3, SYM_VARIABLE | $2, offset, sym_->scope->slots++);
c29d73a0
MM
429 $$ = $1 + 1;
430 }
431 ;
432
63e76204 433filter_body: function_body ;
96d757c1 434
e0f2e42f 435filter:
9eef9c64
MM
436 CF_SYM_KNOWN {
437 cf_assert_symbol($1, SYM_FILTER);
0b39b1cb
MM
438 $$ = $1->filter;
439 }
440 | filter_body {
441 struct filter *f = cfg_alloc(sizeof(struct filter));
442 *f = (struct filter) { .root = $1 };
443 $$ = f;
e0f2e42f 444 }
e0f2e42f
MM
445 ;
446
430da60f
MM
447where_filter:
448 WHERE term {
224b77d4 449 /* Construct 'IF term THEN { ACCEPT; } ELSE { REJECT; }' */
9b46748d 450 $$ = f_new_where($2);
4c553c5a 451 }
430da60f
MM
452 ;
453
ba921648 454function_body:
c29d73a0 455 function_vars '{' cmds '}' {
23e3b1e6 456 $$ = f_linearize($3);
63e76204 457 $$->vars = $1;
84c7e194 458 }
ba921648
PM
459 ;
460
f851f0d7 461conf: function_def ;
ba921648 462function_def:
26bc4f99
OZ
463 FUNCTION symbol {
464 DBG( "Beginning of function %s\n", $2->name );
0b39b1cb 465 $2 = cf_define_symbol($2, SYM_FUNCTION, function, NULL);
bf3eb98e 466 cf_push_scope($2);
26bc4f99
OZ
467 } function_args {
468 /* Make dummy f_line for storing function prototype */
469 struct f_line *dummy = cfg_allocz(sizeof(struct f_line));
470 $2->function = dummy;
93d6096c
OZ
471
472 /* Revert the args */
473 while ($4) {
474 struct f_arg *tmp = $4;
475 $4 = $4->next;
476
26bc4f99
OZ
477 tmp->next = dummy->arg_list;
478 dummy->arg_list = tmp;
479 dummy->args++;
93d6096c 480 }
26bc4f99
OZ
481 } function_body {
482 $6->args = $2->function->args;
483 $6->arg_list = $2->function->arg_list;
484 $2->function = $6;
ae3e1af2 485 cf_pop_scope();
ba921648
PM
486 }
487 ;
488
489/* Programs */
490
491cmds: /* EMPTY */ { $$ = NULL; }
0206c070 492 | cmds_int { $$ = $1.begin; }
5f47c4c1
OZ
493 ;
494
dfe63ed8 495cmd_prep: cmd {
0206c070 496 $$.begin = $$.end = $1;
dfe63ed8
MM
497 if ($1)
498 while ($$.end->next)
499 $$.end = $$.end->next;
500}
501 ;
502
503cmds_int: cmd_prep
504 | cmds_int cmd_prep {
505 if (!$1.begin)
506 $$ = $2;
507 else if (!$2.begin)
508 $$ = $1;
509 else {
510 $$.begin = $1.begin;
511 $$.end = $2.end;
512 $1.end->next = $2.begin;
0206c070 513 }
0206c070 514 }
84c7e194
PM
515 ;
516
2575593e 517block:
ba921648 518 cmd {
2575593e
PM
519 $$=$1;
520 }
521 | '{' cmds '}' {
522 $$=$2;
523 }
524 ;
525
d3dd620b
PM
526/*
527 * Complex types, their bison value is struct f_val
528 */
e3f2d5fc 529fipa:
04632fd7
OZ
530 IP4 %prec PREFIX_DUMMY { $$.type = T_IP; $$.val.ip = ipa_from_ip4($1); }
531 | IP6 %prec PREFIX_DUMMY { $$.type = T_IP; $$.val.ip = ipa_from_ip6($1); }
d3dd620b
PM
532 ;
533
b8cc390e
OZ
534
535
536/*
537 * Set constants. They are also used in switch cases. We use separate
538 * nonterminals for switch (set_atom/switch_atom, set_item/switch_item ...)
539 * to elude a collision between symbol (in expr) in set_atom and symbol
540 * as a function call in switch case cmds.
541 */
542
38506f71 543set_atom:
83715aa8
OZ
544 NUM { $$.type = T_INT; $$.val.i = $1; }
545 | fipa { $$ = $1; }
546 | VPN_RD { $$.type = T_RD; $$.val.ec = $1; }
547 | ENUM { $$.type = pair_a($1); $$.val.i = pair_b($1); }
b2f00837 548 | '(' term ')' {
23e3b1e6 549 if (f_eval(f_linearize($2), cfg_mem, &($$)) > F_RETURN) cf_error("Runtime error");
b2f00837
OZ
550 if (!f_valid_set_type($$.type)) cf_error("Set-incompatible type");
551 }
9eef9c64
MM
552 | CF_SYM_KNOWN {
553 cf_assert_symbol($1, SYM_CONSTANT);
b2f00837 554 if (!f_valid_set_type(SYM_TYPE($1))) cf_error("%s: set-incompatible type", $1->name);
0b39b1cb 555 $$ = *$1->val;
b2f00837 556 }
b8cc390e 557 ;
38506f71 558
b8cc390e
OZ
559switch_atom:
560 NUM { $$.type = T_INT; $$.val.i = $1; }
23e3b1e6 561 | '(' term ')' { $$.type = T_INT; $$.val.i = f_eval_int(f_linearize($2)); }
b8cc390e
OZ
562 | fipa { $$ = $1; }
563 | ENUM { $$.type = pair_a($1); $$.val.i = pair_b($1); }
564 ;
565
60566c5c 566cnum:
23e3b1e6 567 term { $$ = f_eval_int(f_linearize($1)); }
b8cc390e
OZ
568
569pair_item:
60566c5c
OZ
570 '(' cnum ',' cnum ')' { $$ = f_new_pair_item($2, $2, $4, $4); }
571 | '(' cnum ',' cnum DDOT cnum ')' { $$ = f_new_pair_item($2, $2, $4, $6); }
572 | '(' cnum ',' '*' ')' { $$ = f_new_pair_item($2, $2, 0, CC_ALL); }
573 | '(' cnum DDOT cnum ',' cnum ')' { $$ = f_new_pair_set($2, $4, $6, $6); }
574 | '(' cnum DDOT cnum ',' cnum DDOT cnum ')' { $$ = f_new_pair_set($2, $4, $6, $8); }
575 | '(' cnum DDOT cnum ',' '*' ')' { $$ = f_new_pair_item($2, $4, 0, CC_ALL); }
576 | '(' '*' ',' cnum ')' { $$ = f_new_pair_set(0, CC_ALL, $4, $4); }
577 | '(' '*' ',' cnum DDOT cnum ')' { $$ = f_new_pair_set(0, CC_ALL, $4, $6); }
578 | '(' '*' ',' '*' ')' { $$ = f_new_pair_item(0, CC_ALL, 0, CC_ALL); }
579 | '(' cnum ',' cnum ')' DDOT '(' cnum ',' cnum ')'
580 { $$ = f_new_pair_item($2, $8, $4, $10); }
38506f71
PM
581 ;
582
42a0c054
OZ
583ec_kind:
584 RT { $$ = EC_RT; }
585 | RO { $$ = EC_RO; }
586 | UNKNOWN NUM { $$ = $2; }
587 | GENERIC { $$ = EC_GENERIC; }
588 ;
589
590ec_item:
60566c5c
OZ
591 '(' ec_kind ',' cnum ',' cnum ')' { $$ = f_new_ec_item($2, 0, $4, $6, $6); }
592 | '(' ec_kind ',' cnum ',' cnum DDOT cnum ')' { $$ = f_new_ec_item($2, 0, $4, $6, $8); }
593 | '(' ec_kind ',' cnum ',' '*' ')' { $$ = f_new_ec_item($2, 0, $4, 0, EC_ALL); }
42a0c054
OZ
594 ;
595
60566c5c
OZ
596lc_item:
597 '(' cnum ',' cnum ',' cnum ')' { $$ = f_new_lc_item($2, $2, $4, $4, $6, $6); }
598 | '(' cnum ',' cnum ',' cnum DDOT cnum ')' { $$ = f_new_lc_item($2, $2, $4, $4, $6, $8); }
599 | '(' cnum ',' cnum ',' '*' ')' { $$ = f_new_lc_item($2, $2, $4, $4, 0, LC_ALL); }
600 | '(' cnum ',' cnum DDOT cnum ',' '*' ')' { $$ = f_new_lc_item($2, $2, $4, $6, 0, LC_ALL); }
601 | '(' cnum ',' '*' ',' '*' ')' { $$ = f_new_lc_item($2, $2, 0, LC_ALL, 0, LC_ALL); }
602 | '(' cnum DDOT cnum ',' '*' ',' '*' ')' { $$ = f_new_lc_item($2, $4, 0, LC_ALL, 0, LC_ALL); }
603 | '(' '*' ',' '*' ',' '*' ')' { $$ = f_new_lc_item(0, LC_ALL, 0, LC_ALL, 0, LC_ALL); }
604 | '(' cnum ',' cnum ',' cnum ')' DDOT '(' cnum ',' cnum ',' cnum ')'
605 { $$ = f_new_lc_item($2, $10, $4, $12, $6, $14); }
606;
42a0c054 607
b8cc390e
OZ
608set_item:
609 pair_item
42a0c054 610 | ec_item
60566c5c 611 | lc_item
b8cc390e
OZ
612 | set_atom { $$ = f_new_item($1, $1); }
613 | set_atom DDOT set_atom { $$ = f_new_item($1, $3); }
614 ;
615
616switch_item:
617 pair_item
42a0c054 618 | ec_item
60566c5c 619 | lc_item
b8cc390e
OZ
620 | switch_atom { $$ = f_new_item($1, $1); }
621 | switch_atom DDOT switch_atom { $$ = f_new_item($1, $3); }
622 ;
623
38506f71 624set_items:
b8cc390e
OZ
625 set_item
626 | set_items ',' set_item { $$ = f_merge_items($1, $3); }
627 ;
628
629switch_items:
630 switch_item
631 | switch_items ',' switch_item { $$ = f_merge_items($1, $3); }
38506f71
PM
632 ;
633
b1a597e0 634fprefix:
04632fd7
OZ
635 net_ip_ { $$.net = $1; $$.lo = $1.pxlen; $$.hi = $1.pxlen; }
636 | net_ip_ '+' { $$.net = $1; $$.lo = $1.pxlen; $$.hi = net_max_prefix_length[$1.type]; }
637 | net_ip_ '-' { $$.net = $1; $$.lo = 0; $$.hi = $1.pxlen; }
638 | net_ip_ '{' NUM ',' NUM '}' {
5e173e9f 639 $$.net = $1; $$.lo = $3; $$.hi = $5;
6aaaa635
OZ
640 if (($3 > $5) || ($5 > net_max_prefix_length[$1.type]))
641 cf_error("Invalid prefix pattern range: {%u, %u}", $3, $5);
b1a597e0
OZ
642 }
643 ;
644
645fprefix_set:
27550028
OZ
646 fprefix { $$ = f_new_trie(cfg_mem, 0); trie_add_prefix($$, &($1.net), $1.lo, $1.hi); }
647 | fprefix_set ',' fprefix { $$ = $1; if (!trie_add_prefix($$, &($3.net), $3.lo, $3.hi)) cf_error("Mixed IPv4/IPv6 prefixes in prefix set"); }
b1a597e0
OZ
648 ;
649
41be4444 650switch_body: /* EMPTY */ { $$ = NULL; }
b8cc390e
OZ
651 | switch_body switch_items ':' cmds {
652 /* Fill data fields */
653 struct f_tree *t;
23e3b1e6 654 struct f_line *line = f_linearize($4);
b8cc390e 655 for (t = $2; t; t = t->left)
4c553c5a 656 t->data = line;
b8cc390e 657 $$ = f_merge_items($1, $2);
41be4444 658 }
5e173e9f 659 | switch_body ELSECOL cmds {
b8cc390e
OZ
660 struct f_tree *t = f_new_tree();
661 t->from.type = t->to.type = T_VOID;
662 t->right = t;
23e3b1e6 663 t->data = f_linearize($3);
b8cc390e
OZ
664 $$ = f_merge_items($1, t);
665 }
41be4444 666 ;
d3dd620b 667
92a72a4c 668bgp_path_expr:
c0e958e0 669 symbol_value { $$ = $1; }
92a72a4c
OZ
670 | '(' term ')' { $$ = $2; }
671 ;
672
f9491630 673bgp_path:
3e52d112 674 PO bgp_path_tail PC { $$ = $2; }
f9491630
OZ
675 ;
676
3e52d112 677bgp_path_tail:
4f082dfa
MM
678 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; }
679 | 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; }
b2d6d294 680 | '[' ']' 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; }
ef113c6f
OZ
681 | '[' set_items ']' bgp_path_tail {
682 if ($2->from.type != T_INT) cf_error("Only integer sets allowed in path mask");
683 $$ = 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;
684 }
4f082dfa
MM
685 | '*' bgp_path_tail { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_PATH_MASK_ITEM, .val.pmi = { .kind = PM_ASTERISK }, }); $$->next = $2; }
686 | '?' bgp_path_tail { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_PATH_MASK_ITEM, .val.pmi = { .kind = PM_QUESTION }, }); $$->next = $2; }
ec430a7f 687 | '+' bgp_path_tail { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_PATH_MASK_ITEM, .val.pmi = { .kind = PM_LOOP }, }); $$->next = $2; }
4f082dfa 688 | bgp_path_expr bgp_path_tail { $$ = $1; $$->next = $2; }
122deb6d 689 | { $$ = NULL; }
f9491630 690 ;
77de6882 691
23b1539b 692constant:
9b46748d
MM
693 NUM { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_INT, .val.i = $1, }); }
694 | TRUE { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_BOOL, .val.i = 1, }); }
695 | FALSE { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_BOOL, .val.i = 0, }); }
696 | TEXT { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_STRING, .val.s = $1, }); }
697 | fipa { $$ = f_new_inst(FI_CONSTANT, $1); }
698 | VPN_RD { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_RD, .val.ec = $1, }); }
699 | net_ { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_NET, .val.net = $1, }); }
b2d6d294 700 | '[' ']' { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_SET, .val.t = NULL, }); }
ca2ee91a
MM
701 | '[' set_items ']' {
702 DBG( "We've got a set here..." );
9b46748d 703 $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_SET, .val.t = build_tree($2), });
ca2ee91a
MM
704 DBG( "ook\n" );
705 }
9b46748d
MM
706 | '[' fprefix_set ']' { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_PREFIX_SET, .val.ti = $2, }); }
707 | ENUM { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = $1 >> 16, .val.i = $1 & 0xffff, }); }
23b1539b
PM
708 ;
709
42a0c054 710constructor:
9b46748d
MM
711 '(' term ',' term ')' { $$ = f_new_inst(FI_PAIR_CONSTRUCT, $2, $4); }
712 | '(' ec_kind ',' term ',' term ')' { $$ = f_new_inst(FI_EC_CONSTRUCT, $4, $6, $2); }
713 | '(' term ',' term ',' term ')' { $$ = f_new_inst(FI_LC_CONSTRUCT, $2, $4, $6); }
c0999a14 714 | bgp_path { $$ = f_new_inst(FI_PATHMASK_CONSTRUCT, $1); }
42a0c054
OZ
715 ;
716
92a72a4c 717
96d757c1
JMM
718/* This generates the function_call variable list backwards. */
719var_list: /* EMPTY */ { $$ = NULL; }
720 | term { $$ = $1; }
721 | var_list ',' term { $$ = $3; $$->next = $1; }
722
2d496d20 723function_call:
4c0c507b
OZ
724 CF_SYM_KNOWN '(' var_list ')'
725 {
96d757c1
JMM
726 if ($1->class != SYM_FUNCTION)
727 cf_error("You can't call something which is not a function. Really.");
728
4c0c507b
OZ
729 /* Revert the var_list */
730 struct f_inst *args = NULL;
96d757c1 731 while ($3) {
4c0c507b
OZ
732 struct f_inst *tmp = $3;
733 $3 = $3->next;
96d757c1 734
4c0c507b
OZ
735 tmp->next = args;
736 args = tmp;
96d757c1
JMM
737 }
738
4c0c507b 739 $$ = f_new_inst(FI_CALL, args, $1);
2d496d20
PM
740 }
741 ;
742
9eef9c64
MM
743symbol_value: CF_SYM_KNOWN
744 {
745 switch ($1->class) {
746 case SYM_CONSTANT_RANGE:
30667d50 747 $$ = f_new_inst(FI_CONSTANT, *($1->val));
96d757c1 748 break;
9eef9c64 749 case SYM_VARIABLE_RANGE:
96d757c1 750 $$ = f_new_inst(FI_VAR_GET, $1);
9eef9c64
MM
751 break;
752 case SYM_ATTRIBUTE:
753 $$ = f_new_inst(FI_EA_GET, *$1->attribute);
754 break;
755 default:
756 cf_error("Can't get value of symbol %s", $1->name);
757 }
758 }
c0e958e0 759 ;
92a72a4c 760
2bdb5e00 761static_attr:
4c553c5a
MM
762 FROM { $$ = f_new_static_attr(T_IP, SA_FROM, 0); }
763 | GW { $$ = f_new_static_attr(T_IP, SA_GW, 0); }
764 | NET { $$ = f_new_static_attr(T_NET, SA_NET, 1); }
765 | PROTO { $$ = f_new_static_attr(T_STRING, SA_PROTO, 1); }
766 | SOURCE { $$ = f_new_static_attr(T_ENUM_RTS, SA_SOURCE, 1); }
767 | SCOPE { $$ = f_new_static_attr(T_ENUM_SCOPE, SA_SCOPE, 0); }
768 | DEST { $$ = f_new_static_attr(T_ENUM_RTD, SA_DEST, 0); }
769 | IFNAME { $$ = f_new_static_attr(T_STRING, SA_IFNAME, 0); }
770 | IFINDEX { $$ = f_new_static_attr(T_INT, SA_IFINDEX, 1); }
8cc5bb09 771 | WEIGHT { $$ = f_new_static_attr(T_INT, SA_WEIGHT, 0); }
e5468d16 772 | GW_MPLS { $$ = f_new_static_attr(T_INT, SA_GW_MPLS, 0); }
2bdb5e00
PM
773 ;
774
84c7e194 775term:
5a14df39 776 '(' term ')' { $$ = $2; }
9b46748d
MM
777 | term '+' term { $$ = f_new_inst(FI_ADD, $1, $3); }
778 | term '-' term { $$ = f_new_inst(FI_SUBTRACT, $1, $3); }
779 | term '*' term { $$ = f_new_inst(FI_MULTIPLY, $1, $3); }
780 | term '/' term { $$ = f_new_inst(FI_DIVIDE, $1, $3); }
781 | term AND term { $$ = f_new_inst(FI_AND, $1, $3); }
782 | term OR term { $$ = f_new_inst(FI_OR, $1, $3); }
783 | term '=' term { $$ = f_new_inst(FI_EQ, $1, $3); }
784 | term NEQ term { $$ = f_new_inst(FI_NEQ, $1, $3); }
785 | term '<' term { $$ = f_new_inst(FI_LT, $1, $3); }
786 | term LEQ term { $$ = f_new_inst(FI_LTE, $1, $3); }
787 | term '>' term { $$ = f_new_inst(FI_LT, $3, $1); }
788 | term GEQ term { $$ = f_new_inst(FI_LTE, $3, $1); }
789 | term '~' term { $$ = f_new_inst(FI_MATCH, $1, $3); }
790 | term NMA term { $$ = f_new_inst(FI_NOT_MATCH, $1, $3); }
791 | '!' term { $$ = f_new_inst(FI_NOT, $2); }
792 | DEFINED '(' term ')' { $$ = f_new_inst(FI_DEFINED, $3); }
23b1539b 793
c0e958e0 794 | symbol_value { $$ = $1; }
1183b6b2 795 | constant { $$ = $1; }
42a0c054 796 | constructor { $$ = $1; }
4515bdba 797
5a14df39 798 | PREFERENCE { $$ = f_new_inst(FI_PREF_GET); }
2bdb5e00 799
c0e958e0 800 | static_attr { $$ = f_new_inst(FI_RTA_GET, $1); }
fe613ecd 801
c0e958e0 802 | dynamic_attr { $$ = f_new_inst(FI_EA_GET, $1); }
36bbfc70 803
9b46748d
MM
804 | term '.' IS_V4 { $$ = f_new_inst(FI_IS_V4, $1); }
805 | term '.' TYPE { $$ = f_new_inst(FI_TYPE, $1); }
806 | term '.' IP { $$ = f_new_inst(FI_IP, $1); }
807 | term '.' RD { $$ = f_new_inst(FI_ROUTE_DISTINGUISHER, $1); }
808 | term '.' LEN { $$ = f_new_inst(FI_LENGTH, $1); }
809 | term '.' MAXLEN { $$ = f_new_inst(FI_ROA_MAXLEN, $1); }
a2a268da 810 | term '.' ASN { $$ = f_new_inst(FI_ASN, $1); }
ff2ca10c
OZ
811 | term '.' SRC { $$ = f_new_inst(FI_NET_SRC, $1); }
812 | term '.' DST { $$ = f_new_inst(FI_NET_DST, $1); }
9b46748d
MM
813 | term '.' MASK '(' term ')' { $$ = f_new_inst(FI_IP_MASK, $1, $5); }
814 | term '.' FIRST { $$ = f_new_inst(FI_AS_PATH_FIRST, $1); }
815 | term '.' LAST { $$ = f_new_inst(FI_AS_PATH_LAST, $1); }
816 | term '.' LAST_NONAGGREGATED { $$ = f_new_inst(FI_AS_PATH_LAST_NAG, $1); }
a2a268da
AZ
817 | term '.' DATA { $$ = f_new_inst(FI_PAIR_DATA, $1); }
818 | term '.' DATA1 { $$ = f_new_inst(FI_LC_DATA1, $1); }
819 | term '.' DATA2 { $$ = f_new_inst(FI_LC_DATA2, $1); }
0e1fd7ea
AZ
820 | term '.' MIN { $$ = f_new_inst(FI_MIN, $1); }
821 | term '.' MAX { $$ = f_new_inst(FI_MAX, $1); }
7f77e250
PM
822
823/* Communities */
10a53608 824/* This causes one shift/reduce conflict
c0e958e0
MM
825 | dynamic_attr '.' ADD '(' term ')' { }
826 | dynamic_attr '.' DELETE '(' term ')' { }
827 | dynamic_attr '.' CONTAINS '(' term ')' { }
828 | dynamic_attr '.' RESET{ }
10a53608 829*/
7f77e250 830
9b46748d
MM
831 | '+' EMPTY '+' { $$ = f_new_inst(FI_CONSTANT, f_const_empty_path); }
832 | '-' EMPTY '-' { $$ = f_new_inst(FI_CONSTANT, f_const_empty_clist); }
833 | '-' '-' EMPTY '-' '-' { $$ = f_new_inst(FI_CONSTANT, f_const_empty_eclist); }
834 | '-' '-' '-' EMPTY '-' '-' '-' { $$ = f_new_inst(FI_CONSTANT, f_const_empty_lclist); }
835 | PREPEND '(' term ',' term ')' { $$ = f_new_inst(FI_PATH_PREPEND, $3, $5); }
836 | ADD '(' term ',' term ')' { $$ = f_new_inst(FI_CLIST_ADD, $3, $5); }
837 | DELETE '(' term ',' term ')' { $$ = f_new_inst(FI_CLIST_DEL, $3, $5); }
838 | FILTER '(' term ',' term ')' { $$ = f_new_inst(FI_CLIST_FILTER, $3, $5); }
afc54517 839
9b46748d
MM
840 | ROA_CHECK '(' rtable ')' { $$ = f_new_inst(FI_ROA_CHECK_IMPLICIT, $3); }
841 | ROA_CHECK '(' rtable ',' term ',' term ')' { $$ = f_new_inst(FI_ROA_CHECK_EXPLICIT, $5, $7, $3); }
af582c48 842
9b46748d 843 | FORMAT '(' term ')' { $$ = f_new_inst(FI_FORMAT, $3); }
9b0a0ba9 844
a2d15746 845/* | term '.' LEN { $$->code = P('P','l'); } */
7f77e250 846
4c553c5a 847 | function_call
ba921648
PM
848 ;
849
850break_command:
82bfee76 851 ACCEPT { $$ = F_ACCEPT; }
03e3d184
MM
852 | REJECT { $$ = F_REJECT; }
853 | ERROR { $$ = F_ERROR; }
ba921648
PM
854 ;
855
23b1539b 856print_list: /* EMPTY */ { $$ = NULL; }
0206c070
MM
857 | term { $$ = $1; }
858 | term ',' print_list {
859 ASSERT($1);
860 ASSERT($1->next == NULL);
861 $1->next = $3;
862 $$ = $1;
23b1539b
PM
863 }
864 ;
865
866cmd:
49955645 867 IF term THEN block {
9b46748d 868 $$ = f_new_inst(FI_CONDITION, $2, $4, NULL);
23b1539b 869 }
49955645 870 | IF term THEN block ELSE block {
9b46748d 871 $$ = f_new_inst(FI_CONDITION, $2, $4, $6);
23b1539b 872 }
9eef9c64
MM
873 | CF_SYM_KNOWN '=' term ';' {
874 switch ($1->class) {
875 case SYM_VARIABLE_RANGE:
a84b8b6e 876 $$ = f_new_inst(FI_VAR_SET, $3, $1);
9eef9c64
MM
877 break;
878 case SYM_ATTRIBUTE:
a84b8b6e 879 $$ = f_new_inst(FI_EA_SET, $3, *$1->attribute);
9eef9c64
MM
880 break;
881 default:
882 cf_error("Can't assign to symbol %s", $1->name);
883 }
b9d70dc8 884 }
2d496d20 885 | RETURN term ';' {
d4d75628 886 DBG( "Ook, we'll return the value\n" );
a84b8b6e 887 $$ = f_new_inst(FI_RETURN, $2);
2d496d20 888 }
c0e958e0 889 | dynamic_attr '=' term ';' {
a84b8b6e 890 $$ = f_new_inst(FI_EA_SET, $3, $1);
f31156ca 891 }
c0e958e0
MM
892 | static_attr '=' term ';' {
893 if ($1.readonly)
0dc4431c 894 cf_error( "This static attribute is read-only.");
a84b8b6e 895 $$ = f_new_inst(FI_RTA_SET, $3, $1);
0dc4431c
PM
896 }
897 | PREFERENCE '=' term ';' {
a84b8b6e 898 $$ = f_new_inst(FI_PREF_SET, $3);
c8cafc8e 899 }
c0e958e0
MM
900 | UNSET '(' dynamic_attr ')' ';' {
901 $$ = f_new_inst(FI_EA_UNSET, $3);
c7b43f33 902 }
0206c070 903 | break_command print_list ';' {
efd7c87b
MM
904 struct f_inst *breaker = f_new_inst(FI_DIE, $1);
905 if ($2) {
906 struct f_inst *printer = f_new_inst(FI_PRINT, $2);
907 struct f_inst *flusher = f_new_inst(FI_FLUSH);
908 printer->next = flusher;
909 flusher->next = breaker;
0206c070 910 $$ = printer;
efd7c87b 911 } else
0206c070
MM
912 $$ = breaker;
913 }
efd7c87b
MM
914 | PRINT print_list ';' {
915 $$ = f_new_inst(FI_PRINT, $2);
916 $$->next = f_new_inst(FI_FLUSH);
917 }
918 | PRINTN print_list ';' {
919 $$ = f_new_inst(FI_PRINT, $2);
920 }
9b46748d 921 | function_call ';' { $$ = f_new_inst(FI_DROP_RESULT, $1); }
7db7b7db 922 | CASE term '{' switch_body '}' {
9b46748d 923 $$ = f_new_inst(FI_SWITCH, $2, build_tree($4));
7db7b7db 924 }
7d6eebae 925
c0e958e0
MM
926 | dynamic_attr '.' EMPTY ';' { $$ = f_generate_empty($1); }
927 | dynamic_attr '.' PREPEND '(' term ')' ';' { $$ = f_generate_complex( FI_PATH_PREPEND, $1, $5 ); }
928 | dynamic_attr '.' ADD '(' term ')' ';' { $$ = f_generate_complex( FI_CLIST_ADD, $1, $5 ); }
929 | dynamic_attr '.' DELETE '(' term ')' ';' { $$ = f_generate_complex( FI_CLIST_DEL, $1, $5 ); }
930 | dynamic_attr '.' FILTER '(' term ')' ';' { $$ = f_generate_complex( FI_CLIST_FILTER, $1, $5 ); }
3ec0bedc 931 | BT_ASSERT '(' get_cf_position term get_cf_position ')' ';' { $$ = assert_done($4, $3 + 1, $5 - 1); }
c0e958e0 932 | BT_CHECK_ASSIGN '(' get_cf_position lvalue get_cf_position ',' term ')' ';' { $$ = assert_assign(&$4, $7, $3 + 1, $5 - 1); }
9b0a0ba9
OZ
933 ;
934
935get_cf_position:
936{
937 $$ = cf_text;
938};
939
c0e958e0 940lvalue:
9eef9c64 941 CF_SYM_KNOWN { cf_assert_symbol($1, SYM_VARIABLE); $$ = (struct f_lval) { .type = F_LVAL_VARIABLE, .sym = $1 }; }
c0e958e0
MM
942 | PREFERENCE { $$ = (struct f_lval) { .type = F_LVAL_PREFERENCE }; }
943 | static_attr { $$ = (struct f_lval) { .type = F_LVAL_SA, .sa = $1 }; }
944 | dynamic_attr { $$ = (struct f_lval) { .type = F_LVAL_EA, .da = $1 }; };
9b0a0ba9 945
b9d70dc8 946CF_END