From 1371e8b9345d3500bc536150e6bfd684dc6c9c9d Mon Sep 17 00:00:00 2001 From: "Eric V. Smith" Date: Sat, 19 Sep 2015 14:51:32 -0400 Subject: Issue #24965: Implement PEP 498 "Literal String Interpolation". Documentation is still needed, I'll open an issue for that. --- Python/compile.c | 117 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 116 insertions(+), 1 deletion(-) (limited to 'Python/compile.c') diff --git a/Python/compile.c b/Python/compile.c index a6884ec211..3a49ecec28 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -731,6 +731,7 @@ compiler_set_qualname(struct compiler *c) return 1; } + /* Allocate a new block and return a pointer to it. Returns NULL on error. */ @@ -3209,6 +3210,117 @@ compiler_call(struct compiler *c, expr_ty e) e->v.Call.keywords); } +static int +compiler_joined_str(struct compiler *c, expr_ty e) +{ + /* Concatenate parts of a string using ''.join(parts). There are + probably better ways of doing this. + + This is used for constructs like "'x=' f'{42}'", which have to + be evaluated at compile time. */ + + static PyObject *empty_string; + static PyObject *join_string; + + if (!empty_string) { + empty_string = PyUnicode_FromString(""); + if (!empty_string) + return 0; + } + if (!join_string) { + join_string = PyUnicode_FromString("join"); + if (!join_string) + return 0; + } + + ADDOP_O(c, LOAD_CONST, empty_string, consts); + ADDOP_NAME(c, LOAD_ATTR, join_string, names); + VISIT_SEQ(c, expr, e->v.JoinedStr.values); + ADDOP_I(c, BUILD_LIST, asdl_seq_LEN(e->v.JoinedStr.values)); + ADDOP_I(c, CALL_FUNCTION, 1); + return 1; +} + +/* Note that this code uses the builtin functions format(), str(), + repr(), and ascii(). You can break this code, or make it do odd + things, by redefining those functions. */ +static int +compiler_formatted_value(struct compiler *c, expr_ty e) +{ + PyObject *conversion_name = NULL; + + static PyObject *format_string; + static PyObject *str_string; + static PyObject *repr_string; + static PyObject *ascii_string; + + if (!format_string) { + format_string = PyUnicode_InternFromString("format"); + if (!format_string) + return 0; + } + + if (!str_string) { + str_string = PyUnicode_InternFromString("str"); + if (!str_string) + return 0; + } + + if (!repr_string) { + repr_string = PyUnicode_InternFromString("repr"); + if (!repr_string) + return 0; + } + if (!ascii_string) { + ascii_string = PyUnicode_InternFromString("ascii"); + if (!ascii_string) + return 0; + } + + ADDOP_NAME(c, LOAD_GLOBAL, format_string, names); + + /* If needed, convert via str, repr, or ascii. */ + if (e->v.FormattedValue.conversion != -1) { + switch (e->v.FormattedValue.conversion) { + case 's': + conversion_name = str_string; + break; + case 'r': + conversion_name = repr_string; + break; + case 'a': + conversion_name = ascii_string; + break; + default: + PyErr_SetString(PyExc_SystemError, + "Unrecognized conversion character"); + return 0; + } + ADDOP_NAME(c, LOAD_GLOBAL, conversion_name, names); + } + + /* Evaluate the value. */ + VISIT(c, expr, e->v.FormattedValue.value); + + /* If needed, convert via str, repr, or ascii. */ + if (conversion_name) { + /* Call the function we previously pushed. */ + ADDOP_I(c, CALL_FUNCTION, 1); + } + + /* If we have a format spec, use format(value, format_spec). Otherwise, + use the single argument form. */ + if (e->v.FormattedValue.format_spec) { + VISIT(c, expr, e->v.FormattedValue.format_spec); + ADDOP_I(c, CALL_FUNCTION, 2); + } else { + /* No format spec specified, call format(value). */ + ADDOP_I(c, CALL_FUNCTION, 1); + } + + return 1; +} + /* shared code between compiler_call and compiler_class */ static int compiler_call_helper(struct compiler *c, @@ -3878,6 +3990,10 @@ compiler_visit_expr(struct compiler *c, expr_ty e) case Str_kind: ADDOP_O(c, LOAD_CONST, e->v.Str.s, consts); break; + case JoinedStr_kind: + return compiler_joined_str(c, e); + case FormattedValue_kind: + return compiler_formatted_value(c, e); case Bytes_kind: ADDOP_O(c, LOAD_CONST, e->v.Bytes.s, consts); break; @@ -4784,4 +4900,3 @@ PyAST_Compile(mod_ty mod, const char *filename, PyCompilerFlags *flags, { return PyAST_CompileEx(mod, filename, flags, -1, arena); } - -- cgit v1.2.1 From 008499d3f1761db65815faebb31bf95ab7ef9cd4 Mon Sep 17 00:00:00 2001 From: "Eric V. Smith" Date: Tue, 3 Nov 2015 12:45:05 -0500 Subject: Issue 25483: Add an opcode to make f-string formatting more robust. --- Python/compile.c | 98 +++++++++++++++++++------------------------------------- 1 file changed, 33 insertions(+), 65 deletions(-) (limited to 'Python/compile.c') diff --git a/Python/compile.c b/Python/compile.c index 5b53de3ee7..030afedd29 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -1067,6 +1067,10 @@ PyCompile_OpcodeStackEffect(int opcode, int oparg) return 1; case GET_YIELD_FROM_ITER: return 0; + case FORMAT_VALUE: + /* If there's a fmt_spec on the stack, we go from 2->1, + else 1->1. */ + return (oparg & FVS_MASK) == FVS_HAVE_SPEC ? -1 : 0; default: return PY_INVALID_STACK_EFFECT; } @@ -3241,83 +3245,47 @@ compiler_joined_str(struct compiler *c, expr_ty e) return 1; } -/* Note that this code uses the builtin functions format(), str(), - repr(), and ascii(). You can break this code, or make it do odd - things, by redefining those functions. */ +/* Used to implement f-strings. Format a single value. */ static int compiler_formatted_value(struct compiler *c, expr_ty e) { - PyObject *conversion_name = NULL; - - static PyObject *format_string; - static PyObject *str_string; - static PyObject *repr_string; - static PyObject *ascii_string; - - if (!format_string) { - format_string = PyUnicode_InternFromString("format"); - if (!format_string) - return 0; - } - - if (!str_string) { - str_string = PyUnicode_InternFromString("str"); - if (!str_string) - return 0; - } - - if (!repr_string) { - repr_string = PyUnicode_InternFromString("repr"); - if (!repr_string) - return 0; - } - if (!ascii_string) { - ascii_string = PyUnicode_InternFromString("ascii"); - if (!ascii_string) - return 0; - } + /* Our oparg encodes 2 pieces of information: the conversion + character, and whether or not a format_spec was provided. + + Convert the conversion char to 2 bits: + None: 000 0x0 FVC_NONE + !s : 001 0x1 FVC_STR + !r : 010 0x2 FVC_REPR + !a : 011 0x3 FVC_ASCII + + next bit is whether or not we have a format spec: + yes : 100 0x4 + no : 000 0x0 + */ - ADDOP_NAME(c, LOAD_GLOBAL, format_string, names); + int oparg; - /* If needed, convert via str, repr, or ascii. */ - if (e->v.FormattedValue.conversion != -1) { - switch (e->v.FormattedValue.conversion) { - case 's': - conversion_name = str_string; - break; - case 'r': - conversion_name = repr_string; - break; - case 'a': - conversion_name = ascii_string; - break; - default: - PyErr_SetString(PyExc_SystemError, - "Unrecognized conversion character"); - return 0; - } - ADDOP_NAME(c, LOAD_GLOBAL, conversion_name, names); - } - - /* Evaluate the value. */ + /* Evaluate the expression to be formatted. */ VISIT(c, expr, e->v.FormattedValue.value); - /* If needed, convert via str, repr, or ascii. */ - if (conversion_name) { - /* Call the function we previously pushed. */ - ADDOP_I(c, CALL_FUNCTION, 1); + switch (e->v.FormattedValue.conversion) { + case 's': oparg = FVC_STR; break; + case 'r': oparg = FVC_REPR; break; + case 'a': oparg = FVC_ASCII; break; + case -1: oparg = FVC_NONE; break; + default: + PyErr_SetString(PyExc_SystemError, + "Unrecognized conversion character"); + return 0; } - - /* If we have a format spec, use format(value, format_spec). Otherwise, - use the single argument form. */ if (e->v.FormattedValue.format_spec) { + /* Evaluate the format spec, and update our opcode arg. */ VISIT(c, expr, e->v.FormattedValue.format_spec); - ADDOP_I(c, CALL_FUNCTION, 2); - } else { - /* No format spec specified, call format(value). */ - ADDOP_I(c, CALL_FUNCTION, 1); + oparg |= FVS_HAVE_SPEC; } + /* And push our opcode and oparg */ + ADDOP_I(c, FORMAT_VALUE, oparg); return 1; } -- cgit v1.2.1