gt:     return (null == alt)? nt : alt;
context: /*
   * 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.ArrayList;
  import java.util.HashMap;
  import java.util.Iterator;
  import java.util.List;
  import java.util.Map;
  
  import xtc.Constants;
  
  import xtc.tree.Attribute;
  
  import xtc.util.Runtime;
  import xtc.util.Utilities;
  
  /**
   * Visitor to fold duplicate productions into a single production.
   *
   * <p />This visitor assumes that the entire grammar is contained in a
   * single module.
   *
   * @author Robert Grimm
   * @version $Revision: 1.36 $
   */
  public class DuplicateProductionFolder extends GrammarVisitor {
  
    /** The map of from folded nonterminals to replacement nonterminals. */
    protected final Map<NonTerminal, NonTerminal> folded;
  
    /**
     * Create a new duplicate production folder.
     *
     * @param runtime The runtime.
     * @param analyzer The analyzer utility.
     */
    public DuplicateProductionFolder(Runtime runtime, Analyzer analyzer) {
      super(runtime, analyzer);
      folded = new HashMap<NonTerminal, NonTerminal>();
    }
  
    /** Visit the specified grammar. */
    public Object visit(Module m) {
      // Initialize the per-grammar state.
      analyzer.register(this);
      analyzer.init(m);
  
      // Iterate over the grammar, folding duplicates, until none are
      // found.
      EquivalenceTester tester          = new EquivalenceTester();
      boolean           foundDuplicates = false;
      boolean           changed;
      do {
        // Clear the map of replacement nonterminals.
        folded.clear();
  
        changed = false;
  
        // Compare all productions with each other.
        final int l = m.productions.size();
        for (int i=0; i<l; i++) {
          FullProduction p1 = (FullProduction)m.productions.get(i);
  
          // Skip top-level productions, generic productions, and
          // productions that already have been folded.
          if (p1.hasAttribute(Constants.ATT_PUBLIC) ||
              Generifier.isGeneric(p1) ||
              analyzer.isMarked(p1.qName)) {
            continue;
          }
  
          // The nonterminal for the shared production, the shared
          // production, and the list of nonterminal names represented
          // by the shared production.
          NonTerminal    nt         = null;
          FullProduction shared     = null;
          List<String>   sources    = null;
          boolean        isTextOnly = false;
          boolean        isToken    = false;
          boolean        hasOption  = false;
  
          for (int j=i+1; j<l; j++) {
            FullProduction p2 = (FullProduction)m.productions.get(j);
  
            // Skip top-level productions and productions that are not
            // equivalent.
            if (p2.hasAttribute(Constants.ATT_PUBLIC) ||
                (! tester.areEquivalent(p1, p2)) ||
                (p1.getBooleanProperty(Properties.TEXT_ONLY) !=
                 p2.getBooleanProperty(Properties.TEXT_ONLY)) ||
                (p1.getBooleanProperty(Properties.TOKEN) !=
                 p2.getBooleanProperty(Properties.TOKEN))) {
              continue;
            }
  
            // We found duplicate productions.
            foundDuplicates = true;
            changed         = true;
  
            if (null == nt) {
              // This is the first duplicate pair for the production at
              // index i.
              nt         = analyzer.shared();
              shared     = p1;
              if (p1.hasProperty(Properties.DUPLICATES)) {
                sources  = Properties.getDuplicates(p1);
              } else {
                sources  = new ArrayList<String>();
                sources.add(p1.qName.toString());
              }
              isTextOnly = p1.getBooleanProperty(Properties.TEXT_ONLY);
              isToken    = p1.getBooleanProperty(Properties.TOKEN);
              hasOption  = p1.hasProperty(Properties.OPTION);
  
              // Establish a mapping to the shared production.
              folded.put(p1.name, nt);
            }
  
            if (p2.hasProperty(Properties.DUPLICATES)) {
              sources.addAll(Properties.getDuplicates(p2));
            } else {
              sources.add(p2.qName.toString());
            }
            if (p2.hasProperty(Properties.OPTION)) {
              hasOption = true;
            }
  
            // Mark the production for future removal.
            analyzer.mark(p2.qName);
  
            // Establish a mapping to the shared production.
            folded.put(p2.name, nt);
          }
  
          if (null != nt) {
            // Create the shared production.
            shared =
              new FullProduction(new ArrayList<Attribute>(shared.attributes),
                                 shared.type, nt,
                                 nt.qualify(analyzer.module().name.name),
                                 shared.choice);
            shared.setProperty(Properties.DUPLICATES, sources);
            if (isTextOnly) {
              shared.setProperty(Properties.TEXT_ONLY, Boolean.TRUE);
            }
            if (isToken) {
              shared.setProperty(Properties.TOKEN, Boolean.TRUE);
            }
            if (hasOption) {
              shared.setProperty(Properties.OPTION, Boolean.TRUE);
            }
  
            // Replace the first duplicate with the shared production.
            analyzer.remove(p1);
            m.productions.remove(i);
            analyzer.startAdding();
            analyzer.add(shared);
            analyzer.addNewProductionsAt(i);
          }
        }
  
        // If we have not found any duplicate productions, we fall out
        // of the loop.
        if (! changed) {
          break;
        }
  
        // Modify all nonterminals to reference the shared productions.
        for (Production p : m.productions) dispatch(p);
  
        // Remove duplicate productions from the grammar.
        for (Iterator<Production> iter=m.productions.iterator(); iter.hasNext();) {
          Production p = iter.next();
  
          if (analyzer.isMarked(p.qName)) {
            analyzer.unmark(p.qName);
            analyzer.remove((FullProduction)p);
            iter.remove();
          }
        }
      } while (changed);
  
      // Print debugging information.
      if (runtime.test("optionVerbose") && foundDuplicates) {
        Iterator iter = m.productions.iterator();
        while (iter.hasNext()) {
          FullProduction p = (FullProduction)iter.next();
  
          if (p.hasProperty(Properties.DUPLICATES)) {
            String lst = Utilities.format(Properties.getDuplicates(p));
            System.err.println("[Folding " + lst + " into " + p.qName + ']');
          }
        }
      }
  
      return null;
    }
  
    /** Visit the specified nonterminal. */
    public Element visit(NonTerminal nt) {
      NonTerminal alt = folded.get(nt);
  
  