diff options
author | rsc <devnull@localhost> | 2003-11-23 18:04:47 +0000 |
---|---|---|
committer | rsc <devnull@localhost> | 2003-11-23 18:04:47 +0000 |
commit | bc7cb1a15a67c859c8c71c4b52bb35fe9425a63d (patch) | |
tree | 8ca0fe4e2418e6aa18dc74a236c577a719f6c6ed /src/cmd/yacc.c | |
parent | f08fdedcee12c06e3ce9ac9bec363915978e8289 (diff) | |
download | plan9port-bc7cb1a15a67c859c8c71c4b52bb35fe9425a63d.tar.gz plan9port-bc7cb1a15a67c859c8c71c4b52bb35fe9425a63d.tar.bz2 plan9port-bc7cb1a15a67c859c8c71c4b52bb35fe9425a63d.zip |
new utilities.
the .C files compile but are renamed to avoid building automatically.
Diffstat (limited to 'src/cmd/yacc.c')
-rw-r--r-- | src/cmd/yacc.c | 2939 |
1 files changed, 2939 insertions, 0 deletions
diff --git a/src/cmd/yacc.c b/src/cmd/yacc.c new file mode 100644 index 00000000..a9a2b2d7 --- /dev/null +++ b/src/cmd/yacc.c @@ -0,0 +1,2939 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <ctype.h> + +#define Bungetrune Bungetc /* ok for now. */ + +/* + * all these are 32 bit + */ +#define TBITSET ((32+NTERMS)/32) /* BOTCH?? +31 */ +#define BIT(a,i) ((a)[(i)>>5] & (1<<((i)&037))) +#define SETBIT(a,i) ((a)[(i)>>5] |= (1<<((i)&037))) +#define NWORDS(n) (((n)+32)/32) + +#define PARSER "/sys/lib/yaccpar" +#define PARSERS "/sys/lib/yaccpars" +#define TEMPNAME "y.tmp.XXXXXX" +#define ACTNAME "y.acts.XXXXXX" +#define OFILE "tab.c" +#define FILEU "output" +#define FILED "tab.h" +#define FILEDEBUG "debug" + +enum +{ +/* + * the following are adjustable + * according to memory size + */ + ACTSIZE = 40000, + MEMSIZE = 40000, + NSTATES = 2000, + NTERMS = 511, + NPROD = 1600, + NNONTERM = 600, + TEMPSIZE = 2000, + CNAMSZ = 10000, + LSETSIZE = 2400, + WSETSIZE = 350, + + NAMESIZE = 50, + NTYPES = 63, + ISIZE = 400, + + PRIVATE = 0xE000, /* unicode private use */ + + /* relationships which must hold: + TBITSET ints must hold NTERMS+1 bits... + WSETSIZE >= NNONTERM + LSETSIZE >= NNONTERM + TEMPSIZE >= NTERMS + NNONTERM + 1 + TEMPSIZE >= NSTATES + */ + + NTBASE = 010000, + ERRCODE = 8190, + ACCEPTCODE = 8191, + + NOASC = 0, /* no assoc. */ + LASC = 1, /* left assoc. */ + RASC = 2, /* right assoc. */ + BASC = 3, /* binary assoc. */ + + /* flags for state generation */ + + DONE = 0, + MUSTDO = 1, + MUSTLOOKAHEAD = 2, + + /* flags for a rule having an action, and being reduced */ + + ACTFLAG = 04, + REDFLAG = 010, + + /* output parser flags */ + YYFLAG1 = -1000, + + /* parse tokens */ + IDENTIFIER = PRIVATE, + MARK, + TERM, + LEFT, + RIGHT, + BINARY, + PREC, + LCURLY, + IDENTCOLON, + NUMBER, + START, + TYPEDEF, + TYPENAME, + UNION, + + ENDFILE = 0, + + EMPTY = 1, + WHOKNOWS = 0, + OK = 1, + NOMORE = -1000, +}; + + /* macros for getting associativity and precedence levels */ + +#define ASSOC(i) ((i)&03) +#define PLEVEL(i) (((i)>>4)&077) +#define TYPE(i) (((i)>>10)&077) + + /* macros for setting associativity and precedence levels */ + +#define SETASC(i,j) i |= j +#define SETPLEV(i,j) i |= (j<<4) +#define SETTYPE(i,j) i |= (j<<10) + + /* looping macros */ + +#define TLOOP(i) for(i=1; i<=ntokens; i++) +#define NTLOOP(i) for(i=0; i<=nnonter; i++) +#define PLOOP(s,i) for(i=s; i<nprod; i++) +#define SLOOP(i) for(i=0; i<nstate; i++) +#define WSBUMP(x) x++ +#define WSLOOP(s,j) for(j=s; j<cwp; j++) +#define ITMLOOP(i,p,q) for(q=pstate[i+1], p=pstate[i]; p<q; p++) +#define SETLOOP(i) for(i=0; i<tbitset; i++) + + /* command to clobber tempfiles after use */ + +#define ZAPFILE(x) if(x) remove(x) + + /* I/O descriptors */ + +Biobuf* faction; /* file for saving actions */ +Biobuf* fdefine; /* file for #defines */ +Biobuf* fdebug; /* y.debug for strings for debugging */ +Biobuf* ftable; /* y.tab.c file */ +Biobuf* ftemp; /* tempfile to pass 2 */ +Biobuf* finput; /* input file */ +Biobuf* foutput; /* y.output file */ + + /* communication variables between various I/O routines */ + +char* infile; /* input file name */ +int numbval; /* value of an input number */ +char tokname[NAMESIZE+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(parser, OREAD); + if(finput == 0) + error("cannot find parser %s", parser); + warray("yyr1", levprd, nprod); + aryfil(temp1, nprod, 0); + PLOOP(1, i) + temp1[i] = prdptr[i+1]-prdptr[i]-2; + warray("yyr2", temp1, nprod); + + aryfil(temp1, nstate, -1000); + TLOOP(i) + for(j=tstates[i]; j!=0; j=mstates[j]) + temp1[j] = i; + NTLOOP(i) + for(j=ntstates[i]; j!=0; j=mstates[j]) + temp1[j] = -i; + warray("yychk", temp1, nstate); + warray("yydef", defact, nstate); + + /* put out token translation tables */ + /* table 1 has 0-256 */ + aryfil(temp1, 256, 0); + c = 0; + TLOOP(i) { + j = tokset[i].value; + if(j >= 0 && j < 256) { + if(temp1[j]) { + print("yacc bug -- cant have 2 different Ts with same value\n"); + print(" %s and %s\n", tokset[i].name, tokset[temp1[j]].name); + nerrors++; + } + temp1[j] = i; + if(j > c) + c = j; + } + } + warray("yytok1", temp1, c+1); + + /* table 2 has PRIVATE-PRIVATE+256 */ + aryfil(temp1, 256, 0); + c = 0; + TLOOP(i) { + j = tokset[i].value - PRIVATE; + if(j >= 0 && j < 256) { + if(temp1[j]) { + print("yacc bug -- cant have 2 different Ts with same value\n"); + print(" %s and %s\n", tokset[i].name, tokset[temp1[j]].name); + nerrors++; + } + temp1[j] = i; + if(j > c) + c = j; + } + } + warray("yytok2", temp1, c+1); + + /* table 3 has everything else */ + Bprint(ftable, "long yytok3[] =\n{\n"); + c = 0; + TLOOP(i) { + j = tokset[i].value; + if(j >= 0 && j < 256) + continue; + if(j >= PRIVATE && j < 256+PRIVATE) + continue; + + Bprint(ftable, "%4d,%4d,", j, i); + c++; + if(c%5 == 0) + Bprint(ftable, "\n"); + } + Bprint(ftable, "%4d\n};\n", 0); + + /* copy parser text */ + while((c=Bgetrune(finput)) != Beof) { + if(c == '$') { + if((c = Bgetrune(finput)) != 'A') + Bputrune(ftable, '$'); + else { /* copy actions */ + faction = Bopen(actname, OREAD); + if(faction == 0) + error("cannot reopen action tempfile"); + while((c=Bgetrune(faction)) != Beof) + Bputrune(ftable, c); + Bterm(faction); + ZAPFILE(actname); + c = Bgetrune(finput); + } + } + Bputrune(ftable, c); + } + Bterm(ftable); +} + +/* + * copies string q into p, returning next free char ptr + */ +char* +chcopy(char* p, char* q) +{ + int c; + + while(c = *q) { + if(c == '"') + *p++ = '\\'; + *p++ = c; + q++; + } + *p = 0; + return p; +} + +/* + * creates output string for item pointed to by pp + */ +char* +writem(int *pp) +{ + int i,*p; + static char sarr[ISIZE]; + char* q; + + for(p=pp; *p>0; p++) + ; + p = prdptr[-*p]; + q = chcopy(sarr, nontrst[*p-NTBASE].name); + q = chcopy(q, ": "); + for(;;) { + *q = ' '; + p++; + if(p == pp) + *q = '.'; + q++; + *q = '\0'; + i = *p; + if(i <= 0) + break; + q = chcopy(q, symnam(i)); + if(q > &sarr[ISIZE-30]) + error("item too big"); + } + + /* an item calling for a reduction */ + i = *pp; + if(i < 0 ) { + q = chcopy(q, " ("); + sprint(q, "%d)", -i); + } + return sarr; +} + +/* + * return a pointer to the name of symbol i + */ +char* +symnam(int i) +{ + char* cp; + + cp = (i >= NTBASE)? nontrst[i-NTBASE].name: tokset[i].name; + if(*cp == ' ') + cp++; + return cp; +} + +/* + * output the summary on y.output + */ +void +summary(void) +{ + + if(foutput != 0) { + Bprint(foutput, "\n%d/%d terminals, %d/%d nonterminals\n", + ntokens, NTERMS, nnonter, NNONTERM); + Bprint(foutput, "%d/%d grammar rules, %d/%d states\n", + nprod, NPROD, nstate, NSTATES); + Bprint(foutput, "%d shift/reduce, %d reduce/reduce conflicts reported\n", + zzsrconf, zzrrconf); + Bprint(foutput, "%d/%d working sets used\n", + (int)(zzcwp-wsets), WSETSIZE); + Bprint(foutput, "memory: states,etc. %d/%d, parser %d/%d\n", + (int)(zzmemsz-mem0), MEMSIZE, (int)(memp-amem), ACTSIZE); + Bprint(foutput, "%d/%d distinct lookahead sets\n", nlset, LSETSIZE); + Bprint(foutput, "%d extra closures\n", zzclose - 2*nstate); + Bprint(foutput, "%d shift entries, %d exceptions\n", zzacent, zzexcp); + Bprint(foutput, "%d goto entries\n", zzgoent); + Bprint(foutput, "%d entries saved by goto default\n", zzgobest); + } + if(zzsrconf != 0 || zzrrconf != 0) { + print("\nconflicts: "); + if(zzsrconf) + print("%d shift/reduce", zzsrconf); + if(zzsrconf && zzrrconf) + print(", "); + if(zzrrconf) + print("%d reduce/reduce", zzrrconf); + print("\n"); + } + if(ftemp != 0) { + Bterm(ftemp); + ftemp = 0; + } + if(fdefine != 0) { + Bterm(fdefine); + fdefine = 0; + } +} + +/* + * write out error comment -- NEEDS WORK + */ +void +error(char *s, ...) +{ + + nerrors++; + fprint(2, "\n fatal error:"); + fprint(2, s, (&s)[1]); + fprint(2, ", %s:%d\n", infile, lineno); + if(!fatfl) + return; + summary(); + cleantmp(); + exits("error"); +} + +/* + * set elements 0 through n-1 to c + */ +void +aryfil(int *v, int n, int c) +{ + int i; + + for(i=0; i<n; i++) + v[i] = c; +} + +/* + * set a to the union of a and b + * return 1 if b is not a subset of a, 0 otherwise + */ +int +setunion(int *a, int *b) +{ + int i, x, sub; + + sub = 0; + SETLOOP(i) { + x = *a; + *a |= *b; + if(*a != x) + sub = 1; + a++; + b++; + } + return sub; +} + +void +prlook(Lkset* p) +{ + int j, *pp; + + pp = p->lset; + if(pp == 0) + Bprint(foutput, "\tNULL"); + else { + Bprint(foutput, " { "); + TLOOP(j) + if(BIT(pp,j)) + Bprint(foutput, "%s ", symnam(j)); + Bprint(foutput, "}"); + } +} + +/* + * compute an array with the beginnings of productions yielding given nonterminals + * The array pres points to these lists + * the array pyield has the lists: the total size is only NPROD+1 + */ +void +cpres(void) +{ + int c, j, i, **pmem; + static int *pyield[NPROD]; + + pmem = pyield; + NTLOOP(i) { + c = i+NTBASE; + pres[i] = pmem; + fatfl = 0; /* make undefined symbols nonfatal */ + PLOOP(0, j) + if(*prdptr[j] == c) + *pmem++ = prdptr[j]+1; + if(pres[i] == pmem) + error("nonterminal %s not defined!", nontrst[i].name); + } + pres[i] = pmem; + fatfl = 1; + if(nerrors) { + summary(); + cleantmp(); + exits("error"); + } + if(pmem != &pyield[nprod]) + error("internal Yacc error: pyield %d", pmem-&pyield[nprod]); +} + +/* + * compute an array with the first of nonterminals + */ +void +cpfir(void) +{ + int *p, **s, i, **t, ch, changes; + + zzcwp = &wsets[nnonter]; + NTLOOP(i) { + aryfil(wsets[i].ws.lset, tbitset, 0); + t = pres[i+1]; + /* initially fill the sets */ + for(s=pres[i]; s<t; ++s) + for(p = *s; (ch = *p) > 0; ++p) { + if(ch < NTBASE) { + SETBIT(wsets[i].ws.lset, ch); + break; + } + if(!pempty[ch-NTBASE]) + break; + } + } + + /* now, reflect transitivity */ + changes = 1; + while(changes) { + changes = 0; + NTLOOP(i) { + t = pres[i+1]; + for(s = pres[i]; s < t; ++s) + for(p = *s; (ch = (*p-NTBASE)) >= 0; ++p) { + changes |= setunion(wsets[i].ws.lset, wsets[ch].ws.lset); + if(!pempty[ch]) + break; + } + } + } + + NTLOOP(i) + pfirst[i] = flset(&wsets[i].ws); + if(!indebug) + return; + if(foutput != 0) + NTLOOP(i) { + Bprint(foutput, "\n%s: ", nontrst[i].name); + prlook(pfirst[i]); + Bprint(foutput, " %d\n", pempty[i]); + } +} + +/* + * sorts last state,and sees if it equals earlier ones. returns state number + */ +int +state(int c) +{ + Item *p1, *p2, *k, *l, *q1, *q2; + int size1, size2, i; + + p1 = pstate[nstate]; + p2 = pstate[nstate+1]; + if(p1 == p2) + return 0; /* null state */ + /* sort the items */ + for(k = p2-1; k > p1; k--) /* make k the biggest */ + for(l = k-1; l >= p1; --l) + if(l->pitem > k->pitem) { + int *s; + Lkset *ss; + + s = k->pitem; + k->pitem = l->pitem; + l->pitem = s; + ss = k->look; + k->look = l->look; + l->look = ss; + } + size1 = p2 - p1; /* size of state */ + + for(i = (c>=NTBASE)? ntstates[c-NTBASE]: tstates[c]; i != 0; i = mstates[i]) { + /* get ith state */ + q1 = pstate[i]; + q2 = pstate[i+1]; + size2 = q2 - q1; + if(size1 != size2) + continue; + k = p1; + for(l = q1; l < q2; l++) { + if(l->pitem != k->pitem) + break; + k++; + } + if(l != q2) + continue; + /* found it */ + pstate[nstate+1] = pstate[nstate]; /* delete last state */ + /* fix up lookaheads */ + if(nolook) + return i; + for(l = q1, k = p1; l < q2; ++l, ++k ) { + int s; + + SETLOOP(s) + clset.lset[s] = l->look->lset[s]; + if(setunion(clset.lset, k->look->lset)) { + tystate[i] = MUSTDO; + /* register the new set */ + l->look = flset( &clset ); + } + } + return i; + } + /* state is new */ + if(nolook) + error("yacc state/nolook error"); + pstate[nstate+2] = p2; + if(nstate+1 >= NSTATES) + error("too many states"); + if(c >= NTBASE) { + mstates[nstate] = ntstates[c-NTBASE]; + ntstates[c-NTBASE] = nstate; + } else { + mstates[nstate] = tstates[c]; + tstates[c] = nstate; + } + tystate[nstate] = MUSTDO; + return nstate++; +} + +void +putitem(int *ptr, Lkset *lptr) +{ + Item *j; + + if(pidebug && foutput != 0) + Bprint(foutput, "putitem(%s), state %d\n", writem(ptr), nstate); + j = pstate[nstate+1]; + j->pitem = ptr; + if(!nolook) + j->look = flset(lptr); + pstate[nstate+1] = ++j; + if((int*)j > zzmemsz) { + zzmemsz = (int*)j; + if(zzmemsz >= &mem0[MEMSIZE]) + error("out of state space"); + } +} + +/* + * mark nonterminals which derive the empty string + * also, look for nonterminals which don't derive any token strings + */ +void +cempty(void) +{ + + int i, *p; + + /* first, use the array pempty to detect productions that can never be reduced */ + /* set pempty to WHONOWS */ + aryfil(pempty, nnonter+1, WHOKNOWS); + + /* now, look at productions, marking nonterminals which derive something */ +more: + PLOOP(0, i) { + if(pempty[*prdptr[i] - NTBASE]) + continue; + for(p = prdptr[i]+1; *p >= 0; ++p) + if(*p >= NTBASE && pempty[*p-NTBASE] == WHOKNOWS) + break; + /* production can be derived */ + if(*p < 0) { + pempty[*prdptr[i]-NTBASE] = OK; + goto more; + } + } + + /* now, look at the nonterminals, to see if they are all OK */ + NTLOOP(i) { + /* the added production rises or falls as the start symbol ... */ + if(i == 0) + continue; + if(pempty[i] != OK) { + fatfl = 0; + error("nonterminal %s never derives any token string", nontrst[i].name); + } + } + + if(nerrors) { + summary(); + cleantmp(); + exits("error"); + } + + /* now, compute the pempty array, to see which nonterminals derive the empty string */ + /* set pempty to WHOKNOWS */ + aryfil( pempty, nnonter+1, WHOKNOWS); + + /* loop as long as we keep finding empty nonterminals */ + +again: + PLOOP(1, i) { + /* not known to be empty */ + if(pempty[*prdptr[i]-NTBASE] == WHOKNOWS) { + for(p = prdptr[i]+1; *p >= NTBASE && pempty[*p-NTBASE] == EMPTY ; ++p) + ; + /* we have a nontrivially empty nonterminal */ + if(*p < 0) { + pempty[*prdptr[i]-NTBASE] = EMPTY; + /* got one ... try for another */ + goto again; + } + } + } +} + +/* + * generate the states + */ +void +stagen(void) +{ + + int c, i, j, more; + Wset *p, *q; + + /* initialize */ + nstate = 0; + + /* THIS IS FUNNY from the standpoint of portability + * it represents the magic moment when the mem0 array, which has + * been holding the productions, starts to hold item pointers, of a + * different type... + * someday, alloc should be used to allocate all this stuff... for now, we + * accept that if pointers don't fit in integers, there is a problem... + */ + + pstate[0] = pstate[1] = (Item*)mem; + aryfil(clset.lset, tbitset, 0); + putitem(prdptr[0]+1, &clset); + tystate[0] = MUSTDO; + nstate = 1; + pstate[2] = pstate[1]; + + aryfil(amem, ACTSIZE, 0); + + /* now, the main state generation loop */ + for(more=1; more;) { + more = 0; + SLOOP(i) { + if(tystate[i] != MUSTDO) + continue; + tystate[i] = DONE; + aryfil(temp1, nnonter+1, 0); + /* take state i, close it, and do gotos */ + closure(i); + /* generate goto's */ + WSLOOP(wsets, p) { + if(p->flag) + continue; + p->flag = 1; + c = *(p->pitem); + if(c <= 1) { + if(pstate[i+1]-pstate[i] <= p-wsets) + tystate[i] = MUSTLOOKAHEAD; + continue; + } + /* do a goto on c */ + WSLOOP(p, q) + /* this item contributes to the goto */ + if(c == *(q->pitem)) { + putitem(q->pitem+1, &q->ws); + q->flag = 1; + } + if(c < NTBASE) + state(c); /* register new state */ + else + temp1[c-NTBASE] = state(c); + } + if(gsdebug && foutput != 0) { + Bprint(foutput, "%d: ", i); + NTLOOP(j) + if(temp1[j]) + Bprint(foutput, "%s %d, ", + nontrst[j].name, temp1[j]); + Bprint(foutput, "\n"); + } + indgo[i] = apack(&temp1[1], nnonter-1) - 1; + /* do some more */ + more = 1; + } + } +} + +/* + * generate the closure of state i + */ +void +closure(int i) +{ + + Wset *u, *v; + Item *p, *q; + int c, ch, work, k, *pi, **s, **t; + + zzclose++; + + /* first, copy kernel of state i to wsets */ + cwp = wsets; + ITMLOOP(i, p, q) { + cwp->pitem = p->pitem; + cwp->flag = 1; /* this item must get closed */ + SETLOOP(k) + cwp->ws.lset[k] = p->look->lset[k]; + WSBUMP(cwp); + } + + /* now, go through the loop, closing each item */ + work = 1; + while(work) { + work = 0; + WSLOOP(wsets, u) { + if(u->flag == 0) + continue; + /* dot is before c */ + c = *(u->pitem); + if(c < NTBASE) { + u->flag = 0; + /* only interesting case is where . is before nonterminal */ + continue; + } + + /* compute the lookahead */ + aryfil(clset.lset, tbitset, 0); + + /* find items involving c */ + WSLOOP(u, v) + if(v->flag == 1 && *(pi=v->pitem) == c) { + v->flag = 0; + if(nolook) + continue; + while((ch = *++pi) > 0) { + /* terminal symbol */ + if(ch < NTBASE) { + SETBIT(clset.lset, ch); + break; + } + /* nonterminal symbol */ + setunion(clset.lset, pfirst[ch-NTBASE]->lset); + if(!pempty[ch-NTBASE]) + break; + } + if(ch <= 0) + setunion(clset.lset, v->ws.lset); + } + + /* + * now loop over productions derived from c + * c is now nonterminal number + */ + c -= NTBASE; + t = pres[c+1]; + for(s = pres[c]; s < t; ++s) { + /* + * put these items into the closure + * is the item there + */ + WSLOOP(wsets, v) + /* yes, it is there */ + if(v->pitem == *s) { + if(nolook) + goto nexts; + if(setunion(v->ws.lset, clset.lset)) + v->flag = work = 1; + goto nexts; + } + + /* not there; make a new entry */ + if(cwp-wsets+1 >= WSETSIZE) + error( "working set overflow"); + cwp->pitem = *s; + cwp->flag = 1; + if(!nolook) { + work = 1; + SETLOOP(k) cwp->ws.lset[k] = clset.lset[k]; + } + WSBUMP(cwp); + + nexts:; + } + } + } + + /* have computed closure; flags are reset; return */ + if(cwp > zzcwp) + zzcwp = cwp; + if(cldebug && foutput != 0) { + Bprint(foutput, "\nState %d, nolook = %d\n", i, nolook); + WSLOOP(wsets, u) { + if(u->flag) + Bprint(foutput, "flag set!\n"); + u->flag = 0; + Bprint(foutput, "\t%s", writem(u->pitem)); + prlook(&u->ws); + Bprint(foutput, "\n"); + } + } +} + +/* + * decide if the lookahead set pointed to by p is known + * return pointer to a perminent location for the set + */ +Lkset* +flset(Lkset *p) +{ + Lkset *q; + int *u, *v, *w, j; + + for(q = &lkst[nlset]; q-- > lkst;) { + u = p->lset; + v = q->lset; + w = &v[tbitset]; + while(v < w) + if(*u++ != *v++) + goto more; + /* we have matched */ + return q; + more:; + } + /* add a new one */ + q = &lkst[nlset++]; + if(nlset >= LSETSIZE) + error("too many lookahead sets"); + SETLOOP(j) + q->lset[j] = p->lset[j]; + return q; +} + +void +cleantmp(void) +{ + ZAPFILE(actname); + ZAPFILE(tempname); +} + +void +intr(void) +{ + cleantmp(); + exits("interrupted"); +} + +void +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"); + } + 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; +} |