diff --git a/src/compiler.scm b/src/compiler.scm index c4cfd60..44ceac8 100644 --- a/src/compiler.scm +++ b/src/compiler.scm @@ -208,6 +208,55 @@ [(list? expr) (map (lambda (e) (cc e env)) expr)] [else expr])) +;; lift code blocks +;; +;; An incremental approach is required here more than anywhere else. Sigh! +;; +;; 1. Lambdas bound inside named letrec, with no free variables +;; + +(define (lift env expr) + + (define labels '()) + + ;; Replace a function call of the form (NAME ...) with (labelcall NAME ...) + (define (labelcall name) + (lambda (expr) + (cond [((tagged-list name) expr) + (cons 'labelcall (cons name (cdr expr)) )] + [(list? expr) (map (labelcall name) expr)] + [else expr]))) + + (define (lift-lambda name expr) + (assert (lambda? expr)) + + (let* ([formals (second expr)] + [body (third expr)] + [free (free-vars expr env)] + + ;; Use the components to build transformed version + [this (list 'code formals free (cc body env))]) + + ;; Add the code to top label with name + (set! labels (cons (list name this) labels)))) + + ;; Handle simplest letrec to begin with + (assert (letrec? expr)) + + ;; OMG! Scheme is so fucking hard to read, write or understand. + (letrec ([bindings (second expr)]) + (let* ([lvars (map first bindings)] + [lambdas (map second bindings)] + [letrec-body (third expr)]) + + ;; Lift each lambda to top level labels and replace calls in the body + (for-each (lambda (name lam) + (lift-lambda name lam) + (set! letrec-body ((labelcall name) letrec-body))) + lvars lambdas) + + (list 'labels labels letrec-body)))) + ;; Codegen helpers (define (emit-label label) diff --git a/tests/10-cc.scm b/tests/10-cc.scm index cdd5750..9523693 100644 --- a/tests/10-cc.scm +++ b/tests/10-cc.scm @@ -5,9 +5,9 @@ [o (lambda (x) (if (zero? x) #f (e (dec x))))]) (e 25))) -(define result '(letrec ([e (code (x) (o) (if (zero? x) #t (o (dec x))))] - [o (code (x) (e) (if (zero? x) #f (e (dec x))))]) - (e 25))) +(define lifted '(labels ([o (code (x) (e) (if (zero? x) #f (e (dec x))))] + [e (code (x) (o) (if (zero? x) #t (o (dec x))))]) + (labelcall e 25))) (add-tests "free variables" @@ -27,10 +27,13 @@ ;; Something non trivial [(free-vars sample default-env) >> '(e o)]) + (add-tests "closure conversion" ;; The simplest example [(cc '(lambda (x) (+ x y)) default-env) >> - '(code (x) (y) (+ x y))] + '(code (x) (y) (+ x y))]) + +(add-tests "lift" - [(cc sample default-env) >> result]) + [(lift default-env sample ) >> lifted])