Filtering...

defmac

books/misc/defmac

Included Books

top
other
(in-package "ACL2")
include-book
(include-book "xdoc/top" :dir :system)
other
(defxdoc defmac
  :parents (defmacro)
  :short "Define a macro that expands efficiently."
  :long "<p>Example forms</p>

@({
  (include-book "misc/defmac" :dir :system)

  (defmac my-xor (x y)
    (list 'if x (list 'not y) y))

  (defmac my-mac (x &optional (y '3 y-p))
    `(list ,x ,y ,y-p))

  (defmac one-of (x &rest rst)
    :macro-fn one-of-function
    "stubbed-out :doc."
    (declare (xargs :guard (symbol-listp rst)))
    (cond ((null rst) nil)
          (t (list 'or
                   (list 'eq x (list 'quote (car rst)))
                   (list* 'one-of x (cdr rst))))))~/
})

<p>General Form:</p>

@({
  (defmac name macro-args
          :macro-fn name-macro-fn ; optional
          doc-string              ; optional
          dcl ... dcl             ; optional
          body)
})

<p>where @('name') is a new symbolic name (see @(see name)), @(see macro-args)
specifies the formals of the macro (see @(see macro-args) for a description),
and @('body') is a term.  @('Doc-string') is an optional @(see documentation)
string, which (as for @(tsee defmacro)) is essentially ignored by ACL2.  Each
@('dcl') is an optional declaration as for @(see defun) (see @(see
declare)).</p>

<p>See @(see defmacro) for a discussion of @('defmacro'), which is the
traditional way of introducing macros.  @('Defmac') is similar to @('defmacro')
except that the resulting macro may execute significantly more efficiently, as
explained below.  You can use @('defmac') just as you would normally use
@('defmacro'), though your @('defmac') form should include the declaration
@('(declare (xargs :mode :program))') to be truly compatible with @('defmacro'),
which allows calls of @(':')@(see program) mode functions in its body.</p>

<p>A @('defmac') form generates the following form, which introduces a @(see
defun) and a @(see defmacro).  Here we refer to the ``General Form'' above;
hence the @(':macro-fn'), @('doc-string'), and each @('dcl') are optional.  The
@('doc-string') is as specified for @(see defmacro), and each @('dcl') is as
specified for @(see defun).  @(':Macro-fn') specifies @('name-macro-fn') (used
below) as illustrated above, but if @(':macro-fn') is not specified then
@('name-macro-fn') is obtained by adding the suffix @('"-MACRO-FN"') to the
@(see symbol-name) of @('name') to get a symbol in the same package as
@('name').  The list @('(v1 ... vk)') enumerates all the names introduced in
@('macro-args').</p>

@({
  (progn
    (defun name-macro-fn (v1 ... vk)
      dcl ... dcl
      body)
    (defmacro name macro-args
      doc-string
      (name-macro-fn v1 ... vk))
    )
})

<p>The reason for introducing a @('defun') is efficiency.  ACL2 expands a macro
call by running its own evaluator on the body of the macro, and this can be
relatively slow if that body is large.  But with @('defmac'), the evaluator
call reduces quickly to a single raw Lisp call of the (executable counterpart
of) the auxiliary function on the actuals of the macro.</p>")
defmac-fnfunction
(defun defmac-fn
  (mdef)
  (declare (xargs :mode :program))
  (let ((ctx (cons 'defmac (car mdef))))
    (mv-let (macro-fn mdef)
      (cond ((and (true-listp mdef)
           (<= 4 (length mdef))
           (eq (nth 2 mdef) :macro-fn)) (mv (nth 3 mdef)
            (list* (nth 0 mdef) (nth 1 mdef) (nthcdr 4 mdef))))
        (t (mv nil mdef)))
      (mv-let (er-string four)
        (chk-defmacro-width mdef)
        (cond (er-string (list 'er 'soft (kwote ctx) (kwote er-string) (kwote four)))
          (t (let ((name (car four)) (args (cadr four))
                (dcls (caddr four))
                (body (cadddr four)))
              (cond ((or (not (symbolp name)) (keywordp name)) `(er soft
                    ',CTX
                    "Names of macros must be non-keyword symbols.  The name ~x0 ~
                  is thus illegal."
                    ',NAME))
                (t (let ((msg (chk-macro-arglist-msg args nil nil)))
                    (cond (msg `(er soft ',CTX "~@0" ',MSG))
                      (t (let ((doc (if (stringp (car dcls))
                               (car dcls)
                               nil)) (dcls (if (stringp (car dcls))
                                (cdr dcls)
                                dcls))
                            (macro-fn (or macro-fn
                                (intern-in-package-of-symbol (concatenate 'string (symbol-name name) "-MACRO-FN")
                                  name)))
                            (formals (macro-vars args)))
                          `(progn (defun ,MACRO-FN ,FORMALS ,@DCLS ,BODY)
                            (defmacro ,NAME
                              ,ARGS
                              ,@(AND DOC (LIST DOC))
                              (,MACRO-FN ,@FORMALS))))))))))))))))
defmacmacro
(defmacro defmac (&rest mdef) (defmac-fn mdef))
local
(local (encapsulate nil
    (defmac one-of
      (x &rest rst)
      :macro-fn one-of-function
      "
     stubbed-out :doc~/

     ~/~/"
      (declare (xargs :guard (symbol-listp rst)))
      (cond ((null rst) nil)
        (t (list 'or
            (list 'eq x (list 'quote (car rst)))
            (list* 'one-of x (cdr rst))))))
    (defun one-of-test-fn (a) (one-of a u v w))
    (defthm one-of-test-thm
      (iff (one-of-test-fn a) (member a '(u v w))))))