CS170

Project Assignment #5

TARGET DATE : Monday 4/27.


Enhance your interpreter so that it is capable of reading user function definitions and evaluating them correctly. Function definitions in Scheme are of the form,

   (define (<function-name> <formal-parameters>)
           <s-expression>)
Note that, by contrast with definitions of symbols, the function name and the formal parameters are presented as a list. For example:
      (define (member E L)
        (cond ((null? L) #f)
              ((equal? E (car L)) L)
              (else (member E (cdr L)))))
(In this case, the formal parameters are E and L.) The semantics, as discussed in class, are as follows. The s-expression indicated contains symbols that appear in the formal parameter list. When the function is invoked, in general as in,
(< function-name >  < actual-parameters >)
or (for example) as in,
(member (quote b) (quote (a b c)))
the formal parameters are bound to the actual parameters according to their position. In the above example, the formal parameter E is bound to the atom b and the formal parameter L is bound to the list (a b c). These bindings are added on to the global environment to give a LOCAL environment for the s-expression. That is, the binding mechanism is like define, except that the binding should only hold as long as the s-expression is being evaluated. The s-expression in the function definition will generally contain references to the formal parameters. The value that the function returns is the value of the s-expression in the "body" of the function, when the formal parameters are bound to the actual parameters. Note that the s-expression itself could invoke the function, so that this definition of "what a function returns" is recursive!

There are two mechanisms that need to be taken care of here: binding of function names to definitions, and evaluation of functions.

Functions will always be defined globally. When the user makes a function definition, it should therefore affect the global environment. (Alternatively, you could define a separate environment list for functions. This would be more efficient, but is not necessary for this project.) For example, when the user types in the function "member" as above, the interpreter should answer with the name of the function, member, and pair off the function name and its definition, adding the pair to the global environment. That is, in this example, CONS the following pair onto the global environment:
(member 
  (define (member  E  L)
    (cond ((null? L) #f)
          ((equal? E (car L)) L)
          (else (member E (cdr  L))))))
The user should now be capable of invoking the function, as indicated above. What happens when the function is invoked? As always, the function EVAL in your program should take over. It should use ASSOC to see if the first element of the s-expression is a user-defined function, and if so, to recover its definition. It should then:

(1) call EVAL on all the actual parameters,
(2) create a local environment, CONSing all the bindings of formal parameters onto the global environment,
(3) evaluate the s-expression in the body of the function (EVAL again!), passing the local environment as a (value) parameter to the various evaluation functions.

Since the value returned is defined recursively, it is essential to think of the interpretation process recursively. The local environment will be "augmented" as you descend in a recursion, and returned to its original value when you return (like a stack, the bindings are "pushed" onto the beginning of the local environment). Examples will be discussed in class.

It would help to define a couple of new built-in functions (both "predicates", that is, boolean-valued). The first is easy given what you have; the second one (which is not standard Scheme, but you will find useful internally) requires the implementation of function definitions.

list? : returns #t (#f) if the argument is (is not) a list.
function? : returns #t (#f) if the argument is (is not) a function. For example, if you've defined the function member as above, then (function? member) would return #t; if not, it would return #f (or ()).

Here are some functions to try out on your interpreter. There are several versions of some of them, reflecting some of the varied ways of dealing with the empty list and quotes.

Back to CS170 Assignments.