Files
lisp-in-small-pieces/env-register-interpreter.scm

558 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 interpreter uses a register for env instead of sr values
;; Globals
(define undefined-value (make-symbol "undefined"))
(define g.init (make-parameter '()))
(define sg.init (make-vector 100))
(define g.current (make-parameter '()))
(define sg.current (make-vector 100))
(define desc.init (make-parameter '()))
(define *env* (make-parameter #f))
;; 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))
;; 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 (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)))
;; 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)
;; This maybe works maybe
(define (thread m* k)
(if (null? m*)
k
(lambda _ ((car m*) (thread (cdr m*) k)))))
;; for easy testing
(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-r r)
(lambda (e tail?)
(meaning e r tail?)))
(define (n-tail n tail?)
(if (= 1 n)
(list tail?)
(cons #f (n-tail (1- n) tail?))))
(define (meaning* e* r)
"Go through e* defining meanings, thread the expressions and insert
their results into the activation frame"
(let ([m* (map (meaning-r r) e* (make-list (length e*) #f))]
[activation-rec-size (1+ (length e*))])
(lambda (k)
(let lp ([m* m*]
[rank 0]
[k k])
(if (null? m*)
(k (make-activation-rec activation-rec-size))
((car m*) (lambda (v)
(lp (cdr m*)
(1+ rank)
(lambda (v*)
(activation-rec-set! v* rank v)
(k v*))))))))))
;;(meaning*-dotted e* r (length e*) (length n*))
(define (meaning*-dotted e* r fixed-size)
"Same as `meaning' but put only put `fixed-size' arguments in the
activation frame, the rest are made into a list and put at the end"
(let ([m* (map (meaning-r r) e* (make-list (length e*) #f))]
[activation-rec-size (1+ fixed-size)])
(lambda (k)
(let lp ([m* m*]
[rank 0]
[k k])
(cond
[(null? m*)
(let ([v* (make-activation-rec activation-rec-size)])
(activation-rec-set! v* fixed-size '())
(k v*))]
[(< rank fixed-size)
((car m*) (lambda (v)
(lp (cdr m*)
(1+ rank)
(lambda (v*)
(activation-rec-set! v* rank v)
(k v*)))))]
[else
((car m*) (lambda (v)
(lp (cdr m*)
(1+ rank)
(lambda (v*)
(activation-rec-set! v* fixed-size
(cons v (activation-rec-ref v* 0 fixed-size)))
(k v*)))))])))))
(define (meaning-quotation e*)
(lambda (k)
(k e*)))
(define (meaning-alternative e1 e2 e3 r tail?)
(let ([m1 (meaning e1 r #f)]
[m2 (meaning e2 r tail?)]
[m3 (meaning e3 r tail?)])
(lambda (k)
(m1 (lambda (v)
((if v m2 m3) k))))))
(define (meaning-sequence e* r tail?)
(if (null? e*)
(static-error "Illegal syntax (begin)")
(let ([m* (map (meaning-r r) e* (n-tail (length e*) tail?))])
(lambda (k)
((thread m* (lambda (v) (k v))))))))
(define (meaning-reference n r)
(match (compute-kind n r)
[('local . (i . j))
(lambda (k)
(k (activation-rec-ref (*env*) i j)))]
[('global . i)
(if (eq? (global-fetch i) undefined-value)
(lambda (k)
(let ([v (global-fetch i)])
(if (eq? v undefined-value)
(error "Uninitialized variable" n)
(k v))))
(lambda (k)
(k (global-fetch i))))]
[('predefined . i)
(let ([value (predefined-fetch i)])
(lambda (k)
(k value)))]
[_ (static-error "No such variable" n)]))
(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-regular-application f e* r tail?)
(let [(mf (meaning f r #f))
(m* (meaning* e* r))]
(if tail?
(lambda (k)
(mf (lambda (f)
(if (procedure? f)
(m* (lambda (v*)
(f v* k)))
(error "Not a function" f)))))
(lambda (k)
(mf (lambda (f)
(if (procedure? f)
(m* (lambda (v*)
(let ([sr (*env*)])
(f v* (lambda (v)
(*env* sr)
(k v))))))
(error "Not a function"))))))))
(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) (lambda (k)
(k (address)))]
[(1)
(let ([m1 (meaning (first e*) r #f)])
(lambda (k)
(m1 (lambda (v1)
(k (address v1))))))]
[(2) (let ([m1 (meaning (first e*) r #f)]
[m2 (meaning (second e*) r #f)])
(lambda (k)
(m1 (lambda (v1)
(m2 (lambda (v2)
(k (address v1 v2))))))))]
[(3) (let ([m1 (meaning (first e*) r #f)]
[m2 (meaning (second e*) r #f)]
[m3 (meaning (third e*) r #f)])
(lambda (k)
(m1 (lambda (v1)
(m2 (lambda (v2)
(m3 (lambda (v3)
(k (address v1 v2 v3))))))))))])
(static-error "Wrong arity for" f arity))))
(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?))]
(lambda (k)
(m* (lambda (v*)
(*env* (sr-extend* (*env*) v*))
(m+ k))))))
(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?)])
(lambda (k)
(m* (lambda (v*)
(*env* (sr-extend* (*env*) v*))
(m+ k))))))
(define (meaning-assignment n e r)
(let ([m (meaning e r #f)]
[kind (compute-kind n r)])
(if kind
(case (car kind)
[(local)
(let ([i (cadr kind)]
[j (cddr kind)])
(lambda (k)
(m (lambda (v)
(activation-rec-set! (*env*) i j v)
(k undefined-value)))))]
[else (static-error "Cannot set! undefined variable" n)]))))
(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*)]
[arity+1 (1+ arity)]
[r2 (r-extend* r n*)]
[m+ (meaning-sequence body r2 #t)])
(lambda (k)
(let ([sr (*env*)])
(k (lambda (v* k1)
(if (= arity+1 (activation-rec-length v*))
(begin (*env* (sr-extend* sr v*))
(m+ k1))
(error "Incorrect arity"))))))))
(define (meaning-dotted-abstraction n* n body r)
(let* ([arity (length n*)]
[arity+1 (1+ arity)]
[r2 (r-extend* r (append n* (list n)))]
[m+ (meaning-sequence body r2 #t)])
(lambda (k)
(let ([sr (*env*)])
(k (lambda (v* k1)
(if (<= arity+1 (activation-rec-length v*))
(begin (listify! v* arity)
(*env* (sr-extend* sr v*))
(m+ k1))
(error "Incorrect arity"))))))))
(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)))))
;; Tests
(test-begin "env-register-interpreter")
;; quotation
(test-group "meaning-quotation"
(test-eq 'apple
((meaning-quotation 'apple) identity))
(test-eq '(apple pear)
((meaning-quotation '(apple pear)) identity)))
;; 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) identity))
(env-set! '((#f apple pear)))
(test-eq 'pear
((meaning-alternative 'p 't 'f '((p t f)) #t) identity))))
;; sequences
(test-group "meaning-sequence"
(with-fresh-globals
(env-set! '((a b)))
(test-eq 'b
((meaning-sequence '(a b) '((a b)) #t) identity))))
;; 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))) identity))
(env-set! '((0 1) (2)))
(test-eq 0
((meaning-reference 'b '((a) (b c))) identity))
(test-equal 10
((meaning-reference 'a '()) identity))
(test-equal 20
((meaning-reference 'b '()) identity))))
;; applications
(test-group "meaning-application"
(with-fresh-globals
(desc.init `((+ function ,+ . (x y))))
(define (*-abstraction v* k)
(k (* (activation-rec-ref v* 0 0)
(activation-rec-ref v* 0 1))))
(env-set! `((,*-abstraction)))
(test-eq 9
((meaning-regular-application '* '(3 3) '((*)) #t) identity))
(test-eq 2
((meaning-primitive-application '+ '(1 1) '(())) identity))
(test-eq 2 ((meaning-closed-application '(lambda (x) x) '(2) '(()) #t) identity))
(test-equal '(2 3) ((meaning-closed-application '(lambda (x . y) y) '(1 2 3) '(()) #t) identity))))
;; assignment
(test-group "meaning-assignment"
(with-fresh-globals
(*env* (list->activation-rec '((1))))
((meaning-assignment 'apples 2 '((apples))) identity)
(test-eq 2
(activation-rec-ref (*env*) 0 0))))
;; abstraction
(test-group "meaning-abstraction"
(with-fresh-globals
(test-assert (procedure? ((meaning-abstraction '(x) '(x) '(())) identity)))
(*env* (sr-extend* (*env*) (make-activation-record #f (vector 1 2 undefined-value))))
(define (call-with-1-2 f)
(f (make-activation-record #f (vector 1 2 undefined-value)) identity))
(test-eq 2
((meaning-abstraction '(x y) '(y) '(())) call-with-1-2))
(test-equal '(2)
((meaning-abstraction '(x . y) '(y) '(())) call-with-1-2))))
(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 "env-register-interpreter")