]> git.ipfire.org Git - thirdparty/bird.git/blobdiff - filter/config.Y
Filter: Split printing and dying
[thirdparty/bird.git] / filter / config.Y
index 94a7e3070cd2bb5d76a3f724d01d4b51946342cc..c40f28d4292bc981d5ab4fd5bd232f9a1775554b 100644 (file)
 
 CF_HDR
 
-CF_DEFINES
+#include "filter/f-inst.h"
+#include "filter/data.h"
 
-#define P(a,b) ((a << 8) | b)
+CF_DEFINES
 
 static inline u32 pair(u32 a, u32 b) { return (a << 16) | b; }
 static inline u32 pair_a(u32 p) { return p >> 16; }
 static inline u32 pair_b(u32 p) { return p & 0xFFFF; }
 
+#define f_generate_complex(fi_code, da, arg) \
+  f_new_inst(FI_EA_SET, f_new_inst(fi_code, f_new_inst(FI_EA_GET, da), arg), da)
 
 /*
  * Sets and their items are during parsing handled as lists, linked
@@ -37,6 +40,7 @@ f_valid_set_type(int type)
   case T_IP:
   case T_EC:
   case T_LC:
+  case T_RD:
     return 1;
 
   default:
@@ -112,7 +116,7 @@ f_new_ec_item(u32 kind, u32 ipv4_used, u32 key, u32 vf, u32 vt)
 {
   u64 fm, to;
 
-  if (ipv4_used || (key >= 0x10000)) {
+  if ((kind != EC_GENERIC) && (ipv4_used || (key >= 0x10000))) {
     check_u16(vf);
     if (vt == EC_ALL)
       vt = 0xFFFF;
@@ -157,56 +161,54 @@ f_new_lc_item(u32 f1, u32 t1, u32 f2, u32 t2, u32 f3, u32 t3)
 }
 
 static inline struct f_inst *
-f_generate_empty(struct f_inst *dyn)
+f_generate_empty(struct f_dynamic_attr dyn)
 {
-  struct f_inst *e = f_new_inst();
-  e->code = 'E';
+  struct f_val empty;
 
-  switch (dyn->aux & EAF_TYPE_MASK) {
+  switch (dyn.type & EAF_TYPE_MASK) {
     case EAF_TYPE_AS_PATH:
-      e->aux = T_PATH;
+      empty = f_const_empty_path;
       break;
     case EAF_TYPE_INT_SET:
-      e->aux = T_CLIST;
+      empty = f_const_empty_clist;
       break;
     case EAF_TYPE_EC_SET:
-      e->aux = T_ECLIST;
+      empty = f_const_empty_eclist;
       break;
     case EAF_TYPE_LC_SET:
-      e->aux = T_LCLIST;
+      empty = f_const_empty_lclist;
       break;
     default:
       cf_error("Can't empty that attribute");
   }
 
-  dyn->code = P('e','S');
-  dyn->a1.p = e;
-  return dyn;
+  return f_new_inst(FI_EA_SET, f_new_inst(FI_CONSTANT, empty), dyn);
 }
 
+#if 0
 
 static inline struct f_inst *
 f_generate_dpair(struct f_inst *t1, struct f_inst *t2)
 {
   struct f_inst *rv;
 
-  if ((t1->code == 'c') && (t2->code == 'c')) {
-    if ((t1->aux != T_INT) || (t2->aux != T_INT))
+  if ((t1->fi_code == FI_CONSTANT) && (t2->fi_code == FI_CONSTANT)) {
+    if ((t1->val.type != T_INT) || (t2->val.type != T_INT))
       cf_error( "Can't operate with value of non-integer type in pair constructor");
 
-    check_u16(t1->a2.i);
-    check_u16(t2->a2.i);
+    check_u16(t1->a[1].i);
+    check_u16(t2->a[1].i);
 
-    rv = f_new_inst();
-    rv->code = 'c';
-    rv->aux = T_PAIR;
-    rv->a2.i = pair(t1->a2.i, t2->a2.i);
+    rv = f_new_inst(FI_CONSTANT);
+    rv->val = (struct f_val) {
+      .type = T_PAIR,
+      .val.i = pair(t1->a[1].i, t2->a[1].i),
+    };
   }
   else {
-    rv = f_new_inst();
-    rv->code = P('m', 'p');
-    rv->a1.p = t1;
-    rv->a2.p = t2;
+    rv = f_new_inst(FI_PAIR_CONSTRUCT);
+    rv->a[0].p = t1;
+    rv->a[1].p = t2;
   }
 
   return rv;
@@ -219,28 +221,14 @@ f_generate_ec(u16 kind, struct f_inst *tk, struct f_inst *tv)
   int c1 = 0, c2 = 0, ipv4_used = 0;
   u32 key = 0, val2 = 0;
 
-  if (tk->code == 'c') {
-    c1 = 1;
-
-    if (tk->aux == T_INT) {
-      ipv4_used = 0; key = tk->a2.i;
-    }
-    else if (tk->aux == T_QUAD) {
-      ipv4_used = 1; key = tk->a2.i;
-    }
-    else
-      cf_error("Can't operate with key of non-integer/IPv4 type in EC constructor");
-  }
-
-  /* IP->Quad implicit conversion */
-  else if (tk->code == 'C') {
+  if (tk->fi_code == FI_CONSTANT) {
     c1 = 1;
-    struct f_val *val = tk->a1.p;
+    struct f_val *val = &(tk->val);
 
     if (val->type == T_INT) {
       ipv4_used = 0; key = val->val.i;
     }
-    else if (val->type == T_QUAD) {
+    else if (tk->val.type == T_QUAD) {
       ipv4_used = 1; key = val->val.i;
     }
     else if ((val->type == T_IP) && ipa_is_ip4(val->val.ip)) {
@@ -250,11 +238,11 @@ f_generate_ec(u16 kind, struct f_inst *tk, struct f_inst *tv)
       cf_error("Can't operate with key of non-integer/IPv4 type in EC constructor");
   }
 
-  if (tv->code == 'c') {
-    if (tv->aux != T_INT)
+  if (tv->fi_code == FI_CONSTANT) {
+    if (tv->val.type != T_INT)
       cf_error("Can't operate with value of non-integer type in EC constructor");
     c2 = 1;
-    val2 = tv->a2.i;
+    val2 = tv->val.val.i;
   }
 
   if (c1 && c2) {
@@ -275,19 +263,17 @@ f_generate_ec(u16 kind, struct f_inst *tk, struct f_inst *tv)
       ec = ec_as4(kind, key, val2);
     }
 
-    NEW_F_VAL;
-    rv = f_new_inst();
-    rv->code = 'C';
-    rv->a1.p = val;
-    val->type = T_EC;
-    val->val.ec = ec;
+    rv = f_new_inst(FI_CONSTANT);
+    rv->val = (struct f_val) {
+      .type = T_EC,
+      .val.ec = ec,
+    };
   }
   else {
-    rv = f_new_inst();
-    rv->code = P('m','c');
+    rv = f_new_inst(FI_EC_CONSTRUCT);
     rv->aux = kind;
-    rv->a1.p = tk;
-    rv->a2.p = tv;
+    rv->a[0].p = tk;
+    rv->a[1].p = tv;
   }
 
   return rv;
@@ -298,31 +284,60 @@ f_generate_lc(struct f_inst *t1, struct f_inst *t2, struct f_inst *t3)
 {
   struct f_inst *rv;
 
-  if ((t1->code == 'c') && (t2->code == 'c') && (t3->code == 'c')) {
-    if ((t1->aux != T_INT) || (t2->aux != T_INT) || (t3->aux != T_INT))
+  if ((t1->fi_code == FI_CONSTANT) && (t2->fi_code == FI_CONSTANT) && (t3->fi_code == FI_CONSTANT)) {
+    if ((t1->val.type != T_INT) || (t2->val.type != T_INT) || (t3->val.type != T_INT))
       cf_error( "LC - Can't operate with value of non-integer type in tuple constructor");
 
-    rv = f_new_inst();
-    rv->code = 'C';
-
-    NEW_F_VAL;
-    rv->a1.p = val;
-    val->type = T_LC;
-    val->val.lc = (lcomm) { t1->a2.i, t2->a2.i, t3->a2.i };
+    rv = f_new_inst(FI_CONSTANT);
+    rv->val = (struct f_val) {
+      .type = T_LC,
+      .val.lc = (lcomm) { t1->a[1].i, t2->a[1].i, t3->a[1].i },
+    };
   }
   else
   {
-    rv = cfg_allocz(sizeof(struct f_inst3));
-    rv->lineno = ifs->lino;
-    rv->code = P('m','l');
-    rv->a1.p = t1;
-    rv->a2.p = t2;
-    INST3(rv).p = t3;
+    rv = f_new_inst(FI_LC_CONSTRUCT);
+    rv->a[0].p = t1;
+    rv->a[1].p = t2;
+    rv->a[2].p = t3;
   }
 
   return rv;
 }
 
+static inline struct f_inst *
+f_generate_path_mask(struct f_inst *t)
+{
+  uint len = 0;
+  uint dyn = 0;
+  for (const struct f_inst *tt = t; tt; tt = tt->next) {
+    if (tt->fi_code != FI_CONSTANT)
+      dyn++;
+    len++;
+  }
+
+  if (dyn) {
+    struct f_inst *pmc = f_new_inst(FI_PATHMASK_CONSTRUCT);
+    pmc->a[0].p = t;
+    pmc->a[1].i = len;
+    return pmc;
+  }
+
+  struct f_path_mask *pm = cfg_allocz(sizeof(struct f_path_mask) + len * sizeof(struct f_path_mask_item));
+
+  uint i = 0;
+  for (const struct f_inst *tt = t; tt; tt = tt->next)
+    pm->item[i++] = tt->val.val.pmi;
+
+  pm->len = i;
+  struct f_inst *pmc = f_new_inst(FI_CONSTANT);
+  pmc->val = (struct f_val) { .type = T_PATH_MASK, .val.path_mask = pm, };
+
+  return pmc;
+}
+
+#endif
+
 /*
  * Remove all new lines and doubled whitespaces
  * and convert all tabulators to spaces
@@ -371,84 +386,113 @@ assert_copy_expr(const char *start, size_t len)
 static struct f_inst *
 assert_done(struct f_inst *expr, const char *start, const char *end)
 {
-  struct f_inst *i;
-  i = f_new_inst();
-  i->code = P('a','s');
-  i->a1.p = expr;
+  return f_new_inst(FI_ASSERT, expr,
+    (end >= start) ?
+      assert_copy_expr(start, end - start + 1)
+    : "???");
+}
 
-  if (end >= start)
-  {
-    i->a2.p = assert_copy_expr(start, end - start + 1);
-  }
-  else
-  {
-    /* this is a break of lexer buffer */
-    i->a2.p = "???";
+static struct f_inst *
+assert_assign(struct f_lval *lval, struct f_inst *expr, const char *start, const char *end)
+{
+  struct f_inst *setter, *getter, *checker;
+  switch (lval->type) {
+    case F_LVAL_VARIABLE:
+      setter = f_new_inst(FI_VAR_SET, expr, lval->sym);
+      getter = f_new_inst(FI_VAR_GET, lval->sym);
+      break;
+    case F_LVAL_PREFERENCE:
+      setter = f_new_inst(FI_PREF_SET, expr);
+      getter = f_new_inst(FI_PREF_GET);
+      break;
+    case F_LVAL_SA:
+      setter = f_new_inst(FI_RTA_SET, expr, lval->sa);
+      getter = f_new_inst(FI_RTA_GET, lval->sa);
+      break;
+    case F_LVAL_EA:
+      setter = f_new_inst(FI_EA_SET, expr, lval->da);
+      getter = f_new_inst(FI_EA_GET, lval->da);
+      break;
+    default:
+      bug("Unknown lval type");
   }
 
-  return i;
+  checker = f_new_inst(FI_EQ, expr, getter);
+  setter->next = checker;
+  
+  return assert_done(setter, start, end);
 }
 
 CF_DECLS
 
 CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
        ACCEPT, REJECT, ERROR, QUITBIRD,
-       INT, BOOL, IP, PREFIX, PAIR, QUAD, EC, LC,
+       INT, BOOL, IP, TYPE, PREFIX, RD, PAIR, QUAD, EC, LC,
        SET, STRING, BGPMASK, BGPPATH, CLIST, ECLIST, LCLIST,
        IF, THEN, ELSE, CASE,
        TRUE, FALSE, RT, RO, UNKNOWN, GENERIC,
        FROM, GW, NET, MASK, PROTO, SOURCE, SCOPE, DEST, IFNAME, IFINDEX,
        PREFERENCE,
-       ROA_CHECK, ASN,
+       ROA_CHECK, ASN, SRC,
+       IS_V4, IS_V6,
        LEN, MAXLEN,
        DEFINED,
        ADD, DELETE, CONTAINS, RESET,
        PREPEND, FIRST, LAST, LAST_NONAGGREGATED, MATCH,
        EMPTY,
-       FILTER, WHERE, EVAL,
-       BT_ASSERT, BT_TEST_SUITE, FORMAT)
+       FILTER, WHERE, EVAL, ATTRIBUTE,
+       BT_ASSERT, BT_TEST_SUITE, BT_CHECK_ASSIGN, BT_TEST_SAME, FORMAT)
 
 %nonassoc THEN
 %nonassoc ELSE
 
-%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
-%type <f> filter filter_body where_filter
-%type <i> type break_command ec_kind
+%type <xp> cmds_int
+%type <x> term block cmd cmds constant constructor print_list var_list function_call symbol_value bgp_path_expr bgp_path bgp_path_tail
+%type <fda> dynamic_attr
+%type <fsa> static_attr
+%type <f> filter where_filter
+%type <fl> filter_body function_body
+%type <flv> lvalue
+%type <i> type function_params declsn decls
+%type <ecs> ec_kind
+%type <fret> break_command 
 %type <i32> cnum
 %type <e> pair_item ec_item lc_item set_item switch_item set_items switch_items switch_body
 %type <trie> fprefix_set
 %type <v> set_atom switch_atom fipa
 %type <px> fprefix
-%type <s> decls declsn one_decl function_params
-%type <h> bgp_path bgp_path_tail1 bgp_path_tail2
 %type <t> get_cf_position
 
 CF_GRAMMAR
 
-CF_ADDTO(conf, filter_def)
+conf: filter_def ;
 filter_def:
-   FILTER SYM { $2 = cf_define_symbol($2, SYM_FILTER, NULL); cf_push_scope( $2 ); }
+   FILTER CF_SYM_VOID { $2 = cf_define_symbol($2, SYM_FILTER, filter, NULL); cf_push_scope( $2 ); }
      filter_body {
-     $2->def = $4;
-     $4->name = $2->name;
-     DBG( "We have new filter defined (%s)\n", $2->name );
+     struct filter *f = cfg_alloc(sizeof(struct filter));
+     *f = (struct filter) { .sym = $2, .root = $4 };
+     $2->filter = f;
+
      cf_pop_scope();
    }
  ;
 
-CF_ADDTO(conf, filter_eval)
+conf: filter_eval ;
 filter_eval:
-   EVAL term { f_eval_int($2); }
+   EVAL term { f_eval_int(f_linearize($2)); }
  ;
 
-CF_ADDTO(conf, bt_test_suite)
-bt_test_suite:
- BT_TEST_SUITE '(' SYM ',' text ')' {
-  if (!($3->class & SYM_FUNCTION))
-    cf_error("Function expected");
+conf: custom_attr ;
+custom_attr: ATTRIBUTE type CF_SYM_VOID ';' {
+  cf_define_symbol($3, SYM_ATTRIBUTE, attribute, ca_lookup(new_config->pool, $3->name, $2)->fda);
+};
 
-  struct f_bt_test_suite *t = cfg_alloc(sizeof(struct f_bt_test_suite));
-  t->fn = $3->def;
+conf: bt_test_suite ;
+bt_test_suite:
+ BT_TEST_SUITE '(' CF_SYM_KNOWN ',' text ')' {
+  cf_assert_symbol($3, SYM_FUNCTION);
+  struct f_bt_test_suite *t = cfg_allocz(sizeof(struct f_bt_test_suite));
+  t->fn = $3->function;
   t->fn_name = $3->name;
   t->dsc = $5;
 
@@ -456,10 +500,26 @@ bt_test_suite:
  }
  ;
 
+conf: bt_test_same ;
+bt_test_same:
+ BT_TEST_SAME '(' CF_SYM_KNOWN ',' CF_SYM_KNOWN ',' NUM ')' {
+  cf_assert_symbol($3, SYM_FUNCTION);
+  cf_assert_symbol($5, SYM_FUNCTION);
+  struct f_bt_test_suite *t = cfg_allocz(sizeof(struct f_bt_test_suite));
+  t->fn = $3->function;
+  t->cmp = $5->function;
+  t->result = $7;
+  t->fn_name = $3->name;
+  t->dsc = $5->name;
+  add_tail(&new_config->tests, &t->n);
+ }
+ ;
+
 type:
    INT { $$ = T_INT; }
  | BOOL { $$ = T_BOOL; }
  | IP { $$ = T_IP; }
+ | RD { $$ = T_RD; }
  | PREFIX { $$ = T_NET; }
  | PAIR { $$ = T_PAIR; }
  | QUAD { $$ = T_QUAD; }
@@ -478,6 +538,7 @@ type:
          case T_QUAD:
          case T_EC:
          case T_LC:
+         case T_RD:
          case T_IP:
               $$ = T_SET;
               break;
@@ -492,116 +553,89 @@ type:
    }
  ;
 
-one_decl:
-   type SYM {
-     struct f_val * val = cfg_alloc(sizeof(struct f_val));
-     val->type = T_VOID;
-     $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 with ';' at the end */
+decls:
+   /* EMPTY */ { $$ = 0; }
+ | declsn ';' { $$ = $1; }
  ;
 
 /* Declarations that have no ';' at the end. */
-declsn: one_decl { $$ = $1; }
| one_decl ';' declsn {
-     $$ = $1;
-     $$->aux2 = $3;
+declsn:
  type CF_SYM_VOID {
+     cf_define_symbol($2, SYM_VARIABLE | $1, offset, $2->scope->slots++);
+     $$ = $2->scope->slots;
    }
- ;
-
-filter_body:
-   function_body {
-     struct filter *f = cfg_alloc(sizeof(struct filter));
-     f->name = NULL;
-     f->root = $1;
-     $$ = f;
+ | declsn ';' type CF_SYM_VOID {
+     if ($4->scope->slots >= 0xff) cf_error("Too many declarations, at most 255 allowed");
+     cf_define_symbol($4, SYM_VARIABLE | $3, offset, $4->scope->slots++);
+     $$ = $4->scope->slots;
    }
  ;
 
+filter_body: function_body ;
+
 filter:
-   SYM {
-     if ($1->class != SYM_FILTER) cf_error("No such filter.");
-     $$ = $1->def;
+   CF_SYM_KNOWN {
+     cf_assert_symbol($1, SYM_FILTER);
+     $$ = $1->filter;
+   }
+ | filter_body {
+     struct filter *f = cfg_alloc(sizeof(struct filter));
+     *f = (struct filter) { .root = $1 };
+     $$ = f;
    }
- | 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;
-  }
+     /* Construct 'IF term THEN { ACCEPT; } ELSE { REJECT; }' */
+     $$ = f_new_where($2);
+   }
  ;
 
 function_params:
-   '(' declsn ')' { DBG( "Have function parameters\n" ); $$=$2; }
- | '(' ')' { $$=NULL; }
+   '(' declsn ')' { $$ = $2; }
+ | '(' ')' { $$ = 0; }
  ;
 
 function_body:
    decls '{' cmds '}' {
-     if ($1) {
-       /* Prepend instruction to clear local variables */
-       $$ = f_new_inst();
-       $$->code = P('c','v');
-       $$->a1.p = $1;
-       $$->next = $3;
-     } else
-       $$ = $3;
+     $$ = f_linearize($3);
+     $$->vars = $1;
    }
  ;
 
-CF_ADDTO(conf, function_def)
+conf: function_def ;
 function_def:
-   FUNCTION SYM { DBG( "Beginning of function %s\n", $2->name );
-     $2 = cf_define_symbol($2, SYM_FUNCTION, NULL);
+   FUNCTION CF_SYM_VOID { DBG( "Beginning of function %s\n", $2->name );
+     $2 = cf_define_symbol($2, SYM_FUNCTION, 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);
+     $5->vars -= $4;
+     $5->args = $4;
+     $2->function = $5;
      cf_pop_scope();
    }
  ;
 
 /* Programs */
 
-/* Hack: $$ of cmds_int is the last node.
-   $$->next of cmds_int is temporary used for the first node */
-
 cmds: /* EMPTY */ { $$ = NULL; }
- | cmds_int { $$ = $1->next; $1->next = NULL; }
+ | cmds_int { $$ = $1.begin; }
  ;
 
-cmds_int: cmd { $$ = $1; $1->next = $1; }
- | cmds_int cmd { $$ = $2; $2->next = $1->next ; $1->next = $2; }
+cmds_int: cmd {
+  $$.begin = $$.end = $1;
+  while ($$.end->next)
+    $$.end = $$.end->next;
+  }
+ | cmds_int cmd {
+  $$.begin = $1.begin;
+  $1.end->next = $2;
+  $$.end = $2;
+  while ($$.end->next)
+    $$.end = $$.end->next;
+ }
  ;
 
 block:
@@ -631,29 +665,30 @@ fipa:
  */
 
 set_atom:
-   NUM   { $$.type = T_INT; $$.val.i = $1; }
- | fipa  { $$ = $1; }
- | ENUM  { $$.type = pair_a($1); $$.val.i = pair_b($1); }
+   NUM    { $$.type = T_INT; $$.val.i = $1; }
+ | fipa   { $$ = $1; }
+ | VPN_RD { $$.type = T_RD; $$.val.ec = $1; }
+ | ENUM   { $$.type = pair_a($1); $$.val.i = pair_b($1); }
  | '(' term ')' {
-     $$ = f_eval($2, cfg_mem);
+     if (f_eval(f_linearize($2), cfg_mem, &($$)) > F_RETURN) cf_error("Runtime error");
      if (!f_valid_set_type($$.type)) cf_error("Set-incompatible type");
    }
- | SYM {
-     if (!cf_symbol_is_constant($1)) cf_error("%s: constant expected", $1->name);
+ | CF_SYM_KNOWN {
+     cf_assert_symbol($1, SYM_CONSTANT);
      if (!f_valid_set_type(SYM_TYPE($1))) cf_error("%s: set-incompatible type", $1->name);
-     $$ = *(struct f_val *)($1->def);
+     $$ = *$1->val;
    }
  ;
 
 switch_atom:
    NUM   { $$.type = T_INT; $$.val.i = $1; }
- | '(' term ')' { $$.type = T_INT; $$.val.i = f_eval_int($2); }
+ | '(' term ')' { $$.type = T_INT; $$.val.i = f_eval_int(f_linearize($2)); }
  | fipa  { $$ = $1; }
  | ENUM  { $$.type = pair_a($1); $$.val.i = pair_b($1); }
  ;
 
 cnum:
-   term { $$ = f_eval_int($1); }
+   term { $$ = f_eval_int(f_linearize($1)); }
 
 pair_item:
    '(' cnum ',' cnum ')'               { $$ = f_new_pair_item($2, $2, $4, $4); }
@@ -726,8 +761,8 @@ fprefix:
  | net_ip_ '-' { $$.net = $1; $$.lo = 0; $$.hi = $1.pxlen; }
  | net_ip_ '{' NUM ',' NUM '}' {
      $$.net = $1; $$.lo = $3; $$.hi = $5;
-     if ((0 > $3) || ($3 > $5) || ($5 > net_max_prefix_length[$1.type]))
-       cf_error("Invalid prefix pattern range: {%d, %d}", $3, $5);
+     if (($3 > $5) || ($5 > net_max_prefix_length[$1.type]))
+       cf_error("Invalid prefix pattern range: {%u, %u}", $3, $5);
    }
  ;
 
@@ -740,208 +775,190 @@ switch_body: /* EMPTY */ { $$ = NULL; }
  | switch_body switch_items ':' cmds  {
      /* Fill data fields */
      struct f_tree *t;
+     struct f_line *line = f_linearize($4);
      for (t = $2; t; t = t->left)
-       t->data = $4;
+       t->data = line;
      $$ = f_merge_items($1, $2);
    }
  | switch_body ELSECOL cmds {
      struct f_tree *t = f_new_tree();
      t->from.type = t->to.type = T_VOID;
      t->right = t;
-     t->data = $3;
+     t->data = f_linearize($3);
      $$ = f_merge_items($1, t);
  }
  ;
 
-/* CONST '(' expr ')' { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_INT; $$->a2.i = $3; } */
-
 bgp_path_expr:
-   symbol       { $$ = $1; }
+   symbol_value { $$ = $1; }
  | '(' term ')' { $$ = $2; }
  ;
 
 bgp_path:
-   PO  bgp_path_tail1 PC  { $$ = $2; }
- | '/' bgp_path_tail2 '/' { $$ = $2; }
+   PO  bgp_path_tail PC  { $$ = $2; }
  ;
 
-bgp_path_tail1:
-   NUM bgp_path_tail1          { $$ = cfg_allocz(sizeof(struct f_path_mask)); $$->next = $2; $$->kind = PM_ASN; $$->val = $1; }
- | NUM DDOT NUM bgp_path_tail1 { $$ = cfg_allocz(sizeof(struct f_path_mask)); $$->next = $4; $$->kind = PM_ASN_RANGE; $$->val = $1; $$->val2 = $3; }
- | '*' bgp_path_tail1          { $$ = cfg_allocz(sizeof(struct f_path_mask)); $$->next = $2; $$->kind = PM_ASTERISK; }
- | '?' bgp_path_tail1          { $$ = cfg_allocz(sizeof(struct f_path_mask)); $$->next = $2; $$->kind = PM_QUESTION; }
- | bgp_path_expr bgp_path_tail1        { $$ = cfg_allocz(sizeof(struct f_path_mask)); $$->next = $2; $$->kind = PM_ASN_EXPR; $$->val = (uintptr_t) $1; }
+bgp_path_tail:
+   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;  }
+ | 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; }
+ | '*' bgp_path_tail           { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_PATH_MASK_ITEM, .val.pmi = { .kind = PM_ASTERISK }, }); $$->next = $2; }
+ | '?' bgp_path_tail           { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_PATH_MASK_ITEM, .val.pmi = { .kind = PM_QUESTION }, }); $$->next = $2; }
+ | bgp_path_expr bgp_path_tail { $$ = $1; $$->next = $2; }
  |                             { $$ = NULL; }
  ;
 
-bgp_path_tail2:
-   NUM bgp_path_tail2 { $$ = cfg_allocz(sizeof(struct f_path_mask)); $$->next = $2; $$->kind = PM_ASN; $$->val = $1; }
- | '?' bgp_path_tail2 { $$ = cfg_allocz(sizeof(struct f_path_mask)); $$->next = $2; $$->kind = PM_ASTERISK; }
- |                   { $$ = NULL; }
- ;
-
 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; }
- | fipa          { NEW_F_VAL; $$ = f_new_inst(); $$->code = 'C'; $$->a1.p = val; *val = $1; }
- | net_   { NEW_F_VAL; $$ = f_new_inst(); $$->code = 'C'; val->type = T_NET; val->val.net = $1; $$->a1.p = val; }
- | '[' 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" ); }
- | '[' fprefix_set ']' { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_PREFIX_SET;  $$->a2.p = $2; }
- | 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 = $1; $$->a1.p = val; }
+   NUM    { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_INT, .val.i = $1, }); }
+ | TRUE   { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_BOOL, .val.i = 1, }); }
+ | FALSE  { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_BOOL, .val.i = 0, }); }
+ | TEXT   { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_STRING, .val.s = $1, }); }
+ | fipa          { $$ = f_new_inst(FI_CONSTANT, $1); }
+ | VPN_RD { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_RD, .val.ec = $1, }); }
+ | net_   { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_NET, .val.net = $1, }); }
+ | '[' set_items ']' {
+     DBG( "We've got a set here..." );
+     $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_SET, .val.t = build_tree($2), });
+     DBG( "ook\n" );
+ }
+ | '[' fprefix_set ']' { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_PREFIX_SET, .val.ti = $2, }); }
+ | ENUM          { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = $1 >> 16, .val.i = $1 & 0xffff, }); }
  ;
 
 constructor:
-   '(' term ',' term ')' { $$ = f_generate_dpair($2, $4); }
- | '(' ec_kind ',' term ',' term ')' { $$ = f_generate_ec($2, $4, $6); }
- | '(' term ',' term ',' term ')' { $$ = f_generate_lc($2, $4, $6); }
+   '(' term ',' term ')' { $$ = f_new_inst(FI_PAIR_CONSTRUCT, $2, $4); }
+ | '(' ec_kind ',' term ',' term ')' { $$ = f_new_inst(FI_EC_CONSTRUCT, $4, $6, $2); }
+ | '(' term ',' term ',' term ')' { $$ = f_new_inst(FI_LC_CONSTRUCT, $2, $4, $6); }
+ | bgp_path { $$ = f_new_inst(FI_PATHMASK_CONSTRUCT, $1, 0); }
  ;
 
 
-/*
- *  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 is syntantically correct.
- */
-CF_ADDTO(dynamic_attr, INVALID_TOKEN { $$ = NULL; })
-
-rtadot: /* EMPTY, we are not permitted RTA. prefix */
- ;
+/* This generates the function_call variable list backwards. */
+var_list: /* EMPTY */ { $$ = NULL; }
+ | term { $$ = $1; }
+ | var_list ',' term { $$ = $3; $$->next = $1; }
 
 function_call:
-   SYM '(' var_list ')' {
-     struct symbol *sym;
-     struct f_inst *inst = $3;
+   CF_SYM_KNOWN '(' var_list ')' {
      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;
-     }
-   }
- ;
 
-symbol:
-   SYM {
-     $$ = f_new_inst();
+     struct f_inst *fc = f_new_inst(FI_CALL, $1);
+     uint args = 0;
+     while ($3) {
+       args++;
+       struct f_inst *tmp = $3->next;
+       $3->next = fc;
 
-     switch ($1->class & 0xff00) {
-       case SYM_CONSTANT: $$->code = 'C'; break;
-       case SYM_VARIABLE: $$->code = 'V'; break;
-       default: cf_error("%s: variable expected.", $1->name);
+       fc = $3;
+       $3 = tmp;
      }
 
-     $$->a1.p = $1->def;
-     $$->a2.p = $1->name;
+     if (args != $1->function->args)
+       cf_error("Function call '%s' got %u arguments, need %u arguments.",
+          $1->name, args, $1->function->args);
+
+     $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_VOID });
+     $$->next = fc;
    }
+ ;
+
+symbol_value: CF_SYM_KNOWN
+  {
+    switch ($1->class) {
+      case SYM_CONSTANT_RANGE:
+       $$ = f_new_inst(FI_CONSTANT, *($1->val));
+       break;
+      case SYM_VARIABLE_RANGE:
+       $$ = f_new_inst(FI_VAR_GET, $1);
+       break;
+      case SYM_ATTRIBUTE:
+       $$ = f_new_inst(FI_EA_GET, *$1->attribute);
+       break;
+      default:
+       cf_error("Can't get value of symbol %s", $1->name);
+    }
+  }
+ ;
 
 static_attr:
-   FROM    { $$ = f_new_inst(); $$->aux = T_IP;         $$->a2.i = SA_FROM;    $$->a1.i = 1; }
- | GW      { $$ = f_new_inst(); $$->aux = T_IP;         $$->a2.i = SA_GW;      $$->a1.i = 1; }
- | NET     { $$ = f_new_inst(); $$->aux = T_NET;        $$->a2.i = SA_NET; }
- | PROTO   { $$ = f_new_inst(); $$->aux = T_STRING;     $$->a2.i = SA_PROTO; }
- | SOURCE  { $$ = f_new_inst(); $$->aux = T_ENUM_RTS;   $$->a2.i = SA_SOURCE; }
- | SCOPE   { $$ = f_new_inst(); $$->aux = T_ENUM_SCOPE; $$->a2.i = SA_SCOPE;   $$->a1.i = 1; }
- | DEST    { $$ = f_new_inst(); $$->aux = T_ENUM_RTD;   $$->a2.i = SA_DEST;    $$->a1.i = 1; }
- | IFNAME  { $$ = f_new_inst(); $$->aux = T_STRING;     $$->a2.i = SA_IFNAME; }
- | IFINDEX { $$ = f_new_inst(); $$->aux = T_INT;        $$->a2.i = SA_IFINDEX; }
+   FROM    { $$ = f_new_static_attr(T_IP,         SA_FROM,     0); }
+ | GW      { $$ = f_new_static_attr(T_IP,         SA_GW,       0); }
+ | NET     { $$ = f_new_static_attr(T_NET,       SA_NET,       1); }
+ | PROTO   { $$ = f_new_static_attr(T_STRING,     SA_PROTO,    1); }
+ | SOURCE  { $$ = f_new_static_attr(T_ENUM_RTS,   SA_SOURCE,   1); }
+ | SCOPE   { $$ = f_new_static_attr(T_ENUM_SCOPE, SA_SCOPE,    0); }
+ | DEST    { $$ = f_new_static_attr(T_ENUM_RTD,   SA_DEST,     0); }
+ | IFNAME  { $$ = f_new_static_attr(T_STRING,     SA_IFNAME,   0); }
+ | IFINDEX { $$ = f_new_static_attr(T_INT,        SA_IFINDEX,  1); }
  ;
 
 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 NMA term { $$ = f_new_inst(); $$->code = P('!','~');     $$->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; }
-
- | symbol   { $$ = $1; }
+   '(' term ')'                { $$ = $2; }
+ | term '+' term       { $$ = f_new_inst(FI_ADD, $1, $3); }
+ | term '-' term       { $$ = f_new_inst(FI_SUBTRACT, $1, $3); }
+ | term '*' term       { $$ = f_new_inst(FI_MULTIPLY, $1, $3); }
+ | term '/' term       { $$ = f_new_inst(FI_DIVIDE, $1, $3); }
+ | term AND term       { $$ = f_new_inst(FI_AND, $1, $3); }
+ | term OR  term       { $$ = f_new_inst(FI_OR, $1, $3); }
+ | term '=' term       { $$ = f_new_inst(FI_EQ, $1, $3); }
+ | term NEQ term       { $$ = f_new_inst(FI_NEQ, $1, $3); }
+ | term '<' term       { $$ = f_new_inst(FI_LT, $1, $3); }
+ | term LEQ term       { $$ = f_new_inst(FI_LTE, $1, $3); }
+ | term '>' term       { $$ = f_new_inst(FI_LT, $3, $1); }
+ | term GEQ term       { $$ = f_new_inst(FI_LTE, $3, $1); }
+ | term '~' term       { $$ = f_new_inst(FI_MATCH, $1, $3); }
+ | term NMA term       { $$ = f_new_inst(FI_NOT_MATCH, $1, $3); }
+ | '!' term            { $$ = f_new_inst(FI_NOT, $2); }
+ | DEFINED '(' term ')' { $$ = f_new_inst(FI_DEFINED, $3); }
+
+ | symbol_value   { $$ = $1; }
  | constant { $$ = $1; }
  | constructor { $$ = $1; }
 
- | PREFERENCE { $$ = f_new_inst(); $$->code = 'P'; }
+ | PREFERENCE { $$ = f_new_inst(FI_PREF_GET); }
 
- | rtadot static_attr { $$ = $2; $$->code = 'a'; }
+ | static_attr { $$ = f_new_inst(FI_RTA_GET, $1); }
 
- | rtadot dynamic_attr { $$ = $2; $$->code = P('e','a'); }
+ | dynamic_attr { $$ = f_new_inst(FI_EA_GET, $1); }
 
- | 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 '.' MAXLEN { $$ = f_new_inst(); $$->code = P('R','m'); $$->a1.p = $1; }
- | term '.' ASN { $$ = f_new_inst(); $$->code = P('R','a'); $$->a1.p = $1; }
- | term '.' MASK '(' term ')' { $$ = f_new_inst(); $$->code = P('i','M'); $$->a1.p = $1; $$->a2.p = $5; }
- | term '.' FIRST { $$ = f_new_inst(); $$->code = P('a','f'); $$->a1.p = $1; }
- | term '.' LAST  { $$ = f_new_inst(); $$->code = P('a','l'); $$->a1.p = $1; }
- | term '.' LAST_NONAGGREGATED  { $$ = f_new_inst(); $$->code = P('a','L'); $$->a1.p = $1; }
+ | term '.' IS_V4 { $$ = f_new_inst(FI_IS_V4, $1); }
+ | term '.' TYPE { $$ = f_new_inst(FI_TYPE, $1); }
+ | term '.' IP { $$ = f_new_inst(FI_IP, $1); }
+ | term '.' RD { $$ = f_new_inst(FI_ROUTE_DISTINGUISHER, $1); }
+ | term '.' LEN { $$ = f_new_inst(FI_LENGTH, $1); }
+ | term '.' MAXLEN { $$ = f_new_inst(FI_ROA_MAXLEN, $1); }
+ | term '.' ASN { $$ = f_new_inst(FI_ROA_ASN, $1); }
+ | term '.' SRC { $$ = f_new_inst(FI_SADR_SRC, $1); }
+ | term '.' MASK '(' term ')' { $$ = f_new_inst(FI_IP_MASK, $1, $5); }
+ | term '.' FIRST { $$ = f_new_inst(FI_AS_PATH_FIRST, $1); }
+ | term '.' LAST  { $$ = f_new_inst(FI_AS_PATH_LAST, $1); }
+ | term '.' LAST_NONAGGREGATED  { $$ = f_new_inst(FI_AS_PATH_LAST_NAG, $1); }
 
 /* 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{ }
+ | dynamic_attr '.' ADD '(' term ')' { }
+ | dynamic_attr '.' DELETE '(' term ')' { }
+ | dynamic_attr '.' CONTAINS '(' term ')' { }
+ | dynamic_attr '.' RESET{ }
 */
 
- | '+' EMPTY '+' { $$ = f_new_inst(); $$->code = 'E'; $$->aux = T_PATH; }
- | '-' EMPTY '-' { $$ = f_new_inst(); $$->code = 'E'; $$->aux = T_CLIST; }
- | '-' '-' EMPTY '-' '-' { $$ = f_new_inst(); $$->code = 'E'; $$->aux = T_ECLIST; }
- | '-' '-' '-' EMPTY '-' '-' '-' { $$ = f_new_inst(); $$->code = 'E'; $$->aux = T_LCLIST; }
- | 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'; }
- | FILTER '(' term ',' term ')' { $$ = f_new_inst(); $$->code = P('C','a'); $$->a1.p = $3; $$->a2.p = $5; $$->aux = 'f'; }
+ | '+' EMPTY '+' { $$ = f_new_inst(FI_CONSTANT, f_const_empty_path); }
+ | '-' EMPTY '-' { $$ = f_new_inst(FI_CONSTANT, f_const_empty_clist); }
+ | '-' '-' EMPTY '-' '-' { $$ = f_new_inst(FI_CONSTANT, f_const_empty_eclist); }
+ | '-' '-' '-' EMPTY '-' '-' '-' { $$ = f_new_inst(FI_CONSTANT, f_const_empty_lclist); }
+ | PREPEND '(' term ',' term ')' { $$ = f_new_inst(FI_PATH_PREPEND, $3, $5); }
+ | ADD '(' term ',' term ')' { $$ = f_new_inst(FI_CLIST_ADD, $3, $5); }
+ | DELETE '(' term ',' term ')' { $$ = f_new_inst(FI_CLIST_DEL, $3, $5); }
+ | FILTER '(' term ',' term ')' { $$ = f_new_inst(FI_CLIST_FILTER, $3, $5); }
 
- | ROA_CHECK '(' rtable ')' { $$ = f_generate_roa_check($3, NULL, NULL); }
- | ROA_CHECK '(' rtable ',' term ',' term ')' { $$ = f_generate_roa_check($3, $5, $7); }
+ | ROA_CHECK '(' rtable ')' { $$ = f_new_inst(FI_ROA_CHECK_IMPLICIT, $3); }
+ | ROA_CHECK '(' rtable ',' term ',' term ')' { $$ = f_new_inst(FI_ROA_CHECK_EXPLICIT, $5, $7, $3); }
 
- | FORMAT '(' term ')' {  $$ = f_new_inst(); $$->code = P('f','m'); $$->a1.p = $3; }
+ | FORMAT '(' term ')' {  $$ = f_new_inst(FI_FORMAT, $3); }
 
 /* | 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;
-     }
-   }
+ | function_call
  ;
 
 break_command:
@@ -953,110 +970,81 @@ break_command:
  | 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;
+ | term { $$ = $1; }
+ | term ',' print_list {
+     ASSERT($1);
+     ASSERT($1->next == NULL);
+     $1->next = $3;
+     $$ = $1;
    }
  ;
 
-var_list: /* EMPTY */ { $$ = NULL; }
- | var_listn { $$ = $1; }
- ;
-
 cmd:
    IF term THEN block {
-     $$ = f_new_inst();
-     $$->code = '?';
-     $$->a1.p = $2;
-     $$->a2.p = $4;
+     $$ = f_new_inst(FI_CONDITION, $2, $4, NULL);
    }
  | 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;
+     $$ = f_new_inst(FI_CONDITION, $2, $4, $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;
+ | CF_SYM_KNOWN '=' term ';' {
+     switch ($1->class) {
+       case SYM_VARIABLE_RANGE:
+        $$ = f_new_inst(FI_VAR_SET, $3, $1);
+        break;
+       case SYM_ATTRIBUTE:
+        $$ = f_new_inst(FI_EA_SET, $3, *$1->attribute);
+        break;
+       default:
+        cf_error("Can't assign to symbol %s", $1->name);
+     }
    }
  | RETURN term ';' {
-     $$ = f_new_inst();
      DBG( "Ook, we'll return the value\n" );
-     $$->code = 'r';
-     $$->a1.p = $2;
+     $$ = f_new_inst(FI_RETURN, $2);
    }
- | rtadot dynamic_attr '=' term ';' {
-     $$ = $2;
-     $$->code = P('e','S');
-     $$->a1.p = $4;
+ | dynamic_attr '=' term ';' {
+     $$ = f_new_inst(FI_EA_SET, $3, $1);
    }
- | rtadot static_attr '=' term ';' {
-     $$ = $2;
-     if (!$$->a1.i)
+ | static_attr '=' term ';' {
+     if ($1.readonly)
        cf_error( "This static attribute is read-only.");
-     $$->code = P('a','S');
-     $$->a1.p = $4;
+     $$ = f_new_inst(FI_RTA_SET, $3, $1);
    }
  | PREFERENCE '=' term ';' {
-     $$ = f_new_inst();
-     $$->code = P('P','S');
-     $$->a1.p = $3;
+     $$ = f_new_inst(FI_PREF_SET, $3);
+   }
+ | UNSET '(' dynamic_attr ')' ';' {
+     $$ = f_new_inst(FI_EA_UNSET, $3);
    }
- | UNSET '(' rtadot dynamic_attr ')' ';' {
-     $$ = $4;
-     $$->aux = EAF_TYPE_UNDEF | EAF_TEMP;
-     $$->code = P('e','S');
-     $$->a1.p = NULL;
+ | break_command print_list ';' {
+    struct f_inst *breaker = NULL;
+    struct f_inst *printer = NULL;
+    if ($2)
+      printer = f_new_inst(FI_PRINT, $2);
+    if ($1 != F_NONL)
+      breaker = f_new_inst(FI_DIE, $1);
+
+    if (printer && breaker)
+      printer->next = breaker;
+
+    if (printer)
+      $$ = printer;
+    else
+      $$ = breaker;
    }
- | break_command print_list ';' { $$ = f_new_inst(); $$->code = P('p',','); $$->a1.p = $2; $$->a2.i = $1; }
- | function_call ';' { $$ = $1; }
+ | function_call ';' { $$ = f_new_inst(FI_DROP_RESULT, $1); } 
  | CASE term '{' switch_body '}' {
-      $$ = f_new_inst();
-      $$->code = P('S','W');
-      $$->a1.p = $2;
-      $$->a2.p = build_tree( $4 );
+      $$ = f_new_inst(FI_SWITCH, $2, build_tree($4));
    }
 
- | rtadot dynamic_attr '.' EMPTY ';' { $$ = f_generate_empty($2); }
- | 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 ); }
- | rtadot dynamic_attr '.' FILTER '(' term ')' ';'    { $$ = f_generate_complex( P('C','a'), 'f', $2, $6 ); }
+ | dynamic_attr '.' EMPTY ';' { $$ = f_generate_empty($1); }
+ | dynamic_attr '.' PREPEND '(' term ')' ';'   { $$ = f_generate_complex( FI_PATH_PREPEND, $1, $5 ); }
+ | dynamic_attr '.' ADD '(' term ')' ';'       { $$ = f_generate_complex( FI_CLIST_ADD, $1, $5 ); }
+ | dynamic_attr '.' DELETE '(' term ')' ';'    { $$ = f_generate_complex( FI_CLIST_DEL, $1, $5 ); }
+ | dynamic_attr '.' FILTER '(' term ')' ';'    { $$ = f_generate_complex( FI_CLIST_FILTER, $1, $5 ); }
  | BT_ASSERT '(' get_cf_position term get_cf_position ')' ';' { $$ = assert_done($4, $3 + 1, $5 - 1); }
+ | BT_CHECK_ASSIGN '(' get_cf_position lvalue get_cf_position ',' term ')' ';' { $$ = assert_assign(&$4, $7, $3 + 1, $5 - 1); }
  ;
 
 get_cf_position:
@@ -1064,5 +1052,10 @@ get_cf_position:
   $$ = cf_text;
 };
 
+lvalue:
+   CF_SYM_KNOWN { cf_assert_symbol($1, SYM_VARIABLE); $$ = (struct f_lval) { .type = F_LVAL_VARIABLE, .sym = $1 }; }
+ | PREFERENCE { $$ = (struct f_lval) { .type = F_LVAL_PREFERENCE }; }
+ | static_attr { $$ = (struct f_lval) { .type = F_LVAL_SA, .sa = $1 }; }
+ | dynamic_attr { $$ = (struct f_lval) { .type = F_LVAL_EA, .da = $1 }; };
 
 CF_END