(*
 * Further experiments with alists.  The type defs in this file define a
 * "non-naive" Lisp alist structure, as discussed in 501 Lecture Notes 4.
 *
 * Requires lispval2.ml and stack.ml, q.q.v.
 *)
type dataBinding = string * lispVal;
type functionBinding = string * (string list) * lispVal;

type stateStore = dataBinding list;
type environment = functionBinding list;
type stackStore = dataBinding list stack;

(*
 * Given the above type defs, we now define the non-naive alist to be a record
 * of a stateStore, environment, and stackStore.
 *)
type alist = {store:stateStore, env:environment, stack:stackStore};


(*
 * Putting everything together, here is a function that builds the following
 * sample alist:
 *
 *    ( ((x 100) (y 200) (z 30000))   ;The global state store
 *
 *      ((f (x y) (g x y))            ;The environment (for defun bindings)
 *       (g (x y) (+ (h x) y))
 *       (h (x) (+ x 10)))
 *
 *      (((x 10) (y 20)) ((x 10) (y 20)) ((x 10)))  ;The stack
 *    )
 *)
fun makeSampleAlist() =
    let
        val sampleStore = [("x", N(100)), ("y", N(200)), ("z", N(30000))];

        val fDefun = ("f", ["x", "y"], L[A("g"), A("x"), A("y")]);
        val gDefun = ("g", ["x", "y"], L[A("+"), L[A("h"), A("x")], A("y")]);
        val hDefun = ("h", ["x"], L[A("+"), A("x"), N(10)]);               
        val sampleEnv = [fDefun, gDefun, hDefun];

        val fActRec = [("x", N(10)), ("y", N(20))];
        val gActRec = [("x", N(10)), ("y", N(20))];
        val hActRec = [("x", N(10))];
        val sampleStack = push(push(push(newStack, fActRec), gActRec), hActRec);

    in
        {store=sampleStore, env=sampleEnv, stack=sampleStack}:alist
    end;

(*
 * Finally, we construct a sample alist and access one of its components.
 *)
val sampleAlist = makeSampleAlist();

val valueOfBindingOfYInGsActRec =
    getN(#2(hd(tl(top(pop(#stack(sampleAlist)))))));