#include #include "global.h" #include "list.h" #include "source.h" #include "lex.h" #include "nodes.h" #include "conf.h" #include "parser.h" #include "parparse.h" #include "partab.h" #include "gen.h" // The constructor function for ParTable converts the configuration set // information and transitions into a table form for convenient output. ParTable::ParTable() { states = ConfSet::NextId; symbols = ParRule::RuleId; actionTable = (short int **) allocate(ConfSet::NextId*sizeof(int *)); for (int i = 0; i eachConf(&ConfSet::confSets); ConfSet **cs; while (cs = eachConf()) { listIter eachSymbol(&ParRule::ruleSet); ParRule **r; while (r = eachSymbol()) { // For each symbol from a configuration set, we can determine if shift // or reduce actions are possible. ConfSet *shiftDest = (*cs)->destOf(*r); LConfList reduceCogs; listIter eachCog((*cs)->closure); LConf **cg; while (cg = eachCog()) if ((*cg)->conf->dot->parseItem() == 0) // just look out for reductions if ((*cg)->conf->rule == ParRule::systemRoot) // special consideration for reducing to root nonterm acceptState = (*cs)->id(); else if ((*cg)->lookAhead.find(*r)) reduceCogs.append(*cg); // When a shift destination is not found, but only one reduction // possibility, the correct action is clear a reduction. if (shiftDest == 0 && reduceCogs.length() == 1) reduceIs((*cs)->id(), (*r)->id(), reduceCogs.get()); // When a shift destination is found, and no reductions are possible, // the correct action is clear a shift. else if (shiftDest && reduceCogs.length() == 0) shiftIs((*cs)->id(), (*r)->id(), shiftDest->id()); else { // Since productions with a conditional reduction predicate are resolved // dynamically, they are seperated from ordinary reductions. LConfList conditionals; listIter eachRed(&reduceCogs); LConf **cog; LConf *toRemove = 0; while (cog = eachRed()) { ParNode *nd = (*cog)->conf->dot->condNode(); if (toRemove) reduceCogs.remove(toRemove); toRemove = 0; if (nd) { conditionals.append(*cog); toRemove = *cog; } } if (toRemove) reduceCogs.remove(toRemove); // For whatever reductions are left, the first is chosen to be used, // while the reminder are hidden due to the earlier rule. if (reduceCogs.length() > 0) { LConf *chosen = reduceCogs.get(); if (reduceCogs.length()) { error(WARNING, "config set %d: reduce-reduce conflict\n", (*cs)->id()); ParNode *prod = chosen->conf->rule->rhs()->leadsTo (chosen->conf->dot); fprintf(stderr, "use: %s =", chosen->conf->rule->name()); prod->show(chosen->conf->dot, stderr); fprintf(stderr, ", %s\n", (*r)->name()); } // Where there is a shift-reduce conflict, precedence and associativity // rules may be used for static resolution. if (shiftDest) { ParNode *prod = chosen->conf->rule->rhs()->leadsTo (chosen->conf->dot); if (prod->precedence() > (*r)->precedence) reduceIs((*cs)->id(), (*r)->id(), chosen); else if (prod->precedence() < (*r)->precedence) shiftIs((*cs)->id(), (*r)->id(), shiftDest->id()); else if (prod->associativity() == ASSOC_LEFT && (*r)->associativity == ASSOC_LEFT) reduceIs((*cs)->id(), (*r)->id(), chosen); else if (prod->associativity() == ASSOC_RIGHT && (*r)->associativity == ASSOC_RIGHT) shiftIs((*cs)->id(), (*r)->id(), shiftDest->id()); else if (prod->associativity() == ASSOC_NONE && (*r)->associativity == ASSOC_NONE) ; // error -- no action else { error(WARNING, "config set %d: shift-reduce conflict\n", (*cs)->id()); fprintf(stderr, "not using: %s =", chosen->conf->rule->name()); prod->show(chosen->conf->dot, stderr); fprintf(stderr, ", %s\n", (*r)->name()); condShiftIs((*cs)->id(), (*r)->id(), shiftDest->id(), conditionals); } } else // If a shift move is not possible, the first reduction is chosen after // conditional reductions are exhausted. condReduceIs((*cs)->id(), (*r)->id(), chosen, conditionals); } else // If a shift move is possible, it is chosen after conditional // reductions are exhausted. if (shiftDest) condShiftIs((*cs)->id(), (*r)->id(), shiftDest->id(), conditionals); } } } } // The ReduceInfo class records information pertaining to reductions. // Ultimately, it provides the non-terminal name when a handle is // recognised, and the length of the handle. class ReduceInfo { public: ReduceInfo() { index = 1; } int size() { return index; } int makeReduce(ParRule *, ParNode *); ParRule *head(int i) { return(reduceTable[i].head); } ParNode *prod(int i) { return(reduceTable[i].prod); } protected: enum { MAXTABLE = 4*1024 }; struct ReduceRecord { ParRule *head; ParNode *prod; } reduceTable[MAXTABLE]; int index; } reduceInfo; int ReduceInfo::makeReduce(ParRule *r, ParNode *p) { for (int i=1; iconf->rule; ParNode *prod = newSy->rhs()->leadsTo(c->conf->dot); actionTable[state][sy] = - reduceInfo.makeReduce(newSy, prod); } // The CondInfo class records information pertaining to conditional // reductions. In this case, a configuration possibly has more than one // predicate. This sequence of conditionals for the state are // represented consecutively in condInfo, and is terminated by a zero // entry. class CondInfo { enum { MAXTABLE = 1024 }; int condTable[MAXTABLE]; int _index; public: CondInfo() { _index = 0; } int index() { return _index; } void add(int x) { condTable[_index++] = x; } int val(int x) { return condTable[x]; } } condInfo; void ParTable::condShiftIs(int state, int sy, int newState, LConfList &cond) { if (cond.length() == 0) { shiftIs(state, sy, newState); return; } actionTable[state][sy] = ConfSet::NextId+condInfo.index(); listIter forEach(&cond); LConf **cg; while (cg = forEach()) { ParRule *newSy = (*cg)->conf->rule; ParNode *prod = newSy->rhs()->leadsTo((*cg)->conf->dot); condInfo.add (ConfSet::NextId+reduceInfo.makeReduce(newSy, prod)); } condInfo.add(newState); condInfo.add(0); } void ParTable::condReduceIs(int state, int sy, LConf *c, LConfList &cond) { if (cond.length() == 0) { reduceIs(state, sy, c); return; } actionTable[state][sy] = ConfSet::NextId+condInfo.index(); listIter forEach(&cond); LConf **cg; while (cg = forEach()) { ParRule *newSy = (*cg)->conf->rule; ParNode *prod = newSy->rhs()->leadsTo((*cg)->conf->dot); condInfo.add (ConfSet::NextId+reduceInfo.makeReduce(newSy, prod)); } ParRule *newSy = c->conf->rule; ParNode *prod = newSy->rhs()->leadsTo(c->conf->dot); condInfo.add(-reduceInfo.makeReduce(newSy, prod)); condInfo.add(0); } // The following are member functions for printing out appropriate code // fragements in response to the predetermined markers in the template // file. void ParTable::pr_nomodsWarning(FILE *ofdes) { fprintf(ofdes, "This is a generated file. Modifications are futile."); } void ParTable::pr_states(FILE *ofdes) { fprintf(ofdes, "%d", states); } void ParTable::pr_symbols(FILE *ofdes) { fprintf(ofdes, "%d", symbols); } void ParTable::pr_acceptState(FILE *ofdes) { fprintf(ofdes, "%d", acceptState); } void ParTable::pr_tokens(FILE *ofdes) { listIter forEach(&ParRule::ruleSet); ParRule **r; // internal _tokens fprintf(ofdes, "/* internal _tokens */\n"); fprintf(ofdes, "#define _ERR_TOKEN (0)\n"); fprintf(ofdes, "#define _EOF_TOKEN (1)\n"); fprintf(ofdes, "/* tokens defined in the language specs */\n"); while (r = forEach()) if (!(*r)->isNonTerm() && (strcmp((*r)->name(), SYSEOF))) fprintf(ofdes, "#define %s_TOKEN (%d)\n", (*r)->name(), (*r)->id()); } // The writer class produces formatted tables. This is useful for the // initialisation of large arrays to represent transition functions. class writer { FILE *fdes; int col, first; char buf[16]; public: void print(int); void print(char *); writer(FILE *); ~writer(); }; void ParTable::pr_actionTable(FILE *ofdes) { writer v(ofdes); for (int i=0; iid(); fprintf(ofdes, ",\n{ %d, ", nt); if (nt < ParRule::systemRoot->id()) { fprintf(ofdes, "%d, %d, ", reduceInfo.prod(i)->parseItems(), reduceInfo.prod(i)->parseItems() - reduceInfo.prod(i)->inhItems()); if (reduceInfo.prod(i)->actionNode()) fprintf(ofdes, "p_%s_%d, ", reduceInfo.head(i)->name(), i); else fprintf(ofdes, "0, "); if (reduceInfo.prod(i)->condNode()) fprintf(ofdes, "c_%s_%d }", reduceInfo.head(i)->name(), i); else fprintf(ofdes, "0 }"); } else { InhMarker *nod = (InhMarker *) reduceInfo.head(i); Conf *cf = Conf::confs.find(nod->context); ParNode *prod = cf->rule->rhs()->leadsTo(cf->dot); fprintf(ofdes, "-1, %d, i_%s, 0 }", (prod->parseItems()-prod->inhItems())- (cf->dot->parseItems()-cf->dot->inhItems()), reduceInfo.head(i)->name()); } } fprintf(ofdes, "}"); } void ParTable::pr_condTable(FILE *ofdes) { writer w(ofdes); for (int j=0; j forEach(&ParRule::ruleSet); ParRule **r; #if PLAYING_WITH_FIRE // first, write out forward declarations of classes // NOTE: the compiler would not know how to cast correctly // if real declaration is not available! while (r = forEach()) if ((*r)->isNonTerm() && (*r)->id() < ParRule::systemRoot->id()) fprintf(ofdes, "class %sSym;\n", (*r)->name()); fprintf(ofdes, "\n"); #endif #if OLD_CODE // next, write out class templates forEach.init(&ParRule::ruleSet); while (r = forEach()) // write out attribute definition in terms of C++ class if ((*r)->isNonTerm() && (*r)->id() < ParRule::systemRoot->id()) { fprintf(ofdes, "struct %s_attr:public Attr {\n", (*r)->name()); fprintf(ofdes, "virtual~%s_attr(){}\n", (*r)->name()); if ((*r)->attributes) fprintf(ofdes, "%s\n\n", (*r)->attributes); for (int i=1; iactionNode()) { // write out member function for evaluating attributes fprintf(ofdes, "void action_%d", i); pr_signature(reduceInfo.prod(i), 0, ofdes); fprintf(ofdes, ";\n"); } } fprintf(ofdes, "};\n\n"); } fprintf(ofdes, "#line %d \"%s\"\n", Copier::position, Copier::currentfile); #endif } void ParTable::pr_inhAttrdef(FILE *ofdes) { #if OLD_CODE if (ParGenParser::inhAttr) fprintf(ofdes, "%s\n", ParGenParser::inhAttr); for (int i=1; iid() > ParRule::systemRoot->id()) { InhMarker *n = (InhMarker *) reduceInfo.head(i); fprintf(ofdes, "void i_%s", n->name()); Conf *cf = Conf::confs.find(n->context); ParNode *prod = cf->rule->rhs()->leadsTo(cf->dot); pr_signature(prod, cf->dot->parseItem(), ofdes); fprintf(ofdes, ";\n"); } fprintf(ofdes, "#line %d \"%s\"\n", Copier::position, Copier::currentfile); #endif } void ParTable::pr_code(FILE *ofdes) { // write out the drivers for action routines for (int i = 1; i < reduceInfo.size(); ++i) { if (reduceInfo.head(i)->id() > ParRule::systemRoot->id()) continue; ParNode *prod = reduceInfo.prod(i); if (prod && prod->actionNode()) { const char *name = reduceInfo.head(i)->name(); fprintf(ofdes, "static\nSynSym *p_%s_%d(SynSym **%s) {\n", name, i, prod->parseItems() ? "base" : ""); // extern-declaration //fprintf(ofdes, "\textern SynSym *make_%s", name); //pr_form_argument(prod, 0, ofdes); //fprintf(ofdes, ";\n"); // call // fprintf(ofdes, "\treturn new %sSym", name); // pr_argument(prod, 0, ofdes); // fprintf(ofdes, ";\n"); // create parsed symbol int tokenCount = 0; int symCount = 0; countTokens(prod, tokenCount, symCount); fprintf(ofdes, "\t// %s =", name); pr_rule_rhs(prod, 0, ofdes); fprintf(ofdes, " .\n"); fprintf(ofdes, "\tParsSym *p = new ParsSym(\"%s\", %d);\n", name, symCount); pr_argument_add(prod, 0, ofdes); fprintf(ofdes, "\treturn p;\n"); fprintf(ofdes, "}\n\n"); } } #if OLD_CODE /* replaced */ int i; // write out action code as member function code for (i=1; iid() > ParRule::systemRoot->id()) continue; char *name = reduceInfo.head(i)->name(); ParEndNode *n = (ParEndNode *) reduceInfo.prod(i)->actionNode(); if (n) { // write out member function for evaluating attributes fprintf(ofdes, "void %s_attr::action_%d", name, i); pr_signature(reduceInfo.prod(i), 0, ofdes); fprintf(ofdes, "\n{%s\n}\n\n", n->code()); } n = (ParEndNode *) reduceInfo.prod(i)->condNode(); if (n) { // write out member function for evaluating attributes fprintf(ofdes, "int _%s_%d", name, i); pr_signature(reduceInfo.prod(i), 0, ofdes); fprintf(ofdes, "\n{%s\n}\n", n->condCode()); fprintf(ofdes, "inline int c_%s_%d(Attr **base)\n", name, i); fprintf(ofdes, "{ return _%s_%d", name, i); pr_argument(reduceInfo.prod(i), 0, ofdes); fprintf(ofdes, "; }\n\n"); } } // write out the drivers for action routines for (i=1; iid() < ParRule::systemRoot->id()) { char *name = reduceInfo.head(i)->name(); ParNode *prod = reduceInfo.prod(i); if (prod && prod->actionNode()) { fprintf(ofdes, "Attr *p_%s_%d(Attr **base)\n", name, i); fprintf(ofdes, "{ %s_attr *x = new %s_attr();\n", name, name); fprintf(ofdes, " x->action_%d", i); pr_argument(prod, 0, ofdes); fprintf(ofdes, ";\n return x;\n}\n\n"); } } #endif #if OLD_CODE // write out inhertied action code as member function code for (i=1; iid() > ParRule::systemRoot->id()) { InhMarker *n = (InhMarker *) reduceInfo.head(i); fprintf(ofdes, "void Inh_Attr::i_%s", n->name()); Conf *cf = Conf::confs.find(n->context); ParNode *prod = cf->rule->rhs()->leadsTo(cf->dot); pr_signature(prod, cf->dot->parseItem(), ofdes); fprintf(ofdes, "\n{ %s\n}\n\n", ((ParActionNode *)cf->dot)->code()); } // write out the drivers for inhertied routines for (i=1; iid() > ParRule::systemRoot->id()) { InhMarker *n = (InhMarker *) reduceInfo.head(i); char *name; fprintf(ofdes, "Attr *i_%s(Attr **base){\n", name = n->name()); fprintf(ofdes, " Inh_Attr *x = new Inh_Attr();\n"); fprintf(ofdes, " x->i_%s", name); Conf *cf = Conf::confs.find(n->context); ParNode *prod = cf->rule->rhs()->leadsTo(cf->dot); pr_argument(prod, cf->dot->parseItem(), ofdes); fprintf(ofdes, ";\n}\n\n", name); } #endif fprintf(ofdes, "#line %d \"%s\"\n", Copier::position, Copier::currentfile); } void ParTable::pr_argument(ParNode *prod, ParNode *limit, FILE *ofdes) { fprintf(ofdes, "("); int j; for (j = 0, prod = prod->parseItem(); prod != limit; ++j) { ParRule *rule = prod->nontermRule(); const char *name = rule->isNonTerm() ? rule->name() : "Token"; fprintf(ofdes, "\n\t\t(const %sSym &)base[%d]->cast(%sSym::TheType)", name, j+1, name); prod = prod->next()->parseItem(); if (prod != limit) fprintf(ofdes, ","); } fprintf(ofdes, ")"); } void ParTable::pr_rule_rhs(ParNode *prod, ParNode *limit, FILE *ofdes) { int j; for (j = 0, prod = prod->parseItem(); prod != limit; ++j) { ParRule *rule = prod->nontermRule(); fprintf(ofdes, " %s", rule->name()); prod = prod->next()->parseItem(); } } void ParTable::pr_argument_add(ParNode *prod, ParNode *limit, FILE *ofdes) { int j; for (j = 0, prod = prod->parseItem(); prod != limit; ++j) { ParRule *rule = prod->nontermRule(); const char *name = rule->isNonTerm() ? rule->name() : "Token"; fprintf(ofdes, "\tp->append(*base[%d]);\n", j+1, name); prod = prod->next()->parseItem(); } } void ParTable::pr_form_argument(ParNode *prod, ParNode *limit, FILE *ofdes) { fprintf(ofdes, "("); int j; for (j = 0, prod = prod->parseItem(); prod != limit; ++j) { ParRule *rule = prod->nontermRule(); fprintf(ofdes, "const %sSym &", rule->isNonTerm() ? rule->name() : "Token", j); prod = prod->next()->parseItem(); if (prod != limit) fprintf(ofdes, ", "); } fprintf(ofdes, ")"); } void ParTable::pr_signature(ParNode *p, ParNode *limit, FILE *ofdes) { fprintf(ofdes, "("); p = p->parseItem(); while (p != limit) { ParRule *rule = p->nontermRule(); fprintf(ofdes,"%sSym *%s", (rule->isNonTerm() ? rule->name() : "Token"), p->alias()); p = p->next()->parseItem(); if (p == limit) break; fprintf(ofdes, ", "); } fprintf(ofdes, ")"); } void ParTable::pr_prelude(FILE *ofdes) { if (ParGenParser::prelude) fprintf(ofdes, "%s\n", ParGenParser::prelude); } void ParTable::pr_postlude(FILE *ofdes) { if (ParGenParser::postlude) fprintf(ofdes, "%s\n", ParGenParser::postlude); } void ParTable::countTokens(ParNode *prod, int &tokenCount, int &totalCount) { tokenCount = 0; totalCount = 0; int j; for (j = 0, prod = prod->parseItem(); prod != 0; ++j) { ParRule *rule = prod->nontermRule(); totalCount++; if (!rule->isNonTerm()) tokenCount++; prod = prod->next()->parseItem(); } } void writer::print(char *s) { if (!first) { fprintf(fdes, ", "); col += 2; } first = FALSE; col += strlen(s); if (col > 70) { col = 0; fprintf(fdes, "\n"); } fprintf(fdes, "%s", s); } void writer::print(int i) { if (!first) { fprintf(fdes, ", "); col += 2; } first = FALSE; sprintf(buf, "%d", i); col += strlen(buf); if (col > 70) { col = 0; fprintf(fdes, "\n"); } fprintf(fdes, "%s", buf); } writer::writer(FILE *fd) { fdes = fd; col = 0; first = TRUE; fprintf(fdes, "{"); } writer::~writer() { fprintf(fdes, "}"); }