Newer
Older
benjamin.franksen
committed
/*************************************************************************\
Copyright (c) 2010-2012 Helmholtz-Zentrum Berlin f. Materialien
benjamin.franksen
committed
und Energie GmbH, Germany (HZB)
This file is distributed subject to a Software License Agreement found
in the file LICENSE that is included with this distribution.
\*************************************************************************/
/*************************************************************************\
Parser specification/implementation
\*************************************************************************/
%include {
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "main.h"
#include "parser.h"
%extra_argument { Node **presult }
benjamin.franksen
committed
ben.franksen
committed
%name snlParser
benjamin.franksen
committed
report("parser giving up\n");
error_at(TOKEN.file, TOKEN.line,
"syntax error near token '%s'\n", TOKEN.str);
report_loc(TOKEN.file, TOKEN.line);
// %stack_overflow {
// report("parser stack overflow\n");
// }
// A stack size of 0 (zero) means stack size gets dynamically increased
// on demand i.e. stack size is essentially unlimited. This is important
// because some rules like the one for IF/ELSE statements employ left-recursion.
%stack_size 0
benjamin.franksen
committed
%token_type { Token }
%default_type { Node* }
/* Standard C operator table, highest precedence first.
Primary Expression Operators () [] . -> expr++ expr-- left-to-right
Unary Operators * & + - ! ~ ++expr --expr (typecast) sizeof() right-to-left
Binary Operators * / % left-to-right
+ -
>> <<
< > <= >=
== !=
&
^
|
&&
||
Ternary Operator ?: right-to-left
Assignment Operators = += -= *= /= %= >>= <<= &= ^= |= right-to-left
Comma , left-to-right
*/
// PRE and POST are pseudo tokens used for precedence declaration.
// They are needed for operators that can appear in more than
// one position (and then have different precedences) because lemon
// allows only one precedence declaration for each token.
// The comma operator is implemented as an extra production,
// so we need no explicit precedence for it.
%right EQUAL ADDEQ SUBEQ ANDEQ OREQ DIVEQ MULEQ MODEQ LSHEQ RSHEQ XOREQ.
%left OROR.
%left ANDAND.
%left VBAR.
%left CARET.
%left AMPERSAND.
%left EQ NE.
%left GT GE LE LT.
%left LSHIFT RSHIFT.
%left ADD SUB.
%left ASTERISK SLASH MOD.
%right NOT TILDE INCR DECR SIZEOF PRE. // omitted duplicates: ADD SUB ASTERISK AMPERSAND
%left LBRACKET LPAREN POINTER PERIOD POST. // omitted duplicates: INCR DECR
ben.franksen
committed
PROGRAM NAME(n)
entry(en)
exit(ex)
*presult = node(D_PROG, n, pp, ds, en, ss, ex, fs);
program_param(r) ::= LPAREN string(x) RPAREN. { r = x; }
program_param(r) ::= . { r = 0; }
ben.franksen
committed
// Definitions
initial_defns(r) ::= initial_defns(xs) initial_defn(x). {
r = link_node(xs, x);
initial_defns(r) ::= . { r = 0; }
initial_defn(r) ::= assign(x). { r = x; }
initial_defn(r) ::= monitor(x). { r = x; }
initial_defn(r) ::= sync(x). { r = x; }
initial_defn(r) ::= syncq(x). { r = x; }
initial_defn(r) ::= declaration(x). { r = x; }
initial_defn(r) ::= option(x). { r = x; }
initial_defn(r) ::= c_code(x). { r = x; }
initial_defn(r) ::= funcdef(x). { r = x; }
initial_defn(r) ::= structdef(x). { r = x; }
final_defns(r) ::= final_defns(xs) final_defn(x). {
r = link_node(xs, x);
final_defns(r) ::= . { r = 0; }
final_defn(r) ::= c_code(x). { r = x; }
final_defn(r) ::= funcdef(x). { r = x; }
final_defn(r) ::= structdef(x). { r = x; }
assign(r) ::= ASSIGN variable(v) to string(t) SEMICOLON. {
r = node(D_ASSIGN, v, 0, t);
assign(r) ::= ASSIGN variable(v) subscript(s) to string(t) SEMICOLON. {
r = node(D_ASSIGN, v, node(E_CONST, s), t);
assign(r) ::= ASSIGN variable(v) to LBRACE(t) strings(ss) RBRACE SEMICOLON. {
r = node(D_ASSIGN, v, 0, node(E_INIT, t, ss));
assign(r) ::= ASSIGN variable(v) SEMICOLON. {
r = node(D_ASSIGN, v, 0, 0);
to ::= TO.
to ::= .
strings(r) ::= strings(xs) COMMA string(x). { r = link_node(xs, x); }
strings(r) ::= string(x). { r = x; }
strings(r) ::= . { r = 0; }
monitor(r) ::= MONITOR variable(v) opt_subscript(s) SEMICOLON. {
r = node(D_MONITOR, v, s);
monitor(r) ::= MONITOR variable(v) opt_subscript(sub) error SEMICOLON. {
r = node(D_MONITOR, v, sub);
report("expected %s';'\n", sub ? "subscript or " : "");
}
sync(r) ::= SYNC variable(v) opt_subscript(s) to event_flag(f) SEMICOLON. {
r = node(D_SYNC, v, s, node(E_VAR, f), 0);
sync(r) ::= SYNC variable(v) opt_subscript(s) to event_flag(f) error SEMICOLON. {
r = node(D_SYNC, v, s, node(E_VAR, f), 0);
report("expected ';'\n");
}
syncq(r) ::= SYNCQ variable(v) opt_subscript(s) to event_flag(f) syncq_size(n) SEMICOLON. {
r = node(D_SYNCQ, v, s, node(E_VAR, f), n);
syncq(r) ::= SYNCQ variable(v) opt_subscript(s) syncq_size(n) SEMICOLON. {
r = node(D_SYNCQ, v, s, 0, n);
%type event_flag {Token}
event_flag(r) ::= NAME(x). { r = x; }
%type variable {Token}
variable(r) ::= NAME(x). { r = x; }
syncq_size(r) ::= INTCON(n). { r = node(E_CONST, n); }
syncq_size(r) ::= . { r = 0; }
opt_subscript(r) ::= subscript(s). { r = node(E_CONST, s); }
opt_subscript(r) ::= . { r = 0; }
benjamin.franksen
committed
%type subscript {Token}
subscript(r) ::= LBRACKET INTCON(n) RBRACKET. { r = n; }
// Declarations
declaration(r) ::= basetype(t) init_declarators(ds) SEMICOLON.
{ r = mk_decls(ds, t); }
init_declarators(r) ::= init_declarator(x). { r = x; }
init_declarators(r) ::= init_declarators(xs) COMMA init_declarator(x).
{ r = link_node(xs, x); }
init_declarator(r) ::= declarator(x). { r = x; }
init_declarator(r) ::= declarator(x) EQUAL(t) init_expr(i).
{ r = node(E_BINOP, t, x, i); }
declarator(r) ::= variable(n). { r = node(E_VAR, n); }
declarator(r) ::= declarator(x) subscript(s). [POST] { r = node(E_SUBSCR, s, x, node(E_CONST, s)); }
declarator(r) ::= declarator(x)
LPAREN(t) param_decls(ps) RPAREN. [POST] { r = node(E_FUNC, t, x, ps); }
declarator(r) ::= LPAREN declarator(x) RPAREN. [PRE] { r = x; }
declarator(r) ::= ASTERISK(t) declarator(x). [PRE] { r = node(E_PRE, t, x); }
declarator(r) ::= CONST(t) declarator(x). [PRE] { r = node(E_PRE, t, x); }
param_decls(r) ::= . { r = 0; }
param_decls(r) ::= param_decl(x). { r = x; }
param_decls(r) ::= param_decls(xs) COMMA param_decl(x). { r = link_node(xs, x); }
// allow parameter declaration with or without identifier
param_decl(r) ::= basetype(t) declarator(d). { r = mk_decl(d, t); }
param_decl(r) ::= type_expr(x). { r = x; }
declaration(r) ::= FOREIGN variables(ds) SEMICOLON. { r = mk_decls(ds, mk_no_type()); }
variables(r) ::= variable(x). { r = node(E_VAR, x); }
variables(r) ::= variables(xs) COMMA variable(x). { r = link_node(xs, node(E_VAR, x)); }
ben.franksen
committed
// Initializer
// Note: comma operator not allowed in 'expr'.
init_expr(r) ::= LPAREN(tc) type_expr(c) RPAREN LBRACE(tx) init_exprs(x) RBRACE.
{ r = node(E_CAST, tc, c, node(E_INIT, tx, x)); }
init_expr(r) ::= LBRACE(t) init_exprs(x) RBRACE. { r = node(E_INIT, t, x); }
init_expr(r) ::= expr(x). { r = x; }
ben.franksen
committed
init_exprs(r) ::= init_exprs(xs) COMMA init_expr(x). { r = link_node(xs, x); }
init_exprs(r) ::= init_expr(x). { r = x; }
init_exprs(r) ::= . { r = 0; }
ben.franksen
committed
// Type expressions
// C standard calls this specifier-qualifier-list
%type prim_type {enum prim_type_tag}
prim_type(r) ::= CHAR. { r = P_CHAR; }
prim_type(r) ::= SHORT. { r = P_SHORT; }
prim_type(r) ::= INT. { r = P_INT; }
prim_type(r) ::= LONG. { r = P_LONG; }
prim_type(r) ::= UNSIGNED CHAR. { r = P_UCHAR; }
prim_type(r) ::= UNSIGNED SHORT. { r = P_USHORT; }
prim_type(r) ::= UNSIGNED INT. { r = P_UINT; }
prim_type(r) ::= UNSIGNED LONG. { r = P_ULONG; }
prim_type(r) ::= INT8T. { r = P_INT8T; }
prim_type(r) ::= UINT8T. { r = P_UINT8T; }
prim_type(r) ::= INT16T. { r = P_INT16T; }
prim_type(r) ::= UINT16T. { r = P_UINT16T; }
prim_type(r) ::= INT32T. { r = P_INT32T; }
prim_type(r) ::= UINT32T. { r = P_UINT32T; }
prim_type(r) ::= FLOAT. { r = P_FLOAT; }
prim_type(r) ::= DOUBLE. { r = P_DOUBLE; }
prim_type(r) ::= STRING. { r = P_STRING; }
basetype(r) ::= prim_type(x). { r = mk_prim_type(x); }
basetype(r) ::= EVFLAG. { r = mk_ef_type(); }
basetype(r) ::= VOID. { r = mk_void_type(); }
basetype(r) ::= ENUM NAME(x). { r = mk_foreign_type(F_ENUM, x.str); }
basetype(r) ::= STRUCT NAME(x). { r = mk_foreign_type(F_STRUCT, x.str); }
basetype(r) ::= UNION NAME(x). { r = mk_foreign_type(F_UNION, x.str); }
basetype(r) ::= TYPENAME NAME(x). { r = mk_foreign_type(F_TYPENAME, x.str); }
type_expr(r) ::= basetype(t). { r = mk_decl(0, t); }
type_expr(r) ::= basetype(t) abs_decl(d). { r = mk_decl(d, t); }
abs_decl(r) ::= LPAREN abs_decl(x) RPAREN. { r = x; }
abs_decl(r) ::= ASTERISK(t). [PRE] { r = node(E_PRE, t, 0); }
abs_decl(r) ::= ASTERISK(t) abs_decl(d). [PRE] { r = node(E_PRE, t, d); }
abs_decl(r) ::= CONST(t). [PRE] { r = node(E_PRE, t, 0); }
abs_decl(r) ::= CONST(t) abs_decl(d). [PRE] { r = node(E_PRE, t, d); }
abs_decl(r) ::= subscript(s). [POST] { r = node(E_SUBSCR, s, 0, node(E_CONST, s)); }
abs_decl(r) ::= abs_decl(d) subscript(s). [POST]{ r = node(E_SUBSCR, s, d, node(E_CONST, s)); }
abs_decl(r) ::= LPAREN(t) param_decls(ps) RPAREN. [POST]
{ r = node(E_FUNC, t, 0, ps); }
abs_decl(r) ::= abs_decl(d) LPAREN(t) param_decls(ps) RPAREN. [POST]
{ r = node(E_FUNC, t, d, ps); }
// not supported: empty brackets, empty parameter list
// abs_decl ::= LBRACKET RBRACKET.
// abs_decl ::= abs_decl LBRACKET RBRACKET.
// abs_decl ::= LPAREN RPAREN.
// abs_decl ::= abs_decl LPAREN RPAREN.
// Option spec
option(r) ::= OPTION option_value(v) NAME(n) SEMICOLON.
{ r = opt_defn(n, v); }
benjamin.franksen
committed
ben.franksen
committed
%type option_value {Token}
option_value(r) ::= ADD(t). { r = t; }
option_value(r) ::= SUB(t). { r = t; }
state_sets(r) ::= state_sets(xs) state_set(x). { r = link_node(xs, x); }
state_sets(r) ::= state_set(x). { r = x; }
state_set(r) ::= SS NAME(n) LBRACE ss_defns(ds) states(xs) RBRACE.
{ r = node(D_SS, n, ds, xs); }
ben.franksen
committed
ss_defns(r) ::= ss_defns(xs) ss_defn(x). { r = link_node(xs, x); }
ss_defns(r) ::= . { r = 0; }
ben.franksen
committed
ss_defn(r) ::= assign(x). { r = x; }
ss_defn(r) ::= monitor(x). { r = x; }
ss_defn(r) ::= sync(x). { r = x; }
ss_defn(r) ::= syncq(x). { r = x; }
ss_defn(r) ::= declaration(x). { r = x; }
states(r) ::= states(xs) state(x). { r = link_node(xs, x); }
states(r) ::= state(x). { r = x; }
ben.franksen
committed
STATE NAME(n) LBRACE state_defns(ds)
entry(en) transitions(ws) exit(ex) RBRACE.
ben.franksen
committed
{
r = node(D_STATE, n, ds, en, ws, ex);
ben.franksen
committed
}
state_defns(r) ::= state_defns(xs) state_defn(x). {
r = link_node(xs, x);
benjamin.franksen
committed
}
state_defns(r) ::= . { r = 0; }
state_defn(r) ::= assign(x). { r = x; }
state_defn(r) ::= monitor(x). { r = x; }
state_defn(r) ::= sync(x). { r = x; }
state_defn(r) ::= syncq(x). { r = x; }
state_defn(r) ::= declaration(x). { r = x; }
state_defn(r) ::= option(x). { r = x; }
entry(r) ::= ENTRY(t) block(b). { r = node(D_ENTEX, t, b); }
entry(r) ::= . { r = 0; }
exit(r) ::= EXIT(t) block(b). { r = node(D_ENTEX, t, b); }
exit(r) ::= . { r = 0; }
transitions(r) ::= transitions(xs) transition(x).{ r = link_node(xs, x); }
transitions(r) ::= transition(x). { r = x; }
transition(r) ::= WHEN(t) LPAREN opt_expr(c) RPAREN block(b) STATE NAME(n). {
ben.franksen
committed
t.str = n.str;
r = node(D_WHEN, t, c, b);
ben.franksen
committed
}
transition(r) ::= WHEN(t) LPAREN opt_expr(c) RPAREN block(b) EXIT. {
r = node(D_WHEN, t, c, b);
transition(r) ::= WHEN(t) LPAREN opt_expr(c) RPAREN block(b) error. {
t.str = 0;
r = node(D_WHEN, t, c, b);
report("expected 'state' or 'exit'\n");
}
block(r) ::= LBRACE(t) block_defns(ds) statements(xs) RBRACE. {
r = node(S_CMPND, t, ds, xs);
ben.franksen
committed
}
block_defns(r) ::= block_defns(ds) block_defn(d). {
r = link_node(ds, d);
ben.franksen
committed
}
block_defns(r) ::= . { r = 0; }
block_defn(r) ::= declaration(x). { r = x; }
block_defn(r) ::= c_code(x). { r = x; }
statements(r) ::= statements(xs) statement(x). { r = link_node(xs, x); }
statements(r) ::= . { r = 0; }
statement(r) ::= BREAK(t) SEMICOLON. { r = node(S_JUMP, t); }
statement(r) ::= CONTINUE(t) SEMICOLON. { r = node(S_JUMP, t); }
statement(r) ::= RETURN(t) opt_expr(x) SEMICOLON.
{ r = node(S_RETURN, t, x); }
statement(r) ::= STATE NAME(t) SEMICOLON. { r = node(S_CHANGE, t); }
statement(r) ::= c_code(x). { r = x; }
statement(r) ::= block(x). { r = x; }
statement(r) ::= IF(t) LPAREN comma_expr(c) RPAREN statement(th).
{ r = node(S_IF, t, c, th, 0); }
statement(r) ::= IF(t) LPAREN comma_expr(c) RPAREN statement(th) ELSE statement(el).
{ r = node(S_IF, t, c, th, el); }
statement(r) ::= WHILE(t) LPAREN comma_expr(c) RPAREN statement(x).
{ r = node(S_WHILE, t, c, x); }
statement(r) ::= for_statement(x). { r = x; }
statement(r) ::= opt_expr(x) SEMICOLON(t). { r = node(S_STMT, t, x); }
// statement(r) ::= error SEMICOLON(t). { r = node(S_STMT, t, 0); report("malformed expression\n"); }
for_statement(r) ::=
benjamin.franksen
committed
FOR(for) LPAREN
opt_expr(init) SEMICOLON opt_expr(cond) SEMICOLON opt_expr(iter)
RPAREN statement(st). { r = node(S_FOR, for, init, cond, iter, st); }
// Note: the non-terminal 'expr' does not include application of the comma operator.
// Comma separated lists of 'expr' can be: function arguments (non-terminal
// 'args')and applications of the comma operator (non-terminal 'comma_expr').
expr(r) ::= INTCON(x). { r = node(E_CONST, x); }
expr(r) ::= FPCON(x). { r = node(E_CONST, x); }
expr(r) ::= string(x). { r = x; }
expr(r) ::= variable(v). { r = node(E_VAR, v); }
// Primary Expression and Unary Postfix Operators
expr(r) ::= LPAREN(t) comma_expr(x) RPAREN. { r = node(E_PAREN, t, x); }
expr(r) ::= expr(x) LPAREN(t) args(y) RPAREN. { r = node(E_FUNC, t, x, y); }
expr(r) ::= EXIT(n) LPAREN(t) args(y) RPAREN. { r = node(E_FUNC, t, node(E_VAR, n), y); }
expr(r) ::= SIZEOF(n) LPAREN(t) type_expr(y) RPAREN. { r = node(E_FUNC, t, node(E_VAR, n), y); }
expr(r) ::= expr(x) LBRACKET(t) expr(y) RBRACKET. { r = node(E_SUBSCR, t, x, y); }
expr(r) ::= expr(x) PERIOD(t) member(y). { r = node(E_SELECT, t, x, y); }
expr(r) ::= expr(x) POINTER(t) member(y). { r = node(E_SELECT, t, x, y); }
expr(r) ::= expr(x) INCR(t). [POST] { r = node(E_POST, t, x); }
expr(r) ::= expr(x) DECR(t). [POST] { r = node(E_POST, t, x); }
expr(r) ::= ADD(t) expr(x). [PRE] { r = node(E_PRE, t, x); }
expr(r) ::= SUB(t) expr(x). [PRE] { r = node(E_PRE, t, x); }
expr(r) ::= ASTERISK(t) expr(x). [PRE] { r = node(E_PRE, t, x); }
expr(r) ::= AMPERSAND(t)expr(x). [PRE] { r = node(E_PRE, t, x); }
expr(r) ::= NOT(t) expr(x). [PRE] { r = node(E_PRE, t, x); }
expr(r) ::= TILDE(t) expr(x). [PRE] { r = node(E_PRE, t, x); }
expr(r) ::= INCR(t) expr(x). [PRE] { r = node(E_PRE, t, x); }
expr(r) ::= DECR(t) expr(x). [PRE] { r = node(E_PRE, t, x); }
expr(r) ::= SIZEOF(t) expr(x). [PRE] { r = node(E_FUNC, t, node(E_VAR, t), x); }
expr(r) ::= LPAREN(t) type_expr(c) RPAREN expr(x). [PRE] { r = node(E_CAST, t, c, x); }
expr(r) ::= expr(x) SUB(t) expr(y). { r = node(E_BINOP, t, x, y); }
expr(r) ::= expr(x) ADD(t) expr(y). { r = node(E_BINOP, t, x, y); }
expr(r) ::= expr(x) ASTERISK(t) expr(y). { r = node(E_BINOP, t, x, y); }
expr(r) ::= expr(x) SLASH(t) expr(y). { r = node(E_BINOP, t, x, y); }
expr(r) ::= expr(x) GT(t) expr(y). { r = node(E_BINOP, t, x, y); }
expr(r) ::= expr(x) GE(t) expr(y). { r = node(E_BINOP, t, x, y); }
expr(r) ::= expr(x) EQ(t) expr(y). { r = node(E_BINOP, t, x, y); }
expr(r) ::= expr(x) NE(t) expr(y). { r = node(E_BINOP, t, x, y); }
expr(r) ::= expr(x) LE(t) expr(y). { r = node(E_BINOP, t, x, y); }
expr(r) ::= expr(x) LT(t) expr(y). { r = node(E_BINOP, t, x, y); }
expr(r) ::= expr(x) OROR(t) expr(y). { r = node(E_BINOP, t, x, y); }
expr(r) ::= expr(x) ANDAND(t) expr(y). { r = node(E_BINOP, t, x, y); }
expr(r) ::= expr(x) LSHIFT(t) expr(y). { r = node(E_BINOP, t, x, y); }
expr(r) ::= expr(x) RSHIFT(t) expr(y). { r = node(E_BINOP, t, x, y); }
expr(r) ::= expr(x) VBAR(t) expr(y). { r = node(E_BINOP, t, x, y); }
expr(r) ::= expr(x) CARET(t) expr(y). { r = node(E_BINOP, t, x, y); }
expr(r) ::= expr(x) AMPERSAND(t) expr(y). { r = node(E_BINOP, t, x, y); }
expr(r) ::= expr(x) MOD(t) expr(y). { r = node(E_BINOP, t, x, y); }
expr(r) ::= expr(c) QUESTION(t) expr(th) COLON expr(el).
{ r = node(E_TERNOP, t, c, th, el); }
expr(r) ::= expr(x) EQUAL(t) expr(y). { r = node(E_BINOP, t, x, y); }
expr(r) ::= expr(x) ADDEQ(t) expr(y). { r = node(E_BINOP, t, x, y); }
expr(r) ::= expr(x) SUBEQ(t) expr(y). { r = node(E_BINOP, t, x, y); }
expr(r) ::= expr(x) ANDEQ(t) expr(y). { r = node(E_BINOP, t, x, y); }
expr(r) ::= expr(x) OREQ(t) expr(y). { r = node(E_BINOP, t, x, y); }
expr(r) ::= expr(x) DIVEQ(t) expr(y). { r = node(E_BINOP, t, x, y); }
expr(r) ::= expr(x) MULEQ(t) expr(y). { r = node(E_BINOP, t, x, y); }
expr(r) ::= expr(x) MODEQ(t) expr(y). { r = node(E_BINOP, t, x, y); }
expr(r) ::= expr(x) LSHEQ(t) expr(y). { r = node(E_BINOP, t, x, y); }
expr(r) ::= expr(x) RSHEQ(t) expr(y). { r = node(E_BINOP, t, x, y); }
expr(r) ::= expr(x) XOREQ(t) expr(y). { r = node(E_BINOP, t, x, y); }
comma_expr(r) ::= comma_expr(xs) COMMA(t) expr(x). { r = node(E_BINOP, t, xs, x); }
comma_expr(r) ::= expr(x). { r = x; }
opt_expr(r) ::= comma_expr(x). { r = x; }
opt_expr(r) ::= . { r = 0; }
// Syntactically the same as opt_expr but interpreted differently.
args(r) ::= args(xs) COMMA expr(x). { r = link_node(xs, x); }
args(r) ::= expr(x). { r = x; }
args(r) ::= . { r = 0; }
string(r) ::= STRCON(t). { r = node(E_STRING, t); }
member(r) ::= NAME(t). { r = node(E_MEMBER, t); }
// Function Definitions
funcdef(r) ::= basetype(t) declarator(d) block(b). {
r = node(D_FUNCDEF, d->token, mk_decl(d, t), b);
// Struct Definitions
structdef(r) ::= STRUCT NAME(t) members(ds) SEMICOLON. { r = node(D_STRUCTDEF, t, ds); }
members(r) ::= LBRACE member_decls(xs) RBRACE. { r = xs; }
member_decls(r) ::= member_decl(x). { r = x; }
member_decls(r) ::= member_decls(xs) member_decl(x). { r = link_node(xs, x); }
member_decl(r) ::= basetype(t) declarator(d) SEMICOLON. { r = mk_decl(d, t); }
member_decl(r) ::= c_code(x). { r = x; }
// Literal (C) code
c_code(r) ::= CCODE(t). { r = node(T_TEXT, t); }