Skip to content

Commit bb434c5

Browse files
committed
handle hygienic captures properly in all ASTTransformers
1 parent 519537e commit bb434c5

File tree

9 files changed

+42
-3
lines changed

9 files changed

+42
-3
lines changed

unpythonic/syntax/__init__.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,6 @@
8787
# However, 0.15.0 is the initial version that runs on `mcpyrate`, and the focus is to just get this running.
8888
# Cleanups can be done in a future release.
8989

90-
# TODO: Update all tree-walking macros to ignore the node (and not recurse!) if it matches `mcpyrate.quotes.is_captured_value`
91-
9290
# TODO: fail-fast: promote `local[]`/`delete[]` usage errors to compile-time errors
9391
# TODO: (doesn't currently work e.g. for `let` with an implicit do (extra bracket notation))
9492

unpythonic/syntax/autoref.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from mcpyrate.quotes import macros, q, u, n, a, h # noqa: F401
88

99
from mcpyrate import gensym
10+
from mcpyrate.quotes import is_captured_value
1011
from mcpyrate.walkers import ASTTransformer
1112

1213
from .astcompat import getconstant, Str
@@ -124,6 +125,8 @@ def makeautoreference(tree):
124125
# TODO: could we use `mcpyrate.utils.rename` here?
125126
class PlaceholderRenamer(ASTTransformer):
126127
def transform(self, tree):
128+
if is_captured_value(tree):
129+
return tree # don't recurse!
127130
if type(tree) is Name and tree.id == "__ar_":
128131
tree.id = our_lambda_argname
129132
elif type(tree) is arg and tree.arg == "__ar_":
@@ -133,6 +136,9 @@ def transform(self, tree):
133136

134137
class AutorefTransformer(ASTTransformer):
135138
def transform(self, tree):
139+
if is_captured_value(tree):
140+
return tree # don't recurse!
141+
136142
referents = self.state.referents
137143
if type(tree) in (Attribute, Subscript, Name) and type(tree.ctx) in (Store, Del):
138144
return tree

unpythonic/syntax/dbg.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from mcpyrate.quotes import macros, q, u, a, h # noqa: F401
1212

1313
from mcpyrate import unparse
14+
from mcpyrate.quotes import is_captured_value
1415
from mcpyrate.walkers import ASTTransformer
1516

1617
from ..dynassign import dyn, make_dynvar
@@ -81,6 +82,8 @@ def dbg_block(body, args):
8182

8283
class DbgBlockTransformer(ASTTransformer):
8384
def transform(self, tree):
85+
if is_captured_value(tree):
86+
return tree # don't recurse!
8487
if type(tree) is Call and type(tree.func) is Name and tree.func.id == pname:
8588
names = [q[u[unparse(node)]] for node in tree.args] # x --> "x"; (1 + 2) --> "(1 + 2)"; ...
8689
names = Tuple(elts=names, lineno=tree.lineno, col_offset=tree.col_offset)

unpythonic/syntax/lambdatools.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
from mcpyrate import gensym
1212
from mcpyrate.expander import MacroExpander
13+
from mcpyrate.quotes import is_captured_value
1314
from mcpyrate.splicing import splice_expression
1415
from mcpyrate.utils import extract_bindings
1516
from mcpyrate.walkers import ASTTransformer
@@ -28,7 +29,9 @@
2829
def multilambda(block_body):
2930
class MultilambdaTransformer(ASTTransformer):
3031
def transform(self, tree):
31-
if type(tree) is not Lambda or type(tree.body) is not List:
32+
if is_captured_value(tree):
33+
return tree # don't recurse!
34+
if not (type(tree) is Lambda and type(tree.body) is List):
3235
return self.generic_visit(tree)
3336
bodys = tree.body
3437
# bracket magic:
@@ -96,6 +99,8 @@ def nameit(myname, tree):
9699

97100
class NamedLambdaTransformer(ASTTransformer):
98101
def transform(self, tree):
102+
if is_captured_value(tree):
103+
return tree # don't recurse!
99104
if islet(tree, expanded=False): # let bindings
100105
view = UnexpandedLetView(tree)
101106
for b in view.bindings:
@@ -185,6 +190,8 @@ def f(tree):
185190

186191
class UnderscoreTransformer(ASTTransformer):
187192
def transform(self, tree):
193+
if is_captured_value(tree):
194+
return tree # don't recurse!
188195
# Don't recurse into nested `f[]`.
189196
# TODO: This would benefit from macro destructuring in the expander.
190197
# TODO: See https://github.com/Technologicat/mcpyrate/issues/3
@@ -239,6 +246,9 @@ def isfunctionoruserlambda(tree):
239246

240247
class EnvifyTransformer(ASTTransformer):
241248
def transform(self, tree):
249+
if is_captured_value(tree):
250+
return tree # don't recurse!
251+
242252
bindings = self.state.bindings
243253
enames = self.state.enames
244254

unpythonic/syntax/letdo.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727

2828
from mcpyrate import gensym
2929
from mcpyrate.markers import ASTMarker
30+
from mcpyrate.quotes import is_captured_value
3031
from mcpyrate.utils import NestingLevelTracker
3132
from mcpyrate.walkers import ASTTransformer
3233

@@ -352,6 +353,8 @@ def do(tree):
352353
def find_localdefs(tree):
353354
class LocaldefCollector(ASTTransformer):
354355
def transform(self, tree):
356+
if is_captured_value(tree):
357+
return tree # don't recurse!
355358
if isinstance(tree, UnpythonicDoLocalMarker):
356359
expr = tree.body
357360
if not isenvassign(expr):
@@ -367,6 +370,8 @@ def transform(self, tree):
367370
def find_deletes(tree):
368371
class DeleteCollector(ASTTransformer):
369372
def transform(self, tree):
373+
if is_captured_value(tree):
374+
return tree # don't recurse!
370375
if isinstance(tree, UnpythonicDoDeleteMarker):
371376
expr = tree.body
372377
if type(expr) is not Name:

unpythonic/syntax/letsyntax.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
FunctionDef, AsyncFunctionDef, ClassDef, Attribute)
99
from copy import deepcopy
1010

11+
from mcpyrate.quotes import is_captured_value
1112
from mcpyrate.walkers import ASTTransformer
1213

1314
from .letdo import implicit_do
@@ -173,6 +174,8 @@ def isthisname(tree):
173174
def splice(tree):
174175
class Splicer(ASTTransformer):
175176
def transform(self, tree):
177+
if is_captured_value(tree):
178+
return tree # don't recurse!
176179
def subst():
177180
# Copy just to be on the safe side. Different instances may be
178181
# edited differently by other macros expanded later.
@@ -198,6 +201,8 @@ def subst():
198201
def splice_barestring(tree):
199202
class BarestringSplicer(ASTTransformer):
200203
def transform(self, tree):
204+
if is_captured_value(tree):
205+
return tree # don't recurse!
201206
if type(tree) in (FunctionDef, AsyncFunctionDef, ClassDef):
202207
if tree.name == name:
203208
tree.name = newname
@@ -234,6 +239,8 @@ def subst(tree):
234239
def splice(tree):
235240
class Splicer(ASTTransformer):
236241
def transform(self, tree):
242+
if is_captured_value(tree):
243+
return tree # don't recurse!
237244
# discard Expr wrapper (identifying a statement position) at use site
238245
# when performing a block substitution
239246
if mode == "block" and type(tree) is Expr and isthisfunc(tree.value):

unpythonic/syntax/prefix.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
from mcpyrate.quotes import macros, q, u, a, t # noqa: F811, F401
1111

12+
from mcpyrate.quotes import is_captured_value
1213
from mcpyrate.walkers import ASTTransformer
1314

1415
from .letdoutil import islet, isdo, UnexpandedLetView, UnexpandedDoView
@@ -22,6 +23,9 @@ def prefix(block_body):
2223

2324
class PrefixTransformer(ASTTransformer):
2425
def transform(self, tree):
26+
if is_captured_value(tree):
27+
return tree # don't recurse!
28+
2529
# Not tuples but syntax: leave alone the:
2630
# - binding pair "tuples" of let, letseq, letrec, their d*, b* variants,
2731
# and let_syntax, abbrev

unpythonic/syntax/testingtools.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from mcpyrate.quotes import macros, q, u, n, a, h # noqa: F401
88

99
from mcpyrate import gensym, unparse
10+
from mcpyrate.quotes import is_captured_value
1011
from mcpyrate.walkers import ASTTransformer
1112

1213
from ast import Tuple, Subscript, Name, Call, copy_location, Compare, arg, Return, parse, Expr, AST
@@ -424,6 +425,8 @@ def _transform_important_subexpr(tree, envname):
424425
# actual source file.
425426
class ImportantSubexprTransformer(ASTTransformer):
426427
def transform(self, tree):
428+
if is_captured_value(tree):
429+
return tree # don't recurse!
427430
# Respect the boundaries of nested test constructs (don't recurse there).
428431
if isunexpandedtestmacro(tree):
429432
return tree

unpythonic/syntax/util.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
If, With, withitem, stmt, NodeTransformer)
88

99
import mcpyrate.markers
10+
from mcpyrate.quotes import is_captured_value
1011
from mcpyrate.walkers import ASTTransformer, ASTVisitor
1112

1213
from .astcompat import getconstant
@@ -248,6 +249,8 @@ def prioritize(tree): # sort key for Call nodes invoking known decorators
248249

249250
class FixIt(ASTTransformer):
250251
def transform(self, tree):
252+
if is_captured_value(tree):
253+
return tree # don't recurse!
251254
# we can robustly sort only decorators for which we know the correct ordering.
252255
if is_decorated_lambda(tree, mode="known"):
253256
decorator_list, thelambda = destructure_decorated_lambda(tree)

0 commit comments

Comments
 (0)