/*
* 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.model.grammar;
import net.sourceforge.chaperon.model.symbol.Nonterminal;
import net.sourceforge.chaperon.model.symbol.Symbol;
import net.sourceforge.chaperon.model.symbol.Terminal;
import org.xml.sax.Attributes;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import java.util.Hashtable;
import java.util.Stack;
/**
* This class should generate a grammar from a SAX stream
*
* @author <a href="mailto:stephan@apache.org">Stephan Michels </a>
* @version CVS $Id: GrammarFactory.java,v 1.5 2003/12/14 09:48:34 benedikta Exp $
*/
public class GrammarFactory extends DefaultHandler
{
/** The namspace of the grammar configuration */
public static final String NS = "http://chaperon.sourceforge.net/schema/grammar/1.0";
/** Element name */
public static final String GRAMMAR_ELEMENT = "grammar";
/** Attribute name of the associativity property */
// public final static String TOKEN_ASSOCIATIVE_ATTRIBUTE = "assoc";
/** Element name */
public static final String PRODUCTION_ELEMENT = "production";
/** Attribute name of the Precedence property */
public static final String PRECEDENCE_ATTRIBUTE = "precedence";
/** Element name */
public static final String NONTERMINALSYMBOL_ELEMENT = "nonterminal";
/** Element name */
public static final String TERMINALSYMBOL_ELEMENT = "terminal";
/** Element name */
public static final String ERRORSYMBOL_ELEMENT = "error";
/** Element name */
public static final String STARTSYMBOL_ELEMENT = "start";
/** Element name */
public static final String PRIORITY_ELEMENT = "priority";
/** Element name */
public static final String ASSOCIATIVITY_ELEMENT = "associativity";
/** Attribute name of the associativity property */
public static final String TYPE_ATTRIBUTE = "type";
/** Attribute name */
public static final String SYMBOL_ATTRIBUTE = "symbol";
private static final int STATE_OUTER = 0;
private static final int STATE_GRAMMAR = 1;
private static final int STATE_PRODUCTION = 2;
private static final int STATE_START = 3;
private static final int STATE_NONTERMINAL = 4;
private static final int STATE_TERMINAL = 5;
private static final int STATE_ASSOCIATIVITY = 6;
private static final int STATE_PRIORITY = 7;
private static final int STATE_PRIORITYTERMINAL = 8;
private static final int STATE_ERROR = 9;
private int state = STATE_OUTER;
private Grammar grammar;
private Hashtable terminals = new Hashtable();
private Hashtable nonterminals = new Hashtable();
private Stack stack;
private Locator locator = null;
private int priorities = 0;
/**
* Returns the generated Grammar
*
* @return Grammar
*/
public Grammar getGrammar()
{
return grammar;
}
private String getLocation()
{
if (locator==null)
return "unknown";
return locator.getSystemId()+":"+locator.getLineNumber()+":"+locator.getColumnNumber();
}
/**
* Receive an object for locating the origin of SAX document events.
*/
public void setDocumentLocator(Locator locator)
{
this.locator = locator;
}
/**
* Receive notification of the beginning of a document.
*/
public void startDocument()
{
stack = new Stack();
state = STATE_OUTER;
}
/**
* Return the content of the associatve attribute
*
* @param namespaceURI
* @param localName
* @param qName
* @param atts Attributes of an element
*
* @throws SAXException
*/
/*private Associativity getAssociativityFromAttributes(Attributes atts)
{
String attribute = atts.getValue(TOKEN_ASSOCIATIVE_ATTRIBUTE);
if ((attribute != null) && (attribute.length() > 0))
return Associativity.valueOf(attribute);
return Associativity.NONASSOC;
}*/
/**
* Return the content of the reducetype attribute
*
* @param atts Attributes of an element
*
* @return Reducetype attribute
*/
/*private ReduceType getReduceTypeFromAttributes(Attributes atts)
{
String attribute = atts.getValue(PRODUCTION_REDUCETYPE_ATTRIBUTE);
if ((attribute != null) && (attribute.length() > 0))
return ReduceType.valueOf(attribute);
return ReduceType.NORMAL;
}*/
/**
* Receive notification of the beginning of an element.
*
* @param namespaceURI The Namespace URI, or the empty string if the element has no Namespace URI
* or if Namespace processing is not being performed.
* @param localName The local name (without prefix), or the empty string if Namespace processing
* is not being performed.
* @param qName The raw XML 1.0 name (with prefix), or the empty string if raw names are not
* available.
* @param atts The attributes attached to the element. If there are no attributes, it shall be an
* empty Attributes object.
*/
public void startElement(String namespaceURI, String localName, String qName, Attributes atts)
throws SAXException
{
if (namespaceURI.equals(NS))
{
if ((localName.equals(GRAMMAR_ELEMENT)) && (state==STATE_OUTER))
{
grammar = new Grammar();
grammar.setLocation(getLocation());
stack.push(grammar);
state = STATE_GRAMMAR;
}
else if ((localName.equals(PRODUCTION_ELEMENT)) && (state==STATE_GRAMMAR))
{
Production production =
new Production(getNonterminal(nonterminals, atts.getValue(SYMBOL_ATTRIBUTE)));
production.setLocation(getLocation());
String precedencesymbol = atts.getValue(PRECEDENCE_ATTRIBUTE);
if ((precedencesymbol!=null) && (precedencesymbol.length()>0))
production.setPrecedence(new Terminal(precedencesymbol));
stack.push(production);
state = STATE_PRODUCTION;
}
else if ((localName.equals(NONTERMINALSYMBOL_ELEMENT)) && (state==STATE_PRODUCTION))
{
stack.push(getNonterminal(nonterminals, atts.getValue(SYMBOL_ATTRIBUTE)));
state = STATE_NONTERMINAL;
}
else if ((localName.equals(TERMINALSYMBOL_ELEMENT)) && (state==STATE_PRODUCTION))
{
stack.push(new Terminal(atts.getValue(SYMBOL_ATTRIBUTE)));
state = STATE_TERMINAL;
}
else if ((localName.equals(ERRORSYMBOL_ELEMENT)) && (state==STATE_PRODUCTION))
{
stack.push(new Error());
state = STATE_ERROR;
}
else if ((localName.equals(STARTSYMBOL_ELEMENT)) && (state==STATE_GRAMMAR))
{
stack.push(new Nonterminal(atts.getValue(SYMBOL_ATTRIBUTE)));
state = STATE_START;
}
else if ((localName.equals(ASSOCIATIVITY_ELEMENT)) && (state==STATE_GRAMMAR))
{
grammar.setAssociativity(getTerminal(terminals, atts.getValue(SYMBOL_ATTRIBUTE)),
new Associativity(atts.getValue(TYPE_ATTRIBUTE)));
state = STATE_ASSOCIATIVITY;
}
else if ((localName.equals(PRIORITY_ELEMENT)) && (state==STATE_GRAMMAR))
{
priorities = 0;
state = STATE_PRIORITY;
}
else if ((localName.equals(TERMINALSYMBOL_ELEMENT)) && (state==STATE_PRIORITY))
{
stack.push(getTerminal(terminals, atts.getValue(SYMBOL_ATTRIBUTE)));
priorities++;
state = STATE_PRIORITYTERMINAL;
}
else
throw new SAXException("Unexpected element "+qName+" at "+getLocation());
}
else
throw new SAXException("Unexpected element "+qName+" at "+getLocation());
}
/**
* Receive notification of the end of an element.
*
* @param namespaceURI The Namespace URI, or the empty string if the element has no Namespace URI
* or if Namespace processing is not being performed.
* @param localName The local name (without prefix), or the empty string if Namespace processing
* is not being performed.
* @param qName The raw XML 1.0 name (with prefix), or the empty string if raw names are not
* available.
*
* @throws SAXException
*/
public void endElement(String namespaceURI, String localName, String qName)
throws SAXException
{
if (namespaceURI.equals(NS))
{
if ((localName.equals(GRAMMAR_ELEMENT)) && (state==STATE_GRAMMAR))
{
grammar = (Grammar)stack.pop();
state = STATE_OUTER;
}
else if ((localName.equals(PRODUCTION_ELEMENT)) && (state==STATE_PRODUCTION))
{
Production production = (Production)stack.pop();
Grammar grammar = (Grammar)stack.peek();
grammar.addProduction(production);
state = STATE_GRAMMAR;
}
else if ((localName.equals(NONTERMINALSYMBOL_ELEMENT)) && (state==STATE_NONTERMINAL))
{
Symbol ntsymbol = (Symbol)stack.pop();
Production production = (Production)stack.peek();
production.getDefinition().addSymbol(ntsymbol);
state = STATE_PRODUCTION;
}
else if ((localName.equals(TERMINALSYMBOL_ELEMENT)) && (state==STATE_TERMINAL))
{
Symbol tsymbol = (Symbol)stack.pop();
Production production = (Production)stack.peek();
production.getDefinition().addSymbol(tsymbol);
state = STATE_PRODUCTION;
}
else if ((localName.equals(ERRORSYMBOL_ELEMENT)) && (state==STATE_ERROR))
{
Symbol error = (Symbol)stack.pop();
Production production = (Production)stack.peek();
production.getDefinition().addSymbol(error);
state = STATE_PRODUCTION;
}
else if ((localName.equals(STARTSYMBOL_ELEMENT)) && (state==STATE_START))
{
Nonterminal ssymbol = (Nonterminal)stack.pop();
Grammar grammar = (Grammar)stack.peek();
grammar.setStartSymbol(ssymbol);
state = STATE_GRAMMAR;
}
else if ((localName.equals(ASSOCIATIVITY_ELEMENT)) && (state==STATE_ASSOCIATIVITY))
state = STATE_GRAMMAR;
else if ((localName.equals(PRIORITY_ELEMENT)) && (state==STATE_PRIORITY))
{
int i = 0;
while (stack.peek() instanceof Terminal)
{
grammar.setPriority((Terminal)stack.pop(), i+1);
i++;
}
state = STATE_GRAMMAR;
}
else if ((localName.equals(TERMINALSYMBOL_ELEMENT)) && (state==STATE_PRIORITYTERMINAL))
state = STATE_PRIORITY;
else
throw new SAXException("Unexpected element "+qName+" at "+getLocation());
}
else
throw new SAXException("Unexpected element "+qName+" at "+getLocation());
}
private Terminal getTerminal(Hashtable terminals, String name)
{
Terminal terminal = (Terminal)terminals.get(name);
if (terminal==null)
{
terminal = new Terminal(name);
terminals.put(name, terminal);
}
return terminal;
}
private Nonterminal getNonterminal(Hashtable nonterminals, String name)
{
Nonterminal nonterminal = (Nonterminal)nonterminals.get(name);
if (nonterminal==null)
{
nonterminal = new Nonterminal(name);
nonterminals.put(name, nonterminal);
}
return nonterminal;
}
}