/*
* Copyright (C) Chaperon. All rights reserved.
* -------------------------------------------------------------------------
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE file.
*/
package net.sourceforge.chaperon.process.extended;
import net.sourceforge.chaperon.common.Decoder;
import net.sourceforge.chaperon.common.SortedCharSet;
import net.sourceforge.chaperon.common.StringSet;
import net.sourceforge.chaperon.model.extended.Definition;
import net.sourceforge.chaperon.model.extended.Element;
import net.sourceforge.chaperon.model.extended.ExtendedGrammar;
import net.sourceforge.chaperon.model.extended.Pattern;
import net.sourceforge.chaperon.model.extended.PatternIterator;
import net.sourceforge.chaperon.model.extended.PatternSet;
import org.apache.commons.logging.Log;
import java.util.HashMap;
/**
* This class contains a automaton of states.
*
* @author <a href="mailto:stephan@apache.org">Stephan Michels</a>
* @version CVS $Id: ExtendedParserAutomaton.java,v 1.3 2004/01/08 11:30:52 benedikta Exp $
*/
public class ExtendedParserAutomaton
{
//private ArrayList states = new ArrayList();
public State first = null;
private ExtendedGrammar grammar;
private Log log;
// intermediate used sets
private String[] symbols;
private PatternSet[] firstSets;
private PatternSet[] lastSets;
private boolean[] nullable;
private HashMap definitions = new HashMap();
//private PatternMap kernelFollowSet;
//private PatternMap followFirstSet;
//private PatternMap followLastSet;
/**
* Create a new automaton of states, calculated with the aid of the grammar. The constructor
* calculates all state and transitions and combine all states with the same core.
*
* @param grammar ExtendedGrammar.
*/
public ExtendedParserAutomaton(ExtendedGrammar grammar)
{
this(grammar, null);
}
/**
* Create a new automaton of states, calculated with the aid of the grammar. The constructor
* calculates all state and transitions and combine all states with the same core.
*
* @param grammar ExtendedGrammar
* @param firstsets First sets.
* @param log Log, which should be used.
*/
public ExtendedParserAutomaton(ExtendedGrammar grammar, Log log)
{
this.grammar = grammar;
this.log = log;
symbols = new String[grammar.getDefinitionCount()];
firstSets = new PatternSet[grammar.getDefinitionCount()];
lastSets = new PatternSet[grammar.getDefinitionCount()];
nullable = new boolean[grammar.getDefinitionCount()];
definitions = new HashMap();
for (int i = 0; i<grammar.getDefinitionCount(); i++)
{
symbols[i] = grammar.getDefinition(i).getSymbol();
firstSets[i] = grammar.getDefinition(i).getFirstSet();
lastSets[i] = grammar.getDefinition(i).getLastSet();
nullable[i] = grammar.getDefinition(i).isNullable();
for (PatternIterator pattern = firstSets[i].getPattern(); pattern.hasNext();)
definitions.put(pattern.next(), grammar.getDefinition(i));
for (PatternIterator pattern = lastSets[i].getPattern(); pattern.hasNext();)
definitions.put(pattern.next(), grammar.getDefinition(i));
}
grammar.update();
//System.out.println("followLastSet=\n"+followLastSet);
// C = closure( [S'=^S] )
addState(getStartState());
for (State state = first; state!=null; state = state.next)
{
//State state = getState(i);
//System.out.println("\nState "+i+"\n"+state);
SortedCharSet limits = new SortedCharSet();
StringSet nonterminals = new StringSet();
PatternSet gotoPattern = new PatternSet();
for (Item item = state.first; item!=null; item = item.next)
{
if (item.position==Item.SHIFT)
{
if (item.pattern.getSymbol()!=null)
nonterminals.addString(item.pattern.getSymbol());
limits.addChar(item.pattern.getLimits());
}
else if (item.position==Item.GOTO)
gotoPattern.addPattern(item.pattern);
limits.addChar(item.lookahead.getLimits());
}
// System.out.println("limits="+limits);
// System.out.println("nonterminals="+nonterminals);
// System.out.println("gotoPattern="+gotoPattern);
// for all other characters from the begin
if ((limits.getCharCount()>=1) && (limits.getChar(0)>'\u0000'))
{
addShiftAction(state, '\u0000', (char)(limits.getChar(0)-1));
addReduceAction(state, '\u0000', (char)(limits.getChar(0)-1));
}
// for each character
for (int j = 0; j<limits.getCharCount(); j++)
{
if ((j>0) && ((limits.getChar(j)-limits.getChar(j-1))>1))
{
addShiftAction(state, (char)(limits.getChar(j-1)+1), (char)(limits.getChar(j)-1));
addReduceAction(state, (char)(limits.getChar(j-1)+1), (char)(limits.getChar(j)-1));
}
addShiftAction(state, limits.getChar(j), limits.getChar(j));
addReduceAction(state, limits.getChar(j), limits.getChar(j));
}
// for all other characters to the end
if (limits.getCharCount()>=1)
{
addShiftAction(state, (char)(limits.getChar(limits.getCharCount()-1)+1), '\u4000');
addReduceAction(state, (char)(limits.getChar(limits.getCharCount()-1)+1), '\u4000');
}
// for universal characters
if (limits.getCharCount()==0)
{
addShiftAction(state, '\u0000', '\u4000');
addReduceAction(state, '\u0000', '\u4000');
}
// for each nonterminal
for (int j = 0; j<nonterminals.getStringCount(); j++)
addGotoAction(state, nonterminals.getString(j));
for (PatternIterator pattern = gotoPattern.getPattern(); pattern.hasNext();)
addGotoAction(state, pattern.next());
addReduceAction(state);
}
}
private boolean isNullable(String symbol)
{
for (int i = 0; i<symbols.length; i++)
if (symbols[i].equals(symbol))
return nullable[i];
return true;
}
private String getSymbol(Pattern pattern)
{
return ((Definition)definitions.get(pattern)).getSymbol();
}
/**
* Return the state as start point for the calculation.
*
* @return Start point for the calculation.
*/
private State getStartState()
{
if (grammar.getStartSymbol()==null)
throw new IllegalArgumentException("Start symbol is not defined");
PatternSet firstSet = grammar.getFirstSet();
System.out.println("firstset = "+firstSet);
State state = new State(grammar);
for (PatternIterator pattern = firstSet.getPattern(); pattern.hasNext();)
{
Pattern firstPattern = pattern.next();
Item item =
new Item(((Definition)definitions.get(firstPattern)).getSymbol(), firstPattern, Item.SHIFT,
null);
//item.end = true;
state.addItem(item);
}
closure(state);
return state;
}
public ExtendedGrammar getExtendedGrammar()
{
return grammar;
}
/**
* Add a state to this automaton.
*
* @param state State.
*
* @return Index of the state in the automaton.
*/
public State addState(State newState)
{
if (first==null)
{
first = newState;
return newState;
}
for (State state = first; state!=null; state = state.next)
{
if (state.equals(newState))
return state;
if (state.next==null)
{
state.next = newState;
return newState;
}
}
return newState;
}
/**
* Return the index of an state. If the automaton does not contain the state, then return this
* method will return -1.
*
* @param state State, which should be found.
*
* @return Index of the state.
*/
public int indexOf(State foreignState)
{
int index = 0;
for (State state = first; state!=null; state = state.next, index++)
if (state.equals(foreignState))
return index;
return -1;
}
/**
* If this automaton contains the state.
*
* @param state State, which should be found.
*
* @return True, if the automaton contains the state.
*/
public boolean contains(State foreignState)
{
for (State state = first; state!=null; state = state.next)
if (state.equals(foreignState))
return true;
return false;
}
public void closure(State state)
{
System.out.println("=====================================\nbefore:\n"+state);
for (Item item = state.first; item!=null; item = item.next)
{
System.out.println("item = "+item+" position="+item.position+" element="+
(item.pattern instanceof Element));
if ((item.position==Item.SHIFT) && (item.pattern instanceof Element))
{
System.out.println("add firstset");
String symbol = ((Element)item.pattern).getSymbol();
System.out.println("pattern="+item.pattern);
Definition definition = ((Definition)definitions.get(item.pattern));
PatternSet firstSet = grammar.getFirstSet(symbol);
for (PatternIterator pattern = firstSet.getPattern(); pattern.hasNext();)
{
Pattern firstPattern = pattern.next();
//Definition definition = ((Definition)definitions.get(firstSet.getPattern(l)));
if (definition.getLastSet().contains(item.pattern))
state.addItem(new Item(symbol, firstPattern, Item.SHIFT, item.lookahead));
for (PatternIterator lookaheads = item.pattern.getSuccessors(); lookaheads.hasNext();)
{
Pattern lookahead = lookaheads.next();
if (!(lookahead instanceof Element))
state.addItem(new Item(symbol, firstPattern, Item.SHIFT, lookahead));
}
for (PatternIterator lookaheads = item.pattern.getAscendingSuccessors();
lookaheads.hasNext();)
{
Pattern lookahead = lookaheads.next();
if (!(lookahead instanceof Element))
state.addItem(new Item(symbol, firstPattern, Item.SHIFT, lookahead));
}
}
}
}
System.out.println("after:\n"+state+"\n");
}
private void addShiftAction(State state, char minimum, char maximum)
{
State newState = new State(grammar);
for (Item item = state.first; item!=null; item = item.next)
if ((item.position==Item.SHIFT) && (item.pattern.contains(minimum, maximum)))
{
newState.addItem(item.getFollowItem());
for (PatternIterator successors = item.pattern.getSuccessors(); successors.hasNext();)
newState.addItem(new Item(item.pattern, successors.next(), Item.SHIFT, item.lookahead));
}
if (!newState.isEmpty())
{
closure(newState);
newState = addState(newState);
//System.out.println("add shift action for "+Decoder.toClass(minimum, maximum));
state.addShiftAction(minimum, maximum, newState);
}
}
private void addGotoAction(State state, String symbol)
{
State newState = new State(grammar);
for (Item item = state.first; item!=null; item = item.next)
if ((item.position==Item.SHIFT) && (item.pattern instanceof Element) &&
(((Element)item.pattern).getSymbol().equals(symbol)))
{
newState.addItem(item.getFollowItem());
for (PatternIterator successors = item.pattern.getSuccessors(); successors.hasNext();)
newState.addItem(new Item(item.pattern, successors.next(), Item.SHIFT, item.lookahead));
}
if (!newState.isEmpty())
{
closure(newState);
newState = addState(newState);
state.addGotoAction(symbol, newState);
}
}
private void addGotoAction(State state, Pattern pattern)
{
State newState = new State(grammar);
for (Item item = state.first; item!=null; item = item.next)
if ((item.position==Item.GOTO) && (item.pattern==pattern))
newState.addItem(item.getFollowItem());
if (!newState.isEmpty())
{
closure(newState);
newState = addState(newState);
state.addGotoAction(pattern, newState);
}
}
private void addReduceAction(State state, char minimum, char maximum)
{
for (Item item = state.first; item!=null; item = item.next)
if (item.position==Item.SHIFT)
{
for (PatternIterator successors = item.pattern.getDescendingSuccessors();
successors.hasNext();)
if (successors.next().contains(minimum, maximum))
if ((item.pattern.getSymbol()!=null) && (isNullable(item.pattern.getSymbol())))
{
state.addLookaheadReduceAction(minimum, maximum, item.pattern.getSymbol(), 0);
break;
}
}
else if (item.position==Item.GOTO)
{
for (int k = 0; k<lastSets.length; k++)
if (lastSets[k].contains(item.pattern))
{
for (PatternIterator successors = item.pattern.getDescendingSuccessors();
successors.hasNext();)
if (successors.next().contains(minimum, maximum))
{
state.addLookaheadReduceAction(minimum, maximum, item.pattern, 0);
break;
}
}
}
else if (item.position==Item.REDUCE)
{
for (PatternIterator successors = item.pattern.getDescendingSuccessors();
successors.hasNext();)
if (successors.next().contains(minimum, maximum))
{
if (item.symbol!=null)
state.addLookaheadReduceAction(minimum, maximum, item.symbol, 2);
else
state.addLookaheadReduceAction(minimum, maximum, item.previous, 2);
break;
}
}
}
private void addReduceAction(State state)
{
for (Item item = state.first; item!=null; item = item.next)
if (item.position==Item.SHIFT)
{
if (grammar.getLastSet().contains(item.pattern))
if ((item.pattern instanceof Element) &&
(isNullable(((Element)item.pattern).getSymbol())))
state.addReduceAction(((Element)item.pattern).getSymbol(), 0);
}
else if (item.position==Item.GOTO)
{
for (int k = 0; k<lastSets.length; k++)
if (lastSets[k].contains(item.pattern))
{
if (grammar.getLastSet().contains(item.pattern))
state.addReduceAction(item.pattern, 0);
}
}
else if (item.position==Item.REDUCE)
{
if (grammar.getLastSet().contains(item.pattern))
{
if (item.symbol!=null)
state.addReduceAction(item.symbol, 2);
else
state.addReduceAction(item.previous, 2);
}
}
}
/**
* Return a string representation of the automaton.
*
* @return String representation of the automaton.
*/
public String toString()
{
StringBuffer buffer = new StringBuffer();
for (State state = first; state!=null; state = state.next)
{
buffer.append("State ");
buffer.append(indexOf(state));
buffer.append(":\n");
buffer.append(state.toString());
//buffer.append(grammar.toString(getState(i).getPreviousPattern(), getState(i).getNextPattern()));
//buffer.append(":\n");
ShiftAction[] shiftActions = state.getShiftActions();
for (int index = 0; index<shiftActions.length; index++)
{
buffer.append("Shift ");
buffer.append(Decoder.toClass(shiftActions[index].minimum, shiftActions[index].maximum));
buffer.append(" -> State ");
buffer.append(indexOf(shiftActions[index].state));
buffer.append("\n");
}
LookaheadReduceAction[] lookaheadReduceActions = state.getLookaheadReduceActions();
for (int index = 0; index<lookaheadReduceActions.length; index++)
{
buffer.append("Reduce ");
if (lookaheadReduceActions[index].symbol!=null)
buffer.append(lookaheadReduceActions[index].symbol);
else
buffer.append(lookaheadReduceActions[index].pattern);
buffer.append(" for ");
buffer.append(Decoder.toClass(lookaheadReduceActions[index].minimum,
lookaheadReduceActions[index].maximum));
buffer.append("\n");
}
ReduceAction[] reduceActions = state.getReduceActions();
for (int index = 0; index<reduceActions.length; index++)
{
buffer.append("Reduce ");
if (reduceActions[index].symbol!=null)
buffer.append(reduceActions[index].symbol);
else
buffer.append(reduceActions[index].pattern);
buffer.append("\n");
}
GotoAction[] gotoactions = state.getGotoActions();
for (int index = 0; index<gotoactions.length; index++)
{
buffer.append("Goto ");
if (gotoactions[index].symbol!=null)
buffer.append(gotoactions[index].symbol);
else
buffer.append(gotoactions[index].pattern);
buffer.append(" -> State ");
buffer.append(indexOf(gotoactions[index].state));
buffer.append("\n");
}
buffer.append("\n");
}
return buffer.toString();
}
}