/*
* 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.build;
import net.sourceforge.chaperon.common.IntegerList;
import net.sourceforge.chaperon.common.IntegerSet;
import net.sourceforge.chaperon.model.grammar.Error;
import net.sourceforge.chaperon.model.grammar.Grammar;
import net.sourceforge.chaperon.model.symbol.Nonterminal;
import net.sourceforge.chaperon.model.symbol.Symbol;
import net.sourceforge.chaperon.model.symbol.SymbolList;
import net.sourceforge.chaperon.model.symbol.SymbolSet;
import net.sourceforge.chaperon.model.symbol.Terminal;
/**
* This class represents a set of items, which means positions of production, in production and
* lookahead symbols. These item sets were used to decribes states.
*
* @author <a href="mailto:stephan@apache.org">Stephan Michels</a>
* @version CVS $Id: ItemSet.java,v 1.17 2003/12/09 19:55:52 benedikta Exp $
*/
public class ItemSet
{
private int capacityIncrement = 25;
private int elementCount = 0;
private int[] productions = new int[25];
private int[] positions = new int[25];
private SymbolSet[] lookaheads = new SymbolSet[25];
// The symbols, which translate the states into other states
private SymbolSet transitionsymbols = new SymbolSet();
private IntegerList transitionstates = new IntegerList();
private Grammar grammar;
private FirstSetCollection firstsets;
private static final EmptyList EMPTYLIST = new EmptyList();
/**
* Create an empty set of items.
*
* @param grammar Grammar.
* @param firstsets The first sets.
*/
public ItemSet(Grammar grammar, FirstSetCollection firstsets)
{
this.grammar = grammar;
this.firstsets = firstsets;
}
/**
* Create a state, which contains this itemset.
*
* @param grammar Grammar.
* @param firstsets The first sets.
* @param I Itemset, which the state should contain.
*/
private ItemSet(Grammar grammar, FirstSetCollection firstsets, ItemSet I)
{
this.grammar = grammar;
this.firstsets = firstsets;
addItemSet(I);
}
/**
* Add a item to this set.
*
* @param production Production.
* @param position Position in this production.
* @param lookahead Lookahead symbol.
*
* @return True, if this item was added
*/
public boolean addItem(int production, int position, Symbol lookahead)
{
if (lookahead==null)
throw new NullPointerException("Lookahead symbol is null");
for (int i = 0; i<elementCount; i++)
if ((productions[i]==production) && (positions[i]==position))
{
if (lookaheads[i]==null)
lookaheads[i] = new SymbolSet();
return lookaheads[i].addSymbol(lookahead);
}
ensureCapacity(elementCount+1);
productions[elementCount] = production;
positions[elementCount] = position;
lookaheads[elementCount] = new SymbolSet();
lookaheads[elementCount++].addSymbol(lookahead);
return true;
}
public boolean addItem(int production, int position, SymbolSet lookaheads)
{
if (lookaheads==null)
throw new NullPointerException("Lookahead symbol set is null");
for (int i = 0; i<elementCount; i++)
if ((productions[i]==production) && (positions[i]==position))
{
if (this.lookaheads[i]==null)
this.lookaheads[i] = new SymbolSet();
return this.lookaheads[i].addSymbol(lookaheads);
}
ensureCapacity(elementCount+1);
productions[elementCount] = production;
positions[elementCount] = position;
this.lookaheads[elementCount] = new SymbolSet();
this.lookaheads[elementCount++].addSymbol(lookaheads);
return true;
}
/**
* Add the items from another itemset to this set. If this set changed the method will return
* true.
*
* @param I ItemSet, which should be addded.
*
* @return True, if this set was changed
*/
public boolean addItemSet(ItemSet I)
{
boolean changed = false;
for (int i = 0; i<I.elementCount; i++)
changed |= addItem(I.productions[i], I.positions[i], I.lookaheads[i]);
return changed;
}
/**
* If this set contains an item, which compare by the production, position and lookahead symbol.
*
* @param production Index of production in the grammar.
* @param position Position in the production.
* @param lookahead Lookahead symbol.
*
* @return True, if this set contains the item.
*/
/*private boolean containsItem(int production, int position, Symbol lookahead)
{
if (lookahead==null)
throw new NullPointerException("Lookahead symbol is null");
for (int i = 0; i<elementCount; i++)
if ((productions[i]==production) && (positions[i]==position))
return lookaheads[i].contains(lookahead);
return false;
}*/
/**
* If this set contains an item, which compare by the production and position.
*
* @param production Index of production in the grammar.
* @param position Position in the production.
*
* @return True, if this set contains the core of the item.
*/
/*private boolean containsItemCore(int production, int position)
{
for (int i = 0; i<elementCount; i++)
if ((productions[i]==production) && (positions[i]==position))
return true;
return false;
}*/
/**
* Test, if all items from a other state exists in this state
*
* @param itemset Other item set.
*
* @return True, if the state contains all items.
*/
public boolean contains(ItemSet itemset)
{
for (int i = 0; i<itemset.elementCount; i++)
{
int production = itemset.productions[i];
int position = itemset.positions[i];
boolean found = false;
for (int j = 0; j<elementCount; j++)
if ((productions[j]==production) && (positions[j]==position))
{
found = lookaheads[j].contains(itemset.lookaheads[i]);
break;
}
if (!found)
return false;
}
return true;
}
/**
* Test, if all cores of the items from another item set exists in this item set.
*
* @param itemset Other item set.
*
* @return True, if the state contains all cores the items.
*/
private boolean containsCore(ItemSet itemset)
{
for (int i = 0; i<itemset.elementCount; i++)
{
int production = itemset.productions[i];
int position = itemset.positions[i];
boolean found = false;
for (int j = 0; (j<elementCount) && !found; j++)
found = ((productions[j]==production) && (positions[j]==position));
if (!found)
return false;
}
return true;
}
/**
* Returns the count of items in this set.
*
* @return Count of items of the core.
*/
public int getItemCount()
{
return elementCount;
}
/**
* Returns true, if this set is empty.
*
* @return True, if this set is empty.
*/
public boolean isEmpty()
{
return (elementCount==0);
}
/**
* Compares two item sets.
*
* @param o Other itemset.
*
* @return True, if the item sets are equal.
*/
public boolean equals(Object o)
{
if (o instanceof ItemSet)
{
ItemSet itemset = (ItemSet)o;
if (itemset.getItemCount()!=getItemCount())
return false;
// The itemset must contain all item from this set.
if (!contains(itemset))
return false;
// And this set must contain all item from the item set
if (!itemset.contains(this))
return false;
return true;
}
return false;
}
/**
* Compares the core of two item sets.
*
* @param o Other itemset.
*
* @return True, if the core of the itemsets are equal.
*/
public boolean equalsCore(Object o)
{
if (o instanceof ItemSet)
{
ItemSet itemset = (ItemSet)o;
// The itemset must contain all item from this set.
if (!containsCore(itemset))
return false;
// And this set must contain all item from the item set
if (!itemset.containsCore(this))
return false;
return true;
}
return false;
}
/**
* Return the next Symbol, which follow the item.
*
* @param index Index the item.
*
* @return Symbol of the next position, otherwise the symbol for an empty list.
*/
private Symbol getItemNext(int index)
{
SymbolList productiondefinition;
if (positions[index]<((productiondefinition =
grammar.getProduction(productions[index]).getDefinition()).getSymbolCount()))
return productiondefinition.getSymbol(positions[index]);
return EMPTYLIST;
}
public SymbolSet getNextTerminals()
{
SymbolSet set = new SymbolSet();
SymbolList productiondefinition;
for (int item = 0; item<elementCount; item++)
if ((positions[item]<((productiondefinition =
grammar.getProduction(productions[item]).getDefinition()).getSymbolCount())) &&
(productiondefinition.getSymbol(positions[item]) instanceof Terminal))
set.addSymbol(productiondefinition.getSymbol(positions[item]));
return set;
}
public SymbolSet getNextNonterminals()
{
SymbolSet set = new SymbolSet();
SymbolList productiondefinition;
for (int item = 0; item<elementCount; item++)
if ((positions[item]<((productiondefinition =
grammar.getProduction(productions[item]).getDefinition()).getSymbolCount())) &&
(productiondefinition.getSymbol(positions[item]) instanceof Nonterminal))
set.addSymbol(productiondefinition.getSymbol(positions[item]));
return set;
}
public Error getNextError()
{
SymbolList productiondefinition;
for (int item = 0; item<elementCount; item++)
if ((positions[item]<((productiondefinition =
grammar.getProduction(productions[item]).getDefinition()).getSymbolCount())) &&
(productiondefinition.getSymbol(positions[item]) instanceof Error))
return (Error)productiondefinition.getSymbol(positions[item]);
return null;
}
/**
* Calculate the closure for this item set.
*
* @return Closure of the item set
*/
public ItemSet closure()
{
ItemSet J = new ItemSet(grammar, firstsets, this); // J=I
SymbolSet b = new SymbolSet();
SymbolSet b2 = new SymbolSet();
boolean changed = false;
do
{
changed = false;
// for every item in itemset I
for (int i = 0; i<J.elementCount; i++)
{
SymbolList productiondefinition = grammar.getProduction(J.productions[i]).getDefinition();
// and not A=XYZ^
if (J.positions[i]<productiondefinition.getSymbolCount())
{
Symbol symbol = productiondefinition.getSymbol(J.positions[i]); // A=X ^symbol Z
// for every item [A=u^Bv,a] in J and production B=w in G
if (symbol instanceof Nonterminal)
{
int pos = J.positions[i]+1; // for the FIRST set from (va)
b.clear();
// if [A=u^Bv,a]
if (pos<productiondefinition.getSymbolCount())
{
// then is b the list of all terminal symbols from FIRST(va)
do
{
if (productiondefinition.getSymbol(pos) instanceof Terminal)
{
b2.clear();
b2.addSymbol(productiondefinition.getSymbol(pos));
}
else
{
b2.clear();
b2.addSymbol(firstsets.getFirstSet(productiondefinition.getSymbol(pos)));
}
b.addSymbol(b2);
pos++;
}
while ((b2.contains(EMPTYLIST)) && (pos<productiondefinition.getSymbolCount()));
if (b.contains(EMPTYLIST))
b.addSymbol(J.lookaheads[i]);
b.removeSymbol(EMPTYLIST);
}
else if (pos>=productiondefinition.getSymbolCount())
// otherwise is b FIRST(a)
b.addSymbol(J.lookaheads[i]);
// list of all productions B
IntegerList productionlist = grammar.getProductionList(symbol);
// for alle productions B
for (int j = 0; j<productionlist.getCount(); j++)
{
// if J doesn't contain [B=^w,b] , should it added
for (int k = 0; k<b.getSymbolCount(); k++)
changed |= J.addItem(productionlist.get(j), 0, b.getSymbol(k));
}
}
}
}
}
while (changed);
return J;
}
/**
* Calculates the next state by a transition through the symbol X.
*
* @param symbol A Symbol, which can be a terminal or a nonterminal symbol.
*
* @return The next state, represented by an item set.
*/
public ItemSet jump(Symbol symbol)
{
ItemSet J = new ItemSet(grammar, firstsets);
// For every item [A=u^Xv,a] in I
for (int i = 0; i<elementCount; i++)
{
if (getItemNext(i).equals(symbol))
// add [A=uX^v,a] to J
J.addItem(productions[i], positions[i]+1, lookaheads[i]);
}
// jump(I,X) = closure(J)
return J.closure();
}
/**
* Add a transition to this state.
*
* @param symbol Symbol, which forces a transition into another state.
* @param state Destination state.
*/
public void setTransition(Symbol symbol, int state)
{
if (transitionsymbols.contains(symbol))
transitionstates.set(transitionsymbols.indexOf(symbol), state);
else
{
transitionsymbols.addSymbol(symbol);
transitionstates.add(state);
}
}
/**
* Returns the destination state of a transition.
*
* @param symbol Symbol, which force the transition.
*
* @return Destination state.
*/
public int getTransition(Symbol symbol)
{
if (transitionsymbols.contains(symbol))
return transitionstates.get(transitionsymbols.indexOf(symbol));
return -1;
}
/**
* Returns all symbols, which forces a transition.
*
* @return List of symbols.
*/
public SymbolSet getShiftSymbols()
{
return transitionsymbols;
}
/**
* Returns the list of productions, which could be reduced.
*
* @return List of indicies for the productions.
*/
public IntegerSet getReduceProductions()
{
IntegerSet reduceproductions = new IntegerSet();
for (int i = 0; i<elementCount; i++)
{
if (getItemNext(i).equals(EMPTYLIST)) // for all A=u^ and all symbols in FOLLOW(A)
reduceproductions.add(productions[i]);
}
return reduceproductions;
}
/**
* Returns the list of productions, which a special symbol reduce.
*
* @param lookahead Symbol, which the productions reduce.
*
* @return Set of indicies from the productions.
*/
public IntegerSet getReduceProductions(Symbol lookahead)
{
IntegerSet reduceproductions = new IntegerSet();
for (int i = 0; i<elementCount; i++)
{
// for all A=u^ and all symbols in FOLLOW(A)
if ((getItemNext(i).equals(EMPTYLIST)) &&
(lookaheads[i].contains(lookahead) || lookaheads[i].contains(Error.instance)))
reduceproductions.add(productions[i]);
}
return reduceproductions;
}
/**
* Return all symbols, which reduce productions in this state.
*
* @return Set of symbols.
*/
public SymbolSet getReduceSymbols()
{
SymbolSet reducesymbols = new SymbolSet();
for (int i = 0; i<elementCount; i++)
{
if (getItemNext(i).equals(EMPTYLIST)) // for all A=u^ and all symbols in FOLLOW(A)
reducesymbols.addSymbol(lookaheads[i]);
}
return reducesymbols;
}
/**
* Ensure the capacity for adding items.
*
* @param minCapacity Minimum capacity.
*/
private void ensureCapacity(int minCapacity)
{
if (productions.length>=minCapacity)
return;
int newCapacity = productions.length+capacityIncrement;
if (capacityIncrement<=0)
newCapacity = productions.length*2;
int[] newProductions = new int[Math.max(newCapacity, minCapacity)];
int[] newPositions = new int[Math.max(newCapacity, minCapacity)];
SymbolSet[] newLookaheads = new SymbolSet[Math.max(newCapacity, minCapacity)];
System.arraycopy(productions, 0, newProductions, 0, productions.length);
System.arraycopy(positions, 0, newPositions, 0, productions.length);
System.arraycopy(lookaheads, 0, newLookaheads, 0, productions.length);
productions = newProductions;
positions = newPositions;
lookaheads = newLookaheads;
}
/**
* Return a string representation of this item set.
*
* @return String representation of this item set.
*/
public String toString()
{
StringBuffer buffer = new StringBuffer();
SymbolList list;
for (int production = 0; production<grammar.getProductionCount(); production++)
{
list = grammar.getProduction(production).getDefinition();
for (int position = 0; position<=list.getSymbolCount(); position++)
{
for (int item = 0; item<elementCount; item++)
if ((productions[item]==production) && (positions[item]==position))
{
buffer.append(grammar.getProduction(production).getSymbol());
buffer.append(" -> ");
for (int i = 0; i<list.getSymbolCount(); i++)
{
if (i==position)
buffer.append(".");
buffer.append(list.getSymbol(i)+" ");
}
if (position==list.getSymbolCount())
buffer.append(".");
buffer.append(" , ");
for (int lookahead = 0; lookahead<lookaheads[item].getSymbolCount(); lookahead++)
{
if (lookahead>0)
buffer.append(" / ");
buffer.append(lookaheads[item].getSymbol(lookahead).toString());
}
buffer.append("\n");
break;
}
}
}
SymbolSet set = getShiftSymbols();
for (int index = 0; index<set.getSymbolCount(); index++)
{
buffer.append("Transition for ");
buffer.append(set.getSymbol(index));
buffer.append(" -> State ");
buffer.append(String.valueOf(getTransition(set.getSymbol(index))));
buffer.append("\n");
}
return buffer.toString();
}
}