remove define-private
[bpt/guile.git] / module / language / scheme / translate.scm
index b4fb5e3..afa2276 100644 (file)
@@ -6,12 +6,12 @@
 ;; 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,
 ;;; 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 match)
   :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)
-  (call-with-ghil-environment (make-ghil-mod e) '()
-    (lambda (env vars)
-      (<ghil-lambda> env #f vars #f (trans env #f x)))))
+  (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
+(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 (macroexpand 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))))
+              (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)
-            (<ghil-ref> e l (ghil-lookup e y))
-            (trans e l y))))
-       (else (<ghil-quote> e l x))))
+              (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)))
          `(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) (<ghil-void> e l))
+  (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)
-     (match tail
+     (pmatch tail
        (() (make:void))
        (else (bad-syntax))))
 
     ;; (quote OBJ)
     ((quote)
-     (match tail
-       ((obj) (<ghil-quote> e l obj))
+     (pmatch tail
+       ((,obj) (make-ghil-quote e l obj))
        (else (bad-syntax))))
 
     ;; (quasiquote OBJ)
     ((quasiquote)
-     (match tail
-       ((obj) (<ghil-quasiquote> e l (trans-quasiquote e l obj)))
+     (pmatch tail
+       ((,obj) (make-ghil-quasiquote e l (trans-quasiquote e l obj)))
        (else (bad-syntax))))
 
-    ((define define-private)
-     (match tail
+    ((define)
+     (pmatch tail
        ;; (define NAME VAL)
-       (((? symbol? name) val)
-       (<ghil-define> e l (ghil-lookup e name) (trans:x val)))
+       ((,name ,val) (guard (symbol? name))
+        (make-ghil-define e l (ghil-lookup e name) (trans:x val)))
 
        ;; (define (NAME FORMALS...) BODY...)
-       ((((? symbol? name) . formals) . body)
-       ;; -> (define NAME (lambda FORMALS BODY...))
-       (let ((val (trans:x `(lambda ,formals ,@body))))
-         (<ghil-define> e l (ghil-lookup e name) val)))
+       (((,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!)
-     (match tail
+     (pmatch tail
        ;; (set! NAME VAL)
-       (((? symbol? name) val)
-       (<ghil-set> e l (ghil-lookup e name) (trans:x val)))
+       ((,name ,val) (guard (symbol? name))
+        (make-ghil-set e l (ghil-lookup e name) (trans:x val)))
 
        ;; (set! (NAME ARGS...) VAL)
-       ((((? symbol? name) . args) val)
-       ;; -> ((setter NAME) ARGS... VAL)
-       (trans:pair `((setter ,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)
-     (match tail
-       ((test then)
-       (<ghil-if> e l (trans:x test) (trans:x then) (make:void)))
-       ((test then else)
-       (<ghil-if> e l (trans:x test) (trans:x then) (trans:x else)))
+     (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)
-     (<ghil-and> e l (map trans:x tail)))
+     (make-ghil-and e l (map trans:x tail)))
 
     ;; (or EXPS...)
     ((or)
-     (<ghil-or> e l (map trans:x tail)))
+     (make-ghil-or e l (map trans:x tail)))
 
     ;; (begin EXPS...)
     ((begin)
-     (<ghil-begin> e l (map trans:x tail)))
+     (make-ghil-begin e l (map trans:x tail)))
 
     ((let)
-     (match tail
+     (pmatch tail
        ;; (let NAME ((SYM VAL) ...) BODY...)
-       (((? symbol? name) (((? symbol? sym) val) ...) body ...)
-       ;; -> (letrec ((NAME (lambda (SYM...) BODY...))) (NAME VAL...))
-       (trans:pair `(letrec ((,name (lambda ,sym ,@body))) (,name ,@val))))
+       ((,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'
-       (<ghil-begin> e l (list (trans:body body))))
+       ((() . ,body)
+        ;; Note: this differs from `begin'
+        (make-ghil-begin e l (list (trans:body body))))
 
        ;; (let ((SYM VAL) ...) BODY...)
-       (((((? symbol? sym) val) ...) body ...)
-       (let ((vals (map trans:x val)))
-         (call-with-ghil-bindings e sym
-           (lambda (vars)
-             (<ghil-bind> e l vars vals (trans:body 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*)
-     (match tail
-       (((def ...) body ...)
-       (if (null? def)
-           (trans:pair `(let () ,@body))
-           (trans:pair `(let (,(car def)) (let* ,(cdr def) ,@body)))))
+     (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)
-     (match tail
-       (((((? symbol? sym) val) ...) body ...)
-       (call-with-ghil-bindings e sym
-         (lambda (vars)
-           (let ((vals (map trans:x val)))
-             (<ghil-bind> e l vars vals (trans:body body))))))
+     (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)
-     (match tail
+     (pmatch tail
        (() (make:void))
-       ((('else . body)) (trans:body body))
-       (((test) . rest) (trans:pair `(or ,test (cond ,@rest))))
-       (((test '=> proc) . rest)
-       (trans:pair `(let ((_t ,test)) (if _t (,proc _t) (cond ,@rest)))))
-       (((test . body) . rest)
-       (trans:pair `(if ,test (begin ,@body) (cond ,@rest))))
+       (((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)
-     (match tail
-       ((exp . clauses)
-       (trans:pair
-        `(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)))))))))
+     (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)
-     (let ()
-       (define (next s x) (if (pair? x) (car x) s))
-       (match tail
-        ((((sym init . update) ...) (test . result) body ...)
-         (trans:pair
-          `(letrec ((_l (lambda ,sym
-                          (if ,test
-                              (let () (void) ,@result)
-                              (let () (void) ,@body
-                                   (_l ,@(map next sym update)))))))
-             (_l ,@init))))
-        (else (bad-syntax)))))
+     (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)
-     (match tail
-       ((formals body ...)
-       (receive (syms rest) (parse-formals formals)
-         (call-with-ghil-environment e syms
-           (lambda (env vars)
-             (<ghil-lambda> env l vars rest (trans-body env l body))))))
+     (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))
-       (match x
-        (() (make:void))
-        ((('else . body)) (trans:pair `(begin ,@body)))
-        (((((? symbol? key) ...) body ...) rest ...)
-         (if (memq 'load-toplevel key)
-             (begin
-               (primitive-eval `(begin ,@(copy-tree body)))
-               (trans:pair `(begin ,@body)))
-             (loop rest)))
-        (else (bad-syntax)))))
+       (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)
-       (<ghil-inline> e l head (map trans:x tail))
-       (<ghil-call> e l (trans:x head) (map trans:x tail))))))
+     (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)))
-          (match (cdr x)
-            ((obj)
+          (pmatch (cdr x)
+            ((,obj)
              (if (eq? (car x) 'unquote)
-                 (<ghil-unquote> e l (trans e l obj))
-                 (<ghil-unquote-splicing> e l (trans e l obj))))
+                 (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)
-    (match (cdr df)
-      (((? symbol? name) val) (list name val))
-      ((((? symbol? name) . formals) . body)
+    (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)))))))
+    (pmatch ls
+      (() (syntax-error l "bad body" body))
+      (((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