Skip to content

[enh] Auto-generate wrapper #14

@Technologicat

Description

@Technologicat

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...)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions