// Copyright (c) 1996-2002 Brian D. Carlstrom

package bdc.scheme.expression;

import bdc.scheme.DebugPair;
import bdc.scheme.Environment;
import bdc.scheme.Pair;
import bdc.scheme.Scheme;
import bdc.scheme.SchemeException;
import bdc.scheme.Stack;
import bdc.scheme.compiler.CompileTimeEnvironment;
import bdc.scheme.exception.CallCCException;
import bdc.util.Constants;
import bdc.util.Fmt;

/**
    Expressions are the internal representation for Scheme code
*/
abstract public class Expression
{
    /**
        The original source for this expression for debugging
    */
    public Pair source;

    /**
        Eval in the global environment for known Expressions
    */
    public Object eval (Scheme scheme) throws SchemeException
    {
        return eval(null, new Stack(scheme));
    }

    /**
        Eval in a lexical environment for known Expressions
    */
    abstract public Object eval (Environment env, Stack stack)
      throws SchemeException;

    /**
        Second pass of the compiler walks the compiled expressions and
        fixes up the Lexical and Local variables.
    */
    abstract public Expression fixupVariables (
        CompileTimeEnvironment environment);

    /**
        Maintain the backtrace as we unwind an error
    */
    public void backTrace (SchemeException se) throws SchemeException
    {
        backTrace(this, se);
    }

    /**
        Maintain the backtrace as we unwind an error
    */
    public static void backTrace (Expression      expression,
                                  SchemeException se)
      throws SchemeException
    {
        /*
            CallCCException isn't an error so be quiet
        */
        if (se instanceof CallCCException) {
            throw se;
        }
        se.backTrace.add(expression);
        throw se;
    }

    /**
        Print the stack trace line for this source
    */
    public static String stackTrace (Object source)
    {
        if (source == null) {
            return "No source available";
        }
        if (source instanceof DebugPair) {
            DebugPair debugPair = (DebugPair)source;
            return Fmt.S("%s(%s): %s",
                         debugPair.filename,
                         Constants.getInteger(debugPair.lineno),
                         debugPair);
        }
        return source.toString();
    }
}
