;; eval-cc6b.scm ;; this is based on eval-cc6.scm, but we've removed apply-k, and also ;; the useless set!'s. (and the unneeded letrec in eval-exp.) ;; registers (define k* 'nobinding) (define acc* 'nobinding) ; was val (define env* 'nobinding) (define exp* 'nobinding) (define rands* 'nobinding) (define sym* 'nobinding) (define proc* 'nobinding) (define args* 'nobinding) ;; rest (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* (lambda () (show-k-trace k*) (variant-case k* (final-k (jumpout) (jumpout acc*)) (post-test-k (then-exp else-exp env k) (if (zero? acc*) (begin (set! k* k) (set! env* env) (set! exp* else-exp) (eval-exp)) (begin (set! k* k) (set! env* env) (set! exp* then-exp) (eval-exp)))) (post-rator-k (rands env k) (set! k* (make-post-rands-k acc* k)) (set! env* env) (set! rands* rands) (eval-rands)) (post-rands-k (proc k) (set! k* k) (set! proc* proc) (set! args* acc*) (apply-closure)) (post-rest-rands-k (a k) (set! k* k) (set! acc* (cons a acc*)) ) (post-first-rands-k (rands env k) (set! k* (make-post-rest-rands-k acc* k)) (set! env* env) (set! rands* (cdr rands)) (eval-rands)) (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 () (variant-case env* (extended-env (formals args env) (if (memq sym* formals) (set! acc* (little-lookup sym* formals args)) (begin (set! env* env) (apply-env)))) (else (set! acc* (global-table sym*)) )))) (define apply-closure (lambda () (variant-case proc* (closure (formals body env) (set! env* (make-extended-env formals args* env)) (set! exp* body) (eval-exp)) (primitive (sym) (set! acc* (apply-primitive sym args*)) ) (continuation-closure (kont) (set! k* kont) (set! acc* (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 () (cond ((null? rands*) (set! acc* '()) ) (else (set! k* (make-post-first-rands-k rands* env* k*)) (set! exp* (car rands*)) (eval-exp))))) (define eval-exp (lambda () (variant-case exp* (lit (datum) (set! acc* datum) ) (varref (var) (set! sym* var) (apply-env)) (if (test-exp then-exp else-exp) (set! k* (make-post-test-k then-exp else-exp env* k*)) (set! exp* test-exp) (eval-exp)) (app (rator rands) (set! k* (make-post-rator-k rands env* k*)) (set! exp* rator) (eval-exp)) (letcc (var exp) (set! env* (make-extended-env/1 var (make-continuation-closure k*) env*)) (set! exp* exp) (eval-exp)) (lambda (formals body) (set! acc* (make-closure formals body env*)) ) ))) ;; 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 () (apply-k*) (loop)))) (letcc escape-to-scheme (begin (set! k* (make-final-k escape-to-scheme)) (set! env* global-table) (set! exp* (parse-exp unparsed-expression)) (eval-exp) (loop)))))))