Choose CPS over ANF due to body of literature on topic

This commit is contained in:
2025-11-26 13:28:10 -06:00
parent b5d3438e79
commit 7eb1ede3d9

View File

@@ -80,86 +80,89 @@
[(e* ...) `(begin ,@(map desugar-exp e*))])) [(e* ...) `(begin ,@(map desugar-exp e*))]))
;; A-Normalization ;; CPS conversion
;; Re-structure the program into "Continuation Passing Style", where non-atomic
;; expressions must pass their continuations explicitly, changing to a very
;; "lambda-like" format
;; - begin expressions are decomposed
;; - let expressions are transformed into closed function applications
;; <prgm> ::= <top> ... ;; <prgm> ::= <top> ...
;; <top> ::= <def> | <exp> ;; <top> ::= <def> | <exp>
;; <def> ::= (define <var> <exp>) ;; <def> ::= (define <var> <exp>)
;; <exp> ::= <aexp> ;; <exp> ::= <aexp>
;; | <cexp> ;; | <cexp>
;; | (let ((<var> <exp>)) <exp>)
;; <cexp> ::= (<aexp> <aexp> ...) ;; <cexp> ::= (<aexp> <aexp> ...)
;; | (if <aexp> <exp> <exp>) ;; | (if <aexp> <cexp> <cexp>)
;; | (set! <var> <exp>) ;; | (set-then! <var> <aexp> <cexp>)
;; <aexp> ::= (lambda (<var> ...) exp) ;; <aexp> ::= (lambda (<var> ...) exp)
;; | <num> | <var> | #t | #f ;; | <num> | <sym> | <var> | #t | #f
;; Atomic expressions are guaranteed to terminate without side effects or errors ;;
;; - All arguments to lambdas are atomic ;; We choose a hybrid transformation based on https://matt.might.net/articles/cps-conversion/
;; - All complex (non-atomic) expressions are let-bound or in a tail position
;; - All let expressions bind a single var
;; - begin expressions are decomposed
;; This is a classical construction, Flanegan et al. 1993 (define undefined-value (make-symbol "undefined"))
;; see https://matt.might.net/articles/a-normalization/
(define (normalize-prgm prgm) (define (hybrid-conversion expr)
(map (lambda (top) ;; M : expr -> aexp
(if (eq? (car top) 'define) ;; T-k : expr, (aexp -> cexp) -> cexp
(normalize-define top) ;; T-c : expr, aexp -> cexp
(normalize-term top))) (define (M expr)
prgm)) ;; M dispatches to the appropriate transformer
(match expr
[`(lambda (,var) ,expr)
(let ([$k (gensym "$k")])
`(lambda (,var ,$k) ,(T-c expr $k)))]
[_ expr]))
(define (normalize-define def) (define (T-k expr k)
(match def ;; T-k takes an explicit continuation and calls it when done
[`(define ,var ,exp) ;; As an invariant, T-k cannot nest a T-c call directly
`(define ,var ,(normalize-term exp))])) (match expr
[`(lambda . ,_) (k (M expr))]
[ (? atomic?) (k (M expr))]
[ ('begin e) (T-k e k)]
[ ('begin e e* ...)
(T-k e (lambda _ (T-k `(begin ,@e*) k)))]
[ ('let ([v* e*] ...) body)
(T-k `((lambda (,@v*) ,body) ,@e*) k)]
[ ('if exp1 exp2 exp3)
(T-k exp1 (lambda ($exp1)
(if $exp1
(T-k exp2 k)
(T-k exp3 k))))]
[ ('set! var expr)
(T-k expr (lambda ($expr)
`(set-then! ,var ,$expr ,(k undefined-value))))]
[(f e* ...)
(let* ([$rv (gensym "$rv")]
[cont `(lambda (,$rv) ,(k $rv))])
(T-k f (lambda ($f)
(T*-k e* (lambda ($e*)
`(,$f ,@$e* ,cont))))))]))
(define (normalize-term term) (define (T-c expr c)
(normalize-exp term identity)) ;; T-c takes a symbolic continuation, and uses it to construct CPS
(match expr
(define (normalize-exp exp k) [`(lambda . ,_) `(,c ,(M expr))]
(match exp [ (? atomic?) `(,c ,(M expr))]
[`(lambda ,params ,exp) [ ('begin e) (T-c e c)]
(k `(lambda ,params ,(normalize-term exp)))] [ ('begin e e* ...)
(T-k e (lambda _ (T-c `(begin ,@e*) c)))]
[`(if ,exp1 ,exp2 ,exp3) [ ('let ([v* e*] ...) body)
(normalize-name (T-c `((lambda (,@v*) ,body) ,@e*) c)]
exp1 [ ('if exp1 exp2 exp3)
(lambda (e1) (let ([$k (gensym "$k")]) ;; Bind cont to avoid blow up
(k `(if ,e1 `((lambda (,$k))
,(normalize-term exp2) ,(T-k exp1 (lambda (aexp)
,(normalize-term exp3)))))] `(if ,aexp
,(T-c exp2 $k)
[(f a* ...) ,(T-c exp3 $k))))
(normalize-name ,c))]
f [ ('set! var expr)
(lambda (t) (T-k expr (lambda ($expr)
(normalize-name* `(set-then ,var ,$expr (,c ,undefined-value))))]
a* [ (f e* ...)
(lambda (t*) (T-k f (lambda ($f)
(k (cons t t*))))))] (T*-k e* (lambda ($e*)
`(,$f ,@$e* ,c)))))]))
[`(let () ,exp) (define-cps-loop T*-k T-k)
(normalize-exp exp k)] (T-c expr 'ktail))
[`(let ((,var ,exp1) . ,more) ,exp2)
(normalize-exp
exp1
(lambda (e)
`(let ((,var ,e))
,(normalize-exp `(let (,@more) ,exp2) k))))]
[('begin e* ... e)
(normalize-exp `(let (,@(map (lambda (t) (cons (gensym "t") t)) e*)) e) k)]
[(? atomic?) (k exp)]))
(define (normalize-name name k)
(normalize-exp
name
(lambda (t)
(if (atomic? t)
(k t)
(let ([$t (gensym "t")])
`(let ([,$t ,t]) ,(k $t)))))))
(define-cps-loop normalize-name* normalize-name)