Added non-correcting error recovery stuff

This commit is contained in:
ceriel 1997-02-21 11:27:57 +00:00
parent a44875cf00
commit c2607fdf0f
21 changed files with 3178 additions and 262 deletions

View file

@ -21,6 +21,10 @@ extern unsigned int LLscnt[];
extern unsigned int LLtcnt[];
extern int LLcsymb;
#if LL_NON_CORR
extern int LLstartsymb;
#endif
#define LLsdecr(d) {LL_assert(LLscnt[d] > 0); LLscnt[d]--;}
#define LLtdecr(d) {LL_assert(LLtcnt[d] > 0); LLtcnt[d]--;}
#define LLsincr(d) LLscnt[d]++

70
util/LLgen/lib/nc_incl Normal file
View file

@ -0,0 +1,70 @@
#define LLALT 9999 /* Alternative is following */
#define LLTERMINAL 1 /* Symbol is a terminal */
#define LLNONTERMINAL 2 /* Symbol is a nonterminal */
#define LLEORULE 0 /* No more alternatives */
struct lhs { /* LHS of a rule */
int nr; /* Nr of the nonterminal */
struct symbol *rhs; /* Pointer to RHS */
char first[LLSETSIZE]; /* First set */
char follow[LLSETSIZE]; /* Follow set */
char empty; /* Set if nonterminal produces empty */
};
struct symbol { /* Symbol in the RHS of a rule */
int x; /* LLTERMINAL or LLNONTERMINAL */
int nr; /* Nr of the symbol */
struct symbol *link; /* Ptr to next rule with this symbol */
struct symbol *next; /* Ptr to next symbol in this rule */
struct lhs *lhs; /* Ptr to LHS */
};
struct terminal { /* Array with links to terminals in a */
struct symbol *link; /* rule */
};
struct nonterminal { /* Array with links to nt's in a rule */
struct symbol *link; /* and pointer to LHS's */
struct lhs *rule;
};
struct stack_elt { /* Stack element */
int flags; /* Some flags */
int nr; /* Nr of symbol */
int ref_count; /* Nr of predecessors */
int hyp_ref_count; /* Temporary nr of predecessors */
int matched; /* Nr of LHS trying to match */
int nr_nexts; /* Nr of successors */
struct edge *edges; /* Array of edges to other stack elt's*/
};
/* Possible flags in a stack element */
#define LLHEAD 1 /* Stack element is a head */
#define LLDUMMY 2 /* Stack element is substituted */
#define LLGEN_SEARCH 8 /* Set by 'generate_heads()' */
struct edge { /* Edges of a stack element */
char flags; /* Some flags */
struct stack_elt *ptr; /* Array with pointers to stack elt's */
};
/* Possible flags in an edge */
#define LLLOOP 1 /* Belongs to a loop */
#define LLLOOP_SEARCH 2 /* Used by 'loop()' */
#define LLHYP_SEARCH 4 /* Used by 'hyp_run()' */
#define PRINT_SEARCH 8 /* DEBUG */
#define LLMARK_SEARCH 16 /* Used by 'mark_loop()' */
#define LLYES 32
#define LLNO 64
#define LLEOSTACK -1 /* Indicates last element of a stack */
#define LLHEADS_BUF_INCR 10 /* Nr of elements the buffer will be */
#define LLCLEANUP_BUF_INCR 25 /* increased by */
#define LL_VIS_INCR 200
/* Macro's to manipulate bit sets */
#define LLIN(a, i) ((a)[(i)/8] & (1 << ((i) % 8)))
#define LLPUTIN(a, i) ((a)[(i)/8] |= (1 << ((i) % 8)))

1791
util/LLgen/lib/nc_rec Normal file

File diff suppressed because it is too large Load diff

View file

@ -12,6 +12,11 @@ unsigned int LLscnt[LL_NSETS];
int LLcsymb, LLsymb;
static int LLlevel;
#if LL_NON_CORR
int LLstartsymb;
static int fake_eof = 0;
#endif
#if LL_ANSI_C
#define LL_VOIDCST (void)
void LLmessage(int);
@ -39,7 +44,38 @@ LLscan(t)
/*
* Check if the next symbol is equal to the parameter
*/
#if LL_NON_CORR
/* See if the error recovery has eaten an eof */
if (fake_eof) {
LLsymb = EOFILE;
fake_eof = 0;
}
else {
LLsymb = LL_LEXI();
}
if (LLsymb == t) {
#else
if ((LLsymb = LL_LEXI()) == t) {
#endif
#if LL_NON_CORR
/* Check if a previous parser has 'crashed', in that
* case continue with non-correcting parser
*/
if (err_seen && !nc_done) {
LLnc_recover();
nc_done = 1;
/* Remember that the error recovery has eaten an eof */
fake_eof = 1;
if (t != LLsymb) {
LLerror(t);
}
else
return;
}
#endif
return;
}
/*
@ -54,6 +90,31 @@ void LLread(void) {
#else
LLread() {
#endif
#if LL_NON_CORR
/* Again, check if another parser has crashed,
* in that case intercept and go to the
* non-correcting parser
*/
if (err_seen && !nc_done) {
LLnc_recover();
nc_done = 1;
/* Pretend we read end of file */
LLsymb = EOFILE;
LLcsymb = LLindex[EOFILE];
fake_eof = 0;
return;
}
if (fake_eof) {
LLsymb = EOFILE;
LLcsymb = LLindex[EOFILE];
fake_eof = 0;
return;
}
#endif
for (;;) {
if ((LLcsymb = LLindex[(LLsymb = LL_LEXI())]) >= 0) return;
LLmessage(0);
@ -85,6 +146,16 @@ LLerror(t)
return;
}
#endif
#if LL_NON_CORR
if ((!nc_done) && (LLsymb > 0) && (LLsymb != EOFILE)) {
LLmessage(0);
LLnc_recover();
nc_done = 1;
LLsymb = EOFILE;
}
#endif
if ((LLcsymb = LLindex[LLsymb]) < 0) {
LLmessage(0);
LLread();
@ -97,7 +168,25 @@ LLerror(t)
LL_VOIDCST LLskip();
#endif
LLtcnt[i]--;
if (LLsymb != t) LLmessage(t);
if (LLsymb != t) {
#if LL_NON_CORR
/* A little kludge here; when using non-correcting recovery
* it can happen that a program is correct but incomplete.
* Here, we test this, and make sure the appropriate
* message is generated
*/
if (! nc_done) {
int oldLLsymb;
oldLLsymb = LLsymb;
LLsymb = EOFILE;
LLmessage(0);
nc_done = 1;
/* Not really, but to prevent more than 1 error message */
LLsymb = oldLLsymb;
}
#endif
LLmessage(t);
}
}
#if LL_ANSI_C
@ -121,6 +210,14 @@ LLsafeerror(t)
}
return;
}
#endif
#if LL_NON_CORR
if ((!nc_done) && (LLsymb > 0) && (LLsymb != EOFILE)) {
LLmessage(0);
LLnc_recover();
nc_done = 1;
LLsymb = EOFILE;
}
#endif
LLmessage(t);
}
@ -265,7 +362,19 @@ static int LLdoskip(e)
continue;
}
#endif /* LL_USERHOOK */
#if LL_NON_CORR
if ((!nc_done) && (LLsymb > 0)) {
LLmessage(0);
LLnc_recover();
nc_done = 1;
fake_eof = 1;
}
else {
LLmessage(0);
}
#else
LLmessage(0);
#endif
retval = 1;
LLread();
}

View file

@ -32,9 +32,7 @@ string store();
p_gram search();
long ftell();
static int nparams; /* parameter count for nonterminals */
static int acount; /* count #of global actions */
static int order;
static p_term t_list;
static int t_cnt;
static p_gram alt_table;
@ -48,9 +46,8 @@ static int max_rules;
#define RULEINCR 32
/* Here are defined : */
STATIC newnorder();
STATIC newtorder();
STATIC copyact();
STATIC newnorder();
STATIC newtorder();
STATIC mkalt();
STATIC mkterm();
STATIC p_gram copyrule();
@ -169,13 +166,20 @@ def { register string p; }
}
';'
| C_ONERROR C_IDENT
{ if (! onerror) {
{
#ifdef NON_CORRECTING
if (non_corr) {
warning(linecount, "%%onerror conflicts with -n option");
}
else
#endif
if (! onerror) {
onerror = store(lextoken.t_string);
}
else error(linecount,"Duplicate %%onerror");
}
';'
| action(0) { acount++; }
| C_ACTION { acount++; }
/*
* A global C-declaration
*/
@ -216,18 +220,20 @@ rule { register p_nont p;
p->n_lineno = linecount;
p->n_off = ftell(fact);
}
[ params(1) { if (nparams > 0) {
[ C_PARAMS { if (lextoken.t_num > 0) {
p->n_flags |= PARAMS;
if (nparams > 15) {
if (lextoken.t_num > 15) {
error(linecount,"Too many parameters");
}
else setntparams(p,nparams);
else setntparams(p,lextoken.t_num);
}
}
]?
[ action(0) { p->n_flags |= LOCALS; }
[ C_ACTION { p->n_flags |= LOCALS; }
]?
':' productions(&rr) ';'
':' { in_production = 1; }
productions(&rr) ';'
{ in_production = 0; }
/*
* Do not use p->n_rule now! The nonterms array
* might have been re-allocated.
@ -235,15 +241,6 @@ rule { register p_nont p;
{ nonterms[g_getcont(temp)].n_rule = rr;}
;
action(int n;)
/*
* The parameter n is non-zero when the opening and closing
* bracket must be copied along with the action
*/
: '{' { copyact('{','}',n,0); }
'}'
;
productions(p_gram *p;)
/*
* One or more alternatives
@ -280,7 +277,7 @@ productions(p_gram *p;)
t = 0;
*p = prod;
}
]+ { if (conflres & ~DEF) {
]+ { if (conflres & (COND|PREFERING|AVOIDING)) {
error(n_lc,
"Resolver on last alternative not allowed");
}
@ -290,7 +287,7 @@ productions(p_gram *p;)
*p = copyrule(&alt_table[n_alts-altcnt],altcnt+1);
}
|
{ if (conflres & ~DEF) {
{ if (conflres & (COND|PREFERING|AVOIDING)) {
error(o_lc,
"No alternation conflict resolver allowed here");
}
@ -336,16 +333,32 @@ simpleproduction(p_gram *p; register int *conflres;)
int cnt, kind;
int termdeleted = 0;
} :
[ C_DEFAULT { *conflres = DEF; }
[ C_DEFAULT { *conflres |= DEF; }
]?
[
/*
* Optional conflict reslover
*/
C_IF expr { *conflres |= COND; }
C_IF C_EXPR { *conflres |= COND; }
| C_PREFER { *conflres |= PREFERING; }
| C_AVOID { *conflres |= AVOIDING; }
]?
[ C_ILLEGAL {
#ifdef NON_CORRECTING
if (n_rules >= max_rules-2) {
rule_table = (p_gram) ralloc(
(p_mem) rule_table,
(unsigned)(max_rules+=RULEINCR)*sizeof(t_gram));
}
elmcnt++;
rule_table[n_rules++] =
*search(TERMINAL, "LLILLEGAL", BOTH);
if (*conflres & DEF) {
error(linecount, "%%illegal not allowed in %%default rule");
}
#endif
}
]?
[ %persistent elem(&elem)
{ if (n_rules >= max_rules-2) {
rule_table = (p_gram) ralloc(
@ -467,9 +480,12 @@ elem (register p_gram pres;)
p_gram p1;
int ln;
p_gram pe;
#ifdef NON_CORRECTING
int erroneous = 0;
#endif
} :
'[' { ln = linecount; }
[ C_WHILE expr { t |= RESOLVER; }
[ C_WHILE C_EXPR { t |= RESOLVER; }
]?
[ C_PERSISTENT { t |= PERSISTENT; }
]?
@ -478,12 +494,32 @@ elem (register p_gram pres;)
mkterm(p1,t,ln,pres);
}
|
[ C_ERRONEOUS {
#ifdef NON_CORRECTING
erroneous = 1;
#endif
}
]?
[
C_IDENT { pe = search(UNKNOWN,lextoken.t_string,BOTH);
*pres = *pe;
#ifdef NON_CORRECTING
if (erroneous) {
if (g_gettype(pres) != TERMINAL){
warning(linecount,
"Erroneous only allowed on terminal");
erroneous = 0;
}
else
pres->g_erroneous = 1;
}
#endif
}
[ params(0) { if (nparams > 15) {
[ C_PARAMS { if (lextoken.t_num > 15) {
error(linecount,"Too many parameters");
} else g_setnpar(pres,nparams);
} else g_setnpar(pres,lextoken.t_num);
if (g_gettype(pres) == TERMINAL) {
error(linecount,
"Terminal with parameters");
@ -492,27 +528,73 @@ elem (register p_gram pres;)
]?
| C_LITERAL { pe = search(LITERAL,lextoken.t_string,BOTH);
*pres = *pe;
#ifdef NON_CORRECTING
if (erroneous)
pres->g_erroneous = 1;
#endif
}
]
| { g_settype(pres,ACTION);
pres->g_lineno = linecount;
#ifdef NON_CORRECTING
g_setsubparse(pres, (p_start) 0);
#endif
}
action(1)
;
params(int formal)
{
long off = ftell(fact);
}
: '(' { copyact('(', ')', formal ? 2 : 0, 0); }
')'
{ if (nparams == 0) {
fseek(fact, off, 0);
[ C_SUBSTART
{
#ifdef NON_CORRECTING
nsubstarts++;
#endif
}
C_IDENT
{
#ifdef NON_CORRECTING
register p_gram temp;
register p_start subp;
temp = search(NONTERM,lextoken.t_string,BOTH);
subp = (p_start) alloc (sizeof(t_start));
subp->ff_nont = g_getcont(temp);
subp->ff_name = (string) 0;
subp->ff_next = (p_start) 0;
g_setsubparse(pres, subp);
#endif
}
[ ',' C_IDENT
{
#ifdef NON_CORRECTING
register p_gram temp;
register p_start ff;
temp = search(NONTERM,lextoken.t_string,BOTH);
ff = g_getsubparse(pres);
while (ff) {
if (ff->ff_nont == g_getcont(temp)) {
warning(linecount, "\"%s\" used twice in %%substart", lextoken.t_string);
break;
}
ff = ff->ff_next;
}
}
;
expr : '(' { copyact('(',')',1,0); }
')'
ff = (p_start) alloc(sizeof(t_start));
ff->ff_nont = g_getcont(temp);
ff->ff_name = (string) 0;
ff->ff_next = g_getsubparse(pres);
g_setsubparse(pres, ff);
#endif
}
]* ';'
]?
C_ACTION
;
repeats(int *kind; int *cnt;) { int t1 = 0; } :
@ -562,119 +644,6 @@ firsts { register string p; }
;
{
STATIC
copyact(ch1,ch2,flag,level) char ch1,ch2; {
/*
* Copy an action to file f. Opening bracket is ch1, closing bracket
* is ch2.
* If flag & 1, copy opening and closing parameters too.
* If flag & 2, don't allow ','.
*/
static int text_seen = 0;
register FILE *f;
register ch; /* Current char */
register match; /* used to read strings */
int saved; /* save linecount */
int sav_strip = strip_grammar;
f = fact;
if (ch1 == '{' || flag != 1) strip_grammar = 0;
if (!level) {
saved = linecount;
text_seen = 0;
nparams = 0; /* count comma's */
putc('\0',f);
fprintf(f,"# line %d \"%s\"\n", linecount,f_input);
}
if (level || (flag & 1)) putc(ch1,f);
for (;;) {
ch = input();
if (ch == ch2) {
if (!level) {
unput(ch);
if (text_seen) nparams++;
}
if (level || (flag & 1)) putc(ch,f);
if (strip_grammar != sav_strip) {
if (ch1 == '{' || flag != 1) putchar(ch);
}
strip_grammar = sav_strip;
return;
}
switch(ch) {
case ')':
case '}':
case ']':
error(linecount,"Parentheses mismatch");
break;
case '(':
text_seen = 1;
copyact('(',')',flag,level+1);
continue;
case '{':
text_seen = 1;
copyact('{','}',flag,level+1);
continue;
case '[':
text_seen = 1;
copyact('[',']',flag,level+1);
continue;
case '/':
ch = input();
unput(ch);
if (ch == '*') {
putc('/', f);
skipcomment(1);
continue;
}
ch = '/';
text_seen = 1;
break;
case ';':
case ',':
if (! level && text_seen) {
text_seen = 0;
nparams++;
if (ch == ',' && (flag & 2)) {
warning(linecount, "Parameters may not be separated with a ','");
ch = ';';
}
}
break;
case '\'':
case '"' :
/*
* watch out for brackets in strings, they do not
* count !
*/
text_seen = 1;
match = ch;
putc(ch,f);
while((ch = input())) {
if (ch == match) break;
if (ch == '\\') {
putc(ch,f);
ch = input();
}
if (ch == '\n') {
error(linecount,"Newline in string");
unput(match);
}
putc(ch,f);
}
if (ch == match) break;
/* Fall through */
case EOF :
if (!level) error(saved,"Action does not terminate");
strip_grammar = sav_strip;
return;
default:
if (c_class[ch] != ISSPA) text_seen = 1;
}
putc(ch,f);
}
}
STATIC p_gram
copyrule(p,length) register p_gram p; {
/*

View file

@ -80,7 +80,7 @@ new_mem(p) register p_info p; {
*/
p->i_size += p->i_incr * p->i_esize;
}
p->i_ptr = !p->i_ptr ?
p->i_ptr = !p->i_ptr ?
alloc(p->i_size) :
ralloc(p->i_ptr, p->i_size);
p->i_max = p->i_ptr + sz;

View file

@ -47,8 +47,8 @@ char c_class[] = {
ISKEY, /* '%' */
0, /* '&' */
ISLIT, /* ''' */
ISTOK, /* '(' */
ISTOK, /* ')' */
ISACT, /* '(' */
0, /* ')' */
ISTOK, /* '*' */
ISTOK, /* '+' */
ISTOK, /* ',' */
@ -130,9 +130,9 @@ char c_class[] = {
ISLET, /* 'x' */
ISLET, /* 'y' */
ISLET, /* 'z' */
ISTOK, /* '{' */
ISACT, /* '{' */
ISTOK, /* '|' */
ISTOK, /* '}' */
0, /* '}' */
0, /* '~' */
0 /* 0177 */
};

View file

@ -14,3 +14,4 @@ extern char c_class[];
#define ISTOK 5
#define ISCOM 6
#define ISLIT 7
#define ISACT 8

View file

@ -26,7 +26,6 @@
static string rcsid1 = "$Id$";
# endif
static string c_first = "> firstset ";
static string c_contains = "> containset ";
static string c_follow = "> followset ";
@ -72,7 +71,7 @@ conflchecks() {
f_input = x->f_name;
for (s = x->f_nonterminals; s != -1; s = p->n_next) {
p = &nonterms[s];
if (check(p->n_rule)) p->n_flags |= VERBOSE;
if (check(p->n_rule)) p->n_flags |= VERBOSE;
}
}
for (x = files; x < maxfiles; x++) {
@ -188,7 +187,7 @@ check(p) register p_gram p; {
n = &nonterms[g_getcont(p)];
if (g_getnpar(p) != getntparams(n)) {
error(p->g_lineno,
"Call of %s: parameter count mismatch",
"Call of %s: parameter count mismatch",
n->n_name);
}
break; }
@ -211,13 +210,13 @@ check(p) register p_gram p; {
temp = setalloc();
setunion(temp,q->t_first);
if (!setintersect(temp,q->t_follow)) {
/*
* q->t_first * q->t_follow != EMPTY
*/
/*
* q->t_first * q->t_follow != EMPTY
*/
if (!(q->t_flags & RESOLVER)) {
/*
* No conflict resolver
*/
/*
* No conflict resolver
*/
error(p->g_lineno,
"Repetition conflict");
retval = 1;
@ -249,7 +248,7 @@ check(p) register p_gram p; {
"Alternation conflict");
retval = 1;
moreverbose(temp);
}
}
} else {
if (l->l_flag & (COND|PREFERING|AVOIDING)) {
l->l_flag |= NOCONF;
@ -257,8 +256,8 @@ check(p) register p_gram p; {
"Conflict resolver without conflict");
}
}
if (l->l_flag & PREFERING) propagate(l->l_symbs,p+1);
free( (p_mem) temp);
if (l->l_flag & PREFERING) propagate(l->l_symbs,p+1);
retval |= check(l->l_rule);
break; }
}
@ -378,7 +377,7 @@ prrule(p) register p_gram p; {
spaces();
p++; continue; }
case LITERAL :
case TERMINAL : {
case TERMINAL : {
register p_token pt = &tokens[g_getcont(p)];
fprintf(f,pt->t_tokno<0400 ?
@ -463,7 +462,7 @@ propagate(set,p) p_set set; register p_gram p; {
while (g_gettype(p) != EORULE) {
setminus(g_getlink(p)->l_symbs,set);
p++;
}
}
}
STATIC

View file

@ -59,6 +59,12 @@ STATIC do_contains();
STATIC contains();
STATIC int nsafes();
STATIC int do_safes();
#ifdef NON_CORRECTING
STATIC int nc_nfirst();
STATIC nc_first();
STATIC int nc_nfollow();
STATIC nc_follow();
#endif
do_compute() {
/*
@ -116,6 +122,31 @@ do_compute() {
setntsafe(p,SCANDONE);
}
co_trans(nsafes);
#ifdef NON_CORRECTING
if (subpars_sim) {
int s;
/* compute the union of the first sets of all start symbols
Used to compute the nc-first-sets when -s option is given */
start_firsts = get_set();
for (st = start; st; st = st->ff_next) {
s = setunion(start_firsts, (&nonterms[st->ff_nont])->n_first);
}
}
if (non_corr) {
/* compute the non_corr first sets for all nonterminals and terms */
co_trans(nc_nfirst);
for (st = start; st; st = st->ff_next) {
p = &nonterms[st->ff_nont];
PUTIN(p->n_nc_follow,0);
}
co_trans(nc_nfollow);
}
#endif
# ifndef NDEBUG
if (debug) {
fputs("Safeties:\n", stderr);
@ -151,6 +182,10 @@ createsets() {
p = &nonterms[i];
p->n_flags |= GENSTATIC;
p->n_first = get_set();
#ifdef NON_CORRECTING
p->n_nc_first = get_set();
p->n_nc_follow = get_set();
#endif
p->n_follow = get_set();
walk(f->f_used, p->n_rule);
}
@ -185,6 +220,10 @@ walk(u, p) p_set u; register p_gram p; {
q = g_getterm(p);
q->t_first = get_set();
#ifdef NON_CORRECTING
q->t_nc_first = get_set();
q->t_nc_follow = get_set();
#endif
q->t_follow = get_set();
walk(u, q->t_rule);
break; }
@ -193,6 +232,9 @@ walk(u, p) p_set u; register p_gram p; {
l = g_getlink(p);
l->l_symbs = get_set();
#ifdef NON_CORRECTING
l->l_nc_symbs = get_set();
#endif
l->l_others = get_set();
walk(u, l->l_rule);
break; }
@ -237,7 +279,7 @@ empty(p) register p_gram p; {
for (;;) {
switch (g_gettype(p)) {
case EORULE :
case EORULE :
return 1;
case TERM : {
register p_term q;
@ -271,6 +313,12 @@ nfirst(p) register p_nont p; {
return first(p->n_first, p->n_rule, 0);
}
#ifdef NON_CORRECTING
STATIC int nc_nfirst(p) register p_nont p; {
return nc_first(p->n_nc_first, p->n_rule, 0);
}
#endif
STATIC
first(setp,p,flag) p_set setp; register p_gram p; {
/*
@ -282,8 +330,8 @@ first(setp,p,flag) p_set setp; register p_gram p; {
*/
register s; /* Will gather return value */
int noenter;/* when set, unables entering of elements into
* setp. This is necessary to walk through the
* rest of rule p.
* setp. This is necessary to walk through the
* rest of rule p.
*/
s = 0;
@ -349,6 +397,108 @@ first(setp,p,flag) p_set setp; register p_gram p; {
}
}
#ifdef NON_CORRECTING
STATIC
nc_first(setp,p,flag) p_set setp; register p_gram p; {
/*
* Compute the non_corr FIRST set of rule p.
* If flag = 0, also the non_corr first sets for terms and
* alternations in the rule p are computed.
* The non_corr FIRST set is put in setp.
* return 1 if the set refered to by "setp" changed
* If the -s flag was given, the union of the first-sets of all
* start symbols is used whenever an action occurs. Else, only the
* first-sets of startsynbols in the %substart are used
*/
register s; /* Will gather return value */
int noenter;/* when set, unables entering of elements into
* setp. This is necessary to walk through the
* rest of rule p.
*/
s = 0;
noenter = 0;
for (;;) {
switch (g_gettype(p)) {
case EORULE :
return s;
case TERM : {
register p_term q;
q = g_getterm(p);
if (flag == 0) {
if (nc_first(q->t_nc_first,q->t_rule,0))/*nothing*/;
}
if (!noenter) s |= setunion(setp,q->t_nc_first);
p++;
if (r_getkind(q) == STAR ||
r_getkind(q) == OPT ||
empty(q->t_rule)) continue;
break; }
case ALTERNATION : {
register p_link l;
l = g_getlink(p);
if (flag == 0) {
if (nc_first(l->l_nc_symbs,l->l_rule,0))/*nothing*/;
}
if (noenter == 0) {
s |= setunion(setp,l->l_nc_symbs);
}
if (g_gettype(p+1) == EORULE) return s;
}
p++;
continue;
case ACTION : {
register p_start subp;
if (!noenter)
if (subpars_sim)
s |= setunion(setp, start_firsts);
else {
for (subp = g_getsubparse(p); subp;
subp = subp->ff_next)
s |= setunion(setp, (&nonterms[subp->ff_nont])->n_nc_first);
}
p++;
continue;
}
case LITERAL :
case TERMINAL :
if (g_getcont(p) == g_getcont(illegal_gram)) {
/* Ignore for this set. */
p++;
continue;
}
if ((noenter == 0) && !IN(setp,g_getcont(p))) {
s = 1;
PUTIN(setp,g_getcont(p));
}
p++;
break;
case NONTERM : {
register p_nont n;
n = &nonterms[g_getcont(p)];
if (noenter == 0) {
s |= setunion(setp,n->n_nc_first);
if (ntneeded) NTPUTIN(setp,g_getcont(p));
}
p++;
if (n->n_flags & EMPTY) continue;
break; }
}
if (flag == 0) {
noenter = 1;
continue;
}
return s;
}
}
#endif
STATIC int
nfollow(p) register p_nont p; {
return follow(p->n_follow, p->n_rule);
@ -426,6 +576,87 @@ follow(setp,p) p_set setp; register p_gram p; {
}
}
#ifdef NON_CORRECTING
STATIC int
nc_nfollow(p) register p_nont p; {
return follow(p->n_nc_follow, p->n_rule);
}
STATIC
nc_follow(setp,p) p_set setp; register p_gram p; {
/*
* setp is the follow set for the rule p.
* Compute the follow sets in the rule p from this set.
* Return 1 if a follow set of a nonterminal changed.
*/
register s; /* Will gather return value */
s = 0;
for (;;) {
switch (g_gettype(p)) {
case EORULE :
return s;
case TERM : {
register p_term q;
q = g_getterm(p);
if (empty(p+1)) {
/*
* If what follows the term can be empty,
* everything that can follow the whole
* rule can also follow the term
*/
s |= setunion(q->t_nc_follow,setp);
}
/*
* Everything that can start the rest of the rule
* can follow the term
*/
s |= nc_first(q->t_nc_follow,p+1,1);
if (r_getkind(q) == STAR ||
r_getkind(q) == PLUS ||
r_getnum(q) ) {
/*
* If the term involves a repetition
* of possibly more than one,
* everything that can start the term
* can also follow it.
*/
s |= nc_follow(q->t_nc_first,q->t_rule);
}
/*
* Now propagate the set computed sofar
*/
s |= nc_follow(q->t_nc_follow, q->t_rule);
break; }
case ALTERNATION :
/*
* Just propagate setp
*/
s |= nc_follow(setp,g_getlink(p)->l_rule);
break;
case NONTERM : {
register p_nont n;
n = &nonterms[g_getcont(p)];
s |= nc_first(n->n_nc_follow,p+1,1);
if (empty(p+1)) {
/*
* If the rest of p can produce empty,
* everything that follows p can follow
* the nonterminal
*/
s |= setunion(n->n_nc_follow,setp);
}
break; }
}
p++;
}
}
#endif
STATIC
co_dirsymb(setp,p) p_set setp; register p_gram p; {
/*
@ -519,7 +750,7 @@ do_lengthcomp() {
* Compute the minimum length of a terminal production for each
* nonterminal.
* This length consists of two fields: the number of terminals,
* and a number that is composed of
* and a number that is composed of
* - the number of this alternative
* - a crude measure of the number of terms and nonterminals in the
* production of this shortest string.
@ -562,6 +793,12 @@ complength(p,le) register p_gram p; p_length le; {
switch (g_gettype(p)) {
case LITERAL :
case TERMINAL :
#ifdef NON_CORRECTING
if (g_getcont(p) == g_getcont(illegal_gram)) {
add(&X, INFINITY, 0);
break;
}
#endif
add(&X, 1, 0);
break;
case ALTERNATION :
@ -571,6 +808,7 @@ complength(p,le) register p_gram p; p_length le; {
while (g_gettype(p) != EORULE) {
cnt++;
l = g_getlink(p);
p++;
complength(l->l_rule,&i);
i.val += cnt;
if (l->l_flag & DEF) {
@ -580,7 +818,6 @@ complength(p,le) register p_gram p; p_length le; {
if (compare(&i, &X) < 0) {
X = i;
}
p++;
}
/* Fall through */
case EORULE :
@ -593,7 +830,7 @@ complength(p,le) register p_gram p; p_length le; {
q = g_getterm(p);
rep = r_getkind(q);
X.val += 1;
if ((q->t_flags&PERSISTENT) ||
if ((q->t_flags&PERSISTENT) ||
rep==FIXED || rep==PLUS) {
complength(q->t_rule,&i);
add(&X, i.cnt, i.val);
@ -661,6 +898,7 @@ setdefaults(p) register p_gram p; {
do {
cnt++;
l = g_getlink(p);
p++;
complength(l->l_rule,&i);
i.val += cnt;
if (l->l_flag & DEF) temp = 1;
@ -671,7 +909,6 @@ setdefaults(p) register p_gram p; {
count = i;
}
setdefaults(l->l_rule);
p++;
} while (g_gettype(p) != EORULE);
if (!temp) {
/* No user specified default */
@ -687,7 +924,7 @@ STATIC
do_contains(n) register p_nont n; {
/*
* Compute the total set of symbols that nonterminal n can
* produce
* produce
*/
if (n->n_contains == 0) {
@ -811,7 +1048,7 @@ do_safes(p,safe,ch) register p_gram p; register int *ch; {
for (;;) {
switch (g_gettype(p)) {
case ACTION:
p++;
p++;
continue;
case LITERAL:
case TERMINAL:
@ -830,12 +1067,13 @@ do_safes(p,safe,ch) register p_gram p; register int *ch; {
safe = t_after(rep, i, retval);
break; }
case ALTERNATION : {
register p_link l;
register p_link l;
register int i;
retval = -1;
while (g_gettype(p) == ALTERNATION) {
l = g_getlink(p);
p++;
if (safe > SAFE && (l->l_flag & DEF)) {
i = do_safes(l->l_rule,SAFESCANDONE,ch);
}
@ -848,7 +1086,6 @@ do_safes(p,safe,ch) register p_gram p; register int *ch; {
}
else if (i > retval) retval = i;
}
p++;
}
return retval; }
case NONTERM : {

View file

@ -36,6 +36,11 @@ extern int ntokens; /* number of terminals */
extern int nterms; /* number of terms */
extern int nalts; /* number of alternatives */
extern p_start start; /* will contain startsymbols */
#ifdef NON_CORRECTING
extern int nsubstarts; /* number of subparserstarts */
extern p_set start_firsts; /* Will contain the union of first sets of
startsymbols when -n -s option is on */
#endif
extern int linecount; /* line number */
extern int assval; /* to create difference between literals
* and other terminals
@ -73,8 +78,17 @@ extern string LLgenid; /* LLgen identification string */
extern t_token lextoken; /* the current token */
extern int nerrors;
extern string rec_file, incl_file;
#ifdef NON_CORRECTING
extern string nc_rec_file, nc_incl_file;
#endif
extern int low_percentage, high_percentage;
extern int min_cases_for_jmptable;
extern int jmptable_option;
extern int ansi_c;
#ifdef NON_CORRECTING
extern int non_corr;
extern int subpars_sim;
extern p_gram illegal_gram;
#endif
extern int strip_grammar;
extern int in_production;

View file

@ -51,6 +51,9 @@ extern gencode();
STATIC opentemp();
STATIC geninclude();
STATIC genrecovery();
#ifdef NON_CORRECTING
STATIC genncrecovery();
#endif
STATIC string genname();
STATIC generate();
STATIC prset();
@ -109,18 +112,31 @@ genhdr()
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 */
@ -138,6 +154,7 @@ gencode(argc) {
}
geninclude();
genrecovery();
fclose(fact);
}
@ -167,13 +184,18 @@ geninclude() {
}
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 FILE *f;
register p_token t;
register int *q;
register p_nont p;
@ -202,6 +224,12 @@ genrecovery() {
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) {
@ -214,7 +242,18 @@ genrecovery() {
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];\n\tLLnewlevel(s);\n\tLLread();\n", f);
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));
}
@ -224,7 +263,18 @@ genrecovery() {
fputs("\tLL_NOSCANDONE(EOFILE);\n",f);
}
else fputs("\tLL_SCANDONE(EOFILE);\n",f);
fputs("\tLLoldlevel(s);\n}\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);
@ -254,6 +304,46 @@ genrecovery() {
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; {
/*
@ -272,7 +362,7 @@ generate(f) p_file f; {
for (ff = f->f_firsts; ff; ff = ff->ff_next) {
macro(ff->ff_name,&nonterms[ff->ff_nont]);
}
/* For every nonterminal generate a function */
for (s = f->f_nonterminals; s != -1; s = p->n_next) {
p = &nonterms[s];
@ -378,7 +468,7 @@ 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();
@ -407,7 +497,7 @@ getparams() {
}
fputs(") ",fpars);
/*
* Now copy the declarations
* Now copy the declarations
*/
l = getc(fact); /* patch: some implementations of fseek
do not work properly after "ungetc"
@ -469,7 +559,7 @@ 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;
@ -911,7 +1001,7 @@ codeforterm(q,safety,toplevel) register p_term q; {
int term_is_persistent = (q->t_flags & PERSISTENT);
int ispushed = NOPOP;
if (!(toplevel > 0 &&
if (!(toplevel > 0 &&
(safety == 0 || (!onerror && safety <= SAFESCANDONE)) &&
(rep_kind == OPT || (rep_kind == FIXED && rep_count == 0)))) {
ispushed = findindex(q->t_contains);
@ -1091,21 +1181,21 @@ genswhead(q, rep_kind, rep_count, safety, ispushed) register p_term q; {
STATIC
gencases(tokenlist, caseno, compacted)
int *tokenlist;
int *tokenlist;
{
/*
* setp points to a bitset indicating which cases must
* be generated.
* YECH, the PCC compiler does not accept many cases without statements
* inbetween, so after every case label an empty statement is
* 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
* statement : label statement
* which is right-recursive, and as is well known, LALR parsers don't
* handle these things very good.
* handle these things very well.
* The grammar should have been written :
* labeledstatement : labels statement ;
* labeledstatement : labels statement ;
* labels : labels label | ;
*/
register p_token p;
@ -1119,7 +1209,7 @@ gencases(tokenlist, caseno, compacted)
(p->t_tokno < 0400 ? "/* case '%s' */\n" :
"/* case %s */\n") :
p->t_tokno<0400 ? "case /* '%s' */ %d : ;\n"
: "case /* %s */ %d : ;\n",
: "case /* %s */ %d : ;\n",
p->t_string, i);
}
}
@ -1220,10 +1310,10 @@ out_list(tokenlist, listno, casecnt)
register int i;
register FILE *f = fpars;
fprintf(f, "static %s LL%d_tklist[] = {",
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]);
}
@ -1260,6 +1350,10 @@ correct_prefix()
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);
}

View file

@ -33,6 +33,10 @@ p_token maxt;
int ntokens;
int nterms, nalts;
int norder, torder;
#ifdef NON_CORRECTING
int nsubstarts;
p_set start_firsts;
#endif
p_start start;
int linecount;
int assval;
@ -42,6 +46,9 @@ FILE *finput;
FILE *fact;
char f_pars[] = PARSERFILE;
char f_temp[] = ACTFILE;
#ifdef NON_CORRECTING
char f_nc[20];
#endif
char f_out[20];
string f_input;
char f_include[20];
@ -64,8 +71,19 @@ string LLgenid = "/* LLgen generated code from source %s */\n";
t_token lextoken;
int nerrors;
string rec_file, incl_file;
#ifdef NON_CORRECTING
string nc_rec_file, nc_incl_file;
#endif
int low_percentage = 10, high_percentage = 30;
int min_cases_for_jmptable = 8;
int jmptable_option;
int ansi_c = 0;
#ifdef NON_CORRECTING
int non_corr = 0;
int subpars_sim = 0;
p_gram illegal_gram;
#endif
int strip_grammar = 0;
int in_production; /* set when the parser is reading a production
rule.
*/

View file

@ -25,7 +25,9 @@
# define ACTFILE "tempXXXXXX" /* temporary file to save actions */
# define HFILE "%spars.h" /* file for "#define's " */
# define RFILE "%spars.c" /* Error recovery */
#ifdef NON_CORRECTING
# define NCFILE "%sncor.c" /* Non-corrcting error recovery */
#endif
extern FILE *finput;
extern FILE *fpars;
extern FILE *fact;
@ -36,3 +38,6 @@ extern char f_out[];
extern string f_input;
extern char f_include[];
extern char f_rec[];
#ifdef NON_CORRECTING
extern char f_nc[];
#endif

View file

@ -41,13 +41,13 @@ extern char *sbrk();
main(argc,argv) register string argv[]; {
register string arg;
string libpath();
char *beg_sbrk;
char *beg_sbrk = 0;
/* Initialize */
assval = 0400;
/* read options */
while (argc >= 2 && (arg = argv[1], *arg == '-')) {
while (*++arg) {
switch(*arg) {
@ -84,7 +84,7 @@ main(argc,argv) register string argv[]; {
fprintf(stderr,"duplicate -r flag\n");
exit(1);
}
rec_file = ++arg;
rec_file = ++arg;
break;
case 'i':
case 'I':
@ -92,7 +92,7 @@ main(argc,argv) register string argv[]; {
fprintf(stderr,"duplicate -i flag\n");
exit(1);
}
incl_file = ++arg;
incl_file = ++arg;
break;
#endif /* not NDEBUG */
case 'x':
@ -104,8 +104,18 @@ main(argc,argv) register string argv[]; {
case 'A':
ansi_c = 1;
continue;
#ifdef NON_CORRECTING
case 'n':
case 'N':
non_corr = 1;
continue;
case 's':
case 'S':
subpars_sim = 1;
continue;
#endif
case 'g':
case 'G':
strip_grammar = 1;
continue;
default:
@ -120,6 +130,13 @@ main(argc,argv) register string argv[]; {
if (verbose) beg_sbrk = sbrk(0);
#ifdef NON_CORRECTING
if ((subpars_sim) && (!non_corr)) {
fprintf(stderr,"option -s illegal without -n, turned off\n");
subpars_sim = 0;
}
#endif
/*
* Now check wether the sets should include nonterminals
*/
@ -139,6 +156,12 @@ main(argc,argv) register string argv[]; {
# ifndef NDEBUG
}
# endif
#ifdef NON_CORRECTING
if (non_corr) {
nc_incl_file = libpath("nc_incl");
nc_rec_file = libpath ("nc_rec");
}
#endif
mktemp(f_temp);
mktemp(f_pars);
if ((fact = fopen(f_temp,"w")) == NULL) {
@ -154,6 +177,10 @@ main(argc,argv) register string argv[]; {
*/
sprintf(f_include, HFILE, prefix ? prefix : "L");
sprintf(f_rec, RFILE, prefix ? prefix : "L");
#ifdef NON_CORRECTING
if (non_corr)
sprintf(f_nc, NCFILE, prefix ? prefix : "L");
#endif
setinit(ntneeded);
maxnt = &nonterms[nnonterms];
maxt = &tokens[ntokens];
@ -216,7 +243,7 @@ readgrammar(argc,argv) char *argv[]; {
/*
* There must be a start symbol!
*/
if (start == 0) {
if (! nerrors && start == 0) {
fatal(linecount,"Missing %%start");
}
if (nerrors) comfatal();
@ -237,7 +264,7 @@ doparse(p) register p_file p; {
}
/* VARARGS1 */
error(lineno,s,t,u) string s,t,u; {
error(lineno,s,t,u) string s,t,u; {
/*
* Just an error message
*/
@ -250,7 +277,7 @@ error(lineno,s,t,u) string s,t,u; {
}
/* VARARGS1 */
warning(lineno,s,t,u) string s,t,u; {
warning(lineno,s,t,u) string s,t,u; {
/*
* Just a warning
*/
@ -292,7 +319,7 @@ copyfile(file) string file; {
register FILE *f;
if ((f = fopen(file,"r")) == NULL) {
fatal(0,"Cannot open libraryfile, call an expert");
fatal(0,"Cannot open library file %s, call an expert",file);
}
while ((c = getc(f)) != EOF) putc(c,fpars);
fclose(f);

View file

@ -51,6 +51,9 @@ name_init() {
nont_info.i_esize = sizeof (t_nont);
nont_info.i_incr = 50;
search(TERMINAL,"EOFILE",ENTERING);
#ifdef NON_CORRECTING
illegal_gram = search(TERMINAL,"LLILLEGAL",ENTERING);
#endif
}
STATIC p_entry
@ -65,10 +68,13 @@ newentry(str, next) string str; p_entry next; {
p->h_name = str;
p->h_next = next;
p->h_type.g_lineno = linecount;
#ifdef NON_CORRECTING
p->h_type.g_erroneous = 0;
#endif
return p;
}
string
string
store(s) string s; {
/*
* Store a string s in the name table
@ -147,14 +153,14 @@ search(type,str,option) register string str; {
"%s : is already defined",str);
}
p->h_type.g_lineno = linecount;
return &(p->h_type);
return &(p->h_type);
}
}
p = newentry(store(str), h_root[i]);
h_root[i] = p;
if (type == TERMINAL || type == LITERAL) {
register p_token pt;
pt = (p_token) new_mem(&token_info);
tokens = (p_token) token_info.i_ptr;
pt->t_string = p->h_name;
@ -166,7 +172,7 @@ search(type,str,option) register string str; {
if (str[2] == '\0') {
switch(str[1]) {
case 'n' :
val = '\n';
val = '\n';
break;
case 'r' :
val = '\r';
@ -175,19 +181,19 @@ search(type,str,option) register string str; {
val = '\b';
break;
case 'f' :
val = '\f';
val = '\f';
break;
case 't' :
val = '\t';
val = '\t';
break;
case '\'':
val = '\'';
val = '\'';
break;
case '\\':
val = '\\';
val = '\\';
break;
default :
error(linecount,e_literal);
error(linecount,e_literal);
}
} else {
/*
@ -200,7 +206,7 @@ search(type,str,option) register string str; {
val = 64*str[1] - 73*'0' +
8*str[2] + str[3];
}
} else {
} else {
/*
* No escape in literal
*/
@ -221,7 +227,7 @@ search(type,str,option) register string str; {
return &(p->h_type);
}
/*
* type == NONTERM || type == UNKNOWN
* type == NONTERM || type == UNKNOWN
* UNKNOWN and not yet declared means : NONTERM
*/
{

View file

@ -15,11 +15,11 @@ LLOPT= # -vvv -x
OBJECTS = main.$(SUF) gencode.$(SUF) compute.$(SUF) LLgen.$(SUF) tokens.$(SUF) \
check.$(SUF) reach.$(SUF) global.$(SUF) name.$(SUF) sets.$(SUF) \
Lpars.$(SUF) alloc.$(SUF) machdep.$(SUF) cclass.$(SUF)
Lpars.$(SUF) alloc.$(SUF) machdep.$(SUF) cclass.$(SUF) savegram.$(SUF)
CSRC = $(SRC_DIR)/main.c $(SRC_DIR)/gencode.c $(SRC_DIR)/compute.c \
$(SRC_DIR)/check.c $(SRC_DIR)/reach.c $(SRC_DIR)/global.c \
$(SRC_DIR)/name.c $(SRC_DIR)/sets.c $(SRC_DIR)/alloc.c \
$(SRC_DIR)/machdep.c $(SRC_DIR)/cclass.c
$(SRC_DIR)/machdep.c $(SRC_DIR)/cclass.c $(SRC_DIR)/savegram.c
CFILES = LLgen.c tokens.c Lpars.c $(CSRC)
GFILES = $(SRC_DIR)/tokens.g $(SRC_DIR)/LLgen.g
FILES = $(SRC_DIR)/types.h $(SRC_DIR)/extern.h \

385
util/LLgen/src/savegram.c Normal file
View file

@ -0,0 +1,385 @@
/* Copyright (c) 1991 by the Vrije Universiteit, Amsterdam, the Netherlands.
* All rights reserved.
*/
#ifdef NON_CORRECTING
/*
* L L G E N
*
* An Extended LL(1) Parser Generator
*
* Author : Ceriel J.H. Jacobs
*/
/*
* savegram.c
* Save the input grammar for non-correcting error recovery
*
* Grammar rules are `flattened' by introducing anonymous nonterminals.
* [B]? becomes X; X: B | {empty}
* [B]+ becomes X: B Y; Y: X | {empty}
* [B]* becomes X; X: B X | {empty}
* [B | C] becomes X; X: B | C
* [B | C]* becomes X; X: B X | C X | {empty} etc.
*/
# include "types.h"
# include "extern.h"
# include "io.h"
# include "assert.h"
# include "sets.h"
#define LLALT 9999
static int nt_highest;
extern int nbytes;
extern p_mem alloc();
extern p_set start_firsts;
extern p_set setalloc();
extern p_gram search();
STATIC save_rule();
STATIC save_set();
/* t_list will contain terms to be `flattened' */
static struct t_list {
p_term term;
int t_nt_num;
} *t_list;
/* Subparse list will contain symbols in %substart */
static struct subparse_list {
p_gram sub_action;
int sub_nt_num;
} *sub_list;
/* Index in t_list */
static int t_list_index;
/* Index in subparse_list */;
static int sub_list_index;
/* File to save grammar to */
static FILE *fgram;
/* Nonterminal number to simulate parsers that get called in actions
used when LLgen called with -n -s options */
int act_nt;
save_grammar(f) FILE *f; {
/*
* Save the grammar
*/
register p_nont p;
register p_start st;
register int nt_nr;
fgram = f;
/* Compute highest nonterminal nr. */
nt_highest = nnonterms + assval - 1;
/* Generate some constants in the grammar file */
/* Allocate terms list */
t_list = (struct t_list *) alloc((unsigned) nterms * sizeof(struct t_list));
t_list_index = 0;
sub_list = (struct subparse_list *) alloc(nsubstarts * sizeof(struct subparse_list));
fputs("static ", fgram);
fputs((prefix ? prefix : "LL"), fgram);
fputs("grammar[] = {\n", fgram);
/* Check if -n -s option is on */
if (subpars_sim) {
/* Allocate action simulation nt */
act_nt = ++nt_highest;
/* write simualtion rule */
fprintf(fgram, "/* Simulation rule */\n");
fprintf(fgram, "%d,\n", act_nt);
/* Put a firstset and a fake followset */
/* Followset optimization is not implemented for
-s because it would be hard, and does not
bring enough improvement to jutify the effort
*/
save_set(start_firsts);
save_set(start_firsts);
/* Simulation rule procudes empty */
fprintf(fgram, "%d,\n", 1);
for (st = start; st; st = st->ff_next)
{
fprintf(fgram, "%d, %d, %d, \n", st->ff_nont + assval,
act_nt, LLALT);
}
fprintf(fgram, "%d, \n", 0);
}
/* Now process all rules */
for (p = nonterms, nt_nr = assval; p < maxnt; p++, nt_nr++) {
fprintf(fgram, "/* nr. %d %s */\n", nt_nr, p->n_name);
fprintf(fgram, "%d, ",nt_nr);
if (! p->n_rule) { /* undefined */
f_input = p->n_string;
error(p->n_lineno,"Nonterminal %s not defined",
p->n_name);
}
/* Save the first_set and follow set */
save_set(p->n_nc_first);
save_set(p->n_nc_follow);
if (p->n_flags & EMPTY)
fprintf(fgram, "%d,\n", 1);
else
fprintf(fgram, "%d,\n", 0);
save_rule(p->n_rule, 0);
fprintf(fgram, "%d,\n", 0);
}
/* Resolve terms, they are on t_list */
fprintf(fgram, "/* Fresh nonterminals */\n");
{ int i;
for (i = 0; i < t_list_index; i++)
{
/* Terms of the form [] without + ? * or number produce
a NIL pointer in the term-list */
if ((t_list + i)->term == (struct term *) 0) {
continue;
}
fprintf(fgram, "%d, ", (t_list + i)->t_nt_num);
/* Save the first and follow sets */
save_set((t_list + i)->term->t_nc_first);
save_set((t_list + i)->term->t_nc_follow);
/* NOTE: A VARIABLE REPETITION COUNT TERMS RULE IS NOT
ALLOWED TO PRODUCE EMPTY IN LLGEN
*/
switch(r_getkind((t_list + i)->term)) {
case FIXED:
/* Already done by repeating new nonterminal */
/* FIXED term-rule may produce empty */
if (empty((t_list +i)->term->t_rule))
fprintf(fgram, "%d,\n", 1);
else
fprintf(fgram, "%d,\n", 0);
save_rule((t_list + i)->term->t_rule, 0);
fprintf(fgram, "%d,\n", 0);
break;
case STAR:
/* Save the rule, appending the new lhs for this rule */
/* Star rules always produce empty */
fprintf(fgram, "1,\n");
save_rule((t_list + i)->term->t_rule,
(t_list + i)->t_nt_num);
fprintf(fgram, "%d,\n%d,\n", LLALT, 0);
/* ALT EMPTY*/
break;
case PLUS:
/* Save the rule appending a fresh nonterminal */
fprintf(fgram, "%d,\n", 0);
save_rule((t_list + i)->term->t_rule, ++nt_highest);
fprintf(fgram, "%d,\n", 0); /* EOR */
fprintf(fgram, "%d, ", nt_highest);
/* First set of the extra nonterm is same as
for the term */
/* Except that the new nonterm also produces empty ! */
save_set((t_list + i)->term->t_nc_first);
save_set((t_list + i)->term->t_nc_follow);
fprintf(fgram, "1,\n");
fprintf(fgram, "%d, ", (t_list+i)->t_nt_num);
fprintf(fgram, "%d,\n%d,\n", LLALT, 0); /* ALT EMPTY */
break;
case OPT:
fprintf(fgram, "1,\n");
save_rule((t_list + i)->term->t_rule, 0);
fprintf(fgram, "%d,\n%d,\n", LLALT, 0); /* ALT EMPTY */
break;
}
}
}
/* Resolve %substarts */
if (!subpars_sim) {
int i,s,check;
p_start ff, gg;
p_set temp_set;
for (i = 0; i < sub_list_index; i++) {
fprintf(fgram, "%d, ", (sub_list + i)->sub_nt_num);
/* Compute the first set */
temp_set = setalloc();
for (ff = g_getsubparse((sub_list + i)->sub_action);
ff; ff = ff->ff_next){
s = setunion(temp_set,
(&nonterms[ff->ff_nont])->n_first);
check = 0;
for (gg =start; gg; gg = gg->ff_next)
if (ff->ff_nont == gg->ff_nont)
check = 1;
if (check == 0)
warning((sub_list + i)->sub_action->g_lineno,
"\"%s\" is not a startsymbol",
(&nonterms[ff->ff_nont])->n_name);
}
save_set(temp_set);
save_set(temp_set);
free(temp_set);
/* Produces empty */
fprintf(fgram, "1,\n");
ff = g_getsubparse((sub_list + i)->sub_action);
for (; ff; ff = ff->ff_next)
fprintf(fgram, "%d, %d, %d, \n", ff->ff_nont + assval,
(sub_list + i)->sub_nt_num,
LLALT);
fprintf(fgram, "%d, \n", 0);
}
}
fprintf(fgram, "%d\n};\n", 0);
fprintf(fgram, "#define LLNNONTERMINALS %d\n", nt_highest - assval + 1);
}
STATIC
save_rule(p, tail) register p_gram p; int tail; {
/*
Walk through rule p, saving it. The non-terminal tail is
appended to the rule. It needs to be appended in this function
to process alt-rules correctly. Tail == 0 means don't append.
*/
int in_alt;
int illegal_num;
/* Processing an alt needs some special care. When processing the
first alternative, we don't want to write the alt-code;
When appending something to the alt, it needs to be appended to
every alternative and not at the end of the rule.
*/
/* Look up the ILLEGAL token number */
illegal_num = tokens[g_getcont(illegal_gram)].t_tokno;
in_alt = 0;
for (;;) {
switch(g_gettype(p)) {
case ALTERNATION :
if (in_alt)
fprintf(fgram, "%d,\n", LLALT);
else
in_alt = 1;
save_rule(g_getlink(p)->l_rule, tail);
break;
case TERM :
/* Make entry in term list */
(t_list + t_list_index)->term = g_getterm(p);
/* Test for [] without specifier */
if (g_getterm(p) == (struct term *) 0) {
t_list_index++;
break;
}
(t_list + t_list_index++)->t_nt_num = ++nt_highest;
fprintf(fgram, "%d, ", nt_highest);
/* Check if repetition, if so handle here */
if (r_getkind(g_getterm(p)) == FIXED)
{
int k;
for (k = 1; k < r_getnum(g_getterm(p)); k++)
fprintf(fgram, "%d, ", nt_highest);
}
break;
case NONTERM :
fprintf(fgram, "%d, ", g_getcont(p) + assval);
break;
case TERMINAL:
if (g_getcont(p) == g_getcont(illegal_gram)) {
/* %illegal. Ignore. */
break;
}
if (p->g_erroneous)
fprintf(fgram, "%d, ", illegal_num);
else
fprintf(fgram, "%d, ",
tokens[g_getcont(p)].t_tokno);
break;
case LITERAL:
if (p->g_erroneous)
fprintf(fgram, "%d, ", illegal_num);
else
fprintf(fgram, "%d, ",
tokens[g_getcont(p)].t_tokno);
break;
case ACTION:
if (subpars_sim) {
fprintf(fgram, "%d, ", act_nt);
}
else if (g_getsubparse(p)) {
/* Allocate nonterminal that will simulate
subparser
*/
(sub_list + sub_list_index)->sub_nt_num =
++nt_highest;
(sub_list + sub_list_index++)->sub_action = p;
fprintf(fgram, "%d, ", nt_highest);
}
break;
case EORULE :
if ((! in_alt) && tail )
/* If this rule is not an alt, append tail now.
If it is an alt, the recursive call of this function
has appended tail to each alternative
*/
fprintf(fgram, "%d, ", tail);
return;
}
p++;
}
}
STATIC
save_set(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(fgram,"0%o,",(int)(i & 0377));
i >>= 8;
if (--j == 0) {
fputs("\n",fgram);
return;
}
}
}
/* NOTREACHED */
}
#endif

View file

@ -31,7 +31,7 @@ extern p_set setalloc();
extern p_set get_set();
extern int setunion();
extern int setintersect();
extern setminus();
extern setminus();
extern int setempty();
extern int findindex();
extern int setcount();

View file

@ -14,7 +14,7 @@
/*
* tokens.g
* Defines the tokens for the grammar of LLgen.
* The lexical analyser and LLmessage are also included here.
* The lexical analyser and LLmessage are also included here.
*/
{
@ -30,7 +30,7 @@ static string rcsidc = "$Id$";
/* Here are defined : */
extern int scanner();
extern LLmessage();
extern LLmessage();
extern int input();
extern unput();
extern skipcomment();
@ -39,12 +39,18 @@ STATIC linedirective();
# endif
STATIC string cpy();
STATIC string vallookup();
STATIC copyact();
static int nparams;
}
/* Classes */
%token C_IDENT ; /* lextoken.t_string contains the identifier read */
%token C_IDENT ; /* lextoken.t_string contains the identifier read */
%token C_NUMBER ; /* lextoken.t_num contains the number read */
%token C_LITERAL ; /* lextoken.t_string contains the literal read */
%token C_EXPR ; /* A C expression (%if or %while) */
%token C_PARAMS ; /* formal or actual parameters */
%token C_ACTION ; /* a C action */
/* Keywords */
@ -60,6 +66,9 @@ STATIC string vallookup();
%token C_AVOID ;
%token C_PREFER ;
%token C_DEFAULT ;
%token C_SUBSTART ;
%token C_ERRONEOUS ;
%token C_ILLEGAL ;
%lexical scanner ;
@ -80,26 +89,143 @@ typedef struct keyword {
*/
static t_keyw resword[] = {
{ "token", C_TOKEN },
{ "avoid", C_AVOID },
{ "token", C_TOKEN },
{ "avoid", C_AVOID },
{ "prefer", C_PREFER },
{ "persistent", C_PERSISTENT },
{ "default", C_DEFAULT },
{ "if", C_IF },
{ "while", C_WHILE },
{ "first", C_FIRST },
{ "start", C_START },
{ "if", C_IF },
{ "while", C_WHILE },
{ "first", C_FIRST },
{ "start", C_START },
{ "lexical", C_LEXICAL },
{ "onerror", C_ONERROR },
{ "prefix", C_PREFIX },
{ 0, 0 }
#ifdef NON_CORRECTING
{ "substart", C_SUBSTART },
{ "erroneous", C_ERRONEOUS },
{ "illegal", C_ILLEGAL },
#endif
{ 0, 0 }
};
static t_token savedtok; /* to save lextoken in case of an insertion */
# ifdef LINE_DIRECTIVE
static int nostartline; /* = 0 if at the start of a line */
static int nostartline; /* = 0 if at the start of a line */
# endif
STATIC
copyact(ch1,ch2,flag,level) char ch1,ch2; {
/*
* Copy an action to file f. Opening bracket is ch1, closing bracket
* is ch2.
* If flag & 1, copy opening and closing parameters too.
* If flag & 2, don't allow ','.
*/
static int text_seen = 0;
register FILE *f;
register ch; /* Current char */
register match; /* used to read strings */
int saved = linecount;
/* save linecount */
int sav_strip = strip_grammar;
f = fact;
if (ch1 == '{' || flag != 1) strip_grammar = 0;
if (!level) {
text_seen = 0;
nparams = 0; /* count comma's */
putc('\0',f);
fprintf(f,"# line %d \"%s\"\n", linecount,f_input);
}
if (level || (flag & 1)) putc(ch1,f);
for (;;) {
ch = input();
if (ch == ch2) {
if (!level) {
if (text_seen) nparams++;
}
if (level || (flag & 1)) putc(ch,f);
if (strip_grammar != sav_strip) {
if (ch1 == '{' || flag != 1) putchar(ch);
}
strip_grammar = sav_strip;
return;
}
switch(ch) {
case ')':
case '}':
case ']':
error(linecount,"Parentheses mismatch");
break;
case '(':
text_seen = 1;
copyact('(',')',flag,level+1);
continue;
case '{':
text_seen = 1;
copyact('{','}',flag,level+1);
continue;
case '[':
text_seen = 1;
copyact('[',']',flag,level+1);
continue;
case '/':
ch = input();
unput(ch);
if (ch == '*') {
putc('/', f);
skipcomment(1);
continue;
}
ch = '/';
text_seen = 1;
break;
case ';':
case ',':
if (! level && text_seen) {
text_seen = 0;
nparams++;
if (ch == ',' && (flag & 2)) {
warning(linecount, "Parameters may not be separated with a ','");
ch = ';';
}
}
break;
case '\'':
case '"' :
/*
* watch out for brackets in strings, they do not
* count !
*/
text_seen = 1;
match = ch;
putc(ch,f);
while((ch = input())) {
if (ch == match) break;
if (ch == '\\') {
putc(ch,f);
ch = input();
}
if (ch == '\n') {
error(linecount,"Newline in string");
unput(match);
}
putc(ch,f);
}
if (ch == match) break;
/* Fall through */
case EOF :
if (!level) error(saved,"Action does not terminate");
strip_grammar = sav_strip;
return;
default:
if (c_class[ch] != ISSPA) text_seen = 1;
}
putc(ch,f);
}
}
scanner() {
/*
* Lexical analyser, what else
@ -108,7 +234,11 @@ scanner() {
register char *p = ltext;
int reserved = 0; /* reserved word? */
char *max = &ltext[LTEXTSZ - 1];
static int nextexpr;
int expect_expr = nextexpr;
long off;
nextexpr = 0;
if (savedtok.t_tokno) {
/* A token has been inserted.
* Now deliver the last lextoken again
@ -127,6 +257,21 @@ scanner() {
}
# endif
switch(c_class[ch]) {
case ISACT :
if (ch == '{') {
copyact('{', '}', in_production, 0);
return C_ACTION;
}
assert(ch == '(');
if (expect_expr) {
copyact('(', ')', 1, 0);
return C_EXPR;
}
off = ftell(fact);
copyact('(', ')', in_production != 0 ? 0 : 2, 0);
if (nparams == 0) fseek(fact, off, 0);
lextoken.t_num = nparams;
return C_PARAMS;
case ISLIT :
for (;;) {
ch = input();
@ -177,7 +322,7 @@ scanner() {
unput(ch);
*p = '\0';
if (reserved) { /*
* Now search for the keyword
* Now search for the keyword
*/
register p_keyw w;
@ -187,6 +332,10 @@ scanner() {
/*
* Return token number.
*/
if (w->w_value == C_IF ||
w->w_value == C_WHILE) {
nextexpr = 1;
}
return w->w_value;
}
w++;
@ -208,11 +357,11 @@ input() {
*/
register c;
if (c = backupc) {
if (c = backupc) {
/* Last char was "unput()". Deliver it again
*/
backupc = 0;
return c;
return c;
}
if ((c = getc(finput)) == EOF) {
nonline = 0;
@ -337,7 +486,7 @@ cpy(s,p,inserted) register string p; {
register string t = 0;
switch(s) {
case C_IDENT :
case C_IDENT :
if (!inserted) t = lextoken.t_string;
else t = "identifier";
break;
@ -353,7 +502,7 @@ cpy(s,p,inserted) register string p; {
t = "literal";
break;
case EOFILE :
t = "endoffile";
t = "end-of-file";
break;
}
if (!t && (t = vallookup(s))) {
@ -382,13 +531,15 @@ cpy(s,p,inserted) register string p; {
case '\r' : *p++ = 'r'; break;
case '\t' : *p++ = 't'; break;
default : *p++='0'+((s&0377)>>6); *p++='0'+((s>>3)&07);
*p++='0'+(s&07);
*p++='0'+(s&07);
}
}
*p++ = '\'';
return p;
}
string strcpy();
LLmessage(d) {
/*
* d is either 0, in which case the current token has been deleted,
@ -400,9 +551,16 @@ LLmessage(d) {
nerrors++;
s = buf;
if (d == 0) {
s = cpy(LLsymb,s,0);
if (d < 0) {
strcpy(buf, "end-of-file expected");
}
else if (d == 0) {
#ifdef LLNONCORR
t = " unexpected";
#else
t = " deleted";
#endif
s = cpy(LLsymb,s,0);
do *s++ = *t; while (*t++);
} else {
s = cpy(d,s,1);
@ -411,12 +569,7 @@ LLmessage(d) {
s = cpy(LLsymb,s,0);
*s = '\0';
}
error(linecount, "%s", buf);
/* Don't change this line to
* error(linecount, buf).
* The string in "buf" might contain '%' ...
*/
if (d) { /*
if (d > 0) { /*
* Save the current token and make up some
* attributes for the inserted token
*/
@ -426,5 +579,17 @@ LLmessage(d) {
else if (d == C_LITERAL) lextoken.t_string = "dummy_literal";
else if (d == C_NUMBER) lextoken.t_num = 1;
}
#ifdef LLNONCORR
else
#endif
error(linecount, "%s", buf);
/* Don't change this line to
* error(linecount, buf).
* The string in "buf" might contain '%' ...
*/
#ifdef LLNONCORR
in_production = 1;
/* To prevent warnings from copyact */
#endif
}
}

View file

@ -40,12 +40,20 @@ typedef struct token {
* structure for the grammar elements
*/
typedef struct gram {
short x; /* for lay-out see comment below */
short g_lineno; /* element found on this line number */
int x; /* for lay-out see comment below */
int g_lineno; /* element found on this line number */
#ifdef NON_CORRECTING
int g_erroneous; /* 1 if element declared erroneous */
#endif
union {
int g_index;
struct term * g_term;
struct link * g_link;
#ifdef NON_CORRECTING
/* If this is an action with a %substart g_subparse
points to the list of startsymbols of the subparser */
struct ff_firsts *g_subparse;
#endif
} g_i;
} t_gram,*p_gram;
@ -78,7 +86,10 @@ typedef struct gram {
# define g_setterm(p,s) ((p)->g_i.g_term = (s))
# define g_setlink(p,s) ((p)->g_i.g_link = (s))
# define g_setnpar(p,s) { assert(((unsigned)(s))<=017);(p)->x=((p)->x&~0170)|((s)<<3);}
#ifdef NON_CORRECTING
# define g_getsubparse(p) ((p)->g_i.g_subparse)
# define g_setsubparse(p,s) ((p)->g_i.g_subparse = (s))
#endif
/*
* Some constants to communicate with the symbol table search routine
*/
@ -101,7 +112,7 @@ typedef struct gram {
* nonterminal structure
*/
typedef struct {
short n_flags; /* low order four bits are reserved
int n_flags; /* low order four bits are reserved
* the parameter count
*/
# define getntparams(p) ((p)->n_flags&017)
@ -110,7 +121,7 @@ typedef struct {
# define RECURSIVE 02000 /* Set if the default rule is recursive */
# define PARAMS 04000 /* tells if a nonterminal has parameters */
# define EMPTY 010000 /* tells if a nonterminal produces empty */
# define LOCALS 020000 /* local declarations ? */
# define LOCALS 020000 /* local declarations ? */
# define REACHABLE 040000 /* can this nonterminal be reached ? */
# define VERBOSE 0100000 /* Set if in LL.output file */
char n_insafety;
@ -119,8 +130,8 @@ typedef struct {
# define setntsafe(p,i) {assert(((unsigned)(i))<=NOSAFETY);(p)->n_insafety=(i);}
# define getntout(p) ((p)->n_outsafety)
# define setntout(p,i) {assert(((unsigned)(i))<=NOSAFETY);(p)->n_outsafety=(i);}
short n_count; /* pieces of code before this rule */
short n_lineno; /* declared on line ... */
int n_count; /* pieces of code before this rule */
int n_lineno; /* declared on line ... */
p_gram n_rule; /* pointer to right hand side of rule */
union {
p_set n_f; /* ptr to "first" set */
@ -131,6 +142,10 @@ typedef struct {
} n_x;
# define n_first n_x.n_f
# define n_string n_x.n_s
#ifdef NON_CORRECTING
p_set n_nc_first; /* Pointer to non-corr first set */
p_set n_nc_follow; /* Pointer to non-corr follow set */
#endif
p_set n_follow; /* pointer to the "follow" set */
p_set n_contains; /* pointer to symbols that can be produced */
string n_name; /* name of nonterminal */
@ -138,7 +153,7 @@ typedef struct {
long n_off; /* index of parameters in action file */
} t_nont, *p_nont;
/*
/*
* hash table structure
*/
typedef struct h_entry {
@ -161,13 +176,16 @@ typedef struct link {
*/
p_gram l_rule; /* pointer to this rule */
p_set l_symbs; /* set, when to take this rule */
#ifdef NON_CORRECTING
p_set l_nc_symbs;
#endif
p_set l_others; /* set, when to take another rule */
} t_link, *p_link;
/*
* Structure for a repitition specification
*/
typedef short t_reps,*p_reps;
typedef int t_reps,*p_reps;
# define FIXED 00 /* a fixed number */
# define STAR 01 /* 0 or more times */
@ -187,7 +205,7 @@ typedef short t_reps,*p_reps;
*/
typedef struct term {
t_reps t_repeats;
short t_flags; /* Low order three bits for safety */
int t_flags; /* Low order three bits for safety */
# define gettout(q) ((q)->t_flags&07)
# define settout(q,i) {assert(((unsigned)(i))<=NOSAFETY);(q)->t_flags&=~07;(q)->t_flags|=i;}
# define PERSISTENT 010 /* Set if this term has %persistent */
@ -199,6 +217,10 @@ typedef struct term {
p_gram t_rule; /* pointer to this term */
p_set t_follow; /* set of followers */
p_set t_first; /* set of firsts */
#ifdef NON_CORRECTING
p_set t_nc_first; /* set of non corr firsts */
p_set t_nc_follow; /* set of non corr followers */
#endif
p_set t_contains; /* contains set */
} t_term, *p_term;