;; eval-cc1.scm ;; In this version we've turned continuations represented as ;; procedures into continuations represented as records. ;; See continuation-closure. (define *k-trace* #f) (define k-trace (lambda () (for-each display '("Kontinuation tracing enabled." #\newline)) (set! *k-trace* #t))) (define stop-k-trace (lambda () (for-each display '("Kontinuation tracing disabled." #\newline)) (set! *k-trace* #f))) (define show-k-trace (lambda (k) (if *k-trace* (begin (display (vector-ref k 0)) ; name (newline))))) ; (run ; '((lambda (n) ; ((lambda (!) ; (! n !)) ; (lambda (n bang) ; (if (zero? n) ; 1 ; (* n (bang (- n 1) bang)))))) ; 5)) ; ; This generates 135 calls to apply-k. (load "record.ss") (define-record lit (datum)) (define-record varref (var)) (define-record lambda (formals body)) (define-record app (rator rands)) (define-record if (test-exp then-exp else-exp)) (define-record letcc (var exp)) (define-record extended-env (formals args env)) (define-record closure (formals body env)) (define-record continuation-closure (kont)) ; NEW (define-record primitive (sym)) ;; This is used to add a single var (and a single arg) to the env. (define make-extended-env/1 (lambda (var arg env) (make-extended-env (list var) (list arg) env))) ;; records for continuations (define-record post-test-k (then-exp else-exp env k)) (define-record post-rator-k (rands env k)) (define-record post-rands-k (proc k)) (define-record post-rest-rands-k (a k)) (define-record post-first-rands-k (rands env k)) (define-record final-k (jumpout)) (define apply-k cons) (define apply-k* (lambda (k val) (show-k-trace k) (variant-case k (final-k (jumpout) (jumpout val)) (post-test-k (then-exp else-exp env k) (if (zero? val) (eval-exp else-exp env k) (eval-exp then-exp env k))) (post-rator-k (rands env k) (eval-rands rands env (make-post-rands-k val k))) (post-rands-k (proc k) (apply-closure proc val k)) (post-rest-rands-k (a k) (apply-k k (cons a val))) (post-first-rands-k (rands env k) (eval-rands (cdr rands) env (make-post-rest-rands-k val k))) (else (error 'apply-k "Not a valid continuation: ~s" k))))) ;; This is a major hack! We really shouldn't use scheme's ;; "apply", but instead have a giant case statement which switches on ;; sym and has in it the code for applying all the primitive ;; functions. (define apply-primitive (lambda (sym args) (case sym ((zero?) (let ((x (apply (eval sym) args))) (if x 1 0))) (else (apply (eval sym) args))))) (define find-lexical-address-ONE (lambda (e cenv) (letrec ((find-lexical-address (lambda (cenv) (cond ((eq? (car cenv) e) 0) (else (+ 1 (find-lexical-address (cdr cenv)))))))) (find-lexical-address cenv)))) (define little-lookup (lambda (item item-list match-list) (list-ref match-list (find-lexical-address-ONE item item-list)))) (define apply-env (lambda (env sym k) (variant-case env (extended-env (formals args env) (if (memq sym formals) (apply-k k (little-lookup sym formals args)) (apply-env env sym k))) (else (apply-k k (global-table sym)))))) (define apply-closure (lambda (proc args k) (variant-case proc (closure (formals body env) (eval-exp body (make-extended-env formals args env) k)) (primitive (sym) (apply-k k (apply-primitive sym args))) (continuation-closure (kont) (apply-k kont (car args))) (else (error 'apply-closure "Invalid closure type: ~s" proc))))) (define global-table (let ((apply* (make-primitive '*)) (apply+ (make-primitive '+)) (apply-cons (make-primitive 'cons)) (apply-car (make-primitive 'car)) (apply-cdr (make-primitive 'cdr)) (apply-zero? (make-primitive 'zero?)) (apply- (make-primitive '-))) (lambda (sym) (case sym ((cons) apply-cons) ((car) apply-car) ((cdr) apply-cdr) ((zero?) apply-zero?) ((X) 10) ((V) 5) ((D) 500) ((C) 100) ((I) 1) ((M) 1000) ((*) apply*) ((+) apply+) ((-) apply-) (else (error 'global-table "unbound variable: ~s" sym)))))) (define eval-rands (lambda (rands env k) (cond ((null? rands) (apply-k k '())) (else (eval-exp (car rands) env (make-post-first-rands-k rands env k)))))) (define eval-exp (letrec ((eval-exp (lambda (e env k) (variant-case e (lit (datum) (apply-k k datum)) (varref (var) (apply-env env var k)) (if (test-exp then-exp else-exp) (eval-exp test-exp env (make-post-test-k then-exp else-exp env k))) (app (rator rands) (eval-exp rator env (make-post-rator-k rands env k))) (letcc (var exp) (eval-exp exp (make-extended-env/1 var (make-continuation-closure k) env) k)) (lambda (formals body) (apply-k k (make-closure formals body env))) )))) (lambda (e env k) (eval-exp e env k)))) ;; run is for testing ;; (it parses the exp and also supplies the global table as an arg to ;; eval-exp) (define run (letrec ((parse-exp (lambda (e) (cond ((number? e) (make-lit e)) ((atom? e) (make-varref e)) ((and (list? e) ; a cheap hack (eq? (car e) 'lambda)) (make-lambda (cadr e) ; formals (parse-exp (caddr e)))) ; body ((and (list? e) ; a cheap hack (eq? (car e) 'letcc)) (make-letcc (cadr e) ; var (parse-exp (caddr e)))) ; exp ((and (list? e) ; a cheap hack (eq? (car e) 'if)) (make-if (parse-exp (cadr e)) ; test exp (parse-exp (caddr e)) ; then exp (parse-exp (cadddr e)))) ; else exp (else ; else application (make-app (parse-exp (car e)) ; operator (map parse-exp (cdr e)))))))) (lambda (unparsed-expression) (letrec ((loop (lambda (pr) (loop (apply-k* (car pr) (cdr pr)))))) (letcc escape-to-scheme (loop (eval-exp (parse-exp unparsed-expression) global-table (make-final-k escape-to-scheme))))))))