Files
lisp-in-small-pieces/direct-style-interpreter.scm

617 lines
18 KiB
Scheme
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

(use-modules (ice-9 match)
(srfi srfi-1)
(srfi srfi-9) ;; Record types
(srfi srfi-64) ;; Testing framework
)
;; This final interpreter removes reified continuations, favoring a magic operator inherited
;; from the implementing language. This allows us to represent procedures as thunks, and
;; the procedures that create them as combinators
;; (note: is this true? don't they still use free variables in their definitions?)
;; ((yes, because they are of dynamic scope, therefore not captured))
;; Globals
(define undefined-value (make-symbol "undefined"))
(define g.init (make-parameter '()))
(define g.current (make-parameter '()))
(define desc.init (make-parameter '()))
(define *env* (make-parameter #f))
(define sg.init (make-vector 100))
(define sg.current (make-vector 100))
;; Macros
(define-syntax with-fresh-globals
(syntax-rules ()
[(_ expr ...)
(parameterize ([g.current '()]
[g.init '()]
[desc.init '()]
[*env* #f])
expr ...)]))
(define-syntax define-initial
(syntax-rules ()
[(_ name value)
(vector-set! sg.init (g.init-extend! 'name) value)]))
(define-syntax define-primitive
(syntax-rules ()
[(_ name value 0) (define-primitive0 name value)]
[(_ name value 1) (define-primitive1 name value)]
[(_ name value 2) (define-primitive2 name value)]
[(_ name value 3) (define-primitive3 name value)]))
(define-syntax-rule (define-primitive0 name value)
(define-initial name
(letrec ([arity+1 (+ 0 1)]
[behavior
(lambda (v* k)
(if (= (activation-rec-length v*) arity+1)
(k (value))
(error "Incorrect arity" 'name)))])
(description-extend! 'name `(function ,value))
behavior)))
(define-syntax-rule (define-primitive1 name value)
(define-initial name
(letrec ([arity+1 (+ 1 1)]
[behavior
(lambda (v* k)
(if (= (activation-rec-length v*) arity+1)
(k (value (activation-rec-ref v* 0)))
(error "Incorrect arity" 'name)))])
(description-extend! 'name `(function ,value a))
behavior)))
(define-syntax-rule (define-primitive2 name value)
(define-initial name
(letrec ([arity+1 (+ 2 1)]
[behavior
(lambda (v* k)
(if (= (activation-rec-length v*) arity+1)
(k (value (activation-rec-ref v* 0)
(activation-rec-ref v* 1)))
(error "Incorrect arity" 'name)))])
(description-extend! 'name `(function ,value a b))
behavior)))
(define-syntax-rule (define-primitive3 name value)
(define-initial name
(letrec ([arity+1 (+ 3 1)]
[behavior
(lambda (v* k)
(if (= (activation-rec-length v*) arity+1)
(k (value (activation-rec-ref v* 0)
(activation-rec-ref v* 1)
(activation-rec-ref v* 2)))
(error "Incorrect arity" 'name)))])
(description-extend! 'name `(function ,value a b c))
behavior)))
;; Record types
(define-record-type <activation-record>
(make-activation-record next vals)
activation-record?
(next activation-record-next activation-record-next-set!)
(vals activation-record-vals))
(define-record-type <closure>
(make-closure code closed-environment)
closure?
(code closure-code)
(closed-environment closure-closed-environment))
;; Helpers
(define (find-indexed pred lst)
(find (lambda (kons) (pred (car kons)))
(zip lst (iota (length lst)))))
(define (local-variable env ref)
(let scan-env ([env env] [i 0])
(if (null? env)
#f
(let ([the-match (find-indexed (lambda (name) (eq? name ref)) (car env))])
(if the-match
`(local ,i . ,(cadr the-match))
(scan-env (cdr env) (+ i 1)))))))
(define (global-variable env ref)
(assq-ref env ref))
(define (compute-kind ref r)
(or (local-variable r ref)
(global-variable (g.current) ref)
(global-variable (g.init) ref)))
(define (global-update! i v)
(vector-set! sg.current i v))
(define (g.current-extend! ref)
(let ([level (length (g.current))])
(g.current (cons (cons ref (cons 'global level)) (g.current)))
level))
(define (g.init-extend! ref)
(let ([level (length (g.init))])
(g.init (cons (cons ref (cons 'predefined level)) (g.init)))
level))
(define (static-error . args)
(apply error "Static Error:" args))
;; activation records
(define (make-activation-rec size)
(make-activation-record #f (make-vector size)))
(define activation-rec-set!
(case-lambda
[(r k v) (vector-set! (activation-record-vals r) k v)]
[(r j k v) (if (zero? j)
(activation-rec-set! r k v)
(activation-rec-set! (activation-record-next r) (1- j) k v))]))
(define activation-rec-ref
(case-lambda
[(r i)
(vector-ref (activation-record-vals r) i)]
[(r i j)
(if (zero? i)
(activation-rec-ref r j)
(activation-rec-ref (activation-record-next r) (1- i) j))]))
(define (activation-rec-length r)
(vector-length (activation-record-vals r)))
(define (listify! v* arity)
(let lp ([index (1- (activation-rec-length v*))]
[result '()])
(if (= arity index)
(activation-rec-set! v* arity result)
(lp (1- index) (cons (activation-rec-ref v* (1- index))
result)))))
;; closures
(define (invoke f v*)
(if (closure? f)
((closure-code f) v* (closure-closed-environment f))
(error "Not a function" f)))
;; function descriptions
(define (description-extend! name description)
(desc.init (cons (cons name description) (desc.init))))
(define (get-description name)
(let ([p (assq name (desc.init))])
(and (pair? p) (cdr p))))
(define desc-args cddr)
(define desc-address cadr)
(define (list->activation-rec lst)
(let lp ([lst lst]
[next #f])
(if (null? lst)
next
(lp (cdr lst)
(make-activation-record next (list->vector (append (car lst) (list undefined-value))))))))
(define (env-set! lst)
(*env* (list->activation-rec lst)))
;; environments
(define (predefined-fetch i)
(vector-ref sg.init i))
(define (global-fetch i)
(vector-ref sg.current i))
(define (r-extend* r n*)
(cons n* r))
(define (sr-extend* sr v*)
(activation-record-next-set! v* sr)
v*)
;; Initial definitions
(define-initial t #t)
(define-initial f #f)
(define-initial nil '())
(define-primitive cons cons 2)
(define-primitive car car 1)
(define-primitive cdr cdr 1)
(define-primitive + + 2)
(define-primitive = = 2)
(define-initial call/cc
(let* ([arity 1]
[arity+1 (1+ arity)])
(lambda (v* k)
(if (= arity+1 (activation-rec-length v*))
((activation-rec-ref v* 0)
(let ([rec (make-activation-rec (+ 1 1))])
(activation-rec-set!
rec 0
(lambda (values kk)
(if (= arity+1 (activation-rec-length values))
(k (activation-rec-ref values 0))
(error "incorrect arity" 'continuation))))
rec)
k)
(error "incorrect arity" 'call/cc)))))
;; Denotations
(define (meaning e r tail?)
"Core of the denotation. `tail?' allows us to avoid capturing the local environment if we know we will not return"
(match e
[(? (negate pair?))
(if (symbol? e) (meaning-reference e r)
(meaning-quotation e))]
[('quote e* ...) (meaning-quotation e*)]
[('lambda v* e* ...) (meaning-abstraction v* e* r)]
[('if e1 e2 e3) (meaning-alternative e1 e2 e3 r tail?)]
[('begin e* ...) (meaning-sequence e* r tail?)]
[('set! n e) (meaning-assignment n e r)]
[(f v* ...) (meaning-application f v* r tail?)]))
(define (meaning-quotation e*)
(CONSTANT e*))
(define (CONSTANT value)
(lambda () value))
(define (meaning-alternative e1 e2 e3 r tail?)
(let ([m1 (meaning e1 r #f)]
[m2 (meaning e2 r tail?)]
[m3 (meaning e3 r tail?)])
(ALTERNATIVE m1 m2 m3)))
(define (ALTERNATIVE m1 m2 m3)
(lambda ()
(if (m1) (m2) (m3))))
(define (meaning-sequence e* r tail?)
(if (null? e*)
(static-error "Illegal syntax (begin)")
(SEQUENCE (meaning-list e* r tail?))))
(define (SEQUENCE m+)
(lambda ()
(let lp ([m* m+])
(if (null? (cdr m*))
((car m*))
(begin
((car m*))
(lp (cdr m*)))))))
(define (meaning-reference n r)
(match (compute-kind n r)
[('local . (i . j))
(if (= i 0)
(SHALLOW-ARGUMENT-REF j)
(DEEP-ARGUMENT-REF i j))]
[('global . i)
(CHECKED-GLOBAL-REF i)]
[('predefined . i)
(PREDEFINED i)]
[_ (static-error "No such variable" n)]))
(define (SHALLOW-ARGUMENT-REF j)
(lambda ()
(activation-rec-ref (*env*) j)))
(define (DEEP-ARGUMENT-REF i j)
(lambda ()
(activation-rec-ref (*env*) i j)))
(define (PREDEFINED i)
(lambda ()
(predefined-fetch i)))
(define (GLOBAL-REF i)
(lambda ()
(global-fetch i)))
(define (CHECKED-GLOBAL-REF i)
(lambda ()
(let ([v (global-fetch i)])
(if (eq? v undefined-value)
(error "Uninitialized variable")
v))))
(define (meaning-application f e* r tail?)
(cond
[(and (symbol? f)
(get-description f)
(compute-kind f r)) =>
(lambda (kind)
(if (eq? (car kind) 'predefined)
(meaning-primitive-application f e* r)
(meaning-regular-application f e* r tail?)))]
[(and (pair? f) (eq? (car f) 'lambda))
(meaning-closed-application f e* r tail?)]
[else (meaning-regular-application f e* r tail?)]))
(define (meaning-list e* r tail?)
(cond
[(null? e*) '()]
[(null? (cdr e*)) (list (meaning (car e*) r tail?))]
[else (cons (meaning (car e*) r #f) (meaning-list (cdr e*) r tail?))]))
(define (meaning* e* r)
(let lp ([rank 0]
[m* (meaning-list e* r #f)])
(if (pair? m*)
(STORE-ARGUMENT (lp (1+ rank) (cdr m*)) (car m*) rank)
(ALLOCATE-FRAME (length e*)))))
(define (meaning*-dotted e* r fixed-size)
(let lp ([rank 0]
[m* (meaning-list e* r #f)])
(cond
[(null? m*) (ALLOCATE-DOTTED-FRAME fixed-size)]
[(> fixed-size rank) (STORE-ARGUMENT (lp (1+ rank) (cdr m*)) (car m*) rank)]
[else (CONS-ARGUMENT (lp (1+ rank) (cdr m*)) (car m*) fixed-size)])))
(define (STORE-ARGUMENT m* m rank)
(lambda ()
(let ([v* (m*)]
[v (m)])
(activation-rec-set! v* rank v)
v*)))
(define (CONS-ARGUMENT m* m size)
(lambda ()
(let* ([v* (m*)]
[v (m)]
[lst (activation-rec-ref v* size)])
(activation-rec-set! v* size (cons v lst))
v*)))
(define (ALLOCATE-FRAME size)
(lambda ()
(make-activation-rec (1+ size))))
(define (ALLOCATE-DOTTED-FRAME size)
(lambda ()
(let ([rec (make-activation-rec (1+ size))])
(activation-rec-set! rec size '())
rec)))
(define (meaning-regular-application f e* r tail?)
(let [(mf (meaning f r #f))
(m* (meaning* e* r))]
((if tail? TR-REGULAR-CALL REGULAR-CALL) mf m*))) ;; hehe new trick
(define (REGULAR-CALL m m+)
(lambda ()
(let* ([f (m)]
[v* (m+)]
[sr (*env*)]
[res (invoke f v*)])
(*env* sr)
res)))
(define (TR-REGULAR-CALL m m+)
(lambda ()
(invoke (m) (m+))))
(define (meaning-primitive-application f e* r)
(let* ([desc (get-description f)]
[address (desc-address desc)]
[arity (length e*)])
(if (= (length (desc-args desc)) arity)
(case arity
[(0) (CALL0 address)]
[(1) (let ([m1 (meaning (first e*) r #f)])
(CALL1 address m1))]
[(2) (let ([m1 (meaning (first e*) r #f)]
[m2 (meaning (second e*) r #f)])
(CALL2 address m1 m2))]
[(3) (let ([m1 (meaning (first e*) r #f)]
[m2 (meaning (second e*) r #f)]
[m3 (meaning (third e*) r #f)])
(CALL3 address m1 m2 m3))])
(static-error "Wrong arity for" f arity))))
(define (CALL0 address)
(lambda ()
(address)))
(define (CALL1 address m1)
(lambda ()
(address (m1))))
(define (CALL2 address m1 m2)
(lambda ()
(let ([v1 (m1)])
(address v1 (m2)))))
(define (CALL3 address m1 m2 m3)
(lambda ()
(let ([v1 (m1)]
[v2 (m2)])
(address v1 v2 (m3)))))
(define (meaning-closed-application e ee* r tail?)
(let lp ([n* (second e)]
[e* ee*]
[fixargs '()])
(cond
[(pair? n*)
(if (pair? e*)
(lp (cdr n*) (cdr e*) (cons (car n*) fixargs))
(static-error "Not enough arguments" e ee*))]
[(null? n*)
(if (pair? e*)
(static-error "Too many arguments" e ee*)
(meaning-fixed-closed-application (second e) (drop e 2) ee* r tail?))]
[else
(meaning-dotted-closed-application (reverse fixargs) n* (drop e 2) ee* r tail?)])))
(define (meaning-fixed-closed-application n* body e* r tail?)
(let* [(m* (meaning* e* r))
(r2 (r-extend* r n*))
(m+ (meaning-sequence body r2 tail?))]
((if tail? TR-FIX-LET FIX-LET) m* m+)))
(define (meaning-dotted-closed-application n* n body e* r tail?)
(let* ([m* (meaning*-dotted e* r (length n*))]
[r2 (r-extend* r (append n* (list n)))]
[m+ (meaning-sequence body r2 tail?)])
((if tail? TR-FIX-LET FIX-LET) m* m+)))
(define (FIX-LET m* m+)
(lambda ()
(*env* (sr-extend* (*env*) (m*)))
(let ([res (m+)])
(*env* (activation-record-next (*env*))) ;; black magic
res)))
(define (TR-FIX-LET m* m+)
(lambda ()
(*env* (sr-extend* (*env*) (m*)))
(m+)))
(define (meaning-assignment n e r)
(let ([m (meaning e r #f)])
(match (compute-kind n r)
[('local . (i . j))
(if (zero? i)
(SHALLOW-ARGUMENT-SET! j m)
(DEEP-ARGUMENT-SET! i j m))]
[('global . i)
(GLOBAL-SET! i m)]
[('predefined _ ...)
(static-error "Cannot set predefined variable" n)]
[_ (static-error "Cannot set! undefined variable" n)])))
(define (SHALLOW-ARGUMENT-SET! j m)
(lambda ()
(activation-rec-set! (*env*) j (m))))
(define (DEEP-ARGUMENT-SET! i j m)
(lambda ()
(activation-rec-set! (*env*) i j (m))))
(define (GLOBAL-SET! i m)
(lambda ()
(global-update! i (m))))
(define (meaning-abstraction nn* body r)
(let lp ([n* nn*]
[fixargs '()])
(cond
[(pair? n*) (lp (cdr n*) (cons (car n*) fixargs))]
[(null? n*) (meaning-fixed-abstraction nn* body r)]
[else (meaning-dotted-abstraction (reverse fixargs) n* body r)])))
(define (meaning-fixed-abstraction n* body r)
(let* ([arity (length n*)]
[r2 (r-extend* r n*)]
[m+ (meaning-sequence body r2 #t)])
(FIX-CLOSURE m+ arity)))
(define (meaning-dotted-abstraction n* n body r)
(let* ([arity (length n*)]
[r2 (r-extend* r (append n* (list n)))]
[m+ (meaning-sequence body r2 #t)])
(NARY-CLOSURE m+ arity)))
(define (FIX-CLOSURE m+ arity)
(lambda ()
(define (the-function v* sr)
(if (= (activation-rec-length v*) (1+ arity))
(begin (*env* (sr-extend* sr v*))
(m+))
(error "Incorrect arity")))
(make-closure the-function (*env*))))
(define (NARY-CLOSURE m+ arity)
(lambda ()
(define (the-function v* sr)
(if (>= (activation-rec-length v*) (1+ arity))
(begin (listify! v* arity)
(*env* (sr-extend* sr v*))
(m+))
(error "Incorrect arity")))
(make-closure the-function (*env*))))
;; Tests
(test-begin "direct-style-interpreter")
;; quotation
(test-group "meaning-quotation"
(test-eq 'apple
((meaning-quotation 'apple)))
(test-eq '(apple pear)
((meaning-quotation '(apple pear)))))
;; alternatives
(test-group "meaning-alternative"
(with-fresh-globals
(env-set! '((#t apple pear)))
(test-eq 'apple
((meaning-alternative 'p 't 'f '((p t f)) #t)))
(env-set! '((#f apple pear)))
(test-eq 'pear
((meaning-alternative 'p 't 'f '((p t f)) #t)))))
;; sequences
(test-group "meaning-sequence"
(with-fresh-globals
(env-set! '((a b)))
(test-eq 'b
((meaning-sequence '(a b) '((a b)) #t)))))
;; references
(test-group "meaning-reference"
(with-fresh-globals
(g.current-extend! 'a)
(vector-set! sg.current 0 10)
(g.init-extend! 'b)
(vector-set! sg.init 0 20)
(env-set! '((0 1)))
(test-eq 1
((meaning-reference 'b '((a b)))))
(env-set! '((0 1) (2)))
(test-eq 0
((meaning-reference 'b '((a) (b c)))))
(test-equal 10
((meaning-reference 'a '())))
(test-equal 20
((meaning-reference 'b '())))))
;; applications
(test-group "meaning-application"
(with-fresh-globals
(desc.init `((+ function ,+ . (x y))))
(define *-abstraction
(FIX-CLOSURE
(lambda ()
(* (activation-rec-ref (*env*) 0)
(activation-rec-ref (*env*) 1)))
2))
(env-set! `((,(*-abstraction))))
(test-eq 9
((meaning-regular-application '* '(3 3) '((*)) #t)))
(test-eq 2
((meaning-primitive-application '+ '(1 1) '(()))))
(test-eq 2 ((meaning-closed-application '(lambda (x) x) '(2) '(()) #t)))
(test-equal '(2 3) ((meaning-closed-application '(lambda (x . y) y) '(1 2 3) '(()) #t)))))
;; assignment
(test-group "meaning-assignment"
(with-fresh-globals
(*env* (list->activation-rec '((1))))
((meaning-assignment 'apples 2 '((apples))))
(test-eq 2
(activation-rec-ref (*env*) 0 0))))
;; abstraction
(test-group "meaning-abstraction"
(with-fresh-globals
(test-assert (closure? ((meaning-abstraction '(x) '(x) '(())))))
(*env* (sr-extend* (*env*) (make-activation-record #f (vector 1 2 undefined-value))))
(define (call-with-1-2 f)
(invoke f (make-activation-record #f (vector 1 2 undefined-value))))
(test-eq 2
(call-with-1-2 ((meaning-abstraction '(x y) '(y) '(())))))
(test-equal '(2)
(call-with-1-2 ((meaning-abstraction '(x . y) '(y) '(())))))))
;; (test-group "call/cc"
;; (test-eq 3 ((meaning '(+ 2 (call/cc (lambda (cc) (cc 1) 2))) '(()) #t) identity))
;; (test-eq 5 ((meaning '((lambda (cont v)
;; (set! v (+ 2 (call/cc (lambda (cc) (set! cont cc) 1))))
;; (if (= v 3)
;; (cont v)
;; v)) #f 0)
;; '(()) #t)
;; identity)))
(test-end "direct-style-interpreter")