/* * 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 xtc.util.Runtime; import xtc.type.AST; /** * Visitor to simplify a grammar. This visitor folds nested choices * and sequences into the embedding choice or sequence, eliminates * choices and sequences with only one alternative or element (with * the exception of the top-level choice of a production), and reduces * repetitions and options to their simplest form. It also reduces * trivial character classes and string literals to character * literals. Note that the resulting grammar may violate the * requirements of {@link CodeGenerator code generation}. * * @author Robert Grimm * @version $Revision: 1.54 $ */ public class Simplifier extends GrammarVisitor { /** * Create a new simplifier. * * @param runtime The runtime. * @param analyzer The analyzer utility. */ public Simplifier(Runtime runtime, Analyzer analyzer) { super(runtime, analyzer); } /** Visit the specified ordered choice. */ public Element visit(OrderedChoice c) { boolean top = isTopLevel; isTopLevel = false; // Process the alternatives. for (int i=0; i<c.alternatives.size(); i++) { needsSequence = true; Sequence s = (Sequence)dispatch(c.alternatives.get(i)); if ((1 == s.size()) && (s.get(0) instanceof OrderedChoice)) { // Fold in the nested choice. OrderedChoice c2 = (OrderedChoice)s.get(0); c.alternatives.remove(i); c.alternatives.addAll(i, c2.alternatives); i += (c2.alternatives.size()-1); } else { // Replace with the processed alternative. c.alternatives.set(i, s); } } needsSequence = false; // Eliminate nested choices with a single alternative. if ((! top) && (1 == c.alternatives.size())) { return c.alternatives.get(0); } else { return c; } } /** Visit the specified repetition. */ public Element visit(Repetition r) { isTopLevel = false; needsSequence = true; Element e = (Element)dispatch(r.element); Element naked = Analyzer.strip(e); if (naked instanceof Repetition) { Repetition r2 = (Repetition)naked; r.once = r.once && r2.once; r.element = r2.element; return r; } else if (naked instanceof Option) { r.once = false; r.element = ((Option)naked).element; return r; } else { r.element = e; return r; } } /** Visit the specified option. */ public Element visit(Option o) { isTopLevel = false; needsSequence = true; Element e = (Element)dispatch(o.element); Element naked = Analyzer.strip(e); if (naked instanceof Option) { o.element = ((Option)naked).element; return o; } else if (naked instanceof Repetition) { ((Repetition)naked).once = false; // Transfer the location info. naked.setLocation(o); return naked; } else { o.element = e; return o; } } /** Visit the specified sequence. */ public Element visit(Sequence s) { isTopLevel = false; boolean preserve = needsSequence; needsSequence = false; // Process the elements of the sequence first. for (int i=0; i<s.size(); i++) { Element e = (Element)dispatch(s.get(i)); if (e instanceof Sequence) { // Fold in a nested sequence. Sequence s2 = (Sequence)e; s.elements.remove(i); s.elements.addAll(i, s2.elements); i += (s2.size()-1); } else { // Replace with the processed element. s.elements.set(i, e); } } // Now, see if we can create any exclusive character classes. int size = s.size(); for (int i=0; i<size-1; i++) { Element e1 = s.get(i); Element e2 = s.get(i+1); if ((e1 instanceof NotFollowedBy) && (e2 instanceof AnyChar)) { e1 = ((NotFollowedBy)e1).element; if (e1 instanceof CharClass) { CharClass c = (CharClass)e1; if (! c.exclusive) { c.exclusive = true; s.elements.set(i, c); s.elements.remove(i+1); size--; } } else if (e1 instanceof CharLiteral) { CharClass c = new CharClass(true, new ArrayList<CharRange>(1)); c.ranges.add(new CharRange(((CharLiteral)e1).c)); s.elements.set(i, c); s.elements.remove(i+1); size--; } } } // Eliminate sequences with a single element. However, if the // parent element requires a sequence, this sequence is not // eliminated. if ((1 == s.size()) && (! preserve)) { return s.get(0); } else { return s; } } /** Visit the specified voided element. */ public Element visit(VoidedElement v) { isTopLevel = false; needsSequence = false; v.element = Analyzer.strip((Element)dispatch(v.element)); // If the voided element is a void nonterminal, eliminate the // voided element wrapper. Next, if the voided element is another // voided element, also eliminate the voided element wrapper. // Otherwise, if the element is a sequence, restore it to a choice // so that it can be lifted. if ((v.element instanceof NonTerminal) && AST.isVoid(analyzer.lookup((NonTerminal)v.element).type)) { return v.element; } else if (v.element instanceof VoidedElement) { return v.element; } else if (v.element instanceof Sequence) { OrderedChoice c = new OrderedChoice(new ArrayList<Sequence>(1)); c.alternatives.add((Sequence)v.element); c.setLocation(v.element); v.element = c; } return v; } /** Visit the specified binding. */ public Element visit(Binding b) { isTopLevel = false; needsSequence = false; b.element = Analyzer.strip((Element)dispatch(b.element)); if (b.element instanceof Sequence) { // If the element is a sequence, restore it to an ordered choice // so that it will be lifted. OrderedChoice c = new OrderedChoice(new ArrayList<Sequence>(1)); c.alternatives.add((Sequence)b.element); c.setLocation(b.element); b.element = c; } return b; } /** Visit the specified string match. */ public Element visit(StringMatch m) { isTopLevel = false; needsSequence = false; m.element = Analyzer.strip((Element)dispatch(m.element)); if (m.element instanceof Sequence) { // If the element is a sequence, restore it to an ordered choice // so that it will be lifted. OrderedChoice c = new OrderedChoice(new ArrayList<Sequence>(1)); c.alternatives.add((Sequence)m.element); c.setLocation(m.element); m.element = c; } else if (((m.element instanceof Repetition) && (! analyzer.current().isMemoized()) && runtime.test("optimizeRepeated")) || ((m.element instanceof Option) && runtime.test("optimizeOptional"))) { // If the element is a repetition or option that won't be // desugared, restore it to an ordered choice so that it will be // lifted. OrderedChoice c = new OrderedChoice(new ArrayList<Sequence>(1)); c.alternatives.add(new Sequence(m.element)); c.setLocation(m.element); m.element = c; } return m; } /** Visit the specified character class. */ public Element visit(CharClass c) { isTopLevel = false; needsSequence = false; // Turn non-exclusive character classes with a single range and // equal first and last characters into a character literal. if ((! c.exclusive) && (1 == c.ranges.size())) { CharRange r = c.ranges.get(0); if (r.first == r.last) { CharLiteral cl = new CharLiteral(r.first); // Transfer the location info. cl.setLocation(c); return cl; } } return c.normalize(); } }