/* * xtc - The eXTensible Compiler * Copyright (C) 2004-2008 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 xtc.util.Runtime; /** * Visitor to optimize the recognition of terminals by introducing * character switches. Note that the different alternatives in an * ordered choice must have been normalized into sequences. * * @author Robert Grimm * @version $Revision: 1.30 $ */ public class TerminalOptimizer extends GrammarVisitor { /** * Create a new terminal optimizer. * * @param runtime The runtime. * @param analyzer The analyzer utility. */ public TerminalOptimizer(Runtime runtime, Analyzer analyzer) { super(runtime, analyzer); } /** Visit the specified ordered choice. */ public Element visit(OrderedChoice c) { // First, see if we can generate any character switches that span // several alternatives. for (int i=0; i<c.alternatives.size(); i++) { if (analyzer.hasTerminalPrefix(c.alternatives.get(i))) { // Are there more? int j; for (j=i+1; j<c.alternatives.size(); j++) { if (! analyzer.hasTerminalPrefix(c.alternatives.get(j))) { break; } } if (i != j-1) { // There are alternatives to optimize. Normalize them first. for (int k=i; k<j; k++) { c.alternatives.set(k, analyzer.normalizeTerminals(c.alternatives.get(k))); } Element result = null; for (int k=i; k<j; k++) { result = analyzer.joinTerminals(c.alternatives.get(k), result); } // Replace old alternatives with result. c.alternatives.subList(i, j).clear(); if (result instanceof Sequence) { c.alternatives.add(i, (Sequence)result); // Continue processing with i+1. } else { OrderedChoice choice = (OrderedChoice)result; c.alternatives.addAll(i, choice.alternatives); i += (choice.alternatives.size() - 1); } if (runtime.test("optionVerbose")) { System.err.println("[Folding terminals in " + analyzer.current().qName + "]"); } } } } // Next, process the alternatives individually. Note that the number // of alternatives may have changed. final int size = c.alternatives.size(); for (int i=0; i<size; i++) { c.alternatives.set(i, (Sequence)dispatch(c.alternatives.get(i))); } return c; } /** Visit the specified sequence. */ public Element visit(Sequence s) { // Process the elements first. final int size = s.size(); for (int i=0; i<size; i++) { s.elements.set(i, (Element)dispatch(s.get(i))); } // Now, see if we can turn character classes into character // switches. for (int i=s.size()-1; i>=0; i--) { Element e = s.get(i); if (e instanceof CharClass) { CharClass klass = (CharClass)e; final int kount = klass.count(); if ((1 < kount) && (kount <= Analyzer.MAX_COUNT)) { CharSwitch sw = new CharSwitch(klass, s.subSequence(i+1)); sw.setLocation(s); s.elements.set(i, sw); s = s.subSequence(0, i+1); if (runtime.test("optionVerbose")) { System.err.println("[Creating char switch in " + analyzer.current().qName + "]"); } } } } // Done. return s; } /** Visit the specified predicate. */ public Element visit(Predicate p) { // Ignore the element. In general, switch statements may lead to // embedded ordered choices, which are not (yet) supported by the // code generator. return p; } }