Sequencing, Forms, and Recursion
SEQUENCING:
EXP ::== (begin EXP COMPOUND)
COMPOUND ::== EXP*
Parser needs to recognize (begin ...)
(begin e1) ==> e1
(begin e1 e2 e3) ==> (begin e1 (begin e2 e3))
Parser gives you:
(define-record begin (exp1 exp2))
Evaluating begin expressions:
Quiz:
Here's the parser for begin...
Write the variant-case clause to evaluate begin
expressions:
(define-record begin (exp1 exp2))
(define begin-exp?
(lambda (exp)
(eq? (car exp) 'begin)))
(define parse
(lambda (datum)
(cond
...
...
[(begin-exp? datum)
(if (null? (cddr datum))
(parse (cadr datum))
(make-begin
(parse (cadr datum))
(parse (cons 'begin (cddr datum)))))]
(variant-case exp
[lit (datum) datum]
[varref (var)
(cell-ref (apply-env env var))]
[begin (exp1 exp2)
(let ([tmp (eval-exp exp1 env)])
(eval-exp exp2 env))]
Interpreter:
(define eval-exp
(lambda (exp env)
(variant-case exp
[lit (datum) datum]
[varref (var)
(cell-ref (apply-env env var))]
[begin (exp1 exp2)
(let ([tmp (eval-exp exp1 env)])
(eval-exp exp2 env))]
[if (test-exp then-exp else-exp)
(if (true-value? (eval-exp test-exp env))
(eval-exp then-exp env)
(eval-exp else-exp env))]
[app (rator rands)
(let ([proc (eval-exp rator env)]
[args (eval-rands rands env)])
(apply-proc proc args))]
[varassign (var exp)
(cell-set! (apply-env env var) (eval-exp exp env))]
[let (decls body)
(let ([vars (map decl->var decls)]
[exps (map decl->exp decls)])
(let ([new-env (extend-env vars
(map make-cell
(eval-rands exps env))
env)])
(eval-exp body new-env)))]
[proc (formals body)
(make-closure formals body env)]
[else (error 'Eval-exp "Invalid abstract syntax: ~s"
exp)])))
(define the-empty-env (create-empty-ff))
(define extend-env extend-ff*)
(define apply-env
(lambda (ff symbol)
(variant-case ff
[empty-ff ()
(begin
(displayln "Symbol: [" symbol "] not in environment")
'**NotFound)]
[extended-ff (sym val ff)
(if (eq? sym symbol)
val
(apply-env ff symbol))]
[else (error
'apply-env "Invalid finite function")])))
Implementing define!
(define repl
(lambda ()
(display "==> ")
(let ([form (parse (read))])
(variant-case form
[define (var exp)
(let ([res (eval-exp exp init-env)])
(defhelp res var))]
[else (write (eval-exp form init-env))]))
(newline)
(repl)))
(define defhelp
(lambda (res var)
(let ([val (apply-env init-env var)])
(if (eq? val '**NotFound)
(set! init-env (extend-env
(list var)
(list (make-cell res))
init-env))
(cell-set! val res)))))
==> (define x 3)
==> x
3
==> (+ x 1)
4
==> (define x 5)
==> x
5
==> (begin (:= x 6) x)
6
==> x
6
Recursion!
(define fact
(proc (n)
(if (zero n)
1
(* n (fact (sub1 n))))))
Quiz!
Will this work with out current interpreter
that has define ?
==> (define fact
(proc (n)
(if (zero n)
1
(* n (fact (sub1 n))))))
==> fact
#4(closure (n) #4(if #3(app #2(varref zero) ....))))))))
#4(extended-ff x #2("cell" 6)
#4(extended-ff emptylist #2("cell" ())
#4(extended-ff + #2("cell" #2(prim-proc +))
....
#4(extended-ff less #2("cell" #2(prim-proc less))
#1(empty-ff)))))))))))))))))))
==> (fact 0)
1
==> (fact 1)
Symbol: [fact] not in environment
Error in cell-ref: Invalid arg to cell-ref:**notfound.
Type (debug) to enter the debugger.
EXP ::== (letrec DECLS EXP)
DECLS ::== (DECL+)
DECL ::== (VAR EXP)
(define-record letrec (decls body))
(define-record decl (var exp))
Our approach:
[let (decls body)
(let ([vars (map decl->var decls)]
[exps (map decl->exp decls)])
(let ([new-env (extend-env vars
(map make-cell
(eval-rands exps env))
env)])
(eval-exp body new-env)))]
Why can't we use let?
How does letrec work?
[letrec (decls body)
(let ([vars (map decl->var decls)]
[exps (map decl->exp decls)])
(let ([new-env (extend-rec-env vars
(map make-cell exps) env)])
(eval-exp body new-env)))]
letrec works exactly the same as let EXCEPT:
We use extend-rec-env
a new environment operator to deal with
recursive procedures.
(define-record extended-rec-env (vars vals old-env))
extend-rec-env makes an extended-rec-env record
(define extend-rec-env
(lambda (vars vals env)
(make-extended-rec-env
vars
(list->vector vals)
env)))
Why?
So that when we access the environment to get
the binding for "fact", we use this record
How do we access environment using apply-env
(define apply-env
(lambda (ff symbol)
(variant-case ff
[empty-ff ()
(begin
(displayln "Symbol: [" symbol "] not in environment")
'**NotFound)]
[extended-ff (sym val ff)
(if (eq? sym symbol)
val
(apply-env ff symbol))]
[else (error
'apply-env "Invalid finite function")])))
Now ff can be an extended-rec-env record so:
(define the-empty-env (create-empty-ff))
(define extend-env extend-ff*)
(define apply-env
(lambda (ff symbol)
(variant-case ff
[extended-rec-env (vars vals old-env)
(let ([p (ribassoc symbol vars vals '*fail*)])
(if (eq? p '*fail*)
(apply-env old-env symbol)
(make-cell (make-closure
(proc->formals (cell-ref p))
(proc->body (cell-ref p))
ff))))]
[empty-ff ()
(begin
(displayln "Symbol: [" symbol "] not in environment")
'**NotFound)]
[extended-ff (sym val ff)
(if (eq? sym symbol)
val
(apply-env ff symbol))]
[else (error
'apply-env "Invalid finite function")])))
we use ribassoc, since extended-rec-env records
associate varref with values using
(make-extended-rec-env
vars
(list->vector vals) <===
env)))
Lexical scoping leads to all this complexity
closures:
recursive procedures:
Dynamic scoping leads to a simple interpreter but
difficult to understand problems.
Sushil Louis
Last modified: Wed Nov 17 11:27:37 PST 1999