/* Copyright (c) 1991 by the Vrije Universiteit, Amsterdam, the Netherlands. * For full copyright and restrictions on use see the file COPYING in the top * level of the LLgen tree. */ /* * L L G E N * * An Extended LL(1) Parser Generator * * Author : Ceriel J.H. Jacobs */ /* * gencode.c * Defines the routine "gencode", which generates the parser * we wanted so badly. * This file is a mess, it should be cleaned up some time. */ #include # include "types.h" # include "io.h" # include "extern.h" # include "sets.h" # include "assert.h" # include "cclass.h" # ifndef NORCSID static string rcsid3 = "$Id$"; #endif /* NORCSID */ /* * Some codestrings used more than once */ static string c_arrend = "0 };\n"; static string c_close = "}\n"; static string c_break = "break;\n"; static string c_read = "LLread();\n"; /* Some constants used for reading from the action file */ # define ENDDECL 0400 # define IDENT 0401 static int nlabel; /* count for the generation of labels */ static int firsts; /* are there any? */ static int listcount; /* In this file the following routines are defined: */ extern gencode(); STATIC opentemp(); STATIC geninclude(); STATIC genrecovery(); #ifdef NON_CORRECTING STATIC genncrecovery(); #endif STATIC string genname(); STATIC generate(); STATIC void prset(); STATIC void macro(); STATIC controlline(); STATIC getparams(); STATIC getansiparams(); STATIC genprototypes(); STATIC gettok(); STATIC void rulecode(); STATIC int * dopush(); STATIC int * mk_tokenlist(); STATIC void getaction(); STATIC void alternation(); STATIC codeforterm(); STATIC genswhead(); STATIC gencases(); STATIC genpush(); STATIC genpop(); STATIC void genincrdecr(); STATIC add_cases(); STATIC int analyze_switch(); STATIC out_list(); STATIC genextname(); STATIC correct_prefix(); # define NOPOP -20000 p_mem alloc(), ralloc(); doclose(f) FILE *f; { if (ferror(f) != 0) { fatal(0,"Write error on temporary"); } fclose(f); } STATIC int * mk_tokenlist() { register int i = ntokens; register int *p = (int *)alloc((unsigned)(i * sizeof(int))) + i; while (i--) *--p = -1; return p; } STATIC genhdr() { if (!firsts) fputs("#define LLNOFIRSTS\n", fpars); if (ansi_c) fputs("#define LL_ANSI_C 1\n", fpars); else { fputs("#if __STDC__ || __cplusplus\n#define LL_ANSI_C 1\n#endif\n", fpars); } #ifdef NON_CORRECTING if (non_corr) fputs("#define LL_NON_CORR 1\n", fpars); #endif fprintf(fpars, "#define LL_LEXI %s\n", lexical); copyfile(incl_file); } gencode(argc) { register p_file p = files; /* Set up for code generation */ if ((fact = fopen(f_temp,"r")) == NULL) { fatal(0,e_noopen,f_temp); } #ifdef NON_CORRECTING /* The non-correcting error recovery must be generated BEFORE parser code is generated!!!! In case of conflict resolvers, the code-generation process will delete conflicting symbols from first and followsets, making them UNUSABLE for the non-correcting error recovery code. */ if (non_corr) genncrecovery(); #endif /* For every source file .... */ while (argc--) { /* Open temporary */ f_input = p->f_name; opentemp(f_input); correct_prefix(); /* generate code ... */ generate(p); getaction(2); doclose(fpars); /* And install */ install(genname(p->f_name),p->f_name); p++; } geninclude(); genrecovery(); fclose(fact); } STATIC opentemp(str) string str; { if ((fpars = fopen(f_pars,"w")) == NULL) { fatal(0,e_noopen,f_pars); } if (!str) str = "."; fprintf(fpars,LLgenid,str); } STATIC geninclude() { register p_token p; int maxno = 0; opentemp((string) 0); for (p = tokens; p < maxt; p++) { if (p->t_tokno > maxno) maxno = p->t_tokno; if (p->t_tokno >= 0400) { fprintf(fpars,"#define %s %d\n", p->t_string, p->t_tokno); } } fprintf(fpars, "#define %s_MAXTOKNO %d\n", prefix ? prefix : "LL", maxno); #ifdef NON_CORRECTING if (non_corr) { fprintf(fpars, "#define %sNONCORR\n", prefix ? prefix : "LL"); } #endif doclose(fpars); install(f_include, "."); } STATIC genrecovery() { register FILE *f; register p_token t; register int *q; register p_nont p; register p_set *psetl; int *index; int i; register p_start st; opentemp((string) 0); f = fpars; correct_prefix(); genhdr(); for (st = start; st; st = st->ff_next) { /* Make sure that every set the parser needs is in the list * before generating a define of the number of them! */ p = &nonterms[st->ff_nont]; if (g_gettype(p->n_rule) == ALTERNATION) { findindex(p->n_contains); } } i = maxptr - setptr; fprintf(f, "#define LL_SSIZE %d\n#define LL_NSETS %d\n#define LL_NTERMINALS %d\n", nbytes, i > 0 ? i : 1, ntokens); if (onerror) fprintf(f,"#define LL_USERHOOK %s\n", onerror); #ifdef NON_CORRECTING if (non_corr) { fputs("static int nc_done = 0;\n", f); fputs("static int err_seen = 0;\n", f); } #endif /* Now generate the routines that call the startsymbols */ fputs("#if LL_ANSI_C\n", f); for (st = start; st; st = st->ff_next) { p = &nonterms[st->ff_nont]; fputs("void ", f); genextname(st->ff_nont, p->n_name, f); fputs("(void);\n", f); } fputs("#endif\n", f); for (st = start; st; st = st->ff_next) { fprintf(f, "#if LL_ANSI_C\nvoid %s(void)\n#else\n%s()\n#endif\n", st->ff_name, st->ff_name); p = &nonterms[st->ff_nont]; fputs(" {\n\tunsigned int s[LL_NTERMINALS+LL_NSETS+2];", f); #ifdef NON_CORRECTING if (non_corr) { fputs(" \n\tint oldstartsymb;", f); fputs(" \n\tint oldncflag;", f); fputs(" \n\toldstartsymb = LLstartsymb;", f); fputs(" \n\toldncflag = nc_done;", f); fputs(" \n\tnc_done = 0;", f); fprintf(f, "\n\tLLstartsymb = %d;", st->ff_nont + assval); } #endif fputs("\n\tLLnewlevel(s);\n\tLLread();\n", f); if (g_gettype(p->n_rule) == ALTERNATION) { genpush(findindex(p->n_contains)); } genextname(st->ff_nont, p->n_name, f); fputs("();\n", f); if (getntout(p) == NOSCANDONE) { fputs("\tLL_NOSCANDONE(EOFILE);\n",f); } else fputs("\tLL_SCANDONE(EOFILE);\n",f); fputs("\tLLoldlevel(s);\n",f); #ifdef NON_CORRECTING if (non_corr) { fputs("\tLLstartsymb = oldstartsymb;\n", f); fputs("\tif (nc_done == 1) { \n", f); fputs("\t\terr_seen = 1;\n", f); fputs("\tnc_done = oldncflag;\n", f); fputs("\t}\n", f); } #endif fputs("}\n", f); } /* Now generate the sets */ fputs("static char LLsets[] = {\n",f); for (psetl = setptr; psetl < maxptr; psetl++) prset(*psetl); fputs(c_arrend, f); index = (int *) alloc((unsigned) (assval * sizeof(int))); for (q = index; q < &index[assval];) *q++ = -1; for (t = tokens; t < maxt; t++) { index[t->t_tokno] = t - tokens; } fputs("#define LLindex (LL_index+1)\nstatic short LL_index[] = {0,0,\n",f); for (q = index+1; q < &index[assval]; q++) { fprintf(f, "%d,\n", *q); } fputs(c_arrend, f); free((p_mem) index); if (onerror) { fputs("static short LLtok[] = {\n", f); for (t = tokens; t < maxt; t++) { fprintf(f, t->t_tokno<0400 ? "'%s',\n" : "%s,\n",t->t_string); } fputs(c_arrend, f); } fputs("#define LL_NEWMESS\n", f); copyfile(rec_file); doclose(f); install(f_rec, "."); } #ifdef NON_CORRECTING STATIC genncrecovery() { register FILE *f; register p_token t; register int *q; int *index; /* Generate the non-correcting error recovery file */ opentemp((string) 0); f = fpars; genhdr(); correct_prefix(); save_grammar(f); fprintf(f, "#define LLFIRST_NT %d\n", assval); fprintf(f, "#define LLSETSIZE %d\n", nbytes); index = (int *) alloc((unsigned) (assval * sizeof(int))); for (q = index; q < &index[assval];) *q++ = -1; for (t = tokens; t < maxt; t++) { index[t->t_tokno] = t - tokens; } fputs("#define LLindex (LL_index+1)\nstatic short LL_index[] = {0,0,\n",f); for (q = index+1; q < &index[assval]; q++) { fprintf(f, "%d,\n", *q); } fputs(c_arrend, f); free((p_mem) index); copyfile(nc_incl_file); copyfile(nc_rec_file); doclose(f); install(f_nc, "."); } #endif STATIC generate(f) p_file f; { /* * Generates a parsing routine for every nonterminal */ register int s; register p_nont p; int i; register p_first ff; int mustpop; int is_first = 1; fprintf(fpars, "#define LL_LEXI %s\n", lexical); listcount = 0; /* Generate first sets */ for (ff = f->f_firsts; ff; ff = ff->ff_next) { macro(ff->ff_name,&nonterms[ff->ff_nont]); } genhdr(); /* For every nonterminal generate a function */ for (s = f->f_nonterminals; s != -1; s = p->n_next) { p = &nonterms[s]; /* Generate functions in the order in which the nonterminals * were defined in the grammar. This is important, because * of synchronisation with the action file */ while (p->n_count--) getaction(1); if (g_gettype(p->n_rule) == EORULE && getntparams(p) == 0) { continue; } if (is_first) genprototypes(f); is_first = 0; if (p->n_flags & GENSTATIC) fputs("static\n", fpars); fputs("#if LL_ANSI_C\nvoid\n#endif\n", fpars); genextname(s, p->n_name, fpars); if (p->n_flags & PARAMS) { long off = ftell(fact); fputs("(\n#if LL_ANSI_C\n", fpars); controlline(); getansiparams(1); fseek(fact, off, 0); fputs("#else\n", fpars); controlline(); getparams(); fputs("#endif\n{\n", fpars); } else fputs("(\n#if LL_ANSI_C\nvoid\n#endif\n) {\n", fpars); if (p->n_flags & LOCALS) getaction(1); i = getntsafe(p); mustpop = NOPOP; if (g_gettype(p->n_rule) == ALTERNATION && i > SAFESCANDONE) { mustpop = findindex(p->n_contains); if (i == NOSCANDONE) { fputs(c_read, fpars); i = SCANDONE; } } nlabel = 1; rulecode(p->n_rule, i, getntout(p) != NOSCANDONE, mustpop); fputs(c_close, fpars); } } STATIC void prset(p) p_set p; { register int k; register unsigned i; int j; j = nbytes; for (;;) { i = (unsigned) *p++; for (k = 0; k < sizeof(int); k++) { fprintf(fpars,"'\\%o',",(int)(i & 0377)); i >>= 8; if (--j == 0) { fputs("\n",fpars); return; } } } /* NOTREACHED */ } STATIC void macro(s,n) string s; p_nont n; { int i; i = findindex(n->n_first); if (i < 0) { fprintf(fpars, "#define %s(x) ((x) == %d)\n", s, tokens[-(i+1)].t_tokno); return; } firsts = 1; fprintf(fpars,"#define %s(x) LLfirst((x), %d)\n", s, i); } STATIC controlline() { /* Copy a compiler control line */ register int l; register FILE *f1,*f2; f1 = fact; f2 = fpars; l = getc(f1); assert(l == '\0'); do { l = getc(f1); if (l == EOF) fatal(0, "temp file mangled"); putc(l,f2); } while ( l != '\n' ) ; } STATIC getparams() { /* getparams is called if a nonterminal has parameters. The names * of the parameters have to be found, and they should be declared */ long off; register int l; long ftell(); char first; char add_semi = ' '; first = ' '; /* save offset in file to be able to copy the declaration later */ off = ftell(fact); /* First pass through declaration, find the parameter names */ ltext[0] = '\0'; while ((l = gettok()) != ENDDECL) { if ((l == ';' || l == ',') && ltext[0] != '\0') { /* * The last identifier found before a ';' or a ',' * must be a parameter */ fprintf(fpars,"%c%s", first, ltext); first = ','; ltext[0] = '\0'; } } if (ltext[0] != '\0') { fprintf(fpars, "%c%s", first, ltext); add_semi = ';'; } fputs(") ",fpars); /* * Now copy the declarations */ l = getc(fact); /* patch: some implementations of fseek do not work properly after "ungetc" */ fseek(fact,off,0); getaction(0); fprintf(fpars, "%c\n",add_semi); } STATIC genprototypes(f) register p_file f; { /* * Generate prototypes for all nonterminals */ register int i; register p_nont p; long off = ftell(fact); fputs("#if LL_ANSI_C\n", fpars); for (i = 0; i < nnonterms; i++) { if (! IN(f->f_used, i)) continue; p = &nonterms[i]; if (g_gettype(p->n_rule) == EORULE && getntparams(p) == 0) { continue; } if (p->n_flags & GENSTATIC) fputs("static ", fpars); fputs("void ", fpars); genextname(i, p->n_name, fpars); if (p->n_flags & PARAMS) { fputs("(\n", fpars); fseek(fact, p->n_off, 0); controlline(); getansiparams(0); } else fputs("(void);\n", fpars); } fseek(fact, off, 0); fputs("#else\n", fpars); for (i = 0; i < nnonterms; i++) { if (! IN(f->f_used, i)) continue; p = &nonterms[i]; if (!(p->n_flags & GENSTATIC)) continue; if (g_gettype(p->n_rule) == EORULE && getntparams(p) == 0) { continue; } fputs("static ", fpars); genextname(i, p->n_name, fpars); fputs("();\n", fpars); } fputs("#endif\n", fpars); } STATIC getansiparams(mkdef) { /* getansiparams is called if a nonterminal has parameters * and an ANSI C function definition/declaration has to be produced. * If a definition has to be produced, "mkdef" is set to 1. */ register int l; int delayed = 0; ltext[0] = '\0'; while ((l = gettok()) != ENDDECL) { if (l > 0177 || c_class[l] != ISSPA) { if (delayed) { fputc(',', fpars); delayed = 0; } } if ((l == ';' || l == ',') && ltext[0] != '\0') { /* * The last identifier found before a ';' or a ',' * must be a parameter */ delayed = 1; ltext[0] = '\0'; } else if (l == IDENT) fprintf(fpars, "%s", ltext); else fputc(l, fpars); } fprintf(fpars, ") %c\n", mkdef ? ' ' : ';'); } STATIC gettok() { /* Read from the action file. */ register int ch; register string c; register FILE *f; f = fact; ch = getc(f); switch(ch) { case '\n': ch = getc(f); if (ch != EOF) { ungetc(ch,f); if (ch != '\0') return '\n'; } return ENDDECL; case '\0': ungetc(ch,f); /* Fall through */ case EOF : return ENDDECL; default : if (c_class[ch] == ISLET) { c = ltext; do { *c++ = ch; if (c-ltext >= LTEXTSZ) --c; ch = getc(f); } while (c_class[ch] == ISLET || c_class[ch] == ISDIG); if (ch != EOF) ungetc(ch,f); *c = '\0'; return IDENT; } return ch; } } STATIC void rulecode(p,safety,mustscan,mustpop) register p_gram p; { /* * Code for a production rule. */ register int toplevel = 1; register FILE *f; register int *ppu; int *pushlist; int *ppushlist; /* * First generate code to push the contains sets of this rule * on a stack */ ppushlist = dopush(p,safety,1, &pushlist); if (mustpop != NOPOP) for (ppu = pushlist; ppu < ppushlist; ppu++) { if (*ppu == mustpop) { *ppu = mustpop = NOPOP; break; } } if (g_gettype(p) != ALTERNATION) { genpop(mustpop); mustpop = NOPOP; } for (ppu = pushlist; ppu < ppushlist; ppu++) genpush(*ppu); free((p_mem) pushlist); f = fpars; for (;;) { switch (g_gettype(p)) { case EORULE : if (mustscan && safety == NOSCANDONE) { fputs(c_read,f); } return; case LITERAL : case TERMINAL : { register p_token t; string s; t = &tokens[g_getcont(p)]; if (toplevel == 0) { fprintf(f,"LLtdecr(%d);\n", g_getcont(p)); } if (safety == SAFE) { fputs("LL_SAFE(",f); } else if (safety == SAFESCANDONE) { fputs("LL_SSCANDONE(",f); } else if (safety == SCANDONE) { fputs("LL_SCANDONE(",f); } else /* if (safety == NOSCANDONE) */ { fputs("LL_NOSCANDONE(", f); } if (t->t_tokno < 0400) s = "'%s');\n"; else s = "%s);\n"; fprintf(f,s,t->t_string); if (safety <= SAFESCANDONE && toplevel > 0) { safety = NOSCANDONE; toplevel = -1; p++; continue; } safety = NOSCANDONE; break; } case NONTERM : { register p_nont n = &nonterms[g_getcont(p)]; if (safety == NOSCANDONE && getntsafe(n) < NOSCANDONE) { safety = getntsafe(n); fputs(c_read, f); } if (toplevel == 0 && (g_gettype(n->n_rule) != ALTERNATION || getntsafe(n) <= SAFESCANDONE)) { genpop(findindex(n->n_contains)); } genextname(g_getcont(p), n->n_name, f); if (g_getnpar(p)) { fputs("(\n", f); controlline(); getaction(0); fputs(");\n",f); } else fputs("();\n", f); safety = getntout(n); break; } case TERM : safety = codeforterm(g_getterm(p), safety, toplevel); break; case ACTION : getaction(1); p++; continue; case ALTERNATION : alternation(p, safety, mustscan, mustpop, 0); return; } p++; toplevel = 0; } } STATIC void alternation(pp, safety, mustscan, mustpop, lb) p_gram pp; { register p_gram p = pp; register FILE *f = fpars; register p_link l; int hulp, hulp1,hulp2; int haddefault = 0; int nsafe; p_set set; p_set setalloc(); int *tokenlist = mk_tokenlist(); int casecnt = 0; int compacted; int unsafe; assert(safety < NOSCANDONE); hulp = nlabel++; hulp1 = nlabel++; hulp2 = nlabel++; if (!lb) lb = hulp1; unsafe = onerror || safety > SAFESCANDONE; if (!unsafe) { genpop(mustpop); mustpop = NOPOP; } if (unsafe && hulp1 == lb) { fprintf(f,"goto L_%d; /* so that the label is used for certain */\nL_%d: ;\n", hulp1, hulp1); } if (safety == SAFE) { /* check if we can avoid to generate the switch */ for (;;) { if (g_gettype(p) == EORULE) return; l = g_getlink(p); if (l->l_flag & COND) break; if ((g_gettype(l->l_rule) != TERMINAL && g_gettype(l->l_rule) != LITERAL) || g_gettype(l->l_rule+1) != EORULE) break; p++; } p = pp; } while (g_gettype(p) != EORULE) { set = 0; l = g_getlink(p); if (l->l_flag & COND) { if (!(l->l_flag & NOCONF)) { set = setalloc(); setunion(set, l->l_others); setintersect(set, l->l_symbs); setminus(l->l_symbs, set); setminus(l->l_others, set); add_cases(set, tokenlist, casecnt++); } } if (!unsafe && (l->l_flag & DEF)) { haddefault = 1; } else add_cases(l->l_symbs, tokenlist, casecnt++); if (l->l_flag & DEF) { haddefault = 1; } if ((l->l_flag & COND) && !(l->l_flag & NOCONF)) { p++; if (g_gettype(p+1) == EORULE) { setminus(g_getlink(p)->l_symbs, set); free((p_mem) set); continue; } free((p_mem) set); if (!haddefault) { } else { add_cases(l->l_others, tokenlist, casecnt++); unsafe = 0; } break; } p++; } unsafe = onerror || safety > SAFESCANDONE; p = pp; haddefault = 0; compacted = analyze_switch(tokenlist); if (compacted) { fputs("{", f); out_list(tokenlist, listcount++, casecnt); } else fputs("switch(LLcsymb) {\n", f); casecnt = 0; while (g_gettype(p) != EORULE) { l = g_getlink(p); if (l->l_flag & COND) { if (l->l_flag & NOCONF) { fputs("#ifdef ___NOCONFLICT___\n", f); } else gencases(tokenlist, casecnt++, compacted); controlline(); fputs("if (!",f); getaction(0); fprintf(f,") goto L_%d;\n", hulp); if (l->l_flag & NOCONF) { fputs("#endif\n", f); } } if (!unsafe && (l->l_flag & DEF)) { haddefault = 1; fputs("default:\n", f); } else gencases(tokenlist, casecnt++, compacted); nsafe = SAFE; if (l->l_flag & DEF) { if (unsafe) { fprintf(f,"goto L_%d;\nL_%d: ;\n", hulp2, hulp2); } if (safety != SAFE) nsafe = SAFESCANDONE; } rulecode(l->l_rule, nsafe, mustscan, mustpop); fputs(c_break,f); if (unsafe && (l->l_flag & DEF)) { haddefault = 1; fprintf(f, "default: if (LLskip()) goto L_%d;\ngoto L_%d;\n", lb, hulp2); } if ((l->l_flag & COND) && !(l->l_flag & NOCONF)) { p++; fprintf(f,"goto L_%d;\nL_%d : ;\n", hulp, hulp); if (g_gettype(p+1) == EORULE) { continue; } if (!haddefault) { fputs("default:\n", f); } else { gencases(tokenlist, casecnt++, compacted); safety = SAFE; unsafe = 0; } if (! unsafe) { genpop(mustpop); mustpop = NOPOP; } alternation(p,safety,mustscan,mustpop,lb); break; } p++; } if (compacted) fputs(c_close, f); fputs(c_close, f); free((p_mem) tokenlist); } STATIC int * dopush(p,safety,toplevel,pp) register p_gram p; int **pp; { /* * The safety only matters if toplevel != 0 */ unsigned int i = 100; register int *ip = (int *) alloc(100 * sizeof(int)); *pp = ip; for (;;) { if (ip - *pp >= i) { *pp = (int *) ralloc((p_mem) (*pp), (i + 100) * sizeof(int)); ip = *pp + i; i += 100; } switch(g_gettype(p)) { case EORULE : case ALTERNATION : return ip; case TERM : { register p_term q; int rep_kind, rep_count; q = g_getterm(p); rep_kind = r_getkind(q); rep_count = r_getnum(q); if (!(toplevel > 0 && safety <= SAFESCANDONE && (rep_kind == OPT || (rep_kind == FIXED && rep_count == 0)))) { *ip++ = findindex(q->t_contains); } break; } case LITERAL : case TERMINAL : if (toplevel > 0 && safety <= SAFESCANDONE) { toplevel = -1; p++; safety = NOSCANDONE; continue; } if (toplevel == 0) *ip++ = -(g_getcont(p)+1); break; case NONTERM : { register p_nont n; n = &nonterms[g_getcont(p)]; if (toplevel == 0 || (g_gettype(n->n_rule) == ALTERNATION && getntsafe(n) > SAFESCANDONE)) { *ip++ = findindex(n->n_contains); } break; } case ACTION : p++; continue; } toplevel = 0; safety = NOSCANDONE; p++; } } # define max(a,b) ((a) < (b) ? (b) : (a)) STATIC void getaction(flag) { /* Read an action from the action file. * flag = 1 if it is an action, * 0 when reading parameters */ register int ch; register FILE *f; int mark = 0; if (flag == 1) { controlline(); } f = fpars; for (;;) { ch = gettok(); switch(ch) { case ENDDECL: if (flag != 2) break; ch = getc(fact); assert(ch == '\0'); fputs("\n",f); if (mark) return; mark = 1; continue; case IDENT : fputs(ltext,f); continue; } mark = 0; if (ch == ENDDECL) break; putc(ch,f); } if (flag) fputs("\n",f); } STATIC codeforterm(q,safety,toplevel) register p_term q; { /* * Generate code for a term */ register FILE *f = fpars; register int rep_count = r_getnum(q); register int rep_kind = r_getkind(q); int term_is_persistent = (q->t_flags & PERSISTENT); int ispushed = NOPOP; if (!(toplevel > 0 && (safety == 0 || (!onerror && safety <= SAFESCANDONE)) && (rep_kind == OPT || (rep_kind == FIXED && rep_count == 0)))) { ispushed = findindex(q->t_contains); } if (safety == NOSCANDONE && (rep_kind != FIXED || rep_count == 0 || gettout(q) != NOSCANDONE)) { fputs(c_read, f); safety = SCANDONE; } if (rep_kind == PLUS && !term_is_persistent) { int temp; temp = findindex(q->t_first); if (temp != ispushed) { genpop(ispushed); ispushed = temp; genpush(temp); } } if (rep_count) { /* N > 0, so generate fixed forloop */ fputs("{\nregister LL_i;\n", f); assert(ispushed != NOPOP); fprintf(f, "for (LL_i = %d; LL_i >= 0; LL_i--) {\n", rep_count - 1); if (rep_kind == FIXED) { fputs("if (!LL_i) ", f); genpop(ispushed); genpush(ispushed); if (safety == NOSCANDONE) { assert(gettout(q) == NOSCANDONE); fputs(c_read, f); } } } else if (rep_kind != OPT && rep_kind != FIXED) { /* '+' or '*', so generate infinite loop */ fputs("for (;;) {\n",f); } else if (rep_kind == OPT && (safety == 0 || (!onerror && safety <= SAFESCANDONE))) { genpop(ispushed); ispushed = NOPOP; } if (rep_kind == STAR || rep_kind == OPT) { genswhead(q, rep_kind, rep_count, safety, ispushed); } rulecode(q->t_rule, t_safety(rep_kind,rep_count,term_is_persistent,safety), gettout(q) != NOSCANDONE, rep_kind == FIXED ? ispushed : NOPOP); if (gettout(q) == NOSCANDONE && rep_kind != FIXED) { fputs(c_read, f); } /* in the case of '+', the if is after the code for the rule */ if (rep_kind == PLUS) { if (rep_count) { fputs("if (!LL_i) break;\n", f); } genswhead(q, rep_kind, rep_count, safety, ispushed); } if (rep_kind != OPT && rep_kind != FIXED) fputs("continue;\n", f); if (rep_kind != FIXED) { fputs(c_close, f); /* Close switch */ fputs(c_close, f); if (rep_kind != OPT) { genpop(ispushed); fputs(c_break, f); } } if (rep_kind != OPT && (rep_kind != FIXED || rep_count > 0)) { fputs(c_close, f); /* Close for */ if (rep_count > 0) { fputs(c_close, f);/* Close Register ... */ } } return t_after(rep_kind, rep_count, gettout(q)); } STATIC genswhead(q, rep_kind, rep_count, safety, ispushed) register p_term q; { /* * Generate switch statement for term q */ register FILE *f = fpars; p_set p1; p_set setalloc(); int hulp1 = 0, hulp2; int safeterm; int termissafe = 0; int casecnt = 0; int *tokenlist = mk_tokenlist(); int compacted; if (rep_kind == PLUS) safeterm = gettout(q); else if (rep_kind == OPT) safeterm = safety; else /* if (rep_kind == STAR) */ safeterm = max(safety, gettout(q)); hulp2 = nlabel++; fprintf(f, "goto L_%d;\nL_%d : ", hulp2, hulp2); if (q->t_flags & RESOLVER) { hulp1 = nlabel++; if (! (q->t_flags & NOCONF)) { p1 = setalloc(); setunion(p1,q->t_first); setintersect(p1,q->t_follow); /* * p1 now points to a set containing the conflicting * symbols */ setminus(q->t_first, p1); setminus(q->t_follow, p1); setminus(q->t_contains, p1); add_cases(p1, tokenlist, casecnt++); free((p_mem) p1); } } if (safeterm == 0 || (!onerror && safeterm <= SAFESCANDONE)) { termissafe = 1; } else add_cases(q->t_follow, tokenlist, casecnt++); if (!onerror && (q->t_flags & PERSISTENT) && safeterm != SAFE) { add_cases(q->t_contains, tokenlist, casecnt); } else add_cases(q->t_first, tokenlist, casecnt); compacted = analyze_switch(tokenlist); fputs("{", f); if (compacted) { out_list(tokenlist, listcount++, casecnt); } else fputs("switch(LLcsymb) {\n", f); casecnt = 0; if (q->t_flags & RESOLVER) { if (q->t_flags & NOCONF) { fputs("#ifdef ___NOCONFLICT___\n", f); } else gencases(tokenlist, casecnt++, compacted); controlline(); fputs("if (", f); getaction(0); fprintf(f, ") goto L_%d;\n", hulp1); if (q->t_flags & NOCONF) { fputs("#endif /* ___NOCONFLICT___ */\n", f); } } if (safeterm == 0 || (!onerror && safeterm <= SAFESCANDONE)) { fputs("default:\n", f); } else gencases(tokenlist, casecnt++, compacted); if (rep_kind == OPT) genpop(ispushed); fputs(c_break, f); if (! termissafe) { int i; static int nvar; assert(ispushed != NOPOP); if (ispushed >= 0) i = -ispushed; else i = tokens[-(ispushed+1)].t_tokno; ++nvar; fprintf(f,"default:{int LL_%d=LLnext(%d);\n;if (!LL_%d) {\n", nvar, i, nvar); if (rep_kind == OPT) genpop(ispushed); fputs(c_break, f); fputs(c_close, f); fprintf(f,"else if (LL_%d & 1) goto L_%d;}\n",nvar, hulp2); } gencases(tokenlist, casecnt, compacted); if (q->t_flags & RESOLVER) { fprintf(f, "goto L_%d;\nL_%d : ;\n", hulp1, hulp1); } if (rep_kind == OPT) genpop(ispushed); if (rep_count > 0) { assert(ispushed != NOPOP); fputs(rep_kind == STAR ? "if (!LL_i) " : "if (LL_i == 1) ", f); genpop(ispushed); } free((p_mem) tokenlist); } STATIC gencases(tokenlist, caseno, compacted) int *tokenlist; { /* * setp points to a bitset indicating which cases must * be generated. * YECH, the PCC compiler does not accept many cases without * statements in between, so after every case label an empty * statement is generated. * The C-grammar used by PCC is really stupid on this point : * it contains the rule * statement : label statement * which is right-recursive, and as is well known, LALR parsers don't * handle these things very well. * The grammar should have been written : * labeledstatement : labels statement ; * labels : labels label | ; */ register p_token p; register int i; if (compacted) fprintf(fpars, "case %d :\n", caseno); for (i = 0, p = tokens; i < ntokens; i++, p++) { if (tokenlist[i] == caseno) { fprintf(fpars, compacted ? (p->t_tokno < 0400 ? "/* case '%s' */\n" : "/* case %s */\n") : p->t_tokno<0400 ? "case /* '%s' */ %d : ;\n" : "case /* %s */ %d : ;\n", p->t_string, i); } } } static char namebuf[20]; STATIC string genname(s) string s; { /* * Generate a target file name from the * source file name s. */ register string c,d; c = namebuf; while (*s) { if (*s == '/') { while (*s == '/') s++; if (*s) c = namebuf; else break; } *c++ = *s++; } for (d = c; --d > namebuf;) if (*d == '.') break; if (d == namebuf) d = c; if (d >= &namebuf[12]) { fatal(0,"%s : filename too long",namebuf); } *d++ = '.'; *d++ = 'c'; *d = '\0'; return namebuf; } STATIC genpush(d) { genincrdecr("incr", d); } STATIC void genincrdecr(s, d) string s; { if (d == NOPOP) return; if (d >= 0) { fprintf(fpars, "LLs%s(%d);\n", s, d / nbytes); return; } fprintf(fpars, "LLt%s(%d);\n", s, -(d + 1)); } STATIC genpop(d) { genincrdecr("decr", d); } STATIC int analyze_switch(tokenlist) int *tokenlist; { register int i; int ncases = 0; int percentage; int maxcase = 0, mincase = 0; if (! jmptable_option) return 0; for (i = 0; i < ntokens; i++) { if (tokenlist[i] >= 0) { ncases++; if (! mincase) mincase = i + 1; maxcase = i + 1; } } if (ncases < min_cases_for_jmptable) return 0; percentage = ncases * 100 / (maxcase - mincase); fprintf(fpars, "/* percentage is %d */\n", percentage); return percentage >= low_percentage && percentage <= high_percentage; } STATIC add_cases(s, tokenlist, caseno) p_set s; int *tokenlist; { register int i; for (i = 0; i < ntokens; i++) { if (IN(s, i)) { tokenlist[i] = caseno; } } } STATIC out_list(tokenlist, listno, casecnt) int *tokenlist; { register int i; register FILE *f = fpars; fprintf(f, "static %s LL%d_tklist[] = {", casecnt <= 127 ? "char" : "short", listno); for (i = 0; i < ntokens; i++) { fprintf(f, "%c%d,", i % 10 == 0 ? '\n': ' ', tokenlist[i]); } fputs(c_arrend, f); fprintf(f, "switch(LL%d_tklist[LLcsymb]) {\n", listno); } STATIC genextname(d, s, f) char *s; FILE *f; { fprintf(f, "%s%d_%s", prefix ? prefix : "LL", d, s); } STATIC correct_prefix() { register FILE *f = fpars; register char *s = prefix; if (s) { fprintf(f, "#define LLsymb %ssymb\n", s); fprintf(f, "#define LLerror %serror\n", s); fprintf(f, "#define LLsafeerror %ssafeerror\n", s); fprintf(f, "#ifndef LL_FASTER\n#define LLscan %sscan\n#endif\n", s); fprintf(f, "#define LLscnt %sscnt\n", s); fprintf(f, "#define LLtcnt %stcnt\n", s); fprintf(f, "#define LLcsymb %scsymb\n", s); fprintf(f, "#define LLread %sread\n", s); fprintf(f, "#define LLskip %sskip\n", s); fprintf(f, "#define LLnext %snext\n", s); fprintf(f, "#define LLfirst %sfirst\n", s); fprintf(f, "#define LLnewlevel %snewlevel\n", s); fprintf(f, "#define LLoldlevel %soldlevel\n", s); fprintf(f, "#define LLmessage %smessage\n", s); #ifdef NON_CORRECTING fprintf(f, "#define LLnc_recovery %sncrecovery\n", s); fprintf(f, "#define LLstartsymb %sstartsymb\n", s); #endif } fprintf(f, "#include \"%s\"\n", f_include); }