package jburg;

import java.util.*;

public class Reducer<Nonterminal, NodeType>
{
    final ProductionTable<Nonterminal, NodeType> productionTable;

    public Reducer(ProductionTable<Nonterminal, NodeType> productionTable)
    {
        this.productionTable = productionTable;
    }

    public void label(BurgInput<NodeType> node)
    {
        Operator<Nonterminal, NodeType> op = productionTable.getOperator(node.getNodeType(), node.getSubtreeCount());

        if (op != null) {
            int subtreeCount = node.getSubtreeCount();

            if (subtreeCount > 0) {

                for (int i = 0; i < subtreeCount; i++) {
                    label(node.getSubtree(i));
                }

                // Navigate the operator's transition table.
                HyperPlane<Nonterminal, NodeType> current = op.transitionTable;

                for (int dim = 0; dim < node.getSubtreeCount(); dim++) {
                    RepresenterState<Nonterminal, NodeType> rs = op.getRepresenterState(node.getSubtree(dim).getStateNumber(), dim);

                    if (dim < subtreeCount-1) {
                        current = current.getNextDimension(rs);
                    } else {
                        node.setStateNumber(current.getResultState(rs).number);
                    }
                }

            } else {
                node.setStateNumber(op.getLeafState().number);
            }
        }
    }

    public Object reduce(BurgInput<NodeType> node, Nonterminal goal)
    throws Exception
    {
        // TODO: Run the entire algorithm iteratively, using the stack.
        Stack<Production<Nonterminal>> productions = new Stack<Production<Nonterminal>>();
        return reduce(node, goal, productions);
    }

    private Object reduce(BurgInput<NodeType> node, Nonterminal goal, Stack<Production<Nonterminal>> pendingProductions)
    throws Exception
    {
        if (node.getStateNumber() < 0) {
            throw new IllegalStateException(String.format("Unlabeled node %s",node));
        }

        State<Nonterminal,NodeType> state = productionTable.getState(node.getStateNumber());

        // Run pre-callbacks on any closures to get to the pattern matcher.
        Production<Nonterminal> current = state.getProduction(goal);

        while(current instanceof Closure) {
            // TODO: pre-callbacks.
            pendingProductions.push(current);
            current = state.getProduction(((Closure<Nonterminal>)current).source);
        }

        // TODO: Run the pattern matcher's pre-callback

        Object result = null;

        // Reduce children and collect results
        if (current.postCallback != null) {

            @SuppressWarnings("unchecked")
			PatternMatcher<Nonterminal, NodeType> patternMatcher = (PatternMatcher<Nonterminal, NodeType>)current;

            switch(node.getSubtreeCount()) {
                case 0:
                    result = current.postCallback.invoke(node);
                    break;

                case 1:
                    result = current.postCallback.invoke(node, reduce(node.getSubtree(0), patternMatcher.getNonterminal(0)));
                    break;

                case 2:
                    result = current.postCallback.invoke(
                        node,
                        reduce(node.getSubtree(0), patternMatcher.getNonterminal(0)),
                        reduce(node.getSubtree(1), patternMatcher.getNonterminal(1))
                        );
                    break;

                default: {

                    int formalCount = current.postCallback.getParameterCount();
                    int actualCount = node.getSubtreeCount();

                    Object[] actuals = new Object[formalCount];

                    if (formalCount == actualCount) {
                        for (int i = 0; i < node.getSubtreeCount(); i++) {
                            actuals[i] = reduce(node.getSubtree(i), patternMatcher.getNonterminal(i));
                        }

                    } else if (formalCount < actualCount) {
                        throw new IllegalStateException("variadic callbacks not supported yet.");

                    } else {
                        throw new IllegalStateException(String.format("Expected %d actuals, received %d",formalCount, actualCount));
                    }

                    result = current.postCallback.invoke(node, actuals);
                }
            }
        }

        while (!pendingProductions.isEmpty()) {
            Production<Nonterminal> closure = pendingProductions.pop();
            if (closure.postCallback != null) {
                result = closure.postCallback.invoke(node, result);
            }
        }

        return result;
    }
}

@@ -30,16 +30,376 @@
 til.*;%0A%0A
+/**%0A * A Reducer is the actual tree parsing automaton.%0A * The reducer works in two passes:%0A * %3Cli%3E label(node) traverses the input tree and%0A * assigns each node a state number.%0A * %3Cli%3E reduce(node) traverses the tree a second%0A * time, running the productions specified by each%0A * node's state number to rewrite the tree as%0A * specified by the productions.%0A */%0A
 public c
@@ -631,32 +631,131 @@
 onTable;%0A    %7D%0A%0A
+    /**%0A     * First pass: label a tree.%0A     * @param node the root of the tree to label.%0A     */%0A
     public void 
@@ -1875,24 +1875,332 @@
    %7D%0A    %7D%0A%0A
+    /**%0A     * Second pass: reduce the tree to the desired goal.%0A     * @param node the root of the tree.%0A     * @param goal the nonterminal corresponding to the%0A     * desired result object.%0A     * @return the result of deriving the tree using the%0A     * productions specified by each node's state.%0A     */%0A
     public O
@@ -2497,24 +2497,595 @@
 ns);%0A    %7D%0A%0A
+    /**%0A     * Recursively reduce subtrees.%0A     * @param node the root of a subtree.%0A     * @param goal the subtree's state.%0A     * @param pendingProductions    a stack of productions whose%0A     * antecedent production has yet to be completed; passed here%0A     * to avoid extra object creation, and eventually to be used%0A     * to run the algorithm iteratively.%0A     * @return the result of deriving the subtree.%0A     * @throws exceptions from the production's semantic action routines,%0A     * and a few diagnostics for unlabeled trees or mismatched parameters.%0A     */%0A
     private 
@@ -3658,31 +3658,112 @@
     
-// TODO: pre-c
+if (current.preCallback != null) %7B%0A                current.preC
 allback
-s
 .
+invoke(node, goal);%0A            %7D
 %0A   
@@ -3911,55 +3911,103 @@
     
-// TODO: Run the pattern matcher's pre-c
+if (current.preCallback != null) %7B%0A            current.preC
 allback
+.invoke(node,goal);%0A        %7D
 %0A%0A  

Add pre-reduction callbacks