Statistics
| Branch: | Revision:

iof-bird-daemon / filter / config.Y @ 14f6aca4

History | View | Annotate | Download (17.6 KB)

1
/*
2
 *	BIRD - filters
3
 *
4
 *	Copyright 1998--2000 Pavel Machek
5
 *
6
 *	Can be freely distributed and used under the terms of the GNU GPL.
7
 *
8
	FIXME: priority of ! should be lower
9
 */
10

    
11
CF_HDR
12

    
13
CF_DEFINES
14

    
15
#define P(a,b) ((a<<8) | b)
16

    
17
static int make_pair(int i1, int i2)
18
{
19
  unsigned u1 = i1;
20
  unsigned u2 = i2;
21

    
22
  if ((u1 > 0xFFFF) || (u2 > 0xFFFF))
23
    cf_error( "Can't operate with value out of bounds in pair constructor");
24

    
25
  return (u1 << 16) | u2;
26
}
27

    
28
CF_DECLS
29

    
30
CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
31
	ACCEPT, REJECT, ERROR, QUITBIRD,
32
	INT, BOOL, IP, PREFIX, PAIR, SET, STRING, BGPMASK, BGPPATH, CLIST,
33
	IF, THEN, ELSE, CASE,
34
	TRUE, FALSE,
35
	FROM, GW, NET, MASK, PROTO, SOURCE, SCOPE, CAST, DEST, PREFERENCE,
36
	LEN,
37
	DEFINED,
38
	ADD, DELETE, CONTAINS, RESET,
39
	PREPEND, FIRST, LAST, MATCH,
40
	EMPTY,
41
	FILTER, WHERE, EVAL)
42

    
43
%nonassoc THEN
44
%nonassoc ELSE
45

    
46
%type <x> term block cmds cmds_int cmd function_body constant print_one print_list var_list var_listn dynamic_attr static_attr function_call symbol dpair bgp_path_expr
47
%type <f> filter filter_body where_filter
48
%type <i> type break_command cpair
49
%type <e> set_item set_items switch_body
50
%type <trie> fprefix_set
51
%type <v> set_atom fprefix fprefix_s fipa
52
%type <s> decls declsn one_decl function_params 
53
%type <h> bgp_path bgp_path_tail1 bgp_path_tail2
54

    
55
CF_GRAMMAR
56

    
57
CF_ADDTO(conf, filter_def)
58
filter_def:
59
   FILTER SYM { cf_push_scope( $2 ); } filter_body {
60
     $2 = cf_define_symbol($2, SYM_FILTER, $4);
61
     $4->name = $2->name;
62
     DBG( "We have new filter defined (%s)\n", $2->name );
63
     cf_pop_scope();
64
   }
65
 ;
66

    
67
CF_ADDTO(conf, filter_eval)
68
filter_eval:
69
   EVAL term { f_eval_int($2); }
70
 ;
71

    
72
type:
73
   INT { $$ = T_INT; }
74
 | BOOL { $$ = T_BOOL; }
75
 | IP { $$ = T_IP; }
76
 | PREFIX { $$ = T_PREFIX; }
77
 | PAIR { $$ = T_PAIR; }
78
 | STRING { $$ = T_STRING; }
79
 | BGPMASK { $$ = T_PATH_MASK; }
80
 | BGPPATH { $$ = T_PATH; }
81
 | CLIST { $$ = T_CLIST; }
82
 | type SET { 
83
	switch ($1) {
84
	  case T_INT:
85
	  case T_IP:
86
	  case T_PAIR:
87
	       $$ = T_SET;
88
	       break;
89

    
90
	  case T_PREFIX:
91
	       $$ = T_PREFIX_SET;
92
	    break;
93

    
94
	  default:
95
		cf_error( "You can't create sets of this type." );
96
	}
97
   }
98
 ;
99

    
100
one_decl:
101
   type SYM {
102
     struct f_val * val = cfg_alloc(sizeof(struct f_val)); 
103
     val->type = $1; 
104
     $2 = cf_define_symbol($2, SYM_VARIABLE | $1, val);
105
     DBG( "New variable %s type %x\n", $2->name, $1 );
106
     $2->aux2 = NULL;
107
     $$=$2;
108
   }
109
 ;
110

    
111
/* Decls with ';' at the end */
112
decls: /* EMPTY */ { $$ = NULL; }
113
 | one_decl ';' decls {
114
     $$ = $1;
115
     $$->aux2 = $3;
116
   }
117
 ;
118

    
119
/* Declarations that have no ';' at the end. */
120
declsn: one_decl { $$ = $1; }
121
 | declsn ';' one_decl {
122
     $$ = $1;
123
     $$->aux2 = $3;
124
   }
125
 ;
126

    
127
filter_body:
128
   function_body {
129
     struct filter *f = cfg_alloc(sizeof(struct filter));
130
     f->name = NULL;
131
     f->root = $1;
132
     $$ = f;
133
   }
134
 ;
135

    
136
filter:
137
   SYM {
138
     if ($1->class != SYM_FILTER) cf_error("No such filter.");
139
     $$ = $1->def;
140
   }
141
 | filter_body
142
 ;
143

    
144
where_filter:
145
   WHERE term {
146
     /* Construct 'IF term THEN ACCEPT; REJECT;' */
147
     struct filter *f = cfg_alloc(sizeof(struct filter));
148
     struct f_inst *i, *acc, *rej;
149
     acc = f_new_inst();		/* ACCEPT */
150
     acc->code = P('p',',');
151
     acc->a1.p = NULL;
152
     acc->a2.i = F_ACCEPT;
153
     rej = f_new_inst();		/* REJECT */
154
     rej->code = P('p',',');
155
     rej->a1.p = NULL;
156
     rej->a2.i = F_REJECT;
157
     i = f_new_inst();			/* IF */
158
     i->code = '?';
159
     i->a1.p = $2;
160
     i->a2.p = acc;
161
     i->next = rej;
162
     f->name = NULL;
163
     f->root = i;
164
     $$ = f;
165
  }
166
 ;
167

    
168
function_params:
169
   '(' declsn ')' { DBG( "Have function parameters\n" ); $$=$2; }
170
 | '(' ')' { $$=NULL; }
171
 ;
172

    
173
function_body:
174
   decls '{' cmds '}' {
175
     $$ = $3;
176
   }
177
 ;
178

    
179
CF_ADDTO(conf, function_def)
180
function_def:
181
   FUNCTION SYM { DBG( "Beginning of function %s\n", $2->name );
182
     $2 = cf_define_symbol($2, SYM_FUNCTION, NULL);
183
     cf_push_scope($2);
184
   } function_params function_body {
185
     $2->def = $5;
186
     $2->aux2 = $4;
187
     DBG("Hmm, we've got one function here - %s\n", $2->name); 
188
     cf_pop_scope();
189
   }
190
 ;
191

    
192
/* Programs */
193

    
194
/* Hack: $$ of cmds_int is the last node.
195
   $$->next of cmds_int is temporary used for the first node */
196

    
197
cmds: /* EMPTY */ { $$ = NULL; }
198
 | cmds_int { $$ = $1->next; $1->next = NULL; }
199
 ;
200

    
201
cmds_int: cmd { $$ = $1; $1->next = $1; }
202
 | cmds_int cmd { $$ = $2; $2->next = $1->next ; $1->next = $2; }
203
 ;
204

    
205
block:
206
   cmd {
207
     $$=$1;
208
   }
209
 | '{' cmds '}' {
210
     $$=$2;
211
   }
212
 ;
213

    
214
/*
215
 * Simple types, their bison value is int
216
 */
217
cpair:
218
   '(' NUM ',' NUM ')' { $$ = make_pair($2, $4); }
219
 ;
220

    
221
/*
222
 * Complex types, their bison value is struct f_val
223
 */
224
fipa:
225
   IPA %prec PREFIX_DUMMY { $$.type = T_IP; $$.val.px.ip = $1; }
226
 ;
227

    
228
set_atom:
229
   NUM   { $$.type = T_INT; $$.val.i = $1; }
230
 | cpair { $$.type = T_PAIR; $$.val.i = $1; }
231
 | fipa  { $$ = $1; }
232
 | ENUM  {  $$.type = $1 >> 16; $$.val.i = $1 & 0xffff; }
233
 ; 
234

    
235
set_item:
236
   set_atom { 
237
	$$ = f_new_tree(); 
238
	$$->from = $1; 
239
	$$->to = $1;
240
   }
241
 | set_atom '.' '.' set_atom { 
242
	$$ = f_new_tree(); 
243
	$$->from = $1; 
244
	$$->to = $4; 
245
   }
246
 ;
247

    
248
set_items:
249
   set_item { $$ = $1; }
250
 | set_items ',' set_item { $$ = $3; $$->left = $1; }
251
 ;
252

    
253
fprefix_s:
254
   IPA '/' NUM %prec '/' {
255
     if (($3 < 0) || ($3 > MAX_PREFIX_LENGTH) || !ip_is_prefix($1, $3)) cf_error("Invalid network prefix: %I/%d.", $1, $3);
256
     $$.type = T_PREFIX; $$.val.px.ip = $1; $$.val.px.len = $3;
257
   }
258
 ;
259

    
260
fprefix:
261
   fprefix_s { $$ = $1; }
262
 | fprefix_s '+' { $$ = $1; $$.val.px.len |= LEN_PLUS; }
263
 | fprefix_s '-' { $$ = $1; $$.val.px.len |= LEN_MINUS; }
264
 | fprefix_s '{' NUM ',' NUM '}' { 
265
     if (! ((0 <= $3) && ($3 <= $5) && ($5 <= MAX_PREFIX_LENGTH))) cf_error("Invalid prefix pattern range: {%d, %d}.", $3, $5);
266
     $$ = $1; $$.val.px.len |= LEN_RANGE | ($3 << 16) | ($5 << 8);
267
   }
268
 ;
269

    
270
fprefix_set:
271
   fprefix { $$ = f_new_trie(); trie_add_prefix($$, &($1.val.px)); }
272
 | fprefix_set ',' fprefix { $$ = $1; trie_add_prefix($$, &($3.val.px)); }
273
 ;
274

    
275
switch_body: /* EMPTY */ { $$ = NULL; }
276
 | switch_body set_item ':' cmds  {
277
     $$ = $2;
278
     $$->data = $4;
279
     $$->left = $1;
280
   }
281
 | switch_body ELSE ':' cmds {
282
     $$ = f_new_tree(); 
283
     $$->from.type = T_VOID; 
284
     $$->to.type = T_VOID;
285
     $$->data = $4;
286
     $$->left = $1;
287
   }
288
 ;
289

    
290
/* CONST '(' expr ')' { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_INT; $$->a2.i = $3; } */
291

    
292
bgp_path_expr:
293
   symbol       { $$ = $1; }   
294
 | '(' term ')' { $$ = $2; }
295
 ;
296

    
297
bgp_path:
298
   PO  bgp_path_tail1 PC  { $$ = $2; }
299
 | '/' bgp_path_tail2 '/' { $$ = $2; }
300
 ;
301

    
302
bgp_path_tail1:
303
   NUM bgp_path_tail1 { $$ = cfg_alloc(sizeof(struct f_path_mask)); $$->next = $2; $$->kind = PM_ASN;      $$->val = $1; }
304
 | '*' bgp_path_tail1 { $$ = cfg_alloc(sizeof(struct f_path_mask)); $$->next = $2; $$->kind = PM_ASTERISK; $$->val  = 0; }
305
 | '?' bgp_path_tail1 { $$ = cfg_alloc(sizeof(struct f_path_mask)); $$->next = $2; $$->kind = PM_QUESTION; $$->val  = 0; }
306
 | bgp_path_expr bgp_path_tail1 { $$ = cfg_alloc(sizeof(struct f_path_mask)); $$->next = $2; $$->kind = PM_ASN_EXPR; $$->val = (uintptr_t) $1; }
307
 |  		      { $$ = NULL; }
308
 ;
309

    
310
bgp_path_tail2:
311
   NUM bgp_path_tail2 { $$ = cfg_alloc(sizeof(struct f_path_mask)); $$->next = $2; $$->kind = PM_ASN;      $$->val = $1; }
312
 | '?' bgp_path_tail2 { $$ = cfg_alloc(sizeof(struct f_path_mask)); $$->next = $2; $$->kind = PM_ASTERISK; $$->val  = 0; }
313
 | 		      { $$ = NULL; }
314
 ;
315

    
316
dpair:
317
   '(' term ',' term ')' {
318
        if (($2->code == 'c') && ($4->code == 'c'))
319
          { 
320
            if (($2->aux != T_INT) || ($4->aux != T_INT))
321
              cf_error( "Can't operate with value of non-integer type in pair constructor" );
322
            $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_PAIR;  $$->a2.i = make_pair($2->a2.i, $4->a2.i);
323
          }
324
	else
325
	  { $$ = f_new_inst(); $$->code = P('m', 'p'); $$->a1.p = $2; $$->a2.p = $4; }
326
    }
327
 ;
328

    
329
constant:
330
   NUM    { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_INT;  $$->a2.i = $1; }
331
 | TRUE   { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_BOOL; $$->a2.i = 1;  }
332
 | FALSE  { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_BOOL; $$->a2.i = 0;  }
333
 | TEXT   { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_STRING; $$->a2.p = $1; }
334
 | fipa	   { NEW_F_VAL; $$ = f_new_inst(); $$->code = 'C'; $$->a1.p = val; *val = $1; }
335
 | fprefix_s {NEW_F_VAL; $$ = f_new_inst(); $$->code = 'C'; $$->a1.p = val; *val = $1; }
336
 | '[' 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" ); }
337
 | '[' fprefix_set ']' { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_PREFIX_SET;  $$->a2.p = $2; }
338
 | ENUM	  { $$ = f_new_inst(); $$->code = 'c'; $$->aux = $1 >> 16; $$->a2.i = $1 & 0xffff; }
339
 | bgp_path { NEW_F_VAL; $$ = f_new_inst(); $$->code = 'C'; val->type = T_PATH_MASK; val->val.path_mask = $1; $$->a1.p = val; }
340
 ;
341

    
342

    
343
/*
344
 *  Maybe there are no dynamic attributes defined by protocols.
345
 *  For such cases, we force the dynamic_attr list to contain
346
 *  at least an invalid token, so it's syntantically correct.
347
 */
348
CF_ADDTO(dynamic_attr, INVALID_TOKEN { $$ = NULL; })
349

    
350
rtadot: /* EMPTY, we are not permitted RTA. prefix */
351
 ;
352

    
353
function_call:
354
   SYM '(' var_list ')' {
355
     struct symbol *sym;
356
     struct f_inst *inst = $3;
357
     if ($1->class != SYM_FUNCTION)
358
       cf_error("You can't call something which is not a function. Really.");
359
     DBG("You are calling function %s\n", $1->name);
360
     $$ = f_new_inst();
361
     $$->code = P('c','a');
362
     $$->a1.p = inst;
363
     $$->a2.p = $1->def;
364
     sym = $1->aux2;
365
     while (sym || inst) {
366
       if (!sym || !inst)
367
	 cf_error("Wrong number of arguments for function %s.", $1->name);
368
       DBG( "You should pass parameter called %s\n", sym->name);
369
       inst->a1.p = sym;
370
       sym = sym->aux2;
371
       inst = inst->next;
372
     }
373
   }
374
 ;
375

    
376
symbol:
377
   SYM {
378
     $$ = f_new_inst();
379
     switch ($1->class) {
380
       case SYM_NUMBER:
381
	$$ = f_new_inst();
382
	$$->code = 'c'; 
383
	$$->aux = T_INT; 
384
	$$->a2.i = $1->aux;
385
	break;
386
       case SYM_IPA:
387
	{ NEW_F_VAL; $$ = f_new_inst(); $$->code = 'C'; $$->a1.p = val; val->type = T_IP; val->val.px.ip = * (ip_addr *) ($1->def); }
388
	break;
389
       case SYM_VARIABLE | T_BOOL:
390
       case SYM_VARIABLE | T_INT:
391
       case SYM_VARIABLE | T_PAIR:
392
       case SYM_VARIABLE | T_STRING:
393
       case SYM_VARIABLE | T_IP:
394
       case SYM_VARIABLE | T_PREFIX:
395
       case SYM_VARIABLE | T_PREFIX_SET:
396
       case SYM_VARIABLE | T_SET:
397
       case SYM_VARIABLE | T_PATH:
398
       case SYM_VARIABLE | T_PATH_MASK:
399
       case SYM_VARIABLE | T_CLIST:
400
	 $$->code = 'V';
401
	 $$->a1.p = $1->def;
402
	 $$->a2.p = $1->name;
403
	 break;
404
       default:
405
	 cf_error("%s: variable expected.", $1->name );
406
     }
407
   }
408

    
409
static_attr:
410
   FROM    { $$ = f_new_inst(); $$->aux = T_IP;         $$->a2.i = OFFSETOF(struct rta, from);   $$->a1.i = 1; }
411

    
412
 | GW      { $$ = f_new_inst(); $$->aux = T_IP;         $$->a2.i = OFFSETOF(struct rta, gw);     $$->a1.i = 1; }
413
 | NET     { $$ = f_new_inst(); $$->aux = T_PREFIX;     $$->a2.i = 0x12345678; /* This is actually ok - T_PREFIX is special-cased. */ }
414
 | PROTO   { $$ = f_new_inst(); $$->aux = T_STRING;     $$->a2.i = 0x12345678; /* T_STRING is also special-cased. */ }
415
 | SOURCE  { $$ = f_new_inst(); $$->aux = T_ENUM_RTS;   $$->a2.i = OFFSETOF(struct rta, source); }
416
 | SCOPE   { $$ = f_new_inst(); $$->aux = T_ENUM_SCOPE; $$->a2.i = OFFSETOF(struct rta, scope);  $$->a1.i = 1; }
417
 | CAST    { $$ = f_new_inst(); $$->aux = T_ENUM_RTC;   $$->a2.i = OFFSETOF(struct rta, cast); }
418
 | DEST    { $$ = f_new_inst(); $$->aux = T_ENUM_RTD;   $$->a2.i = OFFSETOF(struct rta, dest); }
419
 ;
420

    
421
term:
422
   '(' term ')'      { $$ = $2; }
423
 | term '+' term     { $$ = f_new_inst(); $$->code = '+';        $$->a1.p = $1; $$->a2.p = $3; }
424
 | term '-' term     { $$ = f_new_inst(); $$->code = '-';        $$->a1.p = $1; $$->a2.p = $3; }
425
 | term '*' term     { $$ = f_new_inst(); $$->code = '*';        $$->a1.p = $1; $$->a2.p = $3; }
426
 | term '/' term     { $$ = f_new_inst(); $$->code = '/';        $$->a1.p = $1; $$->a2.p = $3; }
427
 | term AND term     { $$ = f_new_inst(); $$->code = '&';        $$->a1.p = $1; $$->a2.p = $3; }
428
 | term OR  term     { $$ = f_new_inst(); $$->code = '|';        $$->a1.p = $1; $$->a2.p = $3; }
429
 | term '=' term     { $$ = f_new_inst(); $$->code = P('=','='); $$->a1.p = $1; $$->a2.p = $3; }
430
 | term NEQ term { $$ = f_new_inst(); $$->code = P('!','=');     $$->a1.p = $1; $$->a2.p = $3; }
431
 | term '<' term     { $$ = f_new_inst(); $$->code = '<';        $$->a1.p = $1; $$->a2.p = $3; }
432
 | term LEQ term { $$ = f_new_inst(); $$->code = P('<','=');     $$->a1.p = $1; $$->a2.p = $3; }
433
 | term '>' term     { $$ = f_new_inst(); $$->code = '<';        $$->a1.p = $3; $$->a2.p = $1; }
434
 | term GEQ term { $$ = f_new_inst(); $$->code = P('<','=');     $$->a1.p = $3; $$->a2.p = $1; }
435
 | term '~' term     { $$ = f_new_inst(); $$->code = '~';        $$->a1.p = $1; $$->a2.p = $3; }
436
 | '!' term { $$ = f_new_inst(); $$->code = '!'; $$->a1.p = $2; }
437
 | DEFINED '(' term ')' { $$ = f_new_inst(); $$->code = P('d','e');  $$->a1.p = $3; }
438

    
439
 | symbol   { $$ = $1; }
440
 | constant { $$ = $1; }
441
 | dpair    { $$ = $1; }
442

    
443
 | PREFERENCE { $$ = f_new_inst(); $$->code = 'P'; }
444

    
445
 | rtadot static_attr { $$ = $2; $$->code = 'a'; }
446

    
447
 | rtadot dynamic_attr { $$ = $2; $$->code = P('e','a'); }
448

    
449
 | term '.' IP { $$ = f_new_inst(); $$->code = P('c','p'); $$->a1.p = $1; $$->aux = T_IP; }
450
 | term '.' LEN { $$ = f_new_inst(); $$->code = 'L'; $$->a1.p = $1; }
451
 | term '.' MASK '(' term ')' { $$ = f_new_inst(); $$->code = P('i','M'); $$->a1.p = $1; $$->a2.p = $5; }
452
 | term '.' FIRST { $$ = f_new_inst(); $$->code = P('a','f'); $$->a1.p = $1; }
453
 | term '.' LAST  { $$ = f_new_inst(); $$->code = P('a','l'); $$->a1.p = $1; }
454

    
455
/* Communities */
456
/* This causes one shift/reduce conflict
457
 | rtadot dynamic_attr '.' ADD '(' term ')' { }
458
 | rtadot dynamic_attr '.' DELETE '(' term ')' { }
459
 | rtadot dynamic_attr '.' CONTAINS '(' term ')' { }
460
 | rtadot dynamic_attr '.' RESET{ }
461
*/
462

    
463
 | '+' EMPTY '+' { $$ = f_new_inst(); $$->code = 'E'; $$->aux = T_PATH; }
464
 | '-' EMPTY '-' { $$ = f_new_inst(); $$->code = 'E'; $$->aux = T_CLIST; }
465
 | PREPEND '(' term ',' term ')' { $$ = f_new_inst(); $$->code = P('A','p'); $$->a1.p = $3; $$->a2.p = $5; } 
466
 | ADD '(' term ',' term ')' { $$ = f_new_inst(); $$->code = P('C','a'); $$->a1.p = $3; $$->a2.p = $5; $$->aux = 'a'; } 
467
 | DELETE '(' term ',' term ')' { $$ = f_new_inst(); $$->code = P('C','a'); $$->a1.p = $3; $$->a2.p = $5; $$->aux = 'd'; }
468

    
469
/* | term '.' LEN { $$->code = P('P','l'); } */
470

    
471
/* function_call is inlined here */
472
 | SYM '(' var_list ')' {
473
     struct symbol *sym;
474
     struct f_inst *inst = $3;
475
     if ($1->class != SYM_FUNCTION)
476
       cf_error("You can't call something which is not a function. Really.");
477
     DBG("You are calling function %s\n", $1->name);
478
     $$ = f_new_inst();
479
     $$->code = P('c','a');
480
     $$->a1.p = inst;
481
     $$->a2.p = $1->def;
482
     sym = $1->aux2;
483
     while (sym || inst) {
484
       if (!sym || !inst)
485
	 cf_error("Wrong number of arguments for function %s.", $1->name);
486
       DBG( "You should pass parameter called %s\n", sym->name);
487
       inst->a1.p = sym;
488
       sym = sym->aux2;
489
       inst = inst->next;
490
     }
491
   }
492
 ;
493

    
494
break_command:
495
   QUITBIRD { $$ = F_QUITBIRD; }
496
 | ACCEPT { $$ = F_ACCEPT; }
497
 | REJECT { $$ = F_REJECT; }
498
 | ERROR { $$ = F_ERROR; }
499
 | PRINT { $$ = F_NOP; }
500
 | PRINTN { $$ = F_NONL; }
501
 ;
502

    
503
print_one:
504
   term { $$ = f_new_inst(); $$->code = 'p'; $$->a1.p = $1; $$->a2.p = NULL; }
505
 ;
506

    
507
print_list: /* EMPTY */ { $$ = NULL; }
508
 | print_one { $$ = $1; }
509
 | print_one ',' print_list {
510
     if ($1) {
511
       $1->next = $3;
512
       $$ = $1;
513
     } else $$ = $3;
514
   }
515
 
516
 ;
517

    
518
var_listn: term { 
519
     $$ = f_new_inst();
520
     $$->code = 's';
521
     $$->a1.p = NULL;
522
     $$->a2.p = $1;
523
     $$->next = NULL;
524
   }
525
 | term ',' var_listn {
526
     $$ = f_new_inst();
527
     $$->code = 's';
528
     $$->a1.p = NULL;
529
     $$->a2.p = $1;
530
     $$->next = $3;
531
   }
532
 ;
533

    
534
var_list: /* EMPTY */ { $$ = NULL; }
535
 | var_listn { $$ = $1; }
536
 ;
537

    
538
cmd:
539
   IF term THEN block {
540
     $$ = f_new_inst();
541
     $$->code = '?';
542
     $$->a1.p = $2;
543
     $$->a2.p = $4;
544
   }
545
 | IF term THEN block ELSE block {
546
     struct f_inst *i = f_new_inst();
547
     i->code = '?';
548
     i->a1.p = $2;
549
     i->a2.p = $4;
550
     $$ = f_new_inst();
551
     $$->code = '?';
552
     $$->a1.p = i;
553
     $$->a2.p = $6;
554
   }
555
 | SYM '=' term ';' {
556
     $$ = f_new_inst();
557
     DBG( "Ook, we'll set value\n" );
558
     if (($1->class & ~T_MASK) != SYM_VARIABLE)
559
       cf_error( "You may set only variables." );
560
     $$->code = 's';
561
     $$->a1.p = $1;
562
     $$->a2.p = $3;
563
   }
564
 | RETURN term ';' {
565
     $$ = f_new_inst();
566
     DBG( "Ook, we'll return the value\n" );
567
     $$->code = 'r';
568
     $$->a1.p = $2;
569
   }
570
 | rtadot dynamic_attr '=' term ';' {
571
     $$ = $2;
572
     $$->code = P('e','S');
573
     $$->a1.p = $4;
574
   }
575
 | rtadot static_attr '=' term ';' {
576
     $$ = $2;
577
     if (!$$->a1.i)
578
       cf_error( "This static attribute is read-only.");
579
     $$->code = P('a','S');
580
     $$->a1.p = $4;
581
   }
582
 | PREFERENCE '=' term ';' {
583
     $$ = f_new_inst();
584
     $$->code = P('P','S');
585
     $$->a1.p = $3;
586
   } 
587
 | UNSET '(' rtadot dynamic_attr ')' ';' {
588
     $$ = $4;
589
     $$->aux = EAF_TYPE_UNDEF | EAF_TEMP;
590
     $$->code = P('e','S');
591
     $$->a1.p = NULL;
592
   }
593
 | break_command print_list ';' { $$ = f_new_inst(); $$->code = P('p',','); $$->a1.p = $2; $$->a2.i = $1; }
594
 | function_call ';' { $$ = $1; }
595
 | CASE term '{' switch_body '}' {
596
      $$ = f_new_inst();
597
      $$->code = P('S','W');
598
      $$->a1.p = $2;
599
      $$->a2.p = build_tree( $4 );
600
   }
601

    
602

    
603
 | rtadot dynamic_attr '.' EMPTY ';' 
604
  { struct f_inst *i = f_new_inst(); i->code = 'E'; i->aux = T_CLIST; $$ = $2; $$->code = P('e','S'); $$->a1.p = i; }
605
 | rtadot dynamic_attr '.' PREPEND '(' term ')' ';'   { $$ = f_generate_complex( P('A','p'), 'x', $2, $6 ); }
606
 | rtadot dynamic_attr '.' ADD '(' term ')' ';'       { $$ = f_generate_complex( P('C','a'), 'a', $2, $6 ); } 
607
 | rtadot dynamic_attr '.' DELETE '(' term ')' ';'    { $$ = f_generate_complex( P('C','a'), 'd', $2, $6 ); } 
608
 ;
609

    
610
CF_END