compile call/cc, yee ha
[bpt/guile.git] / module / language / scheme / translate.scm
dissimilarity index 76%
index 7f84437..49f9058 100644 (file)
-;;; Guile Scheme specification
-
-;; Copyright (C) 2001 Free Software Foundation, Inc.
-
-;; This program is free software; you can redistribute it and/or modify
-;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation; either version 2, or (at your option)
-;; any later version.
-;;
-;; This program is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-;; GNU General Public License for more details.
-;;
-;; You should have received a copy of the GNU General Public License
-;; along with this program; see the file COPYING.  If not, write to
-;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-;; Boston, MA 02111-1307, USA.
-
-;;; Code:
-
-(define-module (language scheme translate)
-  :use-module (system base pmatch)
-  :use-module (system base language)
-  :use-module (system il ghil)
-  :use-module (ice-9 receive)
-  :use-module (srfi srfi-39)
-  :use-module ((system base compile) :select (syntax-error))
-  :export (translate))
-
-
-;; Module in which compile-time code (macros) is evaluated.
-(define &compile-time-module (make-parameter #f))
-
-(define (eval-at-compile-time exp)
-  "Evaluate @var{exp} in the current compile-time module."
-  (catch #t
-    (lambda ()
-      (save-module-excursion
-       (lambda ()
-        (eval exp (&compile-time-module)))))
-    (lambda (key . args)
-      (syntax-error #f
-                   (format #f "~a: compile-time evaluation failed" exp)
-                   (cons key args)))))
-
-(define (translate x e)
-  (parameterize ((&compile-time-module (make-module)))
-
-    ;; Import only core bindings in the macro module.
-    (module-use! (&compile-time-module) the-root-module)
-
-    (call-with-ghil-environment (make-ghil-mod e) '()
-      (lambda (env vars)
-       (make-ghil-lambda env #f vars #f (trans env #f x))))))
-
-\f
-;;;
-;;; Macro tricks
-;;;
-
-(define (expand-macro e)
-  ;; Similar to `macroexpand' in `boot-9.scm' except that it does not expand
-  ;; `define-macro' and `defmacro'.
-  (cond
-   ((pair? e)
-    (let* ((head (car e))
-          (val (and (symbol? head)
-                    (false-if-exception
-                     (module-ref (&compile-time-module) head)))))
-      (case head
-       ((defmacro define-macro)
-        ;; Normally, these are expanded as `defmacro:transformer' but we
-        ;; don't want it to happen since they are handled by `trans-pair'.
-        e)
-
-       ((use-syntax)
-        ;; `use-syntax' is used to express a compile-time dependency
-        ;; (because we use a macro from that module, or because one of our
-        ;; macros uses bindings from that module).  Thus, we arrange to get
-        ;; the current compile-time module to use it.
-        (let* ((module-name (cadr e))
-               (module (false-if-exception (resolve-module module-name))))
-          (if (module? module)
-              (let ((public-if (module-public-interface module)))
-                (module-use! (&compile-time-module) public-if))
-              (syntax-error #f "invalid `use-syntax' form" e)))
-        '(void))
-
-       ((begin let let* letrec lambda quote quasiquote if and or
-               set! cond case eval-case define do)
-        ;; All these built-in macros should not be expanded.
-        e)
-
-       (else
-        ;; Look for a macro.
-        (let ((ref (false-if-exception
-                    (module-ref (&compile-time-module) head))))
-          (if (macro? ref)
-              (expand-macro
-               (save-module-excursion
-                (lambda ()
-                  (let ((transformer (macro-transformer ref))
-                        (syntax-error syntax-error))
-                    (set-current-module (&compile-time-module))
-                    (catch #t
-                      (lambda ()
-                        (transformer (copy-tree e) (current-module)))
-                      (lambda (key . args)
-                        (syntax-error #f
-                                      (format #f "~a: macro transformer failed"
-                                              head)
-                                      (cons key args))))))))
-              e))))))
-
-   (#t e)))
-
-\f
-;;;
-;;; Translator
-;;;
-
-(define %scheme-primitives
-  '(not null? eq? eqv? equal? pair? list? cons car cdr set-car! set-cdr!))
-
-(define %forbidden-primitives
-  ;; Guile's `procedure->macro' family is evil because it crosses the
-  ;; compilation boundary.  One solution might be to evaluate calls to
-  ;; `procedure->memoizing-macro' at compilation time, but it may be more
-  ;; compicated than that.
-  '(procedure->syntax procedure->macro procedure->memoizing-macro))
-
-(define (trans e l x)
-  (cond ((pair? x)
-        (let ((y (expand-macro x)))
-          (if (eq? x y)
-              (trans-pair e (or (location x) l) (car x) (cdr x))
-              (trans e l y))))
-       ((symbol? x)
-        (let ((y (symbol-expand x)))
-          (if (symbol? y)
-              (make-ghil-ref e l (ghil-lookup e y))
-              (trans e l y))))
-       (else (make-ghil-quote e l x))))
-
-(define (symbol-expand x)
-  (let loop ((s (symbol->string x)))
-    (let ((i (string-rindex s #\.)))
-      (if i
-       (let ((sym (string->symbol (substring s (1+ i)))))
-         `(slot ,(loop (substring s 0 i)) (quote ,sym)))
-       (string->symbol s)))))
-
-(define (valid-bindings? bindings . it-is-for-do)
-  (define (valid-binding? b)
-    (pmatch b 
-      ((,sym ,var) (guard (symbol? sym)) #t)
-      ((,sym ,var ,update) (guard (pair? it-is-for-do) (symbol? sym)) #t)
-      (else #f)))
-  (and (list? bindings) (and-map valid-binding? bindings)))
-
-(define (trans-pair e l head tail)
-  (define (trans:x x) (trans e l x))
-  (define (trans:pair x) (trans-pair e l (car x) (cdr x)))
-  (define (trans:body body) (trans-body e l body))
-  (define (make:void) (make-ghil-void e l))
-  (define (bad-syntax)
-    (syntax-error l (format #f "bad ~A" head) (cons head tail)))
-  ;; have to use a case first, because pmatch treats e.g. (quote foo)
-  ;; and (unquote foo) specially
-  (case head
-    ;; (void)
-    ((void)
-     (pmatch tail
-       (() (make:void))
-       (else (bad-syntax))))
-
-    ;; (quote OBJ)
-    ((quote)
-     (pmatch tail
-       ((,obj) (make-ghil-quote e l obj))
-       (else (bad-syntax))))
-
-    ;; (quasiquote OBJ)
-    ((quasiquote)
-     (pmatch tail
-       ((,obj) (make-ghil-quasiquote e l (trans-quasiquote e l obj)))
-       (else (bad-syntax))))
-
-    ((define define-private) ;; what is define-private?
-     (pmatch tail
-       ;; (define NAME VAL)
-       ((,name ,val) (guard (symbol? name))
-        (make-ghil-define e l (ghil-lookup e name) (trans:x val)))
-
-       ;; (define (NAME FORMALS...) BODY...)
-       (((,name . ,formals) . ,body) (guard (symbol? name))
-        ;; -> (define NAME (lambda FORMALS BODY...))
-        (let ((val (trans:x `(lambda ,formals ,@body))))
-          (make-ghil-define e l (ghil-lookup e name) val)))
-
-       (else (bad-syntax))))
-
-    ;; simple macros
-    ((defmacro define-macro)
-     ;; Evaluate the macro definition in the current compile-time module.
-     (eval-at-compile-time (cons head tail))
-
-     ;; FIXME: We need to evaluate them in the runtime module as well.
-     (make:void))
-
-    ((set!)
-     (pmatch tail
-       ;; (set! NAME VAL)
-       ((,name ,val) (guard (symbol? name))
-        (make-ghil-set e l (ghil-lookup e name) (trans:x val)))
-
-       ;; (set! (NAME ARGS...) VAL)
-       (((,name . ,args) ,val) (guard (symbol? name))
-        ;; -> ((setter NAME) ARGS... VAL)
-        (trans:pair `((setter ,name) . (,@args ,val))))
-
-       (else (bad-syntax))))
-
-    ;; (if TEST THEN [ELSE])
-    ((if)
-     (pmatch tail
-       ((,test ,then)
-        (make-ghil-if e l (trans:x test) (trans:x then) (make:void)))
-       ((,test ,then ,else)
-        (make-ghil-if e l (trans:x test) (trans:x then) (trans:x else)))
-       (else (bad-syntax))))
-
-    ;; (and EXPS...)
-    ((and)
-     (make-ghil-and e l (map trans:x tail)))
-
-    ;; (or EXPS...)
-    ((or)
-     (make-ghil-or e l (map trans:x tail)))
-
-    ;; (begin EXPS...)
-    ((begin)
-     (make-ghil-begin e l (map trans:x tail)))
-
-    ((let)
-     (pmatch tail
-       ;; (let NAME ((SYM VAL) ...) BODY...)
-       ((,name ,bindings . ,body) (guard (symbol? name)
-                                         (valid-bindings? bindings))
-        ;; -> (letrec ((NAME (lambda (SYM...) BODY...))) (NAME VAL...))
-        (trans:pair `(letrec ((,name (lambda ,(map car bindings) ,@body)))
-                       (,name ,@(map cadr bindings)))))
-
-       ;; (let () BODY...)
-       ((() . ,body)
-        ;; Note: this differs from `begin'
-        (make-ghil-begin e l (list (trans:body body))))
-
-       ;; (let ((SYM VAL) ...) BODY...)
-       ((,bindings . ,body) (guard (valid-bindings? bindings))
-        (let ((vals (map trans:x (map cadr bindings))))
-          (call-with-ghil-bindings e (map car bindings)
-            (lambda (vars)
-              (make-ghil-bind e l vars vals (trans:body body))))))
-       (else (bad-syntax))))
-
-    ;; (let* ((SYM VAL) ...) BODY...)
-    ((let*)
-     (pmatch tail
-       ((() . ,body)
-        (trans:pair `(let () ,@body)))
-       ((((,sym ,val) . ,rest) . ,body) (guard (symbol? sym))
-        (trans:pair `(let ((,sym ,val)) (let* ,rest ,@body))))
-       (else (bad-syntax))))
-
-    ;; (letrec ((SYM VAL) ...) BODY...)
-    ((letrec)
-     (pmatch tail
-       ((,bindings . ,body) (guard (valid-bindings? bindings))
-        (call-with-ghil-bindings e (map car bindings)
-          (lambda (vars)
-            (let ((vals (map trans:x (map cadr bindings))))
-              (make-ghil-bind e l vars vals (trans:body body))))))
-       (else (bad-syntax))))
-
-    ;; (cond (CLAUSE BODY...) ...)
-    ((cond)
-     (pmatch tail
-       (() (make:void))
-       (((else . ,body)) (trans:body body))
-       (((,test) . ,rest) (trans:pair `(or ,test (cond ,@rest))))
-       (((,test => ,proc) . ,rest)
-        ;; FIXME hygiene!
-        (trans:pair `(let ((_t ,test)) (if _t (,proc _t) (cond ,@rest)))))
-       (((,test . ,body) . ,rest)
-        (trans:pair `(if ,test (begin ,@body) (cond ,@rest))))
-       (else (bad-syntax))))
-
-    ;; (case EXP ((KEY...) BODY...) ...)
-    ((case)
-     (pmatch tail
-       ((,exp . ,clauses)
-        (trans:pair
-         ;; FIXME hygiene!
-         `(let ((_t ,exp))
-            ,(let loop ((ls clauses))
-               (cond ((null? ls) '(void))
-                     ((eq? (caar ls) 'else) `(begin ,@(cdar ls)))
-                     (else `(if (memv _t ',(caar ls))
-                                (begin ,@(cdar ls))
-                                ,(loop (cdr ls)))))))))
-       (else (bad-syntax))))
-
-    ;; (do ((SYM VAL [UPDATE]) ...) (TEST RESULT...) BODY...)
-    ((do)
-     (pmatch tail
-       ((,bindings (,test . ,result) . ,body)
-        (let ((sym (map car bindings))
-              (val (map cadr bindings))
-              (update (map cddr bindings)))
-          (define (next s x) (if (pair? x) (car x) s))
-          (trans:pair
-           ;; FIXME hygiene!
-           `(letrec ((_l (lambda ,sym
-                           (if ,test
-                               (let () (void) ,@result)
-                               (let () (void) ,@body
-                                    (_l ,@(map next sym update)))))))
-              (_l ,@val)))))
-       (else (bad-syntax))))
-
-    ;; (lambda FORMALS BODY...)
-    ((lambda)
-     (pmatch tail
-       ((,formals . ,body)
-        (receive (syms rest) (parse-formals formals)
-          (call-with-ghil-environment e syms
-        (lambda (env vars)
-              (make-ghil-lambda env l vars rest (trans-body env l body))))))
-       (else (bad-syntax))))
-
-    ((eval-case)
-     (let loop ((x tail))
-       (pmatch x
-        (() (make:void))
-        (((else . ,body)) (trans:pair `(begin ,@body)))
-        (((,keys . ,body) . ,rest) (guard (list? keys) (and-map symbol? keys))
-         (if (memq 'load-toplevel keys)
-             (begin
-               (primitive-eval `(begin ,@(copy-tree body)))
-               (trans:pair `(begin ,@body)))
-             (loop rest)))
-        (else (bad-syntax)))))
-
-    (else
-     (if (memq head %scheme-primitives)
-        (make-ghil-inline e l head (map trans:x tail))
-        (if (memq head %forbidden-primitives)
-            (syntax-error l (format #f "`~a' is forbidden" head)
-                          (cons head tail))
-            (make-ghil-call e l (trans:x head) (map trans:x tail)))))))
-
-(define (trans-quasiquote e l x)
-  (cond ((not (pair? x)) x)
-       ((memq (car x) '(unquote unquote-splicing))
-        (let ((l (location x)))
-          (pmatch (cdr x)
-            ((,obj)
-             (if (eq? (car x) 'unquote)
-                 (make-ghil-unquote e l (trans e l obj))
-                 (make-ghil-unquote-splicing e l (trans e l obj))))
-            (else (syntax-error l (format #f "bad ~A" (car x)) x)))))
-       (else (cons (trans-quasiquote e l (car x))
-                   (trans-quasiquote e l (cdr x))))))
-
-(define (trans-body e l body)
-  (define (define->binding df)
-    (pmatch (cdr df)
-      ((,name ,val) (guard (symbol? name)) (list name val))
-      (((,name . ,formals) . ,body) (guard (symbol? name))
-       (list name `(lambda ,formals ,@body)))
-      (else (syntax-error (location df) "bad define" df))))
-  ;; main
-  (let loop ((ls body) (ds '()))
-    (cond ((null? ls) (syntax-error l "bad body" body))
-         ((and (pair? (car ls)) (eq? (caar ls) 'define))
-          (loop (cdr ls) (cons (car ls) ds)))
-         (else
-          (if (null? ds)
-              (trans-pair e l 'begin ls)
-              (trans-pair e l 'letrec (cons (map define->binding ds) ls)))))))
-
-(define (parse-formals formals)
-  (cond
-   ;; (lambda x ...)
-   ((symbol? formals) (values (list formals) #t))
-   ;; (lambda (x y z) ...)
-   ((list? formals) (values formals #f))
-   ;; (lambda (x y . z) ...)
-   ((pair? formals)
-    (let loop ((l formals) (v '()))
-      (if (pair? l)
-         (loop (cdr l) (cons (car l) v))
-         (values (reverse! (cons l v)) #t))))
-   (else (syntax-error (location formals) "bad formals" formals))))
-
-(define (location x)
-  (and (pair? x)
-       (let ((props (source-properties x)))
-        (and (not (null? props))
-             (cons (assq-ref props 'line) (assq-ref props 'column))))))
+;;; Guile Scheme specification
+
+;; Copyright (C) 2001 Free Software Foundation, Inc.
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 2, or (at your option)
+;; any later version.
+;;
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+;;
+;; You should have received a copy of the GNU General Public License
+;; along with this program; see the file COPYING.  If not, write to
+;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+;; Boston, MA 02111-1307, USA.
+
+;;; Code:
+
+(define-module (language scheme translate)
+  #:use-module (system base pmatch)
+  #:use-module (system base language)
+  #:use-module (system il ghil)
+  #:use-module (system il inline)
+  #:use-module (ice-9 receive)
+  #:use-module ((system base compile) #:select (syntax-error))
+  #:export (translate))
+
+
+(define (translate x e)
+  (call-with-ghil-environment (make-ghil-toplevel-env) '()
+    (lambda (env vars)
+      (make-ghil-lambda env #f vars #f '() (trans env (location x) x)))))
+
+\f
+;;;
+;;; Translator
+;;;
+
+(define *forbidden-primitives*
+  ;; Guile's `procedure->macro' family is evil because it crosses the
+  ;; compilation boundary.  One solution might be to evaluate calls to
+  ;; `procedure->memoizing-macro' at compilation time, but it may be more
+  ;; compicated than that.
+  '(procedure->syntax procedure->macro))
+
+;; Looks up transformers relative to the current module at
+;; compilation-time. See also the discussion of ghil-lookup in ghil.scm.
+(define (lookup-transformer head retrans)
+  (let* ((mod (current-module))
+         (val (and (symbol? head)
+                   (and=> (module-variable mod head) 
+                          (lambda (var)
+                            ;; unbound vars can happen if the module
+                            ;; definition forward-declared them
+                            (and (variable-bound? var) (variable-ref var)))))))
+    (cond
+     ((assq-ref custom-transformer-table val))
+
+     ((defmacro? val)
+      (lambda (env loc exp)
+        (retrans (apply (defmacro-transformer val) (cdr exp)))))
+
+     ((and (macro? val) (eq? (macro-name val) 'sc-macro))
+      ;; syncase!
+      (let* ((the-syncase-module (resolve-module '(ice-9 syncase)))
+             (eec (module-ref the-syncase-module 'expansion-eval-closure))
+             (sc-expand3 (module-ref the-syncase-module 'sc-expand3)))
+        (lambda (env loc exp)
+          (retrans
+           (with-fluids ((eec (module-eval-closure mod)))
+             (sc-expand3 exp 'c '(compile load eval)))))))
+
+     ((primitive-macro? val)
+      (syntax-error #f "unhandled primitive macro" head))
+
+     ((macro? val)
+      (syntax-error #f "unknown kind of macro" head))
+
+     (else #f))))
+
+(define (trans e l x)
+  (define (retrans x) (trans e (location x) x))
+  (cond ((pair? x)
+         (let ((head (car x)) (tail (cdr x)))
+           (cond
+            ((lookup-transformer head retrans)
+             => (lambda (t) (t e l x)))
+
+            ;; FIXME: lexical/module overrides of forbidden primitives
+            ((memq head *forbidden-primitives*)
+            (syntax-error l (format #f "`~a' is forbidden" head)
+                          (cons head tail)))
+
+            (else
+             (let ((tail (map retrans tail)))
+               (or (and (symbol? head)
+                        (try-inline-with-env e l (cons head tail)))
+                   (make-ghil-call e l (retrans head) tail)))))))
+
+       ((symbol? x)
+         (make-ghil-ref e l (ghil-lookup e x)))
+
+        ;; fixme: non-self-quoting objects like #<foo>
+       (else
+         (make-ghil-quote e l #:obj x))))
+
+(define (valid-bindings? bindings . it-is-for-do)
+  (define (valid-binding? b)
+    (pmatch b 
+      ((,sym ,var) (guard (symbol? sym)) #t)
+      ((,sym ,var ,update) (guard (pair? it-is-for-do) (symbol? sym)) #t)
+      (else #f)))
+  (and (list? bindings) (and-map valid-binding? bindings)))
+
+(define-macro (make-pmatch-transformers env loc retranslate . body)
+  (define exp (gensym))
+  (define (make1 clause)
+    (let ((sym (car clause))
+          (clauses (cdr clause)))
+      `(cons ,sym
+             (lambda (,env ,loc ,exp)
+               (define (,retranslate x) (trans ,env (location x) x))
+               (pmatch (cdr ,exp)
+                ,@clauses
+                (else (syntax-error ,loc (format #f "bad ~A" ',sym) ,exp)))))))
+  `(list ,@(map make1 body)))
+
+(define *the-compile-toplevel-symbol* 'compile-toplevel)
+
+(define custom-transformer-table
+  (make-pmatch-transformers
+   e l retrans
+   (quote
+    ;; (quote OBJ)
+    ((,obj) (make-ghil-quote e l obj)))
+    
+   (quasiquote
+    ;; (quasiquote OBJ)
+    ((,obj) (make-ghil-quasiquote e l (trans-quasiquote e l obj 0))))
+
+   (define
+    ;; (define NAME VAL)
+    ((,name ,val) (guard (symbol? name)
+                         (ghil-toplevel-env? (ghil-env-parent e)))
+     (make-ghil-define e l (ghil-define (ghil-env-parent e) name)
+                       (maybe-name-value! (retrans val) name)))
+    ;; (define (NAME FORMALS...) BODY...)
+    (((,name . ,formals) . ,body) (guard (symbol? name))
+     ;; -> (define NAME (lambda FORMALS BODY...))
+     (retrans `(define ,name (lambda ,formals ,@body)))))
+
+   (set!
+    ;; (set! NAME VAL)
+    ((,name ,val) (guard (symbol? name))
+     (make-ghil-set e l (ghil-lookup e name) (retrans val)))
+
+    ;; (set! (NAME ARGS...) VAL)
+    (((,name . ,args) ,val) (guard (symbol? name))
+     ;; -> ((setter NAME) ARGS... VAL)
+     (retrans `((setter ,name) . (,@args ,val)))))
+
+   (if
+    ;; (if TEST THEN [ELSE])
+    ((,test ,then)
+     (make-ghil-if e l (retrans test) (retrans then) (retrans '(begin))))
+    ((,test ,then ,else)
+     (make-ghil-if e l (retrans test) (retrans then) (retrans else))))
+
+   (and
+    ;; (and EXPS...)
+    (,tail (make-ghil-and e l (map retrans tail))))
+
+   (or
+    ;; (or EXPS...)
+    (,tail (make-ghil-or e l (map retrans tail))))
+
+   (begin
+     ;; (begin EXPS...)
+     (,tail (make-ghil-begin e l (map retrans tail))))
+
+   (let
+    ;; (let NAME ((SYM VAL) ...) BODY...)
+    ((,name ,bindings . ,body) (guard (symbol? name)
+                                      (valid-bindings? bindings))
+     ;; -> (letrec ((NAME (lambda (SYM...) BODY...))) (NAME VAL...))
+     (retrans `(letrec ((,name (lambda ,(map car bindings) ,@body)))
+                 (,name ,@(map cadr bindings)))))
+
+    ;; (let () BODY...)
+    ((() . ,body)
+     ;; Note: this differs from `begin'
+     (make-ghil-begin e l (list (trans-body e l body))))
+    
+    ;; (let ((SYM VAL) ...) BODY...)
+    ((,bindings . ,body) (guard (valid-bindings? bindings))
+     (let ((vals (map retrans (map cadr bindings))))
+       (call-with-ghil-bindings e (map car bindings)
+         (lambda (vars)
+           (make-ghil-bind e l vars vals (trans-body e l body)))))))
+
+   (let*
+    ;; (let* ((SYM VAL) ...) BODY...)
+    ((() . ,body)
+     (retrans `(let () ,@body)))
+    ((((,sym ,val) . ,rest) . ,body) (guard (symbol? sym))
+     (retrans `(let ((,sym ,val)) (let* ,rest ,@body)))))
+
+   (letrec
+    ;; (letrec ((SYM VAL) ...) BODY...)
+    ((,bindings . ,body) (guard (valid-bindings? bindings))
+     (call-with-ghil-bindings e (map car bindings)
+        (lambda (vars)
+          (let ((vals (map retrans (map cadr bindings))))
+            (make-ghil-bind e l vars vals (trans-body e l body)))))))
+
+   (cond
+    ;; (cond (CLAUSE BODY...) ...)
+    (() (retrans '(begin)))
+    (((else . ,body)) (retrans `(begin ,@body)))
+    (((,test) . ,rest) (retrans `(or ,test (cond ,@rest))))
+    (((,test => ,proc) . ,rest)
+     ;; FIXME hygiene!
+     (retrans `(let ((_t ,test)) (if _t (,proc _t) (cond ,@rest)))))
+    (((,test . ,body) . ,rest)
+     (retrans `(if ,test (begin ,@body) (cond ,@rest)))))
+
+   (case
+    ;; (case EXP ((KEY...) BODY...) ...)
+    ((,exp . ,clauses)
+     (retrans
+      ;; FIXME hygiene!
+      `(let ((_t ,exp))
+         ,(let loop ((ls clauses))
+            (cond ((null? ls) '(begin))
+                  ((eq? (caar ls) 'else) `(begin ,@(cdar ls)))
+                  (else `(if (memv _t ',(caar ls))
+                             (begin ,@(cdar ls))
+                             ,(loop (cdr ls))))))))))
+
+    (do
+     ;; (do ((SYM VAL [UPDATE]) ...) (TEST RESULT...) BODY...)
+     ((,bindings (,test . ,result) . ,body)
+      (let ((sym (map car bindings))
+            (val (map cadr bindings))
+            (update (map cddr bindings)))
+        (define (next s x) (if (pair? x) (car x) s))
+        (retrans
+         ;; FIXME hygiene!
+         `(letrec ((_l (lambda ,sym
+                         (if ,test
+                             (begin ,@result)
+                             (begin ,@body
+                                    (_l ,@(map next sym update)))))))
+            (_l ,@val))))))
+
+    (lambda
+     ;; (lambda FORMALS BODY...)
+     ((,formals . ,body)
+      (receive (syms rest) (parse-formals formals)
+        (call-with-ghil-environment e syms
+          (lambda (env vars)
+            (receive (meta body) (parse-lambda-meta body)
+              (make-ghil-lambda env l vars rest meta
+                                (trans-body env l body))))))))
+
+    (eval-case
+     (,clauses
+      (retrans
+       `(begin
+          ;; Compilation of toplevel units is always wrapped in a lambda
+          ,@(let ((toplevel? (ghil-toplevel-env? (ghil-env-parent e))))
+              (let loop ((seen '()) (in clauses) (runtime '()))
+                (cond
+                 ((null? in) runtime)
+                 (else
+                  (pmatch (car in)
+                   ((else . ,body)
+                    (if (and toplevel? (not (memq *the-compile-toplevel-symbol* seen)))
+                        (primitive-eval `(begin ,@body)))
+                    (if (memq (if toplevel? *the-compile-toplevel-symbol* 'evaluate) seen)
+                        runtime
+                        body))
+                   ((,keys . ,body) (guard (list? keys) (and-map symbol? keys))
+                    (for-each (lambda (k)
+                                (if (memq k seen)
+                                    (syntax-error l "eval-case condition seen twice" k)))
+                              keys)
+                    (if (and toplevel? (memq *the-compile-toplevel-symbol* keys))
+                        (primitive-eval `(begin ,@body)))
+                    (loop (append keys seen)
+                          (cdr in)
+                          (if (memq (if toplevel? 'load-toplevel 'evaluate) keys)
+                              (append runtime body)
+                              runtime)))
+                   (else (syntax-error l "bad eval-case clause" (car in))))))))))))
+
+    ;; FIXME: make this actually do something
+    (start-stack
+     ((,tag ,expr) (retrans expr)))
+
+    ;; FIXME: not hygienic, relies on @apply not being shadowed
+    (apply
+     (,args (retrans `(@apply ,@args))))
+
+    (@apply
+     ((,proc ,arg1 . ,args)
+      (let ((args (cons (retrans arg1) (map retrans args))))
+        (cond ((and (symbol? proc)
+                    (not (ghil-lookup e proc #f))
+                    (and=> (module-variable (current-module) proc)
+                           (lambda (var)
+                             (and (variable-bound? var)
+                                  (lookup-apply-transformer (variable-ref var))))))
+               ;; that is, a variable, not part of this compilation
+               ;; unit, but defined in the toplevel environment, and has
+               ;; an apply transformer registered
+               => (lambda (t) (t e l args)))
+              (else (make-ghil-inline e l 'apply
+                                      (cons (retrans proc) args)))))))
+
+    ;; FIXME: not hygienic, relies on @call-with-values not being shadowed
+    (call-with-values
+     ((,producer ,consumer)
+      (retrans `(@call-with-values ,producer ,consumer)))
+     (else #f))
+
+    (@call-with-values
+     ((,producer ,consumer)
+      (make-ghil-mv-call e l (retrans producer) (retrans consumer))))
+
+    ;; FIXME: not hygienic, relies on @call-with-current-continuation
+    ;; not being shadowed
+    (call-with-current-continuation
+     ((,proc)
+      (retrans `(@call-with-current-continuation ,proc)))
+     (else #f))
+
+    (@call-with-current-continuation
+     ((,proc)
+      (make-ghil-inline e l 'call/cc (list (retrans proc)))))
+
+    (receive
+     ((,formals ,producer-exp . ,body)
+      ;; Lovely, self-referential usage. Not strictly necessary, the
+      ;; macro would do the trick; but it's good to test the mv-bind
+      ;; code.
+      (receive (syms rest) (parse-formals formals)
+        (call-with-ghil-bindings e syms
+          (lambda (vars)
+            (make-ghil-mv-bind e l (retrans `(lambda () ,producer-exp))
+                               vars rest (trans-body e l body)))))))
+
+    (values
+     ((,x) (retrans x))
+     (,args (make-ghil-values e l (map retrans args))))))
+
+(define (lookup-apply-transformer proc)
+  (cond ((eq? proc values)
+         (lambda (e l args)
+           (make-ghil-values* e l args)))
+        (else #f)))
+
+(define (trans-quasiquote e l x level)
+  (cond ((not (pair? x)) x)
+       ((memq (car x) '(unquote unquote-splicing))
+        (let ((l (location x)))
+          (pmatch (cdr x)
+            ((,obj)
+              (cond
+               ((zero? level) 
+                (if (eq? (car x) 'unquote)
+                    (make-ghil-unquote e l (trans e l obj))
+                    (make-ghil-unquote-splicing e l (trans e l obj))))
+               (else
+                (list (car x) (trans-quasiquote e l obj (1- level))))))
+            (else (syntax-error l (format #f "bad ~A" (car x)) x)))))
+        ((eq? (car x) 'quasiquote)
+        (let ((l (location x)))
+          (pmatch (cdr x)
+            ((,obj) (list 'quasiquote (trans-quasiquote e l obj (1+ level))))
+             (else (syntax-error l (format #f "bad ~A" (car x)) x)))))
+       (else (cons (trans-quasiquote e l (car x) level)
+                   (trans-quasiquote e l (cdr x) level)))))
+
+(define (trans-body e l body)
+  (define (define->binding df)
+    (pmatch (cdr df)
+      ((,name ,val) (guard (symbol? name)) (list name val))
+      (((,name . ,formals) . ,body) (guard (symbol? name))
+       (list name `(lambda ,formals ,@body)))
+      (else (syntax-error (location df) "bad define" df))))
+  ;; main
+  (let loop ((ls body) (ds '()))
+    (pmatch ls
+      (() (syntax-error l "bad body" body))
+      (((define . _) . _)
+       (loop (cdr ls) (cons (car ls) ds)))
+      (else
+       (if (null? ds)
+           (trans e l `(begin ,@ls))
+           (trans e l `(letrec ,(map define->binding ds) ,@ls)))))))
+
+(define (parse-formals formals)
+  (cond
+   ;; (lambda x ...)
+   ((symbol? formals) (values (list formals) #t))
+   ;; (lambda (x y z) ...)
+   ((list? formals) (values formals #f))
+   ;; (lambda (x y . z) ...)
+   ((pair? formals)
+    (let loop ((l formals) (v '()))
+      (if (pair? l)
+         (loop (cdr l) (cons (car l) v))
+         (values (reverse! (cons l v)) #t))))
+   (else (syntax-error (location formals) "bad formals" formals))))
+
+(define (parse-lambda-meta body)
+  (cond ((or (null? body) (null? (cdr body))) (values '() body))
+        ((string? (car body))
+         (values `((documentation . ,(car body))) (cdr body)))
+        (else (values '() body))))
+
+(define (maybe-name-value! val name)
+  (cond
+   ((ghil-lambda? val)
+    (if (not (assq-ref (ghil-lambda-meta val) 'name))
+        (set! (ghil-lambda-meta val)
+              (acons 'name name (ghil-lambda-meta val))))))
+  val)
+
+(define (location x)
+  (and (pair? x)
+       (let ((props (source-properties x)))
+        (and (not (null? props))
+             (vector (assq-ref props 'line)
+                      (assq-ref props 'column)
+                      (assq-ref props 'filename))))))