Commit 6c5fab7c authored by Stefan Behnel's avatar Stefan Behnel

implement PEP 530 by allowing async generator expressions

parent ddec13cb
...@@ -208,11 +208,22 @@ class PostParse(ScopeTrackingTransform): ...@@ -208,11 +208,22 @@ class PostParse(ScopeTrackingTransform):
def visit_GeneratorExpressionNode(self, node): def visit_GeneratorExpressionNode(self, node):
# unpack a generator expression into the corresponding DefNode # unpack a generator expression into the corresponding DefNode
node.def_node = Nodes.DefNode(node.pos, name=node.name, collector = YieldNodeCollector()
doc=None, collector.visitchildren(node.loop)
args=[], star_arg=None, node.def_node = Nodes.DefNode(
starstar_arg=None, node.pos, name=node.name, doc=None,
body=node.loop) args=[], star_arg=None, starstar_arg=None,
body=node.loop, is_async_def=collector.has_await)
self.visitchildren(node)
return node
def visit_ComprehensionNode(self, node):
# enforce local scope also in Py2 for async generators (seriously, that's a Py3.6 feature...)
if not node.has_local_scope:
collector = YieldNodeCollector()
collector.visitchildren(node.loop)
if collector.has_await:
node.has_local_scope = True
self.visitchildren(node) self.visitchildren(node)
return node return node
......
...@@ -501,7 +501,7 @@ def p_call_parse_args(s, allow_genexp=True): ...@@ -501,7 +501,7 @@ def p_call_parse_args(s, allow_genexp=True):
break break
s.next() s.next()
if s.sy == 'for': if s.sy in ('for', 'async'):
if not keyword_args and not last_was_tuple_unpack: if not keyword_args and not last_was_tuple_unpack:
if len(positional_args) == 1 and len(positional_args[0]) == 1: if len(positional_args) == 1 and len(positional_args[0]) == 1:
positional_args = [[p_genexp(s, positional_args[0][0])]] positional_args = [[p_genexp(s, positional_args[0][0])]]
...@@ -1196,7 +1196,7 @@ def p_f_string_expr(s, unicode_value, pos, starting_index, is_raw): ...@@ -1196,7 +1196,7 @@ def p_f_string_expr(s, unicode_value, pos, starting_index, is_raw):
# list_display ::= "[" [listmaker] "]" # list_display ::= "[" [listmaker] "]"
# listmaker ::= (test|star_expr) ( comp_for | (',' (test|star_expr))* [','] ) # listmaker ::= (test|star_expr) ( comp_for | (',' (test|star_expr))* [','] )
# comp_iter ::= comp_for | comp_if # comp_iter ::= comp_for | comp_if
# comp_for ::= "for" expression_list "in" testlist [comp_iter] # comp_for ::= ["async"] "for" expression_list "in" testlist [comp_iter]
# comp_if ::= "if" test [comp_iter] # comp_if ::= "if" test [comp_iter]
def p_list_maker(s): def p_list_maker(s):
...@@ -1208,7 +1208,7 @@ def p_list_maker(s): ...@@ -1208,7 +1208,7 @@ def p_list_maker(s):
return ExprNodes.ListNode(pos, args=[]) return ExprNodes.ListNode(pos, args=[])
expr = p_test_or_starred_expr(s) expr = p_test_or_starred_expr(s)
if s.sy == 'for': if s.sy in ('for', 'async'):
if expr.is_starred: if expr.is_starred:
s.error("iterable unpacking cannot be used in comprehension") s.error("iterable unpacking cannot be used in comprehension")
append = ExprNodes.ComprehensionAppendNode(pos, expr=expr) append = ExprNodes.ComprehensionAppendNode(pos, expr=expr)
...@@ -1230,7 +1230,7 @@ def p_list_maker(s): ...@@ -1230,7 +1230,7 @@ def p_list_maker(s):
def p_comp_iter(s, body): def p_comp_iter(s, body):
if s.sy == 'for': if s.sy in ('for', 'async'):
return p_comp_for(s, body) return p_comp_for(s, body)
elif s.sy == 'if': elif s.sy == 'if':
return p_comp_if(s, body) return p_comp_if(s, body)
...@@ -1239,11 +1239,17 @@ def p_comp_iter(s, body): ...@@ -1239,11 +1239,17 @@ def p_comp_iter(s, body):
return body return body
def p_comp_for(s, body): def p_comp_for(s, body):
# s.sy == 'for'
pos = s.position() pos = s.position()
s.next() # [async] for ...
kw = p_for_bounds(s, allow_testlist=False) is_async = False
kw.update(else_clause = None, body = p_comp_iter(s, body)) if s.sy == 'async':
is_async = True
s.next()
# s.sy == 'for'
s.expect('for')
kw = p_for_bounds(s, allow_testlist=False, is_async=is_async)
kw.update(else_clause=None, body=p_comp_iter(s, body), is_async=is_async)
return Nodes.ForStatNode(pos, **kw) return Nodes.ForStatNode(pos, **kw)
def p_comp_if(s, body): def p_comp_if(s, body):
...@@ -1311,7 +1317,7 @@ def p_dict_or_set_maker(s): ...@@ -1311,7 +1317,7 @@ def p_dict_or_set_maker(s):
else: else:
break break
if s.sy == 'for': if s.sy in ('for', 'async'):
# dict/set comprehension # dict/set comprehension
if len(parts) == 1 and isinstance(parts[0], list) and len(parts[0]) == 1: if len(parts) == 1 and isinstance(parts[0], list) and len(parts[0]) == 1:
item = parts[0][0] item = parts[0][0]
...@@ -1441,13 +1447,13 @@ def p_testlist_comp(s): ...@@ -1441,13 +1447,13 @@ def p_testlist_comp(s):
s.next() s.next()
exprs = p_test_or_starred_expr_list(s, expr) exprs = p_test_or_starred_expr_list(s, expr)
return ExprNodes.TupleNode(pos, args = exprs) return ExprNodes.TupleNode(pos, args = exprs)
elif s.sy == 'for': elif s.sy in ('for', 'async'):
return p_genexp(s, expr) return p_genexp(s, expr)
else: else:
return expr return expr
def p_genexp(s, expr): def p_genexp(s, expr):
# s.sy == 'for' # s.sy == 'async' | 'for'
loop = p_comp_for(s, Nodes.ExprStatNode( loop = p_comp_for(s, Nodes.ExprStatNode(
expr.pos, expr = ExprNodes.YieldExprNode(expr.pos, arg=expr))) expr.pos, expr = ExprNodes.YieldExprNode(expr.pos, arg=expr)))
return ExprNodes.GeneratorExpressionNode(expr.pos, loop=loop) return ExprNodes.GeneratorExpressionNode(expr.pos, loop=loop)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment