-
Notifications
You must be signed in to change notification settings - Fork 4
Description
On some occasions, a macro needs to invoke another:
@macros.expr
def aif(tree, gen_sym, **kw):
# no hq[], the whole point of "aif" is to leak "it".
bindings = [q[(it, ast_literal[test])]]
body = q[ast_literal[then] if it else ast_literal[otherwise]]
return _let(body, bindings, "let", gen_sym) # <-- needs let[]Or itself:
@macros.expr
def cond(tree, **kw):
return _cond(tree)
def _cond(tree):
elts = tree.elts
if len(elts) == 1: # final "otherwise" branch
return elts[0]
if not elts:
assert False, "Expected cond[test1, then1, test2, then2, ..., otherwise]"
test, then, *more = elts
return hq[ast_literal[then] if ast_literal[test] else ast_literal[_cond(more)]]This leads to boilerplate. To be able to, from inside a macro, "call" another macro, which may be defined in another module, it is (AFAIK, IIUC) currently necessary to split the definition into two parts:
- Decorated wrapper, for use by normal run-time code
- Syntax transformer function, for use from inside a macro
Having to split at all is slightly inelegant, but bearable; having to do it manually is too much typing. :)
Hence, enhancement suggestion: is it possible to automate this? I would imagine that the macro decorator could do the definition shuffling, and save the syntax transformer function (i.e. the original function being decorated) into an attribute of the macro name, e.g. .transform (unless this somehow doesn't fit the MacroPy implementation; my mental model here is regular decorators for regular functions).
This would shorten the second example to:
@macros.expr
def cond(tree, **kw):
elts = tree.elts
if len(elts) == 1: # final "otherwise" branch
return elts[0]
if not elts:
assert False, "Expected cond[test1, then1, test2, then2, ..., otherwise]"
test, then, *more = elts
return hq[ast_literal[then] if ast_literal[test] else ast_literal[cond.transform(more)]]eliminating the explicit wrapper.
More use cases to follow later; I'll have to dig PG's On Lisp a bit for concrete examples where macros need to use other macros. (Racket, on the other hand, is in a large part built via macros on top of macros on top of...)