Commit f17abb85 authored by Stefan Behnel's avatar Stefan Behnel

refactored 'starred' status into a separate node to support syntax error...

refactored 'starred' status into a separate node to support syntax error handling outside of assignments
parent d0fa1c81
...@@ -2866,6 +2866,49 @@ class AttributeNode(ExprNode): ...@@ -2866,6 +2866,49 @@ class AttributeNode(ExprNode):
# #
#------------------------------------------------------------------- #-------------------------------------------------------------------
class StarredTargetNode(ExprNode):
# A starred expression like "*a"
#
# This is only allowed in sequence assignment targets such as
#
# a, *b = (1,2,3,4) => a = 1 ; b = [2,3,4]
#
# and will be removed during type analysis (or generate an error
# if it's found at unexpected places).
#
# target ExprNode
subexprs = ['target']
is_starred = 1
type = py_object_type
def __init__(self, pos, target):
self.pos = pos
self.target = target
def analyse_declarations(self, env):
error(self.pos, "can use starred expression only as assignment target")
self.target.analyse_declarations(env)
def analyse_types(self, env):
error(self.pos, "can use starred expression only as assignment target")
self.target.analyse_types(env)
self.type = self.target.type
def analyse_target_declaration(self, env):
self.target.analyse_target_declaration(env)
def analyse_target_types(self, env):
self.target.analyse_target_types(env)
self.type = self.target.type
def calculate_result_code(self):
return ""
def generate_result_code(self, code):
pass
class SequenceNode(ExprNode): class SequenceNode(ExprNode):
# Base class for list and tuple constructor nodes. # Base class for list and tuple constructor nodes.
# Contains common code for performing sequence unpacking. # Contains common code for performing sequence unpacking.
...@@ -2883,7 +2926,22 @@ class SequenceNode(ExprNode): ...@@ -2883,7 +2926,22 @@ class SequenceNode(ExprNode):
def compile_time_value_list(self, denv): def compile_time_value_list(self, denv):
return [arg.compile_time_value(denv) for arg in self.args] return [arg.compile_time_value(denv) for arg in self.args]
def replace_starred_target_node(self):
# replace a starred node in the targets by the contained expression
self.starred_assignment = False
args = []
for arg in self.args:
if arg.is_starred:
if self.starred_assignment:
error(arg.pos, "more than 1 starred expression in assignment")
self.starred_assignment = True
arg = arg.target
arg.is_starred = True
args.append(arg)
self.args = args
def analyse_target_declaration(self, env): def analyse_target_declaration(self, env):
self.replace_starred_target_node()
for arg in self.args: for arg in self.args:
arg.analyse_target_declaration(env) arg.analyse_target_declaration(env)
...@@ -2899,7 +2957,6 @@ class SequenceNode(ExprNode): ...@@ -2899,7 +2957,6 @@ class SequenceNode(ExprNode):
self.iterator = PyTempNode(self.pos, env) self.iterator = PyTempNode(self.pos, env)
self.unpacked_items = [] self.unpacked_items = []
self.coerced_unpacked_items = [] self.coerced_unpacked_items = []
self.starred_assignment = False
for arg in self.args: for arg in self.args:
arg.analyse_target_types(env) arg.analyse_target_types(env)
if arg.is_starred: if arg.is_starred:
...@@ -2908,14 +2965,13 @@ class SequenceNode(ExprNode): ...@@ -2908,14 +2965,13 @@ class SequenceNode(ExprNode):
"starred target must have Python object (list) type") "starred target must have Python object (list) type")
if arg.type is py_object_type: if arg.type is py_object_type:
arg.type = Builtin.list_type arg.type = Builtin.list_type
self.starred_assignment = True
unpacked_item = PyTempNode(self.pos, env) unpacked_item = PyTempNode(self.pos, env)
coerced_unpacked_item = unpacked_item.coerce_to(arg.type, env) coerced_unpacked_item = unpacked_item.coerce_to(arg.type, env)
self.unpacked_items.append(unpacked_item) self.unpacked_items.append(unpacked_item)
self.coerced_unpacked_items.append(coerced_unpacked_item) self.coerced_unpacked_items.append(coerced_unpacked_item)
self.type = py_object_type self.type = py_object_type
env.use_utility_code(unpacking_utility_code) env.use_utility_code(unpacking_utility_code)
def generate_result_code(self, code): def generate_result_code(self, code):
self.generate_operation_code(code) self.generate_operation_code(code)
...@@ -2923,13 +2979,13 @@ class SequenceNode(ExprNode): ...@@ -2923,13 +2979,13 @@ class SequenceNode(ExprNode):
if self.starred_assignment: if self.starred_assignment:
self.generate_starred_assignment_code(rhs, code) self.generate_starred_assignment_code(rhs, code)
else: else:
self.generate_normal_assignment_code(rhs, code) self.generate_parallel_assignment_code(rhs, code)
for item in self.unpacked_items: for item in self.unpacked_items:
item.release(code) item.release(code)
rhs.free_temps(code) rhs.free_temps(code)
def generate_normal_assignment_code(self, rhs, code): def generate_parallel_assignment_code(self, rhs, code):
# Need to work around the fact that generate_evaluation_code # Need to work around the fact that generate_evaluation_code
# allocates the temps in a rather hacky way -- the assignment # allocates the temps in a rather hacky way -- the assignment
# is evaluated twice, within each if-block. # is evaluated twice, within each if-block.
......
...@@ -59,7 +59,6 @@ cpdef p_testlist(PyrexScanner s) ...@@ -59,7 +59,6 @@ cpdef p_testlist(PyrexScanner s)
#------------------------------------------------------- #-------------------------------------------------------
cpdef flatten_parallel_assignments(input, output) cpdef flatten_parallel_assignments(input, output)
cpdef find_parallel_assignment_size(input)
cpdef p_global_statement(PyrexScanner s) cpdef p_global_statement(PyrexScanner s)
cpdef p_expression_or_assignment(PyrexScanner s) cpdef p_expression_or_assignment(PyrexScanner s)
......
...@@ -142,13 +142,15 @@ def p_comparison(s): ...@@ -142,13 +142,15 @@ def p_comparison(s):
return n1 return n1
def p_starred_expr(s): def p_starred_expr(s):
pos = s.position()
if s.sy == '*': if s.sy == '*':
starred = True starred = True
s.next() s.next()
else: else:
starred = False starred = False
expr = p_bit_expr(s) expr = p_bit_expr(s)
expr.is_starred = starred if starred:
expr = ExprNodes.StarredTargetNode(pos, expr)
return expr return expr
def p_cascaded_cmp(s): def p_cascaded_cmp(s):
...@@ -935,18 +937,26 @@ def flatten_parallel_assignments(input, output): ...@@ -935,18 +937,26 @@ def flatten_parallel_assignments(input, output):
if starred_targets: if starred_targets:
if starred_targets > 1: if starred_targets > 1:
error(lhs.pos, "more than 1 starred expression in assignment") error(lhs.pos, "more than 1 starred expression in assignment")
output.append([lhs,rhs])
continue
elif lhs_size - starred_targets > rhs_size: elif lhs_size - starred_targets > rhs_size:
error(lhs.pos, "need more than %d value%s to unpack" error(lhs.pos, "need more than %d value%s to unpack"
% (rhs_size, (rhs_size != 1) and 's' or '')) % (rhs_size, (rhs_size != 1) and 's' or ''))
output.append([lhs,rhs])
continue
map_starred_assignment(lhs_targets, starred_assignments, map_starred_assignment(lhs_targets, starred_assignments,
lhs.args, rhs.args) lhs.args, rhs.args)
else: else:
if lhs_size > rhs_size: if lhs_size > rhs_size:
error(lhs.pos, "need more than %d value%s to unpack" error(lhs.pos, "need more than %d value%s to unpack"
% (rhs_size, (rhs_size != 1) and 's' or '')) % (rhs_size, (rhs_size != 1) and 's' or ''))
output.append([lhs,rhs])
continue
elif lhs_size < rhs_size: elif lhs_size < rhs_size:
error(lhs.pos, "too many values to unpack (expected %d, got %d)" error(lhs.pos, "too many values to unpack (expected %d, got %d)"
% (lhs_size, rhs_size)) % (lhs_size, rhs_size))
output.append([lhs,rhs])
continue
else: else:
for targets, expr in zip(lhs_targets, lhs.args): for targets, expr in zip(lhs_targets, lhs.args):
targets.append(expr) targets.append(expr)
...@@ -988,8 +998,7 @@ def map_starred_assignment(lhs_targets, starred_assignments, lhs_args, rhs_args) ...@@ -988,8 +998,7 @@ def map_starred_assignment(lhs_targets, starred_assignments, lhs_args, rhs_args)
targets.append(expr) targets.append(expr)
# the starred target itself, must be assigned a (potentially empty) list # the starred target itself, must be assigned a (potentially empty) list
target = lhs_args[starred] target = lhs_args[starred].target # unpack starred node
target.is_starred = False
starred_rhs = rhs_args[starred:] starred_rhs = rhs_args[starred:]
if lhs_remaining: if lhs_remaining:
starred_rhs = starred_rhs[:-lhs_remaining] starred_rhs = starred_rhs[:-lhs_remaining]
......
__doc__ = u""" __doc__ = u"""
>>> class FakeSeq(object):
... def __init__(self, length):
... self._values = range(1,length+1)
... def __getitem__(self, i):
... return self._values[i]
>>> unpack([1,2]) >>> unpack([1,2])
(1, 2) (1, 2)
>>> unpack_list([1,2]) >>> unpack_list([1,2])
...@@ -6,6 +12,8 @@ __doc__ = u""" ...@@ -6,6 +12,8 @@ __doc__ = u"""
>>> unpack_tuple((1,2)) >>> unpack_tuple((1,2))
(1, 2) (1, 2)
>>> unpack( FakeSeq(2) )
(1, 2)
>>> unpack('12') >>> unpack('12')
('1', '2') ('1', '2')
...@@ -30,6 +38,8 @@ __doc__ = u""" ...@@ -30,6 +38,8 @@ __doc__ = u"""
>>> unpack_recursive((1,2,3,4)) >>> unpack_recursive((1,2,3,4))
(1, [2, 3], 4) (1, [2, 3], 4)
>>> unpack_recursive( FakeSeq(4) )
(1, [2, 3], 4)
>>> unpack_typed((1,2)) >>> unpack_typed((1,2))
([1], 2) ([1], 2)
......
# invalid syntax (not handled by the parser)
def syntax1():
a = b = c = d = e = f = g = h = i = 1 # prevent undefined names
list_of_sequences = [[1,2], [3,4]]
*a
*1
*"abc"
*a*b
[*a, *b]
(a, b, *c, d, e, f, *g, h, i)
for *a,*b in list_of_sequences:
pass
_ERRORS = u"""
8: 4: can use starred expression only as assignment target
10: 4: can use starred expression only as assignment target
12: 4: can use starred expression only as assignment target
14: 4: can use starred expression only as assignment target
16: 5: can use starred expression only as assignment target
16: 9: can use starred expression only as assignment target
18:11: can use starred expression only as assignment target
18:24: can use starred expression only as assignment target
20:11: more than 1 starred expression in assignment
"""
# invalid syntax (as handled by the parser)
def syntax():
*a, *b = 1,2,3,4,5
# wrong size RHS (as handled by the parser)
def length1():
a, b = [1,2,3]
def length2():
a, b = [1]
def length3():
a, b = []
def length4():
a, *b = []
def length5():
a, *b, c = []
a, *b, c = [1]
def length_recursive():
*(a, b), c = (1,2)
_ERRORS = u"""
5:4: more than 1 starred expression in assignment
10:4: too many values to unpack (expected 2, got 3)
13:4: need more than 1 value to unpack
16:4: need more than 0 values to unpack
19:4: need more than 0 values to unpack
22:4: need more than 0 values to unpack
23:4: need more than 1 value to unpack
26:6: need more than 1 value to unpack
"""
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