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