6.001 Structure and Interpretation of Computer Programs
Recitation
#20
Friday, November 19, 2004
Review
Practice
Change the interpreter to support the following
special forms, some of which should already be familiar to you from Scheme.
BR>
1. (set!* var exp) evaluates exp and assign its
value to var.
Although set! in Scheme has an
undefined return value, your set!*
should return var's previous
value.
First we modify eval to check for a set! special form:
(define (eval exp env)
(cond
((number? exp) exp)
((symbol? exp) (lookup exp env))
((define? exp) (eval-define exp env))
((if? exp) (eval-if exp env))
((lambda? exp) (eval-lambda exp env))
((let? exp) (eval-let exp env))
((set!? exp) (eval-set! exp env))
((application? exp) (apply* (eval (car exp) env)
(map (lambda (e) (eval e env))
(cdr exp))))
(else
(error "unknown expression " exp))))
Then we define how set! is evaluated:
(define (set!? exp) (tag-check exp 'set!*))
(define (eval-set! exp env)
(let ((name (second exp))
(value (eval (third exp) env)))
(let ((binding (lookup-binding name env)))
(let ((old-value (binding-value binding)))
(set-binding-value! binding value)
old-value))))
Finally, we need a new form of lookup that returns bindings instead of values, and a
mutator that lets us change a binding's value:
(define (lookup-binding name env)
(if (null? env)
(error "unbound variable:" name)
(let ((binding (table-get (car env) name)))
(if (pair? binding)
binding
(lookup-binding name (cdr env))))))
(define (set-binding-value! binding value)
(set-car! (cdr binding) value))
2. (case* expr
((val val ...) consequent)
((val val ...) consequent)
...
(else* alternate))
Case* evaluates expr and compares its
value (using eqv?) against each of
the listed values (which are not
evaluated). When a match is found, the corresponding consequent expression
is evaluated and returned as the result of the case*. If no matches are found, the
alternate
expression is evaluated and returned instead. You can assume the else* clause is required if you
want.
(define (eval exp env)
(cond
((number? exp) exp)
((symbol? exp) (lookup exp env))
((define? exp) (eval-define exp env))
((if? exp) (eval-if exp env))
((lambda? exp) (eval-lambda exp env))
((let? exp) (eval-let exp env))
((case? exp) (eval-case exp env))
((application? exp) (apply* (eval (car exp) env)
(map (lambda (e) (eval e env))
(cdr exp))))
(else
(error "unknown expression " exp))))
(define (case? exp) (tag-check exp 'case*))
(define (eval-case exp env)
(let ((target-value (eval (second exp) env)))
(eval-case-clauses target-value (cddr exp) env)))
(define (eval-case-clauses target-value clauses env)
(if (null? clauses)
'undefined
(let ((clause (car clauses)))
(cond ((else-clause? clause) (eval (second clause) env))
((value-found? target-value (first clause))
(eval (second clause) env))
(else (eval-case-clauses target-value (cdr clauses) env))))))
(define (else-clause? clause) (tag-check clause 'else*))
(define (value-found? target values)
(not (null? (memv target values))))
3. (quote* expr) returns expr without
evaluating it.
(define (eval exp env)
(cond
((number? exp) exp)
((symbol? exp) (lookup exp env))
((define? exp) (eval-define exp env))
((if? exp) (eval-if exp env))
((lambda? exp) (eval-lambda exp env))
((let? exp) (eval-let exp env))
((quote? exp) (eval-quote exp env))
((application? exp) (apply* (eval (car exp) env)
(map (lambda (e) (eval e env))
(cdr exp))))
(else
(error "unknown expression " exp))))
(define (quote? exp) (tag-check exp 'quote*))
(define (eval-quote exp env)
(second exp))
4. (begin* e1 e2
... eN) evaluates each expression in sequence, returning the value
of eN as its final result.
(define (eval exp env)
(cond
((number? exp) exp)
((symbol? exp) (lookup exp env))
((define? exp) (eval-define exp env))
((if? exp) (eval-if exp env))
((lambda? exp) (eval-lambda exp env))
((let? exp) (eval-let exp env))
((begin? exp) (eval-begin exp env))
((application? exp) (apply* (eval (car exp) env)
(map (lambda (e) (eval e env))
(cdr exp))))
(else
(error "unknown expression " exp))))
(define (begin? exp) (tag-check exp 'begin*))
(define (eval-begin exp env)
(eval-begin-body (cdr exp) env))
(define (eval-begin-body body env)
(if (null? body)
'undefined
(let ((value (eval (car body) env)))
(if (null? (cdr body))
value
(eval-begin-body (cdr body) env)))))
5. (ask* obj message args ...) evaluates obj and args but not message, and then uses
ask to send the message to the
object. This special form allows us to avoid quoting the message all the time
--- instead of writing (ask person
'NAME), a Scheme* programmer writes (ask* person NAME).
(define (eval exp env)
(cond
((number? exp) exp)
((symbol? exp) (lookup exp env))
((define? exp) (eval-define exp env))
((if? exp) (eval-if exp env))
((lambda? exp) (eval-lambda exp env))
((let? exp) (eval-let exp env))
((ask? exp) (eval-ask exp env))
((application? exp) (apply* (eval (car exp) env)
(map (lambda (e) (eval e env))
(cdr exp))))
(else
(error "unknown expression " exp))))
(define (ask? exp) (tag-check exp 'ask*))
(define (eval-ask exp env)
(let ((object (eval (second exp)))
(message (third exp))
(args (map (lambda (e) (eval e env)) (cdddr exp))))
(apply ask object message args)))
6. (define* (name arg arg ...) body) defines a procedure name with arguments
(arg arg ...) and body body.
(define (eval-define exp env)
(if (procedure-define? exp)
(eval-procedure-define exp env)
(eval-variable-define exp env)))
(define (procedure-define? exp)
(and (tag-check exp 'define*)
(pair? (second exp))))
(define (eval-procedure-define exp env)
(let ((name (first (second exp)))
(args (cdr (second exp)))
(body (third exp)))
(table-put! (car env) name (make-compound args body env))
'undefined))
(define (eval-variable-define exp env)
(let ((name (cadr exp))
(defined-to-be (caddr exp)))
(table-put! (car env) name (eval defined-to-be env))
'undefined))