/*
* 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.HashSet;
import java.util.Set;
import xtc.Constants;
import xtc.type.AST;
import xtc.util.Runtime;
/**
* Visitor to turn the semantic value of a production to void. This
* visitor converts non-void productions whose semantic values are
* never bound to void productions. As a practical matter, it only
* converts productions that do not contain (parser) actions.
*
* <p />This visitor assumes that the entire grammar is contained in a
* single module.
*
* @author Robert Grimm
* @version $Revision: 1.39 $
*/
public class ProductionVoider extends GrammarVisitor {
/** Visitor to determine which productions are voidable. */
public static class Tester extends GrammarVisitor {
/** The flag for whether we are currently unmarking productions. */
protected boolean secondPhase;
/** The flag for whether a production may be voided. */
protected boolean voidable;
/**
* Create a new tester.
*
* @param runtime The runtime.
* @param analyzer The analyzer utility.
*/
public Tester(Runtime runtime, Analyzer analyzer) {
super(runtime, analyzer);
}
/**
* Get the set of nonterminals corresponding to voidable
* productions. Note that this method must only be called after
* visiting the corresponding grammar with this visitor.
*
* @return The set of voidable nonterminals.
*/
public Set<NonTerminal> voidable() {
return new HashSet<NonTerminal>(analyzer.marked());
}
/** Visit the specified grammar. */
public Object visit(Module m) {
// Initialize the per-grammar state.
analyzer.register(this);
analyzer.init(m);
// On the first iteration, mark all productions that might be
// voidable.
secondPhase = false;
for (Production p : m.productions) {
// Skip top-level productions and productions that are already
// void.
if (p.hasAttribute(Constants.ATT_PUBLIC) || AST.isVoid(p.type)) {
continue;
}
// Clear the per-production state.
voidable = true;
// Process the production.
analyzer.process(p);
// Tabulate the results.
if (voidable) {
analyzer.mark(p.qName);
}
}
// On the second iteration, eliminate those productions that are
// bound. Note that this time around we process all
// productions.
secondPhase = true;
for (Production p : m.productions) {
// Process the production.
analyzer.process(p);
}
// Done.
return null;
}
/** Visit the specified binding. */
public Element visit(Binding b) {
// We allow bindings in voidable productions, so that they can
// contain semantic predicates. However, we disallow bindings
// to CodeGenerator.VALUE because they act like
// programmer-specified bindings (e.g., yyValue can be
// referenced in a semantic predicate) but are also constrained
// in their type (i.e., yyValue always has the production's
// type).
if (CodeGenerator.VALUE.equals(b.name)) {
voidable = false;
}
isBound = true;
b.element = (Element)dispatch(b.element);
return b;
}
/** Visit the specified nonterminal. */
public Element visit(NonTerminal nt) {
if (secondPhase) {
if (isBound) {
Production p = analyzer.lookup(nt);
if (analyzer.current() != p) {
// Self-referential productions can still be voided.
analyzer.unmark(p.qName);
}
}
}
isBound = false;
return nt;
}
/** Visit the specified semantic predicate. */
public Element visit(SemanticPredicate p) {
isBound = false;
return p;
}
/** Visit the specified action. */
public Element visit(Action a) {
isBound = false;
voidable = ! a.setsValue();
return a;
}
/** Visit the specified parser action. */
public Element visit(ParserAction pa) {
isBound = false;
voidable = false;
return pa;
}
}
// =========================================================================
/**
* Create a new production voider.
*
* @param runtime The runtime.
* @param analyzer The analyzer utility.
*/
public ProductionVoider(Runtime runtime, Analyzer analyzer) {
super(runtime, analyzer);
}
/** Visit the specified grammar. */
public Object visit(Module m) {
boolean changed;
do {
changed = false;
Tester tester = new Tester(runtime, analyzer);
tester.dispatch(m);
Set voidable = tester.voidable();
analyzer.register(this);
analyzer.init(m);
for (Production p : m.productions) {
// Only process voidable productions.
if (! voidable.contains(p.qName)) {
continue;
}
// Process the production.
if (runtime.test("optionVerbose")) {
System.err.println("[Voiding " + p.qName + "]");
}
analyzer.process(p);
changed = true;
// Patch the type.
p.type = AST.VOID;
p.setProperty(Properties.VOIDED, Boolean.TRUE);
}
} while (changed);
// Done.
return null;
}
/** Visit the specified voided element. */
public Element visit(VoidedElement v) {
v.element = (Element)dispatch(v.element);
// The whole production is being voided; eliminate the voider.
return v.element;
}
/** Visit the specified binding. */
public Element visit(Binding b) {
b.element = (Element)dispatch(b.element);
// Remove bindings to synthetic variables, as they are only used
// inside value elements.
if (Analyzer.isSynthetic(b.name)) {
return b.element;
} else {
return b;
}
}
/** Visit the specified value element. */
public Element visit(ValueElement e) {
// All value elements become null value elements.
return NullValue.VALUE;
}
}