rename yacc so we can make sure we get it.
diff --git a/src/cmd/9yacc.c b/src/cmd/9yacc.c
new file mode 100644
index 0000000..6614414
--- /dev/null
+++ b/src/cmd/9yacc.c
@@ -0,0 +1,2939 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <ctype.h>
+
+#define	Bungetrune	Bungetc		/* ok for now. */
+
+/*
+ * all these are 32 bit
+ */
+#define TBITSET		((32+NTERMS)/32)	/* BOTCH?? +31 */
+#define BIT(a,i)	((a)[(i)>>5] & (1<<((i)&037)))
+#define SETBIT(a,i)	((a)[(i)>>5] |= (1<<((i)&037)))
+#define NWORDS(n)	(((n)+32)/32)
+
+#define PARSER		"#9/lib/yaccpar"
+#define PARSERS		"#9/lib/yaccpars"
+#define TEMPNAME	"y.tmp.XXXXXX"
+#define ACTNAME		"y.acts.XXXXXX"
+#define OFILE		"tab.c"
+#define FILEU		"output"
+#define FILED		"tab.h"
+#define FILEDEBUG	"debug"
+
+enum
+{
+/*
+ * the following are adjustable
+ * according to memory size
+ */
+	ACTSIZE		= 40000,
+	MEMSIZE		= 40000,
+	NSTATES		= 2000,
+	NTERMS		= 511,
+	NPROD		= 1600,
+	NNONTERM	= 600,
+	TEMPSIZE	= 2000,
+	CNAMSZ		= 10000,
+	LSETSIZE	= 2400,
+	WSETSIZE	= 350,
+
+	NAMESIZE	= 50,
+	NTYPES		= 63,
+	ISIZE		= 400,
+
+	PRIVATE		= 0xE000,	/* unicode private use */
+
+	/* relationships which must hold:
+		TBITSET ints must hold NTERMS+1 bits...
+		WSETSIZE >= NNONTERM
+		LSETSIZE >= NNONTERM
+		TEMPSIZE >= NTERMS + NNONTERM + 1
+		TEMPSIZE >= NSTATES
+	*/
+
+	NTBASE		= 010000,
+	ERRCODE		= 8190,
+	ACCEPTCODE	= 8191,
+
+	NOASC		= 0,	/* no assoc. */
+	LASC		= 1,	/* left assoc. */
+	RASC		= 2,	/* right assoc. */
+	BASC		= 3,	/* binary assoc. */
+
+	/* flags for state generation */
+
+	DONE		= 0,
+	MUSTDO		= 1,
+	MUSTLOOKAHEAD	= 2,
+
+	/* flags for a rule having an action, and being reduced */
+
+	ACTFLAG		= 04,
+	REDFLAG		= 010,
+
+	/* output parser flags */
+	YYFLAG1		= -1000,
+
+	/* parse tokens */
+	IDENTIFIER	= PRIVATE,
+	MARK,
+	TERM,
+	LEFT,
+	RIGHT,
+	BINARY,
+	PREC,
+	LCURLY,
+	IDENTCOLON,
+	NUMBER,
+	START,
+	TYPEDEF,
+	TYPENAME,
+	UNION,
+
+	ENDFILE		= 0,
+
+	EMPTY		= 1,
+	WHOKNOWS	= 0,
+	OK		= 1,
+	NOMORE		= -1000,
+};
+
+	/* macros for getting associativity and precedence levels */
+
+#define ASSOC(i)	((i)&03)
+#define PLEVEL(i)	(((i)>>4)&077)
+#define TYPE(i)		(((i)>>10)&077)
+
+	/* macros for setting associativity and precedence levels */
+
+#define SETASC(i,j)	i |= j
+#define SETPLEV(i,j)	i |= (j<<4)
+#define SETTYPE(i,j)	i |= (j<<10)
+
+	/* looping macros */
+
+#define TLOOP(i)	for(i=1; i<=ntokens; i++)
+#define NTLOOP(i)	for(i=0; i<=nnonter; i++)
+#define PLOOP(s,i)	for(i=s; i<nprod; i++)
+#define SLOOP(i)	for(i=0; i<nstate; i++)
+#define WSBUMP(x)	x++
+#define WSLOOP(s,j)	for(j=s; j<cwp; j++)
+#define ITMLOOP(i,p,q)	for(q=pstate[i+1], p=pstate[i]; p<q; p++)
+#define SETLOOP(i)	for(i=0; i<tbitset; i++)
+
+	/* command to clobber tempfiles after use */
+
+#define	ZAPFILE(x)	if(x) remove(x)
+
+	/* I/O descriptors */
+
+Biobuf*	faction;	/* file for saving actions */
+Biobuf*	fdefine;	/* file for #defines */
+Biobuf*	fdebug;		/* y.debug for strings for debugging */
+Biobuf*	ftable;		/* y.tab.c file */
+Biobuf*	ftemp;		/* tempfile to pass 2 */
+Biobuf*	finput;		/* input file */
+Biobuf*	foutput;	/* y.output file */
+
+	/* communication variables between various I/O routines */
+
+char*	infile;			/* input file name */
+int	numbval;		/* value of an input number */
+char	tokname[NAMESIZE+4];	/* input token name, slop for runes and 0 */
+
+	/* structure declarations */
+
+typedef
+struct
+{
+	int	lset[TBITSET];
+} Lkset;
+
+typedef
+struct
+{
+	int*	pitem;
+	Lkset*	look;
+} Item;
+
+typedef
+struct
+{
+	char*	name;
+	int	value;
+} Symb;
+
+typedef
+struct
+{
+	int*	pitem;
+	int	flag;
+	Lkset	ws;
+} Wset;
+
+	/* storage of names */
+
+char	cnames[CNAMSZ];		/* place where token and nonterminal names are stored */
+int	cnamsz = CNAMSZ;	/* size of cnames */
+char*	cnamp = cnames;		/* place where next name is to be put in */
+int	ndefout = 4;		/* number of defined symbols output */
+char*	tempname;
+char*	actname;
+char	ttempname[] = TEMPNAME;
+char	tactname[] = ACTNAME;
+char*	parser = PARSER;
+char*	yydebug;
+
+	/* storage of types */
+int	ntypes;			/* number of types defined */
+char*	typeset[NTYPES];	/* pointers to type tags */
+
+	/* token information */
+
+int	ntokens = 0 ;		/* number of tokens */
+Symb	tokset[NTERMS];
+int	toklev[NTERMS];		/* vector with the precedence of the terminals */
+
+	/* nonterminal information */
+
+int	nnonter = -1;		/* the number of nonterminals */
+Symb	nontrst[NNONTERM];
+int	start;			/* start symbol */
+
+	/* assigned token type values */
+int	extval = 0;
+
+char*	ytabc = OFILE;	/* name of y.tab.c */
+
+	/* grammar rule information */
+
+int	mem0[MEMSIZE] ;		/* production storage */
+int*	mem = mem0;
+int	nprod = 1;		/* number of productions */
+int*	prdptr[NPROD];		/* pointers to descriptions of productions */
+int	levprd[NPROD];		/* precedence levels for the productions */
+int	rlines[NPROD];		/* line number for this rule */
+
+	/* state information */
+
+int	nstate = 0;		/* number of states */
+Item*	pstate[NSTATES+2];	/* pointers to the descriptions of the states */
+int	tystate[NSTATES];	/* contains type information about the states */
+int	defact[NSTATES];	/* the default actions of states */
+int	tstates[NTERMS];	/* states generated by terminal gotos */
+int	ntstates[NNONTERM]; 	/* states generated by nonterminal gotos */
+int	mstates[NSTATES];	/* chain of overflows of term/nonterm generation lists  */
+int	lastred; 		/* the number of the last reduction of a state */
+
+	/* lookahead set information */
+
+Lkset	lkst[LSETSIZE];
+int	nolook;			/* flag to turn off lookahead computations */
+int	tbitset;		/* size of lookahead sets */
+int	nlset = 0;		/* next lookahead set index */
+int	nolook = 0;		/* flag to suppress lookahead computations */
+Lkset	clset;  		/* temporary storage for lookahead computations */
+
+	/* working set information */
+
+Wset	wsets[WSETSIZE];
+Wset*	cwp;
+
+	/* storage for action table */
+
+int	amem[ACTSIZE];		/* action table storage */
+int*	memp = amem;		/* next free action table position */
+int	indgo[NSTATES];		/* index to the stored goto table */
+
+	/* temporary vector, indexable by states, terms, or ntokens */
+
+int	temp1[TEMPSIZE];	/* temporary storage, indexed by terms + ntokens or states */
+int	lineno = 1;		/* current input line number */
+int	fatfl = 1;  		/* if on, error is fatal */
+int	nerrors = 0;		/* number of errors */
+
+	/* statistics collection variables */
+
+int	zzgoent;
+int	zzgobest;
+int	zzacent;
+int	zzexcp;
+int	zzclose;
+int	zzrrconf;
+int	zzsrconf;
+
+int*	ggreed = lkst[0].lset;
+int*	pgo = wsets[0].ws.lset;
+int*	yypgo = &nontrst[0].value;
+
+int	maxspr = 0;  		/* maximum spread of any entry */
+int	maxoff = 0;  		/* maximum offset into a array */
+int*	pmem = mem0;
+int*	maxa;
+int	nxdb = 0;
+int	adb = 0;
+
+
+	/* storage for information about the nonterminals */
+
+int**	pres[NNONTERM+2];  	/* vector of pointers to productions yielding each nonterminal */
+Lkset*	pfirst[NNONTERM+2];	/* vector of pointers to first sets for each nonterminal */
+int	pempty[NNONTERM+1];	/* vector of nonterminals nontrivially deriving e */
+
+	/* random stuff picked out from between functions */
+
+int	indebug = 0;
+Wset*	zzcwp = wsets;
+int	zzgoent = 0;
+int	zzgobest = 0;
+int	zzacent = 0;
+int	zzexcp = 0;
+int	zzclose = 0;
+int	zzsrconf = 0;
+int*	zzmemsz = mem0;
+int	zzrrconf = 0;
+int	pidebug = 0;		/* debugging flag for putitem */
+int	gsdebug = 0;
+int	cldebug = 0;		/* debugging flag for closure */
+int	pkdebug = 0;
+int	g2debug = 0;
+
+struct
+{
+	char*	name;
+	long	value;
+} resrv[] =
+{
+	"binary",	BINARY,
+	"left",		LEFT,
+	"nonassoc",	BINARY,
+	"prec",		PREC,
+	"right",	RIGHT,
+	"start",	START,
+	"term",		TERM,
+	"token",	TERM,
+	"type",		TYPEDEF,
+	"union",	UNION,
+	0,
+};
+
+	/* define functions */
+
+void	main(int, char**);
+void	others(void);
+char*	chcopy(char*, char*);
+char*	writem(int*);
+char*	symnam(int);
+void	summary(void);
+void	error(char*, ...);
+void	aryfil(int*, int, int);
+int	setunion(int*, int*);
+void	prlook(Lkset*);
+void	cpres(void);
+void	cpfir(void);
+int	state(int);
+void	putitem(int*, Lkset*);
+void	cempty(void);
+void	stagen(void);
+void	closure(int);
+Lkset*	flset(Lkset*);
+void	cleantmp(void);
+void	intr(void);
+void	setup(int, char**);
+void	finact(void);
+int	defin(int, char*);
+void	defout(int);
+char*	cstash(char*);
+long	gettok(void);
+int	fdtype(int);
+int	chfind(int, char*);
+void	cpyunion(void);
+void	cpycode(void);
+int	skipcom(void);
+void	cpyact(int);
+void	openup(char*, int, int, int, char*);
+void	output(void);
+int	apack(int*, int);
+void	go2out(void);
+void	go2gen(int);
+void	precftn(int, int, int);
+void	wract(int);
+void	wrstate(int);
+void	warray(char*, int*, int);
+void	hideprod(void);
+void	callopt(void);
+void	gin(int);
+void	stin(int);
+int	nxti(void);
+void	osummary(void);
+void	aoutput(void);
+void	arout(char*, int*, int);
+int	gtnm(void);
+
+void
+main(int argc, char *argv[])
+{
+
+	setup(argc, argv);	/* initialize and read productions */
+	tbitset = NWORDS(ntokens);
+	cpres();		/* make table of which productions yield a given nonterminal */
+	cempty();		/* make a table of which nonterminals can match the empty string */
+	cpfir();		/* make a table of firsts of nonterminals */
+	stagen();		/* generate the states */
+	output();		/* write the states and the tables */
+	go2out();
+	hideprod();
+	summary();
+	callopt();
+	others();
+	exits(0);
+}
+
+/*
+ * put out other arrays, copy the parsers
+ */
+void
+others(void)
+{
+	int c, i, j;
+
+	finput = Bopen(unsharp(parser), OREAD);
+	if(finput == 0)
+		error("cannot open parser %s: %r", parser);
+	warray("yyr1", levprd, nprod);
+	aryfil(temp1, nprod, 0);
+	PLOOP(1, i)
+		temp1[i] = prdptr[i+1]-prdptr[i]-2;
+	warray("yyr2", temp1, nprod);
+
+	aryfil(temp1, nstate, -1000);
+	TLOOP(i)
+		for(j=tstates[i]; j!=0; j=mstates[j])
+			temp1[j] = i;
+	NTLOOP(i)
+		for(j=ntstates[i]; j!=0; j=mstates[j])
+			temp1[j] = -i;
+	warray("yychk", temp1, nstate);
+	warray("yydef", defact, nstate);
+
+	/* put out token translation tables */
+	/* table 1 has 0-256 */
+	aryfil(temp1, 256, 0);
+	c = 0;
+	TLOOP(i) {
+		j = tokset[i].value;
+		if(j >= 0 && j < 256) {
+			if(temp1[j]) {
+				print("yacc bug -- cant have 2 different Ts with same value\n");
+				print("	%s and %s\n", tokset[i].name, tokset[temp1[j]].name);
+				nerrors++;
+			}
+			temp1[j] = i;
+			if(j > c)
+				c = j;
+		}
+	}
+	warray("yytok1", temp1, c+1);
+
+	/* table 2 has PRIVATE-PRIVATE+256 */
+	aryfil(temp1, 256, 0);
+	c = 0;
+	TLOOP(i) {
+		j = tokset[i].value - PRIVATE;
+		if(j >= 0 && j < 256) {
+			if(temp1[j]) {
+				print("yacc bug -- cant have 2 different Ts with same value\n");
+				print("	%s and %s\n", tokset[i].name, tokset[temp1[j]].name);
+				nerrors++;
+			}
+			temp1[j] = i;
+			if(j > c)
+				c = j;
+		}
+	}
+	warray("yytok2", temp1, c+1);
+
+	/* table 3 has everything else */
+	Bprint(ftable, "long	yytok3[] =\n{\n");
+	c = 0;
+	TLOOP(i) {
+		j = tokset[i].value;
+		if(j >= 0 && j < 256)
+			continue;
+		if(j >= PRIVATE && j < 256+PRIVATE)
+			continue;
+
+		Bprint(ftable, "%4d,%4d,", j, i);
+		c++;
+		if(c%5 == 0)
+			Bprint(ftable, "\n");
+	}
+	Bprint(ftable, "%4d\n};\n", 0);
+
+	/* copy parser text */
+	while((c=Bgetrune(finput)) != Beof) {
+		if(c == '$') {
+			if((c = Bgetrune(finput)) != 'A')
+				Bputrune(ftable, '$');
+			else { /* copy actions */
+				faction = Bopen(actname, OREAD);
+				if(faction == 0)
+					error("cannot reopen action tempfile");
+				while((c=Bgetrune(faction)) != Beof)
+					Bputrune(ftable, c);
+				Bterm(faction);
+				ZAPFILE(actname);
+				c = Bgetrune(finput);
+			}
+		}
+		Bputrune(ftable, c);
+	}
+	Bterm(ftable);
+}
+
+/*
+ * copies string q into p, returning next free char ptr
+ */
+char*
+chcopy(char* p, char* q)
+{
+	int c;
+
+	while(c = *q) {
+		if(c == '"')
+			*p++ = '\\';
+		*p++ = c;
+		q++;
+	}
+	*p = 0;
+	return p;
+}
+
+/*
+ * creates output string for item pointed to by pp
+ */
+char*
+writem(int *pp)
+{
+	int i,*p;
+	static char sarr[ISIZE];
+	char* q;
+
+	for(p=pp; *p>0; p++)
+		;
+	p = prdptr[-*p];
+	q = chcopy(sarr, nontrst[*p-NTBASE].name);
+	q = chcopy(q, ": ");
+	for(;;) {
+		*q = ' ';
+		p++;
+		if(p == pp)
+			*q = '.';
+		q++;
+		*q = '\0';
+		i = *p;
+		if(i <= 0)
+			break;
+		q = chcopy(q, symnam(i));
+		if(q > &sarr[ISIZE-30])
+			error("item too big");
+	}
+
+	/* an item calling for a reduction */
+	i = *pp;
+	if(i < 0 ) {
+		q = chcopy(q, "    (");
+		sprint(q, "%d)", -i);
+	}
+	return sarr;
+}
+
+/*
+ * return a pointer to the name of symbol i
+ */
+char*
+symnam(int i)
+{
+	char* cp;
+
+	cp = (i >= NTBASE)? nontrst[i-NTBASE].name: tokset[i].name;
+	if(*cp == ' ')
+		cp++;
+	return cp;
+}
+
+/*
+ * output the summary on y.output
+ */
+void
+summary(void)
+{
+
+	if(foutput != 0) {
+		Bprint(foutput, "\n%d/%d terminals, %d/%d nonterminals\n",
+			ntokens, NTERMS, nnonter, NNONTERM);
+		Bprint(foutput, "%d/%d grammar rules, %d/%d states\n",
+			nprod, NPROD, nstate, NSTATES);
+		Bprint(foutput, "%d shift/reduce, %d reduce/reduce conflicts reported\n",
+			zzsrconf, zzrrconf);
+		Bprint(foutput, "%d/%d working sets used\n",
+			(int)(zzcwp-wsets), WSETSIZE);
+		Bprint(foutput, "memory: states,etc. %d/%d, parser %d/%d\n",
+			(int)(zzmemsz-mem0), MEMSIZE, (int)(memp-amem), ACTSIZE);
+		Bprint(foutput, "%d/%d distinct lookahead sets\n", nlset, LSETSIZE);
+		Bprint(foutput, "%d extra closures\n", zzclose - 2*nstate);
+		Bprint(foutput, "%d shift entries, %d exceptions\n", zzacent, zzexcp);
+		Bprint(foutput, "%d goto entries\n", zzgoent);
+		Bprint(foutput, "%d entries saved by goto default\n", zzgobest);
+	}
+	if(zzsrconf != 0 || zzrrconf != 0) {
+		print("\nconflicts: ");
+		if(zzsrconf)
+			print("%d shift/reduce", zzsrconf);
+		if(zzsrconf && zzrrconf)
+			print(", ");
+		if(zzrrconf)
+			print("%d reduce/reduce", zzrrconf);
+		print("\n");
+	}
+	if(ftemp != 0) {
+		Bterm(ftemp);
+		ftemp = 0;
+	}
+	if(fdefine != 0) {
+		Bterm(fdefine);
+		fdefine = 0;
+	}
+}
+
+/*
+ * write out error comment -- NEEDS WORK
+ */
+void
+error(char *s, ...)
+{
+
+	nerrors++;
+	fprint(2, "\n fatal error:");
+	fprint(2, s, (&s)[1]);
+	fprint(2, ", %s:%d\n", infile, lineno);
+	if(!fatfl)
+		return;
+	summary();
+	cleantmp();
+	exits("error");
+}
+
+/*
+ * set elements 0 through n-1 to c
+ */
+void
+aryfil(int *v, int n, int c)
+{
+	int i;
+
+	for(i=0; i<n; i++)
+		v[i] = c;
+}
+
+/*
+ * set a to the union of a and b
+ * return 1 if b is not a subset of a, 0 otherwise
+ */
+int
+setunion(int *a, int *b)
+{
+	int i, x, sub;
+
+	sub = 0;
+	SETLOOP(i) {
+		x = *a;
+		*a |= *b;
+		if(*a != x)
+			sub = 1;
+		a++;
+		b++;
+	}
+	return sub;
+}
+
+void
+prlook(Lkset* p)
+{
+	int j, *pp;
+
+	pp = p->lset;
+	if(pp == 0)
+		Bprint(foutput, "\tNULL");
+	else {
+		Bprint(foutput, " { ");
+		TLOOP(j)
+			if(BIT(pp,j))
+				Bprint(foutput, "%s ", symnam(j));
+		Bprint(foutput, "}");
+	}
+}
+
+/*
+ * compute an array with the beginnings of  productions yielding given nonterminals
+ * The array pres points to these lists
+ * the array pyield has the lists: the total size is only NPROD+1
+ */
+void
+cpres(void)
+{
+	int c, j, i, **pmem;
+	static int *pyield[NPROD];
+
+	pmem = pyield;
+	NTLOOP(i) {
+		c = i+NTBASE;
+		pres[i] = pmem;
+		fatfl = 0;  	/* make undefined  symbols  nonfatal */
+		PLOOP(0, j)
+			if(*prdptr[j] == c)
+				*pmem++ =  prdptr[j]+1;
+		if(pres[i] == pmem)
+			error("nonterminal %s not defined!", nontrst[i].name);
+	}
+	pres[i] = pmem;
+	fatfl = 1;
+	if(nerrors) {
+		summary();
+		cleantmp();
+		exits("error");
+	}
+	if(pmem != &pyield[nprod])
+		error("internal Yacc error: pyield %d", pmem-&pyield[nprod]);
+}
+
+/*
+ * compute an array with the first of nonterminals
+ */
+void
+cpfir(void)
+{
+	int *p, **s, i, **t, ch, changes;
+
+	zzcwp = &wsets[nnonter];
+	NTLOOP(i) {
+		aryfil(wsets[i].ws.lset, tbitset, 0);
+		t = pres[i+1];
+		/* initially fill the sets */
+		for(s=pres[i]; s<t; ++s)
+			for(p = *s; (ch = *p) > 0; ++p) {
+				if(ch < NTBASE) {
+					SETBIT(wsets[i].ws.lset, ch);
+					break;
+				}
+				if(!pempty[ch-NTBASE])
+					break;
+			}
+	}
+
+	/* now, reflect transitivity */
+	changes = 1;
+	while(changes) {
+		changes = 0;
+		NTLOOP(i) {
+			t = pres[i+1];
+			for(s = pres[i]; s < t; ++s)
+				for(p = *s; (ch = (*p-NTBASE)) >= 0; ++p) {
+					changes |= setunion(wsets[i].ws.lset, wsets[ch].ws.lset);
+					if(!pempty[ch])
+						break;
+				}
+		}
+	}
+
+	NTLOOP(i)
+		pfirst[i] = flset(&wsets[i].ws);
+	if(!indebug)
+		return;
+	if(foutput != 0)
+		NTLOOP(i) {
+			Bprint(foutput, "\n%s: ", nontrst[i].name);
+			prlook(pfirst[i]);
+			Bprint(foutput, " %d\n", pempty[i]);
+		}
+}
+
+/*
+ * sorts last state,and sees if it equals earlier ones. returns state number
+ */
+int
+state(int c)
+{
+	Item *p1, *p2, *k, *l, *q1, *q2;
+	int size1, size2, i;
+
+	p1 = pstate[nstate];
+	p2 = pstate[nstate+1];
+	if(p1 == p2)
+		return 0;	/* null state */
+	/* sort the items */
+	for(k = p2-1; k > p1; k--)	/* make k the biggest */
+		for(l = k-1; l >= p1; --l)
+			if(l->pitem > k->pitem) {
+				int *s;
+				Lkset *ss;
+
+				s = k->pitem;
+				k->pitem = l->pitem;
+				l->pitem = s;
+				ss = k->look;
+				k->look = l->look;
+				l->look = ss;
+			}
+	size1 = p2 - p1;	/* size of state */
+
+	for(i = (c>=NTBASE)? ntstates[c-NTBASE]: tstates[c]; i != 0; i = mstates[i]) {
+		/* get ith state */
+		q1 = pstate[i];
+		q2 = pstate[i+1];
+		size2 = q2 - q1;
+		if(size1 != size2)
+			continue;
+		k = p1;
+		for(l = q1; l < q2; l++) {
+			if(l->pitem != k->pitem)
+				break;
+			k++;
+		}
+		if(l != q2)
+			continue;
+		/* found it */
+		pstate[nstate+1] = pstate[nstate];	/* delete last state */
+		/* fix up lookaheads */
+		if(nolook)
+			return i;
+		for(l = q1, k = p1; l < q2; ++l, ++k ) {
+			int s;
+
+			SETLOOP(s)
+				clset.lset[s] = l->look->lset[s];
+			if(setunion(clset.lset, k->look->lset)) {
+				tystate[i] = MUSTDO;
+				/* register the new set */
+				l->look = flset( &clset );
+			}
+		}
+		return i;
+	}
+	/* state is new */
+	if(nolook)
+		error("yacc state/nolook error");
+	pstate[nstate+2] = p2;
+	if(nstate+1 >= NSTATES)
+		error("too many states");
+	if(c >= NTBASE) {
+		mstates[nstate] = ntstates[c-NTBASE];
+		ntstates[c-NTBASE] = nstate;
+	} else {
+		mstates[nstate] = tstates[c];
+		tstates[c] = nstate;
+	}
+	tystate[nstate] = MUSTDO;
+	return nstate++;
+}
+
+void
+putitem(int *ptr, Lkset *lptr)
+{
+	Item *j;
+
+	if(pidebug && foutput != 0)
+		Bprint(foutput, "putitem(%s), state %d\n", writem(ptr), nstate);
+	j = pstate[nstate+1];
+	j->pitem = ptr;
+	if(!nolook)
+		j->look = flset(lptr);
+	pstate[nstate+1] = ++j;
+	if((int*)j > zzmemsz) {
+		zzmemsz = (int*)j;
+		if(zzmemsz >=  &mem0[MEMSIZE])
+			error("out of state space");
+	}
+}
+
+/*
+ * mark nonterminals which derive the empty string
+ * also, look for nonterminals which don't derive any token strings
+ */
+void
+cempty(void)
+{
+
+	int i, *p;
+
+	/* first, use the array pempty to detect productions that can never be reduced */
+	/* set pempty to WHONOWS */
+	aryfil(pempty, nnonter+1, WHOKNOWS);
+
+	/* now, look at productions, marking nonterminals which derive something */
+more:
+	PLOOP(0, i) {
+		if(pempty[*prdptr[i] - NTBASE])
+			continue;
+		for(p = prdptr[i]+1; *p >= 0; ++p)
+			if(*p >= NTBASE && pempty[*p-NTBASE] == WHOKNOWS)
+				break;
+		/* production can be derived */
+		if(*p < 0) {
+			pempty[*prdptr[i]-NTBASE] = OK;
+			goto more;
+		}
+	}
+
+	/* now, look at the nonterminals, to see if they are all OK */
+	NTLOOP(i) {
+		/* the added production rises or falls as the start symbol ... */
+		if(i == 0)
+			continue;
+		if(pempty[i] != OK) {
+			fatfl = 0;
+			error("nonterminal %s never derives any token string", nontrst[i].name);
+		}
+	}
+
+	if(nerrors) {
+		summary();
+		cleantmp();
+		exits("error");
+	}
+
+	/* now, compute the pempty array, to see which nonterminals derive the empty string */
+	/* set pempty to WHOKNOWS */
+	aryfil( pempty, nnonter+1, WHOKNOWS);
+
+	/* loop as long as we keep finding empty nonterminals */
+
+again:
+	PLOOP(1, i) {
+		/* not known to be empty */
+		if(pempty[*prdptr[i]-NTBASE] == WHOKNOWS) {
+			for(p = prdptr[i]+1; *p >= NTBASE && pempty[*p-NTBASE] == EMPTY ; ++p)
+				;
+			/* we have a nontrivially empty nonterminal */
+			if(*p < 0) {
+				pempty[*prdptr[i]-NTBASE] = EMPTY;
+				/* got one ... try for another */
+				goto again;
+			}
+		}
+	}
+}
+
+/*
+ * generate the states
+ */
+void
+stagen(void)
+{
+
+	int c, i, j, more;
+	Wset *p, *q;
+
+	/* initialize */
+	nstate = 0;
+
+	/* THIS IS FUNNY from the standpoint of portability
+	 * it represents the magic moment when the mem0 array, which has
+	 * been holding the productions, starts to hold item pointers, of a
+	 * different type...
+	 * someday, alloc should be used to allocate all this stuff... for now, we
+	 * accept that if pointers don't fit in integers, there is a problem...
+	 */
+
+	pstate[0] = pstate[1] = (Item*)mem;
+	aryfil(clset.lset, tbitset, 0);
+	putitem(prdptr[0]+1, &clset);
+	tystate[0] = MUSTDO;
+	nstate = 1;
+	pstate[2] = pstate[1];
+
+	aryfil(amem, ACTSIZE, 0);
+
+	/* now, the main state generation loop */
+	for(more=1; more;) {
+		more = 0;
+		SLOOP(i) {
+			if(tystate[i] != MUSTDO)
+				continue;
+			tystate[i] = DONE;
+			aryfil(temp1, nnonter+1, 0);
+			/* take state i, close it, and do gotos */
+			closure(i);
+			/* generate goto's */
+			WSLOOP(wsets, p) {
+				if(p->flag)
+					continue;
+				p->flag = 1;
+				c = *(p->pitem);
+				if(c <= 1) {
+					if(pstate[i+1]-pstate[i] <= p-wsets)
+						tystate[i] = MUSTLOOKAHEAD;
+					continue;
+				}
+				/* do a goto on c */
+				WSLOOP(p, q)
+					/* this item contributes to the goto */
+					if(c == *(q->pitem)) {
+						putitem(q->pitem+1, &q->ws);
+						q->flag = 1;
+					}
+				if(c < NTBASE)
+					state(c);	/* register new state */
+				else
+					temp1[c-NTBASE] = state(c);
+			}
+			if(gsdebug && foutput != 0) {
+				Bprint(foutput, "%d: ", i);
+				NTLOOP(j)
+					if(temp1[j])
+						Bprint(foutput, "%s %d, ",
+						nontrst[j].name, temp1[j]);
+				Bprint(foutput, "\n");
+			}
+			indgo[i] = apack(&temp1[1], nnonter-1) - 1;
+			/* do some more */
+			more = 1;
+		}
+	}
+}
+
+/*
+ * generate the closure of state i
+ */
+void
+closure(int i)
+{
+
+	Wset *u, *v;
+	Item *p, *q;
+	int c, ch, work, k, *pi, **s, **t;
+
+	zzclose++;
+
+	/* first, copy kernel of state i to wsets */
+	cwp = wsets;
+	ITMLOOP(i, p, q) {
+		cwp->pitem = p->pitem;
+		cwp->flag = 1;			/* this item must get closed */
+		SETLOOP(k)
+			cwp->ws.lset[k] = p->look->lset[k];
+		WSBUMP(cwp);
+	}
+
+	/* now, go through the loop, closing each item */
+	work = 1;
+	while(work) {
+		work = 0;
+		WSLOOP(wsets, u) {
+			if(u->flag == 0)
+				continue;
+			/* dot is before c */
+			c = *(u->pitem);
+			if(c < NTBASE) {
+				u->flag = 0;
+				/* only interesting case is where . is before nonterminal */
+				continue;
+			}
+
+			/* compute the lookahead */
+			aryfil(clset.lset, tbitset, 0);
+
+			/* find items involving c */
+			WSLOOP(u, v)
+				if(v->flag == 1 && *(pi=v->pitem) == c) {
+					v->flag = 0;
+					if(nolook)
+						continue;
+					while((ch = *++pi) > 0) {
+						/* terminal symbol */
+						if(ch < NTBASE) {
+							SETBIT(clset.lset, ch);
+							break;
+						}
+						/* nonterminal symbol */
+						setunion(clset.lset, pfirst[ch-NTBASE]->lset);
+						if(!pempty[ch-NTBASE])
+							break;
+					}
+					if(ch <= 0)
+						setunion(clset.lset, v->ws.lset);
+				}
+
+			/*
+			 * now loop over productions derived from c
+			 * c is now nonterminal number
+			 */
+			c -= NTBASE;
+			t = pres[c+1];
+			for(s = pres[c]; s < t; ++s) {
+				/*
+				 * put these items into the closure
+				 * is the item there
+				 */
+				WSLOOP(wsets, v)
+					/* yes, it is there */
+					if(v->pitem == *s) {
+						if(nolook)
+							goto nexts;
+						if(setunion(v->ws.lset, clset.lset))
+							v->flag = work = 1;
+						goto nexts;
+					}
+
+				/*  not there; make a new entry */
+				if(cwp-wsets+1 >= WSETSIZE)
+					error( "working set overflow");
+				cwp->pitem = *s;
+				cwp->flag = 1;
+				if(!nolook) {
+					work = 1;
+					SETLOOP(k) cwp->ws.lset[k] = clset.lset[k];
+				}
+				WSBUMP(cwp);
+
+			nexts:;
+			}
+		}
+	}
+
+	/* have computed closure; flags are reset; return */
+	if(cwp > zzcwp)
+		zzcwp = cwp;
+	if(cldebug && foutput != 0) {
+		Bprint(foutput, "\nState %d, nolook = %d\n", i, nolook);
+		WSLOOP(wsets, u) {
+			if(u->flag)
+				Bprint(foutput, "flag set!\n");
+			u->flag = 0;
+			Bprint(foutput, "\t%s", writem(u->pitem));
+			prlook(&u->ws);
+			Bprint(foutput, "\n");
+		}
+	}
+}
+
+/*
+ * decide if the lookahead set pointed to by p is known
+ * return pointer to a perminent location for the set
+ */
+Lkset*
+flset(Lkset *p)
+{
+	Lkset *q;
+	int *u, *v, *w, j;
+
+	for(q = &lkst[nlset]; q-- > lkst;) {
+		u = p->lset;
+		v = q->lset;
+		w = &v[tbitset];
+		while(v < w)
+			if(*u++ != *v++)
+				goto more;
+		/* we have matched */
+		return q;
+	more:;
+	}
+	/* add a new one */
+	q = &lkst[nlset++];
+	if(nlset >= LSETSIZE)
+		error("too many lookahead sets");
+	SETLOOP(j)
+		q->lset[j] = p->lset[j];
+	return q;
+}
+
+void
+cleantmp(void)
+{
+	ZAPFILE(actname);
+	ZAPFILE(tempname);
+}
+
+void
+intr(void)
+{
+	cleantmp();
+	exits("interrupted");
+}
+
+void
+setup(int argc, char *argv[])
+{
+	long c, t;
+	int i, j, fd, lev, ty, ytab, *p;
+	int vflag, dflag, stem;
+	char actnm[8], *stemc, *s, dirbuf[128];
+
+	ytab = 0;
+	vflag = 0;
+	dflag = 0;
+	stem = 0;
+	stemc = "y";
+	foutput = 0;
+	fdefine = 0;
+	fdebug = 0;
+	ARGBEGIN{
+	case 'v':
+	case 'V':
+		vflag++;
+		break;
+	case 'D':
+		yydebug = ARGF();
+		break;
+	case 'd':
+		dflag++;
+		break;
+	case 'o':
+		ytab++;
+		ytabc = ARGF();
+		break;
+	case 's':
+		stem++;
+		stemc = ARGF();
+		break;
+	case 'S':
+		parser = PARSERS;
+		break;
+	default:
+		error("illegal option: %c", ARGC());
+	}ARGEND
+	openup(stemc, dflag, vflag, ytab, ytabc);
+
+	if((fd = mkstemp(ttempname)) >= 0){
+		tempname = ttempname;
+		ftemp = Bfdopen(fd, OWRITE);
+	}
+	if((fd = mkstemp(tactname)) >= 0){
+		actname = tactname;
+		faction = Bfdopen(fd, OWRITE);
+	}
+	if(ftemp == 0 || faction == 0)
+		error("cannot open temp file");
+	if(argc < 1)
+		error("no input file");
+	infile = argv[0];
+	if(infile[0] != '/' && getwd(dirbuf, sizeof dirbuf)!=nil){
+		i = strlen(infile)+1+strlen(dirbuf)+1+10;
+		s = malloc(i);
+		if(s != nil){
+			snprint(s, i, "%s/%s", dirbuf, infile);
+			cleanname(s);
+			infile = s;
+		}
+	}
+	finput = Bopen(infile, OREAD);
+	if(finput == 0)
+		error("cannot open '%s'", argv[0]);
+	cnamp = cnames;
+
+	defin(0, "$end");
+	extval = PRIVATE;	/* tokens start in unicode 'private use' */
+	defin(0, "error");
+	defin(1, "$accept");
+	defin(0, "$unk");
+	mem = mem0;
+	i = 0;
+
+	for(t = gettok(); t != MARK && t != ENDFILE;)
+	switch(t) {
+	case ';':
+		t = gettok();
+		break;
+
+	case START:
+		if(gettok() != IDENTIFIER)
+			error("bad %%start construction");
+		start = chfind(1, tokname);
+		t = gettok();
+		continue;
+
+	case TYPEDEF:
+		if(gettok() != TYPENAME)
+			error("bad syntax in %%type");
+		ty = numbval;
+		for(;;) {
+			t = gettok();
+			switch(t) {
+			case IDENTIFIER:
+				if((t=chfind(1, tokname)) < NTBASE) {
+					j = TYPE(toklev[t]);
+					if(j != 0 && j != ty)
+						error("type redeclaration of token %s",
+							tokset[t].name);
+					else
+						SETTYPE(toklev[t], ty);
+				} else {
+					j = nontrst[t-NTBASE].value;
+					if(j != 0 && j != ty)
+						error("type redeclaration of nonterminal %s",
+							nontrst[t-NTBASE].name );
+					else
+						nontrst[t-NTBASE].value = ty;
+				}
+			case ',':
+				continue;
+			case ';':
+				t = gettok();
+			default:
+				break;
+			}
+			break;
+		}
+		continue;
+
+	case UNION:
+		/* copy the union declaration to the output */
+		cpyunion();
+		t = gettok();
+		continue;
+
+	case LEFT:
+	case BINARY:
+	case RIGHT:
+		i++;
+
+	case TERM:
+		/* nonzero means new prec. and assoc. */
+		lev = t-TERM;
+		ty = 0;
+
+		/* get identifiers so defined */
+		t = gettok();
+
+		/* there is a type defined */
+		if(t == TYPENAME) {
+			ty = numbval;
+			t = gettok();
+		}
+		for(;;) {
+			switch(t) {
+			case ',':
+				t = gettok();
+				continue;
+
+			case ';':
+				break;
+
+			case IDENTIFIER:
+				j = chfind(0, tokname);
+				if(j >= NTBASE)
+					error("%s defined earlier as nonterminal", tokname);
+				if(lev) {
+					if(ASSOC(toklev[j]))
+						error("redeclaration of precedence of %s", tokname);
+					SETASC(toklev[j], lev);
+					SETPLEV(toklev[j], i);
+				}
+				if(ty) {
+					if(TYPE(toklev[j]))
+						error("redeclaration of type of %s", tokname);
+					SETTYPE(toklev[j],ty);
+				}
+				t = gettok();
+				if(t == NUMBER) {
+					tokset[j].value = numbval;
+					if(j < ndefout && j > 3)
+						error("please define type number of %s earlier",
+							tokset[j].name);
+					t = gettok();
+				}
+				continue;
+			}
+			break;
+		}
+		continue;
+
+	case LCURLY:
+		defout(0);
+		cpycode();
+		t = gettok();
+		continue;
+
+	default:
+		error("syntax error");
+	}
+	if(t == ENDFILE)
+		error("unexpected EOF before %%");
+
+	/* t is MARK */
+	Bprint(ftable, "extern	int	yyerrflag;\n");
+	Bprint(ftable, "#ifndef	YYMAXDEPTH\n");
+	Bprint(ftable, "#define	YYMAXDEPTH	150\n");
+	Bprint(ftable, "#endif\n" );
+	if(!ntypes) {
+		Bprint(ftable, "#ifndef	YYSTYPE\n");
+		Bprint(ftable, "#define	YYSTYPE	int\n");
+		Bprint(ftable, "#endif\n");
+	}
+	Bprint(ftable, "YYSTYPE	yylval;\n");
+	Bprint(ftable, "YYSTYPE	yyval;\n");
+
+	prdptr[0] = mem;
+
+	/* added production */
+	*mem++ = NTBASE;
+
+	/* if start is 0, we will overwrite with the lhs of the first rule */
+	*mem++ = start;
+	*mem++ = 1;
+	*mem++ = 0;
+	prdptr[1] = mem;
+	while((t=gettok()) == LCURLY)
+		cpycode();
+	if(t != IDENTCOLON)
+		error("bad syntax on first rule");
+
+	if(!start)
+		prdptr[0][1] = chfind(1, tokname);
+
+	/* read rules */
+	while(t != MARK && t != ENDFILE) {
+		/* process a rule */
+		rlines[nprod] = lineno;
+		if(t == '|')
+			*mem++ = *prdptr[nprod-1];
+		else
+			if(t == IDENTCOLON) {
+				*mem = chfind(1, tokname);
+				if(*mem < NTBASE)
+					error("token illegal on LHS of grammar rule");
+				mem++;
+			} else
+				error("illegal rule: missing semicolon or | ?");
+		/* read rule body */
+		t = gettok();
+
+	more_rule:
+		while(t == IDENTIFIER) {
+			*mem = chfind(1, tokname);
+			if(*mem < NTBASE)
+				levprd[nprod] = toklev[*mem];
+			mem++;
+			t = gettok();
+		}
+		if(t == PREC) {
+			if(gettok() != IDENTIFIER)
+				error("illegal %%prec syntax");
+			j = chfind(2, tokname);
+			if(j >= NTBASE)
+				error("nonterminal %s illegal after %%prec",
+					nontrst[j-NTBASE].name);
+			levprd[nprod] = toklev[j];
+			t = gettok();
+		}
+		if(t == '=') {
+			levprd[nprod] |= ACTFLAG;
+			Bprint(faction, "\ncase %d:", nprod);
+			cpyact(mem-prdptr[nprod]-1);
+			Bprint(faction, " break;");
+			if((t=gettok()) == IDENTIFIER) {
+
+				/* action within rule... */
+				sprint(actnm, "$$%d", nprod);
+
+				/* make it a nonterminal */
+				j = chfind(1, actnm);
+
+				/*
+				 * the current rule will become rule number nprod+1
+				 * move the contents down, and make room for the null
+				 */
+				for(p = mem; p >= prdptr[nprod]; --p)
+					p[2] = *p;
+				mem += 2;
+
+				/* enter null production for action */
+				p = prdptr[nprod];
+				*p++ = j;
+				*p++ = -nprod;
+
+				/* update the production information */
+				levprd[nprod+1] = levprd[nprod] & ~ACTFLAG;
+				levprd[nprod] = ACTFLAG;
+				if(++nprod >= NPROD)
+					error("more than %d rules", NPROD);
+				prdptr[nprod] = p;
+
+				/* make the action appear in the original rule */
+				*mem++ = j;
+
+				/* get some more of the rule */
+				goto more_rule;
+			}
+		}
+
+		while(t == ';')
+			t = gettok();
+		*mem++ = -nprod;
+
+		/* check that default action is reasonable */
+		if(ntypes && !(levprd[nprod]&ACTFLAG) && nontrst[*prdptr[nprod]-NTBASE].value) {
+
+			/* no explicit action, LHS has value */
+			int tempty;
+
+			tempty = prdptr[nprod][1];
+			if(tempty < 0)
+				error("must return a value, since LHS has a type");
+			else
+				if(tempty >= NTBASE)
+					tempty = nontrst[tempty-NTBASE].value;
+				else
+					tempty = TYPE(toklev[tempty]);
+			if(tempty != nontrst[*prdptr[nprod]-NTBASE].value)
+				error("default action causes potential type clash");
+		}
+		nprod++;
+		if(nprod >= NPROD)
+			error("more than %d rules", NPROD);
+		prdptr[nprod] = mem;
+		levprd[nprod] = 0;
+	}
+
+	/* end of all rules */
+	defout(1);
+
+	finact();
+	if(t == MARK) {
+		Bprint(ftable, "\n#line\t%d\t\"%s\"\n", lineno, infile);
+		while((c=Bgetrune(finput)) != Beof)
+			Bputrune(ftable, c);
+	}
+	Bterm(finput);
+}
+
+/*
+ * finish action routine
+ */
+void
+finact(void)
+{
+
+	Bterm(faction);
+	Bprint(ftable, "#define YYEOFCODE %d\n", 1);
+	Bprint(ftable, "#define YYERRCODE %d\n", 2);
+}
+
+/*
+ * define s to be a terminal if t=0
+ * or a nonterminal if t=1
+ */
+int
+defin(int nt, char *s)
+{
+	int val;
+	Rune rune;
+
+	val = 0;
+	if(nt) {
+		nnonter++;
+		if(nnonter >= NNONTERM)
+			error("too many nonterminals, limit %d",NNONTERM);
+		nontrst[nnonter].name = cstash(s);
+		return NTBASE + nnonter;
+	}
+
+	/* must be a token */
+	ntokens++;
+	if(ntokens >= NTERMS)
+		error("too many terminals, limit %d", NTERMS);
+	tokset[ntokens].name = cstash(s);
+
+	/* establish value for token */
+	/* single character literal */
+	if(s[0] == ' ') {
+		val = chartorune(&rune, &s[1]);
+		if(s[val+1] == 0) {
+			val = rune;
+			goto out;
+		}
+	}
+
+	/* escape sequence */
+	if(s[0] == ' ' && s[1] == '\\') {
+		if(s[3] == 0) {
+			/* single character escape sequence */
+			switch(s[2]) {
+			case 'n':	val = '\n'; break;
+			case 'r':	val = '\r'; break;
+			case 'b':	val = '\b'; break;
+			case 't':	val = '\t'; break;
+			case 'f':	val = '\f'; break;
+			case '\'':	val = '\''; break;
+			case '"':	val = '"'; break;
+			case '\\':	val = '\\'; break;
+			default:	error("invalid escape");
+			}
+			goto out;
+		}
+
+		/* \nnn sequence */
+		if(s[2] >= '0' && s[2] <= '7') {
+			if(s[3] < '0' ||
+			   s[3] > '7' ||
+			   s[4] < '0' ||
+			   s[4] > '7' ||
+			   s[5] != 0)
+				error("illegal \\nnn construction");
+			val = 64*s[2] + 8*s[3] + s[4] - 73*'0';
+			if(val == 0)
+				error("'\\000' is illegal");
+			goto out;
+		}
+		error("unknown escape");
+	}
+	val = extval++;
+
+out:
+	tokset[ntokens].value = val;
+	toklev[ntokens] = 0;
+	return ntokens;
+}
+
+/*
+ * write out the defines (at the end of the declaration section)
+ */
+void
+defout(int last)
+{
+	int i, c;
+	char sar[NAMESIZE+10];
+
+	for(i=ndefout; i<=ntokens; i++) {
+		/* non-literals */
+		c = tokset[i].name[0];
+		if(c != ' ' && c != '$') {
+			Bprint(ftable, "#define	%s	%d\n",
+				tokset[i].name, tokset[i].value);
+			if(fdefine)
+				Bprint(fdefine, "#define\t%s\t%d\n",
+					tokset[i].name, tokset[i].value);
+		}
+	}
+	ndefout = ntokens+1;
+	if(last && fdebug) {
+		Bprint(fdebug, "char*	yytoknames[] =\n{\n");
+		TLOOP(i) {
+			if(tokset[i].name) {
+				chcopy(sar, tokset[i].name);
+				Bprint(fdebug, "\t\"%s\",\n", sar);
+				continue;
+			}
+			Bprint(fdebug, "\t0,\n");
+		}
+		Bprint(fdebug, "};\n");
+	}
+}
+
+char*
+cstash(char *s)
+{
+	char *temp;
+
+	temp = cnamp;
+	do {
+		if(cnamp >= &cnames[cnamsz])
+			error("too many characters in id's and literals");
+		else
+			*cnamp++ = *s;
+	} while(*s++);
+	return temp;
+}
+
+long
+gettok(void)
+{
+	long c;
+	Rune rune;
+	int i, base, match, reserve;
+	static int peekline;
+
+begin:
+	reserve = 0;
+	lineno += peekline;
+	peekline = 0;
+	c = Bgetrune(finput);
+	while(c == ' ' || c == '\n' || c == '\t' || c == '\f') {
+		if(c == '\n')
+			lineno++;
+		c = Bgetrune(finput);
+	}
+
+	/* skip comment */
+	if(c == '/') {
+		lineno += skipcom();
+		goto begin;
+	}
+	switch(c) {
+	case Beof:
+		return ENDFILE;
+
+	case '{':
+		Bungetrune(finput);
+		return '=';
+
+	case '<':
+		/* get, and look up, a type name (union member name) */
+		i = 0;
+		while((c=Bgetrune(finput)) != '>' && c >= 0 && c != '\n') {
+			rune = c;
+			c = runetochar(&tokname[i], &rune);
+			if(i < NAMESIZE)
+				i += c;
+		}
+		if(c != '>')
+			error("unterminated < ... > clause");
+		tokname[i] = 0;
+		for(i=1; i<=ntypes; i++)
+			if(!strcmp(typeset[i], tokname)) {
+				numbval = i;
+				return TYPENAME;
+			}
+		ntypes++;
+		numbval = ntypes;
+		typeset[numbval] = cstash(tokname);
+		return TYPENAME;
+
+	case '"':
+	case '\'':
+		match = c;
+		tokname[0] = ' ';
+		i = 1;
+		for(;;) {
+			c = Bgetrune(finput);
+			if(c == '\n' || c <= 0)
+				error("illegal or missing ' or \"" );
+			if(c == '\\') {
+				tokname[i] = '\\';
+				if(i < NAMESIZE)
+					i++;
+				c = Bgetrune(finput);
+			} else
+				if(c == match)
+					break;
+			rune = c;
+			c = runetochar(&tokname[i], &rune);
+			if(i < NAMESIZE)
+				i += c;
+		}
+		break;
+
+	case '%':
+	case '\\':
+		switch(c = Bgetrune(finput)) {
+		case '0':	return TERM;
+		case '<':	return LEFT;
+		case '2':	return BINARY;
+		case '>':	return RIGHT;
+		case '%':
+		case '\\':	return MARK;
+		case '=':	return PREC;
+		case '{':	return LCURLY;
+		default:	reserve = 1;
+		}
+
+	default:
+		/* number */
+		if(isdigit(c)) {
+			numbval = c-'0';
+			base = (c=='0')? 8: 10;
+			for(c = Bgetrune(finput); isdigit(c); c = Bgetrune(finput))
+				numbval = numbval*base + (c-'0');
+			Bungetrune(finput);
+			return NUMBER;
+		}
+		if(islower(c) || isupper(c) || c=='_' || c=='.' || c=='$')  {
+			i = 0;
+			while(islower(c) || isupper(c) || isdigit(c) ||
+			    c == '-' || c=='_' || c=='.' || c=='$') {
+				if(reserve && isupper(c))
+					c += 'a'-'A';
+				rune = c;
+				c = runetochar(&tokname[i], &rune);
+				if(i < NAMESIZE)
+					i += c;
+				c = Bgetrune(finput);
+			}
+		} else
+			return c;
+		Bungetrune(finput);
+	}
+	tokname[i] = 0;
+
+	/* find a reserved word */
+	if(reserve) {
+		for(c=0; resrv[c].name; c++)
+			if(strcmp(tokname, resrv[c].name) == 0)
+				return resrv[c].value;
+		error("invalid escape, or illegal reserved word: %s", tokname);
+	}
+
+	/* look ahead to distinguish IDENTIFIER from IDENTCOLON */
+	c = Bgetrune(finput);
+	while(c == ' ' || c == '\t'|| c == '\n' || c == '\f' || c == '/') {
+		if(c == '\n')
+			peekline++;
+		/* look for comments */
+		if(c == '/')
+			peekline += skipcom();
+		c = Bgetrune(finput);
+	}
+	if(c == ':')
+		return IDENTCOLON;
+	Bungetrune(finput);
+	return IDENTIFIER;
+}
+
+/*
+ * determine the type of a symbol
+ */
+int
+fdtype(int t)
+{
+	int v;
+
+	if(t >= NTBASE)
+		v = nontrst[t-NTBASE].value;
+	else
+		v = TYPE(toklev[t]);
+	if(v <= 0)
+		error("must specify type for %s", (t>=NTBASE)?
+			nontrst[t-NTBASE].name: tokset[t].name);
+	return v;
+}
+
+int
+chfind(int t, char *s)
+{
+	int i;
+
+	if(s[0] == ' ')
+		t = 0;
+	TLOOP(i)
+		if(!strcmp(s, tokset[i].name))
+			return i;
+	NTLOOP(i)
+		if(!strcmp(s, nontrst[i].name))
+			return NTBASE+i;
+
+	/* cannot find name */
+	if(t > 1)
+		error("%s should have been defined earlier", s);
+	return defin(t, s);
+}
+
+/*
+ * copy the union declaration to the output, and the define file if present
+ */
+void
+cpyunion(void)
+{
+	long c;
+	int level;
+
+	Bprint(ftable, "\n#line\t%d\t\"%s\"\n", lineno, infile);
+	Bprint(ftable, "typedef union ");
+	if(fdefine != 0)
+		Bprint(fdefine, "\ntypedef union ");
+
+	level = 0;
+	for(;;) {
+		if((c=Bgetrune(finput)) == Beof)
+			error("EOF encountered while processing %%union");
+		Bputrune(ftable, c);
+		if(fdefine != 0)
+			Bputrune(fdefine, c);
+		switch(c) {
+		case '\n':
+			lineno++;
+			break;
+		case '{':
+			level++;
+			break;
+		case '}':
+			level--;
+
+			/* we are finished copying */
+			if(level == 0) {
+				Bprint(ftable, " YYSTYPE;\n");
+				if(fdefine != 0)
+					Bprint(fdefine, "\tYYSTYPE;\nextern\tYYSTYPE\tyylval;\n");
+				return;
+			}
+		}
+	}
+}
+
+/*
+ * copies code between \{ and \}
+ */
+void
+cpycode(void)
+{
+
+	long c;
+
+	c = Bgetrune(finput);
+	if(c == '\n') {
+		c = Bgetrune(finput);
+		lineno++;
+	}
+	Bprint(ftable, "\n#line\t%d\t\"%s\"\n", lineno, infile);
+	while(c != Beof) {
+		if(c == '\\') {
+			if((c=Bgetrune(finput)) == '}')
+				return;
+			Bputc(ftable, '\\');
+		}
+		if(c == '%') {
+			if((c=Bgetrune(finput)) == '}')
+				return;
+			Bputc(ftable, '%');
+		}
+		Bputrune(ftable, c);
+		if(c == '\n')
+			lineno++;
+		c = Bgetrune(finput);
+	}
+	error("eof before %%}");
+}
+
+/*
+ * skip over comments
+ * skipcom is called after reading a '/'
+ */
+int
+skipcom(void)
+{
+	long c;
+	int i;
+
+	/* i is the number of lines skipped */
+	i = 0;
+	if(Bgetrune(finput) != '*')
+		error("illegal comment");
+	c = Bgetrune(finput);
+	while(c != Beof) {
+		while(c == '*')
+			if((c=Bgetrune(finput)) == '/')
+				return i;
+		if(c == '\n')
+			i++;
+		c = Bgetrune(finput);
+	}
+	error("EOF inside comment");
+	return 0;
+}
+
+/*
+ * copy C action to the next ; or closing }
+ */
+void
+cpyact(int offset)
+{
+	long c;
+	int brac, match, j, s, fnd, tok;
+
+	Bprint(faction, "\n#line\t%d\t\"%s\"\n", lineno, infile);
+	brac = 0;
+
+loop:
+	c = Bgetrune(finput);
+swt:
+	switch(c) {
+	case ';':
+		if(brac == 0) {
+			Bputrune(faction, c);
+			return;
+		}
+		goto lcopy;
+
+	case '{':
+		brac++;
+		goto lcopy;
+
+	case '$':
+		s = 1;
+		tok = -1;
+		c = Bgetrune(finput);
+
+		/* type description */
+		if(c == '<') {
+			Bungetrune(finput);
+			if(gettok() != TYPENAME)
+				error("bad syntax on $<ident> clause");
+			tok = numbval;
+			c = Bgetrune(finput);
+		}
+		if(c == '$') {
+			Bprint(faction, "yyval");
+
+			/* put out the proper tag... */
+			if(ntypes) {
+				if(tok < 0)
+					tok = fdtype(*prdptr[nprod]);
+				Bprint(faction, ".%s", typeset[tok]);
+			}
+			goto loop;
+		}
+		if(c == '-') {
+			s = -s;
+			c = Bgetrune(finput);
+		}
+		if(isdigit(c)) {
+			j = 0;
+			while(isdigit(c)) {
+				j = j*10 + (c-'0');
+				c = Bgetrune(finput);
+			}
+			Bungetrune(finput);
+			j = j*s - offset;
+			if(j > 0)
+				error("Illegal use of $%d", j+offset);
+
+		dollar:
+			Bprint(faction, "yypt[-%d].yyv", -j);
+
+			/* put out the proper tag */
+			if(ntypes) {
+				if(j+offset <= 0 && tok < 0)
+					error("must specify type of $%d", j+offset);
+				if(tok < 0)
+					tok = fdtype(prdptr[nprod][j+offset]);
+				Bprint(faction, ".%s", typeset[tok]);
+			}
+			goto loop;
+		}
+		if(isupper(c) || islower(c) || c == '_' || c == '.') {
+			int tok; /* tok used oustide for type info */
+
+			/* look for $name */
+			Bungetrune(finput);
+			if(gettok() != IDENTIFIER)
+				error("$ must be followed by an identifier");
+			tok = chfind(2, tokname);
+			if((c = Bgetrune(finput)) != '#') {
+				Bungetrune(finput);
+				fnd = -1;
+			} else
+				if(gettok() != NUMBER) {
+					error("# must be followed by number");
+					fnd = -1;
+				} else
+					fnd = numbval;
+			for(j=1; j<=offset; ++j)
+				if(tok == prdptr[nprod][j]) {
+					if(--fnd <= 0) {
+						j -= offset;
+						goto dollar;
+					}
+				}
+			error("$name or $name#number not found");
+		}
+		Bputc(faction, '$');
+		if(s < 0 )
+			Bputc(faction, '-');
+		goto swt;
+
+	case '}':
+		brac--;
+		if(brac)
+			goto lcopy;
+		Bputrune(faction, c);
+		return;
+
+	case '/':
+		/* look for comments */
+		Bputrune(faction, c);
+		c = Bgetrune(finput);
+		if(c != '*')
+			goto swt;
+
+		/* it really is a comment */
+		Bputrune(faction, c);
+		c = Bgetrune(finput);
+		while(c >= 0) {
+			while(c == '*') {
+				Bputrune(faction, c);
+				if((c=Bgetrune(finput)) == '/')
+					goto lcopy;
+			}
+			Bputrune(faction, c);
+			if(c == '\n')
+				lineno++;
+			c = Bgetrune(finput);
+		}
+		error("EOF inside comment");
+
+	case '\'':
+		/* character constant */
+		match = '\'';
+		goto string;
+
+	case '"':
+		/* character string */
+		match = '"';
+
+	string:
+		Bputrune(faction, c);
+		while(c = Bgetrune(finput)) {
+			if(c == '\\') {
+				Bputrune(faction, c);
+				c = Bgetrune(finput);
+				if(c == '\n')
+					lineno++;
+			} else
+				if(c == match)
+					goto lcopy;
+				if(c == '\n')
+					error("newline in string or char. const.");
+			Bputrune(faction, c);
+		}
+		error("EOF in string or character constant");
+
+	case Beof:
+		error("action does not terminate");
+
+	case '\n':
+		lineno++;
+		goto lcopy;
+	}
+
+lcopy:
+	Bputrune(faction, c);
+	goto loop;
+}
+
+void
+openup(char *stem, int dflag, int vflag, int ytab, char *ytabc)
+{
+	char buf[256];
+
+	if(vflag) {
+		sprint(buf, "%s.%s", stem, FILEU);
+		foutput = Bopen(buf, OWRITE);
+		if(foutput == 0)
+			error("cannot open %s", buf);
+	}
+	if(yydebug) {
+		sprint(buf, "%s.%s", stem, FILEDEBUG);
+		if((fdebug = Bopen(buf, OWRITE)) == 0)
+			error("can't open %s", buf);
+	}
+	if(dflag) {
+		sprint(buf, "%s.%s", stem, FILED);
+		fdefine = Bopen(buf, OWRITE);
+		if(fdefine == 0)
+			error("can't create %s", buf);
+	}
+	if(ytab == 0)
+		sprint(buf, "%s.%s", stem, OFILE);
+	else
+		strcpy(buf, ytabc);
+	ftable = Bopen(buf, OWRITE);
+	if(ftable == 0)
+		error("cannot open table file %s", buf);
+}
+
+/*
+ * print the output for the states
+ */
+void
+output(void)
+{
+	int i, k, c;
+	Wset *u, *v;
+
+	Bprint(ftable, "short	yyexca[] =\n{");
+	if(fdebug)
+		Bprint(fdebug, "char*	yystates[] =\n{\n");
+
+	/* output the stuff for state i */
+	SLOOP(i) {
+		nolook = tystate[i]!=MUSTLOOKAHEAD;
+		closure(i);
+
+		/* output actions */
+		nolook = 1;
+		aryfil(temp1, ntokens+nnonter+1, 0);
+		WSLOOP(wsets, u) {
+			c = *(u->pitem);
+			if(c > 1 && c < NTBASE && temp1[c] == 0) {
+				WSLOOP(u, v)
+					if(c == *(v->pitem))
+						putitem(v->pitem+1, (Lkset*)0);
+				temp1[c] = state(c);
+			} else
+				if(c > NTBASE && temp1[(c -= NTBASE) + ntokens] == 0)
+					temp1[c+ntokens] = amem[indgo[i]+c];
+		}
+		if(i == 1)
+			temp1[1] = ACCEPTCODE;
+
+		/* now, we have the shifts; look at the reductions */
+		lastred = 0;
+		WSLOOP(wsets, u) {
+			c = *u->pitem;
+
+			/* reduction */
+			if(c <= 0) {
+				lastred = -c;
+				TLOOP(k)
+					if(BIT(u->ws.lset, k)) {
+						if(temp1[k] == 0)
+							temp1[k] = c;
+						else
+						if(temp1[k] < 0) { /* reduce/reduce conflict */
+							if(foutput)
+								Bprint(foutput,
+									"\n%d: reduce/reduce conflict"
+									" (red'ns %d and %d ) on %s",
+									i, -temp1[k], lastred,
+									symnam(k));
+							if(-temp1[k] > lastred)
+								temp1[k] = -lastred;
+							zzrrconf++;
+						} else
+							/* potential shift/reduce conflict */
+							precftn( lastred, k, i );
+					}
+				}
+		}
+		wract(i);
+	}
+
+	if(fdebug)
+		Bprint(fdebug, "};\n");
+	Bprint(ftable, "};\n");
+	Bprint(ftable, "#define	YYNPROD	%d\n", nprod);
+	Bprint(ftable, "#define	YYPRIVATE %d\n", PRIVATE);
+	if(yydebug)
+		Bprint(ftable, "#define	yydebug	%s\n", yydebug);
+}
+
+/*
+ * pack state i from temp1 into amem
+ */
+int
+apack(int *p, int n)
+{
+	int *pp, *qq, *rr, off, *q, *r;
+
+	/* we don't need to worry about checking because
+	 * we will only look at entries known to be there...
+	 * eliminate leading and trailing 0's
+	 */
+
+	q = p+n;
+	for(pp = p, off = 0; *pp == 0 && pp <= q; ++pp, --off)
+		;
+ 	/* no actions */
+	if(pp > q)
+		return 0;
+	p = pp;
+
+	/* now, find a place for the elements from p to q, inclusive */
+	r = &amem[ACTSIZE-1];
+	for(rr = amem; rr <= r; rr++, off++) {
+		for(qq = rr, pp = p; pp <= q; pp++, qq++)
+			if(*pp != 0)
+				if(*pp != *qq && *qq != 0)
+					goto nextk;
+
+		/* we have found an acceptable k */
+		if(pkdebug && foutput != 0)
+			Bprint(foutput, "off = %d, k = %d\n", off, (int)(rr-amem));
+		for(qq = rr, pp = p; pp <= q; pp++, qq++)
+			if(*pp) {
+				if(qq > r)
+					error("action table overflow");
+				if(qq > memp)
+					memp = qq;
+				*qq = *pp;
+			}
+		if(pkdebug && foutput != 0)
+			for(pp = amem; pp <= memp; pp += 10) {
+				Bprint(foutput, "\t");
+				for(qq = pp; qq <= pp+9; qq++)
+					Bprint(foutput, "%d ", *qq);
+				Bprint(foutput, "\n");
+			}
+		return(off);
+	nextk:;
+	}
+	error("no space in action table");
+	return 0;
+}
+
+/*
+ * output the gotos for the nontermninals
+ */
+void
+go2out(void)
+{
+	int i, j, k, best, count, cbest, times;
+
+	/* mark begining of gotos */
+	Bprint(ftemp, "$\n");
+	for(i = 1; i <= nnonter; i++) {
+		go2gen(i);
+
+		/* find the best one to make default */
+		best = -1;
+		times = 0;
+
+		/* is j the most frequent */
+		for(j = 0; j <= nstate; j++) {
+			if(tystate[j] == 0)
+				continue;
+			if(tystate[j] == best)
+				continue;
+
+			/* is tystate[j] the most frequent */
+			count = 0;
+			cbest = tystate[j];
+			for(k = j; k <= nstate; k++)
+				if(tystate[k] == cbest)
+					count++;
+			if(count > times) {
+				best = cbest;
+				times = count;
+			}
+		}
+
+		/* best is now the default entry */
+		zzgobest += times-1;
+		for(j = 0; j <= nstate; j++)
+			if(tystate[j] != 0 && tystate[j] != best) {
+				Bprint(ftemp, "%d,%d,", j, tystate[j]);
+				zzgoent++;
+			}
+
+		/* now, the default */
+		if(best == -1)
+			best = 0;
+		zzgoent++;
+		Bprint(ftemp, "%d\n", best);
+	}
+}
+
+/*
+ * output the gotos for nonterminal c
+ */
+void
+go2gen(int c)
+{
+	int i, work, cc;
+	Item *p, *q;
+
+
+	/* first, find nonterminals with gotos on c */
+	aryfil(temp1, nnonter+1, 0);
+	temp1[c] = 1;
+	work = 1;
+	while(work) {
+		work = 0;
+		PLOOP(0, i)
+
+			/* cc is a nonterminal */
+			if((cc=prdptr[i][1]-NTBASE) >= 0)
+				/* cc has a goto on c */
+				if(temp1[cc] != 0) {
+
+					/* thus, the left side of production i does too */
+					cc = *prdptr[i]-NTBASE;
+					if(temp1[cc] == 0) {
+						  work = 1;
+						  temp1[cc] = 1;
+					}
+				}
+	}
+
+	/* now, we have temp1[c] = 1 if a goto on c in closure of cc */
+	if(g2debug && foutput != 0) {
+		Bprint(foutput, "%s: gotos on ", nontrst[c].name);
+		NTLOOP(i)
+			if(temp1[i])
+				Bprint(foutput, "%s ", nontrst[i].name);
+		Bprint(foutput, "\n");
+	}
+
+	/* now, go through and put gotos into tystate */
+	aryfil(tystate, nstate, 0);
+	SLOOP(i)
+		ITMLOOP(i, p, q)
+			if((cc = *p->pitem) >= NTBASE)
+				/* goto on c is possible */
+				if(temp1[cc-NTBASE]) {
+					tystate[i] = amem[indgo[i]+c];
+					break;
+				}
+}
+
+/*
+ * decide a shift/reduce conflict by precedence.
+ * r is a rule number, t a token number
+ * the conflict is in state s
+ * temp1[t] is changed to reflect the action
+ */
+void
+precftn(int r, int t, int s)
+{
+	int lp, lt, action;
+
+	lp = levprd[r];
+	lt = toklev[t];
+	if(PLEVEL(lt) == 0 || PLEVEL(lp) == 0) {
+
+		/* conflict */
+		if(foutput != 0)
+			Bprint(foutput,
+				"\n%d: shift/reduce conflict (shift %d(%d), red'n %d(%d)) on %s",
+				s, temp1[t], PLEVEL(lt), r, PLEVEL(lp), symnam(t));
+		zzsrconf++;
+		return;
+	}
+	if(PLEVEL(lt) == PLEVEL(lp))
+		action = ASSOC(lt);
+	else
+		if(PLEVEL(lt) > PLEVEL(lp))
+			action = RASC;  /* shift */
+		else
+			action = LASC;  /* reduce */
+	switch(action) {
+	case BASC:  /* error action */
+		temp1[t] = ERRCODE;
+		break;
+	case LASC:  /* reduce */
+		temp1[t] = -r;
+		break;
+	}
+}
+
+/*
+ * output state i
+ * temp1 has the actions, lastred the default
+ */
+void
+wract(int i)
+{
+	int p, p0, p1, ntimes, tred, count, j, flag;
+
+	/* find the best choice for lastred */
+	lastred = 0;
+	ntimes = 0;
+	TLOOP(j) {
+		if(temp1[j] >= 0)
+			continue;
+		if(temp1[j]+lastred == 0)
+			continue;
+		/* count the number of appearances of temp1[j] */
+		count = 0;
+		tred = -temp1[j];
+		levprd[tred] |= REDFLAG;
+		TLOOP(p)
+			if(temp1[p]+tred == 0)
+				count++;
+		if(count > ntimes) {
+			lastred = tred;
+			ntimes = count;
+		}
+	}
+
+	/*
+	 * for error recovery, arrange that, if there is a shift on the
+	 * error recovery token, `error', that the default be the error action
+	 */
+	if(temp1[2] > 0)
+		lastred = 0;
+
+	/* clear out entries in temp1 which equal lastred */
+	TLOOP(p)
+		if(temp1[p]+lastred == 0)
+			temp1[p] = 0;
+
+	wrstate(i);
+	defact[i] = lastred;
+	flag = 0;
+	TLOOP(p0)
+		if((p1=temp1[p0]) != 0) {
+			if(p1 < 0) {
+				p1 = -p1;
+				goto exc;
+			}
+			if(p1 == ACCEPTCODE) {
+				p1 = -1;
+				goto exc;
+			}
+			if(p1 == ERRCODE) {
+				p1 = 0;
+			exc:
+				if(flag++ == 0)
+					Bprint(ftable, "-1, %d,\n", i);
+				Bprint(ftable, "\t%d, %d,\n", p0, p1);
+				zzexcp++;
+				continue;
+			}
+			Bprint(ftemp, "%d,%d,", p0, p1);
+			zzacent++;
+		}
+	if(flag) {
+		defact[i] = -2;
+		Bprint(ftable, "\t-2, %d,\n", lastred);
+	}
+	Bprint(ftemp, "\n");
+}
+
+/*
+ * writes state i
+ */
+void
+wrstate(int i)
+{
+	int j0, j1;
+	Item *pp, *qq;
+	Wset *u;
+
+	if(fdebug) {
+		if(lastred) {
+			Bprint(fdebug, "	0, /*%d*/\n", i);
+		} else {
+			Bprint(fdebug, "	\"");
+			ITMLOOP(i, pp, qq)
+				Bprint(fdebug, "%s\\n", writem(pp->pitem));
+			if(tystate[i] == MUSTLOOKAHEAD)
+				WSLOOP(wsets + (pstate[i+1] - pstate[i]), u)
+					if(*u->pitem < 0)
+						Bprint(fdebug, "%s\\n", writem(u->pitem));
+			Bprint(fdebug, "\", /*%d*/\n", i);
+		}
+	}
+	if(foutput == 0)
+		return;
+	Bprint(foutput, "\nstate %d\n", i);
+	ITMLOOP(i, pp, qq)
+		Bprint(foutput, "\t%s\n", writem(pp->pitem));
+	if(tystate[i] == MUSTLOOKAHEAD)
+		/* print out empty productions in closure */
+		WSLOOP(wsets+(pstate[i+1]-pstate[i]), u)
+			if(*u->pitem < 0)
+				Bprint(foutput, "\t%s\n", writem(u->pitem));
+
+	/* check for state equal to another */
+	TLOOP(j0)
+		if((j1=temp1[j0]) != 0) {
+			Bprint(foutput, "\n\t%s  ", symnam(j0));
+			/* shift, error, or accept */
+			if(j1 > 0) {
+				if(j1 == ACCEPTCODE)
+					Bprint(foutput,  "accept");
+				else
+					if(j1 == ERRCODE)
+						Bprint(foutput, "error");
+					else
+						Bprint(foutput, "shift %d", j1);
+			} else
+				Bprint(foutput, "reduce %d (src line %d)", -j1, rlines[-j1]);
+		}
+
+	/* output the final production */
+	if(lastred)
+		Bprint(foutput, "\n\t.  reduce %d (src line %d)\n\n",
+			lastred, rlines[lastred]);
+	else
+		Bprint(foutput, "\n\t.  error\n\n");
+
+	/* now, output nonterminal actions */
+	j1 = ntokens;
+	for(j0 = 1; j0 <= nnonter; j0++) {
+		j1++;
+		if(temp1[j1])
+			Bprint(foutput, "\t%s  goto %d\n", symnam(j0+NTBASE), temp1[j1]);
+	}
+}
+
+void
+warray(char *s, int *v, int n)
+{
+	int i;
+
+	Bprint(ftable, "short	%s[] =\n{", s);
+	for(i=0;;) {
+		if(i%10 == 0)
+			Bprint(ftable, "\n");
+		Bprint(ftable, "%4d", v[i]);
+		i++;
+		if(i >= n) {
+			Bprint(ftable, "\n};\n");
+			break;
+		}
+		Bprint(ftable, ",");
+	}
+}
+
+/*
+ * in order to free up the mem and amem arrays for the optimizer,
+ * and still be able to output yyr1, etc., after the sizes of
+ * the action array is known, we hide the nonterminals
+ * derived by productions in levprd.
+ */
+
+void
+hideprod(void)
+{
+	int i, j;
+
+	j = 0;
+	levprd[0] = 0;
+	PLOOP(1, i) {
+		if(!(levprd[i] & REDFLAG)) {
+			j++;
+			if(foutput != 0)
+				Bprint(foutput, "Rule not reduced:   %s\n", writem(prdptr[i]));
+		}
+		levprd[i] = *prdptr[i] - NTBASE;
+	}
+	if(j)
+		print("%d rules never reduced\n", j);
+}
+
+void
+callopt(void)
+{
+	int i, *p, j, k, *q;
+
+	/* read the arrays from tempfile and set parameters */
+	finput = Bopen(tempname, OREAD);
+	if(finput == 0)
+		error("optimizer cannot open tempfile");
+
+	pgo[0] = 0;
+	temp1[0] = 0;
+	nstate = 0;
+	nnonter = 0;
+	for(;;) {
+		switch(gtnm()) {
+		case '\n':
+			nstate++;
+			pmem--;
+			temp1[nstate] = pmem - mem0;
+		case ',':
+			continue;
+		case '$':
+			break;
+		default:
+			error("bad tempfile");
+		}
+		break;
+	}
+
+	pmem--;
+	temp1[nstate] = yypgo[0] = pmem - mem0;
+	for(;;) {
+		switch(gtnm()) {
+		case '\n':
+			nnonter++;
+			yypgo[nnonter] = pmem-mem0;
+		case ',':
+			continue;
+		case -1:
+			break;
+		default:
+			error("bad tempfile");
+		}
+		break;
+	}
+	pmem--;
+	yypgo[nnonter--] = pmem - mem0;
+	for(i = 0; i < nstate; i++) {
+		k = 32000;
+		j = 0;
+		q = mem0 + temp1[i+1];
+		for(p = mem0 + temp1[i]; p < q ; p += 2) {
+			if(*p > j)
+				j = *p;
+			if(*p < k)
+				k = *p;
+		}
+		/* nontrivial situation */
+		if(k <= j) {
+			/* j is now the range */
+/*			j -= k;			*//* call scj */
+			if(k > maxoff)
+				maxoff = k;
+		}
+		tystate[i] = (temp1[i+1]-temp1[i]) + 2*j;
+		if(j > maxspr)
+			maxspr = j;
+	}
+
+	/* initialize ggreed table */
+	for(i = 1; i <= nnonter; i++) {
+		ggreed[i] = 1;
+		j = 0;
+
+		/* minimum entry index is always 0 */
+		q = mem0 + yypgo[i+1] - 1;
+		for(p = mem0+yypgo[i]; p < q ; p += 2) {
+			ggreed[i] += 2;
+			if(*p > j)
+				j = *p;
+		}
+		ggreed[i] = ggreed[i] + 2*j;
+		if(j > maxoff)
+			maxoff = j;
+	}
+
+	/* now, prepare to put the shift actions into the amem array */
+	for(i = 0; i < ACTSIZE; i++)
+		amem[i] = 0;
+	maxa = amem;
+	for(i = 0; i < nstate; i++) {
+		if(tystate[i] == 0 && adb > 1)
+			Bprint(ftable, "State %d: null\n", i);
+		indgo[i] = YYFLAG1;
+	}
+	while((i = nxti()) != NOMORE)
+		if(i >= 0)
+			stin(i);
+		else
+			gin(-i);
+
+	/* print amem array */
+	if(adb > 2 )
+		for(p = amem; p <= maxa; p += 10) {
+			Bprint(ftable, "%4d  ", (int)(p-amem));
+			for(i = 0; i < 10; ++i)
+				Bprint(ftable, "%4d  ", p[i]);
+			Bprint(ftable, "\n");
+		}
+
+	/* write out the output appropriate to the language */
+	aoutput();
+	osummary();
+	ZAPFILE(tempname);
+}
+
+void
+gin(int i)
+{
+	int *p, *r, *s, *q1, *q2;
+
+	/* enter gotos on nonterminal i into array amem */
+	ggreed[i] = 0;
+
+	q2 = mem0+ yypgo[i+1] - 1;
+	q1 = mem0 + yypgo[i];
+
+	/* now, find amem place for it */
+	for(p = amem; p < &amem[ACTSIZE]; p++) {
+		if(*p)
+			continue;
+		for(r = q1; r < q2; r += 2) {
+			s = p + *r + 1;
+			if(*s)
+				goto nextgp;
+			if(s > maxa)
+				if((maxa = s) > &amem[ACTSIZE])
+					error("a array overflow");
+		}
+		/* we have found amem spot */
+		*p = *q2;
+		if(p > maxa)
+			if((maxa = p) > &amem[ACTSIZE])
+				error("a array overflow");
+		for(r = q1; r < q2; r += 2) {
+			s = p + *r + 1;
+			*s = r[1];
+		}
+		pgo[i] = p-amem;
+		if(adb > 1)
+			Bprint(ftable, "Nonterminal %d, entry at %d\n", i, pgo[i]);
+		return;
+
+	nextgp:;
+	}
+	error("cannot place goto %d\n", i);
+}
+
+void
+stin(int i)
+{
+	int *r, *s, n, flag, j, *q1, *q2;
+
+	tystate[i] = 0;
+
+	/* enter state i into the amem array */
+	q2 = mem0+temp1[i+1];
+	q1 = mem0+temp1[i];
+	/* find an acceptable place */
+	for(n = -maxoff; n < ACTSIZE; n++) {
+		flag = 0;
+		for(r = q1; r < q2; r += 2) {
+			if((s = *r + n + amem) < amem)
+				goto nextn;
+			if(*s == 0)
+				flag++;
+			else
+				if(*s != r[1])
+					goto nextn;
+		}
+
+		/* check that the position equals another only if the states are identical */
+		for(j=0; j<nstate; j++) {
+			if(indgo[j] == n) {
+
+				/* we have some disagreement */
+				if(flag)
+					goto nextn;
+				if(temp1[j+1]+temp1[i] == temp1[j]+temp1[i+1]) {
+
+					/* states are equal */
+					indgo[i] = n;
+					if(adb > 1)
+						Bprint(ftable,
+						"State %d: entry at %d equals state %d\n",
+						i, n, j);
+					return;
+				}
+
+				/* we have some disagreement */
+				goto nextn;
+			}
+		}
+
+		for(r = q1; r < q2; r += 2) {
+			if((s = *r+n+amem) >= &amem[ACTSIZE])
+				error("out of space in optimizer a array");
+			if(s > maxa)
+				maxa = s;
+			if(*s != 0 && *s != r[1])
+				error("clobber of a array, pos'n %d, by %d", s-amem, r[1]);
+			*s = r[1];
+		}
+		indgo[i] = n;
+		if(adb > 1)
+			Bprint(ftable, "State %d: entry at %d\n", i, indgo[i]);
+		return;
+	nextn:;
+	}
+	error("Error; failure to place state %d\n", i);
+}
+
+/*
+ * finds the next i
+ */
+int
+nxti(void)
+{
+	int i, max, maxi;
+
+	max = 0;
+	maxi = 0;
+	for(i = 1; i <= nnonter; i++)
+		if(ggreed[i] >= max) {
+			max = ggreed[i];
+			maxi = -i;
+		}
+	for(i = 0; i < nstate; ++i)
+		if(tystate[i] >= max) {
+			max = tystate[i];
+			maxi = i;
+		}
+	if(nxdb)
+		Bprint(ftable, "nxti = %d, max = %d\n", maxi, max);
+	if(max == 0)
+		return NOMORE;
+	return maxi;
+}
+
+/*
+ * write summary
+ */
+void
+osummary(void)
+{
+
+	int i, *p;
+
+	if(foutput == 0)
+		return;
+	i = 0;
+	for(p = maxa; p >= amem; p--)
+		if(*p == 0)
+			i++;
+
+	Bprint(foutput, "Optimizer space used: input %d/%d, output %d/%d\n",
+		(int)(pmem-mem0+1), MEMSIZE, (int)(maxa-amem+1), ACTSIZE);
+	Bprint(foutput, "%d table entries, %d zero\n", (int)(maxa-amem+1), i);
+	Bprint(foutput, "maximum spread: %d, maximum offset: %d\n", maxspr, maxoff);
+}
+
+/*
+ * this version is for C
+ * write out the optimized parser
+ */
+void
+aoutput(void)
+{
+	Bprint(ftable, "#define\tYYLAST\t%d\n", (int)(maxa-amem+1));
+	arout("yyact", amem, (maxa-amem)+1);
+	arout("yypact", indgo, nstate);
+	arout("yypgo", pgo, nnonter+1);
+}
+
+void
+arout(char *s, int *v, int n)
+{
+	int i;
+
+	Bprint(ftable, "short	%s[] =\n{", s);
+	for(i = 0; i < n;) {
+		if(i%10 == 0)
+			Bprint(ftable, "\n");
+		Bprint(ftable, "%4d", v[i]);
+		i++;
+		if(i == n)
+			Bprint(ftable, "\n};\n");
+		else
+			Bprint(ftable, ",");
+	}
+}
+
+/*
+ * read and convert an integer from the standard input
+ * return the terminating character
+ * blanks, tabs, and newlines are ignored
+ */
+int
+gtnm(void)
+{
+	int sign, val, c;
+
+	sign = 0;
+	val = 0;
+	while((c=Bgetrune(finput)) != Beof) {
+		if(isdigit(c)) {
+			val = val*10 + c-'0';
+			continue;
+		}
+		if(c == '-') {
+			sign = 1;
+			continue;
+		}
+		break;
+	}
+	if(sign)
+		val = -val;
+	*pmem++ = val;
+	if(pmem >= &mem0[MEMSIZE])
+		error("out of space");
+	return c;
+}