Author: Armin Rigo <[email protected]>
Branch: py3.5-fstring-pep498
Changeset: r89700:7b27b30dddca
Date: 2017-01-22 23:58 +0100
http://bitbucket.org/pypy/pypy/changeset/7b27b30dddca/
Log: {...:format_spec}
diff --git a/pypy/interpreter/astcompiler/assemble.py
b/pypy/interpreter/astcompiler/assemble.py
--- a/pypy/interpreter/astcompiler/assemble.py
+++ b/pypy/interpreter/astcompiler/assemble.py
@@ -759,7 +759,8 @@
return -_num_args(arg) - 1
def _compute_FORMAT_VALUE(arg):
- #if arg contains some flag: return -1
+ if (arg & consts.FVS_MASK) == consts.FVS_HAVE_SPEC:
+ return -1
return 0
def _compute_BUILD_STRING(arg):
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
@@ -1238,7 +1238,7 @@
parse_tree = self.recursive_parser.parse_source(source, info)
return ast_from_node(self.space, parse_tree, info)
- def _f_string_expr(self, joined_pieces, u, start, atom_node):
+ def _f_string_expr(self, joined_pieces, u, start, atom_node, rec=0):
conversion = -1 # the conversion char. -1 if not specified.
format_spec = None
nested_depth = 0 # nesting level for braces/parens/brackets in exprs
@@ -1270,7 +1270,14 @@
self.error("f-string: invalid conversion character: "
"expected 's', 'r', or 'a'", atom_node)
if ch == u':':
- XXX
+ if rec >= 2:
+ self.error("f-string: expressions nested too deeply",
atom_node)
+ subpieces = []
+ p = self._parse_f_string(subpieces, u, p, atom_node, rec + 1)
+ format_spec = self._f_string_to_ast_node(subpieces, atom_node)
+ ch = u[p] if p >= 0 else u'\x00'
+ p += 1
+
if ch != u'}':
self.error("f-string: expecting '}'", atom_node)
end_f_string = p
@@ -1283,36 +1290,50 @@
joined_pieces.append(fval)
return end_f_string
- def _parse_f_string(self, joined_pieces, w_string, atom_node):
+ def _parse_f_string(self, joined_pieces, u, start, atom_node, rec=0):
space = self.space
- u = space.unicode_w(w_string)
- start = 0
- p1 = u.find(u'{')
+ p1 = u.find(u'{', start)
+ prestart = start
while True:
if p1 < 0:
p1 = len(u)
p2 = u.find(u'}', start, p1)
if p2 >= 0:
+ self._f_constant_string(joined_pieces, u[prestart:p2],
+ atom_node)
pn = p2 + 1
if pn < len(u) and u[pn] == u'}': # '}}' => single '}'
- self._f_constant_string(joined_pieces, u[start:pn],
- atom_node)
start = pn + 1
- else:
- self.error("f-string: single '}' is not allowed",
atom_node)
- continue
- self._f_constant_string(joined_pieces, u[start:p1], atom_node)
+ prestart = pn
+ continue
+ return p2 # found a single '}', stop here
+ self._f_constant_string(joined_pieces, u[prestart:p1], atom_node)
if p1 == len(u):
- break # no more '{' or '}' left
+ return -1 # no more '{' or '}' left
pn = p1 + 1
if pn < len(u) and u[pn] == u'{': # '{{' => single '{'
- start = pn
- p1 = u.find(u'{', start + 1)
+ start = pn + 1
+ prestart = pn
else:
assert u[p1] == u'{'
- start = self._f_string_expr(joined_pieces, u, pn, atom_node)
+ start = self._f_string_expr(joined_pieces, u, pn,
+ atom_node, rec)
assert u[start - 1] == u'}'
- p1 = u.find(u'{', start)
+ prestart = start
+ p1 = u.find(u'{', start)
+
+ def _f_string_to_ast_node(self, joined_pieces, atom_node):
+ # remove empty Strs
+ values = [node for node in joined_pieces
+ if not (isinstance(node, ast.Str) and not node.s)]
+ if len(values) > 1:
+ return ast.JoinedStr(values, atom_node.get_lineno(),
+ atom_node.get_column())
+ elif len(values) == 1:
+ return values[0]
+ else:
+ assert len(joined_pieces) > 0 # they are all empty strings
+ return joined_pieces[0]
def handle_atom(self, atom_node):
first_child = atom_node.get_child(0)
@@ -1349,7 +1370,12 @@
if not saw_f:
self._add_constant_string(joined_pieces, w_next, atom_node)
else:
- self._parse_f_string(joined_pieces, w_next, atom_node)
+ p = self._parse_f_string(joined_pieces,
+ space.unicode_w(w_next), 0,
+ atom_node)
+ if p != -1:
+ self.error("f-string: single '}' is not allowed",
+ atom_node)
if len(joined_pieces) == 1: # <= the common path
return joined_pieces[0] # ast.Str, Bytes or FormattedValue
# with more than one piece, it is a combination of Str and
@@ -1359,17 +1385,7 @@
if isinstance(node, ast.Bytes):
self.error("cannot mix bytes and nonbytes literals",
atom_node)
- # remove empty Strs
- values = [node for node in joined_pieces
- if not (isinstance(node, ast.Str) and not node.s)]
- if len(values) > 1:
- return ast.JoinedStr(values, atom_node.get_lineno(),
- atom_node.get_column())
- elif len(values) == 1:
- return values[0]
- else:
- assert len(joined_pieces) > 0 # they are all empty strings
- return joined_pieces[0]
+ return self._f_string_to_ast_node(joined_pieces, atom_node)
#
elif first_child_type == tokens.NUMBER:
num_value = self.parse_number(first_child.get_value())
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
@@ -1503,6 +1503,9 @@
if fmt.conversion == ord('s'): arg = consts.FVC_STR
if fmt.conversion == ord('r'): arg = consts.FVC_REPR
if fmt.conversion == ord('a'): arg = consts.FVC_ASCII
+ if fmt.format_spec is not None:
+ arg |= consts.FVS_HAVE_SPEC
+ fmt.format_spec.walkabout(self)
self.emit_op_arg(ops.FORMAT_VALUE, arg)
diff --git a/pypy/interpreter/astcompiler/test/test_compiler.py
b/pypy/interpreter/astcompiler/test/test_compiler.py
--- a/pypy/interpreter/astcompiler/test/test_compiler.py
+++ b/pypy/interpreter/astcompiler/test/test_compiler.py
@@ -1176,6 +1176,9 @@
yield self.st, """x = 'hi'; z = f'''{\nx}'''""", 'z', 'hi'
+ yield self.st, """x = 'hi'; z = f'{x:5}'""", 'z', 'hi '
+ yield self.st, """x = 42; z = f'{x:5}'""", 'z', ' 42'
+
def test_fstring_error(self):
raises(SyntaxError, self.run, "f'{}'")
raises(SyntaxError, self.run, "f'{ \t }'")
diff --git a/pypy/interpreter/pyopcode.py b/pypy/interpreter/pyopcode.py
--- a/pypy/interpreter/pyopcode.py
+++ b/pypy/interpreter/pyopcode.py
@@ -1614,6 +1614,11 @@
def FORMAT_VALUE(self, oparg, next_instr):
from pypy.interpreter.astcompiler import consts
space = self.space
+ #
+ if (oparg & consts.FVS_MASK) == consts.FVS_HAVE_SPEC:
+ w_spec = self.popvalue()
+ else:
+ w_spec = space.newunicode(u'')
w_value = self.popvalue()
#
conversion = oparg & consts.FVC_MASK
@@ -1625,7 +1630,7 @@
from pypy.objspace.std.unicodeobject import ascii_from_object
w_value = ascii_from_object(space, w_value)
#
- w_res = space.format(w_value, space.newunicode(u''))
+ w_res = space.format(w_value, w_spec)
self.pushvalue(w_res)
@jit.unroll_safe
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit