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