|
19 | 19 | let as _let, letseq as _letseq, letrec as _letrec, \ |
20 | 20 | dlet as _dlet, dletseq as _dletseq, dletrec as _dletrec, \ |
21 | 21 | blet as _blet, bletseq as _bletseq, bletrec as _bletrec |
| 22 | +from unpythonic.syntax.letsyntax import let_syntax_expr |
22 | 23 | from unpythonic.syntax.prefix import prefix as _prefix |
23 | 24 | from unpythonic.syntax.tailtools import autoreturn as _autoreturn, tco as _tco, \ |
24 | 25 | continuations as _continuations, bind |
@@ -477,6 +478,61 @@ def do0(tree, gen_sym, **kw): |
477 | 478 |
|
478 | 479 | # ----------------------------------------------------------------------------- |
479 | 480 |
|
| 481 | +@macros.expr |
| 482 | +def let_syntax(tree, args, gen_sym, **kw): |
| 483 | + """Introduce local **syntactic** bindings. |
| 484 | +
|
| 485 | + Usage:: |
| 486 | +
|
| 487 | + let_syntax((lhs, rhs), ...)[body] |
| 488 | +
|
| 489 | + let_syntax((lhs, rhs), ...)[[body0, ...]] |
| 490 | +
|
| 491 | + The bindings are applied **at macro expansion time**, substituting |
| 492 | + the expression on the RHS for each instance of the corresponding LHS. |
| 493 | + This macro runs in the first pass (outside in). |
| 494 | +
|
| 495 | + This is useful to e.g. locally abbreviate long macro names, |
| 496 | + or to splice in several (parametric) instances of a common pattern. |
| 497 | +
|
| 498 | + The LHS may be: |
| 499 | +
|
| 500 | + - A bare name (e.g. ``x``), or |
| 501 | +
|
| 502 | + - A simple template of the form ``f(x, ...)``. The names inside the |
| 503 | + parentheses declare the formal parameters of the template. |
| 504 | +
|
| 505 | + Templates support only positional arguments, with no default values. |
| 506 | +
|
| 507 | + In the body, a template is used like a function call. Just like in an |
| 508 | + actual function call, when the template is substituted, the formal |
| 509 | + parameters on its RHS get replaced by the argument values from the |
| 510 | + "call" site; but ``let_syntax`` performs this at macro-expansion time. |
| 511 | +
|
| 512 | + This is a two-step process. In the first step, we apply template substitutions. |
| 513 | + In the second step, we apply bare name substitutions to the result of the |
| 514 | + first step. (So RHSs of templates may use any of the bare-name definitions.) |
| 515 | +
|
| 516 | + Within each step, the substitutions are applied **in the order specified**. |
| 517 | + So if the bindings are ``((x, y), (y, z))``, then ``x`` transforms to ``z``. |
| 518 | + But if the bindings are ``((y, z), (x, y))``, then ``x`` transforms to ``y``, |
| 519 | + and only an explicit ``y`` at the use site transforms to ``z``. |
| 520 | +
|
| 521 | + Inspired by Racket's ``let-syntax``, see: |
| 522 | + https://docs.racket-lang.org/reference/let.html |
| 523 | +
|
| 524 | + **CAUTION**: This is essentially a toy macro system inside a macro system. |
| 525 | + The usual caveats of macro systems apply. Especially, we support absolutely |
| 526 | + no form of hygiene. Be very, very careful to avoid name conflicts. |
| 527 | +
|
| 528 | + ``let_syntax`` is meant only for simple local substitutions. If you need to |
| 529 | + do something complex, prefer writing a real macro directly in MacroPy. |
| 530 | + """ |
| 531 | + with dyn.let(gen_sym=gen_sym): # gen_sym is only needed by the implicit do. |
| 532 | + return (yield from let_syntax_expr(bindings=args, body=tree)) |
| 533 | + |
| 534 | +# ----------------------------------------------------------------------------- |
| 535 | + |
480 | 536 | @macros.expr |
481 | 537 | def forall(tree, **kw): |
482 | 538 | """[syntax, expr] Nondeterministic evaluation. |
|
0 commit comments