@@ -58,7 +58,12 @@ def _letimpl(bindings, body, mode):
5858 """bindings: sequence of ast.Tuple: (k1, v1), (k2, v2), ..., (kn, vn)"""
5959 assert mode in ("let" , "letrec" )
6060
61+ # The let constructs are currently inside-out macros; expand other macro
62+ # invocations in both bindings and body.
63+ #
64+ # But apply the implicit `do` (extra bracket syntax) first.
6165 body = implicit_do (body )
66+ body = dyn ._macro_expander .visit (body )
6267 if not bindings :
6368 # Optimize out a `let` with no bindings. The macro layer cannot trigger
6469 # this case, because our syntaxes always require at least one binding.
@@ -81,6 +86,8 @@ def _letimpl(bindings, body, mode):
8186 # `let[(...) in ...]`, `let[..., where(...)]` - and in these cases,
8287 # both the bindings and the body reside inside the brackets.
8388 return body # pragma: no cover
89+ bindings = dyn ._macro_expander .visit (bindings )
90+
8491 names , values = zip (* [b .elts for b in bindings ]) # --> (k1, ..., kn), (v1, ..., vn)
8592 names = [k .id for k in names ] # any duplicates will be caught by env at run-time
8693
@@ -327,20 +334,24 @@ class UnpythonicDoDeleteMarker(UnpythonicLetDoMarker):
327334# TODO: fail-fast: promote `local[]`/`delete[]` usage errors to compile-time errors
328335# TODO: (doesn't currently work e.g. for `let` with an implicit do (extra bracket notation))
329336def local (tree ): # syntax transformer
330- # if _do_level.value < 1:
331- # raise SyntaxError("local[] is only valid within a do[] or do0[]") # pragma: no cover
337+ if _do_level .value < 1 :
338+ raise SyntaxError ("local[] is only valid within a do[] or do0[]" ) # pragma: no cover
332339 return UnpythonicDoLocalMarker (tree )
333340
334341def delete (tree ): # syntax transformer
335- # if _do_level.value < 1:
336- # raise SyntaxError("delete[] is only valid within a do[] or do0[]") # pragma: no cover
342+ if _do_level .value < 1 :
343+ raise SyntaxError ("delete[] is only valid within a do[] or do0[]" ) # pragma: no cover
337344 return UnpythonicDoDeleteMarker (tree )
338345
339346def do (tree ):
340347 if type (tree ) not in (Tuple , List ):
341348 raise SyntaxError ("do body: expected a sequence of comma-separated expressions" ) # pragma: no cover, let's not test the macro expansion errors.
342349
343- # Handle nested `local[]`/`delete[]`.
350+ # Handle nested `local[]`/`delete[]`. This will also expand any other nested macro invocations.
351+ # TODO: If we want to make `do` an outside-in macro, instantiate another expander here and register
352+ # TODO: only the `local` and `delete` transformers to it - grabbing them from the current expander's
353+ # TODO: bindings to respect as-imports. (Expander instances are cheap in `mcpyrate`.)
354+ # TODO: Grep the `unpythonic` codebase (and `mcpyrate` demos) for `MacroExpander` to see how.
344355 with _do_level .changed_by (+ 1 ):
345356 tree = dyn ._macro_expander .visit (tree )
346357
0 commit comments