(DEFINE (BIND VARS ARGS ENV)
(COND ((= (LENGTH VARS) (LENGTH ARGS))
(CONS (CONS VARS ARGS) ENV))
(T (ERROR))))
(DEFINE (VALUE NAME ENV)
(VALUE1 NAME (LOOKUP NAME ENV)))
(DEFINE (VALUE1 NAME SLOT)
(COND ((EQ SLOT '&UNBOUND) (ERROR))
(T (CAR SLOT))))
(DEFINE (LOOKUP NAME ENV)
(COND ((NULL ENV) '&UNBOUND)
(T (LOOKUP1 NAME (CAAR ENV) (CDAR ENV) ENV))))
(DEFINE (LOOKUP1 NAME VARS VALS ENV)
(COND ((NULL VARS) (LOOKUP NAME (CDR ENV)))
((EQ NAME (CAR VARS)) VALS)
(T (LOOKUP1 NAME (CDR VARS) (CDR VALS) ENV))))
Figure 3
Utility Routines for Maintaining Environments
The interpreter uses several utility procedures for maintaining symbol tables (see Figure 3). A symbol table is represented as a list of buckets; each bucket is a list whose car is a list of names and whose cdr is a list Of corresponding values. {Note This ain't A-lists} If a variable name occurs in more than one bucket, the leftmost such bucket has priority; in this way new symbol definitions added to the front of the list can supersede old ones.
BIND takes a list of names, a list of values, and a symbol table, and produces a new symbol table which is the old one augmented by an extra bucket containing the new set of associations. (It also performs a useful error check — LENGTH returns the length of a list.)
VALUE is essentially an interface to LOOKUP. We define it because later, in Part Three, we will want to use different versions of VALUE1 without changing the underlying algorithm in LOOKUP. The check for &UNBOUND catches incorrect references to undefined variables.
LOOKUP takes a name and a symbol table, and returns that portion of a bucket whose car is the associated value. (This definition will be more useful later than one in which the value itself is returned.)
Note carefully the use of the variable PROCEDURES in the interpreter. When DRIVER-LOOP-1 called EVAL it passes the current list of defined procedures (both primitive and user-defined). DRIVER-LOOP-1 is the only routine which augments the value of PROCEDURES, and this value is only