summaryrefslogtreecommitdiffstats
path: root/Python/ast.c
diff options
context:
space:
mode:
Diffstat (limited to 'Python/ast.c')
-rw-r--r--Python/ast.c72
1 files changed, 67 insertions, 5 deletions
diff --git a/Python/ast.c b/Python/ast.c
index 89206c3..7d30e5f 100644
--- a/Python/ast.c
+++ b/Python/ast.c
@@ -8,6 +8,7 @@
#include "node.h"
#include "ast.h"
#include "token.h"
+#include "pythonrun.h"
#include <assert.h>
@@ -4270,6 +4271,55 @@ decode_bytes_with_escapes(struct compiling *c, const node *n, const char *s,
return result;
}
+/* Shift locations for the given node and all its children by adding `lineno`
+ and `col_offset` to existing locations. */
+static void fstring_shift_node_locations(node *n, int lineno, int col_offset)
+{
+ n->n_col_offset = n->n_col_offset + col_offset;
+ for (int i = 0; i < NCH(n); ++i) {
+ if (n->n_lineno && n->n_lineno < CHILD(n, i)->n_lineno) {
+ /* Shifting column offsets unnecessary if there's been newlines. */
+ col_offset = 0;
+ }
+ fstring_shift_node_locations(CHILD(n, i), lineno, col_offset);
+ }
+ n->n_lineno = n->n_lineno + lineno;
+}
+
+/* Fix locations for the given node and its children.
+
+ `parent` is the enclosing node.
+ `n` is the node which locations are going to be fixed relative to parent.
+ `expr_str` is the child node's string representation, incuding braces.
+*/
+static void
+fstring_fix_node_location(const node *parent, node *n, char *expr_str)
+{
+ char *substr = NULL;
+ char *start;
+ int lines = LINENO(parent) - 1;
+ int cols = parent->n_col_offset;
+ /* Find the full fstring to fix location information in `n`. */
+ while (parent && parent->n_type != STRING)
+ parent = parent->n_child;
+ if (parent && parent->n_str) {
+ substr = strstr(parent->n_str, expr_str);
+ if (substr) {
+ start = substr;
+ while (start > parent->n_str) {
+ if (start[0] == '\n')
+ break;
+ start--;
+ }
+ cols += substr - start;
+ /* Fix lineno in mulitline strings. */
+ while ((substr = strchr(substr + 1, '\n')))
+ lines--;
+ }
+ }
+ fstring_shift_node_locations(n, lines, cols);
+}
+
/* Compile this expression in to an expr_ty. Add parens around the
expression, in order to allow leading spaces in the expression. */
static expr_ty
@@ -4278,6 +4328,7 @@ fstring_compile_expr(const char *expr_start, const char *expr_end,
{
PyCompilerFlags cf;
+ node *mod_n;
mod_ty mod;
char *str;
Py_ssize_t len;
@@ -4287,9 +4338,10 @@ fstring_compile_expr(const char *expr_start, const char *expr_end,
assert(*(expr_start-1) == '{');
assert(*expr_end == '}' || *expr_end == '!' || *expr_end == ':');
- /* If the substring is all whitespace, it's an error. We need to catch
- this here, and not when we call PyParser_ASTFromString, because turning
- the expression '' in to '()' would go from being invalid to valid. */
+ /* If the substring is all whitespace, it's an error. We need to catch this
+ here, and not when we call PyParser_SimpleParseStringFlagsFilename,
+ because turning the expression '' in to '()' would go from being invalid
+ to valid. */
for (s = expr_start; s != expr_end; s++) {
char c = *s;
/* The Python parser ignores only the following whitespace
@@ -4315,9 +4367,19 @@ fstring_compile_expr(const char *expr_start, const char *expr_end,
str[len+2] = 0;
cf.cf_flags = PyCF_ONLY_AST;
- mod = PyParser_ASTFromString(str, "<fstring>",
- Py_eval_input, &cf, c->c_arena);
+ mod_n = PyParser_SimpleParseStringFlagsFilename(str, "<fstring>",
+ Py_eval_input, 0);
+ if (!mod_n) {
+ PyMem_RawFree(str);
+ return NULL;
+ }
+ /* Reuse str to find the correct column offset. */
+ str[0] = '{';
+ str[len+1] = '}';
+ fstring_fix_node_location(n, mod_n, str);
+ mod = PyAST_FromNode(mod_n, &cf, "<fstring>", c->c_arena);
PyMem_RawFree(str);
+ PyNode_Free(mod_n);
if (!mod)
return NULL;
return mod->v.Expression.body;