/*
* 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 xtc.tree.Visitor;
/**
* Visitor to provide a conservative estimate for the cost of parsing
* a production. One unit of cost is approximately equivalent to the
* effort involved in parsing a character and testing it for a
* specific value. The cost estimate for a production includes the
* costs for any other productions referenced by that production. If
* the cost cannot be statically determined (for example, for a
* repetition) or a set of productions is mutually recursive, the cost
* is assumed to be unlimited, as represented by
* <code>Integer.MAX_VALUE</code>. This visitor must be invoked by
* visiting a grammar, which clears any previous cost estimates and
* annotates each production with its estimated cost.
*
* <p />This visitor assumes that the entire grammar is contained in a
* single module.
*
* @author Robert Grimm
* @version $Revision: 1.26 $
*/
public class CostEstimator extends Visitor {
/** The analyzer utility. */
protected final Analyzer analyzer;
/**
* Create a new cost estimator.
*
* @param analyzer The analyzer utility.
*/
public CostEstimator(Analyzer analyzer) {
this.analyzer = analyzer;
}
/** Visit the specified grammar. */
public void visit(Module m) {
// Initialize the per-grammar state.
analyzer.register(this);
analyzer.init(m);
// Clear any previous cost estimates.
for (Production p : m.productions) p.removeProperty(Properties.COST);
// Now, set the cost estimates.
for (Production p : m.productions) {
if (! p.hasProperty(Properties.COST)) {
analyzer.process(p);
}
}
}
/** Visit the specified production. */
public Integer visit(Production p) {
analyzer.workingOn(p.qName);
Integer cost = (Integer)dispatch(p.choice);
analyzer.notWorkingOn(p.qName);
p.setProperty(Properties.COST, cost);
return cost;
}
/** Visit the specified ordered choice. */
public Integer visit(OrderedChoice c) {
// In the worst case, none of the alternatives parses. Hence, the
// overall cost is the sum of the costs of all alternatives.
int cost = 0;
for (Sequence s : c.alternatives) {
cost = add(cost, (Integer)dispatch(s));
}
return cost;
}
/** Visit the specified repetition. */
public Integer visit(Repetition r) {
return Integer.MAX_VALUE;
}
/** Visit the specified option. */
public Integer visit(Option o) {
// In the worst case, the optional element does not parse. Hence,
// we add one unit for the empty case.
return add(1, (Integer)dispatch(o.element));
}
/** Visit the specified sequence. */
public Integer visit(Sequence s) {
int cost = 0;
for (Element e : s.elements) {
cost = add(cost, (Integer)dispatch(e));
}
return cost;
}
/** Visit the specified predicate. */
public Integer visit(Predicate p) {
// We add one unit for the test implied by a predicate.
return add(1, (Integer)dispatch(p.element));
}
/** Visit the specified voided element. */
public Integer visit(VoidedElement v) {
// Voiding elements comes for free as no code is generated.
return (Integer)dispatch(v.element);
}
/** Visit the specified binding. */
public Integer visit(Binding b) {
// Bindings are pretty much for free.
return (Integer)dispatch(b.element);
}
/** Visit the specified string match. */
public Integer visit(StringMatch m) {
// We add one unit for the test performed by a string match.
return add(1, (Integer)dispatch(m.element));
}
/** Visit the specified nonterminal. */
public Integer visit(NonTerminal nt) {
Production p = analyzer.lookup(nt);
if (analyzer.isBeingWorkedOn(p.qName)) {
// A recursion has unlimited cost.
return Integer.MAX_VALUE;
} else {
// We add one unit for testing the nonterminal.
return add(1, p.hasProperty(Properties.COST)?
(Integer)p.getProperty(Properties.COST) : (Integer)dispatch(p));
}
}
/** Visit the specified string literal. */
public Integer visit(StringLiteral l) {
// Each character in the string literal requires a test.
return l.text.length();
}
/** Visit the specified character case. */
public Integer visit(CharCase c) {
if (null == c.element) {
return 0;
} else {
return (Integer)dispatch(c.element);
}
}
/** Visit the specified character switch. */
public Integer visit(CharSwitch sw) {
// A character switch has disjoint cases, so the cost is the
// maximum cost of the cases and default (+ 1 for the character
// switch itself).
int cost = 0;
for (CharCase kase : sw.cases) {
cost = Math.max(cost, (Integer)dispatch(kase) + 1);
}
if (null == sw.base) {
cost = Math.max(cost, (Integer)dispatch(sw.base) + 1);
}
return cost;
}
/**
* Visit the specified terminal. This method provides the default
* implementation for any character elements, character classes, and
* character literals.
*/
public Integer visit(Terminal t) {
return 1;
}
/** Visit the specified node marker. */
public Integer visit(NodeMarker m) {
return 0;
}
/** Visit the specified action. */
public Integer visit(Action a) {
// An action typically creates a semantic value, so the cost is 1.
return 1;
}
/** Visit the specified parser action. */
public Integer visit(ParserAction a) {
// Parser actions may consume arbritrary inputs, so they have
// unlimited cost.
return Integer.MAX_VALUE;
}
/** Visit the specified null literal. */
public Integer visit(NullLiteral l) {
// Null literals are free.
return 0;
}
/** Visit the specified string value. */
public Integer visit(StringValue v) {
// A string value without a static text creates a string, so the
// cost is 1.
return null == v.text ? 1 : 0;
}
/** Visit the specified proper list value. */
public Integer visit(ProperListValue v) {
// A proper list value creates a pair for each element.
return v.elements.size();
}
/** Visit the specified action base value. */
public Integer visit(ActionBaseValue v) {
// An action base value applies all actions on the list, so the
// cost is proportional to the length of the list.
return Integer.MAX_VALUE;
}
/** Visit the specified generic node value. */
public Integer visit(GenericNodeValue v) {
// A generic value creates a generic node, so the cost is 2 (1 for
// the node, 1 for the children).
return 2;
}
/** Visit the specified generic action value. */
public Integer visit(GenericActionValue v) {
// A generic action value creates a new action, so the cost is 1.
return 1;
}
/** Visit the specified generic recursion value. */
public Integer visit(GenericRecursionValue v) {
// A generic recursion value creates a new action and a new pair,
// so the cost is 2.
return 2;
}
/**
* Visit the specified value element. This method provides the
* default implementation for null values and empty list values.
*/
public Integer visit(ValueElement v) {
return 0;
}
/**
* Add the two specified estimates.
*
* @param e1 The first estimate.
* @param e2 The second estimate.
* @return The sum.
*/
protected static int add(final int e1, final int e2) {
if ((Integer.MAX_VALUE == e1) || (Integer.MAX_VALUE == e2)) {
return Integer.MAX_VALUE;
} else {
long sum = e1 + e2;
if (Integer.MAX_VALUE < sum) {
return Integer.MAX_VALUE;
} else {
return (int)sum;
}
}
}
}