(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 (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")