-- JunoBindImpl.mesa
-- June 6, 1983 2:04 pm Greg Nelson
-- Last Edited by: Gnelson, December 9, 1983 6:49 pm

-- An alist is a list of NILs and 2-element lists.  Each two-element list [v, u] represents
-- a binding of the name v to the value u.  Note that [v, u] means Cons[v, Cons[u, NIL]]. 
-- Each NIL in the alist represents a previous Push, and is searched for by Pop.

-- All of these procedures treat their alist as the complete environment except Lookup
-- and Bind, which, if the designated variable is not on their alist argument, use the
-- top-level alist from Juno2Top.

DIRECTORY JunoBind, Lisp, JunoSyntax, Juno2Top, Atom;

JunoBindImpl: PROGRAM IMPORTS Lisp, Juno2Top, Atom EXPORTS JunoBind =
BEGIN OPEN Lisp, JunoBind, JS: JunoSyntax;

Lookup2: PROC [v: JS.Variable, al: Alist] 
           RETURNS [alcdr: Alist] =
 {WHILE al # NIL AND (Car[al] = NIL OR Car[Car[al]] # v) 
  DO al ← Cdr[al] ENDLOOP;
  alcdr ← al};

nil: ATOM = Atom.MakeAtom["nil"];

Lookup: PUBLIC PROC [v: JS.Variable, al: Alist] 
          RETURNS [Value] =
          {IF v = NIL OR v = nil THEN RETURN[NIL];
           al ← Lookup2[v, al];
           IF al = NIL THEN al ← Lookup2[v, Juno2Top.al];
           IF al = NIL THEN ERROR ELSE RETURN [Cadr[Car[al]]]};

Bind: PUBLIC PROC[al: Alist, v: JS.Variable, u: Value] RETURNS [newal: Alist]
= {newal ← al;
    al ← Lookup2[v, al];
    IF al = NIL THEN al ← Lookup2[v, Juno2Top.al];
    IF al = NIL THEN ERROR;
    Rplaca[Cdr[Car[al]], u]};

BindLoop: PUBLIC PROC[al: Alist, al2: Alist] RETURNS [newal: Alist] =
  {newal ←  al;
   WHILE al2 # NIL DO
     IF Car[al2] # NIL THEN newal ← Bind[newal, Car[Car[al2]], Cadr[Car[al2]]];
     al2 ← Cdr[al2]
   ENDLOOP};

Push: PUBLIC PROC [al: Alist] RETURNS [Alist] = {RETURN [Cons[NIL, al]]};

Pop: PUBLIC PROC [al: Alist] RETURNS [Alist] =
  {WHILE al # NIL AND Car[al] # NIL DO al ← Cdr[al] ENDLOOP;
   IF al = NIL THEN ERROR;
   RETURN [Cdr[al]]};

EmptyBinding: PUBLIC PROC RETURNS [Alist] = {RETURN [NIL]};

NewBind: PUBLIC PROC [al: Alist, v: JS.Variable, u: Value] 
            RETURNS [newal: Alist]
= {newal ← Cons[Cons[v, Cons[u, NIL]], al]};

Extend: PUBLIC PROC [al2, al: Alist] RETURNS [newal: Alist] =
  {newal ← al;
   WHILE al2 # NIL 
   DO newal ← NewBind[newal, Car[Car[al2]], Cadr[Car[al2]]]; al2 ← Cdr[al2] ENDLOOP;
   -- error in the above loop means al2 has a NIL mark in it, which it shouldn't.
   };
 
 InDomain: PUBLIC PROC[arg: JS.Variable, al: Alist] RETURNS [BOOL] =
   {WHILE al # NIL AND (Car[al] = NIL OR Car[Car[al]] # arg) DO  al ← Cdr[al] ENDLOOP;
    RETURN [al # NIL]};  
   
END.