[pypy-commit] pypy py3k: allow ... anywhere and make it its own token
gutworth
noreply at buildbot.pypy.org
Wed Mar 14 22:38:01 CET 2012
Author: Benjamin Peterson <benjamin at python.org>
Branch: py3k
Changeset: r53610:5d2f6423dabb
Date: 2012-03-14 16:37 -0500
http://bitbucket.org/pypy/pypy/changeset/5d2f6423dabb/
Log: allow ... anywhere and make it its own token
diff --git a/pypy/interpreter/astcompiler/ast.py b/pypy/interpreter/astcompiler/ast.py
--- a/pypy/interpreter/astcompiler/ast.py
+++ b/pypy/interpreter/astcompiler/ast.py
@@ -1637,6 +1637,25 @@
pass
+class Ellipsis(expr):
+
+ def __init__(self, lineno, col_offset):
+ expr.__init__(self, lineno, col_offset)
+ self.initialization_state = 3
+
+ def walkabout(self, visitor):
+ visitor.visit_Ellipsis(self)
+
+ def mutate_over(self, visitor):
+ return visitor.visit_Ellipsis(self)
+
+ def sync_app_attrs(self, space):
+ if (self.initialization_state & ~0) ^ 3:
+ self.missing_field(space, ['lineno', 'col_offset'], 'Ellipsis')
+ else:
+ pass
+
+
class Attribute(expr):
def __init__(self, value, attr, ctx, lineno, col_offset):
@@ -1874,24 +1893,6 @@
class slice(AST):
pass
-class Ellipsis(slice):
-
- def __init__(self):
- self.initialization_state = 0
-
- def walkabout(self, visitor):
- visitor.visit_Ellipsis(self)
-
- def mutate_over(self, visitor):
- return visitor.visit_Ellipsis(self)
-
- def sync_app_attrs(self, space):
- if (self.initialization_state & ~0) ^ 0:
- self.missing_field(space, [], 'Ellipsis')
- else:
- pass
-
-
class Slice(slice):
def __init__(self, lower, upper, step):
@@ -2562,6 +2563,8 @@
return self.default_visitor(node)
def visit_Bytes(self, node):
return self.default_visitor(node)
+ def visit_Ellipsis(self, node):
+ return self.default_visitor(node)
def visit_Attribute(self, node):
return self.default_visitor(node)
def visit_Subscript(self, node):
@@ -2576,8 +2579,6 @@
return self.default_visitor(node)
def visit_Const(self, node):
return self.default_visitor(node)
- def visit_Ellipsis(self, node):
- return self.default_visitor(node)
def visit_Slice(self, node):
return self.default_visitor(node)
def visit_ExtSlice(self, node):
@@ -2778,6 +2779,9 @@
def visit_Bytes(self, node):
pass
+ def visit_Ellipsis(self, node):
+ pass
+
def visit_Attribute(self, node):
node.value.walkabout(self)
@@ -2800,9 +2804,6 @@
def visit_Const(self, node):
pass
- def visit_Ellipsis(self, node):
- pass
-
def visit_Slice(self, node):
if node.lower:
node.lower.walkabout(self)
@@ -5830,6 +5831,23 @@
__init__=interp2app(Bytes_init),
)
+def Ellipsis_init(space, w_self, __args__):
+ w_self = space.descr_self_interp_w(Ellipsis, w_self)
+ args_w, kwargs_w = __args__.unpack()
+ if args_w:
+ w_err = space.wrap("Ellipsis constructor takes no arguments")
+ raise OperationError(space.w_TypeError, w_err)
+ for field, w_value in kwargs_w.iteritems():
+ space.setattr(w_self, space.wrap(field), w_value)
+
+Ellipsis.typedef = typedef.TypeDef("Ellipsis",
+ expr.typedef,
+ __module__='_ast',
+ _fields=_FieldsWrapper([]),
+ __new__=interp2app(get_AST_new(Ellipsis)),
+ __init__=interp2app(Ellipsis_init),
+)
+
def Attribute_get_value(space, w_self):
if w_self.w_dict is not None:
w_obj = w_self.getdictvalue(space, 'value')
@@ -6391,23 +6409,6 @@
__new__=interp2app(get_AST_new(slice)),
)
-def Ellipsis_init(space, w_self, __args__):
- w_self = space.descr_self_interp_w(Ellipsis, w_self)
- args_w, kwargs_w = __args__.unpack()
- if args_w:
- w_err = space.wrap("Ellipsis constructor takes no arguments")
- raise OperationError(space.w_TypeError, w_err)
- for field, w_value in kwargs_w.iteritems():
- space.setattr(w_self, space.wrap(field), w_value)
-
-Ellipsis.typedef = typedef.TypeDef("Ellipsis",
- slice.typedef,
- __module__='_ast',
- _fields=_FieldsWrapper([]),
- __new__=interp2app(get_AST_new(Ellipsis)),
- __init__=interp2app(Ellipsis_init),
-)
-
def Slice_get_lower(space, w_self):
if w_self.w_dict is not None:
w_obj = w_self.getdictvalue(space, 'lower')
diff --git a/pypy/interpreter/astcompiler/astbuilder.py b/pypy/interpreter/astcompiler/astbuilder.py
--- a/pypy/interpreter/astcompiler/astbuilder.py
+++ b/pypy/interpreter/astcompiler/astbuilder.py
@@ -211,11 +211,15 @@
dot_count = 0
while i < child_count:
child = import_node.children[i]
- if child.type == syms.dotted_name:
+ child_type = child.type
+ if child_type == syms.dotted_name:
module = self.alias_for_import_name(child, False)
i += 1
break
- elif child.type != tokens.DOT:
+ elif child_type == tokens.ELLIPSIS:
+ # Special case for tokenization.
+ dot_count += 2
+ elif child_type != tokens.DOT:
break
i += 1
dot_count += 1
@@ -923,8 +927,6 @@
def handle_slice(self, slice_node):
first_child = slice_node.children[0]
- if first_child.type == tokens.DOT:
- return ast.Ellipsis()
if len(slice_node.children) == 1 and first_child.type == syms.test:
index = self.handle_expr(first_child)
return ast.Index(index)
@@ -1138,6 +1140,8 @@
elif first_child_type == tokens.NUMBER:
num_value = self.parse_number(first_child.value)
return ast.Num(num_value, atom_node.lineno, atom_node.column)
+ elif first_child_type == tokens.ELLIPSIS:
+ return ast.Ellipsis(atom_node.lineno, atom_node.column)
elif first_child_type == tokens.LPAR:
second_child = atom_node.children[1]
if second_child.type == tokens.RPAR:
diff --git a/pypy/interpreter/astcompiler/asthelpers.py b/pypy/interpreter/astcompiler/asthelpers.py
--- a/pypy/interpreter/astcompiler/asthelpers.py
+++ b/pypy/interpreter/astcompiler/asthelpers.py
@@ -168,3 +168,9 @@
class __extend__(ast.Num):
constant = True
+
+
+class __extend__(ast.Ellipsis):
+
+ _description = "Ellipsis"
+ constant = True
diff --git a/pypy/interpreter/astcompiler/codegen.py b/pypy/interpreter/astcompiler/codegen.py
--- a/pypy/interpreter/astcompiler/codegen.py
+++ b/pypy/interpreter/astcompiler/codegen.py
@@ -825,6 +825,9 @@
space = self.space
self.load_const(const.value)
+ def visit_Ellipsis(self, e):
+ self.load_const(self.space.w_Ellipsis)
+
def visit_UnaryOp(self, op):
self.update_position(op.lineno)
op.operand.walkabout(self)
@@ -1126,9 +1129,7 @@
self.emit_op_arg(ops.BUILD_SLICE, arg)
def _nested_slice(self, slc, ctx):
- if isinstance(slc, ast.Ellipsis):
- self.load_const(self.space.w_Ellipsis)
- elif isinstance(slc, ast.Slice):
+ if isinstance(slc, ast.Slice):
self._complex_slice(slc, ctx)
elif isinstance(slc, ast.Index):
slc.value.walkabout(self)
@@ -1140,10 +1141,6 @@
kind = "index"
if ctx != ast.AugStore:
slc.value.walkabout(self)
- elif isinstance(slc, ast.Ellipsis):
- kind = "ellipsis"
- if ctx != ast.AugStore:
- self.load_const(self.space.w_Ellipsis)
elif isinstance(slc, ast.Slice):
kind = "slice"
if ctx != ast.AugStore:
diff --git a/pypy/interpreter/astcompiler/optimize.py b/pypy/interpreter/astcompiler/optimize.py
--- a/pypy/interpreter/astcompiler/optimize.py
+++ b/pypy/interpreter/astcompiler/optimize.py
@@ -62,6 +62,12 @@
return self.s
+class __extend__(ast.Ellipsis):
+
+ def as_constant_truth(self, space):
+ return True
+
+
class __extend__(ast.Const):
def as_constant(self):
diff --git a/pypy/interpreter/astcompiler/test/test_astbuilder.py b/pypy/interpreter/astcompiler/test/test_astbuilder.py
--- a/pypy/interpreter/astcompiler/test/test_astbuilder.py
+++ b/pypy/interpreter/astcompiler/test/test_astbuilder.py
@@ -765,6 +765,7 @@
("{1, 2, 3}", "literal"),
("(x > 4)", "comparison"),
("(x if y else a)", "conditional expression"),
+ ("...", "Ellipsis"),
)
test_contexts = (
("assign to", "%s = 23"),
@@ -1076,8 +1077,6 @@
slc = self.get_first_expr("x[1:2:3]").slice
for field in (slc.lower, slc.upper, slc.step):
assert isinstance(field, ast.Num)
- sub = self.get_first_expr("x[...]")
- assert isinstance(sub.slice, ast.Ellipsis)
sub = self.get_first_expr("x[1,2,3]")
slc = sub.slice
assert isinstance(slc, ast.Index)
@@ -1093,6 +1092,12 @@
assert isinstance(complex_slc.upper, ast.Num)
assert complex_slc.step is None
+ def test_ellipsis(self):
+ e = self.get_first_expr("...")
+ assert isinstance(e, ast.Ellipsis)
+ sub = self.get_first_expr("x[...]")
+ assert isinstance(sub.slice.value, ast.Ellipsis)
+
def test_string(self):
space = self.space
s = self.get_first_expr("'hi'")
diff --git a/pypy/interpreter/astcompiler/tools/Python.asdl b/pypy/interpreter/astcompiler/tools/Python.asdl
--- a/pypy/interpreter/astcompiler/tools/Python.asdl
+++ b/pypy/interpreter/astcompiler/tools/Python.asdl
@@ -69,6 +69,7 @@
| Num(object n) -- a number as a PyObject.
| Str(string s) -- need to specify raw, unicode, etc?
| Bytes(string s)
+ | Ellipsis
-- other literals? bools?
-- the following expression can appear in assignment context
@@ -87,7 +88,7 @@
expr_context = Load | Store | Del | AugLoad | AugStore | Param
- slice = Ellipsis | Slice(expr? lower, expr? upper, expr? step)
+ slice = Slice(expr? lower, expr? upper, expr? step)
| ExtSlice(slice* dims)
| Index(expr value)
diff --git a/pypy/interpreter/pyparser/data/Grammar3.2 b/pypy/interpreter/pyparser/data/Grammar3.2
--- a/pypy/interpreter/pyparser/data/Grammar3.2
+++ b/pypy/interpreter/pyparser/data/Grammar3.2
@@ -53,7 +53,8 @@
raise_stmt: 'raise' [test ['from' test]]
import_stmt: import_name | import_from
import_name: 'import' dotted_as_names
-import_from: ('from' ('.'* dotted_name | '.'+)
+# note below: the ('.' | '...') is necessary because '...' is tokenized as ELLIPSIS
+import_from: ('from' (('.' | '...')* dotted_name | ('.' | '...')+)
'import' ('*' | '(' import_as_names ')' | import_as_names))
import_as_name: NAME ['as' NAME]
dotted_as_name: dotted_name ['as' NAME]
@@ -106,12 +107,12 @@
atom: ('(' [yield_expr|testlist_comp] ')' |
'[' [testlist_comp] ']' |
'{' [dictorsetmaker] '}' |
- NAME | NUMBER | STRING+)
+ NAME | NUMBER | STRING+ | '...')
testlist_comp: (test|star_expr) ( comp_for | (',' (test|star_expr))* [','] )
lambdef: 'lambda' [varargslist] ':' test
trailer: '(' [arglist] ')' | '[' subscriptlist ']' | '.' NAME
subscriptlist: subscript (',' subscript)* [',']
-subscript: '.' '.' '.' | test | [test] ':' [test] [sliceop]
+subscript: test | [test] ':' [test] [sliceop]
sliceop: ':' [test]
exprlist: expr (',' expr)* [',']
testlist: test (',' test)* [',']
diff --git a/pypy/interpreter/pyparser/dfa_generated.py b/pypy/interpreter/pyparser/dfa_generated.py
--- a/pypy/interpreter/pyparser/dfa_generated.py
+++ b/pypy/interpreter/pyparser/dfa_generated.py
@@ -7,10 +7,10 @@
accepts = [True, True, True, True, True, True, True, True,
True, True, False, True, True, True, True, True,
False, False, False, True, False, False, False,
- True, False, True, False, True, False, True,
- False, False, True, False, False, True, True,
- True, False, False, True, False, False, False,
- True]
+ True, False, True, False, True, False, False,
+ True, False, False, True, False, False, True,
+ True, True, False, False, True, False, False,
+ False, True]
states = [
# 0
{'\t': 0, '\n': 14, '\x0c': 0,
@@ -104,9 +104,9 @@
'7': 5, '8': 5, '9': 5, 'E': 26,
'J': 14, 'e': 26, 'j': 14},
# 6
- {'0': 27, '1': 27, '2': 27, '3': 27,
- '4': 27, '5': 27, '6': 27, '7': 27,
- '8': 27, '9': 27},
+ {'.': 28, '0': 27, '1': 27, '2': 27,
+ '3': 27, '4': 27, '5': 27, '6': 27,
+ '7': 27, '8': 27, '9': 27},
# 7
{'*': 13, '=': 14},
# 8
@@ -126,27 +126,27 @@
# 15
{'\n': 14},
# 16
- {automata.DEFAULT: 31, '\n': 28,
- '\r': 28, "'": 29, '\\': 30},
+ {automata.DEFAULT: 32, '\n': 29,
+ '\r': 29, "'": 30, '\\': 31},
# 17
- {automata.DEFAULT: 34, '\n': 28,
- '\r': 28, '"': 32, '\\': 33},
+ {automata.DEFAULT: 35, '\n': 29,
+ '\r': 29, '"': 33, '\\': 34},
# 18
{'\n': 14, '\r': 15},
# 19
- {automata.DEFAULT: 19, '\n': 28, '\r': 28},
+ {automata.DEFAULT: 19, '\n': 29, '\r': 29},
# 20
- {'0': 35, '1': 35, '2': 35, '3': 35,
- '4': 35, '5': 35, '6': 35, '7': 35,
- '8': 35, '9': 35, 'A': 35, 'B': 35,
- 'C': 35, 'D': 35, 'E': 35, 'F': 35,
- 'a': 35, 'b': 35, 'c': 35, 'd': 35,
- 'e': 35, 'f': 35},
+ {'0': 36, '1': 36, '2': 36, '3': 36,
+ '4': 36, '5': 36, '6': 36, '7': 36,
+ '8': 36, '9': 36, 'A': 36, 'B': 36,
+ 'C': 36, 'D': 36, 'E': 36, 'F': 36,
+ 'a': 36, 'b': 36, 'c': 36, 'd': 36,
+ 'e': 36, 'f': 36},
# 21
- {'0': 36, '1': 36, '2': 36, '3': 36,
- '4': 36, '5': 36, '6': 36, '7': 36},
+ {'0': 37, '1': 37, '2': 37, '3': 37,
+ '4': 37, '5': 37, '6': 37, '7': 37},
# 22
- {'0': 37, '1': 37},
+ {'0': 38, '1': 38},
# 23
{'.': 25, '0': 23, '1': 24, '2': 24,
'3': 24, '4': 24, '5': 24, '6': 24,
@@ -160,71 +160,73 @@
# 25
{'0': 25, '1': 25, '2': 25, '3': 25,
'4': 25, '5': 25, '6': 25, '7': 25,
- '8': 25, '9': 25, 'E': 38, 'J': 14,
- 'e': 38, 'j': 14},
+ '8': 25, '9': 25, 'E': 39, 'J': 14,
+ 'e': 39, 'j': 14},
# 26
- {'+': 39, '-': 39, '0': 40, '1': 40,
- '2': 40, '3': 40, '4': 40, '5': 40,
- '6': 40, '7': 40, '8': 40, '9': 40},
+ {'+': 40, '-': 40, '0': 41, '1': 41,
+ '2': 41, '3': 41, '4': 41, '5': 41,
+ '6': 41, '7': 41, '8': 41, '9': 41},
# 27
{'0': 27, '1': 27, '2': 27, '3': 27,
'4': 27, '5': 27, '6': 27, '7': 27,
- '8': 27, '9': 27, 'E': 38, 'J': 14,
- 'e': 38, 'j': 14},
+ '8': 27, '9': 27, 'E': 39, 'J': 14,
+ 'e': 39, 'j': 14},
# 28
+ {'.': 14},
+ # 29
{},
- # 29
+ # 30
{"'": 14},
- # 30
- {automata.DEFAULT: 41, '\n': 14, '\r': 15},
# 31
- {automata.DEFAULT: 31, '\n': 28,
- '\r': 28, "'": 14, '\\': 30},
+ {automata.DEFAULT: 42, '\n': 14, '\r': 15},
# 32
+ {automata.DEFAULT: 32, '\n': 29,
+ '\r': 29, "'": 14, '\\': 31},
+ # 33
{'"': 14},
- # 33
- {automata.DEFAULT: 42, '\n': 14, '\r': 15},
# 34
- {automata.DEFAULT: 34, '\n': 28,
- '\r': 28, '"': 14, '\\': 33},
+ {automata.DEFAULT: 43, '\n': 14, '\r': 15},
# 35
- {'0': 35, '1': 35, '2': 35, '3': 35,
- '4': 35, '5': 35, '6': 35, '7': 35,
- '8': 35, '9': 35, 'A': 35, 'B': 35,
- 'C': 35, 'D': 35, 'E': 35, 'F': 35,
- 'a': 35, 'b': 35, 'c': 35, 'd': 35,
- 'e': 35, 'f': 35},
+ {automata.DEFAULT: 35, '\n': 29,
+ '\r': 29, '"': 14, '\\': 34},
# 36
{'0': 36, '1': 36, '2': 36, '3': 36,
- '4': 36, '5': 36, '6': 36, '7': 36},
+ '4': 36, '5': 36, '6': 36, '7': 36,
+ '8': 36, '9': 36, 'A': 36, 'B': 36,
+ 'C': 36, 'D': 36, 'E': 36, 'F': 36,
+ 'a': 36, 'b': 36, 'c': 36, 'd': 36,
+ 'e': 36, 'f': 36},
# 37
- {'0': 37, '1': 37},
+ {'0': 37, '1': 37, '2': 37, '3': 37,
+ '4': 37, '5': 37, '6': 37, '7': 37},
# 38
- {'+': 43, '-': 43, '0': 44, '1': 44,
- '2': 44, '3': 44, '4': 44, '5': 44,
- '6': 44, '7': 44, '8': 44, '9': 44},
+ {'0': 38, '1': 38},
# 39
- {'0': 40, '1': 40, '2': 40, '3': 40,
- '4': 40, '5': 40, '6': 40, '7': 40,
- '8': 40, '9': 40},
+ {'+': 44, '-': 44, '0': 45, '1': 45,
+ '2': 45, '3': 45, '4': 45, '5': 45,
+ '6': 45, '7': 45, '8': 45, '9': 45},
# 40
- {'0': 40, '1': 40, '2': 40, '3': 40,
- '4': 40, '5': 40, '6': 40, '7': 40,
- '8': 40, '9': 40, 'J': 14, 'j': 14},
+ {'0': 41, '1': 41, '2': 41, '3': 41,
+ '4': 41, '5': 41, '6': 41, '7': 41,
+ '8': 41, '9': 41},
# 41
- {automata.DEFAULT: 41, '\n': 28,
- '\r': 28, "'": 14, '\\': 30},
+ {'0': 41, '1': 41, '2': 41, '3': 41,
+ '4': 41, '5': 41, '6': 41, '7': 41,
+ '8': 41, '9': 41, 'J': 14, 'j': 14},
# 42
- {automata.DEFAULT: 42, '\n': 28,
- '\r': 28, '"': 14, '\\': 33},
+ {automata.DEFAULT: 42, '\n': 29,
+ '\r': 29, "'": 14, '\\': 31},
# 43
- {'0': 44, '1': 44, '2': 44, '3': 44,
- '4': 44, '5': 44, '6': 44, '7': 44,
- '8': 44, '9': 44},
+ {automata.DEFAULT: 43, '\n': 29,
+ '\r': 29, '"': 14, '\\': 34},
# 44
- {'0': 44, '1': 44, '2': 44, '3': 44,
- '4': 44, '5': 44, '6': 44, '7': 44,
- '8': 44, '9': 44, 'J': 14, 'j': 14},
+ {'0': 45, '1': 45, '2': 45, '3': 45,
+ '4': 45, '5': 45, '6': 45, '7': 45,
+ '8': 45, '9': 45},
+ # 45
+ {'0': 45, '1': 45, '2': 45, '3': 45,
+ '4': 45, '5': 45, '6': 45, '7': 45,
+ '8': 45, '9': 45, 'J': 14, 'j': 14},
]
pseudoDFA = automata.DFA(states, accepts)
diff --git a/pypy/interpreter/pyparser/gendfa.py b/pypy/interpreter/pyparser/gendfa.py
--- a/pypy/interpreter/pyparser/gendfa.py
+++ b/pypy/interpreter/pyparser/gendfa.py
@@ -142,6 +142,7 @@
bracket = groupStr(states, "[](){}")
special = group(states,
makeEOL(),
+ chainStr(states, "..."),
groupStr(states, "@:;.,`"))
funny = group(states, operator, bracket, special)
# ____________________________________________________________
diff --git a/pypy/interpreter/pyparser/pytoken.py b/pypy/interpreter/pyparser/pytoken.py
--- a/pypy/interpreter/pyparser/pytoken.py
+++ b/pypy/interpreter/pyparser/pytoken.py
@@ -62,6 +62,7 @@
_add_tok('DOUBLESLASHEQUAL',"//=" )
_add_tok('AT', "@" )
_add_tok('RARROW', "->")
+_add_tok('ELLIPSIS', "...")
_add_tok('OP')
_add_tok('ERRORTOKEN')
diff --git a/pypy/interpreter/pyparser/pytokenizer.py b/pypy/interpreter/pyparser/pytokenizer.py
--- a/pypy/interpreter/pyparser/pytokenizer.py
+++ b/pypy/interpreter/pyparser/pytokenizer.py
@@ -172,8 +172,9 @@
pos = end
token, initial = line[start:end], line[start]
- if initial in numchars or \
- (initial == '.' and token != '.'): # ordinary number
+ if (initial in numchars or \
+ (initial == '.' and token != '.' and token != '...')):
+ # ordinary number
token_list.append((tokens.NUMBER, token, lnum, start, line))
last_comment = ''
elif initial in '\r\n':
diff --git a/pypy/interpreter/test/test_compiler.py b/pypy/interpreter/test/test_compiler.py
--- a/pypy/interpreter/test/test_compiler.py
+++ b/pypy/interpreter/test/test_compiler.py
@@ -449,6 +449,9 @@
space = self.space
w_d = space.newdict()
space.exec_(code, w_d, w_d)
+ snip = "d[. . .]"
+ space.raises_w(space.w_SyntaxError, self.compiler.compile,
+ snip, '<test>', 'exec', 0)
def test_chained_access_augassign(self):
snippet = str(py.code.Source(r'''
@@ -757,6 +760,12 @@
assert math.copysign(1., ns['c'][0]) == -1.0
assert math.copysign(1., ns['c'][1]) == -1.0
+ def test_ellipsis_anywhere(self):
+ """
+ x = ...
+ assert x is Ellipsis
+ """
+
class AppTestOptimizer:
@@ -793,6 +802,10 @@
assert isinstance(ns["x"][0], int)
assert isinstance(ns["y"][0], int)
+ def test_ellipsis_truth(self):
+ co = compile("if ...: x + 3\nelse: x + 4", "<test>", "exec")
+ assert 4 not in co.co_consts
+
def test_division_folding(self):
def code(source):
return compile(source, "<test>", "exec")
More information about the pypy-commit
mailing list