]>
Commit | Line | Data |
---|---|---|
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 | ||
11 | CF_HDR | |
12 | ||
2edb31b0 MM |
13 | CF_DEFINES |
14 | ||
b8cc390e | 15 | #define P(a,b) ((a << 8) | b) |
2d496d20 | 16 | |
b8cc390e OZ |
17 | static inline u32 pair(u32 a, u32 b) { return (a << 16) | b; } |
18 | static inline u32 pair_a(u32 p) { return p >> 16; } | |
19 | static inline u32 pair_b(u32 p) { return p & 0xFFFF; } | |
20 | ||
21 | ||
22 | /* | |
23 | * Sets and their items are during parsing handled as lists, linked | |
24 | * through left ptr. The first item in a list also contains a pointer | |
25 | * to the last item in a list (right ptr). For convenience, even items | |
26 | * are handled as one-item lists. Lists are merged by f_merge_items(). | |
27 | */ | |
b2f00837 OZ |
28 | static int |
29 | f_valid_set_type(int type) | |
30 | { | |
31 | switch (type) | |
32 | { | |
33 | case T_INT: | |
34 | case T_PAIR: | |
35 | case T_QUAD: | |
36 | case T_ENUM: | |
37 | case T_IP: | |
38 | case T_EC: | |
39 | return 1; | |
40 | ||
41 | default: | |
42 | return 0; | |
43 | } | |
44 | } | |
b8cc390e OZ |
45 | |
46 | static inline struct f_tree * | |
47 | f_new_item(struct f_val from, struct f_val to) | |
92a72a4c | 48 | { |
b8cc390e OZ |
49 | struct f_tree *t = f_new_tree(); |
50 | t->right = t; | |
51 | t->from = from; | |
52 | t->to = to; | |
53 | return t; | |
54 | } | |
92a72a4c | 55 | |
b8cc390e OZ |
56 | static inline struct f_tree * |
57 | f_merge_items(struct f_tree *a, struct f_tree *b) | |
58 | { | |
59 | if (!a) return b; | |
60 | a->right->left = b; | |
61 | a->right = b->right; | |
62 | b->right = NULL; | |
63 | return a; | |
64 | } | |
92a72a4c | 65 | |
b8cc390e OZ |
66 | static inline struct f_tree * |
67 | f_new_pair_item(int fa, int ta, int fb, int tb) | |
68 | { | |
69 | struct f_tree *t = f_new_tree(); | |
70 | t->right = t; | |
71 | t->from.type = t->to.type = T_PAIR; | |
72 | t->from.val.i = pair(fa, fb); | |
73 | t->to.val.i = pair(ta, tb); | |
74 | return t; | |
92a72a4c OZ |
75 | } |
76 | ||
b8cc390e OZ |
77 | static inline struct f_tree * |
78 | f_new_pair_set(int fa, int ta, int fb, int tb) | |
4fc36f39 | 79 | { |
b8cc390e OZ |
80 | struct f_tree *lst = NULL; |
81 | int i; | |
c454872f | 82 | |
b8cc390e OZ |
83 | if ((fa == ta) || ((fb == 0) && (tb == 0xFFFF))) |
84 | return f_new_pair_item(fa, ta, fb, tb); | |
85 | ||
86 | if ((ta < fa) || (tb < fb)) | |
87 | cf_error( "From value cannot be higher that To value in pair sets"); | |
c454872f | 88 | |
b8cc390e OZ |
89 | for (i = fa; i <= ta; i++) |
90 | lst = f_merge_items(lst, f_new_pair_item(i, i, fb, tb)); | |
91 | ||
92 | return lst; | |
4fc36f39 OF |
93 | } |
94 | ||
42a0c054 OZ |
95 | #define EC_ALL 0xFFFFFFFF |
96 | ||
97 | static struct f_tree * | |
98 | f_new_ec_item(u32 kind, u32 ipv4_used, u32 key, u32 vf, u32 vt) | |
99 | { | |
100 | u64 fm, to; | |
101 | ||
102 | if (ipv4_used || (key >= 0x10000)) { | |
103 | check_u16(vf); | |
104 | if (vt == EC_ALL) | |
105 | vt = 0xFFFF; | |
106 | else | |
107 | check_u16(vt); | |
108 | } | |
109 | ||
110 | if (kind == EC_GENERIC) { | |
111 | fm = ec_generic(key, vf); | |
112 | to = ec_generic(key, vt); | |
113 | } | |
114 | else if (ipv4_used) { | |
115 | fm = ec_ip4(kind, key, vf); | |
116 | to = ec_ip4(kind, key, vt); | |
117 | } | |
118 | else if (key < 0x10000) { | |
119 | fm = ec_as2(kind, key, vf); | |
120 | to = ec_as2(kind, key, vt); | |
121 | } | |
122 | else { | |
123 | fm = ec_as4(kind, key, vf); | |
124 | to = ec_as4(kind, key, vt); | |
125 | } | |
126 | ||
127 | struct f_tree *t = f_new_tree(); | |
128 | t->right = t; | |
129 | t->from.type = t->to.type = T_EC; | |
130 | t->from.val.ec = fm; | |
131 | t->to.val.ec = to; | |
132 | return t; | |
133 | } | |
134 | ||
135 | static inline struct f_inst * | |
136 | f_generate_empty(struct f_inst *dyn) | |
5e173e9f | 137 | { |
42a0c054 OZ |
138 | struct f_inst *e = f_new_inst(); |
139 | e->code = 'E'; | |
140 | ||
141 | switch (dyn->aux & EAF_TYPE_MASK) { | |
142 | case EAF_TYPE_AS_PATH: | |
143 | e->aux = T_PATH; | |
144 | break; | |
145 | case EAF_TYPE_INT_SET: | |
146 | e->aux = T_CLIST; | |
147 | break; | |
148 | case EAF_TYPE_EC_SET: | |
149 | e->aux = T_ECLIST; | |
150 | break; | |
151 | default: | |
152 | cf_error("Can't empty that attribute"); | |
153 | } | |
154 | ||
155 | dyn->code = P('e','S'); | |
156 | dyn->a1.p = e; | |
157 | return dyn; | |
158 | } | |
159 | ||
160 | ||
161 | static inline struct f_inst * | |
162 | f_generate_dpair(struct f_inst *t1, struct f_inst *t2) | |
163 | { | |
164 | struct f_inst *rv; | |
165 | ||
166 | if ((t1->code == 'c') && (t2->code == 'c')) { | |
167 | if ((t1->aux != T_INT) || (t2->aux != T_INT)) | |
168 | cf_error( "Can't operate with value of non-integer type in pair constructor"); | |
169 | ||
170 | check_u16(t1->a2.i); | |
171 | check_u16(t2->a2.i); | |
172 | ||
173 | rv = f_new_inst(); | |
174 | rv->code = 'c'; | |
175 | rv->aux = T_PAIR; | |
176 | rv->a2.i = pair(t1->a2.i, t2->a2.i); | |
177 | } | |
178 | else { | |
179 | rv = f_new_inst(); | |
180 | rv->code = P('m', 'p'); | |
181 | rv->a1.p = t1; | |
182 | rv->a2.p = t2; | |
183 | } | |
184 | ||
185 | return rv; | |
186 | } | |
187 | ||
188 | static inline struct f_inst * | |
189 | f_generate_ec(u16 kind, struct f_inst *tk, struct f_inst *tv) | |
190 | { | |
191 | struct f_inst *rv; | |
192 | int c1 = 0, c2 = 0, ipv4_used = 0; | |
193 | u32 key = 0, val2 = 0; | |
194 | ||
195 | if (tk->code == 'c') { | |
196 | c1 = 1; | |
197 | ||
198 | if (tk->aux == T_INT) { | |
199 | ipv4_used = 0; key = tk->a2.i; | |
200 | } | |
201 | else if (tk->aux == T_QUAD) { | |
202 | ipv4_used = 1; key = tk->a2.i; | |
203 | } | |
204 | else | |
205 | cf_error("Can't operate with key of non-integer/IPv4 type in EC constructor"); | |
206 | } | |
207 | ||
42a0c054 OZ |
208 | /* IP->Quad implicit conversion */ |
209 | else if (tk->code == 'C') { | |
210 | c1 = 1; | |
211 | struct f_val *val = tk->a1.p; | |
1103b32e OZ |
212 | |
213 | if (val->type == T_INT) { | |
214 | ipv4_used = 0; key = val->val.i; | |
215 | } | |
216 | else if (val->type == T_QUAD) { | |
217 | ipv4_used = 1; key = val->val.i; | |
218 | } | |
5e173e9f JMM |
219 | else if ((val->type == T_IP) && ipa_is_ip4(val->val.ip)) { |
220 | ipv4_used = 1; key = ipa_to_u32(val->val.ip); | |
42a0c054 OZ |
221 | } |
222 | else | |
223 | cf_error("Can't operate with key of non-integer/IPv4 type in EC constructor"); | |
224 | } | |
42a0c054 OZ |
225 | |
226 | if (tv->code == 'c') { | |
227 | if (tv->aux != T_INT) | |
228 | cf_error("Can't operate with value of non-integer type in EC constructor"); | |
229 | c2 = 1; | |
230 | val2 = tv->a2.i; | |
231 | } | |
232 | ||
233 | if (c1 && c2) { | |
234 | u64 ec; | |
5e173e9f | 235 | |
42a0c054 OZ |
236 | if (kind == EC_GENERIC) { |
237 | ec = ec_generic(key, val2); | |
238 | } | |
239 | else if (ipv4_used) { | |
240 | check_u16(val2); | |
241 | ec = ec_ip4(kind, key, val2); | |
242 | } | |
243 | else if (key < 0x10000) { | |
244 | ec = ec_as2(kind, key, val2); | |
245 | } | |
246 | else { | |
247 | check_u16(val2); | |
248 | ec = ec_as4(kind, key, val2); | |
249 | } | |
250 | ||
251 | NEW_F_VAL; | |
252 | rv = f_new_inst(); | |
253 | rv->code = 'C'; | |
5e173e9f | 254 | rv->a1.p = val; |
42a0c054 OZ |
255 | val->type = T_EC; |
256 | val->val.ec = ec; | |
257 | } | |
258 | else { | |
259 | rv = f_new_inst(); | |
260 | rv->code = P('m','c'); | |
261 | rv->aux = kind; | |
262 | rv->a1.p = tk; | |
263 | rv->a2.p = tv; | |
264 | } | |
265 | ||
266 | return rv; | |
78e33c29 | 267 | } |
42a0c054 OZ |
268 | |
269 | ||
270 | ||
b9d70dc8 PM |
271 | CF_DECLS |
272 | ||
e4a73dbf | 273 | CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN, |
ba921648 | 274 | ACCEPT, REJECT, ERROR, QUITBIRD, |
42a0c054 OZ |
275 | INT, BOOL, IP, PREFIX, PAIR, QUAD, EC, |
276 | SET, STRING, BGPMASK, BGPPATH, CLIST, ECLIST, | |
7db7b7db | 277 | IF, THEN, ELSE, CASE, |
42a0c054 | 278 | TRUE, FALSE, RT, RO, UNKNOWN, GENERIC, |
a5fc5958 OZ |
279 | FROM, GW, NET, MASK, PROTO, SOURCE, SCOPE, CAST, DEST, IFNAME, IFINDEX, |
280 | PREFERENCE, | |
0264ccf6 | 281 | ROA_CHECK, |
36bbfc70 | 282 | LEN, |
f4536657 | 283 | DEFINED, |
7f77e250 | 284 | ADD, DELETE, CONTAINS, RESET, |
9c9cc35c | 285 | PREPEND, FIRST, LAST, LAST_NONAGGREGATED, MATCH, |
afc54517 | 286 | EMPTY, |
1c20608e | 287 | FILTER, WHERE, EVAL) |
b9d70dc8 | 288 | |
f4536657 | 289 | %nonassoc THEN |
4ed8718a | 290 | %nonassoc ELSE |
f4536657 | 291 | |
42a0c054 | 292 | %type <x> term block cmds cmds_int cmd function_body constant constructor print_one print_list var_list var_listn dynamic_attr static_attr function_call symbol bgp_path_expr |
430da60f | 293 | %type <f> filter filter_body where_filter |
42a0c054 OZ |
294 | %type <i> type break_command pair_expr ec_kind |
295 | %type <i32> pair_atom ec_expr | |
296 | %type <e> pair_item ec_item set_item switch_item set_items switch_items switch_body | |
b1a597e0 | 297 | %type <trie> fprefix_set |
5e173e9f JMM |
298 | %type <v> set_atom switch_atom fipa |
299 | %type <px> fprefix | |
300 | %type <s> decls declsn one_decl function_params | |
f9491630 | 301 | %type <h> bgp_path bgp_path_tail1 bgp_path_tail2 |
b9d70dc8 PM |
302 | |
303 | CF_GRAMMAR | |
304 | ||
e0f2e42f MM |
305 | CF_ADDTO(conf, filter_def) |
306 | filter_def: | |
b2b7bbfc OZ |
307 | FILTER SYM { $2 = cf_define_symbol($2, SYM_FILTER, NULL); cf_push_scope( $2 ); } |
308 | filter_body { | |
309 | $2->def = $4; | |
ae3e1af2 | 310 | $4->name = $2->name; |
d4d75628 | 311 | DBG( "We have new filter defined (%s)\n", $2->name ); |
ae3e1af2 | 312 | cf_pop_scope(); |
b9d70dc8 PM |
313 | } |
314 | ; | |
315 | ||
1c20608e MM |
316 | CF_ADDTO(conf, filter_eval) |
317 | filter_eval: | |
318 | EVAL term { f_eval_int($2); } | |
319 | ; | |
320 | ||
ba921648 PM |
321 | type: |
322 | INT { $$ = T_INT; } | |
323 | | BOOL { $$ = T_BOOL; } | |
324 | | IP { $$ = T_IP; } | |
5e173e9f | 325 | | PREFIX { $$ = T_NET; } |
ba921648 | 326 | | PAIR { $$ = T_PAIR; } |
126683fe | 327 | | QUAD { $$ = T_QUAD; } |
42a0c054 | 328 | | EC { $$ = T_EC; } |
ba921648 | 329 | | STRING { $$ = T_STRING; } |
10a53608 PM |
330 | | BGPMASK { $$ = T_PATH_MASK; } |
331 | | BGPPATH { $$ = T_PATH; } | |
332 | | CLIST { $$ = T_CLIST; } | |
42a0c054 | 333 | | ECLIST { $$ = T_ECLIST; } |
ba921648 PM |
334 | | type SET { |
335 | switch ($1) { | |
b1a597e0 | 336 | case T_INT: |
b1a597e0 | 337 | case T_PAIR: |
126683fe | 338 | case T_QUAD: |
42a0c054 | 339 | case T_EC: |
126683fe | 340 | case T_IP: |
b1a597e0 OZ |
341 | $$ = T_SET; |
342 | break; | |
343 | ||
5e173e9f | 344 | case T_NET: |
b1a597e0 OZ |
345 | $$ = T_PREFIX_SET; |
346 | break; | |
347 | ||
ba921648 | 348 | default: |
a5a947d4 | 349 | cf_error( "You can't create sets of this type." ); |
ba921648 | 350 | } |
b1a597e0 | 351 | } |
ba921648 PM |
352 | ; |
353 | ||
6dc7a0cb PM |
354 | one_decl: |
355 | type SYM { | |
4ee39ff2 OZ |
356 | struct f_val * val = cfg_alloc(sizeof(struct f_val)); |
357 | val->type = T_VOID; | |
083c43e2 | 358 | $2 = cf_define_symbol($2, SYM_VARIABLE | $1, val); |
d4d75628 | 359 | DBG( "New variable %s type %x\n", $2->name, $1 ); |
083c43e2 | 360 | $2->aux2 = NULL; |
6542ece9 | 361 | $$=$2; |
ba921648 PM |
362 | } |
363 | ; | |
364 | ||
6dc7a0cb PM |
365 | /* Decls with ';' at the end */ |
366 | decls: /* EMPTY */ { $$ = NULL; } | |
367 | | one_decl ';' decls { | |
368 | $$ = $1; | |
083c43e2 | 369 | $$->aux2 = $3; |
6dc7a0cb PM |
370 | } |
371 | ; | |
372 | ||
3c989eb4 | 373 | /* Declarations that have no ';' at the end. */ |
6dc7a0cb | 374 | declsn: one_decl { $$ = $1; } |
736fd730 | 375 | | one_decl ';' declsn { |
4515bdba | 376 | $$ = $1; |
083c43e2 | 377 | $$->aux2 = $3; |
6dc7a0cb PM |
378 | } |
379 | ; | |
380 | ||
e0f2e42f | 381 | filter_body: |
ba921648 | 382 | function_body { |
e0f2e42f MM |
383 | struct filter *f = cfg_alloc(sizeof(struct filter)); |
384 | f->name = NULL; | |
ba921648 | 385 | f->root = $1; |
e0f2e42f MM |
386 | $$ = f; |
387 | } | |
388 | ; | |
389 | ||
390 | filter: | |
391 | SYM { | |
a5a947d4 | 392 | if ($1->class != SYM_FILTER) cf_error("No such filter."); |
e0f2e42f MM |
393 | $$ = $1->def; |
394 | } | |
395 | | filter_body | |
396 | ; | |
397 | ||
430da60f MM |
398 | where_filter: |
399 | WHERE term { | |
400 | /* Construct 'IF term THEN ACCEPT; REJECT;' */ | |
401 | struct filter *f = cfg_alloc(sizeof(struct filter)); | |
402 | struct f_inst *i, *acc, *rej; | |
403 | acc = f_new_inst(); /* ACCEPT */ | |
2d496d20 | 404 | acc->code = P('p',','); |
430da60f MM |
405 | acc->a1.p = NULL; |
406 | acc->a2.i = F_ACCEPT; | |
407 | rej = f_new_inst(); /* REJECT */ | |
2d496d20 | 408 | rej->code = P('p',','); |
430da60f MM |
409 | rej->a1.p = NULL; |
410 | rej->a2.i = F_REJECT; | |
411 | i = f_new_inst(); /* IF */ | |
412 | i->code = '?'; | |
413 | i->a1.p = $2; | |
414 | i->a2.p = acc; | |
415 | i->next = rej; | |
416 | f->name = NULL; | |
417 | f->root = i; | |
418 | $$ = f; | |
419 | } | |
420 | ; | |
421 | ||
ba921648 | 422 | function_params: |
d4d75628 | 423 | '(' declsn ')' { DBG( "Have function parameters\n" ); $$=$2; } |
6dc7a0cb | 424 | | '(' ')' { $$=NULL; } |
ba921648 | 425 | ; |
b9d70dc8 | 426 | |
ba921648 PM |
427 | function_body: |
428 | decls '{' cmds '}' { | |
aa461248 OZ |
429 | if ($1) { |
430 | /* Prepend instruction to clear local variables */ | |
431 | $$ = f_new_inst(); | |
432 | $$->code = P('c','v'); | |
433 | $$->a1.p = $1; | |
434 | $$->next = $3; | |
435 | } else | |
436 | $$ = $3; | |
84c7e194 | 437 | } |
ba921648 PM |
438 | ; |
439 | ||
440 | CF_ADDTO(conf, function_def) | |
441 | function_def: | |
bf3eb98e MM |
442 | FUNCTION SYM { DBG( "Beginning of function %s\n", $2->name ); |
443 | $2 = cf_define_symbol($2, SYM_FUNCTION, NULL); | |
444 | cf_push_scope($2); | |
445 | } function_params function_body { | |
446 | $2->def = $5; | |
083c43e2 | 447 | $2->aux2 = $4; |
d4d75628 | 448 | DBG("Hmm, we've got one function here - %s\n", $2->name); |
ae3e1af2 | 449 | cf_pop_scope(); |
ba921648 PM |
450 | } |
451 | ; | |
452 | ||
453 | /* Programs */ | |
454 | ||
5f47c4c1 OZ |
455 | /* Hack: $$ of cmds_int is the last node. |
456 | $$->next of cmds_int is temporary used for the first node */ | |
457 | ||
ba921648 | 458 | cmds: /* EMPTY */ { $$ = NULL; } |
5f47c4c1 OZ |
459 | | cmds_int { $$ = $1->next; $1->next = NULL; } |
460 | ; | |
461 | ||
462 | cmds_int: cmd { $$ = $1; $1->next = $1; } | |
463 | | cmds_int cmd { $$ = $2; $2->next = $1->next ; $1->next = $2; } | |
84c7e194 PM |
464 | ; |
465 | ||
2575593e | 466 | block: |
ba921648 | 467 | cmd { |
2575593e PM |
468 | $$=$1; |
469 | } | |
470 | | '{' cmds '}' { | |
471 | $$=$2; | |
472 | } | |
473 | ; | |
474 | ||
d3dd620b PM |
475 | /* |
476 | * Complex types, their bison value is struct f_val | |
477 | */ | |
e3f2d5fc | 478 | fipa: |
04632fd7 OZ |
479 | IP4 %prec PREFIX_DUMMY { $$.type = T_IP; $$.val.ip = ipa_from_ip4($1); } |
480 | | IP6 %prec PREFIX_DUMMY { $$.type = T_IP; $$.val.ip = ipa_from_ip6($1); } | |
d3dd620b PM |
481 | ; |
482 | ||
b8cc390e OZ |
483 | |
484 | ||
485 | /* | |
486 | * Set constants. They are also used in switch cases. We use separate | |
487 | * nonterminals for switch (set_atom/switch_atom, set_item/switch_item ...) | |
488 | * to elude a collision between symbol (in expr) in set_atom and symbol | |
489 | * as a function call in switch case cmds. | |
490 | */ | |
491 | ||
38506f71 | 492 | set_atom: |
b2f00837 | 493 | NUM { $$.type = T_INT; $$.val.i = $1; } |
92a72a4c | 494 | | fipa { $$ = $1; } |
b8cc390e | 495 | | ENUM { $$.type = pair_a($1); $$.val.i = pair_b($1); } |
b2f00837 OZ |
496 | | '(' term ')' { |
497 | $$ = f_eval($2, cfg_mem); | |
498 | if (!f_valid_set_type($$.type)) cf_error("Set-incompatible type"); | |
499 | } | |
500 | | SYM { | |
501 | if (!cf_symbol_is_constant($1)) cf_error("%s: constant expected", $1->name); | |
502 | if (!f_valid_set_type(SYM_TYPE($1))) cf_error("%s: set-incompatible type", $1->name); | |
503 | $$ = *(struct f_val *)($1->def); | |
504 | } | |
b8cc390e | 505 | ; |
38506f71 | 506 | |
b8cc390e OZ |
507 | switch_atom: |
508 | NUM { $$.type = T_INT; $$.val.i = $1; } | |
509 | | '(' term ')' { $$.type = T_INT; $$.val.i = f_eval_int($2); } | |
b8cc390e OZ |
510 | | fipa { $$ = $1; } |
511 | | ENUM { $$.type = pair_a($1); $$.val.i = pair_b($1); } | |
512 | ; | |
513 | ||
514 | pair_expr: | |
515 | term { $$ = f_eval_int($1); check_u16($$); } | |
516 | ||
517 | pair_atom: | |
518 | pair_expr { $$ = pair($1, $1); } | |
519 | | pair_expr DDOT pair_expr { $$ = pair($1, $3); } | |
520 | | '*' { $$ = 0xFFFF; } | |
521 | ; | |
522 | ||
523 | pair_item: | |
524 | '(' pair_atom ',' pair_atom ')' { | |
525 | $$ = f_new_pair_set(pair_a($2), pair_b($2), pair_a($4), pair_b($4)); | |
995e5894 | 526 | } |
b8cc390e OZ |
527 | | '(' pair_atom ',' pair_atom ')' DDOT '(' pair_expr ',' pair_expr ')' { |
528 | /* Hack: $2 and $4 should be pair_expr, but that would cause shift/reduce conflict */ | |
529 | if ((pair_a($2) != pair_b($2)) || (pair_a($4) != pair_b($4))) | |
530 | cf_error("syntax error"); | |
117e3c4b | 531 | $$ = f_new_pair_item(pair_b($2), $8, pair_b($4), $10); |
995e5894 | 532 | } |
38506f71 PM |
533 | ; |
534 | ||
42a0c054 OZ |
535 | ec_expr: |
536 | term { $$ = f_eval_int($1); } | |
537 | ||
538 | ec_kind: | |
539 | RT { $$ = EC_RT; } | |
540 | | RO { $$ = EC_RO; } | |
541 | | UNKNOWN NUM { $$ = $2; } | |
542 | | GENERIC { $$ = EC_GENERIC; } | |
543 | ; | |
544 | ||
545 | ec_item: | |
546 | '(' ec_kind ',' ec_expr ',' ec_expr ')' { $$ = f_new_ec_item($2, 0, $4, $6, $6); } | |
547 | | '(' ec_kind ',' ec_expr ',' ec_expr DDOT ec_expr ')' { $$ = f_new_ec_item($2, 0, $4, $6, $8); } | |
548 | | '(' ec_kind ',' ec_expr ',' '*' ')' { $$ = f_new_ec_item($2, 0, $4, 0, EC_ALL); } | |
549 | ; | |
550 | ||
b8cc390e OZ |
551 | set_item: |
552 | pair_item | |
42a0c054 | 553 | | ec_item |
b8cc390e OZ |
554 | | set_atom { $$ = f_new_item($1, $1); } |
555 | | set_atom DDOT set_atom { $$ = f_new_item($1, $3); } | |
556 | ; | |
557 | ||
558 | switch_item: | |
559 | pair_item | |
42a0c054 | 560 | | ec_item |
b8cc390e OZ |
561 | | switch_atom { $$ = f_new_item($1, $1); } |
562 | | switch_atom DDOT switch_atom { $$ = f_new_item($1, $3); } | |
563 | ; | |
564 | ||
38506f71 | 565 | set_items: |
b8cc390e OZ |
566 | set_item |
567 | | set_items ',' set_item { $$ = f_merge_items($1, $3); } | |
568 | ; | |
569 | ||
570 | switch_items: | |
571 | switch_item | |
572 | | switch_items ',' switch_item { $$ = f_merge_items($1, $3); } | |
38506f71 PM |
573 | ; |
574 | ||
b1a597e0 | 575 | fprefix: |
04632fd7 OZ |
576 | net_ip_ { $$.net = $1; $$.lo = $1.pxlen; $$.hi = $1.pxlen; } |
577 | | net_ip_ '+' { $$.net = $1; $$.lo = $1.pxlen; $$.hi = net_max_prefix_length[$1.type]; } | |
578 | | net_ip_ '-' { $$.net = $1; $$.lo = 0; $$.hi = $1.pxlen; } | |
579 | | net_ip_ '{' NUM ',' NUM '}' { | |
5e173e9f | 580 | $$.net = $1; $$.lo = $3; $$.hi = $5; |
04632fd7 | 581 | if ((0 > $3) || ($3 > $5) || ($5 > net_max_prefix_length[$1.type])) |
5e173e9f | 582 | cf_error("Invalid prefix pattern range: {%d, %d}", $3, $5); |
b1a597e0 OZ |
583 | } |
584 | ; | |
585 | ||
586 | fprefix_set: | |
04632fd7 OZ |
587 | fprefix { $$ = f_new_trie(cfg_mem, sizeof(struct f_trie_node)); trie_add_prefix($$, &($1.net), $1.lo, $1.hi); } |
588 | | fprefix_set ',' fprefix { $$ = $1; trie_add_prefix($$, &($3.net), $3.lo, $3.hi); } | |
b1a597e0 OZ |
589 | ; |
590 | ||
41be4444 | 591 | switch_body: /* EMPTY */ { $$ = NULL; } |
b8cc390e OZ |
592 | | switch_body switch_items ':' cmds { |
593 | /* Fill data fields */ | |
594 | struct f_tree *t; | |
595 | for (t = $2; t; t = t->left) | |
596 | t->data = $4; | |
597 | $$ = f_merge_items($1, $2); | |
41be4444 | 598 | } |
5e173e9f | 599 | | switch_body ELSECOL cmds { |
b8cc390e OZ |
600 | struct f_tree *t = f_new_tree(); |
601 | t->from.type = t->to.type = T_VOID; | |
602 | t->right = t; | |
603 | t->data = $3; | |
604 | $$ = f_merge_items($1, t); | |
605 | } | |
41be4444 | 606 | ; |
d3dd620b | 607 | |
e4a73dbf PM |
608 | /* CONST '(' expr ')' { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_INT; $$->a2.i = $3; } */ |
609 | ||
92a72a4c OZ |
610 | bgp_path_expr: |
611 | symbol { $$ = $1; } | |
612 | | '(' term ')' { $$ = $2; } | |
613 | ; | |
614 | ||
f9491630 | 615 | bgp_path: |
cf186034 | 616 | PO bgp_path_tail1 PC { $$ = $2; } |
f9491630 | 617 | | '/' bgp_path_tail2 '/' { $$ = $2; } |
f9491630 OZ |
618 | ; |
619 | ||
620 | bgp_path_tail1: | |
c8a6b9a3 OZ |
621 | NUM bgp_path_tail1 { $$ = cfg_alloc(sizeof(struct f_path_mask)); $$->next = $2; $$->kind = PM_ASN; $$->val = $1; } |
622 | | '*' bgp_path_tail1 { $$ = cfg_alloc(sizeof(struct f_path_mask)); $$->next = $2; $$->kind = PM_ASTERISK; $$->val = 0; } | |
623 | | '?' bgp_path_tail1 { $$ = cfg_alloc(sizeof(struct f_path_mask)); $$->next = $2; $$->kind = PM_QUESTION; $$->val = 0; } | |
92a72a4c | 624 | | bgp_path_expr bgp_path_tail1 { $$ = cfg_alloc(sizeof(struct f_path_mask)); $$->next = $2; $$->kind = PM_ASN_EXPR; $$->val = (uintptr_t) $1; } |
f9491630 OZ |
625 | | { $$ = NULL; } |
626 | ; | |
77de6882 | 627 | |
f9491630 | 628 | bgp_path_tail2: |
c8a6b9a3 OZ |
629 | NUM bgp_path_tail2 { $$ = cfg_alloc(sizeof(struct f_path_mask)); $$->next = $2; $$->kind = PM_ASN; $$->val = $1; } |
630 | | '?' bgp_path_tail2 { $$ = cfg_alloc(sizeof(struct f_path_mask)); $$->next = $2; $$->kind = PM_ASTERISK; $$->val = 0; } | |
f9491630 | 631 | | { $$ = NULL; } |
77de6882 PM |
632 | ; |
633 | ||
23b1539b | 634 | constant: |
e4a73dbf | 635 | NUM { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_INT; $$->a2.i = $1; } |
c7b43f33 PM |
636 | | TRUE { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_BOOL; $$->a2.i = 1; } |
637 | | FALSE { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_BOOL; $$->a2.i = 0; } | |
638 | | TEXT { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_STRING; $$->a2.p = $1; } | |
5e173e9f | 639 | | fipa { NEW_F_VAL; $$ = f_new_inst(); $$->code = 'C'; $$->a1.p = val; *val = $1; } |
04632fd7 | 640 | | net_ { NEW_F_VAL; $$ = f_new_inst(); $$->code = 'C'; val->type = T_NET; val->val.net = $1; $$->a1.p = val; } |
d4d75628 | 641 | | '[' set_items ']' { DBG( "We've got a set here..." ); $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_SET; $$->a2.p = build_tree($2); DBG( "ook\n" ); } |
b1a597e0 | 642 | | '[' fprefix_set ']' { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_PREFIX_SET; $$->a2.p = $2; } |
c7b43f33 | 643 | | ENUM { $$ = f_new_inst(); $$->code = 'c'; $$->aux = $1 >> 16; $$->a2.i = $1 & 0xffff; } |
f9491630 | 644 | | bgp_path { NEW_F_VAL; $$ = f_new_inst(); $$->code = 'C'; val->type = T_PATH_MASK; val->val.path_mask = $1; $$->a1.p = val; } |
23b1539b PM |
645 | ; |
646 | ||
42a0c054 | 647 | constructor: |
78e33c29 OZ |
648 | '(' term ',' term ')' { $$ = f_generate_dpair($2, $4); } |
649 | | '(' ec_kind ',' term ',' term ')' { $$ = f_generate_ec($2, $4, $6); } | |
42a0c054 OZ |
650 | ; |
651 | ||
92a72a4c | 652 | |
db1326aa MM |
653 | /* |
654 | * Maybe there are no dynamic attributes defined by protocols. | |
655 | * For such cases, we force the dynamic_attr list to contain | |
b8cc390e | 656 | * at least an invalid token, so it is syntantically correct. |
db1326aa MM |
657 | */ |
658 | CF_ADDTO(dynamic_attr, INVALID_TOKEN { $$ = NULL; }) | |
f4536657 | 659 | |
2e18b87d | 660 | rtadot: /* EMPTY, we are not permitted RTA. prefix */ |
6c14255d | 661 | ; |
f4536657 | 662 | |
2d496d20 PM |
663 | function_call: |
664 | SYM '(' var_list ')' { | |
665 | struct symbol *sym; | |
666 | struct f_inst *inst = $3; | |
667 | if ($1->class != SYM_FUNCTION) | |
a5a947d4 | 668 | cf_error("You can't call something which is not a function. Really."); |
d4d75628 | 669 | DBG("You are calling function %s\n", $1->name); |
2d496d20 PM |
670 | $$ = f_new_inst(); |
671 | $$->code = P('c','a'); | |
672 | $$->a1.p = inst; | |
083c43e2 OZ |
673 | $$->a2.p = $1->def; |
674 | sym = $1->aux2; | |
2d496d20 PM |
675 | while (sym || inst) { |
676 | if (!sym || !inst) | |
a5a947d4 | 677 | cf_error("Wrong number of arguments for function %s.", $1->name); |
d4d75628 | 678 | DBG( "You should pass parameter called %s\n", sym->name); |
2d496d20 | 679 | inst->a1.p = sym; |
083c43e2 | 680 | sym = sym->aux2; |
2d496d20 PM |
681 | inst = inst->next; |
682 | } | |
683 | } | |
684 | ; | |
685 | ||
92a72a4c OZ |
686 | symbol: |
687 | SYM { | |
688 | $$ = f_new_inst(); | |
1103b32e OZ |
689 | |
690 | switch ($1->class & 0xff00) { | |
691 | case SYM_CONSTANT: $$->code = 'C'; break; | |
692 | case SYM_VARIABLE: $$->code = 'V'; break; | |
693 | default: cf_error("%s: variable expected.", $1->name); | |
92a72a4c | 694 | } |
1103b32e OZ |
695 | |
696 | $$->a1.p = $1->def; | |
697 | $$->a2.p = $1->name; | |
92a72a4c OZ |
698 | } |
699 | ||
2bdb5e00 | 700 | static_attr: |
a5fc5958 OZ |
701 | FROM { $$ = f_new_inst(); $$->aux = T_IP; $$->a2.i = SA_FROM; $$->a1.i = 1; } |
702 | | GW { $$ = f_new_inst(); $$->aux = T_IP; $$->a2.i = SA_GW; $$->a1.i = 1; } | |
5e173e9f | 703 | | NET { $$ = f_new_inst(); $$->aux = T_NET; $$->a2.i = SA_NET; } |
a5fc5958 OZ |
704 | | PROTO { $$ = f_new_inst(); $$->aux = T_STRING; $$->a2.i = SA_PROTO; } |
705 | | SOURCE { $$ = f_new_inst(); $$->aux = T_ENUM_RTS; $$->a2.i = SA_SOURCE; } | |
706 | | SCOPE { $$ = f_new_inst(); $$->aux = T_ENUM_SCOPE; $$->a2.i = SA_SCOPE; $$->a1.i = 1; } | |
707 | | CAST { $$ = f_new_inst(); $$->aux = T_ENUM_RTC; $$->a2.i = SA_CAST; } | |
708 | | DEST { $$ = f_new_inst(); $$->aux = T_ENUM_RTD; $$->a2.i = SA_DEST; $$->a1.i = 1; } | |
709 | | IFNAME { $$ = f_new_inst(); $$->aux = T_STRING; $$->a2.i = SA_IFNAME; } | |
710 | | IFINDEX { $$ = f_new_inst(); $$->aux = T_INT; $$->a2.i = SA_IFINDEX; } | |
2bdb5e00 PM |
711 | ; |
712 | ||
84c7e194 | 713 | term: |
f4536657 | 714 | '(' term ')' { $$ = $2; } |
d4d75628 | 715 | | term '+' term { $$ = f_new_inst(); $$->code = '+'; $$->a1.p = $1; $$->a2.p = $3; } |
c5a06f65 PM |
716 | | term '-' term { $$ = f_new_inst(); $$->code = '-'; $$->a1.p = $1; $$->a2.p = $3; } |
717 | | term '*' term { $$ = f_new_inst(); $$->code = '*'; $$->a1.p = $1; $$->a2.p = $3; } | |
718 | | term '/' term { $$ = f_new_inst(); $$->code = '/'; $$->a1.p = $1; $$->a2.p = $3; } | |
5f4aee76 PM |
719 | | term AND term { $$ = f_new_inst(); $$->code = '&'; $$->a1.p = $1; $$->a2.p = $3; } |
720 | | term OR term { $$ = f_new_inst(); $$->code = '|'; $$->a1.p = $1; $$->a2.p = $3; } | |
2d496d20 | 721 | | term '=' term { $$ = f_new_inst(); $$->code = P('=','='); $$->a1.p = $1; $$->a2.p = $3; } |
d4d75628 PM |
722 | | term NEQ term { $$ = f_new_inst(); $$->code = P('!','='); $$->a1.p = $1; $$->a2.p = $3; } |
723 | | term '<' term { $$ = f_new_inst(); $$->code = '<'; $$->a1.p = $1; $$->a2.p = $3; } | |
724 | | term LEQ term { $$ = f_new_inst(); $$->code = P('<','='); $$->a1.p = $1; $$->a2.p = $3; } | |
725 | | term '>' term { $$ = f_new_inst(); $$->code = '<'; $$->a1.p = $3; $$->a2.p = $1; } | |
726 | | term GEQ term { $$ = f_new_inst(); $$->code = P('<','='); $$->a1.p = $3; $$->a2.p = $1; } | |
727 | | term '~' term { $$ = f_new_inst(); $$->code = '~'; $$->a1.p = $1; $$->a2.p = $3; } | |
995e5894 | 728 | | '!' term { $$ = f_new_inst(); $$->code = '!'; $$->a1.p = $2; } |
2d496d20 | 729 | | DEFINED '(' term ')' { $$ = f_new_inst(); $$->code = P('d','e'); $$->a1.p = $3; } |
23b1539b | 730 | |
92a72a4c | 731 | | symbol { $$ = $1; } |
1183b6b2 | 732 | | constant { $$ = $1; } |
42a0c054 | 733 | | constructor { $$ = $1; } |
4515bdba | 734 | |
0dc4431c | 735 | | PREFERENCE { $$ = f_new_inst(); $$->code = 'P'; } |
2bdb5e00 PM |
736 | |
737 | | rtadot static_attr { $$ = $2; $$->code = 'a'; } | |
fe613ecd | 738 | |
db1326aa | 739 | | rtadot dynamic_attr { $$ = $2; $$->code = P('e','a'); } |
36bbfc70 | 740 | |
2d496d20 | 741 | | term '.' IP { $$ = f_new_inst(); $$->code = P('c','p'); $$->a1.p = $1; $$->aux = T_IP; } |
684c6f5a | 742 | | term '.' LEN { $$ = f_new_inst(); $$->code = 'L'; $$->a1.p = $1; } |
2d496d20 | 743 | | term '.' MASK '(' term ')' { $$ = f_new_inst(); $$->code = P('i','M'); $$->a1.p = $1; $$->a2.p = $5; } |
7ea5b00f OZ |
744 | | term '.' FIRST { $$ = f_new_inst(); $$->code = P('a','f'); $$->a1.p = $1; } |
745 | | term '.' LAST { $$ = f_new_inst(); $$->code = P('a','l'); $$->a1.p = $1; } | |
9c9cc35c | 746 | | term '.' LAST_NONAGGREGATED { $$ = f_new_inst(); $$->code = P('a','L'); $$->a1.p = $1; } |
7f77e250 PM |
747 | |
748 | /* Communities */ | |
10a53608 PM |
749 | /* This causes one shift/reduce conflict |
750 | | rtadot dynamic_attr '.' ADD '(' term ')' { } | |
751 | | rtadot dynamic_attr '.' DELETE '(' term ')' { } | |
752 | | rtadot dynamic_attr '.' CONTAINS '(' term ')' { } | |
a2d15746 | 753 | | rtadot dynamic_attr '.' RESET{ } |
10a53608 | 754 | */ |
7f77e250 | 755 | |
e399b6f6 | 756 | | '+' EMPTY '+' { $$ = f_new_inst(); $$->code = 'E'; $$->aux = T_PATH; } |
9c400ec9 | 757 | | '-' EMPTY '-' { $$ = f_new_inst(); $$->code = 'E'; $$->aux = T_CLIST; } |
42a0c054 | 758 | | '-' '-' EMPTY '-' '-' { $$ = f_new_inst(); $$->code = 'E'; $$->aux = T_ECLIST; } |
e399b6f6 | 759 | | PREPEND '(' term ',' term ')' { $$ = f_new_inst(); $$->code = P('A','p'); $$->a1.p = $3; $$->a2.p = $5; } |
4444ed2b PM |
760 | | ADD '(' term ',' term ')' { $$ = f_new_inst(); $$->code = P('C','a'); $$->a1.p = $3; $$->a2.p = $5; $$->aux = 'a'; } |
761 | | DELETE '(' term ',' term ')' { $$ = f_new_inst(); $$->code = P('C','a'); $$->a1.p = $3; $$->a2.p = $5; $$->aux = 'd'; } | |
e08d2ff0 | 762 | | FILTER '(' term ',' term ')' { $$ = f_new_inst(); $$->code = P('C','a'); $$->a1.p = $3; $$->a2.p = $5; $$->aux = 'f'; } |
afc54517 | 763 | |
0264ccf6 PT |
764 | | ROA_CHECK '(' rtable ')' { $$ = f_generate_roa_check($3, NULL, NULL); } |
765 | | ROA_CHECK '(' rtable ',' term ',' term ')' { $$ = f_generate_roa_check($3, $5, $7); } | |
af582c48 | 766 | |
a2d15746 | 767 | /* | term '.' LEN { $$->code = P('P','l'); } */ |
7f77e250 | 768 | |
995e5894 PM |
769 | /* function_call is inlined here */ |
770 | | SYM '(' var_list ')' { | |
771 | struct symbol *sym; | |
772 | struct f_inst *inst = $3; | |
773 | if ($1->class != SYM_FUNCTION) | |
a5a947d4 | 774 | cf_error("You can't call something which is not a function. Really."); |
995e5894 PM |
775 | DBG("You are calling function %s\n", $1->name); |
776 | $$ = f_new_inst(); | |
777 | $$->code = P('c','a'); | |
778 | $$->a1.p = inst; | |
083c43e2 OZ |
779 | $$->a2.p = $1->def; |
780 | sym = $1->aux2; | |
995e5894 PM |
781 | while (sym || inst) { |
782 | if (!sym || !inst) | |
a5a947d4 | 783 | cf_error("Wrong number of arguments for function %s.", $1->name); |
995e5894 PM |
784 | DBG( "You should pass parameter called %s\n", sym->name); |
785 | inst->a1.p = sym; | |
083c43e2 | 786 | sym = sym->aux2; |
995e5894 PM |
787 | inst = inst->next; |
788 | } | |
789 | } | |
ba921648 PM |
790 | ; |
791 | ||
792 | break_command: | |
03e3d184 MM |
793 | QUITBIRD { $$ = F_QUITBIRD; } |
794 | | ACCEPT { $$ = F_ACCEPT; } | |
795 | | REJECT { $$ = F_REJECT; } | |
796 | | ERROR { $$ = F_ERROR; } | |
797 | | PRINT { $$ = F_NOP; } | |
798 | | PRINTN { $$ = F_NONL; } | |
ba921648 PM |
799 | ; |
800 | ||
23b1539b | 801 | print_one: |
2db3b288 | 802 | term { $$ = f_new_inst(); $$->code = 'p'; $$->a1.p = $1; $$->a2.p = NULL; } |
23b1539b PM |
803 | ; |
804 | ||
805 | print_list: /* EMPTY */ { $$ = NULL; } | |
995e5894 PM |
806 | | print_one { $$ = $1; } |
807 | | print_one ',' print_list { | |
23b1539b | 808 | if ($1) { |
995e5894 | 809 | $1->next = $3; |
23b1539b | 810 | $$ = $1; |
995e5894 | 811 | } else $$ = $3; |
23b1539b PM |
812 | } |
813 | ; | |
814 | ||
6dc7a0cb | 815 | var_listn: term { |
d3dd620b PM |
816 | $$ = f_new_inst(); |
817 | $$->code = 's'; | |
818 | $$->a1.p = NULL; | |
819 | $$->a2.p = $1; | |
820 | $$->next = NULL; | |
821 | } | |
6dc7a0cb | 822 | | term ',' var_listn { |
6542ece9 PM |
823 | $$ = f_new_inst(); |
824 | $$->code = 's'; | |
825 | $$->a1.p = NULL; | |
826 | $$->a2.p = $1; | |
827 | $$->next = $3; | |
828 | } | |
829 | ; | |
830 | ||
6dc7a0cb PM |
831 | var_list: /* EMPTY */ { $$ = NULL; } |
832 | | var_listn { $$ = $1; } | |
833 | ; | |
834 | ||
23b1539b | 835 | cmd: |
49955645 MM |
836 | IF term THEN block { |
837 | $$ = f_new_inst(); | |
838 | $$->code = '?'; | |
839 | $$->a1.p = $2; | |
840 | $$->a2.p = $4; | |
23b1539b | 841 | } |
49955645 MM |
842 | | IF term THEN block ELSE block { |
843 | struct f_inst *i = f_new_inst(); | |
844 | i->code = '?'; | |
845 | i->a1.p = $2; | |
846 | i->a2.p = $4; | |
23b1539b PM |
847 | $$ = f_new_inst(); |
848 | $$->code = '?'; | |
49955645 MM |
849 | $$->a1.p = i; |
850 | $$->a2.p = $6; | |
23b1539b | 851 | } |
ba921648 | 852 | | SYM '=' term ';' { |
84c7e194 | 853 | $$ = f_new_inst(); |
d4d75628 | 854 | DBG( "Ook, we'll set value\n" ); |
ba921648 | 855 | if (($1->class & ~T_MASK) != SYM_VARIABLE) |
a5a947d4 | 856 | cf_error( "You may set only variables." ); |
23b1539b | 857 | $$->code = 's'; |
2db3b288 PM |
858 | $$->a1.p = $1; |
859 | $$->a2.p = $3; | |
b9d70dc8 | 860 | } |
2d496d20 PM |
861 | | RETURN term ';' { |
862 | $$ = f_new_inst(); | |
d4d75628 | 863 | DBG( "Ook, we'll return the value\n" ); |
2d496d20 PM |
864 | $$->code = 'r'; |
865 | $$->a1.p = $2; | |
866 | } | |
db1326aa | 867 | | rtadot dynamic_attr '=' term ';' { |
6c14255d | 868 | $$ = $2; |
0dc4431c | 869 | $$->code = P('e','S'); |
6c14255d | 870 | $$->a1.p = $4; |
f31156ca | 871 | } |
0dc4431c PM |
872 | | rtadot static_attr '=' term ';' { |
873 | $$ = $2; | |
874 | if (!$$->a1.i) | |
875 | cf_error( "This static attribute is read-only."); | |
876 | $$->code = P('a','S'); | |
877 | $$->a1.p = $4; | |
878 | } | |
879 | | PREFERENCE '=' term ';' { | |
880 | $$ = f_new_inst(); | |
881 | $$->code = P('P','S'); | |
882 | $$->a1.p = $3; | |
883 | } | |
db1326aa | 884 | | UNSET '(' rtadot dynamic_attr ')' ';' { |
6c14255d | 885 | $$ = $4; |
9f4929e7 | 886 | $$->aux = EAF_TYPE_UNDEF | EAF_TEMP; |
2d496d20 | 887 | $$->code = P('e','S'); |
48f9e019 | 888 | $$->a1.p = NULL; |
c7b43f33 | 889 | } |
2d496d20 PM |
890 | | break_command print_list ';' { $$ = f_new_inst(); $$->code = P('p',','); $$->a1.p = $2; $$->a2.i = $1; } |
891 | | function_call ';' { $$ = $1; } | |
7db7b7db PM |
892 | | CASE term '{' switch_body '}' { |
893 | $$ = f_new_inst(); | |
2d496d20 | 894 | $$->code = P('S','W'); |
7db7b7db | 895 | $$->a1.p = $2; |
41be4444 | 896 | $$->a2.p = build_tree( $4 ); |
7db7b7db | 897 | } |
7d6eebae PM |
898 | |
899 | ||
42a0c054 | 900 | | rtadot dynamic_attr '.' EMPTY ';' { $$ = f_generate_empty($2); } |
7d6eebae | 901 | | rtadot dynamic_attr '.' PREPEND '(' term ')' ';' { $$ = f_generate_complex( P('A','p'), 'x', $2, $6 ); } |
42a0c054 OZ |
902 | | rtadot dynamic_attr '.' ADD '(' term ')' ';' { $$ = f_generate_complex( P('C','a'), 'a', $2, $6 ); } |
903 | | rtadot dynamic_attr '.' DELETE '(' term ')' ';' { $$ = f_generate_complex( P('C','a'), 'd', $2, $6 ); } | |
904 | | rtadot dynamic_attr '.' FILTER '(' term ')' ';' { $$ = f_generate_complex( P('C','a'), 'f', $2, $6 ); } | |
b9d70dc8 PM |
905 | ; |
906 | ||
907 | CF_END |