/*
* xtc - The eXTensible Compiler
* Copyright (C) 2005-2006 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.util.Runtime;
/**
* Visitor to identify a grammar's real root. A real root is a
* top-level nonterminal, whose production directly or indirectly
* depends on all other top-level nonterminals. If a grammar has such
* a root, this visitor annotates the grammar with the {@link
* Properties#ROOT root} property.
*
* <p />Note that this visitor assumes that the entire grammar is
* contained in a single module.
*
* @author Robert Grimm
* @version $Revision: 1.12 $
*/
public class RootFinder extends GrammarVisitor {
/** The set of qualified top-level nonterminals. */
protected Set<NonTerminal> topLevel;
/**
* Create a new root finder.
*
* @param runtime The runtime.
* @param analyzer The analyzer utility.
*/
public RootFinder(Runtime runtime, Analyzer analyzer) {
super(runtime, analyzer);
topLevel = new HashSet<NonTerminal>();
}
/** Visit the specified grammar. */
public Object visit(Module m) {
// Make sure we don't do the work several times.
if (m.hasProperty(Properties.ROOT)) {
return null;
}
// Initialize the per-grammar state.
analyzer.register(this);
analyzer.init(m);
// Fill in the set of top-level nonterminals.
topLevel.clear();
for (Production p : m.productions) {
if (p.hasAttribute(Constants.ATT_PUBLIC)) {
topLevel.add(p.qName);
}
}
// Get the trivial case out of the way.
if (1 == topLevel.size()) {
m.setProperty(Properties.ROOT, topLevel.toArray()[0]);
return null;
}
// Traverse the grammar, starting with each top-level nonterminal.
for (NonTerminal nt : topLevel) {
// Mark the top-level nonterminals.
analyzer.unmarkAll();
analyzer.mark(topLevel);
// Traverse the grammar.
analyzer.notWorkingOnAny();
dispatch(nt);
// Is this nonterminal a real root?
if (! analyzer.hasMarked()) {
if (runtime.test("optionVerbose")) {
System.err.println("[Recognizing " + nt + " as real root]");
}
m.setProperty(Properties.ROOT, nt);
break;
}
}
// Done.
return null;
}
/** Visit the specified nonterminal. */
public Element visit(NonTerminal nt) {
Production p = analyzer.lookup(nt);
if (! analyzer.isBeingWorkedOn(p.qName)) {
analyzer.workingOn(p.qName);
analyzer.unmark(p.qName);
dispatch(p);
}
return nt;
}
}