/* * 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(); } }