%{ #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 #include extern int lineno; int instline,saveline; int startline; int npatterns; int att_type; int patindex[MAXPATTERNS]; extern set_t l_sets[]; int emhere=0; /* lexical analyzer flag */ int optexact=0; /* Inside "with exact" rule */ int optstack=0; /* Inside with STACK rule */ int saferulefound=0; int maxempatlen=0; int maxrule=0; struct varinfo *defcost; int Xstackflag=0; /* set in coercions, moves, and tests. %1 means something different then. */ 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(),percident_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 ADORNACCESS %token ADORNCC %token INT %token ADDR %token EMMNEM %token NUMBER %token DOLLAR PERCENT ALLREG %token IDENT PERC_IDENT %token STRING %token TIMEFACTOR SIZEFACTOR %token COST %type prop_list property ident_list ident_list_el %type att_list att_list_el att_list_el_list structdecl optcost optformat %type kills allocates yields leaving %type generates kill_list kill_list_el uselist uselist_el genlist yieldlist %type leavelist leavelist_el gen_instruction %type opt_erase_list erase_list erase_list_el %type opt_par_string optstring %type register propno att_list_el_type tokenset_no %type adornlist optstar optuses optregvar regvartype optregvartype %type emarg tokarg subreg allreg optsecondstring %type expr %type tokeninstance %type optexpr optexact optstack %type tokenset %type oplist oplist_el %left OR2 %left AND2 %left '|' %left '^' %left '&' %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_int[0] = $1; $$->vi_str[0] = $2; att_type = $1; } att_list_el_list ';' { $3->vi_next = $4; $$ = $3; } ; att_list_el_list : /* empty */ { $$ = 0; } | ',' IDENT att_list_el_list { NEW($$, struct varinfo); $$->vi_next = $3; $$->vi_int[0] = att_type; $$->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 { Xstackflag = 1; cursetno = $3; } optexpr { Xstackflag = 0; cursetno = -1; } TO tokenset_no { cursetno = $8; tokpatlen=2; tokpatset[0] = $3; tokpatset[1] = $8; tokpatro[0] = 1; Xstackflag = 1; } optexpr { Xstackflag = 0; cursetno = -1; } GEN genlist { tokpatlen=0; tokpatro[0]=0; n_move($3,$5,$8,$10,$13); freevi($13); } | error ; /* Now the test part */ tests : TESTS { Xstackflag = 1; } testdeflist { Xstackflag = 0; } | /* 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); cursetno = -1; } | error ; /* Now the stacks */ stacks : STACKINGRULES { Xstackflag = 1; } stackdeflist { Xstackflag = 0; } ; 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); cursetno = -1; } ; optuses : /* empty */ { $$ = -1; nallreg=0;} | USES propno { $$ = $2; nallreg = 1; allreg[0] = $2; } ; /* Now the one-to-one coercion rules */ coercs : COERCIONS { Xstackflag = 1; } coercdeflist { Xstackflag = 0; } ; 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); cursetno = -1; } ; /* 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); if (l_sets[$2].set_size == 0) { error("Tokenset is empty or has elements with different sizes"); } } ; 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; cursetno = -1; } ; 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); } | PERC_IDENT { if (cursetno < 0) { error("%% not allowed here"); } $$ = percident_iocc($1); free($1); } | '{' 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 { $$ = $1; if ($1<1 || $1>tokpatlen) { error("Only %d tokens in stackpattern",tokpatlen); $$ =1; } if (Xstackflag) $$ = 0; } ; 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 { if (cursetno < 0) { error("%% not allowed here"); } $$ = 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); } /* Excluded, because it causes a shift-reduce conflict (problems with a tokenset_no followed by an optexpr) | '-' expr %prec UMINUS { $$ = make_expr(TYPINT,EX_CON, 0, 0); $$ = make_expr(TYPINT,EX_MINUS,i_expr($$),i_expr($2)); } */ | '(' 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 { $$ = make_expr(TYPINT,EX_OR,i_expr($1),i_expr($3)); } | expr '^' expr { $$ = make_expr(TYPINT,EX_XOR,i_expr($1),i_expr($3)); } | expr '&' expr { $$ = make_expr(TYPINT,EX_AND,i_expr($1),i_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"