/*
* 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.model.grammar.Grammar;
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;
import org.apache.commons.logging.Log;
/**
* This class creates a collection of FIRST sets.
*
* @author <a href="mailto:stephan@apache.org">Stephan Michels</a>
* @version CVS $Id: FirstSetCollection.java,v 1.10 2003/12/10 16:34:38 benedikta Exp $
*/
public class FirstSetCollection
{
private Grammar grammar;
private Symbol[] symbols;
private SymbolSet[] firstsets;
private Log log;
private static final EmptyList EMPTYLIST = new EmptyList();
/**
* Create a collection of FIRST sets.
*
* @param grammar Grammar.
*/
public FirstSetCollection(Grammar grammar)
{
this(grammar, null);
}
/**
* Create a collection of FIRST sets
*
* @param grammar Grammar
* @param log Log, whch should be used.
*/
public FirstSetCollection(Grammar grammar, Log log)
{
this.grammar = grammar;
this.log = log;
SymbolSet usedsymbols = grammar.getSymbols();
symbols = new Symbol[usedsymbols.getSymbolCount()];
firstsets = new SymbolSet[usedsymbols.getSymbolCount()];
for (int i = 0; i<usedsymbols.getSymbolCount(); i++)
{
if (log!=null)
log.debug("Generating first set for "+usedsymbols.getSymbol(i).getName());
symbols[i] = usedsymbols.getSymbol(i);
firstsets[i] = first(symbols[i]);
}
}
/**
* Returns the FIRST set for a symbol.
*
* @param symbol Symbol.
*
* @return Set of symbols.
*/
public SymbolSet getFirstSet(Symbol symbol)
{
for (int i = 0; i<symbols.length; i++)
if (symbols[i].equals(symbol))
return firstsets[i];
throw new IllegalArgumentException("No first set found for symbol");
}
/**
* Returns the FIRST set for a sequence of symbols.
*
* @param symbol Sequence of symbols.
*
* @return Set of symbols.
*/
public SymbolSet getFirstSet(SymbolList symbols)
{
SymbolSet firstset = new SymbolSet();
if (symbols.getSymbolCount()==0)
{
firstset.addSymbol(EMPTYLIST);
return firstset;
}
int position = 0;
do
{
firstset.removeSymbol(EMPTYLIST);
if (symbols.getSymbol(position) instanceof Terminal)
firstset.addSymbol(symbols.getSymbol(position));
else
firstset.addSymbol(getFirstSet(symbols.getSymbol(position)));
position++;
}
while ((firstset.contains(EMPTYLIST)) && (position<symbols.getSymbolCount()));
return firstset;
}
/**
* Calculates the FIRST set. The FIRST set is the set of terminal symbols, which come as next
* symbol
*
* @param symbol Symbol.
*
* @return Set of symbol.
*/
private SymbolSet first(Symbol symbol)
{
return first(symbol, new SymbolSet());
}
/**
* Calculates the FIRST set. The FIRST set is the set of terminal symbols, which come as next
* symbol.
*
* @param symbol Symbol.
* @param visited Set of symbol, which were already calculated.
*
* @return Set of symbols.
*/
private SymbolSet first(Symbol symbol, SymbolSet visited)
{
SymbolSet firstset = new SymbolSet();
// if the symbol is a terminal symbol
if (symbol instanceof Terminal)
{
firstset.addSymbol(symbol);
return firstset;
}
if (visited.contains(symbol))
return firstset;
else
visited.addSymbol(symbol);
// if is a non terminal symbol
IntegerList productions = grammar.getProductionList(symbol);
SymbolSet examined = new SymbolSet(); // List of all examined symbols
for (int i = 0; i<productions.getCount(); i++)
{
SymbolList productiondefinition = grammar.getProduction(productions.get(i)).getDefinition();
if (productiondefinition.getSymbolCount()==0)
// Symbol for a empty firstset added
firstset.addSymbol(EMPTYLIST);
else
{
// for every symbol in the production
int j = 0;
Symbol newsymbol;
boolean foundEmptyList;
do
{
foundEmptyList = true;
newsymbol = productiondefinition.getSymbol(j);
if (newsymbol instanceof Terminal)
// if a terminal symbol
firstset.addSymbol(newsymbol);
else if (!newsymbol.equals(symbol))
{
// and if a non terminal symbol
if (!examined.contains(newsymbol))
{
SymbolSet newfirstset = first(newsymbol, visited);
foundEmptyList = newfirstset.contains(EMPTYLIST);
for (int k = 0; k<newfirstset.getSymbolCount(); k++)
if (!newfirstset.getSymbol(k).equals(EMPTYLIST))
firstset.addSymbol(newfirstset.getSymbol(k));
examined.addSymbol(newsymbol);
}
}
j++;
}
while ((!(newsymbol instanceof Terminal)) && (foundEmptyList) &&
(j<productiondefinition.getSymbolCount()) &&
(!productiondefinition.getSymbol(j-1).equals(symbol)));
}
}
return firstset;
}
/**
* Return a string representation of the FIRST sets.
*
* @return String representation of the FIRST sets.
*/
public String toString()
{
StringBuffer buffer = new StringBuffer();
SymbolSet symbols = grammar.getSymbols();
for (int symbol = 0; symbol<symbols.getSymbolCount(); symbol++)
{
buffer.append("first(");
buffer.append(symbols.getSymbol(symbol).toString());
buffer.append(")=");
buffer.append(getFirstSet(symbols.getSymbol(symbol)).toString());
buffer.append("\n");
}
return buffer.toString();
}
}