A kind of eval for use at macro-expansion time (such as for constant-folding purposes). Why?

  1. The standard provides constantp, which may take an environment. But eval takes no environment, always using the global environment.
  2. The value of a constant variable isn't necessarily available at the time constantp returns true (such as in the compilation environment).
  3. If we are passing information via symbol macros (e.g. doing compiler-let-like tricks) then we'd like an easy way to get those values.
Thus macro-eval attempts to reduce a form to a constant value (relative to the given environment) while expanding macro forms. Then the secondary value is T if the form was actually evaluated. This is easy for things that are self-evaluating (predicate self-evaluating-p), or macros that expand to such forms. The more difficult question would be how to recognize things like (sqrt pi) as constant, without duplicating too much of the functionality of the compiler itself. That's almost getting into code walker territory.

(defun quoted-form-p (form) "Return true if FORM is a QUOTE special form." (typep form '(cons (eql quote) (cons t null)))) (defun self-evaluating-p (form) "Return true if FORM evaluates to itself (in any environment)." (typep form '(and atom (or null (eql t) keyword (not symbol))))) (defun constant-form-p (form) "Return true if FORM is a (bound) constant variable." (and (symbolp form) (constantp form) (boundp form))) (defun macro-eval (form &optional env) (prog ((expansion form) expanded-p) macro-eval-1 (when (self-evaluating-p expansion) (return (values expansion t))) (when (quoted-form-p expansion) (return (values (cadr expansion) t))) (multiple-value-setq (expansion expanded-p) (macroexpand-1 expansion env)) (when expanded-p (go macro-eval-1)) (return (if (constant-form-p expansion) ;; (1) We have a constant variable, and ;; (2) the value is actually available. (values (symbol-value expansion) t) ;; No evaluation. (values form nil)))))

Note that constantp is "not permitted" to expand compiler macros, but macro-eval could be extended to do this.

Related reading