/*
 *
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You under the Apache License, Version 2.0
 *  (the "License"); you may not use this file except in compliance with
 *  the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */

package jburg.burg;

//  BURM specification is parsed into ANTLR2 AST objects.
import antlr.collections.AST;

//  The code emitter interface definition.
import jburg.burg.emitlangs.EmitLang;

//  The factory that selects and creates an emitter.
import jburg.burg.emitlangs.JBurgEmitterFactory;

//  The i-node adapter interface definition.
import jburg.burg.inode.InodeAdapter;
import jburg.burg.inode.InodeAdapter2;
//  Interface for i-node adapters that need
//  to emit support routines.
import jburg.burg.inode.InodeAuxiliarySupport;

//  The factory that selects and creates an I-node adapter.
import jburg.burg.inode.InodeAdapterFactory;

//  Token types from ANTLR.
import jburg.parser.JBurgTokenTypes;

//  Version file, generated by build.
import jburg.generator.version.JBurgVersion;


import java.io.PrintStream;

import java.lang.reflect.Modifier;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.Vector;


@SuppressWarnings("nls")
public class JBurgGenerator implements JBurgTokenTypes
{
    /**
     *  The JBurg specification's rules are categorized as:
     *  <ul>
     *  <li> Pattern  (e.g., integer = PLUS(integer, integer) )
     *  <li> Simple Transformational   (e.g., registerOperand  = integer)
     *  <li> Complex Transformational  (e.g., string = integer { code to convert integer to string} )
     *  </ul>
     *
     *  Simple transformational rules allow a subgoal to satisfy
     *  other subgoals without additional processing; complex
     *  transformational rules allow one subgoal to feed additional
     *  processing that can satisfy other subgoals (at added cost).
     *
     *  Syntactically, a simple transformational rule is distinguished 
	 *  by its lack of a block of code.
	 *  Simple transformational rules are also known as "chain rules"
	 *  in simpler BURGs that don't support complex transformational
	 *  rules.  This longer name was adopted to highlight that difference,
	 *  but a simple transformational rule is, in fact, a chain rule
	 *  by a different name.
     */

	/**
     *   The table of all transformation rules; keyed
     *   by the rules' necessary state, e.g., integer
	 *   in the rule number = integer).
     */
    Map<String, Vector<JBurgRule>> transformationRules = new HashMap<String, Vector<JBurgRule>>();

	/**
	 *  The table of all pattern rules, keyed
	 *  by the top-level operator of the pattern,
	 *  e.g., PLUS in the pattern integer = PLUS(integer, integer).
	 */
    Map<String, Vector<JBurgRule>> patternRules = new HashMap<String, Vector<JBurgRule>>();

    /**
     *  The goal states' names become symbolic constants
     *  in the generated reducer.
     */
    Set<String> allGoalStates = new HashSet<String>();

    /**
     *  Closure sets are organized by their goal state; each state's
     *  closure set contains an entry for every closure
     *  path from any other non-terminal state to the target state.
     */
    Map<String, Vector<ClosureRecord>> closureSets = new HashMap<String, Vector<ClosureRecord>>();

    /**  Action code fragments.  */
    ArrayList<JBurgReduceAction> reduceActions = new ArrayList<JBurgReduceAction>();

    /**
     *  Cost function fragments.
     *  Note that cost functions may also be embedded
     *  in the inline code blocks in a specification.
     */
    ArrayList<AST> costFunctions = new ArrayList<AST>();

    /**  The names of any interfaces that the generated BURM implements.  */
    Vector<String> interfaceNames = new Vector<String>();
    
    /** blocks of code to be added into the class verbatim */
    Vector<String> inclassCode = new Vector<String>();

    /**
     *  Rules that can generate compressed annotations.
     */
    RulesByOperatorAndArity compressedAnnotations = new RulesByOperatorAndArity();

    /** The package name of the generated reducer. */
    String packageName = null;

    /**  Header code, copied as-is into the reducer. */
    String headerBlock = null;

    /**
     *  The name of the i-node class that's being labeled and reduced.
     */
    String iNodeClass = null;

	/** 
	 *  I-node adapter class name.  The adapter is instantiated by name.
	 *  @note: specified as an alternative to the I-node class' name,
	 *  which selects a builtin adapter.
	 */
	String iNodeAdapterClass;

    /**
     *  The return types for specific states.
     */
    Map<String, String> returnTypeTable = new HashMap<String, String>();

    /**
     *  The default return type of the reducer functions.
     *  If this is defaulted to null, the generated reducer will return nodes of the iNodeClass.
     */
    String defaultReturnType = null;

    /**
     *  Table of properties to be added to the BURM.
     *  Each one is a name/type pair; the BURM gets
     *  a private member and get/set methods.
     */
    Map<String, String> burmProperties = new HashMap<String, String>();

    /**
     *  Simple transformations simply delegate to their antecedent reduction,
     *  so all transformations to a given nonterminal state can share the
     *  same rule.
     */
    Map<String, JBurgRule> simpleTransformationRules = new HashMap<String,JBurgRule>();

    /**  Include debugging code in the generated reducer?  */
    boolean debugMode;
    
    /** 
     * If the pattern-matcher generator fails, dump its annotation tree here.
     * Note: this is only enabled (or useful) when debugging JBurg's own BURM. 
     */
    String patternMatcherDumpFile = null;

    /**  
     *  Caller's logging interface.  If defaulted to null, informational and
     *  error messages go to System.out and System.err respectively.
     */
    Logger logger = null;
    
    /**
     * Name for the language to emit code in (default is assumed to be java (=emitlang.EmitJava)
     */
    String emitLanguageName = null;
    
    /** Code emitter to use (selected using the language name) */
	EmitLang codeEmitter = null;

	/** I-node adapter to use */
	InodeAdapter iNodeAdapter;
	
	/** 
	 *  If the InodeAdapter is also an InodeAdapter2,
	 *  keep a reference to it. 
	 */
	InodeAdapter2 adapter2;

	/**  Cumulative error count. */
	int errCount = 0;

	/**  Name of the initial parameter to the label routine. */
	private static String initalParamName = "to_be_labelled";
	
	/** Name of the node in the reducer */
	private String reducerNodeName = "__p";
	
	/** Name of the stack of reduced values */
	private String reducedValuesName = "__reducedValues";

    /**  Default error handler.  null means use hard-coded default, i.e., throw an exception. */
    String defaultErrorHandler = null;

    /**  Prologue snippets mapped to the corresponding rule number. */
    Map<Integer,String> prologueBlocks = new TreeMap<Integer,String>();

    /** 
     *  All operators known to any pattern accumulate in this set
     *  so that the BURG can generate operator-specific getNthChild()
     *  routines.
     */
    Set<String> allOperators = new TreeSet<String>();
    
    /**
     * operator mapped to node type. This map is populated by JBurg.NodeType
     * directives.
     * <p>
     * For example, an operator named IdentifierID would be associated with
     * {@code IIdentifierNode*} in this map if the input contained
     * {@code JBurg.NodeType IdentifierID = IIdentifierNode*;
     */
    final Map<String, String> opcodeNodeTypes = new HashMap<String, String>();
    
    /**
     *  The type of the INodes' opcode.  Defaults to int but
     *  can be an enum for maintainability.
     */
    String opcodeType = "int";

    /**
     *  Manifest constants in the JBurg specification.
     */
    Map<String,Integer> manifestConstants = new HashMap<String,Integer>();
    
    /**
     *  Patterns that have been optimized out are represented
     *  by this not-likely-to-compile sequence.
     */
    final private static String nilPattern = "-null-";

    /**
     *  Manifest constant for method declarations.
     */
    final private static Class<?>[] throwsNothing = null;

    /**
     *  Manifest constant for method declarations.
     */
    final private static Class<?>[] throwsException = new Class<?>[] { Exception.class };


    /**
     *  Manifest constant for method declarations.
     */
    final private static String[][] noFormalParameters = new String[][] { };

    /**
     *  Manifest constant for method calls.
     */
    final private static String[] noActualParameters = { };

    /**
     *  Manifest constant for an unfeasible rule.
     */
    final private static String NO_FEASIBLE_RULE = "-1";

    /**
     *  Manifest constant for an uninitialized cost.
     */
    final private static String UNINITIALIZED = "-1";

    /**
     *  NamedPattern holds a named pattern, and keeps a table of the reductions that refer to it.
     *  (See defect 3063143 for problems with multiple reductions sharing a pattern.)
     */
    class NamedPattern
    {
        String patternName;
        AST pattern = null;
        Vector<AST> reductions = new Vector<AST>();

        NamedPattern(String name)
        {
            this.patternName = name;
        }
    }

    /**
     *  A PatternMap keeps the map of pattern names to reductions.
     */
    @SuppressWarnings("serial")
    class PatternMap extends  TreeMap<String,NamedPattern>
    {
        NamedPattern getPattern(String key)
        {
            if ( !super.containsKey(key) )
                put(key, new NamedPattern(key));
            return super.get(key);
        }

        void addPattern(String key, AST pattern)
        {
            NamedPattern p = getPattern(key);
            p.pattern = pattern;
        }

        void addReduction(String key, AST reduction)
        {
            NamedPattern p = getPattern(key);
            p.reductions.add(reduction);
        }
    }

    PatternMap namedPatterns = new  PatternMap();

    /**
     *  Aggregate path computations if more than this threshold
     *  number of pattern elements share a path.
     */
    private int aggregatePathThreshold = Integer.MAX_VALUE;

    public void setAggregatePathThreshold(int threshold)
    {
        this.aggregatePathThreshold = threshold;
    }

    /**
      *  @param root - the root of the AST generated by parsing the .jburg specification.
      */
    public JBurgGenerator(AST root, Logger log) throws Exception
    {
        this.logger = log;

        //  Walk over the children of the root, each of which 
		//  is a self-contained syntactical construct, and
        //  find an appropriate action for each.
        for (AST currentNode = root; currentNode != null;
                currentNode = currentNode.getNextSibling()) 
		{
            switch (currentNode.getType()) 
			{
            case COST_FUNCTION:
                this.costFunctions.add(currentNode);
                break;

            case HEADER_DECLARATION:

				if ( null == this.headerBlock )
				{
					this.headerBlock = getCodeBlock(currentNode);
				}
				else
				{
                    throw new IllegalArgumentException("The class header may only be specified once.");
				}

                break;
                
            case INCLASS_DECLARATION:
				{
            		this.inclassCode.add( stripBrackets(getCodeBlock(currentNode)) );
            	}
				
				break;

            case INODE_ADAPTER_DECLARATION:

                if (this.iNodeAdapterClass == null)
				{
					this.iNodeAdapterClass = getIdentifierText(currentNode.getFirstChild());
				}
				else
				{
                    throw new IllegalArgumentException("INodeAdapter may only be specified once.");
				}

				break;

            case INODE_TYPE_DECLARATION:

                if (this.iNodeClass == null)
				{
                    this.iNodeClass = getIdentifierText(currentNode.getFirstChild());
				}
                else
				{
                    throw new IllegalArgumentException("INodeType may only be specified once.");
				}

                break;
                
            case LANGUAGE_DECLARATION:

            	if ( null == this.emitLanguageName )
				{
            		this.emitLanguageName = currentNode.getFirstChild().getText();
				}
            	else
				{
            		throw new IllegalArgumentException("Language may only be specified once.");
				}

            	break;

            case IMPLEMENTS_INTERFACE_SPECIFICATION:
                this.interfaceNames.addElement(getIdentifierText( currentNode.getFirstChild()) );

                break;

            case PACKAGE_SPECIFICATION:
                if ( null == this.packageName )
				{
					this.packageName = getIdentifierText(currentNode.getFirstChild());
				}
				else
				{
            		throw new IllegalArgumentException("package may only be specified once.");
				}

                break;

            case PROPERTY_SPECIFICATION: 
				{
					String propertyType = getIdentifierText(currentNode.getFirstChild());
					String propertyName = currentNode.getFirstChild().getNextSibling().getText();
					this.burmProperties.put(propertyName, propertyType);
				}

                break;

            case RETURN_DECLARATION:

                if (this.defaultReturnType == null)
                    this.defaultReturnType = getIdentifierText(currentNode.getFirstChild());
                else
                    throw new IllegalArgumentException( "ReturnType may only be specified once.");

                break;

            case PATTERN_RULE:
                addPatternRule(currentNode);

                break;

            case SIMPLE_TRANSFORMATION_RULE:
                addSimpleTransformationRule(currentNode);

                break;

            case TRANSFORMATION_RULE:
                addComplexTransformationRule(currentNode);

                break;

            case TYPED_RETURN_DECLARATION:

                {
                    String stateName = currentNode.getFirstChild().getText();
                    String returnType = getIdentifierText(currentNode.getFirstChild()
                            .getNextSibling());

                    //  Put the return declaration in the table, but only once per state.
                    Object typeCollision = this.returnTypeTable.put(stateName, returnType);
                    if ( null != typeCollision )
                    {
                        throw new IllegalArgumentException(
                            "A state may only specify one ReturnType."
                        );
                    }
                }

                break;

            case PATTERN_DECLARATION:
                {
                    String pattern_name = currentNode.getFirstChild().getText();
                    namedPatterns.addPattern(pattern_name, currentNode);
                }
                break;

            case REDUCTION_DECLARATION:
                {
                    String pattern_name = currentNode.getFirstChild().getNextSibling().getText();
                    namedPatterns.addReduction(pattern_name, currentNode);
                }
                break;

            case DEFAULT_ERROR_HANDLER:
                this.defaultErrorHandler = getCodeBlock(currentNode);
                break;
            case NODE_TYPE:
            {
                final String operatorID = currentNode.getFirstChild().getText();
                assert !operatorID.isEmpty() : "Parser should never create empty operator!";
                final String nodeType = currentNode.getFirstChild().getNextSibling().getText();               
                assert !nodeType.isEmpty() : "Parser should never create empty node type!";
                
                if (this.opcodeNodeTypes.put(operatorID, nodeType) != null)
                {
                    final String message = "Duplicate node type declaration for '"
                        + operatorID
                        + "'.";
                    throw new IllegalArgumentException(message);
                }
                break;
            }
            case OPCODE_TYPE:
                this.opcodeType = currentNode.getFirstChild().getText();
                break;

            case MANIFEST_CONSTANT:
                manifestConstants.put(currentNode.getFirstChild().getText(), Integer.parseInt(currentNode.getFirstChild().getNextSibling().getText()));
                break;

            default:
                throw new IllegalArgumentException("Unknown specification AST type " +
            											String.valueOf(currentNode.getType()));
            }
        }

        //  Set the language emitter.
        codeEmitter = JBurgEmitterFactory.getEmitter(emitLanguageName, getLogger());

        if ( codeEmitter == null )
		{
			throw new IllegalStateException("Unknown language specified: \""+ emitLanguageName +"\"");
        }

		if ( iNodeClass != null )
		{
			if ( iNodeAdapterClass != null )
			{
				try
				{
					this.iNodeAdapter = (InodeAdapter)Class.forName(iNodeAdapterClass).newInstance();
				}
				catch ( Exception ex )
				{
					ex.printStackTrace();
					throw new IllegalArgumentException ("Unable to instantiate i-node adapter " + iNodeAdapterClass );
				}
			}
			else
			{
				this.iNodeAdapter = InodeAdapterFactory.getAdapter(iNodeClass);
			}
			
			codeEmitter.setINodeType(this.iNodeClass);

			if ( this.iNodeAdapter != null )
			{
				logger.info("Using i-node adapter " + this.iNodeAdapter.getClass().getName() );
			}
			else
			{
				getLogger().warning("using default i-node adapter, no adapter matches " + iNodeClass );
				this.iNodeAdapter = new jburg.burg.inode.DefaultAdapter();
			}
			
			//  See if the adapter is also an InodeAdapter2 implementation.
			this.adapter2 = (this.iNodeAdapter instanceof InodeAdapter2)? (InodeAdapter2)this.iNodeAdapter : null;
		}
		else
		{
			throw new IllegalStateException("You must specify the i-node type.");
		}

		codeEmitter.setInodeAdapter(this.iNodeAdapter);

        //  Default return type is the same as the INode class.
        if (this.defaultReturnType == null)
		{
            this.defaultReturnType = this.iNodeClass;
		}

        //  Mutate pattern/reduction decl pairs into pattern rules.
        for ( NamedPattern np: namedPatterns.values() )
        {
            if ( np.pattern != null && np.reductions.size() > 0 )
            {
                AST named_pattern = np.pattern.getFirstChild().getNextSibling().getFirstChild();

                for ( AST reduction: np.reductions )
                {    
                    //  Splice the pattern into the reduction AST.
                
                    AST nt_state = reduction.getFirstChild();
                    AST pattern_name = nt_state.getNextSibling();
                    AST cost_decl = pattern_name.getNextSibling();
                    
                    //  Create a new holder for the pattern
                    //  so the original AST isn't mutated.
                    AST pattern_holder = new antlr.CommonAST();
                    pattern_holder.setFirstChild(named_pattern);
                    nt_state.setNextSibling(pattern_holder);
                    pattern_holder.setNextSibling(cost_decl);
    
                    //  Give the composite AST the appropriate type
                    //  for its pattern.
                    reduction.setType(PATTERN_RULE);
    
                    addPatternRule(reduction);
                }
            }
            else if ( np.pattern != null )
            {
                getLogger().warning("pattern " + np.patternName + " has no reduction - ignored.");
            }
            else if ( np.reductions.size() > 0 )
            {
                throw new IllegalStateException("Reduction " + np.patternName + " has no associated pattern.");
            }
        }

        //  Add target-specific logic to the simple transformations' rules.
        for ( JBurgRule rule: this.simpleTransformationRules.values() )
        {
            String actionCode = String.format(
                "%s%s",
                codeEmitter.genReturnValue(
                    codeEmitter.genPopFromStack(reducedValuesName, getReturnType(rule.getGoalState()))
                ),
                codeEmitter.genEndStmt()
            );

            JBurgReduceAction action = addAction(actionCode, rule.getGoalState());
            action.setAntecedentState(rule.getAntecedentState());
            rule.setReduceAction(action);
        }

        //  Compute the closure sets.
        computeClosureSets();
    }
    
    /**
     *  Add a reduce action to the action list.
	 *  Actions are keyed by number in the generated BURM.
     */
    private JBurgReduceAction addAction(String strAction, String strState)
    {
        JBurgReduceAction newAction = new JBurgReduceAction(strAction);
        this.reduceActions.add(newAction);

        //  The first index is used as the default action for
        //  simple transformation rules.
        newAction.setIndex(this.reduceActions.size());
        newAction.setState(strState);

        return newAction;
    }

    /**
     *  Sort through a rule's action routine specifications and assemble
     *  a JBurgReduceAction from them.
     *  @param parent - the rule's AST.
     *  @param goal_state - the state the rule produces.
     *  
     */
    private JBurgReduceAction decodeReduction(AST parent, String goal_state)
    {
        JBurgReduceAction action = null;
        AST reduction = null;

        if ( hasASTOfType(parent, REDUCTION_ACTION) )
        {
            reduction = getASTByType(parent, REDUCTION_ACTION);
            action = addAction(getCodeBlock(reduction), goal_state);
        }
        else if ( hasASTOfType(parent, EXPLICIT_REDUCTION ) )
        {
            reduction = getASTByType(parent, EXPLICIT_REDUCTION);
            action = addAction( "return " + decodeProcedureCall(reduction) + ";", goal_state );
        }
        else
        {
            throw new IllegalStateException("A reduction must specify an implementation block or callback function");
        }

        if ( hasASTOfType(reduction, PROLOGUE) )
        {
            AST prologue = getASTByType(reduction, PROLOGUE);
            String prologue_action = null;

            if ( hasASTOfType(prologue, BLOCK) )
            {
                prologue_action = getCodeBlock(prologue);
            }
            else if ( hasASTOfType(prologue, PROCEDURE_CALL) )
            {
                prologue_action = "\t" + decodeProcedureCall(prologue) + ";";
            }
            else
            {
                throw new IllegalStateException("Prologue block with no inline code or callback");
            }

            this.prologueBlocks.put(action.index, prologue_action);
        }

        return action;
    }

    private String decodeProcedureCall(AST parent)
    {
        AST procall = getASTByType(parent, PROCEDURE_CALL);

        StringBuilder buffer = new StringBuilder();

        AST id = procall.getFirstChild();
        buffer.append(id.getText());
        buffer.append("(");

        AST param = id.getNextSibling();
        if ( param != null )
        {
            buffer.append(param.getText());

            for ( param = param.getNextSibling(); param != null; param = param.getNextSibling() )
            {
                buffer.append(",");
                buffer.append(param.getText());
            }
        }
        buffer.append(")");

        return buffer.toString();
    }

    /**
     *  Add a complex transformation rule to its table,
     *  and record the rule's reduction action.
     */
    private void addComplexTransformationRule(AST transform)
        throws Exception
    {
        JBurgRule n = addNamedRule(this.transformationRules, transform);

        //  Prepare the rule's reduce action.
        JBurgReduceAction action = decodeReduction(transform, n.getGoalState());
        action.setAntecedentState(n.getAntecedentState());

        //  Add the antecedent state by name as an alias action routine "parameter."
        //  The "parameter" is popped off the reduced values stack into a local variable.
        action.addParameter(
            n.getAntecedentState(), 
            n.getAntecedentState(), 
            ParameterDescriptor.ArityType.Fixed
        );

        n.setReduceAction(action);
    }

    /**
     *  If this simple transformation is not yet known, add it to its rule table.
     */
    private void addSimpleTransformationRule(AST transform)
    {
        JBurgRule newRule = new JBurgRule(transform);

        String key = String.format("%s=%s", newRule.getGoalState(), newRule.getAntecedentState());

        if ( ! this.simpleTransformationRules.containsKey(key) )
        {
            //  Ensure this rule's nonterminal is known.
            this.allGoalStates.add(newRule.getGoalState());
            addNamedRule(this.transformationRules, newRule);

            //  Target-specific action logic will be
            //  added once the code emitter is up.
            this.simpleTransformationRules.put(key, newRule);
        }
    }

    /**
     *  Wrap a pattern rule in an I-node, and add it to pattern rule table.
     */
    private void addPatternRule(AST newRule) throws Exception
    {
        JBurgRule n = addNamedRule(this.patternRules, newRule);

        JBurgReduceAction newAction = decodeReduction(newRule, n.getGoalState());

        n.setReduceAction(newAction);
    }

    /**
     *  Wrap a rule in an I-node, and add it to the list of rules
     *  that produce a particular subgoal.
     */
    private JBurgRule addNamedRule(Map<String, Vector<JBurgRule>> ruleTable, AST newAST)
    {
        JBurgRule newRule = new JBurgRule(newAST);

        //  Ensure this rule's nonterminal is known.
        this.allGoalStates.add( newRule.getGoalState() );
        return addNamedRule(ruleTable, newRule);
    }

    /**
     *  Add a rule to its rule table.
     *  @param ruleTable - the appropriate table for this type of rule.  Keyed by operator.
     *  @param newRule - the rule to add.
     */
    private JBurgRule addNamedRule(Map<String, Vector<JBurgRule>> ruleTable, JBurgRule newRule)
    {
        //  Store the rule by operator.
        String operator = newRule.getOperator();

        Vector<JBurgRule> vRules = ruleTable.get(operator);

        if (vRules == null)
        {
            vRules = new Vector<JBurgRule>();
            ruleTable.put(operator, vRules);
        }

        vRules.add(newRule);

        return newRule;
    }

    /**
     *  Compute the closure set of a rule:  the set of rules that
     *  that can transform this rule's goal to satsify another goal.
	 *  @note computeClosure is public so that it can be applied
	 *    by JBurgUtilities.
     */
    public void computeClosure(JBurgRule n) throws Exception
    {
		//  Get the list of transformation rules that can use this rule's goal state.
        Vector<JBurgRule> vClosureCandidate = this.transformationRules.get(n.getGoalState());

        if (vClosureCandidate != null) 
		{
            //  Found some closures:
            //  create or fetch this target state's closure set.
            Vector<ClosureRecord> vClosureSet = this.closureSets.get(n.getGoalState());

            if (vClosureSet == null) 
			{
                vClosureSet = new Vector<ClosureRecord>();
                this.closureSets.put(n.getGoalState(), vClosureSet);
            }

            for (JBurgRule nPrime:vClosureCandidate ) 
			{

                //  Add this closure to the closure set.
                ClosureRecord newClosure = new ClosureRecord(nPrime);

                if (!vClosureSet.contains(newClosure)) 
				{
                    vClosureSet.add(newClosure);
                }
            }
        }
    }

    /**
     *  Scan all the rules and compute their closure sets.
     */
    private void computeClosureSets() throws Exception
    {
        for (Vector<JBurgRule> patterns : this.patternRules.values() ) 
		{
            try 
			{
                JBurgUtilities.applyToAll( this, patterns, "computeClosure", JBurgRule.class);
            }
			catch (Exception e)
			{
                e.printStackTrace();
            }
        }

        for (Vector<JBurgRule> transformations : this.transformationRules.values() )
		{
            try
			{
                JBurgUtilities.applyToAll( this, transformations, "computeClosure", JBurgRule.class);
            }
			catch (Exception e)
			{
                e.printStackTrace();
            }
        }
    }

    /**
     *  Emit the BURG specification's resultant BURM to an output stream.
     */
    public int generate(String className, PrintStream output)
    {
        try
		{
            codeEmitter.setOpcodeType(this.opcodeType);
        	emitHeader(className, output);
        	codeEmitter.emitInclass(iNodeClass, this.inclassCode, output);

        	emitNTConstants(this.allGoalStates, output);
            emitStatics(output);

			if ( this.iNodeAdapter instanceof InodeAuxiliarySupport )
			{
				((InodeAuxiliarySupport)this.iNodeAdapter).emitAuxiliarySupport(codeEmitter, output);
			}

            emitLabelFunction(this.iNodeClass, output);
            ruleSemanticAnalysis();

            if  ( codeEmitter.supportsSpecializedAnnotations() )
            {
                for (Vector<JBurgRule> vPatternRules: this.patternRules.values())
                    this.compressedAnnotations.addAll(vPatternRules);
            }
            else
            {
                emitComputeCostMatrixFunction(this.iNodeClass, output);
                emitClosures(this.closureSets, this.iNodeClass, output);
            }
            emitActions(this.reduceActions, this.iNodeClass, output);
            emitCostFunctions(this.costFunctions, this.iNodeClass, output);

            if ( codeEmitter.supportsSpecializedAnnotations() )
                emitCompressedAnnotations(output);
            
            //  If there's an InodeAdapter2 present,
            //  emit the getNthChild routine that
            //  implements the adapter's node-handling logic.
            if ( this.adapter2 != null)
                emitGetNthChild(output);

            codeEmitter.emitTrailer(
				className,
				this.iNodeClass,
				this.allGoalStates,
				this.burmProperties,
				this.debugMode,
                this.defaultErrorHandler,
                this.prologueBlocks,
				output
			);
        }
		catch (Exception e)
		{
            e.printStackTrace();
			this.errCount++;
        }

		return this.errCount;
    }

    //  Emit static lookup tables.
    private void emitStatics(PrintStream output)
    {
        //  Emit the static table of subgoal records.
        Map<Integer, Vector<JBurgPatternMatcher>> rules_by_action = new HashMap<Integer,Vector<JBurgPatternMatcher>>();
        int max_action = 0;
        for (Vector<JBurgRule> vPatternRules: patternRules.values())
        {
            for (JBurgRule p: vPatternRules)
            {
                int action_number = p.getReduceAction().getIndex();
                max_action = Math.max(max_action, action_number);
                
                if ( p.patternMatcher.isNary() )
                {
                    rules_by_action.put(action_number,new Vector<JBurgPatternMatcher>());
                    rules_by_action.get(action_number).add(p.patternMatcher);
                }
                else if (!p.patternMatcher.getParameterizedSubtrees().isEmpty() )

                {
                    rules_by_action.put(action_number,p.patternMatcher.getParameterizedSubtrees());
                }
            }
        }

        //  Ensure the transformation rules' actions have subgoal records.
        for ( Vector<JBurgRule> transformations:  this.transformationRules.values() )
            for ( JBurgRule t: transformations )
                max_action = Math.max(max_action, t.getReduceAction().getIndex());
        
        codeEmitter.emitStatics(max_action, rules_by_action, output);

        //  Generate manifest constant declarations.
        for ( Map.Entry<String,Integer> constants : this.manifestConstants.entrySet() )
            genInstanceField(
                output,
                Modifier.PUBLIC + Modifier.STATIC + Modifier.FINAL,
                "int",
                constants.getKey(),
                constants.getValue().toString()
            );
    }

    /**
     *  Get the payload of an identifier, with error checking.
     *  @return the identifier's text.
     */
    private String getIdentifierText(AST p)
    {
		if ( p.getType() != IDENTIFIER )
		{
			throw new IllegalStateException ( "Expected IDENTIFIER, found " + p.toStringTree() );
		}

        return p.getText();
    }

    /**
     *  Does this production have a closure set?
     *  @return true if n has a closure set.
     *  @param n - the production of interest.
     */
    public boolean hasClosure(JBurgProduction n)
    {
        return this.closureSets.get(n.getGoalState()) != null;
    }

    /**
      *  Emit the file/class header information.
      */
    private void emitHeader(String className, PrintStream output)
    {
        genComment(output, " Generated " + new java.util.Date().toString() + " by JBurg version " + JBurgVersion.version  );
        output.println();
        
        codeEmitter.emitHeader(className, this.packageName, this.headerBlock, this.interfaceNames, this.debugMode, output);
    }

	/**
	 *  @return the return type for a specific state.
	 */
	public String getReturnType(String stateName)
	{
		Object result = returnTypeTable.get(stateName);

		if (result == null)
			result = defaultReturnType;

		return result.toString();
	}
	
    /**
     *  setDebugMode controls the level of debugging code that will be generated in the reducer.
     */
    public void setDebugMode(boolean bDebugMode)
    {
        debugMode = bDebugMode;
    }
    
	/**
	 *  @return the text of the parent AST's code block, which is represented as a BLOCK token.
	 */
	private String getCodeBlock( AST parent )
	{
		return getASTByType(parent, BLOCK).getText();
	}

    /**
     *  @return the first child of the parent AST with the specified node type.
     */
    private AST getASTByType(AST parent, int node_type)
    {
        for ( AST current = parent.getFirstChild(); current != null; current = current.getNextSibling() )
        {
            if ( current.getType() == node_type )
                return current;
        }

        throw new IllegalStateException ( "AST " + parent.toString() + " has no child of node type " + node_type + "." );
    }

    private boolean hasASTOfType(AST parent, int node_type)
    {
        for ( AST current = parent.getFirstChild(); current != null; current = current.getNextSibling() )
        {
            if ( current.getType() == node_type )
                return true;
        }

        return false;
    }

    /**
     *  A JBurgProduction is a view of a pattern-matching rule (JBurgRule) 
     *  or a nonterminal transformation rule (ClosureRecord) that exposes
     *  characteristics important in their static analysis.
     */
    public interface JBurgProduction
    {
        /**
         *  @return the nonterminal this production produces.
         */
        public String getGoalState();

        /**
         *  @return the code snippet that computes
         *    or retrieves this production's 
         *    (potentially) cached code.
         */
        public String getCachedCost();

        /**
         *  @return this production's reduce action.
         */
        public JBurgReduceAction getReduceAction();

        /**
         *  Is this production's cost constant in the
         *  context where it's to be invoked?
         *  @param productions - the set of productions at the
         *    point where this productions's cost is required.
         */
        public boolean computesConstantCost(Multimap<String, JBurgProduction> productions);

        /**
         *  Get the constant cost of a production.
         *  @param productions - the set of productions at the
         *    point where this production's cost is required.
         *  @return the cost, with overflow-safe addition.
         *  @pre computesConstantCost(productions) must be true.
         */
        public int getConstantCost(Multimap<String, JBurgProduction> productions);
    }

    /**
     *  JBurgRule contains an AST that represents a rule, 
     *  its associated JBurgReduceAction,
     *  and accessors to the AST's components.
     */
    public class JBurgRule implements JBurgProduction
    {
        /**
         *  The parsed rule.
         */
        AST m_AST;
        
        /**
         *  If the rule is a pattern rule, its pattern matcher.
         */
        JBurgPatternMatcher patternMatcher = null;
        
        /**
         * The rule's reduction action.
         */
        JBurgReduceAction reduceAction;

        /**
         *   The pattern as a String; precomputed so it can be used as a key
         *   to group labeling choices by common patterns.
         */
        private String encodedPattern;

        public JBurgRule(AST n)
        {
            m_AST = n;
            
            if ( m_AST.getType() == PATTERN_RULE )
            {
                AST pattern_root = m_AST.getFirstChild().getNextSibling().getFirstChild();
                try
                {
                    this.patternMatcher = generateMatcher(pattern_root); 
                }
                catch ( Exception ex )
                {
                    logger.exception("Building pattern recognizer for " + m_AST.toStringTree(), ex);
                }
            }
        }

        String getEncodedPattern()
        {
            if ( this.encodedPattern == null )
            {
                this.encodedPattern = 
                    patternMatcher.generatePatternRecognizer(
                        codeEmitter,
                        "node",
                        JBurgGenerator.this.adapter2
                    );
            }
            
            if ( this.encodedPattern == null )
                this.encodedPattern = nilPattern;

            return this.encodedPattern;
        }

        /**
         * @param baseNode - the path to the base of the subtree.
         * @return the subtree's cost specification.
         */
        public String getCost(String baseNode)
        {
            if ( m_AST.getType() != SIMPLE_TRANSFORMATION_RULE )
            {
                AST cost_spec = m_AST.getFirstChild().getNextSibling()
                        .getNextSibling();

                String costText = cost_spec.getFirstChild().getText();

                if ( hasCostFunction() )
                    return genCallMethod(null, costText, baseNode);
                else
                    return costText;
            }
            else
            {
                // Simple transformation rules don't cost anything.
                return "0";
            }
        }
        
        /** 
         *  @return true if the rule has a constant cost.
         */
        public boolean hasConstantCost()
        {
            if ( m_AST.getType() != SIMPLE_TRANSFORMATION_RULE )
            {
                AST cost_spec = m_AST.getFirstChild().getNextSibling()
                        .getNextSibling();

                String costText = cost_spec.getFirstChild().getText();

                boolean ownCostConstant =
                    cost_spec.getType() == LITERAL_COST_SPEC ||
                    JBurgGenerator.this.manifestConstants.containsKey(costText);
                
                return ownCostConstant &&
                     (this.isTerminalPattern() || this.patternMatcher == null);
            }
            else
            {
                // Simple transformation rules all cost 0.
                return true;
            }
        }
        
        /**
         *  @return the rule's cost.
         *  @throws IllegalStateException if the cost isn't constant.
         */
        public Integer getConstantCost()
        {
            if ( m_AST.getType() != SIMPLE_TRANSFORMATION_RULE )
            {
                AST cost_spec = m_AST.getFirstChild().getNextSibling()
                        .getNextSibling();

                String costText = cost_spec.getFirstChild().getText();

                if ( cost_spec.getType() == LITERAL_COST_SPEC )
                {
                    return new Integer(costText);
                }
                else if ( JBurgGenerator.this.manifestConstants.containsKey(costText) )
                {
                    return JBurgGenerator.this.manifestConstants.get(costText);
                }
                else
                {
                    throw new IllegalStateException("non constant cost: " + costText);
                }
            }
            else
            {
                // Simple transformation rules don't cost anything.
                return 0;
            }
        }

        /**
         *  @return true if the cost specification is a function call.
         */
        public boolean hasCostFunction()
        {
            boolean result = false;

            if ( m_AST.getType() != SIMPLE_TRANSFORMATION_RULE )
                result = m_AST.getFirstChild().getNextSibling().getNextSibling().getType() == FUNCTION_CALL;

            return result;
        }

        /**
         *  @return the rule's cached cost.
         */
        @Override
        public String getCachedCost()
        {
            if ( this.hasCostFunction() )
                return String.format("cachedCost_%h()", getCost(reducerNodeName));
            else
                return getCost(reducerNodeName);

        }

        /**
         *  @return the rule's reduction action.
         */
        @Override
        public JBurgReduceAction getReduceAction()
        {
			if ( null == this.reduceAction )
			{
				throw new IllegalStateException( "getReduceAction() has no action to return." );
			}

            return this.reduceAction;
        }

        /**
         *  @return the antecedent state of a nonterminal-to-nonterminal
         *    reduction, i.e., the state that the subtree must be reduced
         *    to before this reduction can run.
         */
        public String getAntecedentState()
        {
             if ( m_AST.getType() == SIMPLE_TRANSFORMATION_RULE )
             {
                 return m_AST.getFirstChild().getNextSibling().getText();
             }
             else if ( m_AST.getType() == TRANSFORMATION_RULE )
             {
                 return m_AST.getFirstChild().getNextSibling().getText();
             }
             else
                 throw new IllegalStateException(String.format("no antecedent for %s", m_AST.getType()));
        }

        /**
         *  @return the node ID of the node at the root of the subtree.
         */
        public String getOperator()
        {
			int type = m_AST.getType();

			if ( PATTERN_RULE == type )
			{
				return m_AST.getFirstChild().getNextSibling().getFirstChild().getFirstChild().getText();
			}
			else
			{
			    //  A transformation rule.
				return m_AST.getFirstChild().getNextSibling().getText();
			}
        }

        /**
         *  @return the nonterminal produced by this reduction.
         */
        @Override
        public String getGoalState()
        {
            return m_AST.getFirstChild().getText();
        }

        /**
         *  Set this rule's associated action code.
         */
        public void setReduceAction(JBurgReduceAction reduceAction)
        {
            this.reduceAction = reduceAction;
        }

        /**
         *  @return true if this node has no children (a "leaf" node).
         */
        public boolean isTerminalPattern()
        {
            return this.isFixedArity() && this.patternMatcher.getNominalArity() == 0;
        }

        /**
         *  @return true if this rule's pattern has a fixed number of "operand" subtrees.
         */
        public boolean isFixedArity()
        {
            return this.patternMatcher != null && !this.patternMatcher.hasNaryTail();
        }

        /**
         *  @return true if this rule needs an out-of-line method to check its cost.
         */
        public boolean needsCostFunction()
        {
            if ( this.isTerminalPattern() )
                return false;
            else if ( this.patternMatcher.hasNaryTail() )
                return  this.patternMatcher.getNominalArity() != this.patternMatcher.getMinimumNaryChildCount();
            else
                return true;
        }

        /**
         *  Is this production's cost constant in the
         *  context where it's to be invoked?
         *  @param productions - the set of productions at the
         *    point where this productions's cost is required.
         */
        @Override
        public boolean computesConstantCost(Multimap<String, JBurgProduction> productions)
        {
            return hasConstantCost();
        }

        /**
         *  Get the constant cost of a production.
         *  @param productions - the set of productions at the
         *    point where this production's cost is required.
         *  @return the cost, with overflow-safe addition.
         *  @pre computesConstantCost(productions) must be true.
         */
        @Override
        public int getConstantCost(Multimap<String, JBurgProduction> productions)
        {
            return getConstantCost();
        }

        @Override
        public String toString()
        {
            return m_AST.toStringTree();
        }
    }

    /**
     *  JBurgReduceAction holds action code fragments and their associated "parameters."
     *  A rule specifies "parameters" by naming individual subgoal states within a pattern
     *  rule specifiction, for example,
     *  <xmp>integer = PLUS ( integer i1, integer i2 )</xmp>
     *  In this example, i1 and i2 are the action routine's "parameters."  Since the action
     *  routines all share a common signature, the parameters are passed via the reducer's
     *  reduced values stack.
     */
    private class JBurgReduceAction
    {
        /** The routine's source, from the input specification. */
        private String actionCode;
        
        /**
         * The non-terminal state produced by this action, e.g., expression from
         * <xmp>expression = ADD(expression l, expression r)</xmp>
         */
        private String m_state;

        /**
         *  The antecedent reduction of a nonterminal-to-nonterminal rule.
         */
        private String antecedentState;
        
        /**
         *  The operator ID of a pattern rule; used to find content-handling
         *  code from the InodeAdapter2.
         */
        private String m_operator;
        
        /**
         * Names and types of the routine's parameters.
         * Types are given as their non-terminal state names; toString()
         * translates these BURM-centric types into the corresponding
         * types in the target language using the mapping set up by
         * the ReturnType directives in the input specification.
         */
        private Vector<ParameterDescriptor> m_parameterList = new Vector<ParameterDescriptor>();

        /**
         *  This action routine's index, assigned in entry order.
         *  The action routines are enumerated as action_1(JBurgNode p), action_2(JBurgNode p),
         *  etc. in the generated reducer.
         */
        int index;
        
        /**
         * Track name-to-subtree mappings to be emitted.
         */
        class NamedSubtree
        {
            String path;
            String name;
            
            NamedSubtree(String path, String name)
            {
                this.path = path;
                this.name = name;
            }
        }
        /**
         *  Saved name-to-subtree mappings.
         */
        Vector<NamedSubtree> namedChildNodes = new Vector<NamedSubtree>();
        
        /**
         * Construct a reduce action.
         * @param strActionCode - the action's implementation
         * code, in a target-specific language 
         */
        public JBurgReduceAction(String strActionCode)
        {
            this.actionCode = strActionCode;
        }

        /**
         * Add a parameter to the action's parameter list.
         */
        public void addParameter(String parmName, String parmState, ParameterDescriptor.ArityType arityType)
            throws Exception
        {
            m_parameterList.add(new ParameterDescriptor(parmName, parmState, arityType));
        }

        /**
         *  Return this action's index (the N constituent of its generated name, action_N).
         */
        public int getIndex()
        {
            return this.index;
        }

        /**
         *  Set this action's index (the N constituent of its generated name, action_N).
         */
        public void setIndex(int index)
        {
            this.index = index;
        }

        /**
         * Set the non-terminal state this reduction derives.
         * @param state - the non-terminal state, e.g., expression from
         * <xmp>expression = ADD(expression l, expression r)</xmp>
         */
        public void setState(String state)
        {
            this.m_state = state;
        }

        /**
         * @return the non-terminal state this reduction derives.
         */
        public String getState()
        {
            return this.m_state;
        }

        /**
         *  @param operator the operator at the root of 
         *    the matched subtree.
         */
        public void setOperator(String operator)
        {
            this.m_operator = operator;
        }

        /**
         * @return the operator at the root of 
         *    the matched subtree.
         */
        public String getOperator()
        {
            return m_operator;
        }

        /**
         *  Return the reduction action's code, 
         *  with prepended logic to pop its parameters off the stack.
         */
        @Override
        public String toString()
        {
            String result = "";

            for ( ParameterDescriptor next_param: m_parameterList )
            {
                String paramName = next_param.paramName;
                String paramType = getReturnType(next_param.paramState);

				if ( next_param.arityType == ParameterDescriptor.ArityType.Variable )
				{
					paramType = codeEmitter.genNaryContainerType(paramType);
				}
                
                result += codeEmitter.genActionRoutineParameter(reducedValuesName, paramType, paramName);
            }
            
            for ( NamedSubtree named_child: this.namedChildNodes )
            {
                result += "\n\t" + iNodeClass + " " + named_child.name + " = " + named_child.path + ";";
            }

            // insert 2 tabs into each line, and convert \r\n to \n so the generated
			// files have consistent line endings.
			String[] actionlines = this.actionCode.toString().split("\n");
			for( int l = 0; l < actionlines.length; ++l )
			{
				if(actionlines[l].endsWith("\r"))
				{
					actionlines[l] = actionlines[l].substring(0,actionlines[l].length()-1);
				}
				result += "\n\t\t" + actionlines[l];
			}

			return result;
        }

        /**
         *  Track a name-to-subtree mapping.  These are unreduced I-nodes,
         *  typically a terminal identified by pattern match.
         *  @param path - the path from the root node to the subtree.
         *  @param name - the name to give to the subtree.
         */
        public void addNamedSubtree(String path, String name)
        {
            namedChildNodes.add(new NamedSubtree(path, name));
        }

        /**
         *  Set this reduction's antecedent state, which
         *  determines what reduction needs to run before
         *  this one to transform one nonterminal to another.
         */
        public void setAntecedentState(String antecedentState)
        {
            this.antecedentState = antecedentState;
        }

        /**
         *  @return true if this rule has an antecedent state,
         *    which also means it's a nonterminal-to-nonterminal rule.
         */
        public boolean hasAntecedentState()
        {
            return getAntecedentState() != null;
        }

        /**
         *  @return this rule's antecedent state,
         *    or null if no antecedent is present.
         */
        public String getAntecedentState()
        {
            return this.antecedentState;
        }
    }

    /**
     *  ClosureRecord tracks the target state, cost, and action code associated
     *  with a reduction from an antecedent nonterminal to a target nonterminal state.
     */
    private class ClosureRecord implements JBurgProduction
    {
        private JBurgRule rule;

        public ClosureRecord(JBurgRule rule) throws Exception
        {
            this.rule = rule;
        }

        /**
         *  Get the closure's uncached cost.
         *  @deprecated Will be removed when rules start caching costs.
         */
        public String getCost(String baseNT)
        {
            return this.rule.getCost(baseNT);
        }

        /**
         *  Get the closure's (potentially) cached cost.
         */
        @Override
        public String getCachedCost()
        {
            String cost = this.rule.getCost(reducerNodeName);

            //  The cost function result will be cached;
            //  return a unique accessor method name.
            if ( hasCostFunction() )
                return genCallMethod(null, String.format("getCostFunctionResult_%h", cost));
            else
                return cost;
        }

        /**
         *  @return the rule's hasCostFunction() result.
         */
        public boolean hasCostFunction()
        {
            return rule.hasCostFunction();
        }

        /**
         *  @return the rule's getConstantCost() result.
         */
        public int getConstantCost()
        {
            return this.rule.getConstantCost();
        }

        /**
         *  @return the rule's hasConstantCost() result.
         */
        public boolean hasConstantCost()
        {
            return this.rule.hasConstantCost();
        }

        /**
         *  @return the rule's getAntecedentState() result.
         */
        public String getAntecedentState()
        {
            return this.rule.getAntecedentState();
        }

        /**
         *  @return the rule's getGoalState() result.
         */
        @Override
        public String getGoalState()
        {
            return this.rule.getGoalState();
        }

        /**
         *  @return this closure's reduce action.
         */
        @Override
        public JBurgReduceAction getReduceAction()
        {
            return rule.getReduceAction();
        }

        /**
         *  Closure records are equal if they have the same goal state, action, and cost.
         *  @see java.lang.Object#equals
         *
         * @param o -- the object to test for equality.
         * @return true if o is a ClosureRecord and it equals this one.
         *
         */
        @Override
        public boolean equals(Object o)
        {
            boolean result = false;

            if (o instanceof ClosureRecord)
            {
                ClosureRecord cuz = (ClosureRecord) o;

                result = getGoalState().equals(cuz.getGoalState());
                result &= getReduceAction().equals(cuz.getReduceAction());
                result &= this.rule.getCost(reducerNodeName).equals(cuz.rule.getCost(reducerNodeName));
            }

            return result;
        }

        /**
         *  @return true if the cost of this closure is known to be zero.
         */
        public boolean costIsZero()
        {
            return hasConstantCost() && getConstantCost() == 0;
        }

        /**
         *  @return the computation of this closure's cost,
         *    which is somewhat error-prone when open coded.
         */
        public String getCostComputation()
        {
            String antecedentCost = 
                genCallMethod(
                    null,
                    "getCost",
                    getNonterminal(this.getAntecedentState())
                );

            if ( this.costIsZero() )
            {
                return antecedentCost;
            }
            else
                return codeEmitter.genOverflowSafeAdd(this.getCachedCost(), antecedentCost);
        }

        /**
         *  Precompute the cost of this closure if possible.
         *  @param productions - the set of productions at the
         *    point where the closure's cost is required.  If
         *    the closure derives a single pattern-match 
         *    rule that has a constant cost, and the closure
         *    itself has a constant cost, then the cost
         *    can be precomputed.
         *  @return the precomputed cost, or the result of
         *    calling {@link getCostComputation()} to get
         *    a non-constant cost.
         */
        public String getCostComputation(Multimap<String, JBurgProduction> productions)
        {
            //  A closure back to a pattern-match with constant cost can be precomputed.
            if ( this.computesConstantCost(productions) )
            {
                return Integer.toString(getConstantCost(productions));
            }
            else
            {
                //  Return the naive cost computation.
                return getCostComputation();
            }
        }

        /**
         *  Get the constant cost of a closure.
         *  @param productions - the set of productions at the
         *    point where the closure's cost is required.
         *  @return the cost, with overflow-safe addition.
         *  @pre computesConstantCost(productions) must be true.
         */
        @Override
        public int getConstantCost(Multimap<String, JBurgProduction> productions)
        {
            long constantAntecedent;
            JBurgProduction production = productions.get(this.getAntecedentState()).get(0);

            if ( production instanceof JBurgRule )
                constantAntecedent = ((JBurgRule)production).getConstantCost();
            else
                constantAntecedent = ((ClosureRecord)production).getConstantCost(productions);

            //  Integer-overflow safe addition.
            @SuppressWarnings("cast")
            long accum = (long)this.getConstantCost() + constantAntecedent;

            if ( accum < Integer.MAX_VALUE )
                return (int) accum;
            else
                return Integer.MAX_VALUE;
        }

        /**
         *  Does this closure compute a constant cost
         *  in the context of the given productions?
         *  @param productions - the set of productions at the
         *    point where the closure's cost is required.  If
         *    the closure derives a single pattern-match 
         *    rule that has a constant cost, and the closure
         *    itself has a constant cost, then the cost
         *    can be precomputed.
         *  @return true if the computed cost is constant.
         */
        @Override
        public boolean computesConstantCost(Multimap<String, JBurgProduction> productions)
        {
            boolean result = false;
            if ( this.hasConstantCost() )
            {
                ArrayList<JBurgProduction> antecedents = productions.get(this.getAntecedentState());

                if ( antecedents.size() == 1 )
                {
                    JBurgProduction production = antecedents.get(0);

                    if ( production instanceof JBurgRule )
                        result = ((JBurgRule)production).hasConstantCost();
                    else
                        result = ((ClosureRecord)production).computesConstantCost(productions);
                }
            }

            return result;
        }

    }

	/**
	 *  A ParameterDescriptor is a pair (name, subgoalState) which parallels
	 *  the FOO(name, subgoalState) found in the specification's syntax.
	 */
    private static class ParameterDescriptor
    {
        String paramName;
        String paramState;
        ArityType arityType;
        
        enum ArityType {Fixed, Variable}

        ParameterDescriptor(String paramName, String paramState, ArityType arityType)
        {
            this.paramName = paramName;
            this.paramState = paramState;
            this.arityType = arityType;
        }
    }

    /**
     *  Finish semantic analysis of the pattern rules.
     */
	private void ruleSemanticAnalysis() throws Exception
    {
        for (Vector<JBurgRule> vPatternRules: this.patternRules.values())
        {
            for (JBurgRule rule: vPatternRules)
            {
                for (JBurgPatternMatcher subgoal: rule.patternMatcher.getParameterizedSubtrees())
                {
                    rule.getReduceAction().addParameter(
                            subgoal.getParameterName(), 
                            subgoal.getSubgoal(), 
                            subgoal.isNary()? ParameterDescriptor.ArityType.Variable: ParameterDescriptor.ArityType.Fixed
                            );
                }

                //  Add the named subtrees as a convenience.
                for ( JBurgPatternMatcher named_terminal: rule.patternMatcher.getNamedSubtrees())
                {
                    rule.getReduceAction().addNamedSubtree(
                            named_terminal.generateReduceTimePath(codeEmitter, reducerNodeName, this.iNodeAdapter, this.adapter2 != null), 
                            named_terminal.getParameterName()
                            );
                }

                //  If the pattern matches an operator, send that operator
                //  to the rule so it can be matched in content-access snippets.
                if ( rule.patternMatcher.matchesOperator() )
                    rule.getReduceAction().setOperator(rule.patternMatcher.getOperator());
            }
        }
    }

	private void emitComputeCostMatrixFunction(String iNodeClass, PrintStream output) throws Exception 
	{
		//  Emit the method declaration and local variables.
		genDeclareMethod( output, Modifier.PRIVATE, "void", "computeCostMatrix", "JBurgAnnotation", "node");
		genBeginBlock(output);
		genLocalVar(output,  "long", "iCost", null );

		//  The calculations are based on finding patterns
		//  that can label the node's type.
		genSwitch(output, genCallMethod( "node", "getOperator" ));

		//  Try each pattern for a match.
		//  Any pattern that matches then checks its 
		//  cost to see if it's less than the current
		//  best cost for the node, and replaces the 
		//  node's current rule and cost with its own
		//  if it's the new best cost.

		for (Vector<JBurgRule> vPatternRules: this.patternRules.values())
		{
            genCase( output, vPatternRules.firstElement().getOperator() );
            
            //  Group rules by the patterns they match.
            Multimap<String, JBurgRule> rules_by_pattern = new Multimap<String, JBurgRule>();

            for (JBurgRule rule: vPatternRules)
            {
                String encoded_pattern = rule.getEncodedPattern();
                rules_by_pattern.addToSet(encoded_pattern, rule);
            }

            
            for ( String encoded_pattern: rules_by_pattern.keySet() )
            {
                ArrayList<JBurgRule> current_rules = rules_by_pattern.get(encoded_pattern);

                //  Emit the structural pattern match if it was not optimized out.
                if ( !encoded_pattern.equals(nilPattern))
                    genIf(output, encoded_pattern);
                
                //  Begin a block whether there was a test or not, to scope variables.
                //  TODO: This will not work for a target that doesn't block-scope locals.
                genBeginBlock(output);
                
                //  Find common path and cost subexpressions.
                Multimap<JBurgPatternMatcher, JBurgPatternMatcher> aggregated_matchers = new Multimap<JBurgPatternMatcher, JBurgPatternMatcher>();
                
                Multimap<String, JBurgPatternMatcher> costs = new Multimap<String, JBurgPatternMatcher>();
                
                for ( JBurgRule rule: rules_by_pattern.get(encoded_pattern))
                {
                    for (JBurgPatternMatcher subgoal: rule.patternMatcher.getParameterizedSubtrees() )
                    {
                        subgoal.aggregatePaths(aggregated_matchers);
                        costs.addToSet(subgoal.generateCost(codeEmitter, "node"), subgoal);
                    }
                }
                
                //  Factor out common paths.
                int factored_path_count = 0;
                for ( JBurgPatternMatcher key: aggregated_matchers.keySet())
                {
                    ArrayList<JBurgPatternMatcher> matchers = aggregated_matchers.get(key);
                    
                    if ( matchers.size() > aggregatePathThreshold  )
                    {
                        String factored_path_var = "factored_path_" + Integer.toString(++factored_path_count);
                        genLocalVar(output, "JBurgAnnotation ", factored_path_var, key.generatePathToRoot(codeEmitter, "node"));
                        
                        for ( JBurgPatternMatcher matcher: matchers)
                            matcher.factoredPath = factored_path_var;
                        
                    }
                }
                
                //  Now that the paths have been factored, factor out common costs.
                int factored_cost_count = 0;
                
                for (String key: costs.keySet() )
                {
                    if ( costs.get(key).size() > 1 )
                    {
                        String factored_cost_var = "factored_cost" + Integer.toString(++factored_cost_count);
                        //  Regenerate the cost because it may now use factored path expressions.
                        ArrayList<JBurgPatternMatcher> matchers = costs.get(key);
                        genLocalVar(output, "int", factored_cost_var, matchers.get(0).generateCost(codeEmitter, "node"));
                        for ( JBurgPatternMatcher subgoal: matchers)
                        {
                            subgoal.factoredCost = factored_cost_var;
                        }
                    }
                }

                //  Emit the cost checks and their labeling logic.
                
                int patternPosition = 0;

                for ( JBurgRule rule: current_rules )
                    emitPatternRule(rule, iNodeClass, output, patternPosition++ == 0);

                genEndBlock(output);
            }

            genEndCase(output);
		}

		genEndSwitch(output);
		genEndBlock(output);
	}
	
	/**
	 *  Emit a routine that inspects the operators found at compiler compile time,
	 *  and emits code to get the corresponding node's Nth child.
	 *  @param output - the current output stream.
	 */
	private void emitGetNthChild(PrintStream output)
	{
	    genDeclareMethod(
            output,
            Modifier.PRIVATE,
            this.iNodeClass,
            "getNthChild",
	        genFormals(this.iNodeClass, "node", "int", "index"),
            throwsNothing
        );
	    genBeginBlock(output);
	    genLocalVar(output, this.iNodeClass, "result", null);
	    
	    genSwitch(output, this.iNodeAdapter.genGetOperator("node", this.codeEmitter));
	    for ( String opcode: this.allOperators)
	    {
	        Integer choice_count = this.adapter2.getMaxNthChildChoice(opcode);
	        if ( choice_count != null )
	        {
                genCase(output, opcode);
	            genSwitch(output, "index");
	            for ( int i = 0; i < choice_count; i++ )
	            {
	                genCase(output, Integer.toString(i));
	                genAssignment(output, "result", this.adapter2.genGetNthChild(opcode, "node", i, codeEmitter));
	                genEndCase(output);
	                
	            }
                //  generate the default case for the index.
                {
                    genDefaultCase(output);
                    genAssignment(output, "result", this.adapter2.genGetDefaultChild(opcode, "node", "index", codeEmitter));
                    genEndCase(output);
                }

                genEndSwitch(output);
	            genEndCase(output);
	        }
	        
	    }
        //  generate the default case for the opcode.
        {
            genDefaultCase(output);
            genAssignment(output, "result", this.iNodeAdapter.genGetNthChild("node", "index", codeEmitter));
            genEndCase(output);
        }
	    genEndSwitch(output);
	    genReturnValue(output, "result");
	    genEndBlock(output);
	}

	private void emitPatternRule(JBurgRule p, String iNodeClass, PrintStream output, boolean isFirstRule ) throws Exception
	{
	    genComment(output, "Try matching " + p.getOperator() + " ==> " + p.getGoalState());
	    
        /*
		 *   Emit code to compute this rule's cost, which is the cost of the rule itself 
		 *   and all the non-terminals associated with it; then emit a comparison with
		 *   the current best cost.
		 */
		Vector<String> sub_costs = new Vector<String>();

		//  Compute the basic cost of the pattern match.
		sub_costs.add(
		    p.getCost( 
    			codeEmitter.genCast( 
    				iNodeClass, 
    				codeEmitter.genAccessMember("node", "m_node")
    			)
            )
		);
		
	   //  Try and compute a constant cost.
        Integer constant_cost = null;

		if ( p.hasConstantCost() )
		{
		    constant_cost = p.getConstantCost();
		}

		
		for ( JBurgPatternMatcher subgoal: p.patternMatcher.getParameterizedSubtrees() )
		{
		    String subgoal_cost = subgoal.generateCost(codeEmitter, "node");
		    if ( subgoal_cost != null )
		    {
		        sub_costs.add(subgoal_cost);
		        constant_cost = null;
		    }
		}
		
		String cost_factor;
        boolean checkCostFactor = true;
		
		if ( constant_cost != null )
		{
		    cost_factor = constant_cost.toString();
            checkCostFactor = constant_cost == Integer.MAX_VALUE;
		}
		else if ( sub_costs.size() == 1 )
		{
		    cost_factor = sub_costs.get(0);
		}
		else
		{
            //  Compute the cost using a long accumulator.
		    cost_factor = "iCost";

    		String match_cost = codeEmitter.genCast("long", sub_costs.get(0));
        		
            for ( int i = 1; i < sub_costs.size(); i++ )
                match_cost = codeEmitter.genAddition(match_cost, codeEmitter.genCast("long", sub_costs.get(i)));
    		
    		genAssignment(output, "iCost", match_cost);
		}

        if ( !isFirstRule || checkCostFactor )
        {
            //  Does this pattern match cost less than the previously best match?
            String getCostCall = genCallMethod ( "node", "getCost", codeEmitter.genGetGoalState(p) );
            String costCheck = codeEmitter.genCmpLess( getCostCall, cost_factor );
            genIf(output, costCheck );
        }

		output.print( codeEmitter.genBeginBlock() );

		/*
		 *   Emit code to handle a successful match.
		 */

		//  Reset the reduce-time rule to fire.
        String strRule = String.valueOf(p.getReduceAction().getIndex());
        
        //  Now that the comparison has been done using long arithmetic,
        //  iCost can be safely truncated to fit in an int.
        if ( cost_factor.equals("iCost"))
            cost_factor = codeEmitter.genCast("int", cost_factor);

        genExpressionStmt(
            output, 
			codeEmitter.genCallMethod(
				"node", 
				"reset", 
				new String[] { codeEmitter.genGetGoalState(p), cost_factor, strRule }
			)
		);

        if (hasClosure(p))
		{
            genExpressionStmt(
                output,
				genCallMethod( 
					null, 
					"closure_" + p.getGoalState(),
					"node", cost_factor
				)
			);
        }

        //  End the block begun by the cost comparison above.
        genEndBlock(output);
    }
	
	private JBurgPatternMatcher generateMatcher(AST pattern_root)
	throws Exception
	{   
        JBurgPatternEncoder patternBURM = new JBurgPatternEncoder();

        //  As we traverse the subtree, we may find parameterized subtrees,
        //  for example, in ADD(expr lhs, expr rhs) lhs and rhs are paramterized
        //  subtrees.  These subtrees play several parts in the computation of the
        //  locally-optimal reduction:
        //  -  They contribute to the rule's computed cost.
        //  -  The reduction's action code may refer to these elements by name.
        //  -  If the rule is of the form OP(nttype1 a [, nttype2 b]), then the rule
        //     must enforce this reduce-time goal state on its subtrees' reductions.
        patternBURM.setSubgoals(new Vector<JBurgPatternMatcher>());
        
        //  There may also be named terminals in the pattern;
        //  as a convenience, record these so that they
        //  can be used by name in the reduction.
        patternBURM.setNamedterminals(new Vector<JBurgPatternMatcher>());
        
        patternBURM.setOperators(this.allOperators);

        try
        {
            patternBURM.burm( pattern_root );
        }
        catch (IllegalStateException burm_error )
        {
            if ( this.patternMatcherDumpFile != null )
            {
                //  Dump the BURM's debugging info.
                java.io.PrintWriter dumper = new java.io.PrintWriter(new java.io.FileWriter(patternMatcherDumpFile));
                dumper.println ( "<?xml version=\"1.0\"?>");
                dumper.println("<BurmDump date=\"" + new java.util.Date().toString() + "\">");
                patternBURM.dump(dumper);
                dumper.println("<AST>");
                dumper.println("<![CDATA[");
                dumper.println(pattern_root.toStringTree());
                dumper.println("]]>");
                dumper.println("</AST>");
                dumper.println("</BurmDump>");
                dumper.flush();
                dumper.close();
            }
            
            throw burm_error;
        }
        
        JBurgPatternMatcher recognizer = (JBurgPatternMatcher) patternBURM.getResult();
        recognizer.setParameterizedSubtrees(patternBURM.getSubgoals());
        recognizer.setNamedSubtrees(patternBURM.getNamedterminals());
        
        return recognizer;
	}

	private void emitActions(ArrayList<JBurgReduceAction> reduceActions, String iNodeClass, PrintStream output)
	{
		int i = 1;

		//  Print out the individual action routines.
		for (JBurgReduceAction nextAction: reduceActions )
		{
            output.println();
			genComment(output, nextAction.getState());
			
            //  Compute the type of the i-node.
            String typeOfOperatorINode;
			final String actionOperator = nextAction.getOperator();

			if ( opcodeNodeTypes.containsKey(actionOperator) )
			    typeOfOperatorINode = opcodeNodeTypes.get(actionOperator);
			else
			    typeOfOperatorINode = iNodeClass;

			genDeclareMethod(
                output,
				Modifier.PRIVATE,
				getReturnType(nextAction.getState()),
				"action_" + String.valueOf(i++),
				genFormals(typeOfOperatorINode, reducerNodeName),
				throwsException
			);
				
			genBeginBlock(output);

			String action_routine = nextAction.toString();
			
			//  Replace #OPERATOR# with a content-access snippet.
			if ( this.adapter2 != null && nextAction.getOperator() != null )
			{
			    String content_access = this.adapter2.genGetContent(nextAction.getOperator(), this.reducerNodeName, nextAction.getState(), codeEmitter);
			    
			    if ( content_access != null )
			    {
			        String content_pattern = "#" + nextAction.getOperator() + "#";
			        action_routine = action_routine.replaceAll( content_pattern, content_access );
			    }
			}
			//  Replace #goalstate with the name of the action's input node.
			
			String goalPattern = "#" + nextAction.getState();
			action_routine = action_routine.replaceAll( goalPattern, this.reducerNodeName );
			
			output.print( action_routine );

			genEndBlock(output);
		}

		//  Emit their common dispatch routine.
		genDeclareMethod(
            output,
			Modifier.PRIVATE,
			"void",
			"dispatchAction",
			genFormals("JBurgAnnotation", "___node", "int", "iRule"),
			throwsException
		);

		genBeginBlock(output);

        genLocalVar(
            output,
            iNodeClass,
            this.reducerNodeName,
            genCallMethod("___node", "getNode")
        );

		genSwitch(output, "iRule" );

		//  Emit the dispatch case statement.
		for (i = 1; i <= reduceActions.size(); i++)
		{
            JBurgReduceAction action = reduceActions.get(i-1);

			genCase(output, String.valueOf(i));
            {
                //  If this is a nonterminal-to-nonterminal
                //  transformation, run the antecedent
                //  reduction action.
                if ( action.hasAntecedentState() )
                {
                    genExpressionStmt(
                        output,
                        genCallMethod(
                            "this",
                            "reduceAntecedent",
                            "___node", codeEmitter.genGetGoalState(action.getAntecedentState())
                        )
                    );
                }

                final String operatorName = action.getOperator();
                final String nodeTypeForOperator =
                    operatorName != null ? opcodeNodeTypes.get(operatorName) : null;
                String nodeParameterString = reducerNodeName;
                if (nodeTypeForOperator != null)
                    nodeParameterString = codeEmitter.genCast(nodeTypeForOperator, nodeParameterString);
                
                genExpressionStmt(
                    output,
                    codeEmitter.genPushToStack(
                        reducedValuesName,
                        genCallMethod(
                            "this",
                            "action_" + String.valueOf(i),
                            nodeParameterString
                        )
                    )
                );
            }
			genEndCase(output);
		}

		genDefaultCase(output);
        {
            genThrow(output, "\"Unmatched reduce action \" + iRule" );
        }
        genEndBlock(output); // genEndCase() without unreachable break
	    
		genEndSwitch(output);
		genEndBlock(output);
	}

	private void emitNTConstants(Set<String> goal_states, PrintStream output)
	{
		Iterator<String> keys = goal_states.iterator();

		int nthConstant = 0;

		output.print(codeEmitter.genBeginLine());
		while (keys.hasNext())
		{
			nthConstant++;

			String strNTName = codeEmitter.genGetGoalState(keys.next().toString() );

            genInstanceField(
                output, 
                Modifier.PUBLIC + Modifier.STATIC + Modifier.FINAL,
                "int",
                strNTName,
                String.valueOf(nthConstant)
			);
		}
	
        genInstanceField(
            output,
            Modifier.PUBLIC + Modifier.STATIC + Modifier.FINAL,
            "int",
            "nStates",
            String.valueOf(nthConstant)
		);
	}

	public void emitCostFunctions(ArrayList<AST> costFunctions, String iNodeClass, PrintStream output) 
	{
		String[][] costParms = genFormals( iNodeClass, reducerNodeName );

		for (int i = 0; i < costFunctions.size(); i++)
		{
			AST currentNode = costFunctions.get(i);

			String functionName = currentNode.getFirstChild().getText();

			genDeclareMethod(output, Modifier.PRIVATE, "int", functionName, costParms, throwsNothing );
			output.print( codeEmitter.genBeginLine() );
			output.print( getCodeBlock(currentNode) );
		}
	}

	public void emitClosures(Map<String, Vector<ClosureRecord>> closureSets, String iNodeClass, PrintStream output)
	{
        //  TODO: This method can be removed once the C++ target
        //  has been updated to 1.9.0 style semantics.
		for (String strClosureNT: closureSets.keySet())
		{
			genDeclareMethod(
				output,
                Modifier.PRIVATE,
                "void",
                "closure_" + strClosureNT,
                genFormals("JBurgAnnotation", reducerNodeName, "long", "c"),
                throwsNothing
			);

			genBeginBlock(output);

			genLocalVar(output, "long", "iCost", null );

			for (ClosureRecord newClosure: closureSets.get(strClosureNT) )
			{
				String strRule;

				if (newClosure.getReduceAction() != null)
					strRule = String.valueOf(newClosure.getReduceAction()
													   .getIndex());
				else
					strRule = "0";

				String closureCost = newClosure.getCost( codeEmitter.genAccessMember(reducerNodeName, "m_node"));

				if (closureCost.equals("0") )
				{
					genAssignment( output, "iCost", "c");
				}
				else
				{
					genAssignment( output, "iCost",  codeEmitter.genAddition( "c", closureCost ));
				}

                genIf(
                    output,
                    codeEmitter.genCmpLess(
                        genCallMethod (
                            reducerNodeName,
                            "getCost", 
                            getNonterminal(newClosure.getGoalState())
                        ),
                        "iCost"
					)
				);
				output.print( codeEmitter.genBeginBlock() );

                genExpressionStmt(
                    output,
					codeEmitter.genCallMethod(
						reducerNodeName,
						"reset",
						new String[] { getNonterminal(newClosure.getGoalState()), codeEmitter.genCast("int","iCost"), strRule }
					)
				);

                genExpressionStmt(
                    output,
					codeEmitter.genCallMethod(
						reducerNodeName,
						"recordAntecedent",
						new String[] { getNonterminal(newClosure.getGoalState() ), getNonterminal(strClosureNT ) }
					)
				);

				if (hasClosure(newClosure))
				{
                    genExpressionStmt(
                        output,
						genCallMethod(
							"this",
							"closure_" + newClosure.getGoalState(),
							reducerNodeName, "iCost"
						)
					);
				}

				genEndBlock(output);
			}

			genEndBlock(output);
		}
	}


	public void emitLabelFunction(String iNodeClass, PrintStream output)
	{
        genDeclareMethod(
            output,
            Modifier.PUBLIC,
            "JBurgAnnotation",
            "label",  
            genFormals(iNodeClass, JBurgGenerator.initalParamName),
            throwsNothing
		);

		genBeginBlock(output);
		genLocalVar (output,  "JBurgAnnotation", "result", codeEmitter.genNullPointer() );
		genLocalVar (output,  "int", "i", null);
		genLocalVar (output,  "int", "arity", null);

        String nodeAlloc = null;

        if ( codeEmitter.supportsSpecializedAnnotations() )
        {
			nodeAlloc = genCallMethod( "this", "getJBurgAnnotation", JBurgGenerator.initalParamName);
        }
        else
        {
			nodeAlloc = codeEmitter.genNewObject( "JBurgAnnotation", new String[] { JBurgGenerator.initalParamName, "nStates + 1" } );
        }
		genAssignment( output, "result", nodeAlloc );

		//  Generate a loop over children that labels them,
		//  and adds their labelled JBurgAnnotation nodes to the current node.
		genAssignment(output, "arity", this.iNodeAdapter.genGetArity(JBurgGenerator.initalParamName, codeEmitter));	
		genAssignment(output, "i", "0");

		genLine(output, codeEmitter.genWhileLoop( codeEmitter.genCmpLess("arity", "i")));
		genBeginBlock(output);
        {
            genExpressionStmt(
                output,
                genCallMethod(
                    "result",
                    "addChild",
                    genCallLabel("i")
                )
            );
            
            genAssignment(output, "i", codeEmitter.genAddition("i", "1") );
        }
		genEndBlock(output); // while

		//  Call the costing function.
        if ( ! this.codeEmitter.supportsSpecializedAnnotations() )
            genExpressionStmt(output, genCallMethod( "this", "computeCostMatrix", "result"));

		genReturnValue(output, "result" );
		genEndBlock(output);
	}

    /**
     *  Emit the subclasses of JBurgAnnotation that encode data for specific pattern matches.
     *  @param output - the destination output stream.
     */
    void emitCompressedAnnotations(PrintStream output)
    {
        //  Emit an annotation object for each equivalence class of rules.
        for ( String operator: this.compressedAnnotations.getOperators() )
            for ( ArrayList<JBurgRule> currentRules : this.compressedAnnotations.getRulesFor(operator) )
                emitAnnotation(output, currentRules);

        //  Emit getJBurgAnnotation
        genDeclareMethod(
            output,
            Modifier.PUBLIC,
            "JBurgAnnotation",
            "getJBurgAnnotation",  
            genFormals(iNodeClass, "node"),
            throwsNothing
        );

        genBeginBlock(output);

	    genSwitch(output, this.iNodeAdapter.genGetOperator("node", this.codeEmitter));

        for ( String operator: compressedAnnotations.getOperators() )
        {
            genCase(output, operator);

            for ( ArrayList<JBurgRule> rules : this.compressedAnnotations.getRulesFor(operator) )
            {
                int arity = getMinumumArity(rules);

                if ( hasNaryness(rules) )
                    genIf(output, String.format("%s >= %d", this.iNodeAdapter.genGetArity("node", codeEmitter), arity));
                else
                    genIf(output, String.format("%s == %d", this.iNodeAdapter.genGetArity("node", codeEmitter), arity));
                indentNextLine();
                genReturnValue(output, String.format("new %s(node)", getSpecializedClassName(rules)));
            }

            genEndCase(output);
        }

        genEndSwitch(output);
        
        genLine(output, "return new JBurgAnnotationGeneral(node, nStates+1);");
        genEndBlock(output); // getJBurgAnnotation
        
    }

    /**
     *  Emit an annotation subclass.
     *  @param output - the output stream.
     *  @param currentRules - the set of rules that are this annotation's
     *    domain.  The rules all have the same operator, but their arity
     *    may differ if some of them are n-ary.
     */
    void emitAnnotation(PrintStream output, ArrayList<JBurgRule> currentRules )
    {
        int nominalArity = getMinumumArity(currentRules);

        // The coalesced graph of closures for all the rules.
        ClosureGraph closureCosts = new ClosureGraph();

        //  Factored common subtrees of each pattern matcher.
        //  TODO: These can be further coalesced by using the right comparator.
        Multimap<JBurgPatternMatcher,JBurgPatternMatcher> commonSubtrees = new Multimap<JBurgPatternMatcher,JBurgPatternMatcher>();

        //  Rules and closures by nonterminal.
        Multimap<String, JBurgProduction> productions = new Multimap<String, JBurgProduction>();

        //  Cached subtree costs.
        Map<String, String> cachedCosts = new HashMap<String, String>();

        //  Are all these rules fixed-arity?
        //  If so, the annotation knows its arity a priori.
        boolean fixedArity = !hasNaryness(currentRules);

        /*
         *  **  Semantic analysis  **
         */
        
        //  Populate the closure graph and factor the pattern matchers.
        for ( JBurgRule rule: currentRules)
        {
            productions.addToSet(rule.getGoalState(), rule);

            closureCosts.addClosures(rule.getGoalState());

            if ( fixedArity )
                rule.patternMatcher.setFixedArityContext(true);

            for ( JBurgPatternMatcher subgoal: rule.patternMatcher.getParameterizedSubtrees() )
                subgoal.findFactors(commonSubtrees);

            if ( rule.patternMatcher.getNominalArity() > 0 )
            {
                cachedCosts.put(rule.getGoalState(), String.format("cachedCostFor_%s", rule.getGoalState()));
            }
        }

        //  Add the closures from the coalesced closure graph
        //  to the set of productions.
        for ( Map.Entry<String, ArrayList<ClosureRecord>> closures: closureCosts.entrySet() )
        {
            for ( ClosureRecord closure: closures.getValue() )
                productions.addToSet(closures.getKey(), closure);
        }

        //  Emitting these variable declarations mutates the 
        //  pattern matchers, so they can only be emitted once.
        //  Capture the results so we can write it more than once.
        Map<JBurgPatternMatcher,String> factored_variables = emitFactoredPathVariables(commonSubtrees);

        /*
         * Begin code-gen of the class.
         */
        output.println();
        String annotationClass = getSpecializedClassName(currentRules);
        
        genLine(output, String.format("class %s extends JBurgSpecializedAnnotation", annotationClass));
        genBeginBlock(output);

        //  Emit a field for each child.
        for ( int fieldIdx = 0; fieldIdx < nominalArity; fieldIdx++ )
        {
            genInstanceField( output, Modifier.PRIVATE, "JBurgAnnotation", String.format("subtree%d", fieldIdx), null);
        }

        if ( !fixedArity )
        {
            genInstanceField( 
                output,
                Modifier.PRIVATE,
                codeEmitter.genNaryContainerType("JBurgAnnotation"),
                "narySubtrees",
                String.format("new %s()", codeEmitter.genNaryContainerType("JBurgAnnotation"))
            );
        }

        // Emit the constructor.
        //  TODO: The emitter needs a genConstructor() API.
        genLine(output, String.format("%s(%s node)", annotationClass, this.iNodeClass));
        genBeginBlock(output);
        //  TODO: The emitter also needs a callSuperclassConstructor() API.
        genLine(output, "super(node);");
        genEndBlock(output);

        for ( String cachedCostVar: cachedCosts.values() )
        {
            genInstanceField( output, Modifier.PRIVATE, "int", cachedCostVar, UNINITIALIZED);
        }

        /*
         *  ** Emit getCost()  **
         */
        genDeclareMethod( output, Modifier.PUBLIC, "int", "getCost", "int", "goalState" );
        genBeginBlock(output);

        genSwitch(output, "goalState");

        for ( Map.Entry<String, ArrayList<JBurgProduction>> productionsByNonterminal: productions.entrySet() )
        {
            genCase(output, getNonterminal(productionsByNonterminal.getKey()));

            ArrayList<JBurgProduction> currentProductions = productionsByNonterminal.getValue();

            //  Try to find the optimal production at compiler-compile time.
            JBurgProduction optimalProduction = findOptimalProduction(currentProductions, productions);

            if ( optimalProduction != null )
            {
                genReturnValue(output, Integer.toString(optimalProduction.getConstantCost(productions)));
            }
            else
            {
                final boolean hasMultipleProductions = currentProductions.size() > 1;
                final boolean cacheCost = cachedCosts.containsKey(productionsByNonterminal.getKey());

                boolean currentCostDeclared = false;

                String bestCostVar;

                if ( cacheCost )
                {
                    bestCostVar = cachedCosts.get(productionsByNonterminal.getKey());
                    genIf ( output, codeEmitter.genCmpEquality(bestCostVar, UNINITIALIZED, true) );
                    genBeginBlock(output);
                }
                else if ( hasMultipleProductions )
                {
                    //  Store the best cost in a local.
                    bestCostVar = "bestCost";
                    genLocalVar(output, "int", "bestCost");
                }
                else
                {
                    bestCostVar = codeEmitter.genMaxIntValue();
                }

                for ( int i = 0; i < currentProductions.size(); i++ )
                {
                    JBurgProduction production = currentProductions.get(i);
                    String productionCost = getCostForProduction(production, productions);

                    if ( currentProductions.size() == 1 && !cacheCost )
                    {
                        //  Only one production, just return its cost.
                        genReturnValue(output, productionCost);
                    }
                    else if ( i == 0 )
                    {
                        //  Emit an assignment with no if guards
                        //  if this is the first (or only) assignment.
                        genAssignment(output, bestCostVar, productionCost);
                        output.println();
                    }
                    else
                    {
                        //  If the cost uses a computation, put it in a temp.
                        if ( !production.computesConstantCost(productions) )
                        {
                            if ( ! currentCostDeclared )
                            {
                                genLocalVar(output, "int", "currentCost", productionCost);
                                currentCostDeclared = true;
                            }
                            else
                            {
                                genAssignment(output, "currentCost", productionCost);
                            }
                            
                            productionCost = "currentCost";
                        }

                        genIf(output, codeEmitter.genCmpLess(bestCostVar, productionCost));
                        indentNextLine();
                        genAssignment(output, bestCostVar, productionCost);
                    }
                }

                if ( cacheCost )
                    //  End the if statement.
                    genEndBlock(output);

                if ( cacheCost || hasMultipleProductions )
                    genReturnValue(output, bestCostVar);
                // else the cost has already been returned.
            }

            genEndBlock(output); // end case without unreachable break
        }
        genEndSwitch(output);
        genReturnValue(output, codeEmitter.genMaxIntValue());
        genEndBlock(output); // getCost

        /*
         *  ** Emit getRule()  **
         */
        genDeclareMethod(output, Modifier.PUBLIC, "int", "getRule", "int", "goalState" );
        genBeginBlock(output);

        genSwitch(output, "goalState");

        for ( Map.Entry<String, ArrayList<JBurgProduction>> productionsByNonterminal: productions.entrySet() )
        {
            genCase(output, getNonterminal(productionsByNonterminal.getKey()));

            ArrayList<JBurgProduction> currentProductions = productionsByNonterminal.getValue();

            //  Try to resolve the optimal production at compiler-compile time.
            JBurgProduction optimalProduction = findOptimalProduction(currentProductions, productions);

            if ( optimalProduction != null )
            {
                genReturnValue(output, Integer.toString(optimalProduction.getReduceAction().getIndex()));
            }
            else
            {
                // Emit the analogous compile-time computation.
                genLocalVar(output, "int", "rule", NO_FEASIBLE_RULE);
                genLocalVar(output, "int", "bestCost", codeEmitter.genMaxIntValue());

                //  Emit this declaration at its first use.
                boolean currentCostDeclared = false;

                for ( int i = 0; i < currentProductions.size(); i++ )
                {
                    boolean costIsConstant = false;
                    String currentProductionCost;

                    //  Extract required information from the production:
                    //  what is its cost, and is it constant?
                    JBurgProduction production = currentProductions.get(i);

                    if ( production.computesConstantCost(productions) )
                    {
                        int constantCost = production.getConstantCost(productions);
                        costIsConstant = constantCost < Integer.MAX_VALUE;
                        currentProductionCost = Integer.toString(constantCost);
                    }
                    else
                    {
                        currentProductionCost = getCostForProduction(production, productions);
                    }

                    //  Generate the necessary tests and assignments.

                    //  If the first production's cost is constant
                    //  (and feasible, which has been checked and
                    //  incorporated into costIsConstant), then 
                    //  testing it against bestCost is a tautology.
                    boolean needComparison = (!costIsConstant) || i > 0;

                    if ( needComparison )
                    {
                        //  If the cost uses a computation, put it in a temp.
                        if ( ! costIsConstant )
                        {
                            if ( ! currentCostDeclared )
                            {
                                genLocalVar(output, "int", "currentCost", currentProductionCost);
                                currentCostDeclared = true;
                            }
                            else
                            {
                                genAssignment(output, "currentCost", currentProductionCost);
                            }
                            currentProductionCost = "currentCost";
                        }

                        genIf(output, codeEmitter.genCmpLess("bestCost", currentProductionCost));
                        genBeginBlock(output);
                    }

                    //  Track the new best cost if there's another choice to be evaluated.
                    if ( i + 1 < currentProductions.size() )
                        genAssignment(output, "bestCost", currentProductionCost);

                    genAssignment(output, "rule", Integer.toString(production.getReduceAction().getIndex()));

                    if ( needComparison )
                        genEndBlock(output);
                }

                genReturnValue(output,"rule");
            }

            genEndBlock(output);  // endCase() without unreachable break statement
        }

        genEndSwitch(output);
        genReturnValue(output, NO_FEASIBLE_RULE);
        genEndBlock(output); // getRule

        /*
         *  ** Emit getArity()  **
         */
        genDeclareMethod(output, Modifier.PUBLIC, "int", "getArity");
        genBeginBlock(output);

        if ( fixedArity )
        {
            genReturnValue(output, Integer.toString(nominalArity));
        }
        else if ( nominalArity != 0 )
        {
            //  TODO: Need an emitter method to get the correct size() call
            genReturnValue(output, String.format("%d + %s", nominalArity, "narySubtrees.size()" ));
        }
        else
        {
            genReturnValue(output, "narySubtrees.size()" );
        }

        genEndBlock(output); // getArity

        /*
         *  **  Emit overrides for getNthChild() and addChild() if necessary  **
         */
        if ( nominalArity > 0 || ! fixedArity )
        {
            //  getNthChild
            genDeclareMethod( output, Modifier.PUBLIC, "JBurgAnnotation", "getNthChild", "int", "index");

            genBeginBlock(output); // getNthChild
            genSwitch(output, "index");

            for ( int i = 0; i < nominalArity; i++ )
            {
                genLine(output, String.format("case %d:", i));
                genSingleLineBlock(output, String.format("return subtree%d;", i));
            }

            genDefaultCase(output);
            if ( ! fixedArity )
            {
                if ( nominalArity == 0 )
                    genLine(output, "return narySubtrees.get(index);");
                else
                    genLine(output, String.format("return narySubtrees.get(index - %d);", nominalArity));
            }
            else
            {
                genThrow(output, "\"Invalid index \" + index");
            }
            genEndBlock(output);  //  genEndCase() without unreachable break

            genEndBlock(output); // switch
            genEndBlock(output); // getNthChild

            //  addChild
            genDeclareMethod(output, Modifier.PUBLIC, "void", "addChild", "JBurgAnnotation", "child");

            genBeginBlock(output); // addChild

            for ( int i = 0; i < nominalArity; i++ )
            {
                if ( i == 0 )
                    genLine(output, String.format("if ( subtree%d == null )", i));
                else
                    genLine(output, String.format("else if ( subtree%d == null )", i));
                genSingleLineBlock(output, String.format("subtree%d = child;", i));
            }

            if ( nominalArity > 0 )
                genLine(output, String.format("else"));

            if ( ! fixedArity )
                genSingleLineBlock(output, "narySubtrees.add(child);");
            else
                genSingleLineBlock(output, "throw new IllegalStateException(\"too many children\");");

            genEndBlock(output); // addChild
        }

        //  Emit caches for any costs that require a function call;
        //  the BURM's contract says these functions are only called once.
        Set<String> emittedCosts = new HashSet<String>();

        for ( ArrayList<ClosureRecord> closures: closureCosts.values() )
            for ( ClosureRecord closure: closures )
                if ( closure.hasCostFunction() && emittedCosts.add(closure.getCachedCost()) )
                    emitCachedCost(output, closure.getCachedCost(), closure.getCost("m_node")); 

        for ( JBurgRule rule: currentRules )
        {
            if ( rule.needsCostFunction() )
                emitGetCostForRule(output, rule, factored_variables);

            if ( rule.hasCostFunction() && emittedCosts.add(rule.getCachedCost()) )
                emitCachedCost(output, rule.getCachedCost(), rule.getCost("m_node")); 
        }

        //  Finish the compressed annotation's class declaration.
        genEndBlock(output);
    }

    /**
     *  @param productions - a list of productions.
     *  @return true if the list has one member and that member has a constant cost.
     */
    boolean hasConstantCost(ArrayList<JBurgProduction> productions)
    {
        return productions != null &&
            productions.size() == 1 && 
            productions.get(0) instanceof JBurgRule &&
            ((JBurgRule)productions.get(0)).hasConstantCost();
    }

    /**
     *  @return the expression to use for a production's cost;
     *    this may be an arithmetic expression or a call
     *    to an implementation method, depending on
     *    the production's complexity.
     */
    String getCostForProduction(JBurgProduction production, Multimap<String,JBurgProduction> productions)
    {
        if ( production instanceof JBurgRule )
        {
            JBurgRule rule = (JBurgRule) production;
            if ( rule.needsCostFunction() )
                return genCallMethod(null, getCostingFunctionForRule(rule), "goalState");
            else
                return getCompositeCostOfRule(rule);
        }
        else
        {
            return ((ClosureRecord)production).getCostComputation(productions);
        }
    }

    /**
     *  Do compiler-compile time dynamic programming to choose
     *  the best alternative from a list of possibilities.
     *  @param currentProductions - the list of productions to choose from.
     *  @param allProductions - the entire set of productions active at the site.
     */
    JBurgProduction findOptimalProduction(List<JBurgProduction> currentProductions, Multimap<String, JBurgProduction> allProductions)
    {
        int bestCost = Integer.MAX_VALUE;
        JBurgProduction result = null;

        for ( JBurgProduction production: currentProductions )
        {
            if ( production.computesConstantCost(allProductions) )
            {
                int cost = production.getConstantCost(allProductions);
                if ( cost < bestCost )
                {
                    result = production;
                    bestCost = cost;
                }
            }
            else
            {
                //  Can't be determined at compiler-compile time.
                result = null;
                break;
            }
        }

        return result;
    }

    /**
     *  Emit a rule's cost function.
     *  @param output - the current output stream.
     *  @param rule - the rule to emit.
     *  @param factored_variables - the list of pattern matchers it's worth factoring.
     */
    void emitGetCostForRule(PrintStream output, JBurgRule rule, Map<JBurgPatternMatcher,String> factored_variables )
    {
        assert(rule.needsCostFunction());

        genDeclareMethod(output, Modifier.PRIVATE, "int", getCostingFunctionForRule(rule), "int", "goalState");
        genBeginBlock(output);
        {
            for ( String var_decl: factored_variables.values() )
                if ( rule.patternMatcher.usesFactoredVariable(var_decl) )
                genLine(output, var_decl);

            output.println();

            String patternMatch = rule.patternMatcher.generatePatternRecognizer(
                    codeEmitter,
                    "this",
                    JBurgGenerator.this.adapter2
                );

            // The pattern match may be null if it's trival.
            if ( patternMatch != null )
            {
                genIf(output, patternMatch);
                indentNextLine();
            }

            genReturnValue(output, getCompositeCostOfRule(rule));

            if ( patternMatch != null )
            {
                genLine(output, codeEmitter.genElse());
                indentNextLine();
                genReturnValue(output, codeEmitter.genMaxIntValue());
            }

        }
        genEndBlock(output);
    }

    /**
     *  @return a unique identifier for the method that computes
     *    a rule's cost (note: this is not the rule's cost function).
     */
    String getCostingFunctionForRule(JBurgRule rule)
    {
        if ( ! ruleNumber.containsKey(rule) )
            ruleNumber.put(rule, String.format("getCostForRule%x", ruleNumber.size()));
        return ruleNumber.get(rule);
    }

    private Map<JBurgRule,String> ruleNumber = new HashMap<JBurgRule, String>();
    
    /**
     *  Emit the cache field and accessor function for a cached 
     *    result of a cost function call.
     */
    void emitCachedCost(PrintStream output, String functionName, String payload)
    {
        //  Strip () characters.
        functionName = functionName.substring(0, functionName.length() -2);

        String varName = String.format("cachedCostFunctionResult_%h", functionName);

        genInstanceField( output, Modifier.PRIVATE, "int", varName, UNINITIALIZED);

        genDeclareMethod( output, Modifier.PRIVATE, "int", functionName );

        genBeginBlock(output);
        {
            genIf ( output, codeEmitter.genCmpEquality(varName, UNINITIALIZED, true) );
            indentNextLine();
            genAssignment(output, varName, payload);
            genReturnValue(output, varName);
        }
        genEndBlock(output);
    }

    /**
     *  Add up all of a rule's cost factors.
     *  @param rule - the rule of interest.
     *  @return an overflow-safe addition of the
     *    rule's cost and the costs of its subtrees.
     */
    String getCompositeCostOfRule(JBurgRule rule)
    {
        String subtreeCost = null;

        for ( JBurgPatternMatcher subgoal: rule.patternMatcher.getParameterizedSubtrees() )
        {
            String subgoalCost = subgoal.generateCost(codeEmitter, "this");

            if ( subgoalCost != null )
                subtreeCost = codeEmitter.genAddition(subtreeCost, codeEmitter.genCast("long", subgoalCost));
        }

        if ( subtreeCost != null )
            return codeEmitter.genOverflowSafeAdd(rule.getCachedCost(), subtreeCost);
        else
            return rule.getCachedCost();
    }

    /**
     *  Emit the factored path variables into a map,
     *  and mutate the affected pattern matchers
     *  to refer to the factored variable.
     *  @param commonSubtrees - the map of common subtrees to their pattern matchers.
     */
    Map<JBurgPatternMatcher,String> emitFactoredPathVariables(Multimap<JBurgPatternMatcher, JBurgPatternMatcher> commonSubtrees)
    {
        Map<JBurgPatternMatcher,String> result = new TreeMap<JBurgPatternMatcher, String>();

        int varNum = 0;
        for ( Map.Entry<JBurgPatternMatcher, ArrayList<JBurgPatternMatcher>> factored: commonSubtrees.entrySet() )
        {
            String varName = String.format("factoredPath_%d", varNum++);

            result.put(factored.getKey(), codeEmitter.genLocalVar("JBurgAnnotation", varName, factored.getKey().generateFactoredReference(codeEmitter)));

            for ( JBurgPatternMatcher matcher: factored.getValue() )
                matcher.factoredPath = varName;
        }
    
        return result;
    }

    /**
     *  ClosuresByNonterminal is a convenience class that holds
     *  closure sets grouped by nonterminal; the NT may
     *  be the production or the antecedent, depending on usage.
     */
    class ClosuresByNonterminal extends Multimap<String, ClosureRecord>
    {
        /**
         *  Add a closure record.
         *  @param nt - the nonterminal that indexes this closure.
         *    May be the production or the antecedent.
         */
        public void addClosure(String nt, ClosureRecord closure)
        {
            if ( ! this.getSet(nt).contains(closure) )
                this.getSet(nt).add(closure);
        }
    }

    /**
     *  A ClosureGraph holds the graph of closures that
     *  can be reached from a starting nonterminal.
     */
    class ClosureGraph extends ClosuresByNonterminal
    {
        /**
         *  Sweep the set of nonterminal-to-nonterminal rules and
         *  add any that can be produced by the most recently
         *  added nonterminal.
         *  @param currentNT - the most recently added nonterminal.
         */
        void addClosures(String currentNT)
        {
            addClosures(currentNT, currentNT);
        }

        /**
         *  Sweep the set of nonterminal-to-nonterminal rules and
         *  add any that can be produced by the most recently
         *  added nonterminal.
         *  @param currentNT - the most recently added nonterminal.
         *  @param patternNT - the nonterminal produced by the pattern match.
         */
        void addClosures(String currentNT, String patternNT)
        {
            if ( JBurgGenerator.this.closureSets.containsKey(currentNT) )
            {
                for ( ClosureRecord closure: JBurgGenerator.this.closureSets.get(currentNT) )
                {
                    String newNT = closure.getGoalState();

                    //  Can't replace the pattern with a closure.
                    if ( !newNT.equals(patternNT) )
                    {
                        if ( ! this.containsKey ( newNT ) )
                        {
                            super.addClosure(newNT, closure);
                            addClosures(newNT);
                        }
                        else
                        {
                            //  Add this closure, but its consequent
                            //  closures are already in the set so
                            //  it's not necessary to add them.
                            super.addClosure(newNT, closure);
                        }
                    }
                }
            }
        }
    }

    /**
     *  @return the current Logger implementation;
     *    constructs a Logger that emits messages
     *    to stdout/stderr.
     */
    Logger getLogger()
    {
        if ( null == this.logger )
            this.logger = new Logger(true, true, true);
        return this.logger;
    }

    /**
     *  Convenience method wraps codeEmitter.genAssignment and prints the result.
     */
	void genAssignment(PrintStream output, String lvalue, String rvalue)
	{
		genLine(output, codeEmitter.genAssignment( lvalue, rvalue ) );
	}

    /**
     *  Convenience method wraps codeEmitter.genComment and prints the result.
     */
    void genComment(PrintStream output, String comment)
    {
        genLine(output, codeEmitter.genComment(comment) );
    }

    /**
     *  Convenience method wraps codeEmitter.genLine and prints the result.
     *  @param output - the current output stream
     *  @param contents - the contents to write.  Contents will be trimmed
     *    of whitespace before output.
     */
    void genLine(PrintStream output, String contents)
    {
        String formattedContents = contents.trim();

        if ( doIndentNextLine )
            formattedContents = "\t" + formattedContents;

        doIndentNextLine = false;
        output.print(codeEmitter.genLine(formattedContents) );
    }

    /**
     *  When set, indent the next line of output to note that
     *  the statement on that line is an if/then branch.
     *  @see {@link #indentNextLine()}, which sets this field.
     *  @see {@link #genLine(PrintStream,String)}, which consumes this field and resets it.
     *
     */
    boolean doIndentNextLine = false;

    /**
     *  Signal that the next line should be indented.
     *  @see {@link #doIndentNextLine}, which this method sets.
     */
    void indentNextLine()
    {
        doIndentNextLine = true;
    }

    /**
     *  Convenience method wraps codeEmitter.genLine and prints the result,
     *  with an extra level of nesting added to improve readability.
     */
    void genSingleLineBlock (PrintStream output, String contents)
    {
        indentNextLine();
        genLine(output, contents);
    }

    /**
     *  Convenience method wraps codeEmitter.genBeginBlock and prints the result.
     */
    void genBeginBlock(PrintStream output)
    {
        output.print(codeEmitter.genBeginBlock());
    }
    
    /**
     *  Convenience method wraps codeEmitter.genEndBlock and prints the result.
     */
    void genEndBlock(PrintStream output)
    {
        output.print(codeEmitter.genEndBlock());
    }

    /**
     *  Convenience method wraps codeEmitter.genSwitch and prints the result.
     */
    void genSwitch(PrintStream output, String expression)
    {
        genLine(output, codeEmitter.genSwitch(expression) );
        genBeginBlock(output);
    }

    /**
     *  Convenience method wraps codeEmitter.genEndSwitch and prints the result.
     */
    void genEndSwitch(PrintStream output)
    {
        genLine(output, codeEmitter.genEndSwitch() );
    }

    /**
     *  Convenience method wraps codeEmitter.genCase and prints the result.
     */
    void genCase(PrintStream output, String selector )
    {
        output.print(codeEmitter.genCase(selector));
    }

    /**
     *  Convenience method wraps codeEmitter.genEndCase and prints the result.
     */
    void genEndCase(PrintStream output)
    {
        output.print(codeEmitter.genEndCase());
    }

    /**
     *  Convenience method wraps codeEmitter.genDefaultCase and prints the result.
     */
    void genDefaultCase(PrintStream output)
    {
        genLine(output, codeEmitter.genDefaultCase());
        genBeginBlock(output);
    }

    /**
     *  Convenience method wraps codeEmitter.genIf and prints the result.
     */
    void genIf(PrintStream output, String condition )
    {
        genLine(output, codeEmitter.genIf(condition));
    }

    /**
     *  Convenience method wraps codeEmitter.genReturnValue and prints the result.
     */
    void genReturnValue(PrintStream output, String value )
    {
        genLine(output, codeEmitter.genReturnValue(value) + codeEmitter.genEndStmt());
    }

    /**
     *  Convenience method prints an expression as a statement.
     */
    void genExpressionStmt(PrintStream output, String expr)
    {
        genLine(output, expr + codeEmitter.genEndStmt());
    }

    /**
     *  Convenience method wraps codeEmitter.genDeclareMethod and prints the result.
     */
    void genDeclareMethod(PrintStream output, int modifiers, String returnClass, String name, String[][] plist, Class<?>[] exceptions )
    {
        output.print(codeEmitter.declareMethod(modifiers, returnClass, name, plist, exceptions));
    }

    /**
     *  Convenience method declares BURM methods that don't throw.
     */
    void genDeclareMethod(PrintStream output, int modifiers, String returnClass, String name, Object ... plist)
    {
        genDeclareMethod(output, modifiers, returnClass, name, genFormals(plist), throwsNothing);
    }

    /**
     *  Convenience method declares BURM methods that don't throw or have parameters.
     */
    void genDeclareMethod(PrintStream output, int modifiers, String returnClass, String name)
    {
        genDeclareMethod(output, modifiers, returnClass, name, noFormalParameters, throwsNothing);
    }

    /**
     *  Generate formal parameters from a list of type, name pairs.
     *  @param raw_formals - a variadic list of type, name pairs.
     *  @return the raw formals marshalled into a 2-dimensional array.
     */
    String[][] genFormals(Object ... raw_formals)
    {
        assert(raw_formals.length % 2 == 0): "n-ary formal parameters must be in (type, name) pairs";

        String[][] plist = new String[raw_formals.length/2][2];
        for ( int i = 0; i < raw_formals.length - 1; i += 2  )
        {
            plist[i/2][0] = raw_formals[i].toString();
            plist[i/2][1] = raw_formals[i+1].toString();
        }

        return plist;
    }

    /**
     *  Convenience method wraps codeEmitter.genLocalVar and prints the result.
     */
    void genLocalVar(PrintStream output, String varType, String varName, String initializer)
    {
        genLine(output, codeEmitter.genLocalVar(varType, varName, initializer));
        output.println();
    }

    /**
     *  Convenience method wraps codeEmitter.genLocalVar and prints the result.
     */
    void genLocalVar(PrintStream output, String varType, String varName)
    {
        genLocalVar(output, varType, varName, null);
    }

    /**
     *  Convenience method wraps codeEmitter.genThrow and prints the result.
     */
    void genThrow( PrintStream output, String diagnostic )
    {
        genLine(output, codeEmitter.genThrow(diagnostic) + codeEmitter.genEndStmt() );
    }

    /**
     *  Convenience method wraps codeEmitter.genInstanceField and prints the result.
     */
    void genInstanceField(PrintStream output, int modifiers, String type, String name, String initializer)
    { 
        genLine(output, codeEmitter.genInstanceField(modifiers, type, name, initializer));
    }

    /**
     *  Convenience method wraps codeEmitter.genGetGoalState.
     */
    String getNonterminal(String raw_nt)
    {
        return codeEmitter.genGetGoalState(raw_nt);
    }

    /**
     *  Convenience method wraps codeEmitter.genCallMethod for methods with no parameters.
     */
    String genCallMethod(String stem, String methodName)
    {
        return codeEmitter.genCallMethod(stem, methodName, noActualParameters );
    }
    
    /**
     *  Convenience method wraps codeEmitter.genCallMethod for methods with one parameter.
     */
    String genCallMethod(String stem, String methodName, Object param)
    {
        return codeEmitter.genCallMethod(stem, methodName, new String[] { param.toString() } );
    }

    /**
     *  Convenience method wraps codeEmitter.genCallMethod for methods with two parameters.
     */
    String genCallMethod(String stem, String methodName, Object param1, Object param2)
    {
        return codeEmitter.genCallMethod(stem, methodName, new String[] { param1.toString(), param2.toString() } );
    }


    /**
     *  @return the input string with its outer
     *    layer of {} brackets stripped off.
     */
    public static String stripBrackets(String src)
    {
        int startchar = src.indexOf('{') + 1;
        int lentoend = src.lastIndexOf('}');
        return src.substring( startchar, lentoend-startchar );
    
    }

    /**
     *  Generate the correct calling sequence to get the nth child
     *  of an input i-node.
     *  @param indexTerm - the n in nth child.
     */
    String genGetNthChild(String indexTerm)
    {
		return this.adapter2 != null ?
		    genCallMethod("this", "getNthChild", JBurgGenerator.initalParamName, indexTerm):
	        this.iNodeAdapter.genGetNthChild( JBurgGenerator.initalParamName, indexTerm, codeEmitter );
    }
		
    /**
     *  Generate the calling sequence for the label() method.
     */
    String genCallLabel(String indexTerm)
    {
        return genCallMethod(
            "this",
            "label",
            codeEmitter.genCast( 
                iNodeClass, 
                genGetNthChild(indexTerm)
            )
        );
    }

    /**
     *  @return true if any rules in the list have n-ary operands.
     */
    private boolean hasNaryness(Collection<JBurgRule> rules)
    {
        boolean result = false;

        for ( JBurgRule rule: rules )
            result |= rule.patternMatcher.hasNaryness();

        return result;
    }

    /**
     *  @return the minumum arity of a list of rules.
     */
    private int getMinumumArity(Collection<JBurgRule> rules)
    {
        int result = Integer.MAX_VALUE;

        for ( JBurgRule rule: rules )
            result = Math.min(result, rule.patternMatcher.getNominalArity());

        return result;
    }

    /**
     *  @return the name of a JBurgAnnotation specialized subclass.
     */
    String getSpecializedClassName(ArrayList<JBurgRule> rules )
    {
        if ( hasNaryness(rules) )
            return String.format("JBurgAnnotation_%s_%d_n", rules.get(0).getOperator(), getMinumumArity(rules));
        else
            return String.format("JBurgAnnotation_%s_%d", rules.get(0).getOperator(), getMinumumArity(rules));
    }

    /**
     *  RulesByOperatorAndArity is a multidimensional associative store that
     *  sorts JBurgRules into equivalence classes, where the equivalaence
     *  relation is "these rules can share an annotation object."
     */
    class RulesByOperatorAndArity
    {
        /**
         *  Unsorted rules.
         */
        Multimap<String, JBurgRule> unsortedRules = new Multimap<String, JBurgRule>();

        Map<String, Iterable<ArrayList<JBurgRule>>> sortedRules = null;

        public void addAll(List<JBurgRule> rules)
        {
            assert sortedRules == null;

            String operator = rules.get(0).getOperator();
            this.unsortedRules.addAllToSet(operator, rules);
        }

        public void addRule(JBurgRule rule)
        {
            assert sortedRules == null;
            this.unsortedRules.addToSet(rule.getOperator(), rule);
        }

        public Set<String> getOperators()
        {
            return unsortedRules.keySet();
        }

        public Iterable<ArrayList<JBurgRule>> getRulesFor(String operator)
        {
            if ( this.sortedRules == null )
            {
                this.sortedRules = new TreeMap<String, Iterable<ArrayList<JBurgRule>>>();

                for ( String op: getOperators() )
                    this.sortedRules.put(op, sortRules(this.unsortedRules.get(op)));
            }

            return this.sortedRules.get(operator);
        }

        private Iterable<ArrayList<JBurgRule>> sortRules( ArrayList<JBurgRule> unsorted_rules)
        {
            //  Find the minumum arity of all n-ary patterns;
            //  any rule with arity >= this limit gets sorted
            //  into the "variable-arity" bucket.
            int arity_requires_variable = Integer.MAX_VALUE;

            for ( JBurgRule rule: unsorted_rules )
                if ( rule.patternMatcher.hasNaryness() )
                    arity_requires_variable = Math.min(arity_requires_variable, rule.patternMatcher.getNominalArity());

            Multimap<Integer, JBurgRule> rules = new Multimap<Integer, JBurgRule>(); 

            for ( JBurgRule rule: unsorted_rules )
            {
                //  All n-ary patterns and any fixed-arity patterns that
                //  overlap with n-ary patterns go into a common annotation;
                //  fixed-arity patterns of arity less than the smallest
                //  n-ary arity can't be confused with an n-ary pattern
                //  and can go in their own annotation.
                Integer key = 
                    rule.patternMatcher.getNominalArity() < arity_requires_variable?
                    rule.patternMatcher.getNominalArity():
                    arity_requires_variable;

                rules.addToSet(key,rule);
            }

            return rules.values();
        }

    }
}
