Choose CPS over ANF due to body of literature on topic
This commit is contained in:
@@ -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)
|
|
||||||
|
|||||||
Reference in New Issue
Block a user