// Copyright (c) 1996-2002 Brian D. Carlstrom

package bdc.scheme.expression;

import bdc.scheme.Environment;
import bdc.scheme.Scheme;
import bdc.scheme.SchemeException;
import bdc.scheme.Stack;
import bdc.scheme.Writer;
import bdc.scheme.compiler.CompileTimeEnvironment;
import bdc.scheme.exception.ArgumentTypeException;
import bdc.scheme.exception.PrimitiveException;
import bdc.util.FastStringBuffer;

public class ApplicationN extends Expression
{
    Expression   operator;
    Expression[] operands;

    public ApplicationN (Expression      operator,
                         ExpressionArray operands)
    {
        operands.trim();
        this.operator = operator;
        this.operands = operands.array();
    }

    public Object eval (Environment environment, Stack stack)
      throws SchemeException
    {
        for (int i = operands.length - 1; i >= 0 ; i--) {
            stack.addElement(operands[i].eval(environment, stack));
        }
        Object expression = operator.eval(environment, stack);
        if (!(expression instanceof Procedure)) {
            throw new ArgumentTypeException(this,
                                            "Procedure",
                                            expression);
        }
        try {
            return ((Procedure)expression).applyN(operands.length, stack);
        }
        catch (SchemeException se) {
            backTrace(se);
            return Scheme.NotReached;
        }
        catch (RuntimeException re) {
            throw new PrimitiveException(re);
        }
        finally {
            stack.inUse -= operands.length;
        }
    }

    /**
        Fixup sub-expressions and return
    */
    public Expression fixupVariables (CompileTimeEnvironment environment)
    {
        operator = operator.fixupVariables(environment);
        for (int i = 0; i < operands.length; i++) {
            operands[i] = operands[i].fixupVariables(environment);
        }
        return this;
    }

    public String toString ()
    {
        FastStringBuffer result = new FastStringBuffer(1024);
        result.append('(');
        result.append(Writer.write(operator));
        result.append(' ');
        for (int i = 0; i < operands.length; i++) {
            result.append(Writer.write(operands[i]));
            if (i+1 < operands.length) {
                result.append(' ');
            }
        }
        result.append(')');
        return result.toString();
    }
}
