Skip to content
Snippets Groups Projects
snl.lem 18.4 KiB
Newer Older
/*************************************************************************\
Copyright (c) 2010-2012 Helmholtz-Zentrum Berlin f. Materialien
                        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
\*************************************************************************/
ben.franksen's avatar
ben.franksen committed
%include {
#include <stdlib.h>
#include <string.h>
#include "var_types.h"
#include "main.h"
#include "parser.h"
%extra_argument { Node **presult }
ben.franksen's avatar
ben.franksen committed

ben.franksen's avatar
ben.franksen committed
%parse_failure {
ben.franksen's avatar
ben.franksen committed
	exit(1);
}

%syntax_error {
	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

%default_type { Node* }
ben.franksen's avatar
ben.franksen committed

/* Standard C operator table, highest precedence first.
ben.franksen's avatar
ben.franksen committed
  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.
ben.franksen's avatar
ben.franksen committed

// The comma operator is implemented as an extra production,
// so we need no explicit precedence for it.
ben.franksen's avatar
ben.franksen committed
// %left	COMMA.
%right	EQUAL ADDEQ SUBEQ ANDEQ OREQ DIVEQ MULEQ MODEQ LSHEQ RSHEQ XOREQ.
ben.franksen's avatar
ben.franksen committed
%right	QUESTION COLON.
%left	OROR.
%left	ANDAND.
%left	VBAR.
%left	CARET.
ben.franksen's avatar
ben.franksen committed
%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's avatar
ben.franksen committed

program ::=
ben.franksen's avatar
ben.franksen committed
	program_param(pp)
ben.franksen's avatar
ben.franksen committed
	state_sets(ss)
ben.franksen's avatar
ben.franksen committed
{
	*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; }
initial_defns(r) ::= initial_defns(xs) initial_defn(x). {
	r = link_node(xs, x);
ben.franksen's avatar
ben.franksen committed
}
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);
ben.franksen's avatar
ben.franksen committed
}
assign(r) ::= ASSIGN variable(v) subscript(s) to string(t) SEMICOLON. {
	r = node(D_ASSIGN, v, node(E_CONST, s), t);
ben.franksen's avatar
ben.franksen committed
}
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);
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);
ben.franksen's avatar
ben.franksen committed
}
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);
ben.franksen's avatar
ben.franksen committed
}
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);
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);
ben.franksen's avatar
ben.franksen committed
}
syncq(r) ::= SYNCQ variable(v) opt_subscript(s) syncq_size(n) SEMICOLON. {
	r = node(D_SYNCQ, v, s, 0, n);
event_flag(r) ::= NAME(x).			{ r = x; }
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; }
subscript(r) ::= LBRACKET INTCON(n) RBRACKET.	{ r = n; }
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; }
// deprecated
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)); }
// 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; }
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; }
// 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; }
%type basetype {Type*}
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); }

// abstract_declarator
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(r) ::= OPTION option_value(v) NAME(n) SEMICOLON.
						{ r = opt_defn(n, v); }
option_value(r) ::= ADD(t).			{ r = t; }
option_value(r) ::= SUB(t).			{ r = t; }
ben.franksen's avatar
ben.franksen committed

// State sets and states

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); }
ss_defns(r) ::= ss_defns(xs) ss_defn(x).	{ r = link_node(xs, x); }
ss_defns(r) ::= .				{ r = 0; }
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; }
	entry(en) transitions(ws) exit(ex) RBRACE.
	r = node(D_STATE, n, ds, en, ws, ex);
state_defns(r) ::= state_defns(xs) state_defn(x). {
	r = link_node(xs, x);
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). {
	r = node(D_WHEN, t, c, b);
transition(r) ::= WHEN(t) LPAREN opt_expr(c) RPAREN block(b) EXIT. {
	t.str = 0;
	r = node(D_WHEN, t, c, b);
transition(r) ::= WHEN(t) LPAREN opt_expr(c) RPAREN block(b) error. {
	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);
block_defns(r) ::= block_defns(ds) block_defn(d). {
	r = link_node(ds, d);
block_defns(r) ::= .				{ r = 0; }
block_defn(r) ::= declaration(x).		{ r = x; }
block_defn(r) ::= c_code(x).			{ r = x; }
ben.franksen's avatar
ben.franksen committed

// Statements

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) ::=
	opt_expr(init) SEMICOLON opt_expr(cond) SEMICOLON opt_expr(iter)
	RPAREN statement(st).		{ r = node(S_FOR, for, init, cond, iter, st); }
ben.franksen's avatar
ben.franksen committed

// Expressions

// 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').

ben.franksen's avatar
ben.franksen committed
// Atomic
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); }
ben.franksen's avatar
ben.franksen committed

// 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); }
ben.franksen's avatar
ben.franksen committed

// Unary Prefix Operators
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); }
ben.franksen's avatar
ben.franksen committed
// Binary Operators, left-to-right
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); }
ben.franksen's avatar
ben.franksen committed

// Ternary Operator, right-to-left
expr(r) ::= expr(c) QUESTION(t) expr(th) COLON expr(el).
						{ r = node(E_TERNOP, t, c, th, el); }
ben.franksen's avatar
ben.franksen committed

// Assignment Operators, right-to-left
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, left-to-right
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; }
// Function arguments
// 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);
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; }
c_code(r) ::= CCODE(t). 			{ r = node(T_TEXT, t); }