/* 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 <stdlib.h> #include <stdio.h> # 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: */ STATIC void doclose(FILE *); STATIC int *mk_tokenlist(void); STATIC void genhdr(void); STATIC void opentemp(string); STATIC void geninclude(void); STATIC void genrecovery(void); #ifdef NON_CORRECTING STATIC void genncrecovery(void); #endif STATIC void generate(p_file); STATIC void prset(p_set); STATIC void macro(string, p_nont); STATIC void controlline(void); STATIC void getparams(void); STATIC void genprototypes(register p_file); STATIC void getansiparams(int); STATIC int gettok(void); STATIC void rulecode(register p_gram, int, int, int); STATIC void alternation(p_gram, int, int, int, int); STATIC int *dopush(register p_gram, int, int, int **); STATIC void getaction(int); STATIC int codeforterm(register p_term, int, int); STATIC void genswhead(register p_term, int, int, int, int); STATIC void gencases(int *, int, int); STATIC string genname(string); STATIC void genpush(int); STATIC void genincrdecr(string, int); STATIC void genpop(int); STATIC int analyze_switch(int *); STATIC void add_cases(p_set, int *, int); STATIC void out_list(int *, int, int); STATIC void genextname(int, char *, FILE *); STATIC void correct_prefix(void); extern void save_grammar(FILE *f); # define NOPOP -20000 STATIC void doclose(FILE *f) { if (ferror(f) != 0) { fatal(0, "Write error on temporary", NULL ); } fclose(f); } STATIC int *mk_tokenlist(void) { register int i = ntokens; register int *p = (int *) alloc(i * sizeof(int)) + i; while (i--) *--p = -1; return p; } STATIC void genhdr(void) { if (!firsts) fputs("#define LLNOFIRSTS\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); } void gencode(int 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 void opentemp(string str) { if ((fpars = fopen(f_pars, "w")) == NULL ) { fatal(0, e_noopen, f_pars); } if (!str) str = "."; fprintf(fpars, LLgenid, str); } STATIC void geninclude(void) { 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 void genrecovery(void) { 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 */ 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); } for (st = start; st; st = st->ff_next) { fprintf(f, "void %s(void)\n", 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(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 void genncrecovery(void) { 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(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 void generate(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("void\n", fpars); genextname(s, p->n_name, fpars); if (p->n_flags & PARAMS) { fputs("(\n", fpars); /* long off = ftell(fact);*/ controlline(); getansiparams(1); /* fseek(fact, off, 0);*/ fputs("{\n", fpars); } else fputs("(\nvoid\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_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(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 void controlline(void) { /* 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", NULL ); putc(l, f2); } while (l != '\n'); } STATIC void getparams(void) { /* 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 void genprototypes(register p_file f) { /* * Generate prototypes for all nonterminals */ register int i; register p_nont p; long off = ftell(fact); 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); } /* 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. */STATIC void getansiparams(int mkdef) { 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 int gettok(void) { /* 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(register p_gram p, int safety, int mustscan, int mustpop) { /* * 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(p_gram pp, int safety, int mustscan, int mustpop, int lb) { 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(register p_gram p, int safety, int toplevel, int **pp) { /* * The safety only matters if toplevel != 0 */ size_t i = 100; register int *ip = (int *) alloc(100 * sizeof(int)); *pp = ip; for (;;) { if ((size_t)(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)) /* Read an action from the action file. * flag = 1 if it is an action, * 0 when reading parameters */STATIC void getaction(int flag) { 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); } /* * Generate code for a term */STATIC int codeforterm(register p_term q, int safety, int toplevel) { 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 int 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 void genswhead(register p_term q, int rep_kind, int rep_count, int safety, int ispushed) { /* * 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 void gencases(int *tokenlist, int caseno, int compacted) { /* * 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]; /* * Generate a target file name from the * source file name s. */STATIC string genname(string 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 void genpush(int d) { genincrdecr("incr", d); } STATIC void genincrdecr(string s, int d) { 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 void genpop(int d) { genincrdecr("decr", d); } STATIC int analyze_switch(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 void add_cases(p_set s, int *tokenlist, int caseno) { register int i; for (i = 0; i < ntokens; i++) { if (IN(s, i)) { tokenlist[i] = caseno; } } } STATIC void out_list(int *tokenlist, int listno, int casecnt) { 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 void genextname(int d, char *s, FILE *f) { fprintf(f, "%s%d_%s", prefix ? prefix : "LL", d, s); } STATIC void correct_prefix(void) { 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); }