/*
* Reference ETL Parser for Java
* Copyright (c) 2000-2009 Constantine A Plotnikov
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package net.sf.etl.parsers.internal.term_parser.compiler;
import java.util.HashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import net.sf.etl.parsers.internal.term_parser.DefaultTermParser;
import net.sf.etl.parsers.internal.term_parser.GrammarLocator;
import net.sf.etl.parsers.internal.term_parser.flattened.GrammarAssembly;
import net.sf.etl.parsers.internal.term_parser.flattened.GrammarView;
import net.sf.etl.parsers.internal.term_parser.states.StateMachinePeerFactory;
import org.xml.sax.InputSource;
/**
* <p>
* This class is a builder for an assembly of grammars. It takes
* {@link GrammarAssembly} and creates set of {@link StateMachinePeerFactory}
* that correspond to grammars in the assembly.
* </p>
*
* <p>
* Note that compilation process assumes that grammar has been parsed correctly.
* It also assumes that flattening process has completed without errors. If
* grammar of the grammar ever changes, the process needs to be reevaluated in
* order to ensure if all assumptions are still true. So far not all these
* assumptions are documented.
* </p>
*
* @author const
*/
// NOTE POST 0.2: add ability to reuse already compiled grammars during
// compilation
public class GrammarAssemblyBuilder {
/**
* A logger for this class
*/
private final static Logger log = Logger.getLogger(GrammarAssembly.class
.getName());
/** an assembly to be compiled */
private final GrammarAssembly assembly;
/** map from grammar view to build */
private final HashMap<GrammarView, GrammarBuilder> viewToBuilder = new HashMap<GrammarView, GrammarBuilder>();
/** a locator for grammar */
private final GrammarLocator locator;
/**
* A builder used to compile grammar assembly.
*
* @param locator
* a locator for this builder
*
* @param assembly
* an assembly to compile
*/
public GrammarAssemblyBuilder(GrammarLocator locator,
GrammarAssembly assembly) {
super();
this.assembly = assembly;
this.locator = locator;
}
/**
* Build this grammar and all related grammars
*
* @param locator
* a locator for the grammar
* @param termParser
* a term parser that requested grammar building
* @param grammarSource
* an input source for grammar
* @return an instance of grammar builder
*/
public static GrammarAssemblyBuilder build(GrammarLocator locator,
DefaultTermParser termParser, InputSource grammarSource) {
final GrammarAssembly view = new GrammarAssembly(locator, termParser);
view.processGrammars(grammarSource);
if (view.hadErrors()) {
if (log.isLoggable(Level.FINE)) {
log
.fine("There has been errors during building view of grammar "
+ "with systemId="
+ grammarSource.getSystemId()
+ " publicId="
+ grammarSource.getPublicId());
}
return null;
}
final GrammarAssemblyBuilder rc = new GrammarAssemblyBuilder(locator,
view);
rc.build();
if (rc.hadErrors()) {
if (log.isLoggable(Level.FINE)) {
log
.fine("There has been errors during building code for grammar "
+ "with systemId="
+ grammarSource.getSystemId()
+ " publicId="
+ grammarSource.getPublicId());
}
return null;
}
return rc;
}
/**
* Build grammars in this grammar assembly
*/
private void build() {
// create grammar builders
for (final GrammarView g : assembly.grammars()) {
if (!g.isAbstract()) {
final GrammarBuilder grammarBuilder = new GrammarBuilder(this,
g);
viewToBuilder.put(g, grammarBuilder);
grammarBuilder.prepare();
}
}
if (hadErrors()) {
return;
}
// build nodes for production
for (final GrammarBuilder grammarBuilder : viewToBuilder.values()) {
if (!grammarBuilder.grammarView().isAbstract()) {
grammarBuilder.buildNodes();
}
}
if (hadErrors()) {
return;
}
// build lookaheads
for (final GrammarBuilder grammarBuilder : viewToBuilder.values()) {
if (!grammarBuilder.grammarView().isAbstract()) {
grammarBuilder.buildLookAhead();
}
}
if (hadErrors()) {
return;
}
// build state machines
for (final GrammarBuilder grammarBuilder : viewToBuilder.values()) {
if (!grammarBuilder.grammarView().isAbstract()) {
grammarBuilder.buildStateMachines();
}
}
if (hadErrors()) {
return;
}
// NOTE: POST 0.2 peephole optimization
// NOTE: POST 0.2 remove unreachable
for (final GrammarBuilder grammarBuilder : viewToBuilder.values()) {
if (!grammarBuilder.grammarView().isAbstract()) {
locator.registerPeer(
grammarBuilder.grammarView().getSystemId(),
grammarBuilder.grammarView().getPublicId(),
grammarBuilder.peerFactory());
}
}
}
/**
* Get grammar builder from grammar grammar context
*
* @param view
* a grammar view
* @return a grammar builder associated with grammar view
*/
public GrammarBuilder grammarBuilder(GrammarView view) {
return viewToBuilder.get(view);
}
/**
* @return true if there were errors
*/
public boolean hadErrors() {
return assembly.hadErrors();
}
/**
* @return peer factory for root grammar
*/
public StateMachinePeerFactory getRootPeerFactory() {
if (hadErrors()) {
throw new IllegalStateException(
"There has been errors during creation of grammar.");
}
final GrammarBuilder rc = viewToBuilder.get(assembly.rootGrammar());
return rc.peerFactory();
}
/**
* @return systemId of root grammar
*/
public String rootSystemId() {
final GrammarBuilder rc = viewToBuilder.get(assembly.rootGrammar());
return rc.grammarView().getSystemId();
}
}