123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599 |
- /* Grammar reduction for Bison.
- Copyright (C) 1988, 1989 Free Software Foundation, Inc.
- This file is part of Bison, the GNU Compiler Compiler.
- Bison is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
- Bison is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with Bison; see the file COPYING. If not, write to
- the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
- /*
- * Reduce the grammar: Find and eliminate unreachable terminals,
- * nonterminals, and productions. David S. Bakin.
- */
- /*
- * Don't eliminate unreachable terminals: They may be used by the user's
- * parser.
- */
- #include <stdio.h>
- #include "system.h"
- #include "files.h"
- #include "gram.h"
- #include "machine.h"
- #include "new.h"
- extern char **tags; /* reader.c */
- extern int verboseflag; /* getargs.c */
- static int statisticsflag; /* XXXXXXX */
- #ifndef TRUE
- #define TRUE (1)
- #define FALSE (0)
- #endif
- typedef int bool;
- typedef unsigned *BSet;
- typedef short *rule;
- /*
- * N is set of all nonterminals which are not useless. P is set of all rules
- * which have no useless nonterminals in their RHS. V is the set of all
- * accessible symbols.
- */
- static BSet N, P, V, V1;
- static int nuseful_productions, nuseless_productions,
- nuseful_nonterminals, nuseless_nonterminals;
- static void useless_nonterminals();
- static void inaccessable_symbols();
- static void reduce_grammar_tables();
- static void print_results();
- static void print_notices();
- void dump_grammar();
- extern void fatals ();
- bool
- bits_equal (L, R, n)
- BSet L;
- BSet R;
- int n;
- {
- int i;
- for (i = n - 1; i >= 0; i--)
- if (L[i] != R[i])
- return FALSE;
- return TRUE;
- }
- int
- nbits (i)
- unsigned i;
- {
- int count = 0;
- while (i != 0) {
- i ^= (i & -i);
- ++count;
- }
- return count;
- }
- int
- bits_size (S, n)
- BSet S;
- int n;
- {
- int i, count = 0;
- for (i = n - 1; i >= 0; i--)
- count += nbits(S[i]);
- return count;
- }
- void
- reduce_grammar ()
- {
- bool reduced;
- /* Allocate the global sets used to compute the reduced grammar */
- N = NEW2(WORDSIZE(nvars), unsigned);
- P = NEW2(WORDSIZE(nrules + 1), unsigned);
- V = NEW2(WORDSIZE(nsyms), unsigned);
- V1 = NEW2(WORDSIZE(nsyms), unsigned);
- useless_nonterminals();
- inaccessable_symbols();
- reduced = (bool) (nuseless_nonterminals + nuseless_productions > 0);
- if (verboseflag)
- print_results();
- if (reduced == FALSE)
- goto done_reducing;
- print_notices();
- if (!BITISSET(N, start_symbol - ntokens))
- fatals("Start symbol %s does not derive any sentence.",
- tags[start_symbol]);
- reduce_grammar_tables();
- /* if (verboseflag) {
- fprintf(foutput, "REDUCED GRAMMAR\n\n");
- dump_grammar();
- }
- */
- /**/ statisticsflag = FALSE; /* someday getopts should handle this */
- if (statisticsflag == TRUE)
- fprintf(stderr,
- "reduced %s defines %d terminal%s, %d nonterminal%s\
- , and %d production%s.\n", infile,
- ntokens, (ntokens == 1 ? "" : "s"),
- nvars, (nvars == 1 ? "" : "s"),
- nrules, (nrules == 1 ? "" : "s"));
- done_reducing:
- /* Free the global sets used to compute the reduced grammar */
- FREE(N);
- FREE(V);
- FREE(P);
- }
- /*
- * Another way to do this would be with a set for each production and then do
- * subset tests against N, but even for the C grammar the whole reducing
- * process takes only 2 seconds on my 8Mhz AT.
- */
- static bool
- useful_production (i, N)
- int i;
- BSet N;
- {
- rule r;
- short n;
- /*
- * A production is useful if all of the nonterminals in its RHS
- * appear in the set of useful nonterminals.
- */
- for (r = &ritem[rrhs[i]]; *r > 0; r++)
- if (ISVAR(n = *r))
- if (!BITISSET(N, n - ntokens))
- return FALSE;
- return TRUE;
- }
- /* Remember that rules are 1-origin, symbols are 0-origin. */
- static void
- useless_nonterminals ()
- {
- BSet Np, Ns;
- int i, n;
- /*
- * N is set as built. Np is set being built this iteration. P is set
- * of all productions which have a RHS all in N.
- */
- Np = NEW2(WORDSIZE(nvars), unsigned);
- /*
- * The set being computed is a set of nonterminals which can derive
- * the empty string or strings consisting of all terminals. At each
- * iteration a nonterminal is added to the set if there is a
- * production with that nonterminal as its LHS for which all the
- * nonterminals in its RHS are already in the set. Iterate until the
- * set being computed remains unchanged. Any nonterminals not in the
- * set at that point are useless in that they will never be used in
- * deriving a sentence of the language.
- *
- * This iteration doesn't use any special traversal over the
- * productions. A set is kept of all productions for which all the
- * nonterminals in the RHS are in useful. Only productions not in
- * this set are scanned on each iteration. At the end, this set is
- * saved to be used when finding useful productions: only productions
- * in this set will appear in the final grammar.
- */
- n = 0;
- while (1)
- {
- for (i = WORDSIZE(nvars) - 1; i >= 0; i--)
- Np[i] = N[i];
- for (i = 1; i <= nrules; i++)
- {
- if (!BITISSET(P, i))
- {
- if (useful_production(i, N))
- {
- SETBIT(Np, rlhs[i] - ntokens);
- SETBIT(P, i);
- }
- }
- }
- if (bits_equal(N, Np, WORDSIZE(nvars)))
- break;
- Ns = Np;
- Np = N;
- N = Ns;
- }
- FREE(N);
- N = Np;
- }
- static void
- inaccessable_symbols ()
- {
- BSet Vp, Vs, Pp;
- int i, n;
- short t;
- rule r;
- /*
- * Find out which productions are reachable and which symbols are
- * used. Starting with an empty set of productions and a set of
- * symbols which only has the start symbol in it, iterate over all
- * productions until the set of productions remains unchanged for an
- * iteration. For each production which has a LHS in the set of
- * reachable symbols, add the production to the set of reachable
- * productions, and add all of the nonterminals in the RHS of the
- * production to the set of reachable symbols.
- *
- * Consider only the (partially) reduced grammar which has only
- * nonterminals in N and productions in P.
- *
- * The result is the set P of productions in the reduced grammar, and
- * the set V of symbols in the reduced grammar.
- *
- * Although this algorithm also computes the set of terminals which are
- * reachable, no terminal will be deleted from the grammar. Some
- * terminals might not be in the grammar but might be generated by
- * semantic routines, and so the user might want them available with
- * specified numbers. (Is this true?) However, the nonreachable
- * terminals are printed (if running in verbose mode) so that the user
- * can know.
- */
- Vp = NEW2(WORDSIZE(nsyms), unsigned);
- Pp = NEW2(WORDSIZE(nrules + 1), unsigned);
- /* If the start symbol isn't useful, then nothing will be useful. */
- if (!BITISSET(N, start_symbol - ntokens))
- goto end_iteration;
- SETBIT(V, start_symbol);
- n = 0;
- while (1)
- {
- for (i = WORDSIZE(nsyms) - 1; i >= 0; i--)
- Vp[i] = V[i];
- for (i = 1; i <= nrules; i++)
- {
- if (!BITISSET(Pp, i) && BITISSET(P, i) &&
- BITISSET(V, rlhs[i]))
- {
- for (r = &ritem[rrhs[i]]; *r >= 0; r++)
- {
- if (ISTOKEN(t = *r)
- || BITISSET(N, t - ntokens))
- {
- SETBIT(Vp, t);
- }
- }
- SETBIT(Pp, i);
- }
- }
- if (bits_equal(V, Vp, WORDSIZE(nsyms)))
- {
- break;
- }
- Vs = Vp;
- Vp = V;
- V = Vs;
- }
- end_iteration:
- FREE(V);
- V = Vp;
- /* Tokens 0, 1, and 2 are internal to Bison. Consider them useful. */
- SETBIT(V, 0); /* end-of-input token */
- SETBIT(V, 1); /* error token */
- SETBIT(V, 2); /* illegal token */
- FREE(P);
- P = Pp;
- nuseful_productions = bits_size(P, WORDSIZE(nrules + 1));
- nuseless_productions = nrules - nuseful_productions;
- nuseful_nonterminals = 0;
- for (i = ntokens; i < nsyms; i++)
- if (BITISSET(V, i))
- nuseful_nonterminals++;
- nuseless_nonterminals = nvars - nuseful_nonterminals;
- /* A token that was used in %prec should not be warned about. */
- for (i = 1; i < nrules; i++)
- if (rprecsym[i] != 0)
- SETBIT(V1, rprecsym[i]);
- }
- static void
- reduce_grammar_tables ()
- {
- /* This is turned off because we would need to change the numbers
- in the case statements in the actions file. */
- #if 0
- /* remove useless productions */
- if (nuseless_productions > 0)
- {
- short np, pn, ni, pi;
- np = 0;
- ni = 0;
- for (pn = 1; pn <= nrules; pn++)
- {
- if (BITISSET(P, pn))
- {
- np++;
- if (pn != np)
- {
- rlhs[np] = rlhs[pn];
- rline[np] = rline[pn];
- rprec[np] = rprec[pn];
- rassoc[np] = rassoc[pn];
- rrhs[np] = rrhs[pn];
- if (rrhs[np] != ni)
- {
- pi = rrhs[np];
- rrhs[np] = ni;
- while (ritem[pi] >= 0)
- ritem[ni++] = ritem[pi++];
- ritem[ni++] = -np;
- }
- } else {
- while (ritem[ni++] >= 0);
- }
- }
- }
- ritem[ni] = 0;
- nrules -= nuseless_productions;
- nitems = ni;
- /*
- * Is it worth it to reduce the amount of memory for the
- * grammar? Probably not.
- */
- }
- #endif /* 0 */
- /* Disable useless productions,
- since they may contain useless nonterms
- that would get mapped below to -1 and confuse everyone. */
- if (nuseless_productions > 0)
- {
- int pn;
- for (pn = 1; pn <= nrules; pn++)
- {
- if (!BITISSET(P, pn))
- {
- rlhs[pn] = -1;
- }
- }
- }
- /* remove useless symbols */
- if (nuseless_nonterminals > 0)
- {
- int i, n;
- /* short j; JF unused */
- short *nontermmap;
- rule r;
- /*
- * create a map of nonterminal number to new nonterminal
- * number. -1 in the map means it was useless and is being
- * eliminated.
- */
- nontermmap = NEW2(nvars, short) - ntokens;
- for (i = ntokens; i < nsyms; i++)
- nontermmap[i] = -1;
- n = ntokens;
- for (i = ntokens; i < nsyms; i++)
- if (BITISSET(V, i))
- nontermmap[i] = n++;
- /* Shuffle elements of tables indexed by symbol number. */
- for (i = ntokens; i < nsyms; i++)
- {
- n = nontermmap[i];
- if (n >= 0)
- {
- sassoc[n] = sassoc[i];
- sprec[n] = sprec[i];
- tags[n] = tags[i];
- } else {
- free(tags[i]);
- }
- }
- /* Replace all symbol numbers in valid data structures. */
- for (i = 1; i <= nrules; i++)
- {
- /* Ignore the rules disabled above. */
- if (rlhs[i] >= 0)
- rlhs[i] = nontermmap[rlhs[i]];
- if (ISVAR (rprecsym[i]))
- /* Can this happen? */
- rprecsym[i] = nontermmap[rprecsym[i]];
- }
- for (r = ritem; *r; r++)
- if (ISVAR(*r))
- *r = nontermmap[*r];
- start_symbol = nontermmap[start_symbol];
- nsyms -= nuseless_nonterminals;
- nvars -= nuseless_nonterminals;
- free(&nontermmap[ntokens]);
- }
- }
- static void
- print_results ()
- {
- int i;
- /* short j; JF unused */
- rule r;
- bool b;
- if (nuseless_nonterminals > 0)
- {
- fprintf(foutput, "Useless nonterminals:\n\n");
- for (i = ntokens; i < nsyms; i++)
- if (!BITISSET(V, i))
- fprintf(foutput, " %s\n", tags[i]);
- }
- b = FALSE;
- for (i = 0; i < ntokens; i++)
- {
- if (!BITISSET(V, i) && !BITISSET(V1, i))
- {
- if (!b)
- {
- fprintf(foutput, "\n\nTerminals which are not used:\n\n");
- b = TRUE;
- }
- fprintf(foutput, " %s\n", tags[i]);
- }
- }
- if (nuseless_productions > 0)
- {
- fprintf(foutput, "\n\nUseless rules:\n\n");
- for (i = 1; i <= nrules; i++)
- {
- if (!BITISSET(P, i))
- {
- fprintf(foutput, "#%-4d ", i);
- fprintf(foutput, "%s :\t", tags[rlhs[i]]);
- for (r = &ritem[rrhs[i]]; *r >= 0; r++)
- {
- fprintf(foutput, " %s", tags[*r]);
- }
- fprintf(foutput, ";\n");
- }
- }
- }
- if (nuseless_nonterminals > 0 || nuseless_productions > 0 || b)
- fprintf(foutput, "\n\n");
- }
- void
- dump_grammar ()
- {
- int i;
- rule r;
- fprintf(foutput,
- "ntokens = %d, nvars = %d, nsyms = %d, nrules = %d, nitems = %d\n\n",
- ntokens, nvars, nsyms, nrules, nitems);
- fprintf(foutput, "Variables\n---------\n\n");
- fprintf(foutput, "Value Sprec Sassoc Tag\n");
- for (i = ntokens; i < nsyms; i++)
- fprintf(foutput, "%5d %5d %5d %s\n",
- i, sprec[i], sassoc[i], tags[i]);
- fprintf(foutput, "\n\n");
- fprintf(foutput, "Rules\n-----\n\n");
- for (i = 1; i <= nrules; i++)
- {
- fprintf(foutput, "%-5d(%5d%5d)%5d : (@%-5d)",
- i, rprec[i], rassoc[i], rlhs[i], rrhs[i]);
- for (r = &ritem[rrhs[i]]; *r > 0; r++)
- fprintf(foutput, "%5d", *r);
- fprintf(foutput, " [%d]\n", -(*r));
- }
- fprintf(foutput, "\n\n");
- fprintf(foutput, "Rules interpreted\n-----------------\n\n");
- for (i = 1; i <= nrules; i++)
- {
- fprintf(foutput, "%-5d %s :", i, tags[rlhs[i]]);
- for (r = &ritem[rrhs[i]]; *r > 0; r++)
- fprintf(foutput, " %s", tags[*r]);
- fprintf(foutput, "\n");
- }
- fprintf(foutput, "\n\n");
- }
- static void
- print_notices ()
- {
- extern int fixed_outfiles;
- if (fixed_outfiles && nuseless_productions)
- fprintf(stderr, "%d rules never reduced\n", nuseless_productions);
- fprintf(stderr, "%s contains ", infile);
- if (nuseless_nonterminals > 0)
- {
- fprintf(stderr, "%d useless nonterminal%s",
- nuseless_nonterminals,
- (nuseless_nonterminals == 1 ? "" : "s"));
- }
- if (nuseless_nonterminals > 0 && nuseless_productions > 0)
- fprintf(stderr, " and ");
- if (nuseless_productions > 0)
- {
- fprintf(stderr, "%d useless rule%s",
- nuseless_productions,
- (nuseless_productions == 1 ? "" : "s"));
- }
- fprintf(stderr, ".\n");
- fflush(stderr);
- }
|