/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 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; } }