ack/util/LLgen/src/check.c

475 lines
9.7 KiB
C

/*
* (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands.
* See the copyright notice in the ACK home directory, in the file "Copyright".
*
*/
/*
* L L G E N
*
* An Extended LL(1) Parser Generator
*
* Author : Ceriel J.H. Jacobs
*/
/*
* check.c
* Several routines to perform checks and printouts
*/
# include "types.h"
# include "extern.h"
# include "io.h"
# include "sets.h"
# include "assert.h"
# ifndef NORCSID
static string rcsid1 = "$Header$";
# endif
static string c_first = "> firstset ";
static string c_contains = "> containset ";
static string c_follow = "> followset ";
p_set setalloc();
static int level;
/* In this file are defined : */
extern conflchecks();
STATIC prline();
STATIC printset();
STATIC int check();
STATIC moreverbose();
STATIC prrule();
STATIC cfcheck();
STATIC resolve();
STATIC propagate();
STATIC spaces();
conflchecks() {
/*
* Check for conflicts, that is,
* in a repeating term, the FIRST and FOLLOW must be disjunct,
* unless there is a disambiguating condition.
* in an alternation, the sets that determine the direction to take,
* must be disjunct.
*/
register p_nont p;
register p_order s;
p_file x = files;
f_input = x->f_name;
if (verbose >= 3) {
for (p = nonterms; p < maxnt; p++) p->n_flags |= VERBOSE;
}
if (verbose) {
if ((fout = fopen(f_out,"w")) == NULL) fatal(1,e_noopen,f_out);
}
/*
* Check the rules in the order in which they are declared,
* and input file by input file, to give proper error messages
*/
for (; x < maxfiles; x++) {
f_input = x->f_name;
for (s = x->f_list; s; s = s->o_next) {
p = &nonterms[s->o_index];
if (check(p->n_rule)) p->n_flags |= VERBOSE;
}
}
for (x = files; x < maxfiles; x++) {
f_input = x->f_name;
for (s = x->f_list; s; s = s->o_next) {
p = &nonterms[s->o_index];
if (p->n_flags & RECURSIVE) {
error(p->n_lineno,
"Recursion in default for nonterminal %s",
p->n_name);
}
/*
* If a printout is needed for this rule in
* LL.output, just do it
*/
if (verbose && (p->n_flags & VERBOSE)) {
fprintf(fout,"\n%s :\n",p->n_name);
printset(p->n_first,c_first);
printset(p->n_contains,c_contains);
printset(p->n_follow,c_follow);
fprintf(fout,"> rule%s\n\t",
p->n_flags&EMPTY ? "\t(EMPTY producing)" : "");
level = 8;
prrule(p->n_rule);
level = 0;
prline("\n");
}
/*
* Now, the conflicts may be resolved
*/
resolve(p->n_rule);
}
}
if (verbose) fclose(fout);
}
STATIC
prline(s) char *s; {
fputs(s, fout);
spaces();
}
STATIC
printset(p,s) register p_set p; string s; {
/*
* Print the elements of a set
*/
register int i;
register int j;
register p_token pt;
string name;
int k;
int hulp;
k = strlen(s) + 2 + level;
/*
* k contains relative level of indentation
*/
fprintf(fout,"%s{ ",s);
j = k;
/*
* j will gather the total length of the line
*/
for (i = 0, pt = tokens; i < ntokens; i++,pt++) {
if (IN(p,i)) {
hulp = strlen(pt->t_string)+1;
if (pt->t_tokno < 0400) hulp += 2;
if ((j += hulp) >= 78) {
/*
* Line becoming too long
*/
j = k+hulp;
prline("\n");
fprintf(fout,">%*c",k - level - 1,' ');
}
fprintf(fout, pt->t_tokno<0400 ? "'%s' " : "%s ",pt->t_string);
}
}
if (ntprint) for (i = 0; i < nnonterms; i++) {
/*
* Nonterminals in the set must also be printed
*/
if (NTIN(p,i)) {
name = nonterms[i].n_name;
hulp = strlen(name) + 3;
if ((j += hulp) >= 78) {
j = k + hulp;
prline("\n");
fprintf(fout,">%*c",k - level - 1,' ');
}
fprintf(fout,"<%s> ",name);
}
}
prline("}\n");
}
STATIC int
check(p) register p_gram p; {
/*
* Search for conflicts in a grammar rule.
*/
register p_set temp;
register int retval;
retval = 0;
for (;;) {
switch (g_gettype(p)) {
case EORULE :
return retval;
case NONTERM : {
register p_nont n;
n = &nonterms[g_getcont(p)];
if (g_getnpar(p) != getntparams(n)) {
error(p->g_lineno,
"Call of %s : parameter count mismatch",
n->n_name);
}
break; }
case TERM : {
register p_term q;
q = g_getterm(p);
retval |= check(q->t_rule);
if (r_getkind(q) == FIXED) break;
if (setempty(q->t_first)) {
q->t_flags |= EMPTYFIRST;
retval = 1;
error(p->g_lineno, "No symbols in term");
}
if (empty(q->t_rule)) {
q->t_flags |= EMPTYTERM;
retval = 1;
error(p->g_lineno, "Term produces empty");
}
temp = setalloc();
setunion(temp,q->t_first);
if (!setintersect(temp,q->t_follow)) {
/*
* q->t_first * q->t_follow != EMPTY
*/
if (!(q->t_flags & RESOLVER)) {
/*
* No conflict resolver
*/
error(p->g_lineno,
"Repetition conflict");
retval = 1;
moreverbose(temp);
}
}
else {
if (q->t_flags & RESOLVER) {
q->t_flags |= NOCONF;
warning(p->g_lineno,
"%%while without conflict");
}
}
free((p_mem) temp);
break; }
case ALTERNATION : {
register p_link l;
l = g_getlink(p);
temp = setalloc();
setunion(temp,l->l_symbs);
if(!setintersect(temp,l->l_others)) {
/*
* temp now contains the conflicting
* symbols
*/
if (!(l->l_flag & (COND|PREFERING|AVOIDING))) {
error(p->g_lineno,
"Alternation conflict");
retval = 1;
moreverbose(temp);
}
} else {
if (l->l_flag & (COND|PREFERING|AVOIDING)) {
l->l_flag |= NOCONF;
warning(p->g_lineno,
"Conflict resolver without conflict");
}
}
if (l->l_flag & PREFERING) propagate(l->l_symbs,p+1);
free( (p_mem) temp);
retval |= check(l->l_rule);
break; }
}
p++;
}
}
STATIC
moreverbose(t) register p_set t; {
/*
* t points to a set containing conflicting symbols and pssibly
* also containing nonterminals.
* Take care that a printout will be prepared for these nonterminals
*/
register int i;
register p_nont p;
if (verbose == 2) for (i = 0, p = nonterms; i < nnonterms; i++, p++) {
if (NTIN(t,i)) p->n_flags |= VERBOSE;
}
}
STATIC
prrule(p) register p_gram p; {
/*
* Create a verbose printout of grammar rule p
*/
register FILE *f;
int present = 0;
int firstalt = 1;
f = fout;
for (;;) {
switch (g_gettype(p)) {
case EORULE :
fputs("\n",f);
return;
case TERM : {
register p_term q;
register int c;
q = g_getterm(p);
if (present) prline("\n");
fputs("[ ",f);
level += 4;
if (q->t_flags & RESOLVER) {
prline("%while (..)\n");
}
if (q->t_flags & PERSISTENT) {
prline("%persistent\n");
}
if (r_getkind(q) != FIXED) {
if (!(q->t_flags & PERSISTENT)) {
prline("> continue repetition on the\n");
}
printset(q->t_first, c_first);
if (q->t_flags & PERSISTENT) {
prline("> continue repetition on the\n");
}
printset(q->t_contains, c_contains);
prline("> terminate repetition on the\n");
printset(q->t_follow,c_follow);
if (q->t_flags & EMPTYFIRST) {
prline(">>> empty first\n");
}
if (q->t_flags & EMPTYTERM) {
prline(">>> term produces empty\n");
}
cfcheck(q->t_first,q->t_follow,
q->t_flags & RESOLVER);
}
prrule(q->t_rule);
level -= 4;
spaces();
c = r_getkind(q);
fputs(c == STAR ? "]*" : c == PLUS ? "]+" :
c == OPT ? "]?" : "]", f);
if (c = r_getnum(q)) {
fprintf(f,"%d",c);
}
prline("\n");
break; }
case ACTION :
fputs("{..} ",f);
break;
case ALTERNATION : {
register p_link l;
l = g_getlink(p);
if (firstalt) {
firstalt = 0;
}
else prline("|\n");
printset(l->l_symbs,"> alternative on ");
cfcheck(l->l_symbs,
l->l_others,
(int)(l->l_flag&(COND|PREFERING|AVOIDING)));
fputs(" ",f);
level += 4;
if (l->l_flag & DEF) {
prline("%default\n");
}
if (l->l_flag & AVOIDING) {
prline("%avoid\n");
}
if (l->l_flag & PREFERING) {
prline("%prefer\n");
}
if (l->l_flag & COND) {
prline("%if ( ... )\n");
}
prrule(l->l_rule);
level -= 4;
if (g_gettype(p+1) == EORULE) {
return;
}
spaces();
p++; continue; }
case LITERAL :
case TERMINAL : {
register p_token pt = &tokens[g_getcont(p)];
fprintf(f,pt->t_tokno<0400 ?
"'%s' " : "%s ", pt->t_string);
break; }
case NONTERM :
fprintf(f,"%s ",nonterms[g_getcont(p)].n_name);
break;
}
p++;
present = 1;
}
}
STATIC
cfcheck(s1,s2,flag) p_set s1,s2; {
/*
* Check if s1 and s2 have elements in common.
* If so, flag must be non-zero, indicating that there is a
* conflict resolver, otherwise, flag must be zero, indicating
* that there is not.
*/
register p_set temp;
temp = setalloc();
setunion(temp,s1);
if (!setintersect(temp,s2)) {
if (! flag) {
printset(temp,">>> conflict on ");
prline("\n");
}
} else {
if (flag) {
prline(">>> %if/%while, no conflict\n");
}
}
free((p_mem) temp);
}
STATIC
resolve(p) register p_gram p; {
/*
* resolve conflicts, as specified by the user
*/
for (;;) {
switch (g_gettype(p)) {
case EORULE :
return;
case TERM :
resolve(g_getterm(p)->t_rule);
break;
case ALTERNATION : {
register p_link l;
l = g_getlink(p);
if (l->l_flag & AVOIDING) {
/*
* On conflicting symbols, this rule
* is never chosen
*/
setminus(l->l_symbs,l->l_others);
}
if (setempty(l->l_symbs)) {
/*
* This may be caused by the statement above
*/
error(p->g_lineno,"Alternative never chosen");
}
resolve(l->l_rule);
break; }
}
p++;
}
}
STATIC
propagate(set,p) p_set set; register p_gram p; {
/*
* Propagate the fact that on the elements of set the grammar rule
* p will not be chosen.
*/
while (g_gettype(p) != EORULE) {
setminus(g_getlink(p)->l_symbs,set);
p++;
}
}
STATIC
spaces() {
if (level > 0) fprintf(fout,"%*c",level,' ');
}