ack/util/LLgen/src/compute.c

922 lines
18 KiB
C
Raw Normal View History

/* Copyright (c) 1991 by the Vrije Universiteit, Amsterdam, the Netherlands.
* All rights reserved.
1985-10-02 22:20:04 +00:00
*/
/*
* L L G E N
*
* An Extended LL(1) Parser Generator
*
* Author : Ceriel J.H. Jacobs
*/
/*
* compute.c
* Defines routines to compute FIRST, FOLLOW etc.
* Also checks the continuation grammar from the specified grammar.
*/
# include "types.h"
# include "extern.h"
# include "sets.h"
# include "assert.h"
# include "io.h"
# ifndef NORCSID
static string rcsid = "$Header$";
1985-10-02 22:20:04 +00:00
# endif
p_set get_set();
typedef struct lngth {
/* Structure used to compute the shortest possible
* length of a terminal production of a rule.
* In case of a tie, the second field is used.
*/
int cnt;
int val;
} t_length, *p_length;
1985-10-02 22:20:04 +00:00
/* Defined in this file : */
extern do_compute();
STATIC createsets();
1985-10-02 22:20:04 +00:00
STATIC walk();
STATIC co_trans();
STATIC int nempty();
1985-10-02 22:20:04 +00:00
extern empty();
STATIC int nfirst();
1985-10-02 22:20:04 +00:00
STATIC first();
STATIC int nfollow();
1985-10-02 22:20:04 +00:00
STATIC follow();
STATIC co_dirsymb();
STATIC co_others();
STATIC do_lengthcomp();
STATIC complength();
STATIC add();
STATIC int compare();
STATIC setdefaults();
1985-10-02 22:20:04 +00:00
STATIC do_contains();
STATIC contains();
STATIC int nsafes();
1985-10-02 22:20:04 +00:00
STATIC int do_safes();
do_compute() {
/*
* Does all the work, by calling other routines (divide and conquer)
*/
register p_nont p;
register p_start st;
createsets();
co_trans(nempty); /* Which nonterminals produce empty? */
co_trans(nfirst); /* Computes first sets */
/*
* Compute FOLLOW sets.
* First put EOFILE in the follow set of the start nonterminals.
*/
for (st = start; st; st = st->ff_next) {
p = &nonterms[st->ff_nont];
PUTIN(p->n_follow,0);
}
co_trans(nfollow);
/*
* Compute the sets which determine which alternative to choose
* in case of a choice
*/
for (p = nonterms; p < maxnt; p++) {
co_dirsymb(p->n_follow,p->n_rule);
}
/*
* Compute the minimum length of productions of nonterminals,
* and then determine the default choices
*/
do_lengthcomp();
/*
* Compute the contains sets
*/
for (p = nonterms; p < maxnt; p++) do_contains(p);
for (p = nonterms; p < maxnt; p++) contains(p->n_rule, (p_set) 0);
/*
* Compute the safety of each nonterminal and term.
* The safety gives an answer to the question whether a scan is done,
* and how it should be handled.
*/
for (p = nonterms; p < maxnt; p++) {
/*
* Don't know anything yet
*/
setntsafe(p, NOSAFETY);
setntout(p, NOSAFETY);
}
for (st = start; st; st = st->ff_next) {
/*
* But start symbols are called with lookahead done
*/
p = &nonterms[st->ff_nont];
setntsafe(p,SCANDONE);
}
co_trans(nsafes);
# ifndef NDEBUG
if (debug) {
fputs("Safeties:\n", stderr);
for (p = nonterms; p < maxnt; p++) {
fprintf(stderr, "%s\t%d\t%d\n",
p->n_name,
getntsafe(p),
getntout(p));
}
}
# endif
}
STATIC
1985-10-02 22:20:04 +00:00
createsets() {
/*
1991-11-26 15:38:41 +00:00
* Allocate space for the sets. Also determine which files use
* which nonterminals, and determine which nonterminals can be
* made static.
1985-10-02 22:20:04 +00:00
*/
register p_nont p;
1991-11-26 15:38:41 +00:00
register p_file f;
register p_start st;
register int i;
int n = NINTS(NBYTES(nnonterms));
p_mem alloc();
1985-10-02 22:20:04 +00:00
1991-11-26 15:38:41 +00:00
for (f = files; f < maxfiles; f++) {
register p_set s;
f->f_used = s = (p_set) alloc((unsigned)n*sizeof(*(f->f_used)));
for (i = n; i; i--) *s++ = 0;
for (i = f->f_nonterminals; i != -1; i = p->n_next) {
p = &nonterms[i];
p->n_flags |= GENSTATIC;
p->n_first = get_set();
p->n_follow = get_set();
walk(f->f_used, p->n_rule);
}
}
for (f = files; f < maxfiles; f++) {
for (i = f->f_nonterminals; i != -1; i = p->n_next) {
register p_file f2;
p = &nonterms[i];
for (f2 = files; f2 < maxfiles; f2++) {
if (f2 != f && IN(f2->f_used, i)) {
p->n_flags &= ~GENSTATIC;
}
}
}
}
for (st = start; st; st = st->ff_next) {
nonterms[st->ff_nont].n_flags &= ~GENSTATIC;
1985-10-02 22:20:04 +00:00
}
}
STATIC
1991-11-26 15:38:41 +00:00
walk(u, p) p_set u; register p_gram p; {
1985-10-02 22:20:04 +00:00
/*
* Walk through the grammar rule p, allocating sets
*/
for (;;) {
switch (g_gettype(p)) {
case TERM : {
register p_term q;
1987-05-12 18:23:09 +00:00
q = g_getterm(p);
q->t_first = get_set();
q->t_follow = get_set();
1991-11-26 15:38:41 +00:00
walk(u, q->t_rule);
1985-10-02 22:20:04 +00:00
break; }
case ALTERNATION : {
register p_link l;
1987-05-12 18:23:09 +00:00
l = g_getlink(p);
l->l_symbs = get_set();
l->l_others = get_set();
1991-11-26 15:38:41 +00:00
walk(u, l->l_rule);
break; }
case NONTERM : {
register int i = g_getcont(p);
PUTIN(u, i);
1985-10-02 22:20:04 +00:00
break; }
case EORULE :
return;
}
p++;
}
}
STATIC
co_trans(fc) int (*fc)(); {
register p_nont p;
register int change;
1985-10-02 22:20:04 +00:00
do {
1985-10-02 22:20:04 +00:00
change = 0;
for (p = nonterms; p < maxnt; p++) {
if ((*fc)(p)) change = 1;
1985-10-02 22:20:04 +00:00
}
} while (change);
}
STATIC int
nempty(p) register p_nont p; {
if (!(p->n_flags & EMPTY) && empty(p->n_rule)) {
p->n_flags |= EMPTY;
return 1;
1985-10-02 22:20:04 +00:00
}
return 0;
1985-10-02 22:20:04 +00:00
}
empty(p) register p_gram p; {
/*
* Does the rule pointed to by p produce empty ?
*/
for (;;) {
switch (g_gettype(p)) {
case EORULE :
return 1;
case TERM : {
register p_term q;
1987-05-12 18:23:09 +00:00
q = g_getterm(p);
if (r_getkind(q) == STAR
|| r_getkind(q) == OPT
1985-10-02 22:20:04 +00:00
|| empty(q->t_rule) ) break;
return 0; }
case ALTERNATION :
1987-05-12 18:23:09 +00:00
if (empty(g_getlink(p)->l_rule)) {
1985-10-02 22:20:04 +00:00
return 1;
}
if (g_gettype(p+1) == EORULE) return 0;
break;
case NONTERM :
1987-05-12 18:23:09 +00:00
if (nonterms[g_getcont(p)].n_flags & EMPTY) {
1985-10-02 22:20:04 +00:00
break;
}
/* Fall through */
case LITERAL :
1985-10-02 22:20:04 +00:00
case TERMINAL :
return 0;
}
p++;
}
}
STATIC int
nfirst(p) register p_nont p; {
return first(p->n_first, p->n_rule, 0);
1985-10-02 22:20:04 +00:00
}
STATIC
first(setp,p,flag) p_set setp; register p_gram p; {
/*
* Compute the FIRST set of rule p.
* If flag = 0, also the first sets for terms and alternations in
* the rule p are computed.
* The FIRST set is put in setp.
* return 1 if the set refered to by "setp" changed
1985-10-02 22:20:04 +00:00
*/
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;
1987-05-12 18:23:09 +00:00
q = g_getterm(p);
if (flag == 0) {
if (first(q->t_first,q->t_rule,0))/*nothing*/;
}
if (!noenter) s |= setunion(setp,q->t_first);
1985-10-02 22:20:04 +00:00
p++;
if (r_getkind(q) == STAR ||
r_getkind(q) == OPT ||
empty(q->t_rule)) continue;
1985-10-02 22:20:04 +00:00
break; }
case ALTERNATION : {
register p_link l;
1987-05-12 18:23:09 +00:00
l = g_getlink(p);
if (flag == 0) {
if (first(l->l_symbs,l->l_rule,0))/*nothing*/;
}
1985-10-02 22:20:04 +00:00
if (noenter == 0) {
s |= setunion(setp,l->l_symbs);
1985-10-02 22:20:04 +00:00
}
if (g_gettype(p+1) == EORULE) return s;
}
/* Fall Through */
case ACTION :
p++;
continue;
case LITERAL :
1985-10-02 22:20:04 +00:00
case TERMINAL :
if ((noenter == 0) && !IN(setp,g_getcont(p))) {
s = 1;
PUTIN(setp,g_getcont(p));
}
p++;
break;
case NONTERM : {
register p_nont n;
1987-05-12 18:23:09 +00:00
n = &nonterms[g_getcont(p)];
1985-10-02 22:20:04 +00:00
if (noenter == 0) {
s |= setunion(setp,n->n_first);
1987-05-12 18:23:09 +00:00
if (ntneeded) NTPUTIN(setp,g_getcont(p));
1985-10-02 22:20:04 +00:00
}
p++;
if (n->n_flags & EMPTY) continue;
break; }
}
if (flag == 0) {
noenter = 1;
continue;
}
return s;
}
}
STATIC int
nfollow(p) register p_nont p; {
return follow(p->n_follow, p->n_rule);
1985-10-02 22:20:04 +00:00
}
STATIC
follow(setp,p) p_set setp; register p_gram p; {
1985-10-02 22:20:04 +00:00
/*
* 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.
1985-10-02 22:20:04 +00:00
*/
register s; /* Will gather return value */
s = 0;
for (;;) {
switch (g_gettype(p)) {
case EORULE :
return s;
case TERM : {
register p_term q;
1987-05-12 18:23:09 +00:00
q = g_getterm(p);
1985-10-02 22:20:04 +00:00
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_follow,setp);
1985-10-02 22:20:04 +00:00
}
/*
* Everything that can start the rest of the rule
* can follow the term
*/
s |= first(q->t_follow,p+1,1);
if (r_getkind(q) == STAR ||
r_getkind(q) == PLUS ||
r_getnum(q) ) {
1985-10-02 22:20:04 +00:00
/*
* If the term involves a repetition
* of possibly more than one,
* everything that can start the term
* can also follow it.
*/
s |= follow(q->t_first,q->t_rule);
1985-10-02 22:20:04 +00:00
}
/*
* Now propagate the set computed sofar
*/
s |= follow(q->t_follow, q->t_rule);
1985-10-02 22:20:04 +00:00
break; }
case ALTERNATION :
/*
* Just propagate setp
*/
1987-05-12 18:23:09 +00:00
s |= follow(setp,g_getlink(p)->l_rule);
1985-10-02 22:20:04 +00:00
break;
case NONTERM : {
register p_nont n;
1987-05-12 18:23:09 +00:00
n = &nonterms[g_getcont(p)];
s |= first(n->n_follow,p+1,1);
1985-10-02 22:20:04 +00:00
if (empty(p+1)) {
/*
* If the rest of p can produce empty,
* everything that follows p can follow
* the nonterminal
*/
s |= setunion(n->n_follow,setp);
1985-10-02 22:20:04 +00:00
}
break; }
}
p++;
}
}
STATIC
co_dirsymb(setp,p) p_set setp; register p_gram p; {
/*
* Walk the rule p, doing the work for alternations
*/
register p_gram s = 0;
for (;;) {
switch (g_gettype(p)) {
case EORULE :
return;
case TERM : {
register p_term q;
1987-05-12 18:23:09 +00:00
q = g_getterm(p);
1985-10-02 22:20:04 +00:00
co_dirsymb(q->t_follow,q->t_rule);
break; }
case ALTERNATION : {
register p_link l;
/*
* Save first alternative
*/
if (!s) s = p;
1987-05-12 18:23:09 +00:00
l = g_getlink(p);
1985-10-02 22:20:04 +00:00
co_dirsymb(setp,l->l_rule);
if (empty(l->l_rule)) {
/*
* If the rule can produce empty, everything
* that follows it can also start it
*/
setunion(l->l_symbs,setp);
1985-10-02 22:20:04 +00:00
}
if (g_gettype(p+1) == EORULE) {
/*
* Every alternation is implemented as a
* choice between two alternatives :
* this one or one of the following.
* The l_others set will contain the starters
* of the other alternatives
*/
co_others(s);
return;
} }
}
p++;
}
}
STATIC
co_others(p) register p_gram p; {
/*
* compute the l_others-sets for the list of alternatives
* indicated by p
*/
register p_link l1,l2;
1987-05-12 18:23:09 +00:00
l1 = g_getlink(p);
1985-10-02 22:20:04 +00:00
p++;
1987-05-12 18:23:09 +00:00
l2 = g_getlink(p);
setunion(l1->l_others,l2->l_symbs);
1985-10-02 22:20:04 +00:00
if (g_gettype(p+1) != EORULE) {
/*
* First compute l2->l_others
*/
co_others(p);
/*
* and then l1->l_others
*/
setunion(l1->l_others,l2->l_others);
1985-10-02 22:20:04 +00:00
}
}
static p_length length;
# define INFINITY 32767
STATIC
ncomplength(p)
register p_nont p;
{
register p_length pl = &length[p - nonterms];
int x = pl->cnt;
pl->cnt = -1;
complength(p->n_rule, pl);
return pl->cnt < INFINITY && x == INFINITY;
}
1985-10-02 22:20:04 +00:00
STATIC
do_lengthcomp() {
1985-10-02 22:20:04 +00:00
/*
* 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
1987-11-27 11:04:07 +00:00
* - the number of this alternative
* - a crude measure of the number of terms and nonterminals in the
* production of this shortest string.
1985-10-02 22:20:04 +00:00
*/
register p_length pl;
register p_nont p;
p_mem alloc();
length = (p_length) alloc((unsigned) (nnonterms * sizeof(*length)));
for (pl = &length[nnonterms-1]; pl >= length; pl--) {
pl->val = pl->cnt = INFINITY;
1985-10-02 22:20:04 +00:00
}
co_trans(ncomplength);
pl = length;
for (p = nonterms; p < maxnt; p++, pl++) {
if (pl->cnt == INFINITY) {
p->n_flags |= RECURSIVE;
}
setdefaults(p->n_rule);
1985-10-02 22:20:04 +00:00
}
free ((p_mem) length);
}
1985-10-02 22:20:04 +00:00
STATIC
complength(p,le) register p_gram p; p_length le; {
1985-10-02 22:20:04 +00:00
/*
* Walk grammar rule p, computing minimum lengths
1985-10-02 22:20:04 +00:00
*/
register p_link l;
register p_term q;
t_length i;
t_length X;
1987-11-27 11:04:07 +00:00
int cnt = 0;
1985-10-02 22:20:04 +00:00
X.cnt = 0;
X.val = 0;
1985-10-02 22:20:04 +00:00
for (;;) {
switch (g_gettype(p)) {
case LITERAL :
case TERMINAL :
1987-11-27 11:04:07 +00:00
add(&X, 1, 0);
break;
1985-10-02 22:20:04 +00:00
case ALTERNATION :
X.cnt = INFINITY;
X.val = INFINITY;
while (g_gettype(p) != EORULE) {
1987-11-27 11:04:07 +00:00
cnt++;
1987-05-12 18:23:09 +00:00
l = g_getlink(p);
complength(l->l_rule,&i);
1987-11-27 11:04:07 +00:00
i.val += cnt;
if (l->l_flag & DEF) {
X = i;
break;
}
if (compare(&i, &X) < 0) {
X = i;
}
p++;
1985-10-02 22:20:04 +00:00
}
/* Fall through */
case EORULE :
le->cnt = X.cnt;
le->val = X.val;
return;
case TERM : {
register int rep;
1987-05-12 18:23:09 +00:00
q = g_getterm(p);
rep = r_getkind(q);
1987-11-27 11:04:07 +00:00
X.val += 1;
if ((q->t_flags&PERSISTENT) ||
rep==FIXED || rep==PLUS) {
complength(q->t_rule,&i);
add(&X, i.cnt, i.val);
if (rep == FIXED && r_getnum(q) > 0) {
for (rep = r_getnum(q) - 1;
rep > 0; rep--) {
add(&X, i.cnt, i.val);
}
}
1985-10-02 22:20:04 +00:00
}
break; }
case NONTERM : {
1987-05-12 18:23:09 +00:00
int nn = g_getcont(p);
register p_length pl = &length[nn];
int x = pl->cnt;
if (x == INFINITY) {
pl->cnt = -1;
complength(nonterms[nn].n_rule,pl);
x = pl->cnt;
}
else if (x == -1) x = INFINITY;
add(&X, x, pl->val);
1987-11-27 11:04:07 +00:00
X.val += 1;
}
1985-10-02 22:20:04 +00:00
}
p++;
}
}
STATIC
add(a, c, v) register p_length a; {
1985-10-02 22:20:04 +00:00
if (a->cnt == INFINITY || c == INFINITY) {
a->cnt = INFINITY;
return;
}
1987-11-27 11:04:07 +00:00
a->val += v;
a->cnt += c;
}
STATIC int
compare(a, b) register p_length a, b; {
if (a->cnt != b->cnt) return a->cnt - b->cnt;
return a->val - b->val;
}
STATIC
setdefaults(p) register p_gram p; {
for (;;) {
switch(g_gettype(p)) {
case EORULE:
return;
case TERM:
1987-05-12 18:23:09 +00:00
setdefaults(g_getterm(p)->t_rule);
break;
case ALTERNATION: {
register p_link l, l1;
1987-11-27 11:04:07 +00:00
int temp = 0, temp1, cnt = 0;
t_length count, i;
count.cnt = INFINITY;
count.val = INFINITY;
1987-05-12 18:23:09 +00:00
l1 = g_getlink(p);
do {
1987-11-27 11:04:07 +00:00
cnt++;
1987-05-12 18:23:09 +00:00
l = g_getlink(p);
complength(l->l_rule,&i);
1987-11-27 11:04:07 +00:00
i.val += cnt;
if (l->l_flag & DEF) temp = 1;
temp1 = compare(&i, &count);
if (temp1 < 0 ||
(temp1 == 0 && l1->l_flag & AVOIDING)) {
l1 = l;
count = i;
}
setdefaults(l->l_rule);
p++;
} while (g_gettype(p) != EORULE);
if (!temp) {
/* No user specified default */
l1->l_flag |= DEF;
}
return; }
}
p++;
}
1985-10-02 22:20:04 +00:00
}
STATIC
do_contains(n) register p_nont n; {
/*
* Compute the total set of symbols that nonterminal n can
* produce
*/
if (n->n_contains == 0) {
n->n_contains = get_set();
1985-10-02 22:20:04 +00:00
contains(n->n_rule,n->n_contains);
/*
* If the rule can produce empty, delete all symbols that
* can follow the rule as well as be in the rule.
* This is needed because the contains-set may only contain
* symbols that are guaranteed to be eaten by the rule!
* Otherwise, the generated parser may loop forever
*/
if (n->n_flags & EMPTY) {
setminus(n->n_contains,n->n_follow);
1985-10-02 22:20:04 +00:00
}
/*
* But the symbols that can start the rule are always
* eaten
*/
setunion(n->n_contains,n->n_first);
1985-10-02 22:20:04 +00:00
}
}
STATIC
contains(p,set) register p_gram p; register p_set set; {
/*
* Does the real computation of the contains-sets
*/
for (;;) {
switch (g_gettype(p)) {
case EORULE :
return;
case TERM : {
register p_term q;
int rep;
1985-10-02 22:20:04 +00:00
1987-05-12 18:23:09 +00:00
q = g_getterm(p);
rep = r_getkind(q);
1985-10-02 22:20:04 +00:00
if ((q->t_flags & PERSISTENT) ||
rep == PLUS || rep == FIXED) {
1985-10-02 22:20:04 +00:00
/*
* In these cases, the term belongs to the
* continuation grammar.
* Otherwise, q->t_contains is just
* q->t_first
*/
if (!q->t_contains) {
q->t_contains = get_set();
1985-10-02 22:20:04 +00:00
}
contains(q->t_rule,q->t_contains);
if (rep != FIXED || empty(q->t_rule)) {
setminus(q->t_contains,q->t_follow);
1985-10-02 22:20:04 +00:00
}
setunion(q->t_contains,q->t_first);
1985-10-02 22:20:04 +00:00
} else {
contains(q->t_rule, (p_set) 0);
q->t_contains = q->t_first;
}
if (set) setunion(set,q->t_contains);
1985-10-02 22:20:04 +00:00
break; }
case NONTERM : {
register p_nont n;
1987-05-12 18:23:09 +00:00
n = &nonterms[g_getcont(p)];
1985-10-02 22:20:04 +00:00
do_contains(n);
if (set) {
setunion(set, n->n_contains);
1987-05-12 18:23:09 +00:00
if (ntneeded) NTPUTIN(set, g_getcont(p));
}
1985-10-02 22:20:04 +00:00
break; }
case ALTERNATION : {
register p_link l;
1987-05-12 18:23:09 +00:00
l = g_getlink(p);
1985-10-02 22:20:04 +00:00
contains(l->l_rule,
(l->l_flag & DEF) ? set : (p_set) 0);
break; }
case LITERAL :
1985-10-02 22:20:04 +00:00
case TERMINAL : {
register hulp;
if (set) {
hulp = g_getcont(p);
assert(hulp < ntokens);
1985-10-02 22:20:04 +00:00
PUTIN(set,hulp);
}}
}
p++;
}
}
STATIC int nsafes(p) register p_nont p; {
int ch;
register int i;
ch = 0;
i = getntsafe(p);
if (i != NOSAFETY) {
i = do_safes(p->n_rule, i, &ch);
if (i < SCANDONE) i = SCANDONE;
/* After a nonterminal, we only know whether a scan was done
or not
*/
if (getntout(p) != i) {
ch = 1;
setntout(p,i);
1985-10-02 22:20:04 +00:00
}
}
return ch;
1985-10-02 22:20:04 +00:00
}
STATIC int
do_safes(p,safe,ch) register p_gram p; register int *ch; {
1985-10-02 22:20:04 +00:00
/*
* Walk the grammar rule, doing the computation described in the
* comment of the procedure above this one.
*/
int retval;
1985-10-02 22:20:04 +00:00
for (;;) {
switch (g_gettype(p)) {
case ACTION:
p++;
continue;
case LITERAL:
1985-10-02 22:20:04 +00:00
case TERMINAL:
1985-10-02 22:49:16 +00:00
safe = NOSCANDONE;
1985-10-02 22:20:04 +00:00
break;
case TERM : {
register p_term q;
int i,rep;
1987-05-12 18:23:09 +00:00
q = g_getterm(p);
i = r_getnum(q);
rep = r_getkind(q);
1985-10-02 22:20:04 +00:00
retval = do_safes(q->t_rule,
t_safety(rep,i,q->t_flags&PERSISTENT,safe),ch);
settout(q, retval);
safe = t_after(rep, i, retval);
1985-10-02 22:20:04 +00:00
break; }
case ALTERNATION : {
register p_link l;
register int i;
1985-10-02 22:20:04 +00:00
retval = -1;
1985-10-02 22:20:04 +00:00
while (g_gettype(p) == ALTERNATION) {
1987-05-12 18:23:09 +00:00
l = g_getlink(p);
if (safe > SAFE && (l->l_flag & DEF)) {
i = do_safes(l->l_rule,SAFESCANDONE,ch);
1985-10-02 22:20:04 +00:00
}
else i = do_safes(l->l_rule,SAFE,ch);
if (retval == -1) retval = i;
else if (i != retval) {
if (i == NOSCANDONE ||
retval == NOSCANDONE) {
retval = SCANDONE;
}
else if (i > retval) retval = i;
}
1985-10-02 22:20:04 +00:00
p++;
}
return retval; }
case NONTERM : {
register p_nont n;
register int nsafe, osafe;
1985-10-02 22:20:04 +00:00
1987-05-12 18:23:09 +00:00
n = &nonterms[g_getcont(p)];
nsafe = getntsafe(n);
1985-10-02 22:49:16 +00:00
osafe = safe;
safe = getntout(n);
if (safe == NOSAFETY) safe = SCANDONE;
1985-10-02 22:49:16 +00:00
if (osafe == nsafe) break;
1985-10-02 22:20:04 +00:00
if (nsafe == NOSAFETY) {
*ch = 1;
setntsafe(n, osafe);
1985-10-02 22:20:04 +00:00
break;
}
1985-10-02 22:49:16 +00:00
if (osafe == NOSCANDONE || nsafe == NOSCANDONE) {
1985-10-02 22:20:04 +00:00
if (nsafe != SCANDONE) {
*ch = 1;
setntsafe(n, SCANDONE);
1985-10-02 22:20:04 +00:00
}
break;
}
1985-10-02 22:49:16 +00:00
if (osafe > nsafe) {
setntsafe(n, osafe);
*ch = 1;
1985-10-02 22:20:04 +00:00
}
break; }
case EORULE :
return safe;
1985-10-02 22:20:04 +00:00
}
p++;
}
}
t_safety(rep, count, persistent, safety) {
if (safety == NOSCANDONE) safety = SCANDONE;
1985-10-02 22:20:04 +00:00
switch(rep) {
default:
assert(0);
case OPT:
if (!persistent || safety < SAFESCANDONE) return SAFE;
1985-10-02 22:20:04 +00:00
return SAFESCANDONE;
case STAR:
if (persistent) return SAFESCANDONE;
return SAFE;
case PLUS:
if (persistent) {
if (safety > SAFESCANDONE) return safety;
return SAFESCANDONE;
}
return safety;
1985-10-02 22:20:04 +00:00
case FIXED:
if (!count) return safety;
1985-10-02 22:20:04 +00:00
return SCANDONE;
}
/* NOTREACHED */
}
1985-10-02 22:49:16 +00:00
t_after(rep, count, outsafety) {
1985-10-02 22:49:16 +00:00
if (count == 0 && (rep == STAR || rep == PLUS)) {
return SAFESCANDONE;
}
if (rep != FIXED) {
if (outsafety <= SAFESCANDONE) return SAFESCANDONE;
return SCANDONE;
}
return outsafety;
1985-10-02 22:49:16 +00:00
}