# include "dextern"

	/* variables used locally */

	/* lookahead computations */

int tbitset;  /* size of lookahead sets */
struct looksets lkst [ LSETSIZE ];
int nlset = 0; /* next lookahead set index */
int nolook = 0; /* flag to suppress lookahead computations */
struct looksets clset;  /* temporary storage for lookahead computations */

	/* working set computations */

struct wset wsets[ WSETSIZE ];
struct wset *cwp;

	/* state information */

int nstate = 0;		/* number of states */
struct item *pstate[NSTATES+2];	/* pointers to the descriptions of the states */
int tystate[NSTATES];	/* contains type information about the states */
int indgo[NSTATES];		/* index to the stored goto table */
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  */

	/* storage for the actions in the parser */

int amem[ACTSIZE];	/* action table storage */
int *memp = amem;	/* next free action table position */

	/* other storage areas */

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 */

	/* storage for information about the nonterminals */

int **pres[NNONTERM+2];  /* vector of pointers to productions yielding each nonterminal */
struct looksets *pfirst[NNONTERM+2];  /* vector of pointers to first sets for each nonterminal */
int pempty[NNONTERM+1];  /* vector of nonterminals nontrivially deriving e */

main(argc,argv) 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();
	exit(0);
	}

others(){ /* put out other arrays, copy the parsers */
	register c, i, j;

	finput = fopen( PARSER, "r" );
	if( finput == NULL ) 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=getc(finput) ) != EOF ){
		if( c == '$' ){
			if( (c=getc(finput)) != 'A' ) putc( '$', ftable );
			else { /* copy actions */
				faction = fopen( ACTNAME, "r" );
				if( faction == NULL ) error( "cannot reopen action tempfile" );
				while( (c=getc(faction) ) != EOF ) putc( c, ftable );
				fclose(faction);
				ZAPFILE(ACTNAME);
				c = getc(finput);
				}
			}
		putc( c, ftable );
		}

	fclose( ftable );
	}

char *chcopy( p, q )  char *p, *q; {
	/* copies string q into p, returning next free char ptr */
	while( (*p = *q++) != 0 ) ++p;
	return( p );
	}

# define ISIZE 400
char *writem(pp) int *pp; { /* creates output string for item pointed to by 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" );
		}

	if( (i = *pp) < 0 ){ /* an item calling for a reduction */
		q = chcopy( q, "    (" );
		sprintf( q, "%d)", -i );
		}

	return( sarr );
	}

char *symnam(i){ /* return a pointer to the name of symbol i */
	char *cp;

	cp = (i>=NTBASE) ? nontrst[i-NTBASE].name : tokset[i].name ;
	if( *cp == ' ' ) ++cp;
	return( cp );
	}

struct 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;

summary(){ /* output the summary on the tty */

	if( foutput!=NULL ){
		fprintf( foutput, "\n%d/%d terminals, %d/%d nonterminals\n", ntokens, NTERMS,
			    nnonter, NNONTERM );
		fprintf( foutput, "%d/%d grammar rules, %d/%d states\n", nprod, NPROD, nstate, NSTATES );
		fprintf( foutput, "%d shift/reduce, %d reduce/reduce conflicts reported\n", zzsrconf, zzrrconf );
		fprintf( foutput, "%d/%d working sets used\n", zzcwp-wsets,  WSETSIZE );
		fprintf( foutput, "memory: states,etc. %d/%d, parser %d/%d\n", zzmemsz-mem0, MEMSIZE,
			    memp-amem, ACTSIZE );
		fprintf( foutput, "%d/%d distinct lookahead sets\n", nlset, LSETSIZE );
		fprintf( foutput, "%d extra closures\n", zzclose - 2*nstate );
		fprintf( foutput, "%d shift entries, %d exceptions\n", zzacent, zzexcp );
		fprintf( foutput, "%d goto entries\n", zzgoent );
		fprintf( foutput, "%d entries saved by goto default\n", zzgobest );
		}
	if( zzsrconf!=0 || zzrrconf!=0 ){
		  fprintf( stdout,"\nconflicts: ");
		  if( zzsrconf )fprintf( stdout, "%d shift/reduce" , zzsrconf );
		  if( zzsrconf && zzrrconf )fprintf( stdout, ", " );
		  if( zzrrconf )fprintf( stdout, "%d reduce/reduce" , zzrrconf );
		  fprintf( stdout, "\n" );
		  }

	fclose( ftemp );
	if( fdefine != NULL ) fclose( fdefine );
	}

/* VARARGS1 */
error(s,a1) char *s; { /* write out error comment */
	
	++nerrors;
	fprintf( stderr, "\n fatal error: ");
	fprintf( stderr, s,a1);
	fprintf( stderr, ", line %d\n", lineno );
	if( !fatfl ) return;
	summary();
	exit(1);
	}

aryfil( v, n, c ) int *v,n,c; { /* set elements 0 through n-1 to c */
	int i;
	for( i=0; i<n; ++i ) v[i] = c;
	}

setunion( a, b ) register *a, *b; {
	/* set a to the union of a and b */
	/* return 1 if b is not a subset of a, 0 otherwise */
	register i, x, sub;

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

prlook( p ) struct looksets *p;{
	register j, *pp;
	pp = p->lset;
	if( pp == 0 ) fprintf( foutput, "\tNULL");
	else {
		fprintf( foutput, " { " );
		TLOOP(j) {
			if( BIT(pp,j) ) fprintf( foutput,  "%s ", symnam(j) );
			}
		fprintf( foutput,  "}" );
		}
	}

cpres(){ /* 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 */
	register **pmem;
	register c, j, i;
	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();
		exit(1);
		}
	if( pmem != &pyield[nprod] ) error( "internal Yacc error: pyield %d", pmem-&pyield[nprod] );
	}

int indebug = 0;
cpfir() {
	/* compute an array with the first of nonterminals */
	register *p, **s, i, **t, ch, changes;

	zzcwp = &wsets[nnonter];
	NTLOOP(i){
		aryfil( wsets[i].ws.lset, tbitset, 0 );
		t = pres[i+1];
		for( s=pres[i]; s<t; ++s ){ /* initially fill the sets */
			for( p = *s; (ch = *p) > 0 ; ++p ) {
				if( ch < NTBASE ) {
					SETBIT( wsets[i].ws.lset, ch );
					break;
					}
				else 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!=NULL) ){
		NTLOOP(i) {
			fprintf( foutput,  "\n%s: ", nontrst[i].name );
			prlook( pfirst[i] );
			fprintf( foutput,  " %d\n", pempty[i] );
			}
		}
	}

state(c){ /* sorts last state,and sees if it equals earlier ones. returns state number */
	int size1,size2;
	register i;
	struct item *p1, *p2, *k, *l, *q1, *q2;
	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;
			struct looksets *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++);
	}

int pidebug = 0; /* debugging flag for putitem */
putitem( ptr, lptr )  int *ptr;  struct looksets *lptr; {
	register struct item *j;

	if( pidebug && (foutput!=NULL) ) {
		fprintf( 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" );
		}
	}

cempty(){ /* mark nonterminals which derive the empty string */
	/* also, look for nonterminals which don't derive any token strings */

# define EMPTY 1
# define WHOKNOWS 0
# define OK 1

	register 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;
			}
		if( *p < 0 ){ /* production can be derived */
			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();
		exit(1);
		}

	/* 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){
		if( pempty[ *prdptr[i]-NTBASE ]==WHOKNOWS ){ /* not known to be empty */
			for( p=prdptr[i]+1; *p>=NTBASE && pempty[*p-NTBASE]==EMPTY ; ++p ) ;
			if( *p < 0 ){ /* we have a nontrivially empty nonterminal */
				pempty[*prdptr[i]-NTBASE] = EMPTY;
				goto again; /* got one ... try for another */
				}
			}
		}

	}

int gsdebug = 0;
stagen(){ /* generate the states */

	int i, j;
	register c;
	register struct 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] = (struct 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);
		WSLOOP(wsets,p){ /* generate goto's */
			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){
				if( c == *(q->pitem) ){ /* this item contributes to the goto */
					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!=NULL) ){
			fprintf( foutput,  "%d: ", i );
			NTLOOP(j) {
				if( temp1[j] ) fprintf( foutput,  "%s %d, ", nontrst[j].name, temp1[j] );
				}
			fprintf( foutput, "\n");
			}
		indgo[i] = apack( &temp1[1], nnonter-1 ) - 1;
		goto more; /* we have done one goto; do some more */
		}
	/* no more to do... stop */
	}

int cldebug = 0; /* debugging flag for closure */
closure(i){ /* generate the closure of state i */

	int c, ch, work, k;
	register struct wset *u, *v;
	int *pi;
	int **s, **t;
	struct item *q;
	register struct item *p;

	++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;
			c = *(u->pitem);  /* dot is before c */
	
			if( c < NTBASE ){
				u->flag = 0;
				continue;  /* only interesting case is where . is before nonterminal */
				}
	
			/* 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 ){
						if( ch < NTBASE ){ /* terminal symbol */
							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 -= NTBASE; /* c is now nonterminal number */
	
			t = pres[c+1];
			for( s=pres[c]; s<t; ++s ){
				/* put these items into the closure */
				WSLOOP(wsets,v){ /* is the item there */
					if( v->pitem == *s ){ /* yes, it is there */
						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!=NULL) ){
		fprintf( foutput, "\nState %d, nolook = %d\n", i, nolook );
		WSLOOP(wsets,u){
			if( u->flag ) fprintf( foutput, "flag set!\n");
			u->flag = 0;
			fprintf( foutput, "\t%s", writem(u->pitem));
			prlook( &u->ws );
			fprintf( foutput,  "\n" );
			}
		}
	}

struct looksets *flset( p )   struct looksets *p; {
	/* decide if the lookahead set pointed to by p is known */
	/* return pointer to a perminent location for the set */

	register struct looksets *q;
	int j, *w;
	register *u, *v;

	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 );
	}
