001: /*
002: * xtc - The eXTensible Compiler
003: * Copyright (C) 2004-2007 Robert Grimm
004: *
005: * This program is free software; you can redistribute it and/or
006: * modify it under the terms of the GNU General Public License
007: * version 2 as published by the Free Software Foundation.
008: *
009: * This program is distributed in the hope that it will be useful,
010: * but WITHOUT ANY WARRANTY; without even the implied warranty of
011: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
012: * GNU General Public License for more details.
013: *
014: * You should have received a copy of the GNU General Public License
015: * along with this program; if not, write to the Free Software
016: * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
017: * USA.
018: */
019: package xtc.parser;
020:
021: import xtc.util.Runtime;
022:
023: import xtc.type.AST;
024:
025: /**
026: * Visitor to turn generic productions into void productions. Note
027: * that this visitor also voids productions that have {@link
028: * xtc.tree.Node} or {@link xtc.tree.GNode} as their types.
029: *
030: * <p />This visitor assumes that the entire grammar is contained in a
031: * single module.
032: *
033: * @author Robert Grimm
034: * @version $Revision: 1.11 $
035: */
036: public class GenericVoider extends GrammarVisitor {
037:
038: /**
039: * Create a new generic voider.
040: *
041: * @param runtime The runtime.
042: * @param analyzer The analyzer utility.
043: */
044: public GenericVoider(Runtime runtime, Analyzer analyzer) {
045: super (runtime, analyzer);
046: }
047:
048: /**
049: * Check the specified binding or string match. If the bound or
050: * matched element has been voided by this visitor, this method
051: * reports an error. However, bindings to yyValue in voided
052: * productions are eliminated.
053: *
054: * @param op The binding or string match.
055: * @return The checked element.
056: */
057: protected Element check(UnaryOperator op) {
058: assert (op instanceof Binding) || (op instanceof StringMatch);
059:
060: Element bound = Analyzer.strip(op.element);
061: if (bound instanceof NonTerminal) {
062: NonTerminal nt = (NonTerminal) bound;
063: FullProduction p = analyzer.lookup(nt);
064:
065: if (p.getBooleanProperty(Properties.VOIDED)) {
066: if (op instanceof StringMatch) {
067: runtime.error(
068: "string match for now voided nonterminal '"
069: + nt + "'", op);
070: } else {
071: if (CodeGenerator.VALUE.equals(((Binding) op).name)
072: && analyzer.current().getBooleanProperty(
073: Properties.VOIDED)) {
074: // If the current production has been voided (i.e., was
075: // generic) and the binding's name is yyValue, we can
076: // safely eliminate the binding. It was used to pass a
077: // value through instead of creating a generic node.
078: return nt;
079: } else {
080: runtime.error(
081: "binding for now voided nonterminal '"
082: + nt + "'", op);
083: }
084: }
085: }
086: }
087:
088: return op;
089: }
090:
091: /** Visit the specified grammar. */
092: public Object visit(Module m) {
093: // Initialize the per-grammar state.
094: analyzer.register(this );
095: analyzer.init(m);
096:
097: // First, mark all productions returning nodes or lists of nodes
098: // as void.
099: for (Production p : m.productions) {
100: if (AST.isNode(p.type)
101: || (AST.isList(p.type) && AST.isNode(AST
102: .getArgument(p.type)))) {
103: p.type = AST.VOID;
104: p.setProperty(Properties.VOIDED, Boolean.TRUE);
105: }
106: }
107:
108: // Second, make sure that no void production is bound.
109: for (Production p : m.productions)
110: analyzer.process(p);
111:
112: // Done.
113: return null;
114: }
115:
116: /** Visit the specified binding. */
117: public Element visit(Binding b) {
118: b.element = (Element) dispatch(b.element);
119: return check(b);
120: }
121:
122: /** Visit the specified string match. */
123: public Element visit(StringMatch m) {
124: m.element = (Element) dispatch(m.element);
125: return check(m);
126: }
127:
128: }
|