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

Reply via email to