/*
* 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.parser.generator;
import java.util.Vector;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;
import net.sourceforge.chaperon.grammar.Grammar;
import net.sourceforge.chaperon.grammar.symbol.EmptySymbol;
import net.sourceforge.chaperon.grammar.symbol.Symbol;
import net.sourceforge.chaperon.grammar.symbol.SymbolList;
import net.sourceforge.chaperon.helpers.IntegerList;
/**
* This class represents a set of items
*
* @author Stephan Michels
* @version CVS $Id: ItemSet.java,v 1.7 2002/05/06 16:22:57 benedikta Exp $
*/
public class ItemSet
{
private int _capacityIncrement = 100;
private int _elementCount = 0;
private int[] _productions = new int[10];
private int[] _positions = new int[10];
private Symbol[] _lookaheads = new Symbol[10];
// The symbols, which translate the states into other states
private SymbolList _transitionsymbols = new SymbolList(true);
private IntegerList _transitionstates = new IntegerList();
private Grammar _grammar;
private FirstSets _firstsets;
/**
* Create an empty set of items
*
* @param grammar Grammar
* @param firstsets The first sets
*/
public ItemSet(Grammar grammar, FirstSets firstsets)
{
_grammar = grammar;
_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, FirstSets firstsets, ItemSet I)
{
_grammar = grammar;
_firstsets = firstsets;
add(I);
}
/**
* Add a item to this set
*
* @param production Production
* @param position Position in this production
* @param lookahead Lookahead symbol
*/
public void addItem(int production, int position, Symbol lookahead)
{
if (!containsItem(production, position, lookahead))
{
ensureCapacity(_elementCount + 1);
_productions[_elementCount] = production;
_positions[_elementCount] = position;
_lookaheads[_elementCount++] = lookahead;
}
}
/**
* Add the items from a itemset to this set.
* If this set changed the method return true.
*
* @param I ItemSet
*
* @return True, if this set changed
*/
public boolean add(ItemSet I)
{
boolean changed = false;
for (int i = 0; i < I._elementCount; i++)
if (!containsItem(I._productions[i], I._positions[i], I._lookaheads[i]))
{
addItem(I._productions[i], I._positions[i], I._lookaheads[i]);
changed = true;
}
return changed;
}
/**
* Get the production from an item from this set
*
* @param index Index of item
*
* @return Index of the production
*/
/*private int getItemProduction(int index)
{
return productions[index];
}*/
/**
* Get the position from an item from this set
*
* @param index Index of item
*
* @return Position in the porduction
*/
/*private int getItemPosition(int index)
{
return positions[index];
}*/
/**
* Get the symbol from an item from this set
*
* @param index Index of item
*
* @return Symbol
*/
/*private Symbol getItemLookAhead(int index)
{
return lookaheads[index];
}*/
/**
* 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)
{
for (int i = 0; i < _elementCount; i++)
if ((_productions[i] == production) &&
(_positions[i] == position) &&
(_lookaheads[i].equals(lookahead)))
return true;
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 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 state
*
* @return True, if the state contains all items
*/
private boolean contains(ItemSet itemset)
{
int i, j;
int production, position;
Symbol lookahead;
boolean found;
for (i = 0; i < itemset._elementCount; i++)
{
production = itemset._productions[i];
position = itemset._positions[i];
lookahead = itemset._lookaheads[i];
found = false;
for (j = 0; (j < _elementCount) && !found; j++)
if ((_productions[j] == production) &&
(_positions[j] == position) &&
(_lookaheads[j]==lookahead))
found = true;
if (!found)
return false;
}
return true;
}
/**
* Test, if all cores of the items from a other
* state exists in this state
*
* @param itemset Other state
*
* @return True, if the state contains all cores the
* items
*/
private boolean containsCore(ItemSet itemset)
{
int i, j;
int production, position;
boolean found;
for (i = 0; i < itemset._elementCount; i++)
{
production = itemset._productions[i];
position = itemset._positions[i];
found = false;
for (j = 0; (j < _elementCount) && !found; j++)
if ((_productions[j] == production) &&
(_positions[j] == position))
found = true;
if (!found)
return false;
}
return true;
}
/**
* Returns the count of items in this set
*
* @return Count of items
*/
public int getSize()
{
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 itemsets are equal
*/
public boolean equals(Object o)
{
if (o instanceof ItemSet)
{
ItemSet itemset = (ItemSet) o;
if (itemset.getSize() != getSize())
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
*/
private Symbol getItemNext(int index)
{
SymbolList productiondefinition;
if (_positions[index] < ((productiondefinition =
_grammar.getProductionList().getProduction(_productions[index]).getDefinition()).getSymbolCount()))
return productiondefinition.getSymbol(_positions[index]);
return EmptySymbol.EMPTY;
}
/**
* Complete the itemset
*
* @return The new Itemset
*/
public ItemSet closure()
{
ItemSet J = new ItemSet(_grammar, _firstsets, this); // J=I
int i, j, k; // Index variables
SymbolList productiondefinition;
SymbolList b = new SymbolList();
SymbolList b2 = new SymbolList(true);
IntegerList productionlist;
Symbol symbol;
// for every item in itemset I
for (i = 0; i < J._elementCount; i++)
{
productiondefinition = _grammar.getProductionList().getProduction(J._productions[i]).getDefinition();
// and not A=XYZ^
if (J._positions[i] < productiondefinition.getSymbolCount())
{
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.isTerminal())
{
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 alle terminal symbols from FIRST(va)
do
{
if (productiondefinition.getSymbol(pos).isTerminal())
{
b2.clear();
b2.addSymbol(productiondefinition.getSymbol(pos));
}
else
b2 = _firstsets.getFirstSet(productiondefinition.getSymbol(pos));
b.addSymbolList(b2);
pos++;
}
while ((b2.contains(EmptySymbol.EMPTY)) && (pos < productiondefinition.getSymbolCount()));
if (b.contains(EmptySymbol.EMPTY))
b.addSymbol(J._lookaheads[i]);
b.removeSymbol(EmptySymbol.EMPTY);
}
else if (pos >= productiondefinition.getSymbolCount())
// otherwise is b FIRST(a)
b.addSymbol(J._lookaheads[i]);
// list of all productions B
productionlist = _grammar.getProductionList().getProductionList(symbol);
// for alle productions B
for (j = 0; j < productionlist.getSize(); j++)
{
// if J doesn't contain [B=^w,b] , should it added
for (k = 0; k < b.getSymbolCount(); k++)
{
if (!J.containsItem(productionlist.get(j), 0, b.getSymbol(k)))
J.addItem(productionlist.get(j), 0, b.getSymbol(k));
}
}
}
}
}
return J;
}
/**
* Calculates the next State by a transition through the symbol X.
*
* @param X A Symbol, which can be a terminal or a non terminal symbol
*
* @return The next state
*/
public ItemSet gotoX(Symbol X)
{
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(X))
// add [A=uX^v,a] to J
J.addItem(_productions[i], _positions[i] + 1, _lookaheads[i]);
}
// goto(I,X) = closure(J)
return J.closure();
}
/**
* Add a transition
*
* @param symbol Symbol, which transform the states into the
* other 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 transform the state
*
* @return List of symbols.
*/
public SymbolList getShiftSymbols()
{
return _transitionsymbols;
}
/**
* Returns the list of productions, which could
* reduced.
*
* @return List of indicies from the productions
*/
public IntegerList getReduceProductions()
{
IntegerList reduceproductions = new IntegerList(true);
for (int i = 0; i < _elementCount; i++)
{
if (getItemNext(i).isEmpty()) // 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 List of indicies from the productions
*/
public IntegerList getReduceProductions(Symbol lookahead)
{
IntegerList reduceproductions = new IntegerList(true);
for (int i = 0; i < _elementCount; i++)
{
// for all A=u^ and all symbols in FOLLOW(A)
if ((getItemNext(i).isEmpty()) && (_lookaheads[i].equals(lookahead)))
reduceproductions.add(_productions[i]);
}
return reduceproductions;
}
/**
* Return all symbol, which reduce productions in
* this state.
*
* @return List of symbols
*/
public SymbolList getReduceSymbols()
{
SymbolList reducesymbols = new SymbolList(true);
for (int i = 0; i < _elementCount; i++)
{
if (getItemNext(i).isEmpty()) // for all A=u^ and all symbols in FOLLOW(A)
reducesymbols.addSymbol(_lookaheads[i]);
}
return reducesymbols;
}
/**
* Ensure the capacity for adding values
*
* @param minCapacity
*/
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)];
Symbol[] newLookaheads = new Symbol[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;
}
/**
* Converts the itemset to a string
*
* @return Content of the itemset
*/
public String toString()
{
StringBuffer buffer = new StringBuffer();
SymbolList list;
for (int i = 0; i < _elementCount; i++)
{
buffer.append(_grammar.getProductionList().getProduction(_productions[i]).getSymbol());
buffer.append("->");
list = (SymbolList) _grammar.getProductionList().getProduction(_productions[i]).getDefinition();
for (int j = 0; j < list.getSymbolCount(); j++)
{
if (j == _positions[i])
buffer.append(".");
buffer.append(list.getSymbol(j) + " ");
}
if (_positions[i] == list.getSymbolCount())
buffer.append(".");
if (_lookaheads[i] != null)
buffer.append("," + _lookaheads[i]);
buffer.append(" [" + _productions[i] + "," + _positions[i] + "]");
buffer.append("\n");
}
return buffer.toString();
}
}