ref: 46cf9d4eb02a3a7c2516be3053994d4fb69eb949
dir: /sys/src/cmd/yacc.c/
#include <u.h> #include <libc.h> #include <bio.h> #include <ctype.h> #define Bungetrune Bungetc /* ok for now. */ /* * all these are 32 bit */ #define TBITSET ((32+NTERMS)/32) /* BOTCH?? +31 */ #define BIT(a,i) ((a)[(i)>>5] & (1<<((i)&037))) #define SETBIT(a,i) ((a)[(i)>>5] |= (1<<((i)&037))) #define NWORDS(n) (((n)+32)/32) #define PARSER "/sys/lib/yaccpar" #define PARSERS "/sys/lib/yaccpars" #define TEMPNAME "y.tmp.XXXXXX" #define ACTNAME "y.acts.XXXXXX" #define OFILE "tab.c" #define FILEU "output" #define FILED "tab.h" #define FILEDEBUG "debug" enum { /* * the following are adjustable * according to memory size */ ACTSIZE = 40000, MEMSIZE = 40000, NSTATES = 2000, NTERMS = 511, NPROD = 1600, NNONTERM = 600, TEMPSIZE = 2000, CNAMSZ = 10000, LSETSIZE = 2400, WSETSIZE = 350, NAMESIZE = 50, NTYPES = 63, ISIZE = 400, PRIVATE = 0xE000, /* unicode private use */ /* relationships which must hold: TBITSET ints must hold NTERMS+1 bits... WSETSIZE >= NNONTERM LSETSIZE >= NNONTERM TEMPSIZE >= NTERMS + NNONTERM + 1 TEMPSIZE >= NSTATES */ NTBASE = 010000, ERRCODE = 8190, ACCEPTCODE = 8191, NOASC = 0, /* no assoc. */ LASC = 1, /* left assoc. */ RASC = 2, /* right assoc. */ BASC = 3, /* binary assoc. */ /* flags for state generation */ DONE = 0, MUSTDO = 1, MUSTLOOKAHEAD = 2, /* flags for a rule having an action, and being reduced */ ACTFLAG = 04, REDFLAG = 010, /* output parser flags */ YYFLAG1 = -1000, /* parse tokens */ IDENTIFIER = PRIVATE, MARK, TERM, LEFT, RIGHT, BINARY, PREC, LCURLY, IDENTCOLON, NUMBER, START, TYPEDEF, TYPENAME, UNION, ENDFILE = 0, EMPTY = 1, WHOKNOWS = 0, OK = 1, NOMORE = -1000, }; /* macros for getting associativity and precedence levels */ #define ASSOC(i) ((i)&03) #define PLEVEL(i) (((i)>>4)&077) #define TYPE(i) (((i)>>10)&077) /* macros for setting associativity and precedence levels */ #define SETASC(i,j) i |= j #define SETPLEV(i,j) i |= (j<<4) #define SETTYPE(i,j) i |= (j<<10) /* looping macros */ #define TLOOP(i) for(i=1; i<=ntokens; i++) #define NTLOOP(i) for(i=0; i<=nnonter; i++) #define PLOOP(s,i) for(i=s; i<nprod; i++) #define SLOOP(i) for(i=0; i<nstate; i++) #define WSBUMP(x) x++ #define WSLOOP(s,j) for(j=s; j<cwp; j++) #define ITMLOOP(i,p,q) for(q=pstate[i+1], p=pstate[i]; p<q; p++) #define SETLOOP(i) for(i=0; i<tbitset; i++) /* command to clobber tempfiles after use */ #define ZAPFILE(bfd, x) {\ if(bfd) Bterm(bfd); \ 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 */ char inpath[1024]; /* input full path */ int numbval; /* value of an input number */ char tokname[NAMESIZE+UTFmax+1]; /* input token name, slop for runes and 0 */ /* structure declarations */ typedef struct { int lset[TBITSET]; } Lkset; typedef struct { int* pitem; Lkset* look; } Item; typedef struct { char* name; int value; } Symb; typedef struct { int* pitem; int flag; Lkset ws; } Wset; /* storage of names */ char cnames[CNAMSZ]; /* place where token and nonterminal names are stored */ int cnamsz = CNAMSZ; /* size of cnames */ char* cnamp = cnames; /* place where next name is to be put in */ int ndefout = 4; /* number of defined symbols output */ char* tempname; char* actname; char ttempname[] = TEMPNAME; char tactname[] = ACTNAME; char* parser = PARSER; char* yydebug; /* storage of types */ int ntypes; /* number of types defined */ char* typeset[NTYPES]; /* pointers to type tags */ /* token information */ int ntokens = 0 ; /* number of tokens */ Symb tokset[NTERMS]; int toklev[NTERMS]; /* vector with the precedence of the terminals */ /* nonterminal information */ int nnonter = -1; /* the number of nonterminals */ Symb nontrst[NNONTERM]; int start; /* start symbol */ /* assigned token type values */ int extval = 0; char* ytabc = OFILE; /* name of y.tab.c */ /* grammar rule information */ int mem0[MEMSIZE] ; /* production storage */ int* mem = mem0; int nprod = 1; /* number of productions */ int* prdptr[NPROD]; /* pointers to descriptions of productions */ int levprd[NPROD]; /* precedence levels for the productions */ int rlines[NPROD]; /* line number for this rule */ /* state information */ int nstate = 0; /* number of states */ Item* pstate[NSTATES+2]; /* pointers to the descriptions of the states */ int tystate[NSTATES]; /* contains type information about the states */ int defact[NSTATES]; /* the default actions of states */ int tstates[NTERMS]; /* states generated by terminal gotos */ int ntstates[NNONTERM]; /* states generated by nonterminal gotos */ int mstates[NSTATES]; /* chain of overflows of term/nonterm generation lists */ int lastred; /* the number of the last reduction of a state */ /* lookahead set information */ Lkset lkst[LSETSIZE]; int nolook; /* flag to turn off lookahead computations */ int tbitset; /* size of lookahead sets */ int nlset = 0; /* next lookahead set index */ int nolook = 0; /* flag to suppress lookahead computations */ Lkset clset; /* temporary storage for lookahead computations */ /* working set information */ Wset wsets[WSETSIZE]; Wset* cwp; /* storage for action table */ int amem[ACTSIZE]; /* action table storage */ int* memp = amem; /* next free action table position */ int indgo[NSTATES]; /* index to the stored goto table */ /* temporary vector, indexable by states, terms, or ntokens */ int temp1[TEMPSIZE]; /* temporary storage, indexed by terms + ntokens or states */ int lineno = 1; /* current input line number */ int fatfl = 1; /* if on, error is fatal */ int nerrors = 0; /* number of errors */ /* statistics collection variables */ int zzgoent; int zzgobest; int zzacent; int zzexcp; int zzclose; int zzrrconf; int zzsrconf; int* ggreed = lkst[0].lset; int* pgo = wsets[0].ws.lset; int* yypgo = &nontrst[0].value; int maxspr = 0; /* maximum spread of any entry */ int maxoff = 0; /* maximum offset into a array */ int* pmem = mem0; int* maxa; int nxdb = 0; int adb = 0; /* storage for information about the nonterminals */ int** pres[NNONTERM+2]; /* vector of pointers to productions yielding each nonterminal */ Lkset* pfirst[NNONTERM+2]; /* vector of pointers to first sets for each nonterminal */ int pempty[NNONTERM+1]; /* vector of nonterminals nontrivially deriving e */ /* random stuff picked out from between functions */ int indebug = 0; Wset* zzcwp = wsets; int zzgoent = 0; int zzgobest = 0; int zzacent = 0; int zzexcp = 0; int zzclose = 0; int zzsrconf = 0; int* zzmemsz = mem0; int zzrrconf = 0; int pidebug = 0; /* debugging flag for putitem */ int gsdebug = 0; int cldebug = 0; /* debugging flag for closure */ int pkdebug = 0; int g2debug = 0; struct { char* name; long value; } resrv[] = { "binary", BINARY, "left", LEFT, "nonassoc", BINARY, "prec", PREC, "right", RIGHT, "start", START, "term", TERM, "token", TERM, "type", TYPEDEF, "union", UNION, 0, }; /* define functions */ void main(int, char**); void others(void); char* chcopy(char*, char*); char* writem(int*); char* symnam(int); void summary(void); void error(char*, ...); void aryfil(int*, int, int); int setunion(int*, int*); void prlook(Lkset*); void cpres(void); void cpfir(void); int state(int); void putitem(int*, Lkset*); void cempty(void); void stagen(void); void closure(int); Lkset* flset(Lkset*); void cleantmp(void); void intr(void); void setup(int, char**); void finact(void); int defin(int, char*); void defout(int); char* cstash(char*); long gettok(void); int fdtype(int); int chfind(int, char*); void cpyunion(void); void cpycode(void); int skipcom(void); void cpyact(int); void openup(char*, int, int, int, char*); void output(void); int apack(int*, int); void go2out(void); void go2gen(int); void precftn(int, int, int); void wract(int); void wrstate(int); void warray(char*, int*, int); void hideprod(void); void callopt(void); void gin(int); void stin(int); int nxti(void); void osummary(void); void aoutput(void); void arout(char*, int*, int); int gtnm(void); void main(int argc, char *argv[]) { setup(argc, argv); /* initialize and read productions */ tbitset = NWORDS(ntokens); cpres(); /* make table of which productions yield a given nonterminal */ cempty(); /* make a table of which nonterminals can match the empty string */ cpfir(); /* make a table of firsts of nonterminals */ stagen(); /* generate the states */ output(); /* write the states and the tables */ go2out(); hideprod(); summary(); callopt(); others(); exits(0); } /* * put out other arrays, copy the parsers */ void others(void) { int c, i, j; finput = Bopen(parser, OREAD); if(finput == 0) error("cannot find parser %s", parser); Blethal(finput, nil); 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 */ Bprint(ftable, "\n#line\t1\t\"%s\"\n", parser); 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"); Blethal(faction, nil); while((c=Bgetrune(faction)) != Beof) Bputrune(ftable, c); ZAPFILE(faction, 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 ap; nerrors++; va_start(ap, s); fprint(2, "%s:%d: fatal error: ", infile, lineno); vfprint(2, s, ap); fprint(2, "\n"); va_end(ap); 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(faction, actname); ZAPFILE(ftemp, tempname); } void intr(void) { cleantmp(); exits("interrupted"); } void usage(void) { fprint(2, "usage: yacc [-Dn] [-vdS] [-o outputfile] [-s stem] grammar\n"); exits("usage"); } void setup(int argc, char *argv[]) { long c, t; int i, j, lev, ty, ytab, *p; int vflag, dflag, stem; char actnm[8], *stemc; ytab = 0; vflag = 0; dflag = 0; stem = 0; stemc = "y"; foutput = 0; fdefine = 0; fdebug = 0; ARGBEGIN{ case 'v': case 'V': vflag++; break; case 'D': yydebug = EARGF(usage()); break; case 'd': dflag++; break; case 'o': ytab++; ytabc = EARGF(usage()); break; case 's': stem++; stemc = ARGF(); break; case 'S': parser = PARSERS; break; default: error("illegal option: %c", ARGC()); }ARGEND openup(stemc, dflag, vflag, ytab, ytabc); ftemp = Bopen(tempname = mktemp(ttempname), OWRITE); faction = Bopen(actname = mktemp(tactname), OWRITE); if(ftemp == 0 || faction == 0) error("cannot open temp file"); Blethal(ftemp, nil); Blethal(faction, nil); if(argc < 1) error("no input file"); infile = argv[0]; finput = Bopen(infile, OREAD); if(finput == 0) error("cannot open '%s'", argv[0]); if(fd2path(Bfildes(finput), inpath, sizeof(inpath)) == -1) error("cannot get path for %s", infile); Blethal(finput, nil); 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, inpath); 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, inpath); 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, inpath); while(c != Beof) { if(c == '\\') { if((c=Bgetrune(finput)) == '}') return; Bputc(ftable, '\\'); } if(c == '%') { if((c=Bgetrune(finput)) == '}') return; Bputc(ftable, '%'); } Bputrune(ftable, c); if(c == '\n') lineno++; c = Bgetrune(finput); } error("eof before %%}"); } /* * skip over comments * skipcom is called after reading a '/' */ int skipcom(void) { long c; int i; /* i is the number of lines skipped */ i = 0; c = Bgetrune(finput); if(c == '/'){ /* C++ //: skip to end of line */ while((c = Bgetrune(finput)) != Beof) if(c == '\n') return 1; }else if(c == '*'){ /* normal C comment */ while((c = Bgetrune(finput)) != Beof) { while(c == '*') if((c = Bgetrune(finput)) == '/') return i; if(c == '\n') i++; } }else error("illegal comment"); error("EOF inside comment"); return 0; } /* * copy C action to the next ; or closing } */ void cpyact(int offset) { long c; int brac, match, j, s, fnd, tok; Bprint(faction, "\n#line\t%d\t\"%s\"\n", lineno, inpath); 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) { snprint(buf, sizeof buf, "%s.%s", stem, FILEU); foutput = Bopen(buf, OWRITE); if(foutput == 0) error("cannot open %s", buf); Blethal(foutput, nil); } if(yydebug) { snprint(buf, sizeof buf, "%s.%s", stem, FILEDEBUG); if((fdebug = Bopen(buf, OWRITE)) == 0) error("can't open %s", buf); } if(dflag) { snprint(buf, sizeof buf, "%s.%s", stem, FILED); fdefine = Bopen(buf, OWRITE); if(fdefine == 0) error("can't create %s", buf); Blethal(fdefine, nil); } if(ytab == 0) snprint(buf, sizeof buf, "%s.%s", stem, OFILE); else strecpy(buf, buf+sizeof buf, ytabc); ftable = Bopen(buf, OWRITE); if(ftable == 0) error("cannot open table file %s", buf); Blethal(ftable, nil); } /* * 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"); Blethal(finput, nil); pgo[0] = 0; temp1[0] = 0; nstate = 0; nnonter = 0; for(;;) { switch(gtnm()) { case '\n': nstate++; pmem--; temp1[nstate] = pmem - mem0; case ',': continue; case '$': break; default: error("bad tempfile"); } break; } pmem--; temp1[nstate] = yypgo[0] = pmem - mem0; for(;;) { switch(gtnm()) { case '\n': nnonter++; yypgo[nnonter] = pmem-mem0; case ',': continue; case -1: break; default: error("bad tempfile"); } break; } pmem--; yypgo[nnonter--] = pmem - mem0; for(i = 0; i < nstate; i++) { k = 32000; j = 0; q = mem0 + temp1[i+1]; for(p = mem0 + temp1[i]; p < q ; p += 2) { if(*p > j) j = *p; if(*p < k) k = *p; } /* nontrivial situation */ if(k <= j) { /* j is now the range */ /* j -= k; /* call scj */ if(k > maxoff) maxoff = k; } tystate[i] = (temp1[i+1]-temp1[i]) + 2*j; if(j > maxspr) maxspr = j; } /* initialize ggreed table */ for(i = 1; i <= nnonter; i++) { ggreed[i] = 1; j = 0; /* minimum entry index is always 0 */ q = mem0 + yypgo[i+1] - 1; for(p = mem0+yypgo[i]; p < q ; p += 2) { ggreed[i] += 2; if(*p > j) j = *p; } ggreed[i] = ggreed[i] + 2*j; if(j > maxoff) maxoff = j; } /* now, prepare to put the shift actions into the amem array */ for(i = 0; i < ACTSIZE; i++) amem[i] = 0; maxa = amem; for(i = 0; i < nstate; i++) { if(tystate[i] == 0 && adb > 1) Bprint(ftable, "State %d: null\n", i); indgo[i] = YYFLAG1; } while((i = nxti()) != NOMORE) if(i >= 0) stin(i); else gin(-i); /* print amem array */ if(adb > 2 ) for(p = amem; p <= maxa; p += 10) { Bprint(ftable, "%4d ", (int)(p-amem)); for(i = 0; i < 10; ++i) Bprint(ftable, "%4d ", p[i]); Bprint(ftable, "\n"); } /* write out the output appropriate to the language */ aoutput(); osummary(); ZAPFILE(ftemp, 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; }