#include <u.h>
#include <libc.h>
#include <bio.h>
#include <ctype.h>

/*
 * all these are 32 bit
 */
#define TBITSET	((32+NTERMS)/32)
#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		"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"

#define NTBASE 010000
#define ERRCODE  8190
#define ACCEPTCODE 8191

/*
 * the following are adjustable
 * according to memory size
 */
#	define ACTSIZE	20000
#	define MEMSIZE	20000
#	define NSTATES	750
#	define NTERMS	255
#	define NPROD	600
#	define NNONTERM	300
#	define TEMPSIZE	1200
#	define CNAMSZ	5000
#	define LSETSIZE	600
#	define WSETSIZE	350

#define NAMESIZE 50
#define NTYPES	63
#define ISIZE	400


	/* relationships which must hold:
	TBITSET ints must hold NTERMS+1 bits...
	WSETSIZE >= NNONTERM
	LSETSIZE >= NNONTERM
	TEMPSIZE >= NTERMS + NNONTERMs + 1
	TEMPSIZE >= NSTATES
	*/

#define NOASC	0  /* no assoc. */
#define LASC	1  /* left assoc. */
#define RASC	2  /* right assoc. */
#define BASC	3  /* binary assoc. */

	/* flags for state generation */

#define DONE	0
#define MUSTDO	1
#define MUSTLOOKAHEAD 2

	/* flags for a rule having an action, and being reduced */

#define ACTFLAG	04
#define REDFLAG	010

	/* output parser flags */
#define YYFLAG1	(-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)

	/* parse tokens */
#define IDENTIFIER	257
#define MARK		258
#define TERM		259
#define LEFT		260
#define RIGHT		261
#define BINARY		262
#define PREC		263
#define LCURLY		264
#define C_IDENTIFIER	265  /* name followed by colon */
#define NUMBER		266
#define START		267
#define TYPEDEF		268
#define TYPENAME	269
#define UNION		270
#define ENDFILE		0

	/* 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];	/* input token name */

	/* structure declarations */

typedef
struct
{
	long	lset[TBITSET];
} Lookset;

typedef
struct
{
	int*	pitem;
	Lookset* look;
} Item;

typedef
struct
{
	char*	name;
	int	value;
} Toksymb;

typedef
struct
{
	char*	name;
	int	tvalue;
} Ntsymb;

typedef
struct
{
	int*	pitem;
	int	flag;
	Lookset	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 = 3;		/* number of defined symbols output */
char*	tempname;
char*	actname;
char	ttempname[] = TEMPNAME;
char	tactname[] = ACTNAME;
char*	parser = PARSER;

	/* storage of types */
int	ntypes;			/* number of types defined */
char*	typeset[NTYPES];	/* pointers to type tags */

	/* token information */

int	ntokens = 0 ;		/* number of tokens */
Toksymb	tokset[NTERMS];
int	toklev[NTERMS];		/* vector with the precedence of the terminals */

	/* nonterminal information */

int	nnonter = -1;		/* the number of nonterminals */
Ntsymb	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 */

Lookset	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 */
Lookset	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].tvalue;

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;

	/* command to clobber tempfiles after use */

#define	ZAPFILE(x)	if(x) remove(x)

	/* storage for information about the nonterminals */

int**	pres[NNONTERM+2];  	/* vector of pointers to productions yielding each nonterminal */
Lookset* 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 */
#define	EMPTY		1
#define	WHOKNOWS	0
#define	OK		1
int	gsdebug = 0;
int	cldebug = 0;		/* debugging flag for closure */
int	tmpx[NTERMS+0400];	/* extval = 0400 in setup is junky */
int	pkdebug = 0;
int	g2debug = 0;
#define NOMORE		-1000

	/* define functions */

int	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(Lookset*);
void	cpres(void);
void	cpfir(void);
int	state(int);
void	putitem(int*, Lookset*);
void	cempty(void);
void	stagen(void);
void	closure(int);
Lookset* flset(Lookset*);
void	cleantmp(void);
void	intr(void);
void	setup(int, char**);
void	finact(void);
int	defin(int, char*);
void	defout(int);
char*	cstash(char*);
int	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, 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	wdef(char*, 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);

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] = tokset[i].value;
	NTLOOP(i)
		for(j=ntstates[i]; j!=0; j=mstates[j])
			temp1[j] = -i;
	warray("yychk", temp1, nstate);
	warray("yydef", defact, nstate);

	/* copy parser text */
	while((c=Bgetc(finput)) != Beof) {
		if(c == '$') {
			if((c=Bgetc(finput)) != 'A')
				Bputc( ftable, '$' );
			else { /* copy actions */
				faction = Bopen(actname, OREAD);
				if(faction == 0)
					error("cannot reopen action tempfile");
				while((c=Bgetc(faction)) != Beof)
					Bputc(ftable, c);
				Bclose(faction);
				ZAPFILE(actname);
				c = Bgetc(finput);
			}
		}
		Bputc(ftable, c);
	}
	Bclose(ftable);
}

/*
 * copies string q into p, returning next free char ptr
 */
char*
chcopy(char* p, char* q)
{
	while(*p = *q++ )
		++p;
	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==pp)? '.': ' ';
		*q = '\0';
		if((i = *p) <= 0)
			break;
		q = chcopy(q, symnam(i));
		if(q > &sarr[ISIZE-30])
			error("item too big");
	}
	/* an item calling for a reduction */
	if((i = *pp) < 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",
			zzcwp-wsets, WSETSIZE);
		Bprint(foutput, "memory: states,etc. %d/%d, parser %d/%d\n",
			zzmemsz-mem0, MEMSIZE, 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)
		Bclose(ftemp);
	if(fdefine != 0)
		Bclose(fdefine);
}

/*
 * write out error comment -- NEEDS WORK
 */
void
error(char *s, ...)
{

	++nerrors;
	fprint(2, "\n fatal error:");
	fprint(2, s, (&s)[1]);
	fprint(2, ", line %d\n", 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
 */
setunion(int *a, int *b)
{
	int i, x, sub;

	sub = 0;
	SETLOOP(i) {
		*a = (x = *a) | *b++;
		if(*a++ != x)
			sub = 1;
	}
	return sub;
}

void
prlook(Lookset* 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
 */
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;
				Lookset *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, Lookset *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;
	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 */
more:
	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;
		/* we have done one goto; do some more */
		goto more;
	}
	/* no more to do... stop */
}

/*
 * 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
 */
Lookset*
flset(Lookset *p)
{
	Lookset *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[])
{
	int i, j, t, lev, ty, c, ytab, *p;
	int vflag, Dflag, dflag, stem;
	char actnm[8], *stemc;

	ytab = 0;
	vflag = 0;
	Dflag = 0;
	dflag = 0;
	stem = 0;
	stemc = "y";
	foutput = 0;
	fdefine = 0;
	fdebug = 0;
	ARGBEGIN{
	case 'v':
	case 'V':
		vflag++;
		break;
	case 'D':
		Dflag++;
		break;
	case 'd':
		dflag++;
		break;
	case 'o':
		ytab++;
		ytabc = ARGF();
		break;
	case 's':
		stem++;
		stemc = ARGF();
		break;
	default:
		error("illegal option: %c", ARGC());
	}ARGEND
	openup(stemc, Dflag, 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");
	if((finput=Bopen(infile=argv[0], OREAD)) == 0)
		error("cannot open '%s'", argv[0]);
	cnamp = cnames;
	defin(0, "$end");
	/* this junky number is known where fdebug is used */
	extval = 0400;
	defin(0, "error");
	defin(1, "$accept");
	mem = mem0;
	i = 0;

	/* sorry -- no yacc parser here.....
		we must bootstrap somehow... */
	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].tvalue;
					if(j != 0 && j != ty)
						error("type redeclaration of nonterminal %s",
							nontrst[t-NTBASE].name );
					else
						nontrst[t-NTBASE].tvalue = 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);
				}
				if((t=gettok()) == NUMBER) {
					tokset[j].value = numbval;
					if(j < ndefout && j > 2)
						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 */
	defout(1);

	Bprint(ftable, "#define yyclearin yychar = -1\n");
	Bprint(ftable, "#define yyerrok yyerrflag = 0\n");
	Bprint(ftable, "extern int yychar;\nextern short yyerrflag;\n");
	Bprint(ftable, "#ifndef YYMAXDEPTH\n#define YYMAXDEPTH 150\n#endif\n" );
	if(!ntypes)
		Bprint(ftable, "#ifndef YYSTYPE\n#define YYSTYPE int\n#endif\n");
	Bprint(ftable, "YYSTYPE yylval, 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 != C_IDENTIFIER)
		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 == C_IDENTIFIER) {
				*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].tvalue) {
			/* 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].tvalue;
				else
					tempty = TYPE(toklev[tempty]);
			if(tempty != nontrst[*prdptr[nprod]-NTBASE].tvalue)
				error("default action causes potential type clash");
		}
		if(++nprod >= NPROD)
			error("more than %d rules", NPROD);
		prdptr[nprod] = mem;
		levprd[nprod] = 0;
	}
	/* end of all rules */
	finact();
	if(t == MARK) {
		Bprint(ftable, "\n# line %d \"%s\"\n", lineno, infile);
		while((c=Bgetc(finput)) != Beof)
			Bputc(ftable, c);
	}
	Bclose(finput);
}

/*
 * finish action routine
 */
void
finact(void)
{

	Bclose(faction);
	Bprint(ftable, "# define YYERRCODE %d\n", tokset[2].value);
}

/*
 * define s to be a terminal if t=0
 * or a nonterminal if t=1
 */
defin(int t, char *s)
{
	int val;

	val = 0;
	if(t) {
		if(++nnonter >= NNONTERM)
			error("too many nonterminals, limit %d",NNONTERM);
		nontrst[nnonter].name = cstash(s);
		return NTBASE + nnonter;
	}
	/* must be a token */
	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] == ' ' && s[2] == 0) {
		val = s[1];
		goto out;
	}
	/* escape sequence */
	if(s[0] == ' ' && s[1] == '\\' && 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] <= '7' && s[2] >= '0') {
		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;
	}
	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;
	char *cp;
	static int beenhere = 0;

	if(!beenhere++ && fdebug != 0)
		for(i = 0; i < sizeof(tmpx)/sizeof(tmpx[0]); i++)
			tmpx[i] = -1;
	for(i = ndefout; i <= ntokens; ++i) {
		cp = tokset[i].name;
		/* literals */
		if(*cp == ' ')
			tmpx[tokset[i].value] = i;
		else {
			Bprint(ftable, "# define %s %d\n",
				tokset[i].name, tokset[i].value);
			if(fdefine != 0)
				Bprint(fdefine, "# define %s %d\n",
					tokset[i].name, tokset[i].value);
			tmpx[tokset[i].value] = i;
		}
	}
	ndefout = ntokens+1;
	if(last && fdebug != 0) {
		Bprint(fdebug, "char *yytoknames[] = {\n");
		for(i = 0; i < sizeof(tmpx)/sizeof(tmpx[0]); i++)
			if(tmpx[i] == -1)
				Bprint(fdebug, "0,");
			else
				Bprint(fdebug, "\"%s\",\n", tokset[tmpx[i]].name);
		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;
}

gettok(void)
{
	int i, base, c, match, reserve;
	static int peekline;

begin:
	reserve = 0;
	lineno += peekline;
	peekline = 0;
	c = Bgetc(finput);
	while(c == ' ' || c == '\n' || c == '\t' || c == '\f') {
		if(c == '\n')
			++lineno;
		c = Bgetc(finput);
	}
	/* skip comment */
	if(c == '/') {
		lineno += skipcom();
		goto begin;
	}
	switch(c) {
	case Beof:
		return ENDFILE;
	case '{':
		Bungetc(finput);
		/* action ... */
		return '=';
	case '<':
		/* get, and look up, a type name (union member name) */
		i = 0;
		while((c=Bgetc(finput)) != '>' && c >= 0 && c != '\n') {
			tokname[i] = c;
			if(++i >= NAMESIZE)
				--i;
		}
		if(c != '>')
			error("unterminated < ... > clause");
		tokname[i] = 0;
		for(i = 1; i <= ntypes; ++i)
			if(!strcmp(typeset[i], tokname)) {
				numbval = i;
				return TYPENAME;
			}
		typeset[numbval = ++ntypes] = cstash(tokname);
		return TYPENAME;
	case '"':
	case '\'':
		match = c;
		tokname[0] = ' ';
		i = 1;
		for(;;) {
			c = Bgetc(finput);
			if(c == '\n' || c <= 0)
				error("illegal or missing ' or \"" );
			if(c == '\\') {
				c = Bgetc(finput);
				tokname[i] = '\\';
				if(++i >= NAMESIZE)
					--i;
			} else
				if(c == match)
					break;
			tokname[i] = c;
			if(++i >= NAMESIZE)
				--i;
		}
		break;
	case '%':
	case '\\':
		switch(c = Bgetc(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 = Bgetc(finput); isdigit(c); c = Bgetc(finput))
				numbval = numbval*base + c - '0';
			Bungetc(finput);
			return NUMBER;
		}
		if(islower(c) || isupper(c) || c=='_' || c=='.' || c=='$')  {
			i = 0;
			while(islower(c) || isupper(c) || isdigit(c) ||
			    c == '-' || c=='_' || c=='.' || c=='$') {
				tokname[i] = c;
				if(reserve && isupper(c))
					tokname[i] += 'a'-'A';
				if(++i >= NAMESIZE)
					--i;
				c = Bgetc(finput);
			}
		} else
			return c;
		Bungetc(finput);
	}
	tokname[i] = 0;
	/* find a reserved word */
	if(reserve) {
		if(!strcmp(tokname,"term"))
			return TERM;
		if(!strcmp(tokname,"token"))
			return TERM;
		if(!strcmp(tokname,"left"))
			return LEFT;
		if(!strcmp(tokname,"nonassoc"))
			return BINARY;
		if(!strcmp(tokname,"binary"))
			return BINARY;
		if(!strcmp(tokname,"right"))
			return RIGHT;
		if(!strcmp(tokname,"prec"))
			return PREC;
		if(!strcmp(tokname,"start"))
			return START;
		if(!strcmp(tokname,"type"))
			return TYPEDEF;
		if(!strcmp(tokname,"union"))
			return UNION;
		error("invalid escape, or illegal reserved word: %s", tokname);
	}
	/* look ahead to distinguish IDENTIFIER from C_IDENTIFIER */
	c = Bgetc(finput);
	while(c == ' ' || c == '\t'|| c == '\n' || c == '\f' || c == '/') {
		if(c == '\n')
			++peekline;
		/* look for comments */
		if(c == '/')
			peekline += skipcom();
		c = Bgetc(finput);
	}
	if(c == ':')
		return C_IDENTIFIER;
	Bungetc(finput);
	return IDENTIFIER;
}

/*
 * determine the type of a symbol
 */
fdtype(int t)
{
	int v;

	if(t >= NTBASE)
		v = nontrst[t-NTBASE].tvalue;
	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;
}

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)
{

	int level, c;

	Bprint(ftable, "\n# line %d \"%s\"\n", lineno, infile);
	Bprint(ftable, "typedef union ");
	if(fdefine != 0)
		Bprint(fdefine, "\ntypedef union ");

	level = 0;
	for(;;) {
		if((c=Bgetc(finput)) == Beof)
			error("EOF encountered while processing %%union");
		Bputc(ftable, c);
		if(fdefine != 0)
			Bputc(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, " YYSTYPE;\nextern YYSTYPE yylval;\n");
				return;
			}
		}
	}
}

/*
 * copies code between \{ and \}
 */
void
cpycode(void)
{

	int c;

	c = Bgetc(finput);
	if(c == '\n') {
		c = Bgetc(finput);
		lineno++;
	}
	Bprint(ftable, "\n# line %d \"%s\"\n", lineno, infile);
	while(c != Beof) {
		if(c == '\\') {
			if((c=Bgetc(finput)) == '}')
				return;
			Bputc(ftable , '\\');
		}
		if(c == '%') {
			if((c=Bgetc(finput)) == '}')
				return;
			Bputc(ftable , '%');
		}
		Bputc(ftable , c);
		if(c == '\n')
			++lineno;
		c = Bgetc(finput);
	}
	error("eof before %%}");
}

/*
 * skip over comments
 * skipcom is called after reading a '/'
 */
skipcom(void)
{
	int c, i;

	/* i is the number of lines skipped */
	i = 0;
	if(Bgetc(finput) != '*')
		error("illegal comment");
	c = Bgetc(finput);
	while(c != Beof) {
		while(c == '*')
			if((c=Bgetc(finput)) == '/')
				return i;
		if(c == '\n')
			++i;
		c = Bgetc(finput);
	}
	error("EOF inside comment");
}

/*
 * copy C action to the next ; or closing }
 */
void
cpyact(int offset)
{
	int brac, c, match, j, s, tok, fnd;

	Bprint(faction, "\n# line %d \"%s\"\n", lineno, infile);
	brac = 0;

loop:
	c = Bgetc(finput);
swt:
	switch(c) {
	case ';':
		if(brac == 0) {
			Bputc(faction , c);
			return;
		}
		goto lcopy;

	case '{':
		brac++;
		goto lcopy;

	case '$':
		s = 1;
		tok = -1;
		c = Bgetc(finput);
		/* type description */
		if(c == '<') {
			Bungetc(finput);
			if(gettok() != TYPENAME)
				error("bad syntax on $<ident> clause");
			tok = numbval;
			c = Bgetc(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 = Bgetc(finput);
		}
		if(isdigit(c)) {
			j = 0;
			while(isdigit(c)) {
				j= j*10 + c-'0';
				c = Bgetc(finput);
			}
			Bungetc(finput);
			j = j*s - offset;
			if(j > 0)
				error("Illegal use of $%d", j+offset);
		dollar:
			Bprint(faction, "yypvt[-%d]", -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 */
			Bungetc(finput);
			if(gettok() != IDENTIFIER)
				error("$ must be followed by an identifier");
			tok = chfind(2, tokname);
			if((c = Bgetc(finput)) != '#') {
				Bungetc(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 '}':
		if(--brac)
			goto lcopy;
		Bputc(faction, c);
		return;

	case '/':
		/* look for comments */
		Bputc(faction , c);
		c = Bgetc(finput);
		if(c != '*')
			goto swt;
		/* it really is a comment */
		Bputc(faction, c);
		c = Bgetc(finput);
		while(c >= 0) {
			while(c == '*') {
				Bputc(faction, c);
				if((c=Bgetc(finput)) == '/')
					goto lcopy;
			}
			Bputc(faction , c);
			if(c == '\n')
				++lineno;
			c = Bgetc(finput);
		}
		error("EOF inside comment");

	case '\'':
		/* character constant */
		match = '\'';
		goto string;

	case '"':
		/* character string */
		match = '"';

	string:
		Bputc(faction , c);
		while(c=Bgetc(finput)) {
			if(c == '\\') {
				Bputc(faction , c);
				c = Bgetc(finput);
				if(c == '\n')
					++lineno;
			} else
				if(c == match)
					goto lcopy;
				else
					if(c == '\n')
						error("newline in string or char. const.");
			Bputc(faction , c);
		}
		error("EOF in string or character constant");

	case Beof:
		error("action does not terminate");

	case '\n':
		++lineno;
		goto lcopy;
	}

lcopy:
	Bputc(faction, c);
	goto loop;
}

void
openup(char *stem, int Dflag, 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(Dflag) {
		sprint(buf, "%s.%s", stem, FILEDEBUG);
		if((fdebug = Bopen(buf, OWRITE)) == 0)
			error("can't open %s", buf);
		Bprint(fdebug, "#ifdef YYDEBUG\n");
	}
	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 != 0)
		Bprint(fdebug, "char *yystates[] = {\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, (Lookset*)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 != 0)
								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 != 0)
		Bprint(fdebug, "};\n#endif\n");
	Bprint(ftable, "\t};\n");
	wdef("YYNPROD", nprod);
}

/*
 * pack state i from temp1 into amem
 */
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, 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");
}

/*
 * 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 */
		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", tokset[p0].value, p1);
				++zzexcp;
				continue;
			}
			Bprint(ftemp, "%d,%d,", tokset[p0].value, 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 != 0) {
		Bprint(fdebug, "\"");
		if(lastred)
			goto nullmsg;
		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));
	nullmsg:
		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)
		if(temp1[++j1])
			Bprint(foutput, "\t%s  goto %d\n", symnam(j0+NTBASE), temp1[j1]);
}

/*
 * output a definition of s to the value n
 */
void
wdef(char *s, int n)
{
	Bprint(ftable, "# define %s %d\n", s, n);
}

void
warray(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]);
		if(++i == n)
			Bprint(ftable, " };\n");
		else
			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 */
	if((finput=Bopen(tempname, OREAD)) == 0)
		error("optimizer cannot open tempfile");
	pgo[0] = 0;
	temp1[0] = 0;
	nstate = 0;
	nnonter = 0;
	for(;;) {
		switch(gtnm()) {
		case '\n':
			temp1[++nstate] = --pmem - mem0;
		case ',':
			continue;
		case '$':
			break;
		default:
			error("bad tempfile");
		}
		break;
	}

	temp1[nstate] = yypgo[0] = --pmem - mem0;
	for(;;) {
		switch(gtnm()) {
		case '\n':
			yypgo[++nnonter] = pmem-mem0;
		case ',':
			continue;
		case -1:
			break;
		default:
			error("bad tempfile");
		}
		break;
	}
	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  ", 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
 */
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",
		pmem-mem0+1, MEMSIZE, maxa-amem+1, ACTSIZE);
	Bprint(foutput, "%d table entries, %d zero\n", (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 YYLAST %d\n", 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]);
		if(++i == n)
			Bprint(ftable, " };\n");
		else
			Bprint(ftable, ",");
	}
}

/*
 * read and convert an integer from the standard input
 * return the terminating character
 * blanks, tabs, and newlines are ignored
 */
gtnm(void)
{
	int s, val, c;

	s = 1;
	val = 0;
	while((c=Bgetc(finput)) != Beof) {
		if(isdigit(c))
			val = val*10 + c-'0';
		else
			if(c == '-')
				s = -1;
			else
				break;
	}
	*pmem++ = s*val;
	if(pmem > &mem0[MEMSIZE])
		error("out of space");
	return c;
}
