/* * xtc - The eXTensible Compiler * Copyright (C) 2004-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 xtc.util.Runtime; /** * Visitor to fill in meta-data reference counts. This visitor * determines the usage and self counts for a grammar's productions. * Note that this visitor does not create the meta-data records; they * must be created with the {@link MetaDataCreator meta-data creator} * before applying this visitor. Further note that the grammar must * have been {@link Simplifier simplified} and {@link Inliner inlined} * before applying this visitor. Finally, note that this visitor * assumes that the entire grammar is contained in a single module. * * @author Robert Grimm * @version $Revision: 1.22 $ */ public class ReferenceCounter extends GrammarVisitor { /** * The flag for whether the current production is a transformable * direct left-recursive production. */ protected boolean isTransformable; /** * Create a new reference counter. * * @param runtime The runtime. * @param analyzer The analyzer utility. */ public ReferenceCounter(Runtime runtime, Analyzer analyzer) { super(runtime, analyzer); } /** Visit the specified grammar. */ public Object visit(Module m) { // Initialize the per-grammar state. analyzer.register(this); analyzer.init(m); // Clear the usage and self counts. for (Production p : m.productions) { MetaData md = (MetaData)p.getProperty(Properties.META_DATA); md.usageCount = 0; md.selfCount = 0; } // Determine the usage and self counts. for (Production p : m.productions) { isTransformable = ((runtime.test("optimizeLeftRecursions") || runtime.test("optimizeLeftIterations")) && DirectLeftRecurser.isTransformable((FullProduction)p)); analyzer.process(p); } // Done. return null; } /** Visit the specified ordered choice. */ public Element visit(OrderedChoice c) { boolean top = isTopLevel; isTopLevel = false; isBound = false; boolean last = isLastElement; final int length = c.alternatives.size(); for (int i=0; i<length; i++) { isLastElement = top || last; needsSequence = true; Sequence s = c.alternatives.get(i); if (top && isTransformable && DirectLeftRecurser.isRecursive(s, (FullProduction)analyzer.current())) { dispatch(s.subSequence(1)); } else { dispatch(s); } } isLastElement = false; needsSequence = false; // There is no need to set the alternatives again, as the // reference counter does not modify the grammar. return c; } /** Visit the specified nonterminal. */ public Element visit(NonTerminal nt) { MetaData md = (MetaData)analyzer.lookup(nt). getProperty(Properties.META_DATA); md.usageCount++; if (analyzer.current().name.equals(nt)) { md.selfCount++; } // If the nonterminal appears within a once-or-more repetition in // a non-transient production, we need to count it twice. After // all, once-or-more repetitions in non-transient productions are // desugared into the equivalent right-recursive productions. If // the nonterminal appears within an ordered choice inside that // repetition, it may, after all, not need to be counted twice, // since ordered choices are often lifted into their own // productions. However, reproducing the lifting logic here is // prohibitive and, consequently, we make a conservative // assumption that all nonterminals appearing within once-or-more // repetitions in non-transient productions need to be counted // twice. if (isRepeatedOnce && (analyzer.current().isMemoized() || (! runtime.test("optimizeRepeated")))) { md.usageCount++; if (analyzer.current().name.equals(nt)) { md.selfCount++; } } // Done. return nt; } }