ack/util/ncgg/cgg.y
sater 5c71bad6e1 Relaxed input syntax for from STACK coercions
No gen clause is necessary anymore.
1986-03-19 16:14:12 +00:00

995 lines
19 KiB
Plaintext

%{
#ifndef NORCSID
static char rcsid[]= "$Header$";
#endif
#include "param.h"
#include "varinfo.h"
#include "lookup.h"
#include "set.h"
#include "iocc.h"
#include "instruct.h"
#include "expr.h"
#include "extern.h"
#include <cgg_cg.h>
#include <em_reg.h>
extern int lineno;
int instline,saveline;
int startline;
int npatterns;
int patindex[MAXPATTERNS];
int emhere=0; /* lexical analyzer flag */
int optexact=0; /* Inside "with exact" rule */
int optstack=0; /* Inside with <blah> STACK rule */
int saferulefound=0;
int maxempatlen=0;
int maxrule=0;
struct varinfo *defcost;
struct varinfo *gen_inst(),*gen_move(),*gen_test(),*gen_preturn(),*gen_tlab();
struct varinfo *make_erase();
expr_t make_expr(),ident_expr(),subreg_expr(),tokm_expr(),all_expr();
expr_t perc_ident_expr(),sum_expr(),regvar_expr();
set_t ident_to_set(),setproduct(),setsum(),setdiff();
iocc_t subr_iocc(),tokm_iocc(),ident_iocc(),all_iocc(),descr_iocc();
extern int narexpr;
extern expr_t arexp[];
int niops;
iocc_t iops[20];
%}
%union {
int yy_int;
char * yy_str;
varinfo * yy_varinfo;
set_t yy_set;
operand *yy_oplist;
expr_t yy_expr;
iocc_t yy_iocc;
}
%token PROPERTIES
%token REGISTERS
%token TOKENS
%token SETS
%token MOVES
%token TESTS
%token STACKINGRULES COERCIONS
%token INSTRUCTIONS
%token PROC CALL EXAMPLE
%token FROM TO
%token TEST MOVE STACK RETURN
%token PATTERNS PAT WITH EXACT KILLS USES REUSING GEN YIELDS LEAVING
%token DEFINED SAMESIGN SFIT UFIT ROM LOWW HIGHW
%token CMPEQ CMPNE CMPLT CMPGT CMPLE CMPGE OR2 AND2 LSHIFT RSHIFT NOT COMP
%token INREG REGVAR REG_ANY REG_FLOAT REG_LOOP REG_POINTER
%token <yy_int> ADORNACCESS
%token <yy_int> ADORNCC
%token INT
%token ADDR
%token <yy_int> EMMNEM
%token <yy_int> NUMBER
%token <yy_int> DOLLAR PERCENT ALLREG
%token <yy_str> IDENT PERC_IDENT
%token <yy_str> STRING
%token TIMEFACTOR SIZEFACTOR
%token COST
%type <yy_varinfo> prop_list property ident_list ident_list_el
%type <yy_varinfo> att_list att_list_el structdecl optcost optformat
%type <yy_varinfo> kills allocates yields leaving
%type <yy_varinfo> generates kill_list kill_list_el uselist uselist_el genlist yieldlist
%type <yy_varinfo> leavelist leavelist_el gen_instruction
%type <yy_varinfo> opt_erase_list erase_list erase_list_el
%type <yy_str> opt_par_string optstring
%type <yy_int> register propno att_list_el_type tokenset_no
%type <yy_int> adornlist optstar optuses optregvar regvartype optregvartype
%type <yy_int> emarg tokarg subreg allreg optsecondstring
%type <yy_expr> expr
%type <yy_iocc> tokeninstance
%type <yy_int> optexpr optexact optstack
%type <yy_set> tokenset
%type <yy_oplist> oplist oplist_el
%left OR2
%left AND2
%left CMPEQ,CMPNE
%left CMPLT,CMPLE,CMPGT,CMPGE
%left RSHIFT,LSHIFT
%left '+','-'
%left '*','/','%'
%nonassoc NOT,COMP,UMINUS
%start machtable
%%
/*
* The machine table consists of a number of sections, with their
* own associated parsers.
*/
machtable
: constants
properties
registers
tokens
{ make_std_sets(); }
sets
instructions
moves
tests
stacks
coercs
code
;
/*
* Constants are parsed as name=value pairs
*/
constants
: constdeflist
;
constdeflist
: /* empty */
| constdeflist constdef
;
constdef
: IDENT'=' NUMBER
{ n_const($1,$3); free($1); }
| IDENT '=' STRING
{ n_sconst($1,$3); free($1); free($3); }
| TIMEFACTOR '=' NUMBER '/' NUMBER
{ fc1 = $3; fc2 = $5; }
| SIZEFACTOR '=' NUMBER '/' NUMBER
{ fc3 = $3; fc4 = $5; }
| error
;
/*
* Properties are parsed as a list of names optionally followed by their size
*/
properties
: PROPERTIES { make_const(); } prdef_list
;
prdef_list
: prdef_list_el
| prdef_list optcomma prdef_list_el
;
prdef_list_el
: IDENT
{ n_prop($1,wordsize); free($1); }
| IDENT '(' NUMBER ')'
{ n_prop($1,$3); free($1); }
;
/*
* Registers are rows of reglist:proplist pairs
*/
registers
: REGISTERS regdef_list
;
regdef_list
: regdef_list_el
| regdef_list regdef_list_el
;
regdef_list_el
: ident_list ':' prop_list optregvar '.'
{ regline($1,$3,$4); free((char *) $1); free((char *) $3); }
| error '.'
;
optregvar
: /* empty */
{ $$ = -1; }
| REGVAR
{ $$ = reg_any; }
| REGVAR '(' regvartype ')'
{ $$ = $3; }
;
regvartype
: REG_ANY
{ $$ = reg_any;}
| REG_FLOAT
{ $$ = reg_float;}
| REG_LOOP
{ $$ = reg_loop;}
| REG_POINTER
{ $$ = reg_pointer;}
;
ident_list
: ident_list_el
| ident_list optcomma ident_list_el
{ $3->vi_next = $1; $$ = $3; }
;
ident_list_el
: IDENT opt_par_string
{ NEW($$,struct varinfo);
$$->vi_next = 0;
$$->vi_int[0] = n_reg($1,$2,0,0,0);
free($1); if($2!=0) free($2);
}
| IDENT opt_par_string '=' register
{ NEW($$,struct varinfo);
$$->vi_next = 0;
$$->vi_int[0] = n_reg($1,$2,1,$4,0);
free($1); if($2!=0) free($2);
}
| IDENT opt_par_string '=' register '+' register
{ NEW($$,struct varinfo);
$$->vi_next = 0;
$$->vi_int[0] = n_reg($1,$2,2,$4,$6);
free($1); if($2!=0) free($2);
}
;
opt_par_string
: /* empty */
{ $$ = 0; }
| '(' STRING ')'
{ $$ = $2; }
;
register
: IDENT
{ register symbol *sy_p;
sy_p = lookup($1,symreg,mustexist);
$$ = sy_p->sy_value.syv_regno;
free($1);
}
;
prop_list
: property
| prop_list optcomma property
{ $3->vi_next = $1; $$ = $3; }
;
property
: IDENT
{ register symbol *sy_p;
sy_p = lookup($1,symprop,mustexist);
NEW($$,struct varinfo);
$$->vi_next=0;
$$->vi_int[0]=sy_p->sy_value.syv_propno;
free($1);
}
;
propno
: IDENT
{ register symbol *sy_p;
sy_p = lookup($1,symprop,mustexist);
$$ = sy_p->sy_value.syv_propno;
free($1);
}
;
/* tokens are parsed as struct definitions
* types in the struct can be register properties, ADDR or INT
*/
tokens
: TOKENS tokdeflist
;
tokdeflist
: tokdeflist_el
| tokdeflist tokdeflist_el
;
tokdeflist_el
: IDENT '=' structdecl NUMBER optcost optformat '.'
{ n_tok($1,$3,$4,$5,$6);
free($1);
freevi($3);
freevi($5);
freevi($6);
}
| error '.'
;
structdecl
: '{' att_list '}'
{ $$ = $2; }
;
att_list
: /* empty */
{ $$ = 0; }
| att_list_el att_list
{ $1->vi_next = $2; $$ = $1; }
;
att_list_el
: att_list_el_type IDENT ';'
{ NEW ($$,struct varinfo);
$$->vi_next = 0;
$$->vi_int[0] = $1;
$$->vi_str[0] = $2;
}
;
att_list_el_type
: INT
{ $$ = -1; }
| ADDR
{ $$ = -2; }
| propno
;
optcost
:
{ if (defcost==VI_NULL)
$$=VI_NULL;
else {
NEW($$,struct varinfo);
*$$ = *defcost;
}
}
| COST '(' NUMBER ',' NUMBER ')'
{ NEW ($$,struct varinfo);
$$->vi_int[0] = $3;
$$->vi_int[1] = $5;
}
;
optformat
:
{ $$ = 0; }
| STRING optformat
{ NEW($$,struct varinfo);
$$->vi_next = $2;
$$->vi_int[0] = 0;
$$->vi_str[0] = $1;
}
| IDENT optformat
{ NEW($$,struct varinfo);
$$->vi_next = $2;
$$->vi_int[0] = 1;
$$->vi_str[0] = $1;
}
;
optcomma
: ','
| /* empty */
;
/* sets are parsed as ident = expression */
sets
: SETS setdeflist
;
setdeflist
: setdeflist_el
| setdeflist setdeflist_el
;
setdeflist_el
: IDENT '=' tokenset_no '.'
{ n_set($1,$3); free($1); }
| error '.'
;
tokenset_no
: tokenset
{ $$ = setlookup($1); }
;
tokenset
: IDENT
{ $$ = ident_to_set($1); free($1); }
| tokenset '*' tokenset
{ $$ = setproduct($1,$3); }
| tokenset '+' tokenset
{ $$ = setsum($1,$3); }
| tokenset '-' tokenset
{ $$ = setdiff($1,$3); }
| '(' tokenset ')'
{ $$ = $2; }
;
instructions
: INSTRUCTIONS optcost instdef_list
{ defcost = $2; }
;
instdef_list
: instdef_list_el
| instdef_list instdef_list_el
;
instdef_list_el
: IDENT optstring oplist opt_erase_list optcost '.'
{ n_instr($1,$2,$3,$4,$5); freevi($5); }
| error '.'
;
oplist
: /* empty */
{ $$ = 0; }
| oplist_el
| oplist_el ',' oplist
{ $$ = $1; $$->o_next = $3; }
;
oplist_el
: tokenset_no adornlist
{ NEW($$,struct operand);
$$->o_next = 0 ;
$$->o_setno = $1;
$$->o_adorn = $2;
checkprintformat($1);
}
;
adornlist
: /* empty */
{ $$ = 0; }
| ADORNACCESS adornlist
{ if ($2&AD_RWMASK)
error("Only one of :ro,:wo,:rw allowed");
$$ = $1 | $2;
}
| ADORNCC adornlist
{ $$ = $1|$2; }
;
opt_erase_list
: /* empty */
{ $$ = VI_NULL;}
| KILLS erase_list
{ $$ = $2; }
;
erase_list
: erase_list_el
{ $$ = $1; }
| erase_list_el erase_list
{ $1->vi_next = $2; $$ = $1; }
;
erase_list_el
: IDENT
{ $$ = make_erase($1); }
| ADORNCC
{ NEW($$, struct varinfo);
$$->vi_int[0] = -1;
$$->vi_next = VI_NULL;
}
;
/* Now the moves */
moves
: MOVES movedeflist
| /* empty */
;
movedeflist
: movedeflist_el
| movedeflist movedeflist_el
;
movedeflist_el
: FROM
{startline = lineno; }
tokenset_no
{ cursetno = $3; }
optexpr TO tokenset_no
{ cursetno = $7;
tokpatlen=2;
tokpatset[0] = $3;
tokpatset[1] = $7;
tokpatro[0] = 1;
}
optexpr GEN genlist
{ tokpatlen=0;
tokpatro[0]=0;
n_move($3,$5,$7,$9,$11);
freevi($11);
}
| error
;
/* Now the test part */
tests
: TESTS testdeflist
| /* empty */
;
testdeflist
: testdeflist_el
| testdeflist testdeflist_el
;
testdeflist_el
: TO
{ startline = lineno;}
TEST tokenset_no
{ cursetno = $4;
tokpatlen=1;
tokpatset[0]=$4;
tokpatro[0] = 1;
}
optexpr GEN genlist
{ tokpatlen=0;
tokpatro[0] = 0;
n_test($4,$6,$8);
freevi($8);
}
| error
;
/* Now the stacks */
stacks
: STACKINGRULES stackdeflist
;
stackdeflist
: stackdeflist_el
| stackdeflist stackdeflist_el
;
stackdeflist_el
: FROM
{startline = lineno;}
tokenset_no
{ cursetno = $3;
tokpatlen=1;
tokpatset[0] = $3;
tokpatro[0] = 1;
}
optexpr TO STACK optuses GEN genlist
{ tokpatro[0] = 0;
n_stack($3,$5,$8,$10);
freevi($10);
}
;
optuses
: /* empty */
{ $$ = -1; nallreg=0;}
| USES propno
{ $$ = $2; nallreg = 1; allreg[0] = $2; }
;
/* Now the one-to-one coercion rules */
coercs
: COERCIONS coercdeflist
;
coercdeflist
: coercdeflist_el
| coercdeflist coercdeflist_el
;
coercdeflist_el
: FROM
{startline = lineno; tokpatlen=0; inithall();}
STACK allocates generates YIELDS tokeninstance
{ checkhall();
n_coerc(0,0,$4,$5,(struct varinfo *) 0,$7);
freevi($4);
freevi($5);
}
| FROM
{startline = lineno;}
tokenset_no
{ cursetno = $3;
tokpatlen=1;
tokpatset[0]=$3;
tokpatro[0] = 1;
inithall();
}
optexpr allocates generates yields
{ tokpatro[0] = 0;
checkhall();
n_coerc($3,$5,$6,$7,$8);
freevi($6);
freevi($7);
}
;
/* Now the code part */
code
: PATTERNS coderules
;
coderules
: coderule
| coderules coderule
;
coderule
: PAT {emhere=1;} empattern {emhere=0;} optexpr
{ empatexpr = $5;
npatterns = 0;
saferulefound=0;
if (empatlen>maxempatlen)
maxempatlen=empatlen;
}
patterns
{ if (!saferulefound)
error("Previous rule impossible on empty stack");
outpatterns();
}
| PROC IDENT example
{ npatterns = 0; saferulefound=0; inproc=1; n_proc($2); }
patterns
{ if (!saferulefound)
error("Previous rule impossible on empty stack");
outpatterns(); inproc=0;
}
| error
{ skipupto(PAT,"pat"); yyerrok; yyclearin; }
;
example
: /* empty */
{ empatlen = 0; }
| EXAMPLE {emhere=1;} empattern {emhere=0;}
;
empattern
: EMMNEM
{ empatlen = 1; emmnem[0] = $1; }
| empattern EMMNEM
{ NEXT(empatlen,EMPATMAX,"Em pattern");
emmnem[empatlen-1] = $2;
}
;
patterns
: onepattern
{ saferulefound=1;
callproc=0;
}
| morepatterns
{ callproc=0;
if (npatterns>maxrule)
maxrule=npatterns;
}
| CALL IDENT '(' STRING optsecondstring ')'
{ register symbol *sy_p;
saferulefound=1;
sy_p=lookup($2,symproc,mustexist);
callproc=sy_p->sy_value.syv_procoff;
procarg[0] = strlookup($4);
procarg[1] = $5;
free($2);
free($4);
}
;
optsecondstring
: /* empty */
{ $$ = 0; }
| ',' STRING
{ $$ = strlookup($2); free($2); }
;
onepattern
: { inithall(); startline=lineno;
tokpatlen=0; optexact=0; optstack=0;
}
kills allocates generates yields leaving
{
patindex[npatterns++]=codeindex;
checkhall();
dopattern(0,$2,$3,$4,$5,$6);
freevi($2);
freevi($3);
freevi($4);
freevi($5);
freevi($6);
}
;
morepatterns
: { inithall(); } pattern
| morepatterns { inithall(); } pattern
;
pattern
: stackpattern kills allocates generates yields leaving
{ patindex[NEXT(npatterns,MAXPATTERNS,"Patterns")]=codeindex;
if (hall() && !optexact) saferulefound=1;
dopattern(0,$2,$3,$4,$5,$6);
freevi($2);
freevi($3);
freevi($4);
freevi($5);
freevi($6);
}
;
stackpattern
: WITH optexact
{ startline = lineno; }
setlist optstack
;
optexact
: /* empty */
{ $$ = optexact = 0; }
| EXACT
{ $$ = optexact = 1; }
;
optstack
: /* empty */
{ $$ = optstack = 0; }
| STACK
{ $$ = optstack = 1; }
;
setlist
: /* empty */
{ tokpatlen = 0; }
| setlist tokenset_no
{ NEXT(tokpatlen,TOKPATMAX,"Stack pattern");
tokpatset[tokpatlen-1] = $2;
checkunstacking($2);
}
;
kills
: /* empty */
{ $$ = 0; }
| KILLS kill_list
{ $$ = $2;
if (optstack)
error("No sense in giving kills in this pattern");
}
;
kill_list
: kill_list_el
| kill_list_el ',' kill_list
{ $$=$1; $$->vi_next = $3; }
;
kill_list_el
: tokenset_no { cursetno=$1; } optexpr
{ NEW($$,struct varinfo);
$$->vi_next = 0;
$$->vi_int[0]=$1;
$$->vi_int[1]=$3;
}
;
allocates
: /* empty */
{ $$ = 0; nallreg=0;}
| USES uselist
{ $$ = $2; setallreg($2); }
;
uselist
: uselist_el
{ prophall($1->vi_int[0]); }
| uselist_el ',' uselist
{ prophall($1->vi_int[0]); $$=$1; $$->vi_next=$3; }
;
uselist_el
: property
{ $$=$1; $$->vi_int[1] = 0; }
| property '=' tokeninstance
{ if (!existalmove($3,$$->vi_int[0]))
error("No such move defined");
$$=$1; $$->vi_int[1] = $3.in_index;
}
| REUSING tokeninstance
{ NEW($$,struct varinfo);
$$->vi_next = 0;
$$->vi_int[0] = -1;
$$->vi_int[1] = $2.in_index;
}
;
generates
: /* empty */
{ $$ = 0; }
| GEN genlist
{ $$ = $2; }
;
genlist
: /* empty */
{ $$ = 0; }
| gen_instruction genlist
{ if ($1!=0) {
register struct varinfo *tvip;
$$=tvip=$1;
while (tvip->vi_next!=VI_NULL)
tvip=tvip->vi_next;
tvip->vi_next = $2;
} else {
$$ = $2;
}
}
;
gen_instruction
: {instline = lineno; } IDENT optstar gen_oplist
{ saveline =lineno; lineno=instline;
$$ = gen_inst($2,$3); free($2);
lineno = saveline;
}
| NUMBER ':'
{ $$ = gen_tlab($1); }
| MOVE tokeninstance ',' tokeninstance
{ $$ = gen_move($2,$4); }
| TEST tokeninstance
{ $$ = gen_test($2);}
| RETURN
{ $$ = gen_preturn(); }
;
optstar
: /* empty */
{ $$=0; }
| '*'
{ $$=1; }
| '[' NUMBER ']'
{ $$=$2; }
;
gen_oplist
: '.' /* empty gives conflicts */
{ niops=0; }
| tokeninstance
{ niops=1;iops[0]=$1; }
| gen_oplist ',' tokeninstance
{ iops[niops++] = $3; }
;
yields
: /* empty */
{ $$ = 0; }
| YIELDS yieldlist
{ $$ = $2; }
;
yieldlist
: /* empty */
{ $$ = 0; }
| tokeninstance yieldlist
{ checkstacking($1.in_set);
NEW($$,struct varinfo);
$$->vi_next = $2;
$$->vi_int[0] = $1.in_index;
}
;
leaving
: /* empty */
{ $$ = 0; }
| LEAVING {emhere=1; } leavelist
{ emhere=0; $$ = $3; }
;
leavelist
: leavelist_el
| leavelist_el leavelist
{ $$=$1; $$->vi_next=$2; }
;
leavelist_el
: EMMNEM optexpr
{ NEW($$,struct varinfo);
$$->vi_next=0;
$$->vi_int[0] = $1;
$$->vi_int[1] = $2;
}
;
optstring
: /* empty */
{ $$ = 0; }
| STRING
;
optexpr
: /* empty */
{ $$ = 0; }
| expr
{ $$ = $1.ex_index; } /* type checking ? */
;
tokeninstance
: tokarg subreg
{ $$ = subr_iocc($1,$2); }
| tokarg '.' IDENT
{ $$ = tokm_iocc($1,$3); free($3); }
| IDENT
{ $$ = ident_iocc($1); free($1);}
| allreg subreg
{ $$ = all_iocc($1,$2); }
| '{' IDENT attlist '}'
{ $$ = descr_iocc($2); free($2); }
;
attlist
: /* empty */
{ narexpr = 0; }
| attlist ',' expr
{ arexp[narexpr++] = $3; }
;
emarg
: DOLLAR
{ if ($1<1 || $1>empatlen)
error("Only %d instructions in pattern",empatlen);
$$ = $1;
}
;
tokarg
: PERCENT
{ if ($1<1 || $1>tokpatlen) {
error("Only %d tokens in stackpattern",tokpatlen);
$$ =1;
} else {
$$ = $1;
}
}
;
subreg
: /* empty */
{ $$ = 0; }
| '.' NUMBER
{ if ($2<1 || $2>2) {
error("Only 2 subregisters allowed");
$$ = 1;
} else {
$$ = $2;
}
}
;
allreg
: ALLREG
{ if ($1>=nallreg)
fatal("Only %d registers allocated",nallreg);
$$ = $1;
}
;
expr
: NUMBER
{ $$ = make_expr(TYPINT,EX_CON, (int) ($1 & 0xFFFF), (int) ($1>>16));
}
| emarg
{ $$ = make_expr(argtyp(emmnem[$1-1]),EX_ARG,$1,0); }
| STRING
{ $$ = make_expr(TYPADDR,EX_STRING,strlookup($1),0); free($1); }
| IDENT
{ $$ = ident_expr($1); free($1); }
| tokarg subreg
{ $$ = subreg_expr($1,$2); }
| tokarg '.' IDENT
{ $$ = tokm_expr($1,$3); free($3); }
| allreg subreg
{ $$ = all_expr($1,$2); }
| PERC_IDENT
{ $$ = perc_ident_expr($1); free($1); }
| DEFINED '(' expr ')'
{ $$ = make_expr(TYPBOOL,EX_DEFINED,i_expr($3),0); }
| SAMESIGN '(' expr ',' expr ')'
{ $$ = make_expr(TYPBOOL,EX_SAMESIGN,i_expr($3),i_expr($5)); }
| SFIT '(' expr ',' expr ')'
{ $$ = make_expr(TYPBOOL,EX_SFIT,i_expr($3),i_expr($5)); }
| UFIT '(' expr ',' expr ')'
{ $$ = make_expr(TYPBOOL,EX_UFIT,i_expr($3),i_expr($5)); }
| ROM '(' emarg ',' NUMBER ')'
{ $$ = make_expr(TYPINT,EX_ROM,$3-1,chkincl($5,1,3)-1); }
| LOWW '(' emarg ')'
{ $$ = make_expr(TYPINT,EX_LOWW,$3-1,0); }
| HIGHW '(' emarg ')'
{ $$ = make_expr(TYPINT,EX_HIGHW,$3-1,0); }
| '(' expr ')'
{ $$ = $2; }
| expr CMPEQ expr
{ $$ = make_expr(TYPBOOL,eq2expr($1,$3),$1.ex_index,$3.ex_index); }
| expr CMPNE expr
{ $$ = make_expr(TYPBOOL,ne2expr($1,$3),$1.ex_index,$3.ex_index); }
| expr CMPLT expr
{ $$ = make_expr(TYPBOOL,EX_NCPLT,i_expr($1),i_expr($3)); }
| expr CMPGT expr
{ $$ = make_expr(TYPBOOL,EX_NCPGT,i_expr($1),i_expr($3)); }
| expr CMPLE expr
{ $$ = make_expr(TYPBOOL,EX_NCPLE,i_expr($1),i_expr($3)); }
| expr CMPGE expr
{ $$ = make_expr(TYPBOOL,EX_NCPGE,i_expr($1),i_expr($3)); }
| expr OR2 expr
{ $$ = make_expr(TYPBOOL,EX_OR2,b_expr($1),b_expr($3)); }
| expr AND2 expr
{ $$ = make_expr(TYPBOOL,EX_AND2,b_expr($1),b_expr($3)); }
| expr '+' expr
{ $$ = sum_expr($1,$3); }
| expr '-' expr
{ $$ = make_expr(TYPINT,EX_MINUS,i_expr($1),i_expr($3)); }
| expr '*' expr
{ $$ = make_expr(TYPINT,EX_TIMES,i_expr($1),i_expr($3)); }
| expr '/' expr
{ $$ = make_expr(TYPINT,EX_DIVIDE,i_expr($1),i_expr($3)); }
| expr '%' expr
{ $$ = make_expr(TYPINT,EX_MOD,i_expr($1),i_expr($3)); }
| expr LSHIFT expr
{ $$ = make_expr(TYPINT,EX_LSHIFT,i_expr($1),i_expr($3)); }
| expr RSHIFT expr
{ $$ = make_expr(TYPINT,EX_RSHIFT,i_expr($1),i_expr($3)); }
| NOT expr
{ $$ = make_expr(TYPBOOL,EX_NOT,b_expr($2),0); }
| COMP expr
{ $$ = make_expr(TYPINT,EX_COMP,i_expr($2),0); }
| INREG '(' expr ')'
{ $$ = make_expr(TYPINT,EX_INREG,i_expr($3),0); }
| regvartype
{ $$ = make_expr(TYPINT,EX_CON, $1+1, 0); }
| REGVAR '(' expr optregvartype ')'
{ $$ = regvar_expr($3,$4); }
;
optregvartype
: /* empty */
{ $$ = reg_any; }
| ',' regvartype
{ $$ = $2; }
;
%%
#include "scan.c"