Modified the Parenscript macro facilities so that defpsmacro and
authorVladimir Sedach <vsedach@gmail.com>
Sun, 1 Feb 2009 07:18:34 +0000 (00:18 -0700)
committerVladimir Sedach <vsedach@gmail.com>
Sun, 1 Feb 2009 07:18:34 +0000 (00:18 -0700)
define-ps-symbol-macro define their macros in their lexical
environment (previously they were always defining in the null lexical
environment).

This cleared up the implementation of the macro facilities, and now
defmacro and define-symbol-macro explicitly define macros in the null
lexical environment inside Parenscript code (the behavior is unchanged
and the only one that makes sense since Parenscript code is translated
and not evaluated, but previously this was not obvious from looking at
the implementation).

docs/reference.lisp
src/compiler.lisp
src/special-forms.lisp
t/ps-tests.lisp

index 5861157..3865afb 100644 (file)
@@ -1105,12 +1105,20 @@ a-variable  => aVariable
 ;;;t \index{macro}
 ;;;t \index{macrology}
 ;;;t \index{DEFPSMACRO}
+;;;t \index{DEFMACRO/PS}
+;;;t \index{DEFMACRO+PS}
+;;;t \index{DEFINE-PS-SYMBOL-MACRO}
+;;;t \index{IMPORT-MACROS-FROM-LISP}
 ;;;t \index{MACROLET}
 ;;;t \index{SYMBOL-MACROLET}
 ;;;t \index{PS-GENSYM}
 ;;;t \index{compiler}
 
 ; (DEFPSMACRO name lambda-list macro-body)
+; (DEFPSMACRO/PS name lambda-list macro-body)
+; (DEFPSMACRO+PS name lambda-list macro-body)
+; (DEFINE-PS-SYMBOL-MACRO symbol expansion)
+; (IMPORT-MACROS-FROM-LISP symbol*)
 ; (MACROLET ({name lambda-list macro-body}*) body)
 ; (SYMBOL-MACROLET ({name macro-body}*) body)
 ; (PS-GENSYM {string})
@@ -1152,7 +1160,10 @@ a-variable  => aVariable
 
 ;;; Macros can be defined in Parenscript code itself (as opposed to
 ;;; from Lisp) by using the Parenscript `MACROLET' and `DEFMACRO'
-;;; forms.
+;;; forms. Note that macros defined this way are defined in a null
+;;; lexical environment (ex - (let ((x 1)) (defmacro baz (y) `(+ ,y
+;;; ,x))) will not work), since the surrounding Parenscript code is
+;;; just translated to JavaScript and not actually evaluated.
 
 ;;; Parenscript also supports the use of macros defined in the
 ;;; underlying Lisp environment. Existing Lisp macros can be imported
@@ -1175,16 +1186,16 @@ a-variable  => aVariable
 ;;; used by Parenscript.
 
 ;;; Parenscript also supports symbol macros, which can be introduced
-;;; using the Parenscript form `SYMBOL-MACROLET'.For example, the
-;;; Parenscript `WITH-SLOTS' is implemented using symbol macros.
+;;; using the Parenscript form `SYMBOL-MACROLET' or defined in Lisp
+;;; with `DEFINE-PS-SYMBOL-MACRO'. For example, the Parenscript
+;;; `WITH-SLOTS' is implemented using symbol macros.
 
-(defjsmacro with-slots (slots object &rest body)
+(defpsmacro with-slots (slots object &rest body)
   `(symbol-macrolet ,(mapcar #'(lambda (slot)
                                  `(,slot '(slot-value ,object ',slot)))
                              slots)
     ,@body))
 
-
 ;;;# The Parenscript namespace system
 ;;;t \index{package}
 ;;;t \index{namespace}
index 77a6815..a41366c 100644 (file)
@@ -125,32 +125,25 @@ function and the parent macro environment of the macro."
       (lookup-macro-spec name environment)
     (values (cdr macro-spec) parent-env)))
 
-(eval-when (:compile-toplevel :load-toplevel :execute)
-  (defun make-ps-macro-function (args body)
-    (let* ((whole-var (when (eql '&whole (first args)) (second args)))
-           (effective-lambda-list (if whole-var (cddr args) args))
-           (form-arg (or whole-var (gensym "ps-macro-form-arg-")))
-           (body (if (and (cdr body) (stringp (first body))) (rest body) body))) ;; drop docstring
-      (compile nil `(lambda (,form-arg)
-                     (destructuring-bind ,effective-lambda-list
-                         (cdr ,form-arg)
-                       ,@body)))))
-      
-  (defun define-ps-macro% (name args body &key symbol-macro-p)
-    (undefine-ps-special-form name)
-    (setf (get-macro-spec name *ps-macro-toplevel*)
-          (cons symbol-macro-p (make-ps-macro-function args body)))
-    nil))
+(defun make-ps-macro-function (args body)
+  (let* ((whole-var (when (eql '&whole (first args)) (second args)))
+         (effective-lambda-list (if whole-var (cddr args) args))
+         (whole-arg (or whole-var (gensym "ps-macro-form-arg-"))))
+    `(lambda (,whole-arg)
+       (destructuring-bind ,effective-lambda-list
+           (cdr ,whole-arg)
+         ,@body))))
 
 (defmacro defpsmacro (name args &body body)
-  "Define a ParenScript macro, and store it in the toplevel ParenScript
-macro environment."
-  `(define-ps-macro% ',name ',args ',body :symbol-macro-p nil))
+  `(progn (undefine-ps-special-form ',name)
+          (setf (get-macro-spec ',name *ps-macro-toplevel*)
+                (cons nil ,(make-ps-macro-function args body)))
+          ',name))
 
-(defmacro define-ps-symbol-macro (name &body body)
-  "Define a ParenScript symbol macro, and store it in the toplevel ParenScript
-macro environment.  BODY is a Lisp form that should return a ParenScript form."
-  `(define-ps-macro% ',name () ',body :symbol-macro-p t))
+(defmacro define-ps-symbol-macro (symbol expansion)
+  `(progn (undefine-ps-special-form ',symbol)
+          (setf (get-macro-spec ',symbol *ps-macro-toplevel*) (cons t (lambda () ',expansion)))
+          ',symbol))
 
 (defun import-macros-from-lisp (&rest names)
   "Import the named Lisp macros into the ParenScript macro
@@ -158,21 +151,20 @@ environment. When the imported macro is macroexpanded by ParenScript,
 it is first fully macroexpanded in the Lisp macro environment, and
 then that expansion is further expanded by ParenScript."
   (dolist (name names)
-    (define-ps-macro% name '(&rest args)
-      (list `(common-lisp:macroexpand `(,',name ,@args)))
-      :symbol-macro-p nil)))
+    (eval `(defpsmacro ,name (&rest args)
+             (macroexpand `(,',name ,@args))))))
 
 (defmacro defmacro/ps (name args &body body)
   "Define a Lisp macro and import it into the ParenScript macro environment."
   `(progn (defmacro ,name ,args ,@body)
-          (ps:import-macros-from-lisp ',name)))
+          (import-macros-from-lisp ',name)))
 
 (defmacro defmacro+ps (name args &body body)
-  "Define a Lisp macro and a ParenScript macro in their respective
-macro environments. This function should be used when you want to use
-the same macro in both Lisp and ParenScript, but the 'macroexpand' of
-that macro in Lisp makes the Lisp macro unsuitable to be imported into
-the ParenScript macro environment."
+  "Define a Lisp macro and a ParenScript macro with the same macro
+function (ie - the same result from macroexpand-1), for cases when the
+two have different full macroexpansions (for example if the CL macro
+contains implementation-specific code when macroexpanded fully in the
+CL environment)."
   `(progn (defmacro ,name ,args ,@body)
           (defpsmacro ,name ,args ,@body)))
 
@@ -187,7 +179,7 @@ whether any expansion was performed on the form or not."
                                          nil))
               ((ps-macro-p op) (values (ps-macroexpand (funcall (lookup-macro-expansion-function op) form)) t))
               (t (values form nil))))
-      (cond ((ps-symbol-macro-p form) (values (ps-macroexpand (funcall (lookup-macro-expansion-function form) (list form))) t))
+      (cond ((ps-symbol-macro-p form) (values (ps-macroexpand (funcall (lookup-macro-expansion-function form))) t))
             (t (values form nil)))))
 
 ;;;; compiler interface
index f410e11..2e28c50 100644 (file)
@@ -430,7 +430,7 @@ lambda-list::=
       (destructuring-bind (name arglist &body body)
           macro
         (setf (get-macro-spec name macro-env-dict)
-              (cons nil (make-ps-macro-function arglist body)))))
+              (cons nil (eval (make-ps-macro-function arglist body))))))
     (compile-parenscript-form `(progn ,@body))))
 
 (define-ps-special-form symbol-macrolet (expecting symbol-macros &body body)
@@ -440,17 +440,17 @@ lambda-list::=
       (destructuring-bind (name expansion)
           macro
         (setf (get-macro-spec name macro-env-dict)
-              (cons t (make-ps-macro-function () (list `',expansion))))))
+              (cons t (lambda () expansion)))))
     (compile-parenscript-form `(progn ,@body))))
 
 (define-ps-special-form defmacro (expecting name args &body body)
   (declare (ignore expecting))
-  (define-ps-macro% name args body :symbol-macro-p nil)
+  (eval `(defpsmacro ,name ,args ,@body))
   nil)
 
 (define-ps-special-form define-symbol-macro (expecting name expansion)
   (declare (ignore expecting))
-  (define-ps-macro% name () (list `',expansion) :symbol-macro-p t)
+  (eval `(define-ps-symbol-macro ,name ,expansion))
   nil)
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
index 1dbe3ff..1a4ea82 100644 (file)
@@ -489,6 +489,13 @@ x = 2 + sideEffect() + x + 5;")
 };
 "))))
 
+(test macro-environment2
+  (is (string= (normalize-js-code (let ((outer-lexical-variable 1))
+                                    (defpsmacro macro-environment2-macro (x)
+                                      `(+ ,outer-lexical-variable ,x))
+                                    (ps* '(macro-environment2-macro 2))))
+               (normalize-js-code "1 + 2;"))))
+
 (test-ps-js ampersand-whole-1
   (macrolet ((foo (&whole foo bar baz)
                (declare (ignore bar baz))