/* * xtc - The eXTensible Compiler * Copyright (C) 2004-2007 Robert Grimm * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. */ package xtc.parser; import java.util.ArrayList; import java.util.List; import xtc.tree.Visitor; import xtc.type.AST; import xtc.util.Runtime; import xtc.util.Utilities; /** * Visitor to add generic nodes as semantic values. * * <p />For any production with pseudotype "<code>generic</code>" that * does not contain any direct left-recursions (which is also called a * generic node production), this visitor adds the appropriate {@link * GenericNodeValue generic node value} elements, which create a * {@link xtc.tree.GNode generic node} as the productions' semantic * value. The children of such a generic node are the matched * component values of the production, though voided elements, void * nonterminals, and character terminals are not included. If an * alternative assigns {@link CodeGenerator#VALUE} either through a * binding or a semantic action, that alternative's semantic value is * the specified semantic value and not a newly generated generic * node. This visitor requires that all nested choices that do not * appear as the last element in a sequence have been lifted. It also * assumes that the entire grammar is contained in a single module. * * <p />Note that this visitor does not process generic productions * that contain direct left-recursions; they are processed by {@link * DirectLeftRecurser}. * * @author Robert Grimm * @version $Revision: 1.56 $ */ public class Generifier extends Visitor { /** The marker for synthetic variables. */ public static final String MARKER = "g"; /** The runtime. */ protected final Runtime runtime; /** The analyzer utility. */ protected final Analyzer analyzer; /** * The list of variables representing the children of the generic * node to be created. */ protected List<Binding> children; /** The list of node markers. */ protected List<NodeMarker> markers; /** * Create a new generifier. * * @param runtime The runtime. * @param analyzer The analyzer utility. */ public Generifier(Runtime runtime, Analyzer analyzer) { this.runtime = runtime; this.analyzer = analyzer; this.children = new ArrayList<Binding>(); this.markers = new ArrayList<NodeMarker>(); } /** * Create a binding for the specified element. This method also * adds the name of the bound variable to the end of the list of * children. * * @param e The element to bind. * @return The corresponding binding. */ protected Binding bind(Element e) { Binding b = new Binding(analyzer.variable(MARKER), e); children.add(b); return b; } /** Visit the specified grammar. */ public void visit(Module m) { // Initialize the analyzer. analyzer.register(this); analyzer.init(m); // Process the productions. for (Production p : m.productions) { if (isGenericNode((FullProduction)p)) { analyzer.process(p); } } } /** Visit the specified production. */ public void visit(FullProduction p) { // Process the production's element. p.choice = (OrderedChoice)dispatch(p.choice); // Patch the type (but only for dynamically typed productions). if (AST.isDynamicNode(p.type)) p.type = AST.NODE; // Mark the production as a generic node production. markGenericNode(p, runtime.test("optionVerbose")); } /** Visit the specified ordered choice. */ public Element visit(OrderedChoice c) { // Process the alternatives. final int size = c.alternatives.size(); for (int i=0; i<size; i++) { Sequence alternative = c.alternatives.get(i); // We only add generic node values to the current alternative if // that alternative does not contain any simple values, i.e., // either bindings to CodeGenerator.VALUE or value elements. if (! Analyzer.setsValue(alternative, true)) { c.alternatives.set(i, (Sequence)dispatch(alternative)); } } // Done. return c; } /** Visit the specified repetition. */ public Element visit(Repetition r) { return bind(r); } /** Visit the specified option. */ public Element visit(Option o) { return bind(o); } /** Visit the specified sequence. */ public Element visit(Sequence s) { // Remember the current number of children and markers. final int base = children.size(); final int base2 = markers.size(); // Process the elements of the sequence. final int size = s.size(); for (int i=0; i<size; i++) { s.elements.set(i, (Element)dispatch(s.get(i))); } // If this sequence has not ended with a choice, add the // appropriate semantic value. if (! s.hasTrailingChoice()) { String name = analyzer.current().qName.name; if (! markers.isEmpty()) { name = Utilities.qualify(Utilities.getQualifier(name), markers.get(markers.size()-1).name); } final List<Binding> formatting; if (s.hasProperty(Properties.FORMATTING)) { formatting = Properties.getFormatting(s); } else { formatting = new ArrayList<Binding>(0); } s.add(new GenericNodeValue(name, new ArrayList<Binding>(children), formatting)); } // Remove any children and markers added by processing the sequence. if (0 == base) { children.clear(); } else { children.subList(base, children.size()).clear(); } if (0 == base2) { markers.clear(); } else { markers.subList(base2, markers.size()).clear(); } // Done. return s; } /** Visit the specified binding. */ public Element visit(Binding b) { // Record the binding. children.add(b); // We assume that the bound expression does not require any // further processing. I.e., if it is a repetition, option, or // choice, it already has been lifted and replaced by a // nonterminal. // Done. return b; } /** Visit the specified string match. */ public Element visit(StringMatch m) { return bind(m); } /** Visit the specified nonterminal. */ public Element visit(NonTerminal nt) { FullProduction p = analyzer.lookup(nt); if (AST.isVoid(p.type)) { return nt; } else { return bind(nt); } } /** Visit the specified string literal. */ public Element visit(StringLiteral l) { return bind(l); } /** Visit the specified parse tree node. */ public Element visit(ParseTreeNode n) { return bind(n); } /** Visit the specified null literal. */ public Element visit(NullLiteral l) { return bind(l); } /** Visit the specified node marker. */ public Element visit(NodeMarker m) { markers.add(m); return m; } /** * Visit the specified element. This method provides the default * implementation for predicates, voided elements, character * terminals, (parser) actions, and value elements. */ public Element visit(Element e) { return e; } /** * Mark the specified production as a generic node production. * * @param p The production. * @param verbose The flag for whether to be verbose. */ public static void markGenericNode(FullProduction p, boolean verbose) { if (verbose) { System.err.println("[Recognizing " + p.qName + " as generic node]"); } p.setProperty(Properties.GENERIC, Properties.GENERIC_NODE); } /** * Mark the specified production as a generic recursion production. * * @param p The production. * @param verbose The flag for whether to be verbose. */ public static void markGenericRecursion(FullProduction p, boolean verbose) { if (verbose) { System.err.println("[Recognizing " + p.qName + " as generic recursion]"); } p.setProperty(Properties.GENERIC, Properties.GENERIC_RECURSION); } /** * Determine whether the specified production is a generic node or a * generic recursion production. * * @param p The production. * @return <code>true</code> if the specified production is a generic * node or generic recursion production. */ public static boolean isGeneric(FullProduction p) { if (p.hasProperty(Properties.GENERIC)) { Object value = p.getProperty(Properties.GENERIC); return (Properties.GENERIC_NODE.equals(value) || Properties.GENERIC_RECURSION.equals(value)); } else { return AST.isGenericNode(p.type); } } /** * Determine whether the specified production is a generic node * production. A production is a generic node production if its * semantic value is an automatically generated generic node with * the component values as its children. * * @param p The production. * @return <code>true</code> if the specified production is * a generic node production. */ public static boolean isGenericNode(FullProduction p) { if (p.hasProperty(Properties.GENERIC)) { return Properties.GENERIC_NODE.equals(p.getProperty(Properties.GENERIC)); } else { return (AST.isGenericNode(p.type) && (! DirectLeftRecurser.isTransformable(p))); } } /** * Determine whether the specified production is a generic recursion * production. A production is a generic recursion production if * its semantic value is an automatically generated generic node and * the production, as specified, contains one or more direct * left-recursions that can automatically be transformed into the * corresponding right-recursions. * * @see DirectLeftRecurser * * @param p The production. * @return <code>true</code> if the specified production is * a generic recursion production. */ public static boolean isGenericRecursion(FullProduction p) { if (p.hasProperty(Properties.GENERIC)) { return Properties.GENERIC_RECURSION. equals(p.getProperty(Properties.GENERIC)); } else { return (AST.isGenericNode(p.type) && DirectLeftRecurser.isTransformable(p)); } } }