0

I am trying to expand all macros inside a nested list structure. macroexpand-all almost works, but skips (does not expand) the first form in every nested list.

I am using this as a template-mechanism for org-agenda-custom-commands. I can generate agenda-blocks for multiple agenda-commands via macros. This is in init.el (emacs26.2). macroexp-all-forms is able to not skip the first form, but calls macroexpand-all for nested forms.

Here a minimal example from the emacs doc:

(defmacro inc (var)
              (list 'setq var (list '1+ var)))

This works as expected (one macro-call):

ELISP> (macroexpand-all '(inc r))
(setq r
      (1+ r))

This works too (nested, but first form is not a macro-call):

ELISP> (macroexpand-all '(('foo)(inc r)))
(('foo)
 (setq r
       (1+ r)))

This does NOT work (nested and first form is a macro-call):

ELISP> (macroexpand-all '((inc r)(inc r)))
((inc r)
 (setq r
       (1+ r)))

This also does not work:

ELISP> (macroexpand-all '((inc r)))
((inc r))

In the last two examples, the first call to inc is not expanded. What am I missing here? How can i really expand all macros in this situation?

Drew
  • 29,895
  • 7
  • 74
  • 104
bitclick
  • 1
  • 2

2 Answers2

3

macroexpand-all expects a form as the argument, but ((inc r) (inc r)) is not a form. A form should be something that can be evaluated. In the case of a list, that means the first element must be a name of a function, macro or a special operator, or a lambda expression.

The special operator progn can be used for a list of forms to be evaluated sequentially. For example:

(macroexpand-all '(progn (inc r) (inc r)))
;=> (progn
;     (setq r
;           (1+ r))
;     (setq r
;           (1+ r)))

Alternatively, if the list is not meant to be a form, you can use mapcar to apply macroexpand-all to each member of the list. For example:

(mapcar #'macroexpand-all '((inc r) (inc r)))
;=> ((setq r
;          (1+ r))
;    (setq r
;          (1+ r)))

Keep in mind that the result here is a list of forms, but not a form itself; it cannot be evaluated as is, but the individual elements can be.

jkiiski
  • 8,206
  • 2
  • 28
  • 44
1

The form you want to expand should make sense as a form for evaluation. ((inc r)) does not: it can't be legal elisp.

I'm actually fairly surprised that macroexpand-all doesn't raise an error in the case you've given it. If you want it to work you need to fake up something which is potentially legal. For instance

ELISP> (macroexpand-all '(grut (inc r) (inc r)))
(grut
 (setq r
       (1+ r))
 (setq r
       (1+ r)))

ELISP> (cdr (macroexpand-all '(grut (inc r) (inc r))))
((setq r
       (1+ r))
 (setq r
       (1+ r)))

grut does not need to be defined: it only needs not to be a macro so macroexpand-all just ignores it. (In fact, using progn as jkiiski suggests is a better answer).

As an aside, it sounds like you are using macroexpansion to do something other than expand Lisp code: that's almost always a bad idea. It's much better to write your own expander which understands the rules of the language you are implementing in Lisp.