Use C89 size_t for sizes from sizeof() or to malloc() or realloc().
Remove obsolete (unsigned) casts. Sizes were unsigned int in
traditional C but are size_t in C89.
Silence some clang warnings. Add the second pair of round brackets in
`while ((ff = ff->ff_next))` to silence -Wparentheses. Change
`if (nc_first(...))/*nothing*/;` to `(void)nc_first(...);` to silence
-Wempty-body. The code in compute.c nc_first() had the form
`if (x) if (y) s; else t;`. The old indentation (before 10717cc
)
suggests that the "else" belongs to the 2nd "if", so add braces like
`if (x) { if (y) s; else t; }` to silence -Wdangling-else.
Shuffle extern function declarations. Add missing declaration for
LLparse(). Stop declaring RENAME(); it doesn't exist. Move some
declarations from main.c to extern.h, so the C compiler may check that
the declarations are compatible with the function definitions.
Assume that standard C89 remove() is available and doesn't need the
UNLINK() wrapper.
In lib/incl, don't need to include <stdio.h> nor <stdlib.h> to use
assert().
Remove alloc.h. If you don't clean your build, then an outdated
BUILDDIR/obj/util/LLgen/headers/alloc.h will survive but should not
cause harm, because nothing includes it. Don't need to remove alloc.h
from util/LLgen/distr.sh, because it isn't there.
Run the bootstrap to rebuild LLgen.c, Lpars.c, tokens.c.
1517 lines
30 KiB
C
1517 lines
30 KiB
C
/* 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);
|
|
}
|