Author: Carl Friedrich Bolz-Tereick <cfb...@gmx.de>
Branch: py3.6
Changeset: r97401:ce93612a2d0c
Date: 2019-09-07 17:56 +0200
http://bitbucket.org/pypy/pypy/changeset/ce93612a2d0c/

Log:    fix up the position of AST nodes of the expressions with an f-string

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
@@ -2740,9 +2740,9 @@
         w_metavar = get_field(space, w_node, 'metavar', False)
         w_lineno = get_field(space, w_node, 'lineno', False)
         w_col_offset = get_field(space, w_node, 'col_offset', False)
-        _metavar = space.int_w(w_metavar)
-        _lineno = space.int_w(w_lineno)
-        _col_offset = space.int_w(w_col_offset)
+        _metavar = obj_to_int(space, w_metavar, False)
+        _lineno = obj_to_int(space, w_lineno, False)
+        _col_offset = obj_to_int(space, w_col_offset, False)
         return RevDBMetaVar(_metavar, _lineno, _col_offset)
 
 State.ast_type('RevDBMetaVar', 'expr', ['metavar'])
diff --git a/pypy/interpreter/astcompiler/fstring.py 
b/pypy/interpreter/astcompiler/fstring.py
--- a/pypy/interpreter/astcompiler/fstring.py
+++ b/pypy/interpreter/astcompiler/fstring.py
@@ -25,7 +25,7 @@
 def f_constant_string(astbuilder, joined_pieces, w_u, atom_node):
     add_constant_string(astbuilder, joined_pieces, w_u, atom_node)
 
-def f_string_compile(astbuilder, source, atom_node):
+def f_string_compile(astbuilder, source, atom_node, fstr):
     # Note: a f-string is kept as a single literal up to here.
     # At this point only, we recursively call the AST compiler
     # on all the '{expr}' parts.  The 'expr' part is not parsed
@@ -44,16 +44,43 @@
         astbuilder.error("internal error: parser not available for parsing "
                    "the expressions inside the f-string", atom_node)
     assert isinstance(source, str)    # utf-8 encoded
-    source = '(%s)' % source
+
+    paren_source = '(%s)' % source  # to deal with whitespace at the start of 
source
+
+    lineno = 0
+    column_offset = 0
+    if fstr.stnode:
+        stnode = fstr.stnode
+        lineno = stnode.get_lineno() - 1 # one-based
+        # CPython has an equivalent hack :-(
+        column_offset = stnode.value.find(source) + stnode.get_column()
 
     info = pyparse.CompileInfo("<fstring>", "eval",
                                consts.PyCF_SOURCE_IS_UTF8 |
                                consts.PyCF_IGNORE_COOKIE,
                                optimize=astbuilder.compile_info.optimize)
     parser = astbuilder.recursive_parser
-    parse_tree = parser.parse_source(source, info)
-    return ast_from_node(astbuilder.space, parse_tree, info,
-                         recursive_parser=parser)
+    parse_tree = parser.parse_source(paren_source, info)
+
+    return fixup_fstring_positions(
+            ast_from_node(astbuilder.space, parse_tree, info,
+                         recursive_parser=parser),
+            lineno, column_offset)
+
+def fixup_fstring_positions(ast, line_offset, column_offset):
+    visitor = FixPosVisitor(line_offset, column_offset)
+    return ast.mutate_over(visitor)
+
+class FixPosVisitor(ast.ASTVisitor):
+    def __init__(self, line_offset, column_offset):
+        self.line_offset = line_offset
+        self.column_offset = column_offset
+
+    def default_visitor(self, node):
+        if isinstance(node, ast.stmt) or isinstance(node, ast.expr):
+            node.lineno += self.line_offset
+            node.col_offset += self.column_offset
+        return node
 
 
 def unexpected_end_of_string(astbuilder, atom_node):
@@ -177,7 +204,7 @@
     # Compile the expression as soon as possible, so we show errors
     # related to the expression before errors related to the
     # conversion or format_spec.
-    expr = f_string_compile(astbuilder, s[expr_start:i], atom_node)
+    expr = f_string_compile(astbuilder, s[expr_start:i], atom_node, fstr)
     assert isinstance(expr, ast.Expression)
 
     # Check for a conversion char, if present.
@@ -345,7 +372,7 @@
         child = atom_node.get_child(i)
         try:
             w_next = parsestring.parsestr(
-                    space, encoding, child.get_value())
+                    space, encoding, child.get_value(), child)
             if not isinstance(w_next, parsestring.W_FString):
                 add_constant_string(astbuilder, joined_pieces, w_next,
                                     atom_node)
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
@@ -22,7 +22,7 @@
             flags = consts.CO_FUTURE_WITH_STATEMENT
         info = pyparse.CompileInfo("<test>", p_mode, flags)
         tree = self.parser.parse_source(source, info)
-        ast_node = ast_from_node(self.space, tree, info)
+        ast_node = ast_from_node(self.space, tree, info, self.parser)
         return ast_node
 
     def get_first_expr(self, source, p_mode=None, flags=None):
@@ -1476,3 +1476,8 @@
                            " bytes in position 0-1: truncated \\xXX escape")
         assert exc.lineno == 2
         assert exc.offset == 6
+
+    def test_fstring_lineno(self):
+        mod = self.get_ast('x=1\nf"{    x + 1}"')
+        assert mod.body[1].value.values[0].value.lineno == 2
+        assert mod.body[1].value.values[0].value.col_offset == 8
diff --git a/pypy/interpreter/pyparser/parsestring.py 
b/pypy/interpreter/pyparser/parsestring.py
--- a/pypy/interpreter/pyparser/parsestring.py
+++ b/pypy/interpreter/pyparser/parsestring.py
@@ -7,14 +7,15 @@
 
 
 class W_FString(W_Root):
-    def __init__(self, unparsed, raw_mode):
+    def __init__(self, unparsed, raw_mode, stnode):
         assert isinstance(unparsed, str)    # utf-8 encoded string
         self.unparsed = unparsed     # but the quotes are removed
         self.raw_mode = raw_mode
         self.current_index = 0       # for astcompiler.fstring
+        self.stnode = stnode
 
 
-def parsestr(space, encoding, s):
+def parsestr(space, encoding, s, stnode=None):
     """Parses a string or unicode literal, and return usually
     a wrapped value.  If we get an f-string, then instead return
     an unparsed but unquoted W_FString instance.
@@ -88,7 +89,7 @@
     if unicode_literal and not rawmode: # XXX Py_UnicodeFlag is ignored for now
         assert 0 <= ps <= q
         if saw_f:
-            return W_FString(s[ps:q], rawmode)
+            return W_FString(s[ps:q], rawmode, stnode)
         if encoding is None:
             substr = s[ps:q]
         else:
@@ -112,7 +113,7 @@
         if not unicode_literal:
             return space.newbytes(substr)
         elif saw_f:
-            return W_FString(substr, rawmode)
+            return W_FString(substr, rawmode, stnode)
         else:
             v = unicodehelper.str_decode_utf8(substr, 'strict', True, None)
             return space.newtext(*v)
_______________________________________________
pypy-commit mailing list
pypy-commit@python.org
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to