Programming Languages: Interpreters continued...
Implement a stream of positive integers:
(define make-stream
(lambda (val thunk)
(cons val thunk)))
(define stream-cdr
(lambda (stream)
(if (pair? (cdr stream))
(cdr stream)
(let ([s ((cdr stream))])
(set-cdr! stream s)
s) )))
(define the-null-stream
(make-stream
"end-of-stream"
(lambda () the-null-stream)))
(define stream-car car)
;;
;;Make this work:
(define printstream
(lambda (stream n)
(if (zero? n)
(newline)
(begin
(display (stream-car stream))
(newline)
(printstream
(stream-cdr stream)
(sub1 n))))))
(define pint
(make-stream 0 THUNK))
> (printstream pint 20)
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
> pint
(0 1 2 3 4 5 6 7 8 9 10
11 12 13 14 15 16 17 18 19 20 . #)
>
(define n 0)
(define posint
(lambda ()
(set! n (add1 n))
(cons n posint)))
(define pint
(make-stream 0 posint))
> (repl)
==> (let ([x 1])
(let ([x (+ x 2)])
(add1 x)))
???
Procedures:
EXP ::== (proc VARLIST EXP)
VARLIST ::== () | (VARS)
VARS ::== VAR+
(define-record proc (formals body))
==> (let ([f (proc (y z) (* y (- z 5)))])
(f 2 8))
???
==> ((proc (y z) (* y (- z 5))) 2 8)
???
What happens when a procedure is applied?
Evaluate Body in environment that binds
formals to current arguments of procedure.
Variables occuring free obey lexical scoping:
-->
Free vars need to retain bindings that were in
force WHEN PROCEDURE WAS CREATED
==> (let ([x 5])
(let ([f (proc (y z) (* y (- z x)))]
[x 28])
(f 2 8)))
???
What is x in the body of f bound to?
When f is called
body should be evaluated in environment
that binds
y to 2
z to 8
x to 5
That is: in an environment where free-vars in the
procedure are bound to values in the environment
in effect WHEN PROCEDURE IS DEFINED
A first class procedure can be passed to and
returned from other procedures and stored in data
structures. It may be invoked at a place in the
program that is DISTANT from the place where it
was created.
SO: to ensure that a procedure retains the
bindings of its free-vars had at the time of
procedure creation, it must be a CLOSED package,
INDEPENDANT of the environment in which it is
used.
Such a package is called a CLOSURE.
To be independant:
CLOSURE must contain
1. procedure body
2. formals
3. and bindings of free-vars
Instead of storing bindings of free-vars lets
simply store the entire (procedure) creation
environment.
(define-record closure (formals body env))
We now have TWO kinds of procedures
Procedures in the initial environment
(init-env)
User defined procedures
apply-proc needs to cater to both types:
(define apply-proc
(lambda (proc args)
(variant-case proc
[prim-proc (prim-op)
(apply-prim-op prim-op args)]
[closure (formals body env)
(eval-exp
body
(extend-env formals args env))]
[else (error
'apply-proc
"Invalid Procedure")])))
When a proc-expression is evaluated all that is
done is to build a closure and return
[proc (formals body)
(make-closure formals body env)]
(define-record closure (formals body env))
(define eval-exp
(lambda (exp env) ;; changed
(variant-case exp
[lit (datum) datum]
[varref (var) (apply-env env var)] ;; changed
[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))]
[let (decls body)
(let ([vars (map decl->var decls)]
[exps (map decl->exp decls)])
(let ([new-env (extend-env
vars
(eval-rands exps env)
env)])
; (displayln new-env)
(eval-exp body new-env)))]
[proc (formals body)
(make-closure formals body env)]
[else (error
'Eval-exp
"Invalid abstract syntax: ~s"
exp)])))
Variable assignment:
Currently environments directly bind values to
variables.
Simple, easy, one page interpreter.
Lets add ASSIGNMENT to interpreter.
variables are now bound to LOCATIONS in memory,
and it is the contents of this memory location
that are modified by assignment.
The STORE is the collection of all locations
(memory). We will use CELLS to model locations.
(define cell-tag "cell")
(define make-cell
(lambda (x)
(vector cell-tag x)))
(define cell?
(lambda (x)
(and
(vector? x)
(= (vector-length x) 2)
(eq? (vector-ref x 0) cell-tag))))
(define cell-ref
(lambda (x)
(if (cell? x)
(vector-ref x 1)
(error
'cell-ref
"Invalid arg to cell-ref:~s" x))))
(define cell-set!
(lambda (x value)
(if (cell? x)
(vector-set! x 1 value)
(error
'cell-set!
"Invalid cell for cell-set!:~s" x))))
(define cell-swap!
(lambda (cell1 cell2)
(let ([temp (cell-ref cell1)])
(cell-set! cell1 (cell-ref cell2))
(cell-set! cell2 temp))))
First:
when we apply closures we need to
extend environment with cells.
That is we need to make cells of args
(eval-exp
body
(extend-env formals
(map make-cell args)
env))]
(define apply-proc
(lambda (proc args)
(variant-case proc
[prim-proc (prim-op)
(apply-prim-op prim-op args)]
[closure (formals body env)
(eval-exp
body
(extend-env formals
(map make-cell args)
env))]
[else (error
'apply-proc
"Invalid Procedure")])))
Second:
When vars are referenced we need to deference
their binding
[varref (var) (cell-ref (apply-env env var))]
Exercise 5.5.1
Add cells to our interpreter. You will also have
to alter the let clause and add cells to the
initial environment.
Now we can do ASSIGNMENT
EXP ::== (:= var exp)
(define-record varassign (var exp))
We need to extend our parser to parse assignment
syntax and to create varassign records
So our interpreter now needs to be extended:
with the following variant-case clause
[varassign (var exp)
(cell-set!
(apply-env env var)
(eval-exp exp env))]
What about (define var exp) in scheme?
FORM ::== (define VAR EXP)
| EXP
This syntax prevents define's from appearing
inside expressions (EXP)
(define-record define (var exp))
First evaluate exp in the initial-environment.
If var is NOT bound in initial-environment
extend initial environment with var bound to
a cell containing exp's value
ELSE iF var IS bound in the initial-environment
do a top-level assignment of exp's value to
this var
REPL:
After performing a definition
print next prompt without printing any value
After evaluating an expression, print value
Sushil Louis
Last modified: Wed Nov 10 14:13:23 PST 1999