Now that we have described the structure of our Scheme interpreter, we turn to implementing the Frame class that forms environments. Each Frame instance represents an environment in which symbols are bound to values. A frame has a dictionary of bindings, as well as a parent frame that is None for the global frame.

Bindings are not accessed directly, but instead through two Frame methods: lookup and define. The first implements the look-up procedure of the environment model of computation described in Chapter 1. A symbol is matched against the bindings of the current frame. If it is found, the value to which it is bound is returned. If it is not found, look-up proceeds to the parent frame. On the other hand, the define method always binds a symbol to a value in the current frame.

The implementation of lookup and the use of define are left as exercises. As an illustration of their use, consider the following example Scheme program:

 
(define (factorial n)
  (if (= n 0) 1 (* n (factorial (- n 1)))))
 
(factorial 5)
120

The first input expression is a define special form, evaluated by the do_define_form Python function. Defining a function has several steps:

  1. Check the format of the expression to ensure that it is a well-formed Scheme list with at least two elements following the keyword define.
  2. Analyze the first element, in this case a Pair, to find the function name factorial and formal parameter list (n).
  3. Create a LambdaProcedure with the supplied formal parameters, body, and parent environment.
  4. Bind the symbol factorial to this function, in the first frame of the current environment. In this case, the environment consists only of the global frame.

The second input is a call expression. The procedure passed to scheme_apply is the LambdaProcedure just created and bound to the symbol factorial. The args passed is a one-element Scheme list (5). To apply the procedure, a new frame is created that extends the global frame (the parent environment of the factorial procedure). In this frame, the symbol n is bound to the value 5. Then, the body of factorial is evaluated in that environment, and its value is returned.