%{

#ifndef NORCSID
static char rcsid[]="$Header$";
#endif

/*
 * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands.
 * See the copyright notice in the ACK home directory, in the file "Copyright".
 *
 * Author: Hans van Staveren
 */

#include <stdio.h>
#include <assert.h>
#include <ctype.h>
#include <em_spec.h>
#include <em_flag.h>
#include <em_reg.h>

#define extern
#include "booth.h"
#undef extern

%}

%union {
	int yy_int;
	int *yy_intp;
	string yy_string;
	list1 yy_list1;
	list2 yy_list2;
	expr_t yy_expr;
	cost_t yy_cost;
	set_t yy_set;
	ident_p yy_ident;
	char yy_char;
	inst_t yy_instance;
}

%type <yy_list1> list1,structlistel
%type <yy_list2> structlist,structdecl
%type <yy_expr> expr optexpr
%type <yy_cost> optcost cost optcommacost
%type <yy_int> optboolexpr optnocoerc mnem emargno tokargno optprop
%type <yy_int> optcommabool optstack subreg tokenexpressionno optregvar
%type <yy_int> tokeninstanceno code stackreplacement optslashnumber
%type <yy_set> tokenexpression
%type <yy_instance> tokeninstance
%type <yy_string> optformat
%token <yy_string> IDENT TYPENAME
%token <yy_ident> RIDENT,PIDENT,TIDENT,EIDENT
%token <yy_string> LSTRING,STRING
%token <yy_int> NUMBER
%token <yy_intp> CIDENT
%token REGISTERHEAD TOKENHEAD EXPRESSIONHEAD CODEHEAD MOVEHEAD TESTHEAD STACKHEAD
%token REGVAR INREG LOOP POINTER FLOAT
%token TIMEFAC SIZEFAC FORMAT RETURN
%token MOVE ERASE ALLOCATE ELLIPS COST REMOVE STACK
%token SEP SAMESIGN SFIT UFIT ROM DEFINED TOSTRING LOWW HIGHW
%token NOCC SETCC SAMECC TEST NOCOERC
%token <yy_char> LCASELETTER
%start machinespec

%left OR2
%left AND2
%left CMPEQ,CMPNE
%left CMPLT,CMPLE,CMPGT,CMPGE
%left RSHIFT,LSHIFT
%left '+','-'
%left '*','/','%'
%nonassoc NOT,COMP,UMINUS
%nonassoc '$'
%%
machinespec
	: rcsid constants registersection tokensection
		{ inbetween(); }
	  expressionsection codesection movesection testsection stacksection
	;

rcsid
	: /* empty */
	| STRING
		{ strlookup($1); }
	;

constants
	: /* empty */
	| constants CIDENT '=' NUMBER
		{ *$2 = $4; }
	| constants SIZEFAC '=' NUMBER optslashnumber
		{ cc1 = $4; cc2 = $5; }
	| constants TIMEFAC '=' NUMBER optslashnumber
		{ cc3 = $4; cc4 = $5; }
	| constants FORMAT '=' STRING
		{ fmt = $4; }
	;
optslashnumber
	: /* empty */
		{ $$ = 1; }
	| '/' NUMBER
		{ $$ = $2; }
	;

registersection
	: REGISTERHEAD registerdefs
	;
registerdefs
	: /* empty */
	| registerdefs registerdef
	;

registerdef
	: IDENT '=' '(' STRING ',' NUMBER list1 ')' optregvar list1 '.'
		{       register ident_p ip;
			register list1 l;
			register reginfo r;
			int i;

			r=(reginfo) myalloc(sizeof(struct reginfo));
			r->rname = $1;
			r->rrepr = $4;
			r->rsize = $6;
			if($9>=0 && $7!=0)
				yyerror("No subregisters allowed in regvar");
			for (i=0;i<MAXMEMBERS;i++)
				r->rmembers[i] = 0;
			i=0;
			for (l=$7;l!=0;l=l->l1next) {
				ip=ilookup(l->l1name,LOOKUP);
				if (ip->i_type != IREG)
					yyerror("Bad member of set");
				chktabsiz(i,MAXMEMBERS,"Member of register");
				r->rmembers[i++] = ip->i_i.i_regno;
			}
			maxmembers=max(maxmembers,i);
			r->rregvar=$9;
			if ($9>=0) {
				rvused=1;
				chktabsiz(nregvar[$9],MAXREGVARS,"Regvar");
				rvnumbers[$9][nregvar[$9]++] = nmachregs;
			}
			for(i=0;i<PROPSETSIZE;i++)
				r->rprop[i] = 0;
			ip=ilookup($1,ENTER);
			ip->i_type=IREG;
			ip->i_i.i_regno=nmachregs;
			for (l = $10; l!= 0; l=l->l1next) {
				ip = ilookup(l->l1name,HALFWAY);
				if (ip->i_type) {
					if (ip->i_type != IPRP)
						yyerror("Multiple defined symbol");
					else if(machprops[ip->i_i.i_prpno].propset.set_size != r->rsize)
						yyerror("property has more than 1 size");
				} else {
					chktabsiz(nprops,MAXPROPS,"Property");
					ip->i_type = IPRP;
					ip->i_i.i_prpno = nprops;
					machprops[nprops].propname = ip;
					machprops[nprops++].propset.set_size = r->rsize;
				}
				r->rprop[ip->i_i.i_prpno>>4] |= (1<<(ip->i_i.i_prpno&017));
			}
			chktabsiz(nmachregs,MAXREGS,"Register table");
			machregs[nmachregs++] = r;
		}
	| error '.'
	;

optregvar
	: /* nothing */
		{ $$ = -1; }
	| REGVAR
		{ $$ = reg_any; }
	| REGVAR '(' LOOP ')'
		{ $$ = reg_loop; }
	| REGVAR '(' POINTER ')'
		{ $$ = reg_pointer; }
	| REGVAR '(' FLOAT ')'
		{ $$ = reg_float; }
	;

tokensection
	: TOKENHEAD tkdefs
	;
tkdefs
	: /* empty */
	| tkdefs tkdef
	;
tkdef
	: IDENT '=' structdecl NUMBER optcost optformat
		{ register token_p tp;
		  register ident_p ip;

		  chktabsiz(nmachtokens,MAXTOKENS,"Token table");
		  tp = &machtokens[nmachtokens];
		  tp->t_name = $1;
		  tp->t_struct = $3;
		  tp->t_size = $4;
		  tp->t_cost = $5;
		  ip = ilookup($1,ENTER);
		  ip->i_type = ITOK;
		  ip->i_i.i_tokno = nmachtokens++;
		  maxtokensize=max(maxtokensize,structsize($3));
		  setfields(tp,$6);
		}
	| error
	;
structdecl
	: '{' structlist '}'
		{ $$ = lookstruct($2); }
	;
structlist
	: /* empty */
		{ $$=0; }
	| structlistel structlist
		{ $$=(list2) myalloc(sizeof(struct list2str));
		  $$->l2next = $2;
		  $$->l2list = $1;
		}
	;
structlistel
	: TYPENAME list1 ';'
		{ $$=(list1) myalloc(sizeof(struct list1str));
		  $$->l1next = $2;
		  $$->l1name = $1;
		}
	;

optcost : /* empty */
		{ $$.c_size = $$.c_time = 0; }
	| COST '=' '(' expr ',' expr ')'
		{ MUST2BEINT($4,$6);
		  $$.c_size = exp1;
		  $$.c_time = exp2;
		}
	;
optformat
	: /* empty */
		{ $$ = 0; }
	| STRING
	;

expressionsection
	: /* empty */
	| EXPRESSIONHEAD tokenexpressions
	;
tokenexpressions
	: tokenexpressionline
	| tokenexpressionline tokenexpressions
	;
tokenexpressionline
	: IDENT '=' tokenexpression
		{
		  {     register ident_p ip;

			chktabsiz(nmachsets,MAXSETS,"Expression table");
			machsets[nmachsets] = $3;
			ip=ilookup($1,ENTER);
			ip->i_type = IEXP;
			ip->i_i.i_expno = nmachsets++;
		  }
		}
	| error
	;
tokenexpression
	: PIDENT
		{ $$ = machprops[$1->i_i.i_prpno].propset; }
	| TIDENT
		{ register i;

		  for(i=0;i<SETSIZE;i++) $$.set_val[i]=0;
		  $$.set_val[($1->i_i.i_tokno+nmachregs+1)>>4] |=
			01<<(($1->i_i.i_tokno+nmachregs+1)&017);
		  $$.set_size = machtokens[$1->i_i.i_tokno].t_size;
		}
	| EIDENT
		{ $$=machsets[$1->i_i.i_expno]; }
	| tokenexpression '*' tokenexpression
		{ register i;

		  if (($$.set_size=$1.set_size)==0)
			$$.set_size = $3.set_size;
		  for (i=0;i<SETSIZE;i++)
			$$.set_val[i] = $1.set_val[i] & $3.set_val[i];
		}
	| tokenexpression '+' tokenexpression
		{ register i;

		  if ($1.set_size == -1)
			$$.set_size = $3.set_size;
		  else if ($3.set_size == -1)
			$$.set_size = $1.set_size;
		  else if ($1.set_size == $3.set_size)
			$$.set_size = $1.set_size;
		  else
			$$.set_size = 0;
		  for (i=0;i<SETSIZE;i++)
			$$.set_val[i] = $1.set_val[i] | $3.set_val[i];
		}
	| tokenexpression '-' tokenexpression
		{ register i;

		  if ($1.set_size == -1)
			$$.set_size = $3.set_size;
		  else if ($3.set_size == -1)
			$$.set_size = $1.set_size;
		  else if ($1.set_size == $3.set_size)
			$$.set_size = $1.set_size;
		  else
			$$.set_size = 0;
		  for (i=0;i<SETSIZE;i++)
			$$.set_val[i] = $1.set_val[i] & ~ $3.set_val[i];
		}
	| '(' tokenexpression ')'
		{ $$ = $2; }
	;

codesection
	: CODEHEAD coderules
	;
coderules
	: coderule
	| coderules coderule
	;
coderule
	: { nallreg=emrepllen=tokrepllen=0; }
		empattern SEP stackpattern SEP code SEP stackreplacement SEP
		emreplacement SEP cost
			{ int i;

			  if (emrepllen) {
				outbyte(DO_EMREPLACE+(emrepllen<<5));
				for (i=0;i<emrepllen;i++) {
					out(replmnem[i]);
					out(replexpr[i]);
				}
			  }
			  if ($8==0) {
				  outbyte(DO_TOKREPLACE+(tokrepllen<<5));
				  for(i=0;i<tokrepllen;i++)
					out(replinst[i]);
			  } else {
				static int warncount=0;
				if (!warncount++)
					fprintf(stderr,
		"WARNING: convert to stacksection, will disappear soon");
				outbyte(DO_TOKREPLACE);
			  }
			  if ($12.c_size!=0 || $12.c_time!=0) {
				  outbyte(DO_COST);
				  out($12.c_size);
				  out($12.c_time);
			  }
			  outbyte(empatlen==0? DO_RETURN : DO_NEXTEM);
			  fprintf(cfile,"\n");
			  ncoderules++;
			  maxallreg=max(maxallreg,nallreg);
			  if (empatlen==0) { /* coercion */
				if (tokrepllen<1 && $8==0)
					yyerror("No replacement in coercion");
				if (tokpatlen>1)
					yyerror("Token pattern too long");
				if ($8!=0) { /* stacking */
					c1_p cp;
					chktabsiz(nc1,MAXC1,"Coerc table 1");
					cp = &c1coercs[nc1++];
					cp->c1_texpno = pattokexp[1];
					cp->c1_prop = -1;
					cp->c1_codep = $6;
				} else if (tokrepllen>1) { /* splitting */
					c2_p cp;
					chktabsiz(nc2,MAXC2,"Coerc table 2");
					cp= &c2coercs[nc2++];
					cp->c2_texpno = pattokexp[1];
					cp->c2_nsplit = tokrepllen;
					maxsplit=max(maxsplit,tokrepllen);
					for (i=0;i<tokrepllen;i++)
						cp->c2_repl[i] = replinst[i];
					cp->c2_codep = $6;
					if (nallreg>0)
						yyerror("No allocates allowed here");
				} else { /* one to one coercion */
					c3_p cp;
					chktabsiz(nc3,MAXC3,"Coerc table 3");
					cp= &c3coercs[nc3++];
					if (tokpatlen)
						cp->c3_texpno = pattokexp[1];
					else
						cp->c3_texpno = 0;
					if (nallreg>1)
						yyerror("Too many allocates in coercion");
					cp->c3_prop = nallreg==0 ? 0 : allreg[0];
					cp->c3_repl = replinst[0];
					cp->c3_codep = $6;
				}
			  }
			}
	| error
	;
empattern
	: /* empty */
		{ empatlen=0; }
	| mnemlist optboolexpr
		{ register i;

		  empatexpr = $2;
		  patbyte(0);
		  patshort(prevind);
		  prevind = npatbytes - 3;
		  maxempatlen = max(empatlen,maxempatlen);
		  pat(empatlen);
		  for(i=1;i<=empatlen;i++)
			patbyte(patmnem[i]);
		  pat(empatexpr);
		  rulecount = npatbytes;
		  patbyte(1);   /* number of different rules with this pattern */
		  pat(codebytes);       /* first rule */
		}
	| ELLIPS
		{ pattern[rulecount]++;
		  maxrule= max(maxrule,pattern[rulecount]);
		  pat(codebytes);
		}
	;

mnemlist
	:       mnem
		{ empatlen = 1; patmnem[empatlen] = $1; }
	|       mnemlist mnem
		{ chktabsiz(empatlen+1,MAXEMPATLEN,"EM pattern");
		  patmnem[++empatlen] = $2;
		}
	;
mnem    :       IDENT
		{ if(strlen($1)!=3 || ($$=mlookup($1))==0)
			yyerror("not an EM-mnemonic");
		}
	;

stackpattern
	: optnocoerc tokenexpressionlist optstack
		{ register i;

		  if (tokpatlen != 0) {
			  outbyte(($1 ? ( $3 ? DO_XXMATCH: DO_XMATCH ) : DO_MATCH)+(tokpatlen<<5));
			  for(i=1;i<=tokpatlen;i++) {
				out(pattokexp[i]);
			  }
		  }
		  if ($3 && tokpatlen==0 && empatlen==0) {
			  outbyte(DO_COERC);
		  }
		  if ($3 && !$1 && empatlen!=0) {
			outbyte(DO_REMOVE);
			out(allexpno);
		  }
		}
	;

optnocoerc
	: /* empty */
		{ $$ = 0; }
	| NOCOERC ':'
		{ $$ = 1; }
	;

tokenexpressionlist
	: /* empty */
		{ tokpatlen = 0; }
	| tokenexpressionlist tokenexpressionno
		{ chktabsiz(tokpatlen+1,MAXPATLEN,"Token pattern");
		  pattokexp[++tokpatlen] = $2;
		  if (machsets[$2].set_size==0)
			yyerror("Various sized set in tokenpattern");
		}
	;

tokenexpressionno
	: tokenexpression
		{ $$ = exprlookup($1); }
	;

optstack
	:       /* empty */
		{ $$ = 0; }
	|       STACK
		{ $$ = 1; }
	;

code    :
		{ $$ = codebytes; cchandled=ccspoiled=0; }
	  initcode restcode
		{ if (cchandled==0 && ccspoiled!=0) {
			outbyte(DO_ERASE);
			out(ccregexpr);
		  }
		}
	;

initcode
	: /* empty */
	| initcode remove
	| initcode allocate
	;
remove
	: REMOVE '(' tokenexpressionno
		{ curtokexp = $3; }
	  optcommabool ')'
		{ outbyte(DO_REMOVE+ ($5!=0 ? 32 : 0));
		  out($3);
		  if ($5!=0) out($5);
		}
	| REMOVE '(' expr ')'
		{ if ($3.expr_typ != TYPREG)
			yyerror("Expression must be register");
		  outbyte(DO_RREMOVE);
		  out($3.expr_index);
		}
	;
optcommabool
	: /* empty */
		{ $$ = 0; }
	| ',' expr
		{ MUST1BEBOOL($2);
		  $$ = exp1;
		}
	;

restcode: /* empty */
	| restcode LSTRING expr
		{ outbyte(DO_LOUTPUT);
		  out(stringno($2));
		  free($2);
		  out($3.expr_index);
		  ccspoiled++;
		}
	| restcode stringlist
		{ int i;
		  for(i=0;nstr>0;i++,nstr--) {
			if (i%8==0) outbyte(DO_ROUTPUT+(nstr>7 ? 7 : nstr-1)*32);
			out(strar[i]);
		  }
		  ccspoiled++;
		}
	| restcode RETURN
		{ outbyte(DO_PRETURN); }
	| restcode move
	| restcode erase
	| restcode NOCC
		{ outbyte(DO_ERASE);
		  out(ccregexpr);
		  cchandled++;
		}
	| restcode SAMECC
		{ cchandled++; }
	| restcode SETCC '(' tokeninstanceno ')'
		{ outbyte(DO_MOVE);
		  out(ccinstanceno);
		  out($4);
		  cchandled++;
		}
	| restcode TEST '(' tokeninstanceno ')'
		{ outbyte(DO_MOVE);
		  out($4);
		  out(ccinstanceno);
		  ccspoiled=0;
		}
	;

stringlist
	: STRING
		{ nstr=1;
		  strar[0]=stringno($1);
		  free($1);
		}
	| stringlist STRING
		{ chktabsiz(nstr,MAXNSTR,"Consecutiv strings");
		  strar[nstr++] = stringno($2);
		  free($2);
		}
	;

move
	: MOVE '(' tokeninstanceno ',' tokeninstanceno ')'
		{ outbyte(DO_MOVE);
		  out($3);
		  out($5);
		}
	;

erase
	: ERASE '(' expr ')'
		{ outbyte(DO_ERASE);
		  out($3.expr_index);
		  if($3.expr_typ != TYPREG)
			yyerror("Bad argument of erase");
		}
	;

allocate
	: ALLOCATE { dealflag=0; } '(' alloclist ')'
		{ if (dealflag)
			outbyte(DO_REALLOCATE);
		}
	;


alloclist
	: allocel
	| alloclist optcomma allocel
	;

allocel
	: tokeninstanceno       /* deallocate */
		{ outbyte(DO_DEALLOCATE);
		  out($1);
		  dealflag++;
		}
	| PIDENT
		{ allreg[nallreg++] = $1->i_i.i_prpno;
		  outbyte(DO_ALLOCATE);
		  out($1->i_i.i_prpno);
		}
	| PIDENT '=' tokeninstanceno
		{ allreg[nallreg++] = $1->i_i.i_prpno;
		  outbyte(DO_ALLOCATE+32);
		  out($1->i_i.i_prpno);
		  out($3);
		}
	;

stackreplacement
	: /* empty */
		{ $$=0; }
	| STACK
		{ $$=1; }
	| '{' STACK '}'
		{ $$=1; }
	| stackrepllist
		{ $$=0; }
	;
stackrepllist
	: tokeninstanceno
		{ tokrepllen=1; replinst[0] = $1; }
	| stackrepllist tokeninstanceno
		{ chktabsiz(tokrepllen+1,MAXPATLEN,"Stack replacement");
		  replinst[tokrepllen++] = $2;
		}
	;

emreplacement
	: /* empty, normal case */
	| emrepllist
	;
emrepllist
	: mnem optexpr
		{ emrepllen=1;
		  replmnem[0]=$1;
		  replexpr[0]=$2.expr_index;
		}
	| emrepllist mnem optexpr
		{ chktabsiz(emrepllen+1,MAXEMPATLEN,"EM replacement");
		  replmnem[emrepllen]=$2;
		  replexpr[emrepllen]=$3.expr_index;
		  emrepllen++;
		}
	;

cost    : /* empty */
		{ $$.c_size = $$.c_time = 0;
		}
	| '(' expr ',' expr ')'
		{ MUST2BEINT($2,$4);
		  $$.c_size = exp1;
		  $$.c_time = exp2;
		}
	| cost '+' '%' '[' tokargno ']'
		{ $$.c_size = lookup(1,EX_PLUS,$1.c_size,
					lookup(0,EX_COST,$5,0));
		  $$.c_time = lookup(1,EX_PLUS,$1.c_time,
					lookup(0,EX_COST,$5,1));
		}
	;

movesection
	: MOVEHEAD movedefs
	;

movedefs
	: movedef
	| movedefs movedef
	;

movedef
	: '(' tokenexpressionno
		{ curtokexp = $2; }
	  optboolexpr ',' tokenexpressionno
		{ curtokexp = $6;
		  pattokexp[1] = $2;
		  pattokexp[2] = $6;
		  tokpatlen=2;
		}
	  optboolexpr ',' code optcommacost ')'
		{ register move_p mp;

		  outbyte(DO_RETURN);
		  fprintf(cfile,"\n");
		  chktabsiz(nmoves,NMOVES,"Move definition table");
		  mp = &machmoves[nmoves++];
		  mp->m_set1 = $2;
		  mp->m_expr1= $4;
		  mp->m_set2 = $6;
		  mp->m_expr2= $8;
		  mp->m_cindex=$10;
		  mp->m_cost = $11;
		}
	| error
	;

testsection
	: /* empty */
	| TESTHEAD testdefs
	;

testdefs: testdef
	| testdefs testdef
	;

testdef : '(' tokenexpressionno
		{ curtokexp = $2;
		  pattokexp[1] = $2;
		  pattokexp[2] = cocosetno;
		  tokpatlen=2;
		}
	  optboolexpr ',' code optcommacost ')'
		{ register move_p mp;

		  outbyte(DO_RETURN);
		  fprintf(cfile,"\n");
		  chktabsiz(nmoves,NMOVES,"Move definition table(tests)");
		  mp = &machmoves[nmoves++];
		  mp->m_set1 = $2;
		  mp->m_expr1 = $4;
		  mp->m_set2 = cocosetno;
		  mp->m_expr2 = 0;
		  mp->m_cindex = $6;
		  mp->m_cost = $7;
		}
	;

stacksection
	: STACKHEAD stackdefs
	| /* empty */
	;
stackdefs
	: stackdef
	| stackdefs stackdef
	;
stackdef
	: '(' tokenexpressionno
		{ curtokexp = $2;
		  pattokexp[1] = $2;
		  tokpatlen=1;
		}
	  optboolexpr ',' optprop ',' code optcommacost ')'
		{ register c1_p cp;

		  outbyte(DO_TOKREPLACE);
		  outbyte(DO_RETURN);
		  fprintf(cfile,"\n");
		  chktabsiz(nc1,MAXC1,"Stacking table");
		  cp = &c1coercs[nc1++];
		  cp->c1_texpno = $2;
		  cp->c1_expr = $4;
		  cp->c1_prop = $6;
		  cp->c1_codep = $8;
		  cp->c1_cost = $9;
		}
	;

optprop
	: /* empty */
		{ $$ = -1; }
	| PIDENT 
		{ $$ = $1->i_i.i_prpno; }
	;

optcommacost
	: /* empty */
		{ $$.c_size = 0; $$.c_time = 0;}
	| ',' cost
		{ $$ = $2; }
	;

list1   : /* empty */
		{ $$ = 0; }
	| optcomma IDENT list1
		{ $$=(list1) myalloc(sizeof(struct list1str));
		  $$->l1next = $3;
		  $$->l1name = $2;
		}
	;
optcomma: /* nothing */
	| ','
	;
emargno : NUMBER
		{ if ($1<1 || $1>empatlen)
			yyerror("Number after $ out of range");
		  $$ = $1;
		}
	;
tokargno
	: NUMBER
		{ if ($1<1 || $1>tokpatlen)
			yyerror("Number within %[] out of range");
		  $$ = $1;
		}
	;
expr    : '$' emargno
		{ $$.expr_index = lookup(0,EX_ARG,$2,0); $$.expr_typ = argtyp(patmnem[$2]);
		}
	| NUMBER
		{ $$.expr_index = lookup(0,EX_CON,(int)($1&0177777),(int)($1>>16));
		  $$.expr_typ = TYPINT;
		}
	| STRING
		{ $$.expr_index = lookup(0,EX_STRING,strlookup($1),0);
		  $$.expr_typ = TYPSTR;
		}
	| RIDENT
		{ $$.expr_index = lookup(0,EX_REG,$1->i_i.i_regno,0);
		  $$.expr_typ = TYPREG;
		}
	| '%' '[' tokargno '.' IDENT ']'
		{ $$.expr_index = lookup(0,EX_TOKFIELD,$3,
		     findstructel(pattokexp[$3],$5,&$$.expr_typ));
		}
	| '%' '[' tokargno subreg ']'
		{ chkregexp(pattokexp[$3]);
		  $$.expr_index = lookup(0,EX_SUBREG,$3,$4);
		  $$.expr_typ = TYPREG;
		}
	| '%' '[' LCASELETTER subreg ']'
		{ if ($3 >= 'a'+nallreg)
			yyerror("Bad letter in %[x] construct");
		  $$.expr_index = lookup(0,EX_ALLREG,$3-'a'+1,$4);
		  $$.expr_typ = TYPREG;
		}
	| '%' '[' IDENT ']'
		{ $$.expr_index = lookup(0,EX_TOKFIELD,0,
		     findstructel(curtokexp,$3,&$$.expr_typ));
		}
	| TOSTRING '(' expr ')'
		{ MUST1BEINT($3);
		  $$.expr_index = lookup(0,EX_TOSTRING,exp1,0);
		  $$.expr_typ = TYPSTR;
		}
	| DEFINED '(' expr ')'
		{ $$.expr_index = lookup(0,EX_DEFINED,$3.expr_index,0);
		  $$.expr_typ = TYPBOOL;
		}
	| SAMESIGN '(' expr ',' expr ')'
		{ MUST2BEINT($3,$5);
		  $$.expr_index = lookup(1,EX_SAMESIGN,exp1,exp2);
		  $$.expr_typ = TYPBOOL;
		}
	| SFIT '(' expr ',' expr ')'
		{ MUST2BEINT($3,$5);
		  $$.expr_index = lookup(0,EX_SFIT,exp1,exp2);
		  $$.expr_typ = TYPBOOL;
		}
	| UFIT '(' expr ',' expr ')'
		{ MUST2BEINT($3,$5);
		  $$.expr_index = lookup(0,EX_UFIT,exp1,exp2);
		  $$.expr_typ = TYPBOOL;
		}
	| ROM '(' emargno ',' NUMBER ')'
		{ if ($5<1 || $5>3)
			yyerror("Second argument of rom must be >=1 and <=3");
		  $$.expr_index = lookup(0,EX_ROM,$3-1,$5-1);
		  $$.expr_typ = TYPINT;
		}
	| LOWW '(' emargno ')'
		{
		  $$.expr_index = lookup(0,EX_LOWW,$3-1,0);
		  $$.expr_typ = TYPINT;
		}
	| HIGHW '(' emargno ')'
		{
		  $$.expr_index = lookup(0,EX_HIGHW,$3-1,0);
		  $$.expr_typ = TYPINT;
		}
	| '(' expr ')'
		{ $$ = $2; }
	| expr CMPEQ expr
		{ switch(commontype($1,$3)) {
		  case TYPINT:
			$$.expr_index = lookup(1,EX_NCPEQ,$1.expr_index,$3.expr_index);
			break;
		  case TYPSTR:
			$$.expr_index = lookup(1,EX_SCPEQ,$1.expr_index,$3.expr_index);
			break;
		  case TYPREG:
			$$.expr_index = lookup(1,EX_RCPEQ,$1.expr_index,$3.expr_index);
			break;
		  }
		  $$.expr_typ = TYPBOOL;
		}
	| expr CMPNE expr
		{ switch(commontype($1,$3)) {
		  case TYPINT:
			$$.expr_index = lookup(1,EX_NCPNE,$1.expr_index,$3.expr_index);
			break;
		  case TYPSTR:
			$$.expr_index = lookup(1,EX_SCPNE,$1.expr_index,$3.expr_index);
			break;
		  case TYPREG:
			$$.expr_index = lookup(1,EX_RCPNE,$1.expr_index,$3.expr_index);
			break;
		  }
		  $$.expr_typ = TYPBOOL;
		}
	| expr CMPGT expr
		{ MUST2BEINT($1,$3);
		  $$.expr_index = lookup(0,EX_NCPGT,exp1,exp2);
		  $$.expr_typ = TYPBOOL;
		}
	| expr CMPGE expr
		{ MUST2BEINT($1,$3);
		  $$.expr_index = lookup(0,EX_NCPGE,exp1,exp2);
		  $$.expr_typ = TYPBOOL;
		}
	| expr CMPLT expr
		{ MUST2BEINT($1,$3);
		  $$.expr_index = lookup(0,EX_NCPLT,exp1,exp2);
		  $$.expr_typ = TYPBOOL;
		}
	| expr CMPLE expr
		{ MUST2BEINT($1,$3);
		  $$.expr_index = lookup(0,EX_NCPLE,exp1,exp2);
		  $$.expr_typ = TYPBOOL;
		}
	| expr OR2 expr
		{ MUST2BEBOOL($1,$3);
		  $$.expr_index = lookup(0,EX_OR2,exp1,exp2);
		  $$.expr_typ = TYPBOOL;
		}
	| expr AND2 expr
		{ MUST2BEBOOL($1,$3);
		  $$.expr_index = lookup(0,EX_AND2,exp1,exp2);
		  $$.expr_typ = TYPBOOL;
		}
	| expr '+' expr
		{ switch(commontype($1,$3)) {
		  case TYPINT:
			$$.expr_index = lookup(1,EX_PLUS,$1.expr_index,$3.expr_index);
			break;
		  case TYPSTR:
			$$.expr_index = lookup(0,EX_CAT,$1.expr_index,$3.expr_index);
			break;
		  default:
			yyerror("Bad types");
		  }
		  $$.expr_typ = $1.expr_typ;
		}
	| expr '-' expr
		{ MUST2BEINT($1,$3);
		  $$.expr_index = lookup(0,EX_MINUS,exp1,exp2);
		  $$.expr_typ = TYPINT;
		}
	| expr '*' expr
		{ MUST2BEINT($1,$3);
		  $$.expr_index = lookup(1,EX_TIMES,exp1,exp2);
		  $$.expr_typ = TYPINT;
		}
	| expr '/' expr
		{ MUST2BEINT($1,$3);
		  $$.expr_index = lookup(0,EX_DIVIDE,exp1,exp2);
		  $$.expr_typ = TYPINT;
		}
	| expr '%' expr
		{ MUST2BEINT($1,$3);
		  $$.expr_index = lookup(0,EX_MOD,exp1,exp2);
		  $$.expr_typ = TYPINT;
		}
	| expr LSHIFT expr
		{ MUST2BEINT($1,$3);
		  $$.expr_index = lookup(0,EX_LSHIFT,exp1,exp2);
		  $$.expr_typ = TYPINT;
		}
	| expr RSHIFT expr
		{ MUST2BEINT($1,$3);
		  $$.expr_index = lookup(0,EX_RSHIFT,exp1,exp2);
		  $$.expr_typ = TYPINT;
		}
	| NOT expr
		{ MUST1BEBOOL($2);
		  $$.expr_index = lookup(0,EX_NOT,exp1,0);
		  $$.expr_typ = TYPBOOL;
		}
	| COMP expr
		{ MUST1BEINT($2);
		  $$.expr_index = lookup(0,EX_COMP,exp1,0);
		  $$.expr_typ = TYPINT;
		}
	| INREG '(' expr ')'
		{ MUST1BEINT($3);
		  $$.expr_index = lookup(0,EX_INREG,exp1,0);
		  $$.expr_typ = TYPINT;
		}
	| REGVAR '(' expr ')'
		{ MUST1BEINT($3);
		  $$.expr_index = lookup(0,EX_REGVAR,exp1,0);
		  $$.expr_typ = TYPREG;
		}
/*
	| '-' expr %prec UMINUS
		{ MUST1BEINT($2);
		  $$.expr_index = lookup(0,EX_UMINUS,exp1,0);
		  $$.expr_typ = TYPINT;
		}
*/
	;

subreg  : /* empty */
		{ $$=0; }
	| '.' NUMBER
		{ $$=$2; }
	;

optboolexpr
	: /* empty */
		{ $$ = 0; }
	| expr
		{ MUST1BEBOOL($1);
		  $$=exp1;
		}
	;
optexpr
	: /* empty */
		{ $$.expr_typ=0;
		  $$.expr_index=0;
		}
	| expr
	;

tokeninstanceno
	: tokeninstance
		{ $$ = instno($1); }
	;

tokeninstance
	: '%' '[' tokargno subreg ']'
		{ register i;

		  if ($4!=0)
			  chkregexp(pattokexp[$3]);
		  $$.in_which = IN_COPY;
		  $$.in_info[0] = $3;
		  $$.in_info[1] = $4;
		  for (i=2;i<TOKENSIZE;i++)
			$$.in_info[i] = 0;
		}
	| '%' '[' tokargno '.' IDENT ']'
		{ int typ;
		  register i;
		  $$.in_which = IN_COPY;
		  $$.in_info[0] = $3;
		  $$.in_info[1] = findstructel(pattokexp[$3],$5,&typ);
		  if (typ != TYPREG)
			yyerror("Must be register");
		  for (i=2;i<TOKENSIZE;i++)
			$$.in_info[i] = 0;
		}
	| RIDENT
		{ register i;
		  $$.in_which = IN_RIDENT;
		  $$.in_info[0] = $1->i_i.i_regno;
		  for (i=1;i<TOKENSIZE;i++)
			$$.in_info[i] = 0;
		}
	| REGVAR '(' expr ')'
		{ register i;
		  MUST1BEINT($3);
		  $$.in_which = IN_REGVAR;
		  $$.in_info[0] = exp1;
		  for (i=1;i<TOKENSIZE;i++)
			$$.in_info[i] = 0;
		}
	| '%' '[' LCASELETTER subreg ']'
		{ register i;
		  if ($3 >= 'a'+nallreg)
			yyerror("Bad letter in %[x] construct");
		  $$.in_which = IN_ALLOC;
		  $$.in_info[0] = $3-'a';
		  $$.in_info[1] = $4;
		  for (i=2;i<TOKENSIZE;i++)
			$$.in_info[i] = 0;
		}
	| '{' TIDENT attlist '}'
		{ register i;
		  $$.in_which = IN_DESCR;
		  $$.in_info[0] = $2->i_i.i_tokno;
		  for(i=0;i<narexp;i++) {
			if (arexp[i].expr_typ !=
			      machtokens[$2->i_i.i_tokno].t_fields[i].t_type)
				yyerror("Attribute %d has wrong type",i+1);
			$$.in_info[i+1] = arexp[i].expr_index;
		  }
		  for (i=narexp+1;i<TOKENSIZE;i++) {
			if (machtokens[$2->i_i.i_tokno].t_fields[i-1].t_type!=0)
				yyerror("Too few attributes");
			$$.in_info[i] = 0;
		  }
		}
	;

attlist
	: /* empty */
		{ narexp = 0; }
	| attlist ',' expr
		{ arexp[narexp++] = $3; }
	;

%%
