]> git.ipfire.org Git - thirdparty/bird.git/blame - filter/config.Y
Got rid of startup functions and filters_postconfig().
[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 *
f4536657 8 FIXME (nonurgent): define keyword
2d496d20 9 FIXME (for BGP): whole system of paths, path ~ string, path.prepend(), path.originate
c9f8c1a8 10 FIXME: create community lists
60d7d10e 11 FIXME: IP addresses in ipv6
0a06a9b8
PM
12
13
7f77e250 14 FIXME: check messages for \n at the end
b9d70dc8
PM
15 */
16
17CF_HDR
18
2edb31b0
MM
19CF_DEFINES
20
2d496d20
PM
21#define P(a,b) ((a<<8) | b)
22
b9d70dc8
PM
23CF_DECLS
24
e4a73dbf 25CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
ba921648 26 ACCEPT, REJECT, ERROR, QUITBIRD,
10a53608 27 INT, BOOL, IP, PREFIX, PAIR, SET, STRING, BGPMASK, BGPPATH, CLIST,
7db7b7db 28 IF, THEN, ELSE, CASE,
23b1539b 29 TRUE, FALSE,
db1326aa 30 FROM, GW, NET, MASK, SOURCE,
36bbfc70 31 LEN,
f4536657 32 DEFINED,
7f77e250 33 ADD, DELETE, CONTAINS, RESET,
e399b6f6 34 PREPEND, MATCH,
afc54517 35 EMPTY,
1c20608e 36 FILTER, WHERE, EVAL)
b9d70dc8 37
f4536657 38%nonassoc THEN
4ed8718a 39%nonassoc ELSE
f4536657 40
db1326aa 41%type <x> term block cmds cmd function_body constant print_one print_list var_list var_listn dynamic_attr function_call
430da60f 42%type <f> filter filter_body where_filter
4515bdba 43%type <i> type break_command pair
41be4444 44%type <e> set_item set_items switch_body
758458be 45%type <v> set_atom fprefix fprefix_s ipa
6dc7a0cb 46%type <s> decls declsn one_decl function_params
77de6882
PM
47%type <h> bgp_path
48%type <i> bgp_one
b9d70dc8
PM
49
50CF_GRAMMAR
51
e0f2e42f
MM
52CF_ADDTO(conf, filter_def)
53filter_def:
cb8034f4 54 FILTER SYM { cf_push_scope( $2 ); } filter_body {
ae3e1af2
PM
55 cf_define_symbol($2, SYM_FILTER, $4);
56 $4->name = $2->name;
d4d75628 57 DBG( "We have new filter defined (%s)\n", $2->name );
ae3e1af2 58 cf_pop_scope();
b9d70dc8
PM
59 }
60 ;
61
1c20608e
MM
62CF_ADDTO(conf, filter_eval)
63filter_eval:
64 EVAL term { f_eval_int($2); }
65 ;
66
ba921648
PM
67type:
68 INT { $$ = T_INT; }
69 | BOOL { $$ = T_BOOL; }
70 | IP { $$ = T_IP; }
71 | PREFIX { $$ = T_PREFIX; }
72 | PAIR { $$ = T_PAIR; }
73 | STRING { $$ = T_STRING; }
10a53608
PM
74 | BGPMASK { $$ = T_PATH_MASK; }
75 | BGPPATH { $$ = T_PATH; }
76 | CLIST { $$ = T_CLIST; }
ba921648
PM
77 | type SET {
78 switch ($1) {
79 default:
80 cf_error( "You can not create sets of this type\n" );
81 case T_INT: case T_IP: case T_PREFIX: case T_PAIR:
82 }
83 $$ = $1 | T_SET;
84 }
85 ;
86
6dc7a0cb
PM
87one_decl:
88 type SYM {
4107df1d 89 cf_define_symbol($2, SYM_VARIABLE | $1, NULL);
d4d75628 90 DBG( "New variable %s type %x\n", $2->name, $1 );
cb8034f4 91 $2->aux = 0;
d3dd620b
PM
92 {
93 struct f_val * val;
94 val = cfg_alloc(sizeof(struct f_val));
95 val->type = $1;
96 $2->aux2 = val;
97 }
6542ece9 98 $$=$2;
ba921648
PM
99 }
100 ;
101
6dc7a0cb
PM
102/* Decls with ';' at the end */
103decls: /* EMPTY */ { $$ = NULL; }
104 | one_decl ';' decls {
105 $$ = $1;
cb8034f4 106 $$->aux = (int) $3;
6dc7a0cb
PM
107 }
108 ;
109
3c989eb4 110/* Declarations that have no ';' at the end. */
6dc7a0cb
PM
111declsn: one_decl { $$ = $1; }
112 | declsn ';' one_decl {
4515bdba
PM
113 $$ = $1;
114 $$->aux = (int) $3;
6dc7a0cb
PM
115 }
116 ;
117
e0f2e42f 118filter_body:
ba921648 119 function_body {
e0f2e42f
MM
120 struct filter *f = cfg_alloc(sizeof(struct filter));
121 f->name = NULL;
ba921648 122 f->root = $1;
e0f2e42f
MM
123 $$ = f;
124 }
125 ;
126
127filter:
128 SYM {
129 if ($1->class != SYM_FILTER) cf_error("No such filter");
130 $$ = $1->def;
131 }
132 | filter_body
133 ;
134
430da60f
MM
135where_filter:
136 WHERE term {
137 /* Construct 'IF term THEN ACCEPT; REJECT;' */
138 struct filter *f = cfg_alloc(sizeof(struct filter));
139 struct f_inst *i, *acc, *rej;
140 acc = f_new_inst(); /* ACCEPT */
2d496d20 141 acc->code = P('p',',');
430da60f
MM
142 acc->a1.p = NULL;
143 acc->a2.i = F_ACCEPT;
144 rej = f_new_inst(); /* REJECT */
2d496d20 145 rej->code = P('p',',');
430da60f
MM
146 rej->a1.p = NULL;
147 rej->a2.i = F_REJECT;
148 i = f_new_inst(); /* IF */
149 i->code = '?';
150 i->a1.p = $2;
151 i->a2.p = acc;
152 i->next = rej;
153 f->name = NULL;
154 f->root = i;
155 $$ = f;
156 }
157 ;
158
ba921648 159function_params:
d4d75628 160 '(' declsn ')' { DBG( "Have function parameters\n" ); $$=$2; }
6dc7a0cb 161 | '(' ')' { $$=NULL; }
ba921648 162 ;
b9d70dc8 163
ba921648
PM
164function_body:
165 decls '{' cmds '}' {
166 $$ = $3;
84c7e194 167 }
ba921648
PM
168 ;
169
170CF_ADDTO(conf, function_def)
171function_def:
1c20608e 172 FUNCTION SYM { DBG( "Beginning of function %s\n", $2->name ); cf_push_scope($2); } function_params function_body {
ae3e1af2 173 cf_define_symbol($2, SYM_FUNCTION, $5);
cb8034f4 174 $2->aux = (int) $4;
ae3e1af2 175 $2->aux2 = $5;
d4d75628 176 DBG("Hmm, we've got one function here - %s\n", $2->name);
ae3e1af2 177 cf_pop_scope();
ba921648
PM
178 }
179 ;
180
181/* Programs */
182
183cmds: /* EMPTY */ { $$ = NULL; }
184 | cmd cmds {
84c7e194 185 if ($1) {
7db7b7db
PM
186 if ($1->next)
187 bug("Command has next already set\n");
ba921648 188 $1->next = $2;
84c7e194 189 $$ = $1;
ba921648 190 } else $$ = $2;
84c7e194
PM
191 }
192 ;
193
2575593e 194block:
ba921648 195 cmd {
2575593e
PM
196 $$=$1;
197 }
198 | '{' cmds '}' {
199 $$=$2;
200 }
201 ;
202
d3dd620b
PM
203/*
204 * Simple types, their bison value is int
205 */
206pair:
207 '(' NUM ',' NUM ')' { $$ = $2 << 16 | $4; }
208 ;
209
210/*
211 * Complex types, their bison value is struct f_val
212 */
758458be
MM
213fprefix_s:
214 IPA '/' NUM {
215 if (!ip_is_prefix($1, $3)) cf_error("Invalid network prefix: %I/%d", $1, $3);
216 $$.type = T_PREFIX; $$.val.px.ip = $1; $$.val.px.len = $3;
217 }
d3dd620b
PM
218 ;
219
758458be
MM
220fprefix:
221 fprefix_s { $$ = $1; }
222 | fprefix_s '+' { $$ = $1; $$.val.px.len |= LEN_PLUS; }
223 | fprefix_s '-' { $$ = $1; $$.val.px.len |= LEN_MINUS; }
224 | fprefix_s '{' NUM ',' NUM '}' { $$ = $1; $$.val.px.len |= LEN_RANGE | ($3 << 16) | ($5 << 8); }
d3dd620b
PM
225 ;
226
227ipa:
6dc7a0cb 228 IPA { $$.type = T_IP; $$.val.px.ip = $1; }
d3dd620b
PM
229 ;
230
38506f71 231set_atom:
d3dd620b
PM
232 NUM { $$.type = T_INT; $$.val.i = $1; }
233 | pair { $$.type = T_PAIR; $$.val.i = $1; }
234 | ipa { $$ = $1; }
758458be 235 | fprefix { $$ = $1; }
38506f71
PM
236 ;
237
238set_item:
995e5894
PM
239 set_atom {
240 $$ = f_new_tree();
241 $$->from = $1;
242 if ($1.type != T_PREFIX)
243 $$->to = $1;
244 else {
245 $$->to = $1;
246 $$->to.val.px.ip = ipa_or( $$->to.val.px.ip, ipa_not( ipa_mkmask( $$->to.val.px.len ) ));
247 }
248 }
249 | set_atom '.' '.' set_atom {
250 $$ = f_new_tree();
251 $$->from = $1;
252 $$->to = $4;
253 if (($1.type == T_PREFIX) || ($4.type == T_PREFIX)) cf_error( "You can not use prefixes for range" );
254 }
38506f71
PM
255 ;
256
257set_items:
258 set_item { $$ = $1; }
259 | set_items ',' set_item { $$ = $3; $$->left = $1; }
260 ;
261
41be4444
PM
262switch_body: /* EMPTY */ { $$ = NULL; }
263 | set_item ':' cmds switch_body {
264 $$ = $1;
265 $$->data = $3;
266 $$->left = $4;
267 }
268 | ELSE ':' cmds {
269 $$ = f_new_tree();
270 $$->from.type = T_VOID;
271 $$->to.type = T_VOID;
272 $$->data = $3;
273 }
274 ;
d3dd620b 275
e4a73dbf
PM
276/* CONST '(' expr ')' { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_INT; $$->a2.i = $3; } */
277
77de6882
PM
278bgp_one:
279 NUM { $$ = $1; }
4b641bab 280 | '*' { $$ = PM_ANY; }
77de6882
PM
281 ;
282
283bgp_path:
dcab7890
PM
284 bgp_one { $$ = cfg_alloc(sizeof(struct f_path_mask)); $$->next = NULL; $$->val = $1; }
285 | bgp_one bgp_path { $$ = cfg_alloc(sizeof(struct f_path_mask)); $$->next = $2; $$->val = $1; }
77de6882
PM
286 ;
287
23b1539b 288constant:
e4a73dbf 289 NUM { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_INT; $$->a2.i = $1; }
c7b43f33
PM
290 | TRUE { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_BOOL; $$->a2.i = 1; }
291 | FALSE { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_BOOL; $$->a2.i = 0; }
292 | TEXT { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_STRING; $$->a2.p = $1; }
293 | pair { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_PAIR; $$->a2.i = $1; }
d3dd620b 294 | ipa { NEW_F_VAL; $$ = f_new_inst(); $$->code = 'C'; $$->a1.p = val; *val = $1; }
758458be 295 | fprefix_s {NEW_F_VAL; $$ = f_new_inst(); $$->code = 'C'; $$->a1.p = val; *val = $1; }
d4d75628 296 | '[' 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" ); }
c7b43f33 297 | ENUM { $$ = f_new_inst(); $$->code = 'c'; $$->aux = $1 >> 16; $$->a2.i = $1 & 0xffff; }
f71bded6 298 | '/' bgp_path '/' { NEW_F_VAL; $$ = f_new_inst(); $$->code = 'C'; val->type = T_PATH_MASK; val->val.path_mask = $2; $$->a1.p = val; }
23b1539b
PM
299 ;
300
db1326aa
MM
301/*
302 * Maybe there are no dynamic attributes defined by protocols.
303 * For such cases, we force the dynamic_attr list to contain
304 * at least an invalid token, so it's syntantically correct.
305 */
306CF_ADDTO(dynamic_attr, INVALID_TOKEN { $$ = NULL; })
f4536657 307
2e18b87d 308rtadot: /* EMPTY, we are not permitted RTA. prefix */
6c14255d 309 ;
f4536657 310
2d496d20
PM
311function_call:
312 SYM '(' var_list ')' {
313 struct symbol *sym;
314 struct f_inst *inst = $3;
315 if ($1->class != SYM_FUNCTION)
316 cf_error("You can not call something which is not function. Really.");
d4d75628 317 DBG("You are calling function %s\n", $1->name);
2d496d20
PM
318 $$ = f_new_inst();
319 $$->code = P('c','a');
320 $$->a1.p = inst;
321 $$->a2.p = $1->aux2;
322 sym = (void *) $1->aux;
323 while (sym || inst) {
324 if (!sym || !inst)
325 cf_error("wrong number of arguments for function %s.", $1->name);
d4d75628 326 DBG( "You should pass parameter called %s\n", sym->name);
2d496d20
PM
327 inst->a1.p = sym;
328 sym = (void *) sym->aux;
329 inst = inst->next;
330 }
331 }
332 ;
333
84c7e194 334term:
f4536657 335 '(' term ')' { $$ = $2; }
d4d75628 336 | term '+' term { $$ = f_new_inst(); $$->code = '+'; $$->a1.p = $1; $$->a2.p = $3; }
2d496d20 337 | term '=' term { $$ = f_new_inst(); $$->code = P('=','='); $$->a1.p = $1; $$->a2.p = $3; }
d4d75628
PM
338 | term NEQ term { $$ = f_new_inst(); $$->code = P('!','='); $$->a1.p = $1; $$->a2.p = $3; }
339 | term '<' term { $$ = f_new_inst(); $$->code = '<'; $$->a1.p = $1; $$->a2.p = $3; }
340 | term LEQ term { $$ = f_new_inst(); $$->code = P('<','='); $$->a1.p = $1; $$->a2.p = $3; }
341 | term '>' term { $$ = f_new_inst(); $$->code = '<'; $$->a1.p = $3; $$->a2.p = $1; }
342 | term GEQ term { $$ = f_new_inst(); $$->code = P('<','='); $$->a1.p = $3; $$->a2.p = $1; }
343 | term '~' term { $$ = f_new_inst(); $$->code = '~'; $$->a1.p = $1; $$->a2.p = $3; }
995e5894 344 | '!' term { $$ = f_new_inst(); $$->code = '!'; $$->a1.p = $2; }
2d496d20 345 | DEFINED '(' term ')' { $$ = f_new_inst(); $$->code = P('d','e'); $$->a1.p = $3; }
23b1539b 346
1183b6b2 347 | constant { $$ = $1; }
2575593e
PM
348 | SYM {
349 $$ = f_new_inst();
350 switch ($1->class) {
ba921648 351 case SYM_VARIABLE | T_INT:
d3dd620b
PM
352 case SYM_VARIABLE | T_PAIR:
353 case SYM_VARIABLE | T_PREFIX:
354 case SYM_VARIABLE | T_IP:
dcab7890 355 case SYM_VARIABLE | T_PATH_MASK:
e399b6f6 356 case SYM_VARIABLE | T_PATH:
9c400ec9 357 case SYM_VARIABLE | T_CLIST:
d3dd620b
PM
358 $$->code = 'C';
359 $$->a1.p = $1->aux2;
2575593e
PM
360 break;
361 default:
1183b6b2 362 cf_error("Can not use this class of symbol (%s,%x) as variable.", $1->name, $1->class );
2575593e
PM
363 }
364 }
4515bdba 365
6c14255d 366 | rtadot FROM { $$ = f_new_inst(); $$->code = 'a'; $$->aux = T_IP; $$->a2.i = OFFSETOF(struct rta, from); }
36bbfc70 367
6c14255d
PM
368 | rtadot GW { $$ = f_new_inst(); $$->code = 'a'; $$->aux = T_IP; $$->a2.i = OFFSETOF(struct rta, gw); }
369 | rtadot NET { $$ = f_new_inst(); $$->code = 'a'; $$->aux = T_PREFIX; $$->a2.i = 0x12345678; }
b1573615 370 | rtadot SOURCE { $$ = f_new_inst(); $$->code = 'a'; $$->aux = T_ENUM_RTS; $$->a2.i = OFFSETOF(struct rta, source); }
6dc7a0cb 371
db1326aa 372 | rtadot dynamic_attr { $$ = $2; $$->code = P('e','a'); }
36bbfc70 373
2d496d20 374 | term '.' IP { $$ = f_new_inst(); $$->code = P('c','p'); $$->a1.p = $1; $$->aux = T_IP; }
684c6f5a 375 | term '.' LEN { $$ = f_new_inst(); $$->code = 'L'; $$->a1.p = $1; }
2d496d20 376 | term '.' MASK '(' term ')' { $$ = f_new_inst(); $$->code = P('i','M'); $$->a1.p = $1; $$->a2.p = $5; }
7f77e250
PM
377
378/* Communities */
10a53608
PM
379/* This causes one shift/reduce conflict
380 | rtadot dynamic_attr '.' ADD '(' term ')' { }
381 | rtadot dynamic_attr '.' DELETE '(' term ')' { }
382 | rtadot dynamic_attr '.' CONTAINS '(' term ')' { }
a2d15746 383 | rtadot dynamic_attr '.' RESET{ }
10a53608 384*/
7f77e250
PM
385
386/* Paths */
e399b6f6 387 | term '.' PREPEND '(' term ')' { }
a2d15746 388 | term '.' RESET { }
afc54517 389
e399b6f6 390 | '+' EMPTY '+' { $$ = f_new_inst(); $$->code = 'E'; $$->aux = T_PATH; }
9c400ec9 391 | '-' EMPTY '-' { $$ = f_new_inst(); $$->code = 'E'; $$->aux = T_CLIST; }
e399b6f6 392 | PREPEND '(' term ',' term ')' { $$ = f_new_inst(); $$->code = P('A','p'); $$->a1.p = $3; $$->a2.p = $5; }
4444ed2b
PM
393 | ADD '(' term ',' term ')' { $$ = f_new_inst(); $$->code = P('C','a'); $$->a1.p = $3; $$->a2.p = $5; $$->aux = 'a'; }
394 | DELETE '(' term ',' term ')' { $$ = f_new_inst(); $$->code = P('C','a'); $$->a1.p = $3; $$->a2.p = $5; $$->aux = 'd'; }
9c400ec9 395
afc54517 396
a2d15746 397/* | term '.' LEN { $$->code = P('P','l'); } */
7f77e250 398
995e5894
PM
399/* function_call is inlined here */
400 | SYM '(' var_list ')' {
401 struct symbol *sym;
402 struct f_inst *inst = $3;
403 if ($1->class != SYM_FUNCTION)
404 cf_error("You can not call something which is not function. Really.");
405 DBG("You are calling function %s\n", $1->name);
406 $$ = f_new_inst();
407 $$->code = P('c','a');
408 $$->a1.p = inst;
409 $$->a2.p = $1->aux2;
410 sym = (void *) $1->aux;
411 while (sym || inst) {
412 if (!sym || !inst)
413 cf_error("wrong number of arguments for function %s.", $1->name);
414 DBG( "You should pass parameter called %s\n", sym->name);
415 inst->a1.p = sym;
416 sym = (void *) sym->aux;
417 inst = inst->next;
418 }
419 }
ba921648
PM
420 ;
421
422break_command:
423 QUITBIRD { $$ = F_QUITBIRD }
424 | ACCEPT { $$ = F_ACCEPT }
425 | REJECT { $$ = F_REJECT }
426 | ERROR { $$ = F_ERROR }
23b1539b 427 | PRINT { $$ = F_NOP }
d3dd620b 428 | PRINTN { $$ = F_NONL }
ba921648
PM
429 ;
430
23b1539b 431print_one:
2db3b288 432 term { $$ = f_new_inst(); $$->code = 'p'; $$->a1.p = $1; $$->a2.p = NULL; }
23b1539b
PM
433 ;
434
435print_list: /* EMPTY */ { $$ = NULL; }
995e5894
PM
436 | print_one { $$ = $1; }
437 | print_one ',' print_list {
23b1539b 438 if ($1) {
995e5894 439 $1->next = $3;
23b1539b 440 $$ = $1;
995e5894 441 } else $$ = $3;
23b1539b 442 }
995e5894 443
23b1539b
PM
444 ;
445
6dc7a0cb 446var_listn: term {
d3dd620b
PM
447 $$ = f_new_inst();
448 $$->code = 's';
449 $$->a1.p = NULL;
450 $$->a2.p = $1;
451 $$->next = NULL;
452 }
6dc7a0cb 453 | term ',' var_listn {
6542ece9
PM
454 $$ = f_new_inst();
455 $$->code = 's';
456 $$->a1.p = NULL;
457 $$->a2.p = $1;
458 $$->next = $3;
459 }
460 ;
461
6dc7a0cb
PM
462var_list: /* EMPTY */ { $$ = NULL; }
463 | var_listn { $$ = $1; }
464 ;
465
23b1539b 466cmd:
49955645
MM
467 IF term THEN block {
468 $$ = f_new_inst();
469 $$->code = '?';
470 $$->a1.p = $2;
471 $$->a2.p = $4;
23b1539b 472 }
49955645
MM
473 | IF term THEN block ELSE block {
474 struct f_inst *i = f_new_inst();
475 i->code = '?';
476 i->a1.p = $2;
477 i->a2.p = $4;
23b1539b
PM
478 $$ = f_new_inst();
479 $$->code = '?';
49955645
MM
480 $$->a1.p = i;
481 $$->a2.p = $6;
23b1539b 482 }
ba921648 483 | SYM '=' term ';' {
84c7e194 484 $$ = f_new_inst();
d4d75628 485 DBG( "Ook, we'll set value\n" );
ba921648
PM
486 if (($1->class & ~T_MASK) != SYM_VARIABLE)
487 cf_error( "You may only set variables, and this is %x.\n", $1->class );
23b1539b 488 $$->code = 's';
2db3b288
PM
489 $$->a1.p = $1;
490 $$->a2.p = $3;
b9d70dc8 491 }
2d496d20
PM
492 | RETURN term ';' {
493 $$ = f_new_inst();
d4d75628 494 DBG( "Ook, we'll return the value\n" );
2d496d20
PM
495 $$->code = 'r';
496 $$->a1.p = $2;
497 }
db1326aa 498 | rtadot dynamic_attr '=' term ';' {
6c14255d 499 $$ = $2;
2d496d20 500 $$->code = P('e','S');
6c14255d 501 $$->a1.p = $4;
f31156ca 502 }
db1326aa 503 | UNSET '(' rtadot dynamic_attr ')' ';' {
6c14255d 504 $$ = $4;
9f4929e7 505 $$->aux = EAF_TYPE_UNDEF | EAF_TEMP;
2d496d20 506 $$->code = P('e','S');
48f9e019 507 $$->a1.p = NULL;
c7b43f33 508 }
2d496d20
PM
509 | break_command print_list ';' { $$ = f_new_inst(); $$->code = P('p',','); $$->a1.p = $2; $$->a2.i = $1; }
510 | function_call ';' { $$ = $1; }
7db7b7db
PM
511 | CASE term '{' switch_body '}' {
512 $$ = f_new_inst();
2d496d20 513 $$->code = P('S','W');
7db7b7db 514 $$->a1.p = $2;
41be4444 515 $$->a2.p = build_tree( $4 );
7db7b7db 516 }
7d6eebae
PM
517
518
519 | rtadot dynamic_attr '.' EMPTY ';'
520 { struct f_inst *i = f_new_inst(); i->code = 'E'; i->aux = T_CLIST; $$ = $2; $$->code = P('e','S'); $$->a1.p = i; }
521 | rtadot dynamic_attr '.' PREPEND '(' term ')' ';' { $$ = f_generate_complex( P('A','p'), 'x', $2, $6 ); }
522 | rtadot dynamic_attr '.' ADD '(' term ')' ';' { $$ = f_generate_complex( P('C','a'), 'a', $2, $6 ); }
523 | rtadot dynamic_attr '.' DELETE '(' term ')' ';' { $$ = f_generate_complex( P('C','a'), 'd', $2, $6 ); }
b9d70dc8
PM
524 ;
525
526CF_END