/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* This file is part of SableCC. *
* See the file "LICENSE" for copyright information and the *
* terms and conditions for copying, distribution and *
* modification of SableCC. *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
package org.sablecc.sablecc;
import java.util.HashSet;
import java.util.Set;
import java.util.TreeMap;
import java.util.Vector;
@SuppressWarnings({"rawtypes", "unchecked"})
public final class Grammar {
private static TreeMap fastLr0Closure = new TreeMap();
private static TreeMap fastLr1Closure = new TreeMap();
static int startSymbol;
static int startProduction;
static int eof;
static int dummy;
static int[][][] action_;
static int[][] goto_;
static {
reinit();
}
private Grammar() {
}
public static int addTerminal(String name, String errorName) {
return new Symbol(name, errorName, true).index;
}
public static int addNonterminal(String name) {
return new Symbol(name, null, false).index;
}
public static int addProduction(String nonterminal, String name) {
Symbol symbol = Symbol.symbol(nonterminal);
if (symbol.terminal) {
throw new IllegalArgumentException("The symbol " + nonterminal +
" is a terminal.");
}
return new Production(symbol.index, name).index;
}
public static void addSymbolToProduction(String symbol, int production) {
Production.production(production).addSymbol(Symbol.symbol(symbol));
}
public static void reinit() {
fastLr0Closure = new TreeMap();
fastLr1Closure = new TreeMap();
startSymbol = 0;
startProduction = -1;
eof = -1;
dummy = -1;
action_ = null;
goto_ = null;
}
public static void computeLALR() throws ConflictException {
// Add EOF to terminals
eof = addTerminal("EOF", "EOF");
// Add dummy to terminals
dummy = addTerminal("#", null);
// Add the production S'->S
startSymbol = addNonterminal("Start");
Production start = new Production(startSymbol, "Start");
start.addSymbol(Symbol.symbol(0, false));
startProduction = start.index;
computeFirst();
LR0ItemSet set
= new LR0ItemSet();
set.set(new LR0Item(startProduction, 0));
LR1Collection collection = new LR1Collection(set
);
LR0ItemSet[] sets = collection.collection.sets();
Symbol[] terminals = Symbol.terminals();
Symbol[] nonterminals = Symbol.nonterminals();
action_ = new int[sets.length][terminals.length - 1][];
goto_ = new int[sets.length][nonterminals.length - 1];
Set listOfConflictualProds = new HashSet();
StringBuffer conflictMessage = new StringBuffer();
for (int i = 0; i < sets.length; i++) {
System.out.print(".");
LR1ItemSet state = new LR1ItemSet();
{
LR0Item[] items = sets[i].items();
for (int j = 0; j < items.length; j++) {
Symbol[] lookaheads = ((SymbolSet) collection.lookaheads[i].
get
(items[j])).getSymbols();
for (int k = 0; k < lookaheads.length; k++) {
state.set(new LR1Item(items[j], lookaheads[k].index));
}
}
}
state = CLOSURE(state);
LR1Item[] items = state.items();
for (int j = 0; j < terminals.length; j++) {
Integer destination = collection.collection.GOTO(i, terminals[j]);
if (destination != null) {
//Shift action is taken
action_[i][j] = new int[]{0, destination.intValue()};
}
for (int k = 0; k < items.length; k++) {
Production production = Production.
production(items[k].lr0Item.production);
//A conflict occurs only if one is on the end of an alternative
try {
production.rightside(items[k].lr0Item.position);
} catch (Exception e) {
if (production.leftside != startSymbol) {
if (items[k].terminal == terminals[j].index) {
int[] action = action_[i][j];
if (action != null) {
/* The current alternative is conflictual (reduce in a XXX/reduce conflict) */
listOfConflictualProds.add(Symbol.symbol(production.leftside, false).toString());
/* */
switch (action[0]) {
case 0:
conflictMessage.append(
"\n\nshift/reduce conflict in state [stack:" +
collection.collection.names.elementAt(i) + "*] on " +
terminals[j] + " in " + state.toString(terminals[j]));
/* nothing else to do */
break;
case 1:
conflictMessage.append(
"\n\nreduce/reduce conflict in state [stack:" +
collection.collection.names.elementAt(i) + "*] on " +
terminals[j] + " in " + state.toString(terminals[j]));
listOfConflictualProds.add(Symbol.symbol(Production.production(action[1]).leftside, false).toString());
break;
case 2:
conflictMessage.append(
"\n\nreduce/accept conflict in state [stack:" +
collection.collection.names.elementAt(i) + "*] on " +
terminals[j] + " in " + state.toString(terminals[j]));
listOfConflictualProds.add(Symbol.symbol(Production.production(action[1]).leftside, false).toString());
break;
}
} else {
//it's a reduction action
action_[i][j] = new int[]{1,
items[k].lr0Item.production};
}
}
} else {
if (terminals[j].index == eof) {
int[] action = action_[i][j];
if (action != null) {
/* The current alternative is conflictual (accept in a XXX/accept conflict) */
listOfConflictualProds.add(Symbol.symbol(production.leftside, false).toString());
/* */
switch (action[0]) {
case 0:
conflictMessage.append(
"shift/accept conflict in state [stack:" +
collection.collection.names.elementAt(i) + "*] on " +
terminals[j] + " in " + state);
/* nothing else to do */
break;
case 1:
conflictMessage.append(
"reduce/accept conflict in state [stack:" +
collection.collection.names.elementAt(i) + "*] on " +
terminals[j] + " in " + state);
listOfConflictualProds.add(Symbol.symbol(Production.production(action[1]).leftside, false).toString());
break;
}
} else {
//accept action is taken
action_[i][j] = new int[]{2};
}
}
}
}//End of catch
}
}
if (listOfConflictualProds.size() > 0) {
throw new ConflictException(listOfConflictualProds, conflictMessage.toString());
}
for (int j = 0; j < nonterminals.length - 1; j++) {
Integer destination = collection.collection.GOTO(i, nonterminals[j]);
if (destination != null) {
goto_[i][j] = destination.intValue();
} else {
goto_[i][j] = -1;
}
}
}
System.out.println();
}
static SymbolSet[] FIRST_Terminal;
static SymbolSet[] FIRST_Nonterminal;
static void computeFirst() {
// Get terminals, nonterminals and productions
Symbol[] terminals = Symbol.terminals();
Symbol[] nonterminals = Symbol.nonterminals();
Production[] productions = Production.productions();
// Initialize FIRST(X) to {}
FIRST_Terminal = new SymbolSet[terminals.length];
for (int i = 0; i < terminals.length; i++) {
FIRST_Terminal[i] = new SymbolSet();
}
FIRST_Nonterminal = new SymbolSet[nonterminals.length];
for (int i = 0; i < nonterminals.length; i++) {
FIRST_Nonterminal[i] = new SymbolSet();
}
// if X is terminal, then FIRST(X) is {X}
for (int i = 0; i < terminals.length; i++) {
FIRST_Terminal[i].setTerminal(terminals[i].index);
}
// if X -> empty is a production, then add empty to FIRST(X)
for (int i = 0; i < productions.length; i++) {
if (productions[i].rightside().length == 0) {
FIRST_Nonterminal[productions[i].leftside].
setEmpty();
}
}
// if X is nonterminal and X -> Y(1) Y(2) ... Y(k) is a production,
// then place t in FIRST(X) if for some i, t is in FIRST(Y(i)), and
// empty is in all of FIRST(Y(1)), ... , FIRST(Y(i-1)).
boolean changed;
do {
changed = false;
for (int i = 0; i < productions.length; i++) {
SymbolSet before =
(SymbolSet) FIRST_Nonterminal[productions[i].leftside].clone();
FIRST_Nonterminal[productions[i].leftside].
or(FIRST(productions[i].rightside()));
if (!before.equals(FIRST_Nonterminal[productions[i].leftside])) {
changed = true;
}
}
}
while (changed);
}
static SymbolSet FIRST(Symbol[] symbols) {
return FIRST(symbols, 0, symbols.length);
}
static SymbolSet FIRST(Symbol[] symbols, int begin) {
return FIRST(symbols, begin, symbols.length);
}
static SymbolSet FIRST(Symbol[] symbols, int begin, int end) {
SymbolSet result = new SymbolSet();
boolean previousContainsEmpty = true;
for (int i = begin; i < end; i++) {
if (!previousContainsEmpty) {
break;
}
if (symbols[i].terminal) {
result.or(FIRST_Terminal[symbols[i].index]);
previousContainsEmpty = FIRST_Terminal[symbols[i].index].getEmpty();
} else {
result.or(FIRST_Nonterminal[symbols[i].index]);
previousContainsEmpty = FIRST_Nonterminal[symbols[i].index].getEmpty();
}
}
if (previousContainsEmpty) {
result.setEmpty();
} else {
result.clearEmpty();
}
return result;
}
static SymbolSet[] FOLLOW;
static void computeFollow() {
// Get terminals, nonterminals and productions
// Symbol[] terminals = Symbol.terminals();
Symbol[] nonterminals = Symbol.nonterminals();
Production[] productions = Production.productions();
// Initialize FOLLOW(A) to {}
FOLLOW = new SymbolSet[nonterminals.length];
for (int i = 0; i < nonterminals.length; i++) {
FOLLOW[i] = new SymbolSet();
}
// Place eof in FOLLOW(S) where S is the start symbol.
FOLLOW[startSymbol].setTerminal(eof);
// IfStmt there is a production A->xBy, then everything in FIRST(y) except
// for empty is placed in FOLLOW(B).
for (int i = 0; i < productions.length; i++) {
Symbol[] rightside = productions[i].rightside();
for (int j = 0; j < rightside.length; j++) {
if (!rightside[j].terminal) {
SymbolSet set
= FIRST(rightside, j + 1);
set.clearEmpty();
FOLLOW[rightside[j].index].or(set
);
}
}
}
// IfStmt there is a production A->xB, or a production A->xBy where FIRST(y)
// contains empty, then everything in FOLLOW(A) is in FOLLOW(B).
boolean changed;
do {
changed = false;
for (int i = 0; i < productions.length; i++) {
Symbol[] rightside = productions[i].rightside();
for (int j = 0; j < rightside.length; j++) {
if (!rightside[j].terminal) {
SymbolSet before =
(SymbolSet) FOLLOW[rightside[j].index].clone();
if (FIRST(rightside, j + 1).getEmpty()) {
FOLLOW[rightside[j].index].
or(FOLLOW[productions[i].leftside]);
}
if (!before.equals(FOLLOW[rightside[j].index])) {
changed = true;
}
}
}
}
}
while (changed);
}
static SymbolSet FOLLOW(int nonterminal) {
return FOLLOW[nonterminal];
}
static LR0ItemSet CLOSURE(LR0Item item) {
LR0ItemSet result = (LR0ItemSet) fastLr0Closure.get(item);
if (result != null) {
return result;
}
result = new LR0ItemSet();
result.set(item);
LR0ItemSet newItems = result;
boolean modified;
do {
modified = false;
LR0Item[] items = newItems.items();
newItems = new LR0ItemSet();
for (int i = 0; i < items.length; i++) {
Production production = Production.production(items[i].production);
Symbol[] rightside = production.rightside();
if (items[i].position < rightside.length) {
Symbol symbol = rightside[items[i].position];
if (!symbol.terminal) {
Production[] alternatives =
Production.alternatives(symbol.index);
for (int j = 0; j < alternatives.length; j++) {
LR0Item newItem = new LR0Item(alternatives[j].index, 0);
if (!result.get(newItem)) {
result.set(newItem);
newItems.set(newItem);
modified = true;
}
}
}
}
}
}
while (modified);
fastLr0Closure.put(item, result);
return result;
}
// private static final SplayTreeMap fastLr0SetClosure = new SplayTreeMap();
static LR0ItemSet CLOSURE(LR0ItemSet set
) {
LR0ItemSet result =
/*
(LR0ItemSet) fastLr0SetClosure.get(set);
if(result != null)
{
return result;
}
result =*/
new LR0ItemSet();
LR0Item[] setItems = set.items();
for (int i = 0; i < setItems.length; i++) {
LR0Item[] items = CLOSURE(setItems[i]).items();
for (int j = 0; j < items.length; j++) {
result.set(items[j]);
}
}
// fastLr0SetClosure.put(set, result);
return result;
}
static LR1ItemSet CLOSURE(LR1Item item) {
LR1ItemSet result = (LR1ItemSet) fastLr1Closure.get(item);
if (result != null) {
return result;
}
result = new LR1ItemSet();
result.set(item);
LR1ItemSet newItems = result;
boolean modified;
do {
modified = false;
LR1Item[] items = newItems.items();
newItems = new LR1ItemSet();
for (int i = 0; i < items.length; i++) {
Production production = Production.production(items[i].lr0Item.production);
Symbol[] rightside = production.rightside();
if (items[i].lr0Item.position < rightside.length) {
Symbol symbol = rightside[items[i].lr0Item.position];
if (!symbol.terminal) {
Vector tailVector = new Vector(0);
for (int k = items[i].lr0Item.position + 1; k < rightside.length; k++) {
tailVector.addElement(rightside[k]);
}
tailVector.addElement(Symbol.symbol(items[i].terminal, true));
Symbol[] tail = new Symbol[tailVector.size()];
tailVector.copyInto(tail);
Symbol[] symbols = FIRST(tail).getSymbols();
Production[] alternatives =
Production.alternatives(symbol.index);
for (int k = 0; k < symbols.length; k++) {
if (symbols[k].terminal) {
for (int j = 0; j < alternatives.length; j++) {
LR1Item newItem = new LR1Item(
new LR0Item(alternatives[j].index, 0),
symbols[k].index);
if (!result.get(newItem)) {
result.set(newItem);
newItems.set(newItem);
modified = true;
}
}
}
}
}
}
}
}
while (modified);
fastLr1Closure.put(item, result);
return result;
}
// private static final SplayTreeMap fastLr1SetClosure = new SplayTreeMap();
static LR1ItemSet CLOSURE(LR1ItemSet set
) {
LR1ItemSet result =
/*
(LR1ItemSet) fastLr1SetClosure.get(set);
if(result != null)
{
return result;
}
result =*/
new LR1ItemSet();
LR1Item[] setItems = set.items();
for (int i = 0; i < setItems.length; i++) {
LR1Item[] items = CLOSURE(new LR1Item(setItems[i].lr0Item, dummy)).items();
for (int j = 0; j < items.length; j++) {
result.set(new LR1Item(items[j].lr0Item,
items[j].terminal == dummy ? setItems[i].terminal : items[j].terminal));
}
}
// fastLr1SetClosure.put(set, result);
return result;
}
static LR0ItemSet GOTO(LR0ItemSet set
, Symbol symbol) {
// LR0ItemSet initialset = set
;
set
= CLOSURE(set
);
LR0ItemSet result = new LR0ItemSet();
// return all items A->xS.y such that A->x.Sy is in set. (S=symbol)
LR0Item[] items = set.items();
for (int i = 0; i < items.length; i++) {
Production production = Production.production(items[i].production);
Symbol[] rightside = production.rightside();
if (items[i].position < rightside.length) {
if (symbol.equals(rightside[items[i].position])) {
result.set(new LR0Item(items[i].production,
items[i].position + 1));
}
}
}
return result;
}
}