// 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.Fmt;

public class Application2 extends Expression
{
    Expression operator;
    Expression o1;
    Expression o2;

    public Application2 (Expression operator,
                         Expression o1,
                         Expression o2)
    {
        this.operator = operator;
        this.o1       = o1;
        this.o2       = o2;
    }

    public Object eval (Environment environment, Stack stack)
      throws SchemeException
    {
        stack.addElement(o2.eval(environment, stack));
        stack.addElement(o1.eval(environment, stack));
        Object expression = operator.eval(environment, stack);
        if (!(expression instanceof Procedure)) {
            throw new ArgumentTypeException(this,
                                            "Procedure",
                                            expression);
        }
        try {
            return ((Procedure)expression).apply2(stack);
        }
        catch (SchemeException se) {
            backTrace(se);
            return Scheme.NotReached;
        }
        catch (RuntimeException re) {
            throw new PrimitiveException(re);
        }
        finally {
            stack.inUse -= 2;
        }
    }

    /**
        Fixup sub-expressions and return
    */
    public Expression fixupVariables (CompileTimeEnvironment environment)
    {
        operator = operator.fixupVariables(environment);
        o1       = o1.fixupVariables(environment);
        o2       = o2.fixupVariables(environment);
        return this;
    }

    public String toString ()
    {
        return Fmt.S("(%s %s %s)",
                     Writer.write(operator),
                     Writer.write(o1),
                     Writer.write(o2));
    }
}
