/*
*
* Copyright 2012 lexergen.
* This file is part of lexergen.
*
* lexergen 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 3 of the License, or
* (at your option) any later version.
*
* lexergen 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 lexergen. If not, see <http://www.gnu.org/licenses/>.
*
* lexergen:
* A tool to chunk source code into tokens for further processing in a compiler chain.
*
* Projectgroup: bi, bii
*
* Authors: Johannes Dahlke
*
* Module: Softwareprojekt Übersetzerbau 2012
*
* Created: Apr. 2012
* Version: 1.0
*
*/
package de.fuberlin.bii.regextodfaconverter.directconverter.lrparser;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import java.util.Stack;
import java.util.concurrent.ArrayBlockingQueue;
import de.fuberlin.bii.regextodfaconverter.directconverter.AutomatEventHandler;
import de.fuberlin.bii.regextodfaconverter.directconverter.lrparser.grammar.ContextFreeGrammar;
import de.fuberlin.bii.regextodfaconverter.directconverter.lrparser.grammar.EmptyString;
import de.fuberlin.bii.regextodfaconverter.directconverter.lrparser.grammar.Nonterminal;
import de.fuberlin.bii.regextodfaconverter.directconverter.lrparser.grammar.ProductionMap;
import de.fuberlin.bii.regextodfaconverter.directconverter.lrparser.grammar.ProductionRule;
import de.fuberlin.bii.regextodfaconverter.directconverter.lrparser.grammar.RuleElement;
import de.fuberlin.bii.regextodfaconverter.directconverter.lrparser.grammar.RuleElementArray;
import de.fuberlin.bii.regextodfaconverter.directconverter.lrparser.grammar.RuleElementSequenz;
import de.fuberlin.bii.regextodfaconverter.directconverter.lrparser.grammar.Symbol;
import de.fuberlin.bii.regextodfaconverter.directconverter.lrparser.grammar.Terminal;
import de.fuberlin.bii.regextodfaconverter.directconverter.lrparser.grammar.TerminalSet;
import de.fuberlin.bii.regextodfaconverter.directconverter.lrparser.grammar.Terminator;
import de.fuberlin.bii.regextodfaconverter.directconverter.lrparser.itemset.Lr0Closure;
import de.fuberlin.bii.regextodfaconverter.directconverter.lrparser.itemset.Lr1Closure;
import de.fuberlin.bii.regextodfaconverter.directconverter.lrparser.itemset.Lr1Item;
import de.fuberlin.bii.regextodfaconverter.directconverter.lrparser.itemset.Lr1ItemSet;
import de.fuberlin.bii.utils.Notification;
import de.fuberlin.bii.utils.Test;
/**
* Implementierung eines LR(1)-Parsers.
*
* @author Johannes Dahlke
*
* @param <Element> der Typ eines Elementes der zu verarbeitenden Eingabe.
*/
@SuppressWarnings("rawtypes")
public class Lr1ItemAutomat<Element extends Symbol> implements ItemAutomat<Element>, ItemAutomatInterior<Element, Lr1Closure> {
private HashSet<Lr1Closure> closures = new HashSet<Lr1Closure>();
private Lr1Closure currentClosure = null;
protected ContextFreeGrammar grammar;
protected ProductionRule startProduction;
private Stack<RuleElement> symbolStack;
private Stack<Lr1Closure> closureStack;
private Queue<Element> inputQueue;
private ReduceEventHandler reduceEventHandler;
private ShiftEventHandler shiftEventHandler;
private Lr1Closure startClosure;
protected boolean isReduceConflictFree = true;
protected Map<Lr1Closure, Map<RuleElement, AutomatEventHandler>> parserTable = new HashMap<Lr1Closure, Map<RuleElement, AutomatEventHandler>>() {
/**
*
*/
private static final long serialVersionUID = 8926758597140012585L;
@Override
public boolean containsKey( Object o) {
for ( Lr1Closure closure : this.keySet()) {
if ( closure.equals( (Lr1Closure) o))
return true;
}
return false;
}
@Override
public Map<RuleElement, AutomatEventHandler> get( Object key) {
for ( Lr1Closure closure : this.keySet()) {
if ( closure.equals( key))
return super.get( closure);
}
return null;
}
};
public Lr1ItemAutomat( ContextFreeGrammar grammar) {
super();
this.grammar = grammar;
InitializeAutomat();
}
private Lr1Closure calcStartClosure() {
Nonterminal startSymbol = grammar.getStartSymbol();
Lr1Item startItem = new Lr1Item( new Nonterminal( startSymbol.toString() + "'"), new RuleElement[] { startSymbol }, new Terminator());
startProduction = startItem.toProduction();
return calcClosureForStartItem( startItem, grammar);
}
private void InitializeAutomat() {
startClosure = calcStartClosure();
closures.add( startClosure);
currentClosure = startClosure;
SetupParserTable( startClosure);
InitializeStacks();
}
@SuppressWarnings("unchecked")
protected void SetupParserTable( Lr1Closure startClosure) {
HashSet<Lr1Closure> unhandledClosures = new HashSet<Lr1Closure>() {
/**
*
*/
private static final long serialVersionUID = 4775891156466011429L;
@Override
public boolean contains( Object o) {
for ( Lr1Closure closure : this) {
if ( closure.equals( (Lr1Closure) o))
return true;
}
return false;
}
};
int closureCounter = 0;
startClosure.setNumber( closureCounter++);
unhandledClosures.add( startClosure);
HashMap<Nonterminal, TerminalSet> followSets = grammar.getFollowSets();
RuleElement nextRuleElement;
while ( unhandledClosures.size() > 0) {
Lr1Closure currentClosure = unhandledClosures.iterator().next();
Map<RuleElement, AutomatEventHandler> handlerMap = new HandlerMap();
for ( Lr1Item item : currentClosure.getItemSet()) {
nextRuleElement = item.peekNextRuleElement();
if ( Test.isAssigned( nextRuleElement)) {
if ( nextRuleElement instanceof Terminal) {
// add shift actions
Lr1Closure toClosure = gotoNextClosure( currentClosure, nextRuleElement, this.grammar);
if ( !parserTable.containsKey( toClosure) && !unhandledClosures.contains( toClosure)) {
toClosure.setNumber( closureCounter++);
unhandledClosures.add( toClosure);
}
ShiftAction<Element, Lr1Closure> shiftAction = new ShiftAction<Element, Lr1Closure>( toClosure, (Terminal<Element>) nextRuleElement);
if ((handlerMap.containsKey( nextRuleElement)
&& !handlerMap.get( nextRuleElement).equals( shiftAction))) {
isReduceConflictFree = false;
// Conflict happen. Store the conflicting action as alternative for later error handling
if ( handlerMap.get( nextRuleElement) instanceof Action) {
Action seniorAction = shiftAction;
seniorAction.addAlternative( (Action) handlerMap.get( nextRuleElement));
handlerMap.put( nextRuleElement, seniorAction);
}
} else {
// otherwise there is not yet defined an action. So we add one
handlerMap.put( nextRuleElement, shiftAction);
}
} else if ( nextRuleElement instanceof Nonterminal) {
// add goto's
Lr1Closure toClosure = gotoNextClosure( currentClosure, nextRuleElement, this.grammar);
if ( !parserTable.containsKey( toClosure) && !unhandledClosures.contains( toClosure)) {
toClosure.setNumber( closureCounter++);
unhandledClosures.add( toClosure);
}
Goto gotoHandler = new Goto( toClosure, (Nonterminal) nextRuleElement);
// check for multiples with gotos not necessary
handlerMap.put( nextRuleElement, gotoHandler);
}
} else {
if ( item.toProduction().equals( startProduction))
// add accept action
handlerMap.put( new Terminator(), new AcceptAction());
else {
// add reduce actions
ProductionRule reduceProduction = item.toProduction();
ReduceAction<Element, Lr1Closure> reduceAction = new ReduceAction<Element, Lr1Closure>( reduceProduction);
Terminal lookahead = item.getLookahead();
if ((handlerMap.containsKey( lookahead)
&& !handlerMap.get( lookahead).equals( reduceAction))) {
isReduceConflictFree = false;
// Conflict happen. Store the conflicting action as alternative for later error handling
if ( handlerMap.get( lookahead) instanceof Action) {
Action seniorAction = (Action) handlerMap.get( lookahead);
seniorAction.addAlternative( reduceAction);
}
} else {
// otherwise there is not yet defined an action. So we add one
handlerMap.put( lookahead, reduceAction);
}
}
}
}
unhandledClosures.remove( currentClosure);
parserTable.put( currentClosure, handlerMap);
}
}
private void InitializeStacks() {
symbolStack = new Stack<RuleElement>();
symbolStack.add( new Terminator());
closureStack = new Stack<Lr1Closure>();
currentClosure = startClosure;
closureStack.add( currentClosure);
}
private void LoadInputIntoQueue( List<Element> input) {
inputQueue = new ArrayBlockingQueue<Element>( input.size());
for ( Element element : input) {
inputQueue.offer( element);
}
}
protected void ResetAutomat() {
closures = new HashSet<Lr1Closure>();
currentClosure = null;
isReduceConflictFree = true;
InitializeStacks();
}
@SuppressWarnings("unchecked")
public boolean match( List<Element> input) throws ItemAutomatException {
ResetAutomat();
LoadInputIntoQueue( input);
int currentSequenceNumber = 0;
Stack<ActionContext> alternativeActionStack = new Stack<ActionContext>();
AutomatEventHandler handler;
boolean accepted = false;
try {
do {
// get next to handle
currentClosure = closureStack.peek();
// we peek the next input element
Terminal terminalToHandle = inputQueue.isEmpty() ? new Terminator() : new Terminal( inputQueue.peek());
handler = parserTable.get( currentClosure).get( terminalToHandle);
alternativeAction: do {
try {
if ( handler instanceof Action
&& ((Action)handler).hasAlternative()) {
// put alternative on stack
Action alternativeAction = ((Action) handler).getAlternative();
ActionContext alternativeActionContext = new ActionContext( alternativeAction, currentSequenceNumber, this, currentClosure);
alternativeActionStack.push( alternativeActionContext);
}
handler.handleOnAutomat( this);
} catch ( Exception e) {
if ( !alternativeActionStack.isEmpty()) {
ActionContext<Element, Lr1Closure> alternativeActionContext = alternativeActionStack.pop();
restoreFromContext( alternativeActionContext);
currentSequenceNumber = alternativeActionContext.getSequenceNumber();
currentClosure = alternativeActionContext.getCurrentClosure();
handler = alternativeActionContext.getAction();
continue alternativeAction;
} else {
Notification.printDebugException( e);
throw new Exception( "Cannot interpret symbol before " + inputQueue.toString());
}
}
break;
} while( true);
if ( handler instanceof ReduceAction) {
currentClosure = closureStack.peek();
RuleElement nonterminalToHandle = symbolStack.peek();
// process the goto
assert nonterminalToHandle instanceof Nonterminal;
AutomatEventHandler gotoHandler = parserTable.get( currentClosure).get( nonterminalToHandle);
gotoHandler.handleOnAutomat( this);
// notify about reduce action
if ( Test.isAssigned( reduceEventHandler)) {
reduceEventHandler.handle( this, ((ReduceAction) handler).getReduceRule(), currentSequenceNumber);
}
} else if ( handler instanceof ShiftAction) {
if ( Test.isAssigned( shiftEventHandler))
shiftEventHandler.handle( this, (Terminal) terminalToHandle, currentSequenceNumber);
}
currentSequenceNumber++;
accepted = handler instanceof AcceptAction;
} while ( !accepted);
} catch ( Exception e) {
Notification.printDebugException( e);
throw new ItemAutomatException( e.getMessage());
//return false;
}
return true;
}
private void restoreFromContext( ActionContext<Element, Lr1Closure> actionContext) {
ActionContext.shallowCopyStack( actionContext.getItemAutomat().getClosureStack(), this.closureStack);
ActionContext.shallowCopyStack( actionContext.getItemAutomat().getSymbolStack(), this.symbolStack);
this.inputQueue = ActionContext.shallowCopyQueue( actionContext.getItemAutomat().getInputQueue());
}
private static Lr1Closure calcClosureForStartItem( Lr1Item startItem, ContextFreeGrammar grammar) {
Lr1ItemSet itemSet = new Lr1ItemSet();
itemSet.add( startItem);
Lr1Closure closure = calcClosureOfItemSet( itemSet, grammar);
closure.putAsKernelItem( startItem);
return closure;
}
private static Lr1Closure calcClosureOfItemSet( Lr1ItemSet itemSet, ContextFreeGrammar grammar) {
Lr1Closure result = new Lr1Closure();
// @see Drachenbuch S. 315
for ( Lr1Item item : itemSet) {
if ( item.getAnalysePosition() == 0)
result.addAsNonkernelItem( item);
else
result.addAsKernelItem( item);
}
boolean hasClosureGrown;
do {
hasClosureGrown = false;
Lr1ItemSet currentItemSet = new Lr1ItemSet( result.keySet());
for ( Lr1Item item : currentItemSet) {
RuleElement ruleElement = item.peekNextRuleElement();
Terminal lookahead = ((Lr1Item)item).getLookahead();
RuleElementSequenz sequenzLeftAfterNextRuleElement = ((Lr1Item)item).getSequenzLeftAfterNextRuleElement();
RuleElementSequenz sequenzForFirstSetLookup = new RuleElementArray( sequenzLeftAfterNextRuleElement, lookahead);
TerminalSet firstSetOfRuleElementSequenz = grammar.getFirstSetOfRuleElementSequenz( sequenzForFirstSetLookup);
if ( Test.isAssigned( ruleElement)
&& ruleElement instanceof Nonterminal) {
Nonterminal leftRuleSideCandidate = (Nonterminal) ruleElement;
HashSet<RuleElementSequenz> setOfRightRules = grammar.get( ruleElement);
if ( Test.isAssigned( setOfRightRules)) {
for ( RuleElementSequenz rightRuleSide : setOfRightRules) {
for ( Terminal lookAheadTerminal : firstSetOfRuleElementSequenz) {
Lr1Item newItem = new Lr1Item( leftRuleSideCandidate, rightRuleSide, lookAheadTerminal);
if ( !result.containsKey( newItem)) {
result.addAsNonkernelItem( newItem);
hasClosureGrown = true;
}
}
}
}
}
}
} while ( hasClosureGrown);
return result;
}
/**
* Formal: GOTO( I,X) = CLOSURE( { [A -> aX.b, y] | [A -> a.Xb, y] \in I })
*
* @param itemSet
* @return
*/
protected static Lr1Closure gotoNextClosure( Lr1Closure fromClosure, RuleElement transitionElement, ContextFreeGrammar grammar) {
Lr1ItemSet fromItemSet = fromClosure.getItemSet();
Lr1ItemSet toItemSet = new Lr1ItemSet();
for ( Lr1Item fromItem : fromItemSet) {
if ( transitionElement.equals( fromItem.peekNextRuleElement()))
toItemSet.add( new Lr1Item( fromItem, fromItem.getLookahead(), fromItem.getAnalysePosition() + 1));
}
return calcClosureOfItemSet( toItemSet, grammar);
}
public Stack<RuleElement> getSymbolStack() {
return symbolStack;
}
public Stack<Lr1Closure> getClosureStack() {
return closureStack;
}
public Queue<Element> getInputQueue() {
return inputQueue;
}
@Override
public String toString() {
String result = "";
List<Lr1Closure> closureList = new ArrayList<Lr1Closure>( parserTable.keySet());
List<ProductionRule> productionList = new ArrayList<ProductionRule>( grammar.getProductions());
RuleElement[] terminalElements = new RuleElement[grammar.getTerminals().size() + 1];
terminalElements = grammar.getTerminals().toArray( terminalElements);
terminalElements[grammar.getTerminals().size()] = new Terminator();
Arrays.sort( terminalElements);
RuleElement[] nonterminalElements = new RuleElement[grammar.getNonterminals().size()];
nonterminalElements = grammar.getNonterminals().toArray( nonterminalElements);
Arrays.sort( nonterminalElements);
Collections.sort( closureList, new Comparator<Lr1Closure>() {
public int compare( Lr1Closure c1, Lr1Closure c2) {
return c1.getNumber().compareTo( c2.getNumber());
}
});
for ( int i = 0; i < closureList.size(); i++) {
Lr1Closure closure = closureList.get( i);
Map<RuleElement, AutomatEventHandler> handlerMap = parserTable.get( closure);
if ( closure.getName().isEmpty())
closure.setNumber( closureList.indexOf( closure));
result += closure.getName() + "\t";
for ( RuleElement ruleElement : terminalElements) {
AutomatEventHandler handler = handlerMap.get( ruleElement);
if ( handler instanceof ShiftAction) {
result += " \t " + ruleElement + " : s" + closureList.indexOf( ( (ShiftAction) handler).getToClosure());
} else if ( handler instanceof ReduceAction) {
result += " \t " + ruleElement + " : r" + productionList.indexOf( ( (ReduceAction) handler).getReduceRule());
} else if ( handler instanceof AcceptAction) {
result += " \t " + ruleElement + " : acc";
} else {
result += " \t\t ";
}
}
for ( RuleElement ruleElement : nonterminalElements) {
AutomatEventHandler handler = handlerMap.get( ruleElement);
if ( handler instanceof Goto) {
result += " \t " + ruleElement + " : goto " + closureList.indexOf( ( (Goto) handler).getToClosure());
} else {
result += " \t\t ";
}
}
result += "\n";
}
return result;
}
/**
* Gibt die Grammatik zurück, gegen welche Eingaben geprüft werden.
* @return
*/
public ContextFreeGrammar getGrammar() {
return grammar;
}
public boolean isReduceConflictFree() {
return isReduceConflictFree;
}
public void setReduceEventHandler( ReduceEventHandler reduceEventHandler) {
this.reduceEventHandler = reduceEventHandler;
}
public void setShiftEventHandler( ShiftEventHandler shiftEventHandler) {
this.shiftEventHandler = shiftEventHandler;
}
}