1.0.48.28: make TRULY-THE macroexpandable
[sbcl.git] / src / code / macroexpand.lisp
index b1049bd..cb60c38 100644 (file)
@@ -15,7 +15,7 @@
 
 (defun sb!xc:special-operator-p (symbol)
   #!+sb-doc
-  "If the symbol globally names a special form, returns T, otherwise NIL."
+  "If the symbol globally names a special form, return T, otherwise NIL."
   (declare (symbol symbol))
   (eq (info :function :kind symbol) :special-form))
 
   whenever a runtime expansion is needed. Initially this is set to
   FUNCALL.")
 
-(declaim (ftype (function (t &optional (or null sb!c::lexenv))) sb!xc:macroexpand-1))
 (defun sb!xc:macroexpand-1 (form &optional env)
   #!+sb-doc
-  "If form is a macro (or symbol macro), expands it once. Returns two values,
+  "If form is a macro (or symbol macro), expand it once. Return two values,
    the expanded form and a T-or-NIL flag indicating whether the form was, in
-   fact, a macro. Env is the lexical environment to expand in, which defaults
+   fact, a macro. ENV is the lexical environment to expand in, which defaults
    to the null environment."
   (cond ((and (consp form) (symbolp (car form)))
-        (let ((def (sb!xc:macro-function (car form) env)))
-          (if def
-              (values (funcall sb!xc:*macroexpand-hook*
-                               def
-                               form
-                               ;; As far as I can tell, it's not clear from
-                               ;; the ANSI spec whether a MACRO-FUNCTION
-                               ;; function needs to be prepared to handle
-                               ;; NIL as a lexical environment. CMU CL
-                               ;; passed NIL through to the MACRO-FUNCTION
-                               ;; function, but I prefer SBCL "be conservative
-                               ;; in what it sends and liberal in what it
-                               ;; accepts" by doing the defaulting itself.
-                               ;; -- WHN 19991128
-                               (coerce-to-lexenv env))
-                      t)
-              (values form nil))))
-       ((symbolp form)
-        (let* ((venv (when env (sb!c::lexenv-vars env)))
-               (local-def (cdr (assoc form venv))))
-          (if (and (consp local-def)
-                   (eq (car local-def) 'macro))
-              (values (cdr local-def) t)
-              (values form nil))))
-       (t
-        (values form nil))))
+         (let ((def (sb!xc:macro-function (car form) env)))
+           (if def
+               (values (funcall sb!xc:*macroexpand-hook*
+                                def
+                                form
+                                ;; As far as I can tell, it's not clear from
+                                ;; the ANSI spec whether a MACRO-FUNCTION
+                                ;; function needs to be prepared to handle
+                                ;; NIL as a lexical environment. CMU CL
+                                ;; passed NIL through to the MACRO-FUNCTION
+                                ;; function, but I prefer SBCL "be conservative
+                                ;; in what it sends and liberal in what it
+                                ;; accepts" by doing the defaulting itself.
+                                ;; -- WHN 19991128
+                                (coerce-to-lexenv env))
+                       t)
+               (values form nil))))
+        ((symbolp form)
+         (flet ((perform-symbol-expansion (symbol expansion)
+                  ;; CLHS 3.1.2.1.1 specifies that symbol-macros are expanded
+                  ;; via the macroexpand hook, too.
+                  (funcall sb!xc:*macroexpand-hook*
+                           (constantly expansion)
+                           symbol
+                           env)))
+           (let* ((venv (when env (sb!c::lexenv-vars env)))
+                  (local-def (cdr (assoc form venv))))
+             (cond ((and (consp local-def)
+                         (eq (car local-def) 'macro))
+                    (values (perform-symbol-expansion form (cdr local-def)) t))
+                   (local-def
+                    (values form nil))
+                   ((eq (info :variable :kind form) :macro)
+                    (let ((expansion (info :variable :macro-expansion form)))
+                      (values (perform-symbol-expansion form expansion) t)))
+                   (t
+                    (values form nil))))))
+        (t
+         (values form nil))))
 
-(declaim (ftype (function (t &optional (or null sb!c::lexenv))) sb!xc:macroexpand))
 (defun sb!xc:macroexpand (form &optional env)
   #!+sb-doc
   "Repetitively call MACROEXPAND-1 until the form can no longer be expanded.
    lexical environment to expand in, or NIL (the default) for the null
    environment."
   (labels ((frob (form expanded)
-            (multiple-value-bind (new-form newly-expanded-p)
-                (sb!xc:macroexpand-1 form env)
-              (if newly-expanded-p
-                  (frob new-form t)
-                  (values new-form expanded)))))
+             (multiple-value-bind (new-form newly-expanded-p)
+                 (sb!xc:macroexpand-1 form env)
+               (if newly-expanded-p
+                   (frob new-form t)
+                   (values new-form expanded)))))
+    (frob form nil)))
+
+;;; Like MACROEXPAND-1, but takes care not to expand special forms.
+(defun %macroexpand-1 (form &optional env)
+  (if (or (atom form)
+          (let ((op (car form)))
+            (not (and (symbolp op) (sb!xc:special-operator-p op)))))
+      (sb!xc:macroexpand-1 form env)
+      (values form nil)))
+
+;;; Like MACROEXPAND, but takes care not to expand special forms.
+(defun %macroexpand (form &optional env)
+  (labels ((frob (form expanded)
+             (multiple-value-bind (new-form newly-expanded-p)
+                 (%macroexpand-1 form env)
+               (if newly-expanded-p
+                   (frob new-form t)
+                   (values new-form expanded)))))
     (frob form nil)))