#include <limits.h>
#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<ConfSet::NextId; i++) {
actionTable[i] = (short int *)
allocate(ParRule::RuleId*sizeof(short int));
for (int j = 0; j<ParRule::RuleId; j++)
actionTable[i][j] = 0;
}
fillTable();
}
void ParTable::fillTable()
{
// The parse table is constructed by visiting every configuration set
// and each possible transition for each state.
listIter<ConfSet *> eachConf(&ConfSet::confSets);
ConfSet **cs;
while (cs = eachConf()) {
listIter<ParRule *> 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<LConf *> 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<LConf *> 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; i<index; i++)
if (reduceTable[i].head == r && reduceTable[i].prod == p)
return(i);
reduceTable[index].head = r;
reduceTable[index].prod = p;
return(index++);
}
void ParTable::shiftIs(int state, int sy, int newState)
{
actionTable[state][sy] = newState;
}
void ParTable::reduceIs(int state, int sy, LConf *c)
{
ParRule *newSy = c->conf->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<LConf *> 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<LConf *> 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<ParRule *> 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; i<ConfSet::NextId; i++) {
v.print("\n");
{ writer w(ofdes);
for (int j=0; j<ParRule::RuleId; j++)
w.print(actionTable[i][j]);
}
}
}
void ParTable::pr_reduceTable(FILE *ofdes)
{
fprintf(ofdes, "{\n{ 0, 0, 0, 0}");
for (int i=1; i<reduceInfo.size(); i++) {
int nt = reduceInfo.head(i)->id();
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<condInfo.index(); j++)
w.print(condInfo.val(j));
}
void ParTable::pr_attrdef(FILE *ofdes)
{
listIter<ParRule *> 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; i<reduceInfo.size(); i++) {
if (reduceInfo.head(i) != (*r))
continue;
if (reduceInfo.prod(i)->actionNode()) {
// 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; i<reduceInfo.size(); i++)
if (reduceInfo.head(i)->id() > 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; i<reduceInfo.size(); i++) {
if (reduceInfo.head(i)->id() > 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; i<reduceInfo.size(); i++)
if (reduceInfo.head(i)->id() < 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; i<reduceInfo.size(); i++)
if (reduceInfo.head(i)->id() > 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; i<reduceInfo.size(); i++)
if (reduceInfo.head(i)->id() > 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, "}");
}
syntax highlighted by Code2HTML, v. 0.9.1