Skip to content

Commit c9f33ed

Browse files
committed
Handle 3.6 "async for" better
1 parent 9dd4a53 commit c9f33ed

4 files changed

Lines changed: 212 additions & 135 deletions

File tree

uncompyle6/parsers/parse36.py

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,18 @@ def customize_grammar_rules(self, tokens, customize):
222222
else_suite COME_FROM_LOOP
223223
224224
""")
225-
self.check_reduce['call_kw'] = 'AST'
225+
self.check_reduce["call_kw"] = "AST"
226+
227+
# Opcode names in the custom_ops_processed set have rules that get added
228+
# unconditionally and the rules are constant. So they need to be done
229+
# only once and if we see the opcode a second we don't have to consider
230+
# adding more rules.
231+
#
232+
# Note: BUILD_TUPLE_UNPACK_WITH_CALL gets considered by
233+
# default because it starts with BUILD. So we'll set to ignore it from
234+
# the start.
235+
custom_ops_processed = set()
236+
226237

227238
for i, token in enumerate(tokens):
228239
opname = token.kind
@@ -307,6 +318,59 @@ def customize_grammar_rules(self, tokens, customize):
307318
self.addRule(rule, nop_func)
308319
rule = ('starred ::= %s %s' % ('expr ' * v, opname))
309320
self.addRule(rule, nop_func)
321+
elif opname == "GET_AITER":
322+
self.addRule(
323+
"""
324+
expr ::= generator_exp_async
325+
generator_exp_async ::= load_genexpr LOAD_STR MAKE_FUNCTION_0 expr
326+
GET_AITER CALL_FUNCTION_1
327+
328+
stmt ::= genexpr_func_async
329+
330+
func_async_prefix ::= _come_froms
331+
LOAD_CONST YIELD_FROM
332+
SETUP_EXCEPT GET_ANEXT LOAD_CONST YIELD_FROM
333+
func_async_middle ::= POP_BLOCK JUMP_FORWARD COME_FROM_EXCEPT
334+
DUP_TOP LOAD_GLOBAL COMPARE_OP POP_JUMP_IF_TRUE
335+
END_FINALLY COME_FROM
336+
genexpr_func_async ::= LOAD_FAST func_async_prefix
337+
store func_async_middle comp_iter
338+
JUMP_BACK COME_FROM
339+
POP_TOP POP_TOP POP_TOP POP_EXCEPT POP_TOP
340+
341+
expr ::= list_comp_async
342+
list_comp_async ::= LOAD_LISTCOMP LOAD_STR MAKE_FUNCTION_0
343+
expr GET_AITER CALL_FUNCTION_1
344+
GET_AWAITABLE LOAD_CONST
345+
YIELD_FROM
346+
347+
expr ::= list_comp_async
348+
list_afor2 ::= func_async_prefix
349+
store func_async_middle list_iter
350+
JUMP_BACK
351+
POP_TOP POP_TOP POP_TOP POP_EXCEPT POP_TOP
352+
list_comp_async ::= BUILD_LIST_0 LOAD_FAST list_afor2
353+
get_aiter ::= expr GET_AITER
354+
list_afor ::= get_aiter list_afor2
355+
list_iter ::= list_afor
356+
""",
357+
nop_func,
358+
)
359+
elif opname == "GET_ANEXT":
360+
self.addRule(
361+
"""
362+
func_async_prefix ::= _come_froms SETUP_FINALLY GET_ANEXT LOAD_CONST YIELD_FROM POP_BLOCK
363+
func_async_middle ::= JUMP_FORWARD COME_FROM_EXCEPT
364+
DUP_TOP LOAD_GLOBAL COMPARE_OP POP_JUMP_IF_TRUE
365+
list_afor2 ::= func_async_prefix
366+
store list_iter
367+
JUMP_BACK COME_FROM_FINALLY
368+
END_ASYNC_FOR
369+
""",
370+
nop_func,
371+
)
372+
custom_ops_processed.add(opname)
373+
310374
elif opname == 'SETUP_ANNOTATIONS':
311375
# 3.6 Variable Annotations PEP 526
312376
# This seems to come before STORE_ANNOTATION, and doesn't

uncompyle6/semantics/customize3.py

Lines changed: 0 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -80,136 +80,6 @@ def tryfinallystmt(node):
8080
self.default(node)
8181
self.n_tryfinallystmt = tryfinallystmt
8282

83-
def listcomp_closure3(node):
84-
"""List comprehensions in Python 3 when handled as a closure.
85-
See if we can combine code.
86-
"""
87-
88-
# FIXME: DRY with comprehension_walk_newer
89-
p = self.prec
90-
self.prec = 27
91-
92-
code_obj = node[1].attr
93-
assert iscode(code_obj), node[1]
94-
code = Code(code_obj, self.scanner, self.currentclass, self.debug_opts["asm"])
95-
96-
ast = self.build_ast(code._tokens, code._customize, code)
97-
self.customize(code._customize)
98-
99-
# skip over: sstmt, stmt, return, return_expr
100-
# and other singleton derivations
101-
while len(ast) == 1 or (
102-
ast in ("sstmt", "return") and ast[-1] in ("RETURN_LAST", "RETURN_VALUE")
103-
):
104-
self.prec = 100
105-
ast = ast[0]
106-
107-
n = ast[1]
108-
109-
# Pick out important parts of the comprehension:
110-
# * the variables we iterate over: "stores"
111-
# * the results we accumulate: "n"
112-
113-
# collections is the name of the expression(s) we are iterating over
114-
collections = [node[-3]]
115-
list_ifs = []
116-
117-
if self.version[:2] == (3, 0) and n != "list_iter":
118-
# FIXME 3.0 is a snowflake here. We need
119-
# special code for this. Not sure if this is totally
120-
# correct.
121-
stores = [ast[3]]
122-
assert ast[4] == "comp_iter"
123-
n = ast[4]
124-
# Find the list comprehension body. It is the inner-most
125-
# node that is not comp_.. .
126-
while n == "comp_iter":
127-
if n[0] == "comp_for":
128-
n = n[0]
129-
stores.append(n[2])
130-
n = n[3]
131-
elif n[0] in ("comp_if", "comp_if_not"):
132-
n = n[0]
133-
# FIXME: just a guess
134-
if n[0].kind == "expr":
135-
list_ifs.append(n)
136-
else:
137-
list_ifs.append([1])
138-
n = n[2]
139-
pass
140-
else:
141-
break
142-
pass
143-
144-
# Skip over n[0] which is something like: _[1]
145-
self.preorder(n[1])
146-
147-
else:
148-
assert n == "list_iter"
149-
stores = []
150-
# Find the list comprehension body. It is the inner-most
151-
# node that is not list_.. .
152-
while n == "list_iter":
153-
154-
# recurse one step
155-
n = n[0]
156-
157-
# FIXME: adjust for set comprehension
158-
if n == "list_for":
159-
stores.append(n[2])
160-
n = n[3]
161-
if n[0] == "list_for":
162-
# Dog-paddle down largely singleton reductions
163-
# to find the collection (expr)
164-
c = n[0][0]
165-
if c == "expr":
166-
c = c[0]
167-
# FIXME: grammar is wonky here? Is this really an attribute?
168-
if c == "attribute":
169-
c = c[0]
170-
collections.append(c)
171-
pass
172-
elif n in ("list_if", "list_if_not", "list_if_or_not"):
173-
if n[0].kind == "expr":
174-
list_ifs.append(n)
175-
else:
176-
list_ifs.append([1])
177-
n = n[-2] if n[-1] == "come_from_opt" else n[-1]
178-
pass
179-
elif n == "list_if37":
180-
list_ifs.append(n)
181-
n = n[-1]
182-
pass
183-
elif n == "list_afor":
184-
collections.append(n[0][0])
185-
n = n[1]
186-
stores.append(n[1][0])
187-
n = n[2] if n[2].kind == "list_iter" else n[3]
188-
pass
189-
190-
assert n == "lc_body", ast
191-
192-
self.preorder(n[0])
193-
194-
# FIXME: add indentation around "for"'s and "in"'s
195-
n_colls = len(collections)
196-
for i, store in enumerate(stores):
197-
if i >= n_colls:
198-
break
199-
if collections[i] == "LOAD_DEREF" and co_flags_is_async(code_obj.co_flags):
200-
self.write(" async")
201-
pass
202-
self.write(" for ")
203-
self.preorder(store)
204-
self.write(" in ")
205-
self.preorder(collections[i])
206-
if i < len(list_ifs):
207-
self.preorder(list_ifs[i])
208-
pass
209-
pass
210-
self.prec = p
211-
self.listcomp_closure3 = listcomp_closure3
212-
21383
def n_classdef3(node):
21484
"""Handle "classdef" nonterminal for 3.0 >= version 3.0 < 3.6
21585
"""

uncompyle6/semantics/customize36.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,12 @@ def customize_for_version36(self, version):
7373

7474
"ifstmtl": ("%|if %c:\n%+%c%-",
7575
(0, "testexpr"), (1, "_ifstmts_jumpl")),
76+
77+
"list_afor": (
78+
" async for %[1]{%c} in %c%[1]{%c}",
79+
(1, "store"), (0, "get_aiter"), (3, "list_iter"),
80+
),
81+
7682
"try_except36": ("%|try:\n%+%c%-%c\n\n", 1, -2),
7783
"tryfinally36": ("%|try:\n%+%c%-%|finally:\n%+%c%-\n\n", (1, "returns"), 3),
7884
"tryfinally_return_stmt": ("%|try:\n%+%c%-%|finally:\n%+%|return%-\n\n", 1),

0 commit comments

Comments
 (0)