Brighid McDonnell
Brighid McDonnell

Reputation: 4343

How can I destructure an &rest argument of varying length in my elisp macro?

I have a program that takes as inputs a chunk of data and a list of rules, applying both a set of standard rules and the rules given as input to the chunk of data. The size of both inputs may vary.

I want to be able to write a list of rules like this:

(rule-generating-macro
  (rule-1-name rule-1-target
    (rule-action-macro (progn actions more-actions)))
  (rule-2-name rule-2-target
    (rule-action-macro (or (action-2) (default-action))))
  ;; more rules
 )

Right now, rules are more verbose -- they look more like

(defvar rule-list
  `((rule-1-name rule-1-target
      ,@(rule-action-macro (progn actions more-actions)))
    (rule-2-name rule-2-target
      ,@(rule-action-macro (or (action-2) (default-action))))
  ;; more rules
 )

The latter form looks uglier to me, but I can't figure out how to write a macro that can handle a variable-length &rest argument, iterate over it, and return the transformed structure. Using a defun instead of a defmacro isn't really on the table because (as hopefully the example shows) I'm trying to control evaluation of the list of rules instead of evaluating the list when my program first sees it, and once you need to control evaluation, you're in defmacro territory. In this case, the thorny point is the rule-action-macro part - getting the interpreter to read that and use its expanded value has been problematic.

How can I create a macro that handles a variable-length argument so that I can write rule lists in a concise way?

Upvotes: 2

Views: 997

Answers (1)

sds
sds

Reputation: 60064

defmacro will happily accept a &rest argument (see Defining Macros for Emacs Lisp and Macro Lambda Lists for Common Lisp).

Then you can do pretty much anything you want with it in the macro body - e.g., iterate over it. Remember, macro is much more than just backquote!

E.g.:

(defmacro multidefvar (&rest vars)
  (let ((forms (mapcar (lambda (var) `(defvar ,var)) vars)))
    `(progn ,@forms)))

(macroexpand '(multidefvar a b c d))
==> (PROGN (DEFVAR A) (DEFVAR B) (DEFVAR C) (DEFVAR D))

Upvotes: 5

Related Questions