mirror of
https://gitlab.nic.cz/labs/bird.git
synced 2024-12-22 17:51:53 +00:00
083c43e22e
Filter code used 'aux' integer field of 'symbol' struct to store ptr to next symbol and both 'aux2' and 'def' fields for value. Changed to just 'def' for value and 'aux2' for ptr to next symbol. Also another minor bugfix.
548 lines
16 KiB
Plaintext
548 lines
16 KiB
Plaintext
/*
|
|
* BIRD - filters
|
|
*
|
|
* Copyright 1998--2000 Pavel Machek
|
|
*
|
|
* Can be freely distributed and used under the terms of the GNU GPL.
|
|
*
|
|
FIXME: priority of ! should be lower
|
|
*/
|
|
|
|
CF_HDR
|
|
|
|
CF_DEFINES
|
|
|
|
#define P(a,b) ((a<<8) | b)
|
|
|
|
CF_DECLS
|
|
|
|
CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
|
|
ACCEPT, REJECT, ERROR, QUITBIRD,
|
|
INT, BOOL, IP, PREFIX, PAIR, SET, STRING, BGPMASK, BGPPATH, CLIST,
|
|
IF, THEN, ELSE, CASE,
|
|
TRUE, FALSE,
|
|
FROM, GW, NET, MASK, PROTO, SOURCE, SCOPE, CAST, DEST, PREFERENCE,
|
|
LEN,
|
|
DEFINED,
|
|
ADD, DELETE, CONTAINS, RESET,
|
|
PREPEND, MATCH,
|
|
EMPTY,
|
|
FILTER, WHERE, EVAL)
|
|
|
|
%nonassoc THEN
|
|
%nonassoc ELSE
|
|
|
|
%type <x> term block cmds cmd function_body constant print_one print_list var_list var_listn dynamic_attr static_attr function_call
|
|
%type <f> filter filter_body where_filter
|
|
%type <i> type break_command pair
|
|
%type <e> set_item set_items switch_body
|
|
%type <v> set_atom fprefix fprefix_s fipa
|
|
%type <s> decls declsn one_decl function_params
|
|
%type <h> bgp_path
|
|
|
|
CF_GRAMMAR
|
|
|
|
CF_ADDTO(conf, filter_def)
|
|
filter_def:
|
|
FILTER SYM { cf_push_scope( $2 ); } filter_body {
|
|
$2 = cf_define_symbol($2, SYM_FILTER, $4);
|
|
$4->name = $2->name;
|
|
DBG( "We have new filter defined (%s)\n", $2->name );
|
|
cf_pop_scope();
|
|
}
|
|
;
|
|
|
|
CF_ADDTO(conf, filter_eval)
|
|
filter_eval:
|
|
EVAL term { f_eval_int($2); }
|
|
;
|
|
|
|
type:
|
|
INT { $$ = T_INT; }
|
|
| BOOL { $$ = T_BOOL; }
|
|
| IP { $$ = T_IP; }
|
|
| PREFIX { $$ = T_PREFIX; }
|
|
| PAIR { $$ = T_PAIR; }
|
|
| STRING { $$ = T_STRING; }
|
|
| BGPMASK { $$ = T_PATH_MASK; }
|
|
| BGPPATH { $$ = T_PATH; }
|
|
| CLIST { $$ = T_CLIST; }
|
|
| type SET {
|
|
switch ($1) {
|
|
default:
|
|
cf_error( "You can't create sets of this type." );
|
|
case T_INT: case T_IP: case T_PREFIX: case T_PAIR: ;
|
|
}
|
|
$$ = $1 | T_SET;
|
|
}
|
|
;
|
|
|
|
one_decl:
|
|
type SYM {
|
|
struct f_val * val = cfg_alloc(sizeof(struct f_val));
|
|
val->type = $1;
|
|
$2 = cf_define_symbol($2, SYM_VARIABLE | $1, val);
|
|
DBG( "New variable %s type %x\n", $2->name, $1 );
|
|
$2->aux2 = NULL;
|
|
$$=$2;
|
|
}
|
|
;
|
|
|
|
/* Decls with ';' at the end */
|
|
decls: /* EMPTY */ { $$ = NULL; }
|
|
| one_decl ';' decls {
|
|
$$ = $1;
|
|
$$->aux2 = $3;
|
|
}
|
|
;
|
|
|
|
/* Declarations that have no ';' at the end. */
|
|
declsn: one_decl { $$ = $1; }
|
|
| declsn ';' one_decl {
|
|
$$ = $1;
|
|
$$->aux2 = $3;
|
|
}
|
|
;
|
|
|
|
filter_body:
|
|
function_body {
|
|
struct filter *f = cfg_alloc(sizeof(struct filter));
|
|
f->name = NULL;
|
|
f->root = $1;
|
|
$$ = f;
|
|
}
|
|
;
|
|
|
|
filter:
|
|
SYM {
|
|
if ($1->class != SYM_FILTER) cf_error("No such filter.");
|
|
$$ = $1->def;
|
|
}
|
|
| filter_body
|
|
;
|
|
|
|
where_filter:
|
|
WHERE term {
|
|
/* Construct 'IF term THEN ACCEPT; REJECT;' */
|
|
struct filter *f = cfg_alloc(sizeof(struct filter));
|
|
struct f_inst *i, *acc, *rej;
|
|
acc = f_new_inst(); /* ACCEPT */
|
|
acc->code = P('p',',');
|
|
acc->a1.p = NULL;
|
|
acc->a2.i = F_ACCEPT;
|
|
rej = f_new_inst(); /* REJECT */
|
|
rej->code = P('p',',');
|
|
rej->a1.p = NULL;
|
|
rej->a2.i = F_REJECT;
|
|
i = f_new_inst(); /* IF */
|
|
i->code = '?';
|
|
i->a1.p = $2;
|
|
i->a2.p = acc;
|
|
i->next = rej;
|
|
f->name = NULL;
|
|
f->root = i;
|
|
$$ = f;
|
|
}
|
|
;
|
|
|
|
function_params:
|
|
'(' declsn ')' { DBG( "Have function parameters\n" ); $$=$2; }
|
|
| '(' ')' { $$=NULL; }
|
|
;
|
|
|
|
function_body:
|
|
decls '{' cmds '}' {
|
|
$$ = $3;
|
|
}
|
|
;
|
|
|
|
CF_ADDTO(conf, function_def)
|
|
function_def:
|
|
FUNCTION SYM { DBG( "Beginning of function %s\n", $2->name );
|
|
$2 = cf_define_symbol($2, SYM_FUNCTION, NULL);
|
|
cf_push_scope($2);
|
|
} function_params function_body {
|
|
$2->def = $5;
|
|
$2->aux2 = $4;
|
|
DBG("Hmm, we've got one function here - %s\n", $2->name);
|
|
cf_pop_scope();
|
|
}
|
|
;
|
|
|
|
/* Programs */
|
|
|
|
cmds: /* EMPTY */ { $$ = NULL; }
|
|
| cmd cmds {
|
|
if ($1) {
|
|
if ($1->next)
|
|
bug("Command has next already set");
|
|
$1->next = $2;
|
|
$$ = $1;
|
|
} else $$ = $2;
|
|
}
|
|
;
|
|
|
|
block:
|
|
cmd {
|
|
$$=$1;
|
|
}
|
|
| '{' cmds '}' {
|
|
$$=$2;
|
|
}
|
|
;
|
|
|
|
/*
|
|
* Simple types, their bison value is int
|
|
*/
|
|
pair:
|
|
'(' NUM ',' NUM ')' { $$ = $2 << 16 | $4; }
|
|
;
|
|
|
|
/*
|
|
* Complex types, their bison value is struct f_val
|
|
*/
|
|
fprefix_s:
|
|
IPA '/' NUM %prec '/' {
|
|
if (!ip_is_prefix($1, $3)) cf_error("Invalid network prefix: %I/%d.", $1, $3);
|
|
$$.type = T_PREFIX; $$.val.px.ip = $1; $$.val.px.len = $3;
|
|
}
|
|
;
|
|
|
|
fprefix:
|
|
fprefix_s { $$ = $1; }
|
|
| fprefix_s '+' { $$ = $1; $$.val.px.len |= LEN_PLUS; }
|
|
| fprefix_s '-' { $$ = $1; $$.val.px.len |= LEN_MINUS; }
|
|
| fprefix_s '{' NUM ',' NUM '}' { $$ = $1; $$.val.px.len |= LEN_RANGE | ($3 << 16) | ($5 << 8); }
|
|
;
|
|
|
|
fipa:
|
|
IPA %prec PREFIX_DUMMY { $$.type = T_IP; $$.val.px.ip = $1; }
|
|
;
|
|
|
|
set_atom:
|
|
NUM { $$.type = T_INT; $$.val.i = $1; }
|
|
| pair { $$.type = T_PAIR; $$.val.i = $1; }
|
|
| fipa { $$ = $1; }
|
|
| fprefix { $$ = $1; }
|
|
| ENUM { $$.type = $1 >> 16; $$.val.i = $1 & 0xffff; }
|
|
;
|
|
|
|
set_item:
|
|
set_atom {
|
|
$$ = f_new_tree();
|
|
$$->from = $1;
|
|
if ($1.type != T_PREFIX)
|
|
$$->to = $1;
|
|
else {
|
|
$$->to = $1;
|
|
$$->to.val.px.ip = ipa_or( $$->to.val.px.ip, ipa_not( ipa_mkmask( $$->to.val.px.len ) ));
|
|
}
|
|
}
|
|
| set_atom '.' '.' set_atom {
|
|
$$ = f_new_tree();
|
|
$$->from = $1;
|
|
$$->to = $4;
|
|
if (($1.type == T_PREFIX) || ($4.type == T_PREFIX)) cf_error( "You can't use prefixes for range." );
|
|
}
|
|
;
|
|
|
|
set_items:
|
|
set_item { $$ = $1; }
|
|
| set_items ',' set_item { $$ = $3; $$->left = $1; }
|
|
;
|
|
|
|
switch_body: /* EMPTY */ { $$ = NULL; }
|
|
| set_item ':' cmds switch_body {
|
|
$$ = $1;
|
|
$$->data = $3;
|
|
$$->left = $4;
|
|
}
|
|
| ELSE ':' cmds {
|
|
$$ = f_new_tree();
|
|
$$->from.type = T_VOID;
|
|
$$->to.type = T_VOID;
|
|
$$->data = $3;
|
|
}
|
|
;
|
|
|
|
/* CONST '(' expr ')' { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_INT; $$->a2.i = $3; } */
|
|
|
|
|
|
bgp_path:
|
|
NUM { $$ = cfg_alloc(sizeof(struct f_path_mask)); $$->next = NULL; $$->val = $1; $$->any = 0; }
|
|
| '?' { $$ = cfg_alloc(sizeof(struct f_path_mask)); $$->next = NULL; $$->val = 0; $$->any = 1; }
|
|
| NUM bgp_path { $$ = cfg_alloc(sizeof(struct f_path_mask)); $$->next = $2; $$->val = $1; $$->any = 0; }
|
|
| '?' bgp_path { $$ = cfg_alloc(sizeof(struct f_path_mask)); $$->next = $2; $$->val = 0; $$->any = 1; }
|
|
;
|
|
|
|
constant:
|
|
NUM { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_INT; $$->a2.i = $1; }
|
|
| TRUE { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_BOOL; $$->a2.i = 1; }
|
|
| FALSE { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_BOOL; $$->a2.i = 0; }
|
|
| TEXT { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_STRING; $$->a2.p = $1; }
|
|
| pair { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_PAIR; $$->a2.i = $1; }
|
|
| fipa { NEW_F_VAL; $$ = f_new_inst(); $$->code = 'C'; $$->a1.p = val; *val = $1; }
|
|
| fprefix_s {NEW_F_VAL; $$ = f_new_inst(); $$->code = 'C'; $$->a1.p = val; *val = $1; }
|
|
| '[' 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" ); }
|
|
| ENUM { $$ = f_new_inst(); $$->code = 'c'; $$->aux = $1 >> 16; $$->a2.i = $1 & 0xffff; }
|
|
| '/' bgp_path '/' { NEW_F_VAL; $$ = f_new_inst(); $$->code = 'C'; val->type = T_PATH_MASK; val->val.path_mask = $2; $$->a1.p = val; }
|
|
;
|
|
|
|
/*
|
|
* Maybe there are no dynamic attributes defined by protocols.
|
|
* For such cases, we force the dynamic_attr list to contain
|
|
* at least an invalid token, so it's syntantically correct.
|
|
*/
|
|
CF_ADDTO(dynamic_attr, INVALID_TOKEN { $$ = NULL; })
|
|
|
|
rtadot: /* EMPTY, we are not permitted RTA. prefix */
|
|
;
|
|
|
|
function_call:
|
|
SYM '(' var_list ')' {
|
|
struct symbol *sym;
|
|
struct f_inst *inst = $3;
|
|
if ($1->class != SYM_FUNCTION)
|
|
cf_error("You can't call something which is not a function. Really.");
|
|
DBG("You are calling function %s\n", $1->name);
|
|
$$ = f_new_inst();
|
|
$$->code = P('c','a');
|
|
$$->a1.p = inst;
|
|
$$->a2.p = $1->def;
|
|
sym = $1->aux2;
|
|
while (sym || inst) {
|
|
if (!sym || !inst)
|
|
cf_error("Wrong number of arguments for function %s.", $1->name);
|
|
DBG( "You should pass parameter called %s\n", sym->name);
|
|
inst->a1.p = sym;
|
|
sym = sym->aux2;
|
|
inst = inst->next;
|
|
}
|
|
}
|
|
;
|
|
|
|
static_attr:
|
|
FROM { $$ = f_new_inst(); $$->aux = T_IP; $$->a2.i = OFFSETOF(struct rta, from); $$->a1.i = 1; }
|
|
|
|
| GW { $$ = f_new_inst(); $$->aux = T_IP; $$->a2.i = OFFSETOF(struct rta, gw); $$->a1.i = 1; }
|
|
| NET { $$ = f_new_inst(); $$->aux = T_PREFIX; $$->a2.i = 0x12345678; /* This is actually ok - T_PREFIX is special-cased. */ }
|
|
| PROTO { $$ = f_new_inst(); $$->aux = T_STRING; $$->a2.i = 0x12345678; /* T_STRING is also special-cased. */ }
|
|
| SOURCE { $$ = f_new_inst(); $$->aux = T_ENUM_RTS; $$->a2.i = OFFSETOF(struct rta, source); }
|
|
| SCOPE { $$ = f_new_inst(); $$->aux = T_ENUM_SCOPE; $$->a2.i = OFFSETOF(struct rta, scope); $$->a1.i = 1; }
|
|
| CAST { $$ = f_new_inst(); $$->aux = T_ENUM_RTC; $$->a2.i = OFFSETOF(struct rta, cast); }
|
|
| DEST { $$ = f_new_inst(); $$->aux = T_ENUM_RTD; $$->a2.i = OFFSETOF(struct rta, dest); }
|
|
;
|
|
|
|
term:
|
|
'(' term ')' { $$ = $2; }
|
|
| term '+' term { $$ = f_new_inst(); $$->code = '+'; $$->a1.p = $1; $$->a2.p = $3; }
|
|
| term '-' term { $$ = f_new_inst(); $$->code = '-'; $$->a1.p = $1; $$->a2.p = $3; }
|
|
| term '*' term { $$ = f_new_inst(); $$->code = '*'; $$->a1.p = $1; $$->a2.p = $3; }
|
|
| term '/' term { $$ = f_new_inst(); $$->code = '/'; $$->a1.p = $1; $$->a2.p = $3; }
|
|
| term AND term { $$ = f_new_inst(); $$->code = '&'; $$->a1.p = $1; $$->a2.p = $3; }
|
|
| term OR term { $$ = f_new_inst(); $$->code = '|'; $$->a1.p = $1; $$->a2.p = $3; }
|
|
| term '=' term { $$ = f_new_inst(); $$->code = P('=','='); $$->a1.p = $1; $$->a2.p = $3; }
|
|
| term NEQ term { $$ = f_new_inst(); $$->code = P('!','='); $$->a1.p = $1; $$->a2.p = $3; }
|
|
| term '<' term { $$ = f_new_inst(); $$->code = '<'; $$->a1.p = $1; $$->a2.p = $3; }
|
|
| term LEQ term { $$ = f_new_inst(); $$->code = P('<','='); $$->a1.p = $1; $$->a2.p = $3; }
|
|
| term '>' term { $$ = f_new_inst(); $$->code = '<'; $$->a1.p = $3; $$->a2.p = $1; }
|
|
| term GEQ term { $$ = f_new_inst(); $$->code = P('<','='); $$->a1.p = $3; $$->a2.p = $1; }
|
|
| term '~' term { $$ = f_new_inst(); $$->code = '~'; $$->a1.p = $1; $$->a2.p = $3; }
|
|
| '!' term { $$ = f_new_inst(); $$->code = '!'; $$->a1.p = $2; }
|
|
| DEFINED '(' term ')' { $$ = f_new_inst(); $$->code = P('d','e'); $$->a1.p = $3; }
|
|
|
|
| constant { $$ = $1; }
|
|
| SYM {
|
|
$$ = f_new_inst();
|
|
switch ($1->class) {
|
|
case SYM_NUMBER:
|
|
$$ = f_new_inst();
|
|
$$->code = 'c';
|
|
$$->aux = T_INT;
|
|
$$->a2.i = $1->aux;
|
|
break;
|
|
case SYM_IPA:
|
|
{ NEW_F_VAL; $$ = f_new_inst(); $$->code = 'C'; $$->a1.p = val; val->type = T_IP; val->val.px.ip = * (ip_addr *) ($1->def); }
|
|
break;
|
|
case SYM_VARIABLE | T_INT:
|
|
case SYM_VARIABLE | T_PAIR:
|
|
case SYM_VARIABLE | T_PREFIX:
|
|
case SYM_VARIABLE | T_IP:
|
|
case SYM_VARIABLE | T_PATH_MASK:
|
|
case SYM_VARIABLE | T_PATH:
|
|
case SYM_VARIABLE | T_CLIST:
|
|
$$->code = 'C';
|
|
$$->a1.p = $1->def;
|
|
break;
|
|
default:
|
|
cf_error("%s: variable expected.", $1->name );
|
|
}
|
|
}
|
|
|
|
| PREFERENCE { $$ = f_new_inst(); $$->code = 'P'; }
|
|
|
|
| rtadot static_attr { $$ = $2; $$->code = 'a'; }
|
|
|
|
| rtadot dynamic_attr { $$ = $2; $$->code = P('e','a'); }
|
|
|
|
| term '.' IP { $$ = f_new_inst(); $$->code = P('c','p'); $$->a1.p = $1; $$->aux = T_IP; }
|
|
| term '.' LEN { $$ = f_new_inst(); $$->code = 'L'; $$->a1.p = $1; }
|
|
| term '.' MASK '(' term ')' { $$ = f_new_inst(); $$->code = P('i','M'); $$->a1.p = $1; $$->a2.p = $5; }
|
|
|
|
/* Communities */
|
|
/* This causes one shift/reduce conflict
|
|
| rtadot dynamic_attr '.' ADD '(' term ')' { }
|
|
| rtadot dynamic_attr '.' DELETE '(' term ')' { }
|
|
| rtadot dynamic_attr '.' CONTAINS '(' term ')' { }
|
|
| rtadot dynamic_attr '.' RESET{ }
|
|
*/
|
|
|
|
| '+' EMPTY '+' { $$ = f_new_inst(); $$->code = 'E'; $$->aux = T_PATH; }
|
|
| '-' EMPTY '-' { $$ = f_new_inst(); $$->code = 'E'; $$->aux = T_CLIST; }
|
|
| PREPEND '(' term ',' term ')' { $$ = f_new_inst(); $$->code = P('A','p'); $$->a1.p = $3; $$->a2.p = $5; }
|
|
| ADD '(' term ',' term ')' { $$ = f_new_inst(); $$->code = P('C','a'); $$->a1.p = $3; $$->a2.p = $5; $$->aux = 'a'; }
|
|
| DELETE '(' term ',' term ')' { $$ = f_new_inst(); $$->code = P('C','a'); $$->a1.p = $3; $$->a2.p = $5; $$->aux = 'd'; }
|
|
|
|
/* | term '.' LEN { $$->code = P('P','l'); } */
|
|
|
|
/* function_call is inlined here */
|
|
| SYM '(' var_list ')' {
|
|
struct symbol *sym;
|
|
struct f_inst *inst = $3;
|
|
if ($1->class != SYM_FUNCTION)
|
|
cf_error("You can't call something which is not a function. Really.");
|
|
DBG("You are calling function %s\n", $1->name);
|
|
$$ = f_new_inst();
|
|
$$->code = P('c','a');
|
|
$$->a1.p = inst;
|
|
$$->a2.p = $1->def;
|
|
sym = $1->aux2;
|
|
while (sym || inst) {
|
|
if (!sym || !inst)
|
|
cf_error("Wrong number of arguments for function %s.", $1->name);
|
|
DBG( "You should pass parameter called %s\n", sym->name);
|
|
inst->a1.p = sym;
|
|
sym = sym->aux2;
|
|
inst = inst->next;
|
|
}
|
|
}
|
|
;
|
|
|
|
break_command:
|
|
QUITBIRD { $$ = F_QUITBIRD; }
|
|
| ACCEPT { $$ = F_ACCEPT; }
|
|
| REJECT { $$ = F_REJECT; }
|
|
| ERROR { $$ = F_ERROR; }
|
|
| PRINT { $$ = F_NOP; }
|
|
| PRINTN { $$ = F_NONL; }
|
|
;
|
|
|
|
print_one:
|
|
term { $$ = f_new_inst(); $$->code = 'p'; $$->a1.p = $1; $$->a2.p = NULL; }
|
|
;
|
|
|
|
print_list: /* EMPTY */ { $$ = NULL; }
|
|
| print_one { $$ = $1; }
|
|
| print_one ',' print_list {
|
|
if ($1) {
|
|
$1->next = $3;
|
|
$$ = $1;
|
|
} else $$ = $3;
|
|
}
|
|
|
|
;
|
|
|
|
var_listn: term {
|
|
$$ = f_new_inst();
|
|
$$->code = 's';
|
|
$$->a1.p = NULL;
|
|
$$->a2.p = $1;
|
|
$$->next = NULL;
|
|
}
|
|
| term ',' var_listn {
|
|
$$ = f_new_inst();
|
|
$$->code = 's';
|
|
$$->a1.p = NULL;
|
|
$$->a2.p = $1;
|
|
$$->next = $3;
|
|
}
|
|
;
|
|
|
|
var_list: /* EMPTY */ { $$ = NULL; }
|
|
| var_listn { $$ = $1; }
|
|
;
|
|
|
|
cmd:
|
|
IF term THEN block {
|
|
$$ = f_new_inst();
|
|
$$->code = '?';
|
|
$$->a1.p = $2;
|
|
$$->a2.p = $4;
|
|
}
|
|
| IF term THEN block ELSE block {
|
|
struct f_inst *i = f_new_inst();
|
|
i->code = '?';
|
|
i->a1.p = $2;
|
|
i->a2.p = $4;
|
|
$$ = f_new_inst();
|
|
$$->code = '?';
|
|
$$->a1.p = i;
|
|
$$->a2.p = $6;
|
|
}
|
|
| SYM '=' term ';' {
|
|
$$ = f_new_inst();
|
|
DBG( "Ook, we'll set value\n" );
|
|
if (($1->class & ~T_MASK) != SYM_VARIABLE)
|
|
cf_error( "You may set only variables." );
|
|
$$->code = 's';
|
|
$$->a1.p = $1;
|
|
$$->a2.p = $3;
|
|
}
|
|
| RETURN term ';' {
|
|
$$ = f_new_inst();
|
|
DBG( "Ook, we'll return the value\n" );
|
|
$$->code = 'r';
|
|
$$->a1.p = $2;
|
|
}
|
|
| rtadot dynamic_attr '=' term ';' {
|
|
$$ = $2;
|
|
$$->code = P('e','S');
|
|
$$->a1.p = $4;
|
|
}
|
|
| rtadot static_attr '=' term ';' {
|
|
$$ = $2;
|
|
if (!$$->a1.i)
|
|
cf_error( "This static attribute is read-only.");
|
|
$$->code = P('a','S');
|
|
$$->a1.p = $4;
|
|
}
|
|
| PREFERENCE '=' term ';' {
|
|
$$ = f_new_inst();
|
|
$$->code = P('P','S');
|
|
$$->a1.p = $3;
|
|
}
|
|
| UNSET '(' rtadot dynamic_attr ')' ';' {
|
|
$$ = $4;
|
|
$$->aux = EAF_TYPE_UNDEF | EAF_TEMP;
|
|
$$->code = P('e','S');
|
|
$$->a1.p = NULL;
|
|
}
|
|
| break_command print_list ';' { $$ = f_new_inst(); $$->code = P('p',','); $$->a1.p = $2; $$->a2.i = $1; }
|
|
| function_call ';' { $$ = $1; }
|
|
| CASE term '{' switch_body '}' {
|
|
$$ = f_new_inst();
|
|
$$->code = P('S','W');
|
|
$$->a1.p = $2;
|
|
$$->a2.p = build_tree( $4 );
|
|
}
|
|
|
|
|
|
| rtadot dynamic_attr '.' EMPTY ';'
|
|
{ struct f_inst *i = f_new_inst(); i->code = 'E'; i->aux = T_CLIST; $$ = $2; $$->code = P('e','S'); $$->a1.p = i; }
|
|
| rtadot dynamic_attr '.' PREPEND '(' term ')' ';' { $$ = f_generate_complex( P('A','p'), 'x', $2, $6 ); }
|
|
| rtadot dynamic_attr '.' ADD '(' term ')' ';' { $$ = f_generate_complex( P('C','a'), 'a', $2, $6 ); }
|
|
| rtadot dynamic_attr '.' DELETE '(' term ')' ';' { $$ = f_generate_complex( P('C','a'), 'd', $2, $6 ); }
|
|
;
|
|
|
|
CF_END
|