#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		"/sys/lib/yaccpar"
#define PARSERS		"/sys/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+UTFmax+1]; /* 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(parser, OREAD);
	if(finput == 0)
		error("cannot find parser %s", 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
usage(void)
{
	fprint(2, "usage: yacc [-Dn] [-vdS] [-o outputfile] [-s stem] grammar\n");
	exits("usage");
}

void
setup(int argc, char *argv[])
{
	long c, t;
	int i, j, 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 = EARGF(usage());
		break;
	case 'd':
		dflag++;
		break;
	case 'o':
		ytab++;
		ytabc = EARGF(usage());
		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);

	ftemp = Bopen(tempname = mktemp(ttempname), OWRITE);
	faction = Bopen(actname = mktemp(tactname), 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;
	c = Bgetrune(finput);
	if(c == '/'){			/* C++ //: skip to end of line */
		while((c = Bgetrune(finput)) != Beof)
			if(c == '\n')
				return 1;
	}else if(c == '*'){		/* normal C comment */
		while((c = Bgetrune(finput)) != Beof) {
			while(c == '*')
				if((c = Bgetrune(finput)) == '/')
					return i;
			if(c == '\n')
				i++;
		}
	}else
		error("illegal comment");

	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; copy it */
		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) {
		snprint(buf, sizeof buf, "%s.%s", stem, FILEU);
		foutput = Bopen(buf, OWRITE);
		if(foutput == 0)
			error("cannot open %s", buf);
	}
	if(yydebug) {
		snprint(buf, sizeof buf, "%s.%s", stem, FILEDEBUG);
		if((fdebug = Bopen(buf, OWRITE)) == 0)
			error("can't open %s", buf);
	}
	if(dflag) {
		snprint(buf, sizeof buf, "%s.%s", stem, FILED);
		fdefine = Bopen(buf, OWRITE);
		if(fdefine == 0)
			error("can't create %s", buf);
	}
	if(ytab == 0)
		snprint(buf, sizeof buf, "%s.%s", stem, OFILE);
	else
		strecpy(buf, buf+sizeof 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;
}
