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