// A Lisp Driver to be embedded in Java Applications

// The contents of this file are subject to the Mozilla Public License
// Version 1.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.mozilla.org/MPL/
//
// Software distributed under the License is distributed on an "AS IS"
// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
// License for the specific language governing rights and limitations
// under the License.
//
// The Original Code is JAKLD code, released November 26, 2002.
//
// The Initial Developer of the Original Code is Taiichi Yuasa.
// Portions created by Taiichi Yuasa are Copyright (C) 2002
// Taiichi Yuasa. All Rights Reserved.
//
// Contributor(s): Taiichi Yuasa <yuasa@kuis.kyoto-u.ac.jp>

abstract class Function {

  abstract Object invoke0(List args);
  
  abstract String getName();

  public Object invoke(List args) {
    Eval.checkInterrupt();
    Object val = invoke0(args);
    List tailCalls = List.nil;
    int nTailCalls = 0;
    try {
      while (val instanceof Call) {
        Eval.checkInterrupt();
        Call call = (Call) val;
        val = call.fun.invoke0(call.args);
        if (nTailCalls <= 8) {
          if (nTailCalls == 8)
            ((Pair) ((List) ((List) tailCalls.cdr).cdr).cdr).car = null;
          tailCalls = new Pair(call, tailCalls);
          nTailCalls++;
        } else
          for (Pair tc = (Pair) tailCalls; tc.car != null;
                tc = (Pair) tc.cdr) {
            Call next = (Call) tc.car;
            tc.car = call;
            call = next;
          }
      }
    } catch (RuntimeException e) {
      if (e == Eval.backtraceToken) {
        while (tailCalls != List.nil) {
          Call call = (Call) List.car(tailCalls);
          IO.print(" < " + (call == null ? "..."
                                         : "[" + call.fun.getName() + "]"));
          tailCalls = (List) List.cdr(tailCalls);
        }
        IO.print(" < [" + getName() + "]");
      }
      throw e;
    }
    return val;
  }

  public Object invoke1(Object arg) {
    return invoke(List.list(arg));
  }

  public boolean isSpecialForm() {
    return false;
  }

}
