package scm;

                                // an environment for the underlying
                                // evaluator

import java.util.*;

class Env
{
  Hashtable bindings=null;      // bindings are lazily created
                                // during a set variable
  static Object MAGIC_KLUDGE="**jas-nil-internal";
                                // record "null" as this.

  Env parent;

  Obj lookup(Symbol cvar)
  {
    Object ret;
    Env f;
    Hashtable b;

    for (f=this, ret=null, b=bindings;
         (ret == null) && (f != null);
         f = f.parent)
      {
        b = f.bindings;
        if (b != null) { ret = b.get(cvar); }
      }
    if (ret == null)
      { throw new SchemeError("Unbound variable " + cvar); }
    if (ret == MAGIC_KLUDGE)
      return null;
    else
      return ((Obj)ret);
  }

  void setvar(Symbol cvar, Obj val)
  {
    Object ret;
    Env f;
    Hashtable b;

    for (f=this, ret=null, b=bindings;
         (ret == null) && (f != null);
         f = f.parent)
      {
        b = f.bindings;
        if (b != null) { ret = b.get(cvar); }
      }
    if (ret == null)
      { throw new SchemeError("Attempted to set unbound variable " + cvar); }
    if (val == null)
      { b.put(cvar, MAGIC_KLUDGE); }
    else
      { b.put(cvar, val); }
  }

  void definevar(Symbol v, Obj val)
  {
    if (bindings == null) bindings = new Hashtable();
    if (val == null)
      { bindings.put(v, MAGIC_KLUDGE); }
    else
      { bindings.put(v, val); }
  }

  Env extendenv(Cell formals, Cell params)
  {
    Symbol thissym; Obj thisval;
    Env ret = new Env();

    ret.parent = this;
    if (formals == null)
      {
        if (params != null)
          { throw new SchemeError("mismatched arglist to entend env"); } }
    else
      {
        ret.bindings = new Hashtable();
        while (formals != null)
          {
            thissym = (Symbol) formals.car;
            thisval = params.car;
            if (thisval == null)
              {ret.bindings.put(thissym, MAGIC_KLUDGE);}
            else
              {ret.bindings.put(thissym, thisval);}
            formals = formals.cdr;
            params =  params.cdr;
          }
        if (params != null)
          { throw new SchemeError("mismatched arglist to extend env"); } }
    return ret;
  }
  public String toString()
  {
    String ret;

    return ("Binding is " + bindings + "\nparent is " + parent);
  }
}
