/*
* 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.util.Runtime;
import xtc.type.AST;
/**
* Visitor to turn generic productions into void productions. Note
* that this visitor also voids productions that have {@link
* xtc.tree.Node} or {@link xtc.tree.GNode} as their types.
*
* <p />This visitor assumes that the entire grammar is contained in a
* single module.
*
* @author Robert Grimm
* @version $Revision: 1.11 $
*/
public class GenericVoider extends GrammarVisitor {
/**
* Create a new generic voider.
*
* @param runtime The runtime.
* @param analyzer The analyzer utility.
*/
public GenericVoider(Runtime runtime, Analyzer analyzer) {
super(runtime, analyzer);
}
/**
* Check the specified binding or string match. If the bound or
* matched element has been voided by this visitor, this method
* reports an error. However, bindings to yyValue in voided
* productions are eliminated.
*
* @param op The binding or string match.
* @return The checked element.
*/
protected Element check(UnaryOperator op) {
assert (op instanceof Binding) || (op instanceof StringMatch);
Element bound = Analyzer.strip(op.element);
if (bound instanceof NonTerminal) {
NonTerminal nt = (NonTerminal)bound;
FullProduction p = analyzer.lookup(nt);
if (p.getBooleanProperty(Properties.VOIDED)) {
if (op instanceof StringMatch) {
runtime.error("string match for now voided nonterminal '"+nt+"'", op);
} else {
if (CodeGenerator.VALUE.equals(((Binding)op).name) &&
analyzer.current().getBooleanProperty(Properties.VOIDED)) {
// If the current production has been voided (i.e., was
// generic) and the binding's name is yyValue, we can
// safely eliminate the binding. It was used to pass a
// value through instead of creating a generic node.
return nt;
} else {
runtime.error("binding for now voided nonterminal '" + nt + "'", op);
}
}
}
}
return op;
}
/** Visit the specified grammar. */
public Object visit(Module m) {
// Initialize the per-grammar state.
analyzer.register(this);
analyzer.init(m);
// First, mark all productions returning nodes or lists of nodes
// as void.
for (Production p : m.productions) {
if (AST.isNode(p.type) ||
(AST.isList(p.type) && AST.isNode(AST.getArgument(p.type)))) {
p.type = AST.VOID;
p.setProperty(Properties.VOIDED, Boolean.TRUE);
}
}
// Second, make sure that no void production is bound.
for (Production p : m.productions) analyzer.process(p);
// Done.
return null;
}
/** Visit the specified binding. */
public Element visit(Binding b) {
b.element = (Element)dispatch(b.element);
return check(b);
}
/** Visit the specified string match. */
public Element visit(StringMatch m) {
m.element = (Element)dispatch(m.element);
return check(m);
}
}