]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-132661: Implement PEP 750 (#132662)
authorLysandros Nikolaou <lisandrosnik@gmail.com>
Wed, 30 Apr 2025 09:46:41 +0000 (11:46 +0200)
committerGitHub <noreply@github.com>
Wed, 30 Apr 2025 09:46:41 +0000 (11:46 +0200)
Co-authored-by: Lysandros Nikolaou <lisandrosnik@gmail.com>
Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com>
Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com>
Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com>
Co-authored-by: Wingy <git@wingysam.xyz>
Co-authored-by: Koudai Aono <koxudaxi@gmail.com>
Co-authored-by: Dave Peck <davepeck@gmail.com>
Co-authored-by: Terry Jan Reedy <tjreedy@udel.edu>
Co-authored-by: Paul Everitt <pauleveritt@me.com>
Co-authored-by: sobolevn <mail@sobolevn.me>
81 files changed:
.github/CODEOWNERS
Doc/library/token.rst
Doc/whatsnew/3.14.rst
Grammar/Tokens
Grammar/python.gram
Include/internal/pycore_ast.h
Include/internal/pycore_ast_state.h
Include/internal/pycore_global_objects_fini_generated.h
Include/internal/pycore_global_strings.h
Include/internal/pycore_interpolation.h [new file with mode: 0644]
Include/internal/pycore_magic_number.h
Include/internal/pycore_opcode_metadata.h
Include/internal/pycore_runtime_init_generated.h
Include/internal/pycore_template.h [new file with mode: 0644]
Include/internal/pycore_token.h
Include/internal/pycore_unicodeobject_generated.h
Include/internal/pycore_uop_ids.h
Include/internal/pycore_uop_metadata.h
Include/opcode_ids.h
Lib/_ast_unparse.py
Lib/_opcode_metadata.py
Lib/string/__init__.py [moved from Lib/string.py with 100% similarity]
Lib/string/templatelib.py [new file with mode: 0644]
Lib/test/.ruff.toml
Lib/test/support/ast_helper.py
Lib/test/test_ast/data/ast_repr.txt
Lib/test/test_ast/snippets.py
Lib/test/test_ast/test_ast.py
Lib/test/test_fstring.py
Lib/test/test_grammar.py
Lib/test/test_string/__init__.py [new file with mode: 0644]
Lib/test/test_string/_support.py [new file with mode: 0644]
Lib/test/test_string/test_string.py [moved from Lib/test/test_string.py with 100% similarity]
Lib/test/test_string/test_templatelib.py [new file with mode: 0644]
Lib/test/test_syntax.py
Lib/test/test_tstring.py [new file with mode: 0644]
Lib/test/test_unparse.py
Lib/token.py
Lib/tokenize.py
Makefile.pre.in
Misc/NEWS.d/next/Core_and_Builtins/2025-04-22-15-37-05.gh-issue-132661.XE_A42.rst [new file with mode: 0644]
Objects/clinic/interpolationobject.c.h [new file with mode: 0644]
Objects/interpolationobject.c [new file with mode: 0644]
Objects/object.c
Objects/templateobject.c [new file with mode: 0644]
Objects/unicodeobject.c
PCbuild/_freeze_module.vcxproj
PCbuild/pythoncore.vcxproj
PCbuild/pythoncore.vcxproj.filters
Parser/Python.asdl
Parser/action_helpers.c
Parser/lexer/buffer.c
Parser/lexer/lexer.c
Parser/lexer/lexer.h
Parser/lexer/state.c
Parser/lexer/state.h
Parser/parser.c
Parser/pegen.h
Parser/string_parser.c
Parser/token.c
Parser/tokenizer/file_tokenizer.c
Parser/tokenizer/readline_tokenizer.c
Programs/test_frozenmain.h
Python/Python-ast.c
Python/ast.c
Python/ast_opt.c
Python/ast_unparse.c
Python/bytecodes.c
Python/ceval.c
Python/codegen.c
Python/executor_cases.c.h
Python/generated_cases.c.h
Python/jit.c
Python/opcode_targets.h
Python/optimizer_cases.c.h
Python/pylifecycle.c
Python/symtable.c
Tools/build/generate_token.py
Tools/c-analyzer/TODO
Tools/c-analyzer/cpython/globals-to-fix.tsv
Tools/jit/template.c

index bd199f8168bf9a8e96eabf6597313c6f17fb2201..11e8acd963aa032abecb28aac5e2fee8fe91f0a8 100644 (file)
@@ -320,3 +320,9 @@ Lib/test/test__colorize.py    @hugovk
 
 # Fuzzing
 Modules/_xxtestfuzz/          @ammaraskar
+
+# t-strings
+**/*interpolationobject*      @lysnikolaou
+**/*templateobject*           @lysnikolaou
+**/*templatelib*              @lysnikolaou
+**/*tstring*                  @lysnikolaou
index 24455b1ef778931c0fb21a5f533739711386e250..1529d173e17af4a64bdbe3d36de0c75b52510044 100644 (file)
@@ -131,6 +131,41 @@ The token constants are:
 
       The token string contains the closing quote(s).
 
+.. data:: TSTRING_START
+
+   Token value used to indicate the beginning of a template string literal.
+
+   .. impl-detail::
+
+      The token string includes the prefix and the opening quote(s), but none
+      of the contents of the literal.
+
+   .. versionadded:: next
+
+.. data:: TSTRING_MIDDLE
+
+   Token value used for literal text inside a template string literal
+   including format specifications.
+
+   .. impl-detail::
+
+      Replacement fields (that is, the non-literal parts of t-strings) use
+      the same tokens as other expressions, and are delimited by
+      :data:`LBRACE`, :data:`RBRACE`, :data:`EXCLAMATION` and :data:`COLON`
+      tokens.
+
+   .. versionadded:: next
+
+.. data:: TSTRING_END
+
+   Token value used to indicate the end of a template string literal.
+
+   .. impl-detail::
+
+      The token string contains the closing quote(s).
+
+   .. versionadded:: next
+
 .. data:: ENDMARKER
 
    Token value that indicates the end of input.
index 9e6b69fbc0527350e284741b71f48952d65224a4..dad63e47a62dba68cdeb83812127e95409972a00 100644 (file)
@@ -66,6 +66,7 @@ Summary -- release highlights
 
 * :ref:`PEP 649: deferred evaluation of annotations <whatsnew314-pep649>`
 * :ref:`PEP 741: Python Configuration C API <whatsnew314-pep741>`
+* :ref:`PEP 750: Template Strings <whatsnew314-pep750>`
 * :ref:`PEP 758: Allow except and except* expressions without parentheses <whatsnew314-pep758>`
 * :ref:`PEP 761: Discontinuation of PGP signatures <whatsnew314-pep761>`
 * :ref:`PEP 765: Disallow return/break/continue that exit a finally block <whatsnew314-pep765>`
@@ -92,6 +93,76 @@ If you encounter :exc:`NameError`\s or pickling errors coming out of
 New features
 ============
 
+.. _whatsnew314-pep750:
+
+PEP 750: Template Strings
+-------------------------
+
+Template string literals (t-strings) are a generalization of f-strings,
+using a ``t`` in place of the ``f`` prefix. Instead of evaluating
+to :class:`str`, t-strings evaluate to a new :class:`!string.templatelib.Template` type:
+
+.. code-block:: python
+
+   from string.templatelib import Template
+
+   name = "World"
+   template: Template = t"Hello {name}"
+
+The template can then be combined with functions that operate on the template's
+structure to produce a :class:`str` or a string-like result.
+For example, sanitizing input:
+
+.. code-block:: python
+
+   evil = "<script>alert('evil')</script>"
+   template = t"<p>{evil}</p>"
+   assert html(template) == "<p>&lt;script&gt;alert('evil')&lt;/script&gt;</p>"
+
+As another example, generating HTML attributes from data:
+
+.. code-block:: python
+
+   attributes = {"src": "shrubbery.jpg", "alt": "looks nice"}
+   template = t"<img {attributes} />"
+   assert html(template) == '<img src="shrubbery.jpg" alt="looks nice" class="looks-nice" />'
+
+Unlike f-strings, the ``html`` function has access to template attributes
+containing the original information: static strings, interpolations, and values
+from the original scope. Unlike existing templating approaches, t-strings build
+from the well-known f-string syntax and rules. Template systems thus benefit
+from Python tooling as they are much closer to the Python language, syntax,
+scoping, and more.
+
+Writing template handlers is straightforward:
+
+.. code-block:: python
+
+   from string.templatelib import Template, Interpolation
+
+   def lower_upper(template: Template) -> str:
+       """Render static parts lowercased and interpolations uppercased."""
+       parts: list[str] = []
+       for item in template:
+           if isinstance(item, Interpolation):
+               parts.append(str(item.value).upper())
+           else:
+               parts.append(item.lower())
+       return "".join(parts)
+
+   name = "world"
+   assert lower_upper(t"HELLO {name}") == "hello WORLD"
+
+With this in place, developers can write template systems to sanitize SQL, make
+safe shell operations, improve logging, tackle modern ideas in web development
+(HTML, CSS, and so on), and implement lightweight, custom business DSLs.
+
+See :pep:`750` for more details.
+
+(Contributed by Jim Baker, Guido van Rossum, Paul Everitt, Koudai Aono,
+Lysandros Nikolaou, Dave Peck, Adam Turner, Jelle Zijlstra, Bénédikt Tran,
+and Pablo Galindo Salgado in :gh:`132661`.)
+
 .. _whatsnew314-pep768:
 
 PEP 768: Safe external debugger interface for CPython
index 20bb803b7d58a64bf60ace54ba04e09999ab0df4..e40a4437afb009defa185302e957f9ac30b2bf62 100644 (file)
@@ -62,6 +62,9 @@ SOFT_KEYWORD
 FSTRING_START
 FSTRING_MIDDLE
 FSTRING_END
+TSTRING_START
+TSTRING_MIDDLE
+TSTRING_END
 COMMENT
 NL
 ERRORTOKEN
index 009f5534ea7ee258668656579421b40d56747f5a..940063e5c7ba1e0b56211b2e4ee92edc09866211 100644 (file)
@@ -519,7 +519,7 @@ literal_pattern[pattern_ty]:
 literal_expr[expr_ty]:
     | signed_number !('+' | '-')
     | complex_number
-    | strings
+    | &(STRING|FSTRING_START|TSTRING_START) strings
     | 'None' { _PyAST_Constant(Py_None, NULL, EXTRA) }
     | 'True' { _PyAST_Constant(Py_True, NULL, EXTRA) }
     | 'False' { _PyAST_Constant(Py_False, NULL, EXTRA) }
@@ -859,7 +859,7 @@ atom[expr_ty]:
     | 'True' { _PyAST_Constant(Py_True, NULL, EXTRA) }
     | 'False' { _PyAST_Constant(Py_False, NULL, EXTRA) }
     | 'None' { _PyAST_Constant(Py_None, NULL, EXTRA) }
-    | &(STRING|FSTRING_START) strings
+    | &(STRING|FSTRING_START|TSTRING_START) strings
     | NUMBER
     | &'(' (tuple | group | genexp)
     | &'[' (list | listcomp)
@@ -935,7 +935,7 @@ fstring_middle[expr_ty]:
 fstring_replacement_field[expr_ty]:
     | '{' a=annotated_rhs debug_expr='='? conversion=[fstring_conversion] format=[fstring_full_format_spec] rbrace='}' {
         _PyPegen_formatted_value(p, a, debug_expr, conversion, format, rbrace, EXTRA) }
-    | invalid_replacement_field
+    | invalid_fstring_replacement_field
 fstring_conversion[ResultTokenWithMetadata*]:
     | conv_token="!" conv=NAME { _PyPegen_check_fstring_conversion(p, conv_token, conv) }
 fstring_full_format_spec[ResultTokenWithMetadata*]:
@@ -946,8 +946,27 @@ fstring_format_spec[expr_ty]:
 fstring[expr_ty]:
     | a=FSTRING_START b=fstring_middle* c=FSTRING_END { _PyPegen_joined_str(p, a, (asdl_expr_seq*)b, c) }
 
+tstring_format_spec_replacement_field[expr_ty]:
+    | '{' a=annotated_rhs debug_expr='='? conversion=[fstring_conversion] format=[tstring_full_format_spec] rbrace='}' {
+        _PyPegen_formatted_value(p, a, debug_expr, conversion, format, rbrace, EXTRA) }
+    | invalid_tstring_replacement_field
+tstring_format_spec[expr_ty]:
+    | t=TSTRING_MIDDLE { _PyPegen_decoded_constant_from_token(p, t) }
+    | tstring_format_spec_replacement_field
+tstring_full_format_spec[ResultTokenWithMetadata*]:
+    | colon=':' spec=tstring_format_spec* { _PyPegen_setup_full_format_spec(p, colon, (asdl_expr_seq *) spec, EXTRA) }
+tstring_replacement_field[expr_ty]:
+    | '{' a=annotated_rhs debug_expr='='? conversion=[fstring_conversion] format=[tstring_full_format_spec] rbrace='}' {
+        _PyPegen_interpolation(p, a, debug_expr, conversion, format, rbrace, EXTRA) }
+    | invalid_tstring_replacement_field
+tstring_middle[expr_ty]:
+    | tstring_replacement_field
+    | t=TSTRING_MIDDLE { _PyPegen_constant_from_token(p, t) }
+tstring[expr_ty] (memo):
+    | a=TSTRING_START b=tstring_middle* c=TSTRING_END { _PyPegen_template_str(p, a, (asdl_expr_seq*)b, c) }
+
 string[expr_ty]: s[Token*]=STRING { _PyPegen_constant_from_string(p, s) }
-strings[expr_ty] (memo): a[asdl_expr_seq*]=(fstring|string)+ { _PyPegen_concatenate_strings(p, a, EXTRA) }
+strings[expr_ty] (memo): a[asdl_expr_seq*]=(fstring|string|tstring)+ { _PyPegen_concatenate_strings(p, a, EXTRA) }
 
 list[expr_ty]:
     | '[' a=[star_named_expressions] ']' { _PyAST_List(a, Load, EXTRA) }
@@ -1212,6 +1231,8 @@ invalid_expression:
         RAISE_SYNTAX_ERROR_KNOWN_LOCATION (a, "expected expression before 'if', but statement is given") }
    | a='lambda' [lambda_params] b=':' &FSTRING_MIDDLE  {
         RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, b, "f-string: lambda expressions are not allowed without parentheses") }
+   | a='lambda' [lambda_params] b=':' &TSTRING_MIDDLE  {
+        RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, b, "t-string: lambda expressions are not allowed without parentheses") }
 
 invalid_named_expression(memo):
     | a=expression ':=' expression {
@@ -1454,17 +1475,17 @@ invalid_starred_expression_unpacking:
 invalid_starred_expression:
     | '*' { RAISE_SYNTAX_ERROR("Invalid star expression") }
 
-invalid_replacement_field:
+invalid_fstring_replacement_field:
     | '{' a='=' { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "f-string: valid expression required before '='") }
     | '{' a='!' { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "f-string: valid expression required before '!'") }
     | '{' a=':' { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "f-string: valid expression required before ':'") }
     | '{' a='}' { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "f-string: valid expression required before '}'") }
-    | '{' !annotated_rhs { RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN("f-string: expecting a valid expression after '{'")}
+    | '{' !annotated_rhs { RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN("f-string: expecting a valid expression after '{'") }
     | '{' annotated_rhs !('=' | '!' | ':' | '}') {
         PyErr_Occurred() ? NULL : RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN("f-string: expecting '=', or '!', or ':', or '}'") }
     | '{' annotated_rhs '=' !('!' | ':' | '}') {
         PyErr_Occurred() ? NULL : RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN("f-string: expecting '!', or ':', or '}'") }
-    | '{' annotated_rhs '='? invalid_conversion_character
+    | '{' annotated_rhs '='? invalid_fstring_conversion_character
     | '{' annotated_rhs '='? ['!' NAME] !(':' | '}') {
         PyErr_Occurred() ? NULL : RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN("f-string: expecting ':' or '}'") }
     | '{' annotated_rhs '='? ['!' NAME] ':' fstring_format_spec* !'}' {
@@ -1472,10 +1493,32 @@ invalid_replacement_field:
     | '{' annotated_rhs '='? ['!' NAME] !'}' {
         PyErr_Occurred() ? NULL : RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN("f-string: expecting '}'") }
 
-invalid_conversion_character:
+invalid_fstring_conversion_character:
     | '!' &(':' | '}') { RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN("f-string: missing conversion character") }
     | '!' !NAME { RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN("f-string: invalid conversion character") }
 
+invalid_tstring_replacement_field:
+    | '{' a='=' { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "t-string: valid expression required before '='") }
+    | '{' a='!' { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "t-string: valid expression required before '!'") }
+    | '{' a=':' { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "t-string: valid expression required before ':'") }
+    | '{' a='}' { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "t-string: valid expression required before '}'") }
+    | '{' !annotated_rhs { RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN("t-string: expecting a valid expression after '{'") }
+    | '{' annotated_rhs !('=' | '!' | ':' | '}') {
+        PyErr_Occurred() ? NULL : RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN("t-string: expecting '=', or '!', or ':', or '}'") }
+    | '{' annotated_rhs '=' !('!' | ':' | '}') {
+        PyErr_Occurred() ? NULL : RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN("t-string: expecting '!', or ':', or '}'") }
+    | '{' annotated_rhs '='? invalid_tstring_conversion_character
+    | '{' annotated_rhs '='? ['!' NAME] !(':' | '}') {
+        PyErr_Occurred() ? NULL : RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN("t-string: expecting ':' or '}'") }
+    | '{' annotated_rhs '='? ['!' NAME] ':' fstring_format_spec* !'}' {
+        PyErr_Occurred() ? NULL : RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN("t-string: expecting '}', or format specs") }
+    | '{' annotated_rhs '='? ['!' NAME] !'}' {
+        PyErr_Occurred() ? NULL : RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN("t-string: expecting '}'") }
+
+invalid_tstring_conversion_character:
+    | '!' &(':' | '}') { RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN("t-string: missing conversion character") }
+    | '!' !NAME { RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN("t-string: invalid conversion character") }
+
 invalid_arithmetic:
     | sum ('+'|'-'|'*'|'/'|'%'|'//'|'@') a='not' b=inversion { RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, b, "'not' after an operator must be parenthesized") }
 invalid_factor:
index 69abc3536e312a3e0460336b29496f4742d01ff0..60367202bab6370a027998275208863939fad654 100644 (file)
@@ -361,9 +361,10 @@ enum _expr_kind {BoolOp_kind=1, NamedExpr_kind=2, BinOp_kind=3, UnaryOp_kind=4,
                   ListComp_kind=9, SetComp_kind=10, DictComp_kind=11,
                   GeneratorExp_kind=12, Await_kind=13, Yield_kind=14,
                   YieldFrom_kind=15, Compare_kind=16, Call_kind=17,
-                  FormattedValue_kind=18, JoinedStr_kind=19, Constant_kind=20,
-                  Attribute_kind=21, Subscript_kind=22, Starred_kind=23,
-                  Name_kind=24, List_kind=25, Tuple_kind=26, Slice_kind=27};
+                  FormattedValue_kind=18, Interpolation_kind=19,
+                  JoinedStr_kind=20, TemplateStr_kind=21, Constant_kind=22,
+                  Attribute_kind=23, Subscript_kind=24, Starred_kind=25,
+                  Name_kind=26, List_kind=27, Tuple_kind=28, Slice_kind=29};
 struct _expr {
     enum _expr_kind kind;
     union {
@@ -459,10 +460,21 @@ struct _expr {
             expr_ty format_spec;
         } FormattedValue;
 
+        struct {
+            expr_ty value;
+            constant str;
+            int conversion;
+            expr_ty format_spec;
+        } Interpolation;
+
         struct {
             asdl_expr_seq *values;
         } JoinedStr;
 
+        struct {
+            asdl_expr_seq *values;
+        } TemplateStr;
+
         struct {
             constant value;
             string kind;
@@ -820,8 +832,14 @@ expr_ty _PyAST_Call(expr_ty func, asdl_expr_seq * args, asdl_keyword_seq *
 expr_ty _PyAST_FormattedValue(expr_ty value, int conversion, expr_ty
                               format_spec, int lineno, int col_offset, int
                               end_lineno, int end_col_offset, PyArena *arena);
+expr_ty _PyAST_Interpolation(expr_ty value, constant str, int conversion,
+                             expr_ty format_spec, int lineno, int col_offset,
+                             int end_lineno, int end_col_offset, PyArena
+                             *arena);
 expr_ty _PyAST_JoinedStr(asdl_expr_seq * values, int lineno, int col_offset,
                          int end_lineno, int end_col_offset, PyArena *arena);
+expr_ty _PyAST_TemplateStr(asdl_expr_seq * values, int lineno, int col_offset,
+                           int end_lineno, int end_col_offset, PyArena *arena);
 expr_ty _PyAST_Constant(constant value, string kind, int lineno, int
                         col_offset, int end_lineno, int end_col_offset, PyArena
                         *arena);
index 2f9d5ea366eade35eb7a6f7a5d2d8329c5c0821c..d4ac419f51d6b2d2c46de6f7b6aa89a8027a9f09 100644 (file)
@@ -75,6 +75,7 @@ struct ast_state {
     PyObject *In_singleton;
     PyObject *In_type;
     PyObject *Interactive_type;
+    PyObject *Interpolation_type;
     PyObject *Invert_singleton;
     PyObject *Invert_type;
     PyObject *IsNot_singleton;
@@ -137,6 +138,7 @@ struct ast_state {
     PyObject *Sub_singleton;
     PyObject *Sub_type;
     PyObject *Subscript_type;
+    PyObject *TemplateStr_type;
     PyObject *TryStar_type;
     PyObject *Try_type;
     PyObject *Tuple_type;
@@ -242,6 +244,7 @@ struct ast_state {
     PyObject *slice;
     PyObject *step;
     PyObject *stmt_type;
+    PyObject *str;
     PyObject *subject;
     PyObject *tag;
     PyObject *target;
index e412db1de68f8b0dd1eb5fa0daec81a225cbda76..121466dd2ec1ce62dc8c1934f0379193d9c1a193 100644 (file)
@@ -880,6 +880,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) {
     _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(consts));
     _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(context));
     _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(contravariant));
+    _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(conversion));
     _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(cookie));
     _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(copy));
     _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(copyreg));
@@ -937,6 +938,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) {
     _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(exception));
     _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(existing_file_name));
     _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(exp));
+    _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(expression));
     _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(extend));
     _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(extra_tokens));
     _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(facility));
index 2a6c2065af6bb90fe75df815812baaadfe819386..20e2e6f2a7fc66be6e5ee8ddb7276ef950b82b70 100644 (file)
@@ -371,6 +371,7 @@ struct _Py_global_strings {
         STRUCT_FOR_ID(consts)
         STRUCT_FOR_ID(context)
         STRUCT_FOR_ID(contravariant)
+        STRUCT_FOR_ID(conversion)
         STRUCT_FOR_ID(cookie)
         STRUCT_FOR_ID(copy)
         STRUCT_FOR_ID(copyreg)
@@ -428,6 +429,7 @@ struct _Py_global_strings {
         STRUCT_FOR_ID(exception)
         STRUCT_FOR_ID(existing_file_name)
         STRUCT_FOR_ID(exp)
+        STRUCT_FOR_ID(expression)
         STRUCT_FOR_ID(extend)
         STRUCT_FOR_ID(extra_tokens)
         STRUCT_FOR_ID(facility)
diff --git a/Include/internal/pycore_interpolation.h b/Include/internal/pycore_interpolation.h
new file mode 100644 (file)
index 0000000..dd610c1
--- /dev/null
@@ -0,0 +1,26 @@
+#ifndef Py_INTERNAL_INTERPOLATION_H
+#define Py_INTERNAL_INTERPOLATION_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef Py_BUILD_CORE
+#  error "this header requires Py_BUILD_CORE define"
+#endif
+
+extern PyTypeObject _PyInterpolation_Type;
+
+#define _PyInterpolation_CheckExact(op) Py_IS_TYPE((op), &_PyInterpolation_Type)
+
+PyAPI_FUNC(PyObject *) _PyInterpolation_Build(PyObject *value, PyObject *str,
+                                              int conversion, PyObject *format_spec);
+
+extern PyStatus _PyInterpolation_InitTypes(PyInterpreterState *interp);
+extern PyObject *_PyInterpolation_GetValueRef(PyObject *interpolation);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
index f75b05893affc15acf383becb64e7bd8e0ef48bc..a96cb6236f78b7c2c6782bfbde13169d7d347fb1 100644 (file)
@@ -275,6 +275,7 @@ Known values:
     Python 3.14a6 3620 (Optimize bytecode for all/any/tuple called on a genexp)
     Python 3.14a7 3621 (Optimize LOAD_FAST opcodes into LOAD_FAST_BORROW)
     Python 3.14a7 3622 (Store annotations in different class dict keys)
+    Python 3.14a7 3623 (Add BUILD_INTERPOLATION & BUILD_TEMPLATE opcodes)
 
     Python 3.15 will start with 3650
 
@@ -287,7 +288,7 @@ PC/launcher.c must also be updated.
 
 */
 
-#define PYC_MAGIC_NUMBER 3622
+#define PYC_MAGIC_NUMBER 3623
 /* This is equivalent to converting PYC_MAGIC_NUMBER to 2 bytes
    (little-endian) and then appending b'\r\n'. */
 #define PYC_MAGIC_NUMBER_TOKEN \
index 3b881b30b054f1f7670fa7929fcd553934859586..23751f36ef3d4aa4021c1d4308abcc7ddade3ee3 100644 (file)
@@ -70,6 +70,8 @@ int _PyOpcode_num_popped(int opcode, int oparg)  {
             return 2;
         case BINARY_SLICE:
             return 3;
+        case BUILD_INTERPOLATION:
+            return 2 + (oparg & 1);
         case BUILD_LIST:
             return oparg;
         case BUILD_MAP:
@@ -80,6 +82,8 @@ int _PyOpcode_num_popped(int opcode, int oparg)  {
             return oparg;
         case BUILD_STRING:
             return oparg;
+        case BUILD_TEMPLATE:
+            return 2;
         case BUILD_TUPLE:
             return oparg;
         case CACHE:
@@ -551,6 +555,8 @@ int _PyOpcode_num_pushed(int opcode, int oparg)  {
             return 1;
         case BINARY_SLICE:
             return 1;
+        case BUILD_INTERPOLATION:
+            return 1;
         case BUILD_LIST:
             return 1;
         case BUILD_MAP:
@@ -561,6 +567,8 @@ int _PyOpcode_num_pushed(int opcode, int oparg)  {
             return 1;
         case BUILD_STRING:
             return 1;
+        case BUILD_TEMPLATE:
+            return 1;
         case BUILD_TUPLE:
             return 1;
         case CACHE:
@@ -1082,11 +1090,13 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[267] = {
     [BINARY_OP_SUBTRACT_FLOAT] = { true, INSTR_FMT_IXC0000, HAS_EXIT_FLAG | HAS_ERROR_FLAG },
     [BINARY_OP_SUBTRACT_INT] = { true, INSTR_FMT_IXC0000, HAS_EXIT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG },
     [BINARY_SLICE] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ESCAPES_FLAG },
+    [BUILD_INTERPOLATION] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG },
     [BUILD_LIST] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG },
     [BUILD_MAP] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG },
     [BUILD_SET] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG },
     [BUILD_SLICE] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ERROR_FLAG },
     [BUILD_STRING] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ERROR_FLAG },
+    [BUILD_TEMPLATE] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ESCAPES_FLAG },
     [BUILD_TUPLE] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG },
     [CACHE] = { true, INSTR_FMT_IX, 0 },
     [CALL] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_EVAL_BREAK_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG },
@@ -1330,11 +1340,13 @@ _PyOpcode_macro_expansion[256] = {
     [BINARY_OP_SUBTRACT_FLOAT] = { .nuops = 3, .uops = { { _GUARD_TOS_FLOAT, OPARG_SIMPLE, 0 }, { _GUARD_NOS_FLOAT, OPARG_SIMPLE, 0 }, { _BINARY_OP_SUBTRACT_FLOAT, OPARG_SIMPLE, 5 } } },
     [BINARY_OP_SUBTRACT_INT] = { .nuops = 3, .uops = { { _GUARD_TOS_INT, OPARG_SIMPLE, 0 }, { _GUARD_NOS_INT, OPARG_SIMPLE, 0 }, { _BINARY_OP_SUBTRACT_INT, OPARG_SIMPLE, 5 } } },
     [BINARY_SLICE] = { .nuops = 1, .uops = { { _BINARY_SLICE, OPARG_SIMPLE, 0 } } },
+    [BUILD_INTERPOLATION] = { .nuops = 1, .uops = { { _BUILD_INTERPOLATION, OPARG_SIMPLE, 0 } } },
     [BUILD_LIST] = { .nuops = 1, .uops = { { _BUILD_LIST, OPARG_SIMPLE, 0 } } },
     [BUILD_MAP] = { .nuops = 1, .uops = { { _BUILD_MAP, OPARG_SIMPLE, 0 } } },
     [BUILD_SET] = { .nuops = 1, .uops = { { _BUILD_SET, OPARG_SIMPLE, 0 } } },
     [BUILD_SLICE] = { .nuops = 1, .uops = { { _BUILD_SLICE, OPARG_SIMPLE, 0 } } },
     [BUILD_STRING] = { .nuops = 1, .uops = { { _BUILD_STRING, OPARG_SIMPLE, 0 } } },
+    [BUILD_TEMPLATE] = { .nuops = 1, .uops = { { _BUILD_TEMPLATE, OPARG_SIMPLE, 0 } } },
     [BUILD_TUPLE] = { .nuops = 1, .uops = { { _BUILD_TUPLE, OPARG_SIMPLE, 0 } } },
     [CALL_ALLOC_AND_ENTER_INIT] = { .nuops = 4, .uops = { { _CHECK_PEP_523, OPARG_SIMPLE, 1 }, { _CHECK_AND_ALLOCATE_OBJECT, 2, 1 }, { _CREATE_INIT_FRAME, OPARG_SIMPLE, 3 }, { _PUSH_FRAME, OPARG_SIMPLE, 3 } } },
     [CALL_BOUND_METHOD_EXACT_ARGS] = { .nuops = 9, .uops = { { _CHECK_PEP_523, OPARG_SIMPLE, 1 }, { _CHECK_CALL_BOUND_METHOD_EXACT_ARGS, OPARG_SIMPLE, 1 }, { _INIT_CALL_BOUND_METHOD_EXACT_ARGS, OPARG_SIMPLE, 1 }, { _CHECK_FUNCTION_VERSION, 2, 1 }, { _CHECK_FUNCTION_EXACT_ARGS, OPARG_SIMPLE, 3 }, { _CHECK_STACK_SPACE, OPARG_SIMPLE, 3 }, { _INIT_CALL_PY_EXACT_ARGS, OPARG_SIMPLE, 3 }, { _SAVE_RETURN_OFFSET, OPARG_SAVE_RETURN_OFFSET, 3 }, { _PUSH_FRAME, OPARG_SIMPLE, 3 } } },
@@ -1517,11 +1529,13 @@ const char *_PyOpcode_OpName[267] = {
     [BINARY_OP_SUBTRACT_FLOAT] = "BINARY_OP_SUBTRACT_FLOAT",
     [BINARY_OP_SUBTRACT_INT] = "BINARY_OP_SUBTRACT_INT",
     [BINARY_SLICE] = "BINARY_SLICE",
+    [BUILD_INTERPOLATION] = "BUILD_INTERPOLATION",
     [BUILD_LIST] = "BUILD_LIST",
     [BUILD_MAP] = "BUILD_MAP",
     [BUILD_SET] = "BUILD_SET",
     [BUILD_SLICE] = "BUILD_SLICE",
     [BUILD_STRING] = "BUILD_STRING",
+    [BUILD_TEMPLATE] = "BUILD_TEMPLATE",
     [BUILD_TUPLE] = "BUILD_TUPLE",
     [CACHE] = "CACHE",
     [CALL] = "CALL",
@@ -1782,11 +1796,13 @@ const uint8_t _PyOpcode_Deopt[256] = {
     [BINARY_OP_SUBTRACT_FLOAT] = BINARY_OP,
     [BINARY_OP_SUBTRACT_INT] = BINARY_OP,
     [BINARY_SLICE] = BINARY_SLICE,
+    [BUILD_INTERPOLATION] = BUILD_INTERPOLATION,
     [BUILD_LIST] = BUILD_LIST,
     [BUILD_MAP] = BUILD_MAP,
     [BUILD_SET] = BUILD_SET,
     [BUILD_SLICE] = BUILD_SLICE,
     [BUILD_STRING] = BUILD_STRING,
+    [BUILD_TEMPLATE] = BUILD_TEMPLATE,
     [BUILD_TUPLE] = BUILD_TUPLE,
     [CACHE] = CACHE,
     [CALL] = CALL,
@@ -1995,8 +2011,6 @@ const uint8_t _PyOpcode_Deopt[256] = {
 #endif // NEED_OPCODE_METADATA
 
 #define EXTRA_CASES \
-    case 119: \
-    case 120: \
     case 121: \
     case 122: \
     case 123: \
index 2368157a4fd18bd8b1c21081dd0e63f616373907..de1dfd0cce8d7e09fe702a27a37efd27630d48b1 100644 (file)
@@ -878,6 +878,7 @@ extern "C" {
     INIT_ID(consts), \
     INIT_ID(context), \
     INIT_ID(contravariant), \
+    INIT_ID(conversion), \
     INIT_ID(cookie), \
     INIT_ID(copy), \
     INIT_ID(copyreg), \
@@ -935,6 +936,7 @@ extern "C" {
     INIT_ID(exception), \
     INIT_ID(existing_file_name), \
     INIT_ID(exp), \
+    INIT_ID(expression), \
     INIT_ID(extend), \
     INIT_ID(extra_tokens), \
     INIT_ID(facility), \
diff --git a/Include/internal/pycore_template.h b/Include/internal/pycore_template.h
new file mode 100644 (file)
index 0000000..f2f8bf9
--- /dev/null
@@ -0,0 +1,26 @@
+#ifndef Py_INTERNAL_TEMPLATE_H
+#define Py_INTERNAL_TEMPLATE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef Py_BUILD_CORE
+#  error "this header requires Py_BUILD_CORE define"
+#endif
+
+extern PyTypeObject _PyTemplate_Type;
+extern PyTypeObject _PyTemplateIter_Type;
+
+#define _PyTemplate_CheckExact(op) Py_IS_TYPE((op), &_PyTemplate_Type)
+#define _PyTemplateIter_CheckExact(op) Py_IS_TYPE((op), &_PyTemplateIter_Type)
+
+extern PyObject *_PyTemplate_Concat(PyObject *self, PyObject *other);
+
+PyAPI_FUNC(PyObject *) _PyTemplate_Build(PyObject *strings, PyObject *interpolations);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
index 571cd6249f28126a573667ea41b0d52138aa9cff..5de1f719a2f1a284ab905af3a1691ece58b36383 100644 (file)
@@ -75,10 +75,13 @@ extern "C" {
 #define FSTRING_START   59
 #define FSTRING_MIDDLE  60
 #define FSTRING_END     61
-#define COMMENT         62
-#define NL              63
-#define ERRORTOKEN      64
-#define N_TOKENS        66
+#define TSTRING_START   62
+#define TSTRING_MIDDLE  63
+#define TSTRING_END     64
+#define COMMENT         65
+#define NL              66
+#define ERRORTOKEN      67
+#define N_TOKENS        69
 #define NT_OFFSET       256
 
 /* Special definitions for cooperation with parser */
@@ -91,7 +94,8 @@ extern "C" {
                                  (x) == INDENT    || \
                                  (x) == DEDENT)
 #define ISSTRINGLIT(x)          ((x) == STRING           || \
-                                 (x) == FSTRING_MIDDLE)
+                                 (x) == FSTRING_MIDDLE   || \
+                                 (x) == TSTRING_MIDDLE)
 
 
 // Export these 4 symbols for 'test_peg_generator'
index 72c3346328a5527b9e8582b214f6c318b54c2711..ad78dc8c4d589a12e2d84989794e12d602a12843 100644 (file)
@@ -1272,6 +1272,10 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) {
     _PyUnicode_InternStatic(interp, &string);
     assert(_PyUnicode_CheckConsistency(string, 1));
     assert(PyUnicode_GET_LENGTH(string) != 1);
+    string = &_Py_ID(conversion);
+    _PyUnicode_InternStatic(interp, &string);
+    assert(_PyUnicode_CheckConsistency(string, 1));
+    assert(PyUnicode_GET_LENGTH(string) != 1);
     string = &_Py_ID(cookie);
     _PyUnicode_InternStatic(interp, &string);
     assert(_PyUnicode_CheckConsistency(string, 1));
@@ -1500,6 +1504,10 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) {
     _PyUnicode_InternStatic(interp, &string);
     assert(_PyUnicode_CheckConsistency(string, 1));
     assert(PyUnicode_GET_LENGTH(string) != 1);
+    string = &_Py_ID(expression);
+    _PyUnicode_InternStatic(interp, &string);
+    assert(_PyUnicode_CheckConsistency(string, 1));
+    assert(PyUnicode_GET_LENGTH(string) != 1);
     string = &_Py_ID(extend);
     _PyUnicode_InternStatic(interp, &string);
     assert(_PyUnicode_CheckConsistency(string, 1));
index d6d81f88c8e00cb0390899c0e1d33c3d3dd5b8e4..1c7fde3fa24e42a17420a090ee18c07b015bdfe9 100644 (file)
@@ -28,11 +28,13 @@ extern "C" {
 #define _BINARY_OP_SUBTRACT_FLOAT 316
 #define _BINARY_OP_SUBTRACT_INT 317
 #define _BINARY_SLICE 318
+#define _BUILD_INTERPOLATION BUILD_INTERPOLATION
 #define _BUILD_LIST BUILD_LIST
 #define _BUILD_MAP BUILD_MAP
 #define _BUILD_SET BUILD_SET
 #define _BUILD_SLICE BUILD_SLICE
 #define _BUILD_STRING BUILD_STRING
+#define _BUILD_TEMPLATE BUILD_TEMPLATE
 #define _BUILD_TUPLE BUILD_TUPLE
 #define _CALL_BUILTIN_CLASS 319
 #define _CALL_BUILTIN_FAST 320
index 0ea8e56b6cd0bcbef7f19f9596d104e1b2e72fcd..71378e8782b2bbb3b4bfbdb812044494321b5982 100644 (file)
@@ -150,6 +150,8 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = {
     [_STORE_DEREF] = HAS_ARG_FLAG | HAS_FREE_FLAG | HAS_ESCAPES_FLAG,
     [_COPY_FREE_VARS] = HAS_ARG_FLAG,
     [_BUILD_STRING] = HAS_ARG_FLAG | HAS_ERROR_FLAG,
+    [_BUILD_INTERPOLATION] = HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG,
+    [_BUILD_TEMPLATE] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG,
     [_BUILD_TUPLE] = HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG,
     [_BUILD_LIST] = HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG,
     [_LIST_EXTEND] = HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG,
@@ -330,11 +332,13 @@ const char *const _PyOpcode_uop_name[MAX_UOP_ID+1] = {
     [_BINARY_OP_SUBTRACT_FLOAT] = "_BINARY_OP_SUBTRACT_FLOAT",
     [_BINARY_OP_SUBTRACT_INT] = "_BINARY_OP_SUBTRACT_INT",
     [_BINARY_SLICE] = "_BINARY_SLICE",
+    [_BUILD_INTERPOLATION] = "_BUILD_INTERPOLATION",
     [_BUILD_LIST] = "_BUILD_LIST",
     [_BUILD_MAP] = "_BUILD_MAP",
     [_BUILD_SET] = "_BUILD_SET",
     [_BUILD_SLICE] = "_BUILD_SLICE",
     [_BUILD_STRING] = "_BUILD_STRING",
+    [_BUILD_TEMPLATE] = "_BUILD_TEMPLATE",
     [_BUILD_TUPLE] = "_BUILD_TUPLE",
     [_CALL_BUILTIN_CLASS] = "_CALL_BUILTIN_CLASS",
     [_CALL_BUILTIN_FAST] = "_CALL_BUILTIN_FAST",
@@ -862,6 +866,10 @@ int _PyUop_num_popped(int opcode, int oparg)
             return 0;
         case _BUILD_STRING:
             return oparg;
+        case _BUILD_INTERPOLATION:
+            return 2 + (oparg & 1);
+        case _BUILD_TEMPLATE:
+            return 2;
         case _BUILD_TUPLE:
             return oparg;
         case _BUILD_LIST:
index d93f028b7324063cc7cb8d6e55089d48875a30f3..d9d92b7d2c1ee00764d4a0b2a9e1fb12417e99ab 100644 (file)
@@ -12,123 +12,125 @@ extern "C" {
 /* Instruction opcodes for compiled code */
 #define CACHE                                    0
 #define BINARY_SLICE                             1
-#define CALL_FUNCTION_EX                         2
+#define BUILD_TEMPLATE                           2
 #define BINARY_OP_INPLACE_ADD_UNICODE            3
-#define CHECK_EG_MATCH                           4
-#define CHECK_EXC_MATCH                          5
-#define CLEANUP_THROW                            6
-#define DELETE_SUBSCR                            7
-#define END_FOR                                  8
-#define END_SEND                                 9
-#define EXIT_INIT_CHECK                         10
-#define FORMAT_SIMPLE                           11
-#define FORMAT_WITH_SPEC                        12
-#define GET_AITER                               13
-#define GET_ANEXT                               14
-#define GET_ITER                                15
-#define GET_LEN                                 16
+#define CALL_FUNCTION_EX                         4
+#define CHECK_EG_MATCH                           5
+#define CHECK_EXC_MATCH                          6
+#define CLEANUP_THROW                            7
+#define DELETE_SUBSCR                            8
+#define END_FOR                                  9
+#define END_SEND                                10
+#define EXIT_INIT_CHECK                         11
+#define FORMAT_SIMPLE                           12
+#define FORMAT_WITH_SPEC                        13
+#define GET_AITER                               14
+#define GET_ANEXT                               15
+#define GET_ITER                                16
 #define RESERVED                                17
-#define GET_YIELD_FROM_ITER                     18
-#define INTERPRETER_EXIT                        19
-#define LOAD_BUILD_CLASS                        20
-#define LOAD_LOCALS                             21
-#define MAKE_FUNCTION                           22
-#define MATCH_KEYS                              23
-#define MATCH_MAPPING                           24
-#define MATCH_SEQUENCE                          25
-#define NOP                                     26
-#define NOT_TAKEN                               27
-#define POP_EXCEPT                              28
-#define POP_ITER                                29
-#define POP_TOP                                 30
-#define PUSH_EXC_INFO                           31
-#define PUSH_NULL                               32
-#define RETURN_GENERATOR                        33
-#define RETURN_VALUE                            34
-#define SETUP_ANNOTATIONS                       35
-#define STORE_SLICE                             36
-#define STORE_SUBSCR                            37
-#define TO_BOOL                                 38
-#define UNARY_INVERT                            39
-#define UNARY_NEGATIVE                          40
-#define UNARY_NOT                               41
-#define WITH_EXCEPT_START                       42
-#define BINARY_OP                               43
-#define BUILD_LIST                              44
-#define BUILD_MAP                               45
-#define BUILD_SET                               46
-#define BUILD_SLICE                             47
-#define BUILD_STRING                            48
-#define BUILD_TUPLE                             49
-#define CALL                                    50
-#define CALL_INTRINSIC_1                        51
-#define CALL_INTRINSIC_2                        52
-#define CALL_KW                                 53
-#define COMPARE_OP                              54
-#define CONTAINS_OP                             55
-#define CONVERT_VALUE                           56
-#define COPY                                    57
-#define COPY_FREE_VARS                          58
-#define DELETE_ATTR                             59
-#define DELETE_DEREF                            60
-#define DELETE_FAST                             61
-#define DELETE_GLOBAL                           62
-#define DELETE_NAME                             63
-#define DICT_MERGE                              64
-#define DICT_UPDATE                             65
-#define END_ASYNC_FOR                           66
-#define EXTENDED_ARG                            67
-#define FOR_ITER                                68
-#define GET_AWAITABLE                           69
-#define IMPORT_FROM                             70
-#define IMPORT_NAME                             71
-#define IS_OP                                   72
-#define JUMP_BACKWARD                           73
-#define JUMP_BACKWARD_NO_INTERRUPT              74
-#define JUMP_FORWARD                            75
-#define LIST_APPEND                             76
-#define LIST_EXTEND                             77
-#define LOAD_ATTR                               78
-#define LOAD_COMMON_CONSTANT                    79
-#define LOAD_CONST                              80
-#define LOAD_DEREF                              81
-#define LOAD_FAST                               82
-#define LOAD_FAST_AND_CLEAR                     83
-#define LOAD_FAST_BORROW                        84
-#define LOAD_FAST_BORROW_LOAD_FAST_BORROW       85
-#define LOAD_FAST_CHECK                         86
-#define LOAD_FAST_LOAD_FAST                     87
-#define LOAD_FROM_DICT_OR_DEREF                 88
-#define LOAD_FROM_DICT_OR_GLOBALS               89
-#define LOAD_GLOBAL                             90
-#define LOAD_NAME                               91
-#define LOAD_SMALL_INT                          92
-#define LOAD_SPECIAL                            93
-#define LOAD_SUPER_ATTR                         94
-#define MAKE_CELL                               95
-#define MAP_ADD                                 96
-#define MATCH_CLASS                             97
-#define POP_JUMP_IF_FALSE                       98
-#define POP_JUMP_IF_NONE                        99
-#define POP_JUMP_IF_NOT_NONE                   100
-#define POP_JUMP_IF_TRUE                       101
-#define RAISE_VARARGS                          102
-#define RERAISE                                103
-#define SEND                                   104
-#define SET_ADD                                105
-#define SET_FUNCTION_ATTRIBUTE                 106
-#define SET_UPDATE                             107
-#define STORE_ATTR                             108
-#define STORE_DEREF                            109
-#define STORE_FAST                             110
-#define STORE_FAST_LOAD_FAST                   111
-#define STORE_FAST_STORE_FAST                  112
-#define STORE_GLOBAL                           113
-#define STORE_NAME                             114
-#define SWAP                                   115
-#define UNPACK_EX                              116
-#define UNPACK_SEQUENCE                        117
-#define YIELD_VALUE                            118
+#define GET_LEN                                 18
+#define GET_YIELD_FROM_ITER                     19
+#define INTERPRETER_EXIT                        20
+#define LOAD_BUILD_CLASS                        21
+#define LOAD_LOCALS                             22
+#define MAKE_FUNCTION                           23
+#define MATCH_KEYS                              24
+#define MATCH_MAPPING                           25
+#define MATCH_SEQUENCE                          26
+#define NOP                                     27
+#define NOT_TAKEN                               28
+#define POP_EXCEPT                              29
+#define POP_ITER                                30
+#define POP_TOP                                 31
+#define PUSH_EXC_INFO                           32
+#define PUSH_NULL                               33
+#define RETURN_GENERATOR                        34
+#define RETURN_VALUE                            35
+#define SETUP_ANNOTATIONS                       36
+#define STORE_SLICE                             37
+#define STORE_SUBSCR                            38
+#define TO_BOOL                                 39
+#define UNARY_INVERT                            40
+#define UNARY_NEGATIVE                          41
+#define UNARY_NOT                               42
+#define WITH_EXCEPT_START                       43
+#define BINARY_OP                               44
+#define BUILD_INTERPOLATION                     45
+#define BUILD_LIST                              46
+#define BUILD_MAP                               47
+#define BUILD_SET                               48
+#define BUILD_SLICE                             49
+#define BUILD_STRING                            50
+#define BUILD_TUPLE                             51
+#define CALL                                    52
+#define CALL_INTRINSIC_1                        53
+#define CALL_INTRINSIC_2                        54
+#define CALL_KW                                 55
+#define COMPARE_OP                              56
+#define CONTAINS_OP                             57
+#define CONVERT_VALUE                           58
+#define COPY                                    59
+#define COPY_FREE_VARS                          60
+#define DELETE_ATTR                             61
+#define DELETE_DEREF                            62
+#define DELETE_FAST                             63
+#define DELETE_GLOBAL                           64
+#define DELETE_NAME                             65
+#define DICT_MERGE                              66
+#define DICT_UPDATE                             67
+#define END_ASYNC_FOR                           68
+#define EXTENDED_ARG                            69
+#define FOR_ITER                                70
+#define GET_AWAITABLE                           71
+#define IMPORT_FROM                             72
+#define IMPORT_NAME                             73
+#define IS_OP                                   74
+#define JUMP_BACKWARD                           75
+#define JUMP_BACKWARD_NO_INTERRUPT              76
+#define JUMP_FORWARD                            77
+#define LIST_APPEND                             78
+#define LIST_EXTEND                             79
+#define LOAD_ATTR                               80
+#define LOAD_COMMON_CONSTANT                    81
+#define LOAD_CONST                              82
+#define LOAD_DEREF                              83
+#define LOAD_FAST                               84
+#define LOAD_FAST_AND_CLEAR                     85
+#define LOAD_FAST_BORROW                        86
+#define LOAD_FAST_BORROW_LOAD_FAST_BORROW       87
+#define LOAD_FAST_CHECK                         88
+#define LOAD_FAST_LOAD_FAST                     89
+#define LOAD_FROM_DICT_OR_DEREF                 90
+#define LOAD_FROM_DICT_OR_GLOBALS               91
+#define LOAD_GLOBAL                             92
+#define LOAD_NAME                               93
+#define LOAD_SMALL_INT                          94
+#define LOAD_SPECIAL                            95
+#define LOAD_SUPER_ATTR                         96
+#define MAKE_CELL                               97
+#define MAP_ADD                                 98
+#define MATCH_CLASS                             99
+#define POP_JUMP_IF_FALSE                      100
+#define POP_JUMP_IF_NONE                       101
+#define POP_JUMP_IF_NOT_NONE                   102
+#define POP_JUMP_IF_TRUE                       103
+#define RAISE_VARARGS                          104
+#define RERAISE                                105
+#define SEND                                   106
+#define SET_ADD                                107
+#define SET_FUNCTION_ATTRIBUTE                 108
+#define SET_UPDATE                             109
+#define STORE_ATTR                             110
+#define STORE_DEREF                            111
+#define STORE_FAST                             112
+#define STORE_FAST_LOAD_FAST                   113
+#define STORE_FAST_STORE_FAST                  114
+#define STORE_GLOBAL                           115
+#define STORE_NAME                             116
+#define SWAP                                   117
+#define UNPACK_EX                              118
+#define UNPACK_SEQUENCE                        119
+#define YIELD_VALUE                            120
 #define RESUME                                 128
 #define BINARY_OP_ADD_FLOAT                    129
 #define BINARY_OP_ADD_INT                      130
@@ -246,7 +248,7 @@ extern "C" {
 #define SETUP_WITH                             265
 #define STORE_FAST_MAYBE_NULL                  266
 
-#define HAVE_ARGUMENT                           42
+#define HAVE_ARGUMENT                           43
 #define MIN_SPECIALIZED_OPCODE                 129
 #define MIN_INSTRUMENTED_OPCODE                234
 
index 56d9e935dd9d872b6e1e01eb0602564dca82675b..0b669edb2ffec695a95ba0eb375f7628ccd77706 100644 (file)
@@ -573,21 +573,11 @@ class Unparser(NodeVisitor):
         quote_type = quote_types[0]
         self.write(f"{quote_type}{string}{quote_type}")
 
-    def visit_JoinedStr(self, node):
-        self.write("f")
-
-        fstring_parts = []
-        for value in node.values:
-            with self.buffered() as buffer:
-                self._write_fstring_inner(value)
-            fstring_parts.append(
-                ("".join(buffer), isinstance(value, Constant))
-            )
-
-        new_fstring_parts = []
+    def _ftstring_helper(self, parts):
+        new_parts = []
         quote_types = list(_ALL_QUOTES)
         fallback_to_repr = False
-        for value, is_constant in fstring_parts:
+        for value, is_constant in parts:
             if is_constant:
                 value, new_quote_types = self._str_literal_helper(
                     value,
@@ -606,30 +596,68 @@ class Unparser(NodeVisitor):
                 new_quote_types = [q for q in quote_types if q not in value]
                 if new_quote_types:
                     quote_types = new_quote_types
-            new_fstring_parts.append(value)
+            new_parts.append(value)
 
         if fallback_to_repr:
             # If we weren't able to find a quote type that works for all parts
             # of the JoinedStr, fallback to using repr and triple single quotes.
             quote_types = ["'''"]
-            new_fstring_parts.clear()
-            for value, is_constant in fstring_parts:
+            new_parts.clear()
+            for value, is_constant in parts:
                 if is_constant:
                     value = repr('"' + value)  # force repr to use single quotes
                     expected_prefix = "'\""
                     assert value.startswith(expected_prefix), repr(value)
                     value = value[len(expected_prefix):-1]
-                new_fstring_parts.append(value)
+                new_parts.append(value)
 
-        value = "".join(new_fstring_parts)
+        value = "".join(new_parts)
         quote_type = quote_types[0]
         self.write(f"{quote_type}{value}{quote_type}")
 
-    def _write_fstring_inner(self, node, is_format_spec=False):
+    def _write_ftstring(self, values, prefix):
+        self.write(prefix)
+        fstring_parts = []
+        for value in values:
+            with self.buffered() as buffer:
+                self._write_ftstring_inner(value)
+            fstring_parts.append(
+                ("".join(buffer), isinstance(value, Constant))
+            )
+        self._ftstring_helper(fstring_parts)
+
+    def _tstring_helper(self, node):
+        last_idx = 0
+        for i, value in enumerate(node.values):
+            # This can happen if we have an implicit concat of a t-string
+            # with an f-string
+            if isinstance(value, FormattedValue):
+                if i > last_idx:
+                    # Write t-string until here
+                    self._write_ftstring(node.values[last_idx:i], "t")
+                    self.write(" ")
+                # Write f-string with the current formatted value
+                self._write_ftstring([node.values[i]], "f")
+                if i + 1 < len(node.values):
+                    # Only add a space if there are more values after this
+                    self.write(" ")
+                last_idx = i + 1
+
+        if last_idx < len(node.values):
+            # Write t-string from last_idx to end
+            self._write_ftstring(node.values[last_idx:], "t")
+
+    def visit_JoinedStr(self, node):
+        self._write_ftstring(node.values, "f")
+
+    def visit_TemplateStr(self, node):
+        self._tstring_helper(node)
+
+    def _write_ftstring_inner(self, node, is_format_spec=False):
         if isinstance(node, JoinedStr):
             # for both the f-string itself, and format_spec
             for value in node.values:
-                self._write_fstring_inner(value, is_format_spec=is_format_spec)
+                self._write_ftstring_inner(value, is_format_spec=is_format_spec)
         elif isinstance(node, Constant) and isinstance(node.value, str):
             value = node.value.replace("{", "{{").replace("}", "}}")
 
@@ -641,17 +669,19 @@ class Unparser(NodeVisitor):
             self.write(value)
         elif isinstance(node, FormattedValue):
             self.visit_FormattedValue(node)
+        elif isinstance(node, Interpolation):
+            self.visit_Interpolation(node)
         else:
             raise ValueError(f"Unexpected node inside JoinedStr, {node!r}")
 
-    def visit_FormattedValue(self, node):
-        def unparse_inner(inner):
-            unparser = type(self)()
-            unparser.set_precedence(_Precedence.TEST.next(), inner)
-            return unparser.visit(inner)
+    def _unparse_interpolation_value(self, inner):
+        unparser = type(self)()
+        unparser.set_precedence(_Precedence.TEST.next(), inner)
+        return unparser.visit(inner)
 
+    def _write_interpolation(self, node):
         with self.delimit("{", "}"):
-            expr = unparse_inner(node.value)
+            expr = self._unparse_interpolation_value(node.value)
             if expr.startswith("{"):
                 # Separate pair of opening brackets as "{ {"
                 self.write(" ")
@@ -660,7 +690,13 @@ class Unparser(NodeVisitor):
                 self.write(f"!{chr(node.conversion)}")
             if node.format_spec:
                 self.write(":")
-                self._write_fstring_inner(node.format_spec, is_format_spec=True)
+                self._write_ftstring_inner(node.format_spec, is_format_spec=True)
+
+    def visit_FormattedValue(self, node):
+        self._write_interpolation(node)
+
+    def visit_Interpolation(self, node):
+        self._write_interpolation(node)
 
     def visit_Name(self, node):
         self.write(node.id)
index 4d30b6503fd1e9a0f9ba5e56a2b7fcefb23653fe..1114cf90abbb3a424d37ef9ab6977ef1563addcd 100644 (file)
@@ -215,121 +215,123 @@ opmap = {
     'INSTRUMENTED_LINE': 254,
     'ENTER_EXECUTOR': 255,
     'BINARY_SLICE': 1,
-    'CALL_FUNCTION_EX': 2,
-    'CHECK_EG_MATCH': 4,
-    'CHECK_EXC_MATCH': 5,
-    'CLEANUP_THROW': 6,
-    'DELETE_SUBSCR': 7,
-    'END_FOR': 8,
-    'END_SEND': 9,
-    'EXIT_INIT_CHECK': 10,
-    'FORMAT_SIMPLE': 11,
-    'FORMAT_WITH_SPEC': 12,
-    'GET_AITER': 13,
-    'GET_ANEXT': 14,
-    'GET_ITER': 15,
-    'GET_LEN': 16,
-    'GET_YIELD_FROM_ITER': 18,
-    'INTERPRETER_EXIT': 19,
-    'LOAD_BUILD_CLASS': 20,
-    'LOAD_LOCALS': 21,
-    'MAKE_FUNCTION': 22,
-    'MATCH_KEYS': 23,
-    'MATCH_MAPPING': 24,
-    'MATCH_SEQUENCE': 25,
-    'NOP': 26,
-    'NOT_TAKEN': 27,
-    'POP_EXCEPT': 28,
-    'POP_ITER': 29,
-    'POP_TOP': 30,
-    'PUSH_EXC_INFO': 31,
-    'PUSH_NULL': 32,
-    'RETURN_GENERATOR': 33,
-    'RETURN_VALUE': 34,
-    'SETUP_ANNOTATIONS': 35,
-    'STORE_SLICE': 36,
-    'STORE_SUBSCR': 37,
-    'TO_BOOL': 38,
-    'UNARY_INVERT': 39,
-    'UNARY_NEGATIVE': 40,
-    'UNARY_NOT': 41,
-    'WITH_EXCEPT_START': 42,
-    'BINARY_OP': 43,
-    'BUILD_LIST': 44,
-    'BUILD_MAP': 45,
-    'BUILD_SET': 46,
-    'BUILD_SLICE': 47,
-    'BUILD_STRING': 48,
-    'BUILD_TUPLE': 49,
-    'CALL': 50,
-    'CALL_INTRINSIC_1': 51,
-    'CALL_INTRINSIC_2': 52,
-    'CALL_KW': 53,
-    'COMPARE_OP': 54,
-    'CONTAINS_OP': 55,
-    'CONVERT_VALUE': 56,
-    'COPY': 57,
-    'COPY_FREE_VARS': 58,
-    'DELETE_ATTR': 59,
-    'DELETE_DEREF': 60,
-    'DELETE_FAST': 61,
-    'DELETE_GLOBAL': 62,
-    'DELETE_NAME': 63,
-    'DICT_MERGE': 64,
-    'DICT_UPDATE': 65,
-    'END_ASYNC_FOR': 66,
-    'EXTENDED_ARG': 67,
-    'FOR_ITER': 68,
-    'GET_AWAITABLE': 69,
-    'IMPORT_FROM': 70,
-    'IMPORT_NAME': 71,
-    'IS_OP': 72,
-    'JUMP_BACKWARD': 73,
-    'JUMP_BACKWARD_NO_INTERRUPT': 74,
-    'JUMP_FORWARD': 75,
-    'LIST_APPEND': 76,
-    'LIST_EXTEND': 77,
-    'LOAD_ATTR': 78,
-    'LOAD_COMMON_CONSTANT': 79,
-    'LOAD_CONST': 80,
-    'LOAD_DEREF': 81,
-    'LOAD_FAST': 82,
-    'LOAD_FAST_AND_CLEAR': 83,
-    'LOAD_FAST_BORROW': 84,
-    'LOAD_FAST_BORROW_LOAD_FAST_BORROW': 85,
-    'LOAD_FAST_CHECK': 86,
-    'LOAD_FAST_LOAD_FAST': 87,
-    'LOAD_FROM_DICT_OR_DEREF': 88,
-    'LOAD_FROM_DICT_OR_GLOBALS': 89,
-    'LOAD_GLOBAL': 90,
-    'LOAD_NAME': 91,
-    'LOAD_SMALL_INT': 92,
-    'LOAD_SPECIAL': 93,
-    'LOAD_SUPER_ATTR': 94,
-    'MAKE_CELL': 95,
-    'MAP_ADD': 96,
-    'MATCH_CLASS': 97,
-    'POP_JUMP_IF_FALSE': 98,
-    'POP_JUMP_IF_NONE': 99,
-    'POP_JUMP_IF_NOT_NONE': 100,
-    'POP_JUMP_IF_TRUE': 101,
-    'RAISE_VARARGS': 102,
-    'RERAISE': 103,
-    'SEND': 104,
-    'SET_ADD': 105,
-    'SET_FUNCTION_ATTRIBUTE': 106,
-    'SET_UPDATE': 107,
-    'STORE_ATTR': 108,
-    'STORE_DEREF': 109,
-    'STORE_FAST': 110,
-    'STORE_FAST_LOAD_FAST': 111,
-    'STORE_FAST_STORE_FAST': 112,
-    'STORE_GLOBAL': 113,
-    'STORE_NAME': 114,
-    'SWAP': 115,
-    'UNPACK_EX': 116,
-    'UNPACK_SEQUENCE': 117,
-    'YIELD_VALUE': 118,
+    'BUILD_TEMPLATE': 2,
+    'CALL_FUNCTION_EX': 4,
+    'CHECK_EG_MATCH': 5,
+    'CHECK_EXC_MATCH': 6,
+    'CLEANUP_THROW': 7,
+    'DELETE_SUBSCR': 8,
+    'END_FOR': 9,
+    'END_SEND': 10,
+    'EXIT_INIT_CHECK': 11,
+    'FORMAT_SIMPLE': 12,
+    'FORMAT_WITH_SPEC': 13,
+    'GET_AITER': 14,
+    'GET_ANEXT': 15,
+    'GET_ITER': 16,
+    'GET_LEN': 18,
+    'GET_YIELD_FROM_ITER': 19,
+    'INTERPRETER_EXIT': 20,
+    'LOAD_BUILD_CLASS': 21,
+    'LOAD_LOCALS': 22,
+    'MAKE_FUNCTION': 23,
+    'MATCH_KEYS': 24,
+    'MATCH_MAPPING': 25,
+    'MATCH_SEQUENCE': 26,
+    'NOP': 27,
+    'NOT_TAKEN': 28,
+    'POP_EXCEPT': 29,
+    'POP_ITER': 30,
+    'POP_TOP': 31,
+    'PUSH_EXC_INFO': 32,
+    'PUSH_NULL': 33,
+    'RETURN_GENERATOR': 34,
+    'RETURN_VALUE': 35,
+    'SETUP_ANNOTATIONS': 36,
+    'STORE_SLICE': 37,
+    'STORE_SUBSCR': 38,
+    'TO_BOOL': 39,
+    'UNARY_INVERT': 40,
+    'UNARY_NEGATIVE': 41,
+    'UNARY_NOT': 42,
+    'WITH_EXCEPT_START': 43,
+    'BINARY_OP': 44,
+    'BUILD_INTERPOLATION': 45,
+    'BUILD_LIST': 46,
+    'BUILD_MAP': 47,
+    'BUILD_SET': 48,
+    'BUILD_SLICE': 49,
+    'BUILD_STRING': 50,
+    'BUILD_TUPLE': 51,
+    'CALL': 52,
+    'CALL_INTRINSIC_1': 53,
+    'CALL_INTRINSIC_2': 54,
+    'CALL_KW': 55,
+    'COMPARE_OP': 56,
+    'CONTAINS_OP': 57,
+    'CONVERT_VALUE': 58,
+    'COPY': 59,
+    'COPY_FREE_VARS': 60,
+    'DELETE_ATTR': 61,
+    'DELETE_DEREF': 62,
+    'DELETE_FAST': 63,
+    'DELETE_GLOBAL': 64,
+    'DELETE_NAME': 65,
+    'DICT_MERGE': 66,
+    'DICT_UPDATE': 67,
+    'END_ASYNC_FOR': 68,
+    'EXTENDED_ARG': 69,
+    'FOR_ITER': 70,
+    'GET_AWAITABLE': 71,
+    'IMPORT_FROM': 72,
+    'IMPORT_NAME': 73,
+    'IS_OP': 74,
+    'JUMP_BACKWARD': 75,
+    'JUMP_BACKWARD_NO_INTERRUPT': 76,
+    'JUMP_FORWARD': 77,
+    'LIST_APPEND': 78,
+    'LIST_EXTEND': 79,
+    'LOAD_ATTR': 80,
+    'LOAD_COMMON_CONSTANT': 81,
+    'LOAD_CONST': 82,
+    'LOAD_DEREF': 83,
+    'LOAD_FAST': 84,
+    'LOAD_FAST_AND_CLEAR': 85,
+    'LOAD_FAST_BORROW': 86,
+    'LOAD_FAST_BORROW_LOAD_FAST_BORROW': 87,
+    'LOAD_FAST_CHECK': 88,
+    'LOAD_FAST_LOAD_FAST': 89,
+    'LOAD_FROM_DICT_OR_DEREF': 90,
+    'LOAD_FROM_DICT_OR_GLOBALS': 91,
+    'LOAD_GLOBAL': 92,
+    'LOAD_NAME': 93,
+    'LOAD_SMALL_INT': 94,
+    'LOAD_SPECIAL': 95,
+    'LOAD_SUPER_ATTR': 96,
+    'MAKE_CELL': 97,
+    'MAP_ADD': 98,
+    'MATCH_CLASS': 99,
+    'POP_JUMP_IF_FALSE': 100,
+    'POP_JUMP_IF_NONE': 101,
+    'POP_JUMP_IF_NOT_NONE': 102,
+    'POP_JUMP_IF_TRUE': 103,
+    'RAISE_VARARGS': 104,
+    'RERAISE': 105,
+    'SEND': 106,
+    'SET_ADD': 107,
+    'SET_FUNCTION_ATTRIBUTE': 108,
+    'SET_UPDATE': 109,
+    'STORE_ATTR': 110,
+    'STORE_DEREF': 111,
+    'STORE_FAST': 112,
+    'STORE_FAST_LOAD_FAST': 113,
+    'STORE_FAST_STORE_FAST': 114,
+    'STORE_GLOBAL': 115,
+    'STORE_NAME': 116,
+    'SWAP': 117,
+    'UNPACK_EX': 118,
+    'UNPACK_SEQUENCE': 119,
+    'YIELD_VALUE': 120,
     'INSTRUMENTED_END_FOR': 234,
     'INSTRUMENTED_POP_ITER': 235,
     'INSTRUMENTED_END_SEND': 236,
@@ -363,5 +365,5 @@ opmap = {
     'STORE_FAST_MAYBE_NULL': 266,
 }
 
-HAVE_ARGUMENT = 42
+HAVE_ARGUMENT = 43
 MIN_INSTRUMENTED_OPCODE = 234
similarity index 100%
rename from Lib/string.py
rename to Lib/string/__init__.py
diff --git a/Lib/string/templatelib.py b/Lib/string/templatelib.py
new file mode 100644 (file)
index 0000000..14b40e1
--- /dev/null
@@ -0,0 +1,26 @@
+"""Support for template string literals (t-strings)."""
+
+__all__ = [
+    "Interpolation",
+    "Template",
+]
+
+t = t"{0}"
+Template = type(t)
+Interpolation = type(t.interpolations[0])
+del t
+
+def _template_unpickle(*args):
+    import itertools
+
+    if len(args) != 2:
+        raise ValueError('Template expects tuple of length 2 to unpickle')
+
+    strings, interpolations = args
+    parts = []
+    for string, interpolation in itertools.zip_longest(strings, interpolations):
+        if string is not None:
+            parts.append(string)
+        if interpolation is not None:
+            parts.append(interpolation)
+    return Template(*parts)
index fa8b2b42579b4a2a531d16a45bd6eda77e4acc69..54126bf32619ab724728d43bc25f833296f17838 100644 (file)
@@ -7,6 +7,9 @@ extend-exclude = [
     # Non UTF-8 files
     "encoded_modules/module_iso_8859_1.py",
     "encoded_modules/module_koi8_r.py",
+    # SyntaxError because of t-strings
+    "test_tstring.py",
+    "test_string/test_templatelib.py",
     # New grammar constructions may not yet be recognized by Ruff,
     # and tests re-use the same names as only the grammar is being checked.
     "test_grammar.py",
index 8a0415b6aae33cbd5db6b1599014d9334eb56270..173d299afee491e96ffea784039fb549e40f681e 100644 (file)
@@ -16,6 +16,9 @@ class ASTTestMixin:
                 self.fail(f"{type(a)!r} is not {type(b)!r}")
             if isinstance(a, ast.AST):
                 for field in a._fields:
+                    if isinstance(a, ast.Constant) and field == "kind":
+                        # Skip the 'kind' field for ast.Constant
+                        continue
                     value1 = getattr(a, field, missing)
                     value2 = getattr(b, field, missing)
                     # Singletons are equal by definition, so further
index 3778b9e70a460524f0d746736dd1dae52eb164a8..1c1985519cd8b452628f698d39ff6e32fb6ff8e1 100644 (file)
@@ -206,4 +206,9 @@ Module(body=[Expr(value=IfExp(test=Name(...), body=Call(...), orelse=Call(...)))
 Module(body=[Expr(value=JoinedStr(values=[FormattedValue(...)]))], type_ignores=[])
 Module(body=[Expr(value=JoinedStr(values=[FormattedValue(...)]))], type_ignores=[])
 Module(body=[Expr(value=JoinedStr(values=[FormattedValue(...)]))], type_ignores=[])
-Module(body=[Expr(value=JoinedStr(values=[Constant(...), ..., Constant(...)]))], type_ignores=[])
\ No newline at end of file
+Module(body=[Expr(value=JoinedStr(values=[Constant(...), ..., Constant(...)]))], type_ignores=[])
+Module(body=[Expr(value=TemplateStr(values=[Interpolation(...)]))], type_ignores=[])
+Module(body=[Expr(value=TemplateStr(values=[Interpolation(...)]))], type_ignores=[])
+Module(body=[Expr(value=TemplateStr(values=[Interpolation(...)]))], type_ignores=[])
+Module(body=[Expr(value=TemplateStr(values=[Interpolation(...)]))], type_ignores=[])
+Module(body=[Expr(value=TemplateStr(values=[Constant(...), ..., Constant(...)]))], type_ignores=[])
\ No newline at end of file
index 28d32b2941f30d81e8e25a43a5856b2f8a7390f3..b76f98901d2ad8b064d9499ed4098ea72c8fe224 100644 (file)
@@ -364,6 +364,12 @@ eval_tests = [
   "f'{a:.2f}'",
   "f'{a!r}'",
   "f'foo({a})'",
+  # TemplateStr and Interpolation
+  "t'{a}'",
+  "t'{a:.2f}'",
+  "t'{a!r}'",
+  "t'{a!r:.2f}'",
+  "t'foo({a})'",
 ]
 
 
@@ -597,5 +603,10 @@ eval_results = [
 ('Expression', ('JoinedStr', (1, 0, 1, 10), [('FormattedValue', (1, 2, 1, 9), ('Name', (1, 3, 1, 4), 'a', ('Load',)), -1, ('JoinedStr', (1, 4, 1, 8), [('Constant', (1, 5, 1, 8), '.2f', None)]))])),
 ('Expression', ('JoinedStr', (1, 0, 1, 8), [('FormattedValue', (1, 2, 1, 7), ('Name', (1, 3, 1, 4), 'a', ('Load',)), 114, None)])),
 ('Expression', ('JoinedStr', (1, 0, 1, 11), [('Constant', (1, 2, 1, 6), 'foo(', None), ('FormattedValue', (1, 6, 1, 9), ('Name', (1, 7, 1, 8), 'a', ('Load',)), -1, None), ('Constant', (1, 9, 1, 10), ')', None)])),
+('Expression', ('TemplateStr', (1, 0, 1, 6), [('Interpolation', (1, 2, 1, 5), ('Name', (1, 3, 1, 4), 'a', ('Load',)), 'a', -1, None)])),
+('Expression', ('TemplateStr', (1, 0, 1, 10), [('Interpolation', (1, 2, 1, 9), ('Name', (1, 3, 1, 4), 'a', ('Load',)), 'a', -1, ('JoinedStr', (1, 4, 1, 8), [('Constant', (1, 5, 1, 8), '.2f', None)]))])),
+('Expression', ('TemplateStr', (1, 0, 1, 8), [('Interpolation', (1, 2, 1, 7), ('Name', (1, 3, 1, 4), 'a', ('Load',)), 'a', 114, None)])),
+('Expression', ('TemplateStr', (1, 0, 1, 12), [('Interpolation', (1, 2, 1, 11), ('Name', (1, 3, 1, 4), 'a', ('Load',)), 'a', 114, ('JoinedStr', (1, 6, 1, 10), [('Constant', (1, 7, 1, 10), '.2f', None)]))])),
+('Expression', ('TemplateStr', (1, 0, 1, 11), [('Constant', (1, 2, 1, 6), 'foo(', None), ('Interpolation', (1, 6, 1, 9), ('Name', (1, 7, 1, 8), 'a', ('Load',)), 'a', -1, None), ('Constant', (1, 9, 1, 10), ')', None)])),
 ]
 main()
index dd459487afef1c5533990954a3069b43afc7ac3a..eeac7c21eda98faa44c55453a6a4d98bfb82d81b 100644 (file)
@@ -880,6 +880,25 @@ class AST_Tests(unittest.TestCase):
         for src in srcs:
             ast.parse(src)
 
+    def test_tstring(self):
+        # Test AST structure for simple t-string
+        tree = ast.parse('t"Hello"')
+        self.assertIsInstance(tree.body[0].value, ast.TemplateStr)
+        self.assertIsInstance(tree.body[0].value.values[0], ast.Constant)
+
+        # Test AST for t-string with interpolation
+        tree = ast.parse('t"Hello {name}"')
+        self.assertIsInstance(tree.body[0].value, ast.TemplateStr)
+        self.assertIsInstance(tree.body[0].value.values[0], ast.Constant)
+        self.assertIsInstance(tree.body[0].value.values[1], ast.Interpolation)
+
+        # Test AST for implicit concat of t-string with f-string
+        tree = ast.parse('t"Hello {name}" f"{name}"')
+        self.assertIsInstance(tree.body[0].value, ast.TemplateStr)
+        self.assertIsInstance(tree.body[0].value.values[0], ast.Constant)
+        self.assertIsInstance(tree.body[0].value.values[1], ast.Interpolation)
+        self.assertIsInstance(tree.body[0].value.values[2], ast.FormattedValue)
+
 
 class CopyTests(unittest.TestCase):
     """Test copying and pickling AST nodes."""
index e75e7db378cef17d23122e4d704173e4e524be73..a10d1fd5fd2b1fda7372df7e9e2193e98d8e7e8f 100644 (file)
@@ -1358,7 +1358,6 @@ x = (
         self.assertAllRaise(SyntaxError, "f-string: expecting '}'",
                             ["f'{3!'",
                              "f'{3!s'",
-                             "f'{3!g'",
                              ])
 
         self.assertAllRaise(SyntaxError, 'f-string: missing conversion character',
index 35cd6984267b3be36accb32d06626b9dd40cbbcf..c0681bccd9ec32a2bc436702a277c2a621a56699 100644 (file)
@@ -1507,6 +1507,8 @@ class GrammarTests(unittest.TestCase):
         check('[None (3, 4)]')
         check('[True (3, 4)]')
         check('[... (3, 4)]')
+        check('[t"{x}" (3, 4)]')
+        check('[t"x={x}" (3, 4)]')
 
         msg=r'is not subscriptable; perhaps you missed a comma\?'
         check('[{1, 2} [i, j]]')
@@ -1529,6 +1531,8 @@ class GrammarTests(unittest.TestCase):
         check('[f"x={x}" [i, j]]')
         check('["abc" [i, j]]')
         check('[b"abc" [i, j]]')
+        check('[t"{x}" [i, j]]')
+        check('[t"x={x}" [i, j]]')
 
         msg=r'indices must be integers or slices, not tuple;'
         check('[[1, 2] [3, 4]]')
@@ -1549,6 +1553,8 @@ class GrammarTests(unittest.TestCase):
         check('[[1, 2] [f"{x}"]]')
         check('[[1, 2] [f"x={x}"]]')
         check('[[1, 2] ["abc"]]')
+        check('[[1, 2] [t"{x}"]]')
+        check('[[1, 2] [t"x={x}"]]')
         msg=r'indices must be integers or slices, not'
         check('[[1, 2] [b"abc"]]')
         check('[[1, 2] [12.3]]')
diff --git a/Lib/test/test_string/__init__.py b/Lib/test/test_string/__init__.py
new file mode 100644 (file)
index 0000000..4b16ecc
--- /dev/null
@@ -0,0 +1,5 @@
+import os
+from test.support import load_package_tests
+
+def load_tests(*args):
+    return load_package_tests(os.path.dirname(__file__), *args)
diff --git a/Lib/test/test_string/_support.py b/Lib/test/test_string/_support.py
new file mode 100644 (file)
index 0000000..eaa3354
--- /dev/null
@@ -0,0 +1,55 @@
+import unittest
+from string.templatelib import Interpolation
+
+
+class TStringBaseCase:
+    def assertTStringEqual(self, t, strings, interpolations):
+        """Test template string literal equality.
+
+        The *strings* argument must be a tuple of strings equal to *t.strings*.
+
+        The *interpolations* argument must be a sequence of tuples which are
+        compared against *t.interpolations*. Each tuple consists of
+        (value, expression, conversion, format_spec), though the final two
+        items may be omitted, and are assumed to be None and '' respectively.
+        """
+        self.assertEqual(t.strings, strings)
+        self.assertEqual(len(t.interpolations), len(interpolations))
+
+        for i, exp in zip(t.interpolations, interpolations, strict=True):
+            if len(exp) == 4:
+                actual = (i.value, i.expression, i.conversion, i.format_spec)
+                self.assertEqual(actual, exp)
+                continue
+
+            if len(exp) == 3:
+                self.assertEqual((i.value, i.expression, i.conversion), exp)
+                self.assertEqual(i.format_spec, '')
+                continue
+
+            self.assertEqual((i.value, i.expression), exp)
+            self.assertEqual(i.format_spec, '')
+            self.assertIsNone(i.conversion)
+
+
+def convert(value, conversion):
+    if conversion == "a":
+        return ascii(value)
+    elif conversion == "r":
+        return repr(value)
+    elif conversion == "s":
+        return str(value)
+    return value
+
+
+def fstring(template):
+    parts = []
+    for item in template:
+        match item:
+            case str() as s:
+                parts.append(s)
+            case Interpolation(value, _, conversion, format_spec):
+                value = convert(value, conversion)
+                value = format(value, format_spec)
+                parts.append(value)
+    return "".join(parts)
diff --git a/Lib/test/test_string/test_templatelib.py b/Lib/test/test_string/test_templatelib.py
new file mode 100644 (file)
index 0000000..5cf1878
--- /dev/null
@@ -0,0 +1,122 @@
+import pickle
+import unittest
+from string.templatelib import Template, Interpolation
+
+from test.test_string._support import TStringBaseCase, fstring
+
+
+class TestTemplate(unittest.TestCase, TStringBaseCase):
+
+    def test_common(self):
+        self.assertEqual(type(t'').__name__, 'Template')
+        self.assertEqual(type(t'').__qualname__, 'Template')
+        self.assertEqual(type(t'').__module__, 'string.templatelib')
+
+        a = 'a'
+        i = t'{a}'.interpolations[0]
+        self.assertEqual(type(i).__name__, 'Interpolation')
+        self.assertEqual(type(i).__qualname__, 'Interpolation')
+        self.assertEqual(type(i).__module__, 'string.templatelib')
+
+    def test_basic_creation(self):
+        # Simple t-string creation
+        t = t'Hello, world'
+        self.assertIsInstance(t, Template)
+        self.assertTStringEqual(t, ('Hello, world',), ())
+        self.assertEqual(fstring(t), 'Hello, world')
+
+        # Empty t-string
+        t = t''
+        self.assertTStringEqual(t, ('',), ())
+        self.assertEqual(fstring(t), '')
+
+        # Multi-line t-string
+        t = t"""Hello,
+world"""
+        self.assertEqual(t.strings, ('Hello,\nworld',))
+        self.assertEqual(len(t.interpolations), 0)
+        self.assertEqual(fstring(t), 'Hello,\nworld')
+
+    def test_creation_interleaving(self):
+        # Should add strings on either side
+        t = Template(Interpolation('Maria', 'name', None, ''))
+        self.assertTStringEqual(t, ('', ''), [('Maria', 'name')])
+        self.assertEqual(fstring(t), 'Maria')
+
+        # Should prepend empty string
+        t = Template(Interpolation('Maria', 'name', None, ''), ' is my name')
+        self.assertTStringEqual(t, ('', ' is my name'), [('Maria', 'name')])
+        self.assertEqual(fstring(t), 'Maria is my name')
+
+        # Should append empty string
+        t = Template('Hello, ', Interpolation('Maria', 'name', None, ''))
+        self.assertTStringEqual(t, ('Hello, ', ''), [('Maria', 'name')])
+        self.assertEqual(fstring(t), 'Hello, Maria')
+
+        # Should concatenate strings
+        t = Template('Hello', ', ', Interpolation('Maria', 'name', None, ''),
+                     '!')
+        self.assertTStringEqual(t, ('Hello, ', '!'), [('Maria', 'name')])
+        self.assertEqual(fstring(t), 'Hello, Maria!')
+
+        # Should add strings on either side and in between
+        t = Template(Interpolation('Maria', 'name', None, ''),
+                     Interpolation('Python', 'language', None, ''))
+        self.assertTStringEqual(
+            t, ('', '', ''), [('Maria', 'name'), ('Python', 'language')]
+        )
+        self.assertEqual(fstring(t), 'MariaPython')
+
+    def test_template_values(self):
+        t = t'Hello, world'
+        self.assertEqual(t.values, ())
+
+        name = "Lys"
+        t = t'Hello, {name}'
+        self.assertEqual(t.values, ("Lys",))
+
+        country = "GR"
+        age = 0
+        t = t'Hello, {name}, {age} from {country}'
+        self.assertEqual(t.values, ("Lys", 0, "GR"))
+
+    def test_pickle_template(self):
+        user = 'test'
+        for template in (
+            t'',
+            t"No values",
+            t'With inter {user}',
+            t'With ! {user!r}',
+            t'With format {1 / 0.3:.2f}',
+            Template(),
+            Template('a'),
+            Template(Interpolation('Nikita', 'name', None, '')),
+            Template('a', Interpolation('Nikita', 'name', 'r', '')),
+        ):
+            for proto in range(pickle.HIGHEST_PROTOCOL + 1):
+                with self.subTest(proto=proto, template=template):
+                    pickled = pickle.dumps(template, protocol=proto)
+                    unpickled = pickle.loads(pickled)
+
+                    self.assertEqual(unpickled.values, template.values)
+                    self.assertEqual(fstring(unpickled), fstring(template))
+
+    def test_pickle_interpolation(self):
+        for interpolation in (
+            Interpolation('Nikita', 'name', None, ''),
+            Interpolation('Nikita', 'name', 'r', ''),
+            Interpolation(1/3, 'x', None, '.2f'),
+        ):
+            for proto in range(pickle.HIGHEST_PROTOCOL + 1):
+                with self.subTest(proto=proto, interpolation=interpolation):
+                    pickled = pickle.dumps(interpolation, protocol=proto)
+                    unpickled = pickle.loads(pickled)
+
+                    self.assertEqual(unpickled.value, interpolation.value)
+                    self.assertEqual(unpickled.expression, interpolation.expression)
+                    self.assertEqual(unpickled.conversion, interpolation.conversion)
+                    self.assertEqual(unpickled.format_spec, interpolation.format_spec)
+
+
+if __name__ == '__main__':
+    unittest.main()
index 4c001f9c9b02e0cb215b495c7c3acad7f3129f33..55492350d00714420162ab67dde4ffe4a0b772be 100644 (file)
@@ -1877,6 +1877,14 @@ SyntaxError: cannot assign to f-string expression here. Maybe you meant '==' ins
 Traceback (most recent call last):
 SyntaxError: cannot assign to f-string expression here. Maybe you meant '==' instead of '='?
 
+>>> t'{x}' = 42
+Traceback (most recent call last):
+SyntaxError: cannot assign to t-string expression here. Maybe you meant '==' instead of '='?
+
+>>> t'{x}-{y}' = 42
+Traceback (most recent call last):
+SyntaxError: cannot assign to t-string expression here. Maybe you meant '==' instead of '='?
+
 >>> (x, y, z=3, d, e)
 Traceback (most recent call last):
 SyntaxError: invalid syntax. Maybe you meant '==' or ':=' instead of '='?
diff --git a/Lib/test/test_tstring.py b/Lib/test/test_tstring.py
new file mode 100644 (file)
index 0000000..e72a1ea
--- /dev/null
@@ -0,0 +1,313 @@
+import unittest
+
+from test.test_string._support import TStringBaseCase, fstring
+
+
+class TestTString(unittest.TestCase, TStringBaseCase):
+    def test_string_representation(self):
+        # Test __repr__
+        t = t"Hello"
+        self.assertEqual(repr(t), "Template(strings=('Hello',), interpolations=())")
+
+        name = "Python"
+        t = t"Hello, {name}"
+        self.assertEqual(repr(t),
+            "Template(strings=('Hello, ', ''), "
+            "interpolations=(Interpolation('Python', 'name', None, ''),))"
+        )
+
+    def test_interpolation_basics(self):
+        # Test basic interpolation
+        name = "Python"
+        t = t"Hello, {name}"
+        self.assertTStringEqual(t, ("Hello, ", ""), [(name, "name")])
+        self.assertEqual(fstring(t), "Hello, Python")
+
+        # Multiple interpolations
+        first = "Python"
+        last = "Developer"
+        t = t"{first} {last}"
+        self.assertTStringEqual(
+            t, ("", " ", ""), [(first, 'first'), (last, 'last')]
+        )
+        self.assertEqual(fstring(t), "Python Developer")
+
+        # Interpolation with expressions
+        a = 10
+        b = 20
+        t = t"Sum: {a + b}"
+        self.assertTStringEqual(t, ("Sum: ", ""), [(a + b, "a + b")])
+        self.assertEqual(fstring(t), "Sum: 30")
+
+        # Interpolation with function
+        def square(x):
+            return x * x
+        t = t"Square: {square(5)}"
+        self.assertTStringEqual(
+            t, ("Square: ", ""), [(square(5), "square(5)")]
+        )
+        self.assertEqual(fstring(t), "Square: 25")
+
+        # Test attribute access in expressions
+        class Person:
+            def __init__(self, name):
+                self.name = name
+
+            def upper(self):
+                return self.name.upper()
+
+        person = Person("Alice")
+        t = t"Name: {person.name}"
+        self.assertTStringEqual(
+            t, ("Name: ", ""), [(person.name, "person.name")]
+        )
+        self.assertEqual(fstring(t), "Name: Alice")
+
+        # Test method calls
+        t = t"Name: {person.upper()}"
+        self.assertTStringEqual(
+            t, ("Name: ", ""), [(person.upper(), "person.upper()")]
+        )
+        self.assertEqual(fstring(t), "Name: ALICE")
+
+        # Test dictionary access
+        data = {"name": "Bob", "age": 30}
+        t = t"Name: {data['name']}, Age: {data['age']}"
+        self.assertTStringEqual(
+            t, ("Name: ", ", Age: ", ""),
+            [(data["name"], "data['name']"), (data["age"], "data['age']")],
+        )
+        self.assertEqual(fstring(t), "Name: Bob, Age: 30")
+
+    def test_format_specifiers(self):
+        # Test basic format specifiers
+        value = 3.14159
+        t = t"Pi: {value:.2f}"
+        self.assertTStringEqual(
+            t, ("Pi: ", ""), [(value, "value", None, ".2f")]
+        )
+        self.assertEqual(fstring(t), "Pi: 3.14")
+
+    def test_conversions(self):
+        # Test !s conversion (str)
+        obj = object()
+        t = t"Object: {obj!s}"
+        self.assertTStringEqual(t, ("Object: ", ""), [(obj, "obj", "s")])
+        self.assertEqual(fstring(t), f"Object: {str(obj)}")
+
+        # Test !r conversion (repr)
+        t = t"Data: {obj!r}"
+        self.assertTStringEqual(t, ("Data: ", ""), [(obj, "obj", "r")])
+        self.assertEqual(fstring(t), f"Data: {repr(obj)}")
+
+        # Test !a conversion (ascii)
+        text = "Café"
+        t = t"ASCII: {text!a}"
+        self.assertTStringEqual(t, ("ASCII: ", ""), [(text, "text", "a")])
+        self.assertEqual(fstring(t), f"ASCII: {ascii(text)}")
+
+        # Test !z conversion (error)
+        num = 1
+        with self.assertRaises(SyntaxError):
+            eval("t'{num!z}'")
+
+    def test_debug_specifier(self):
+        # Test debug specifier
+        value = 42
+        t = t"Value: {value=}"
+        self.assertTStringEqual(
+            t, ("Value: value=", ""), [(value, "value", "r")]
+        )
+        self.assertEqual(fstring(t), "Value: value=42")
+
+        # Test debug specifier with format (conversion default to !r)
+        t = t"Value: {value=:.2f}"
+        self.assertTStringEqual(
+            t, ("Value: value=", ""), [(value, "value", None, ".2f")]
+        )
+        self.assertEqual(fstring(t), "Value: value=42.00")
+
+        # Test debug specifier with conversion
+        t = t"Value: {value=!s}"
+        self.assertTStringEqual(
+            t, ("Value: value=", ""), [(value, "value", "s")]
+        )
+
+        # Test white space in debug specifier
+        t = t"Value: {value = }"
+        self.assertTStringEqual(
+            t, ("Value: value = ", ""), [(value, "value", "r")]
+        )
+        self.assertEqual(fstring(t), "Value: value = 42")
+
+    def test_raw_tstrings(self):
+        path = r"C:\Users"
+        t = rt"{path}\Documents"
+        self.assertTStringEqual(t, ("", r"\Documents"), [(path, "path")])
+        self.assertEqual(fstring(t), r"C:\Users\Documents")
+
+        # Test alternative prefix
+        t = tr"{path}\Documents"
+        self.assertTStringEqual(t, ("", r"\Documents"), [(path, "path")])
+
+
+    def test_template_concatenation(self):
+        # Test template + template
+        t1 = t"Hello, "
+        t2 = t"world"
+        combined = t1 + t2
+        self.assertTStringEqual(combined, ("Hello, world",), ())
+        self.assertEqual(fstring(combined), "Hello, world")
+
+        # Test template + string
+        t1 = t"Hello"
+        combined = t1 + ", world"
+        self.assertTStringEqual(combined, ("Hello, world",), ())
+        self.assertEqual(fstring(combined), "Hello, world")
+
+        # Test template + template with interpolation
+        name = "Python"
+        t1 = t"Hello, "
+        t2 = t"{name}"
+        combined = t1 + t2
+        self.assertTStringEqual(combined, ("Hello, ", ""), [(name, "name")])
+        self.assertEqual(fstring(combined), "Hello, Python")
+
+        # Test string + template
+        t = "Hello, " + t"{name}"
+        self.assertTStringEqual(t, ("Hello, ", ""), [(name, "name")])
+        self.assertEqual(fstring(t), "Hello, Python")
+
+    def test_nested_templates(self):
+        # Test a template inside another template expression
+        name = "Python"
+        inner = t"{name}"
+        t = t"Language: {inner}"
+
+        t_interp = t.interpolations[0]
+        self.assertEqual(t.strings, ("Language: ", ""))
+        self.assertEqual(t_interp.value.strings, ("", ""))
+        self.assertEqual(t_interp.value.interpolations[0].value, name)
+        self.assertEqual(t_interp.value.interpolations[0].expression, "name")
+        self.assertEqual(t_interp.value.interpolations[0].conversion, None)
+        self.assertEqual(t_interp.value.interpolations[0].format_spec, "")
+        self.assertEqual(t_interp.expression, "inner")
+        self.assertEqual(t_interp.conversion, None)
+        self.assertEqual(t_interp.format_spec, "")
+
+    def test_syntax_errors(self):
+        for case, err in (
+            ("t'", "unterminated t-string literal"),
+            ("t'''", "unterminated triple-quoted t-string literal"),
+            ("t''''", "unterminated triple-quoted t-string literal"),
+            ("t'{", "'{' was never closed"),
+            ("t'{'", "t-string: expecting '}'"),
+            ("t'{a'", "t-string: expecting '}'"),
+            ("t'}'", "t-string: single '}' is not allowed"),
+            ("t'{}'", "t-string: valid expression required before '}'"),
+            ("t'{=x}'", "t-string: valid expression required before '='"),
+            ("t'{!x}'", "t-string: valid expression required before '!'"),
+            ("t'{:x}'", "t-string: valid expression required before ':'"),
+            ("t'{x;y}'", "t-string: expecting '=', or '!', or ':', or '}'"),
+            ("t'{x=y}'", "t-string: expecting '!', or ':', or '}'"),
+            ("t'{x!s!}'", "t-string: expecting ':' or '}'"),
+            ("t'{x!s:'", "t-string: expecting '}', or format specs"),
+            ("t'{x!}'", "t-string: missing conversion character"),
+            ("t'{x=!}'", "t-string: missing conversion character"),
+            ("t'{x!z}'", "t-string: invalid conversion character 'z': "
+                         "expected 's', 'r', or 'a'"),
+            ("t'{lambda:1}'", "t-string: lambda expressions are not allowed "
+                              "without parentheses"),
+            ("t'{x:{;}}'", "t-string: expecting a valid expression after '{'"),
+        ):
+            with self.subTest(case), self.assertRaisesRegex(SyntaxError, err):
+                eval(case)
+
+    def test_runtime_errors(self):
+        # Test missing variables
+        with self.assertRaises(NameError):
+            eval("t'Hello, {name}'")
+
+    def test_literal_concatenation(self):
+        # Test concatenation of t-string literals
+        t = t"Hello, " t"world"
+        self.assertTStringEqual(t, ("Hello, world",), ())
+        self.assertEqual(fstring(t), "Hello, world")
+
+        # Test concatenation with interpolation
+        name = "Python"
+        t = t"Hello, " t"{name}"
+        self.assertTStringEqual(t, ("Hello, ", ""), [(name, "name")])
+        self.assertEqual(fstring(t), "Hello, Python")
+
+        # Test concatenation with string literal
+        name = "Python"
+        t = t"Hello, {name}" "and welcome!"
+        self.assertTStringEqual(
+            t, ("Hello, ", "and welcome!"), [(name, "name")]
+        )
+        self.assertEqual(fstring(t), "Hello, Pythonand welcome!")
+
+        # Test concatenation with Unicode literal
+        name = "Python"
+        t = t"Hello, {name}" u"and welcome!"
+        self.assertTStringEqual(
+            t, ("Hello, ", "and welcome!"), [(name, "name")]
+        )
+        self.assertEqual(fstring(t), "Hello, Pythonand welcome!")
+
+        # Test concatenation with f-string literal
+        tab = '\t'
+        t = t"Tab: {tab}. " f"f-tab: {tab}."
+        self.assertTStringEqual(t, ("Tab: ", ". f-tab: \t."), [(tab, "tab")])
+        self.assertEqual(fstring(t), "Tab: \t. f-tab: \t.")
+
+        # Test concatenation with raw string literal
+        tab = '\t'
+        t = t"Tab: {tab}. " r"Raw tab: \t."
+        self.assertTStringEqual(
+            t, ("Tab: ", r". Raw tab: \t."), [(tab, "tab")]
+        )
+        self.assertEqual(fstring(t), "Tab: \t. Raw tab: \\t.")
+
+        # Test concatenation with raw f-string literal
+        tab = '\t'
+        t = t"Tab: {tab}. " rf"f-tab: {tab}. Raw tab: \t."
+        self.assertTStringEqual(
+            t, ("Tab: ", ". f-tab: \t. Raw tab: \\t."), [(tab, "tab")]
+        )
+        self.assertEqual(fstring(t), "Tab: \t. f-tab: \t. Raw tab: \\t.")
+
+        what = 't'
+        expected_msg = 'cannot mix bytes and nonbytes literals'
+        for case in (
+            "t'{what}-string literal' b'bytes literal'",
+            "t'{what}-string literal' br'raw bytes literal'",
+        ):
+            with self.assertRaisesRegex(SyntaxError, expected_msg):
+                eval(case)
+
+    def test_triple_quoted(self):
+        # Test triple-quoted t-strings
+        t = t"""
+        Hello,
+        world
+        """
+        self.assertTStringEqual(
+            t, ("\n        Hello,\n        world\n        ",), ()
+        )
+        self.assertEqual(fstring(t), "\n        Hello,\n        world\n        ")
+
+        # Test triple-quoted with interpolation
+        name = "Python"
+        t = t"""
+        Hello,
+        {name}
+        """
+        self.assertTStringEqual(
+            t, ("\n        Hello,\n        ", "\n        "), [(name, "name")]
+        )
+        self.assertEqual(fstring(t), "\n        Hello,\n        Python\n        ")
+
+if __name__ == '__main__':
+    unittest.main()
index 839326f64368095165a1423d850293202b170306..d3af7a8489e650e12d1875dd1371b3ae659ce5da 100644 (file)
@@ -202,6 +202,15 @@ class UnparseTestCase(ASTTestCase):
         self.check_ast_roundtrip('f" something { my_dict["key"] } something else "')
         self.check_ast_roundtrip('f"{f"{f"{f"{f"{f"{1+1}"}"}"}"}"}"')
 
+    def test_tstrings(self):
+        self.check_ast_roundtrip("t'foo'")
+        self.check_ast_roundtrip("t'foo {bar}'")
+        self.check_ast_roundtrip("t'foo {bar!s:.2f}'")
+        self.check_ast_roundtrip("t'foo {bar}' f'{bar}'")
+        self.check_ast_roundtrip("f'{bar}' t'foo {bar}'")
+        self.check_ast_roundtrip("t'foo {bar}' fr'\\hello {bar}'")
+        self.check_ast_roundtrip("t'foo {bar}' u'bar'")
+
     def test_strings(self):
         self.check_ast_roundtrip("u'foo'")
         self.check_ast_roundtrip("r'foo'")
@@ -918,7 +927,7 @@ class DirectoryTestCase(ASTTestCase):
     run_always_files = {"test_grammar.py", "test_syntax.py", "test_compile.py",
                         "test_ast.py", "test_asdl_parser.py", "test_fstring.py",
                         "test_patma.py", "test_type_alias.py", "test_type_params.py",
-                        "test_tokenize.py"}
+                        "test_tokenize.py", "test_tstring.py"}
 
     _files_to_test = None
 
index 54d7cdccadc79a5e126d1d4b4c56c13599eb133f..a1fde61cd8a070b3cdbccfc407f01293ee7241a3 100644 (file)
@@ -66,12 +66,15 @@ SOFT_KEYWORD = 58
 FSTRING_START = 59
 FSTRING_MIDDLE = 60
 FSTRING_END = 61
-COMMENT = 62
-NL = 63
+TSTRING_START = 62
+TSTRING_MIDDLE = 63
+TSTRING_END = 64
+COMMENT = 65
+NL = 66
 # These aren't used by the C tokenizer but are needed for tokenize.py
-ERRORTOKEN = 64
-ENCODING = 65
-N_TOKENS = 66
+ERRORTOKEN = 67
+ENCODING = 68
+N_TOKENS = 69
 # Special definitions for cooperation with parser
 NT_OFFSET = 256
 
index 7afacff7381f1ce482a19bef7441280d42b3fb61..edb1ed8bdb8c9cd67f5feb3e0340b2aba3f55e94 100644 (file)
@@ -251,7 +251,7 @@ class Untokenizer:
                     self.tokens.append(indent)
                     self.prev_col = len(indent)
                 startline = False
-            elif tok_type == FSTRING_MIDDLE:
+            elif tok_type in {FSTRING_MIDDLE, TSTRING_MIDDLE}:
                 if '{' in token or '}' in token:
                     token = self.escape_brackets(token)
                     last_line = token.splitlines()[-1]
@@ -308,7 +308,7 @@ class Untokenizer:
             elif startline and indents:
                 toks_append(indents[-1])
                 startline = False
-            elif toknum == FSTRING_MIDDLE:
+            elif toknum in {FSTRING_MIDDLE, TSTRING_MIDDLE}:
                 tokval = self.escape_brackets(tokval)
 
             # Insert a space between two consecutive brackets if we are in an f-string
index 925e0a243c9e969188e0ebe6bf3d90d6784ccd90..37ce0b55203cf7bc4ccc5314823b71d181a5be2c 100644 (file)
@@ -537,6 +537,7 @@ OBJECT_OBJS=        \
                Objects/floatobject.o \
                Objects/frameobject.o \
                Objects/funcobject.o \
+               Objects/interpolationobject.o \
                Objects/iterobject.o \
                Objects/listobject.o \
                Objects/longobject.o \
@@ -553,6 +554,7 @@ OBJECT_OBJS=        \
                Objects/setobject.o \
                Objects/sliceobject.o \
                Objects/structseq.o \
+               Objects/templateobject.o \
                Objects/tupleobject.o \
                Objects/typeobject.o \
                Objects/typevarobject.o \
@@ -1323,6 +1325,7 @@ PYTHON_HEADERS= \
                $(srcdir)/Include/internal/pycore_interp_structs.h \
                $(srcdir)/Include/internal/pycore_interpframe.h \
                $(srcdir)/Include/internal/pycore_interpframe_structs.h \
+               $(srcdir)/Include/internal/pycore_interpolation.h \
                $(srcdir)/Include/internal/pycore_intrinsics.h \
                $(srcdir)/Include/internal/pycore_jit.h \
                $(srcdir)/Include/internal/pycore_list.h \
@@ -1377,6 +1380,7 @@ PYTHON_HEADERS= \
                $(srcdir)/Include/internal/pycore_structseq.h \
                $(srcdir)/Include/internal/pycore_symtable.h \
                $(srcdir)/Include/internal/pycore_sysmodule.h \
+               $(srcdir)/Include/internal/pycore_template.h \
                $(srcdir)/Include/internal/pycore_time.h \
                $(srcdir)/Include/internal/pycore_token.h \
                $(srcdir)/Include/internal/pycore_traceback.h \
@@ -2525,6 +2529,7 @@ LIBSUBDIRS=       asyncio \
                re \
                site-packages \
                sqlite3 \
+               string \
                sysconfig \
                tkinter \
                tomllib \
@@ -2635,6 +2640,7 @@ TESTSUBDIRS=      idlelib/idle_test \
                test/test_peg_generator \
                test/test_pydoc \
                test/test_pyrepl \
+               test/test_string \
                test/test_sqlite3 \
                test/test_tkinter \
                test/test_tomllib \
diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-04-22-15-37-05.gh-issue-132661.XE_A42.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-04-22-15-37-05.gh-issue-132661.XE_A42.rst
new file mode 100644 (file)
index 0000000..666e8de
--- /dev/null
@@ -0,0 +1,3 @@
+Implement :pep:`750` (Template Strings). Add new syntax for t-strings and
+implement new internal :class:`!string.templatelib.Template` and
+:class:`!string.templatelib.Interpolation` types.
diff --git a/Objects/clinic/interpolationobject.c.h b/Objects/clinic/interpolationobject.c.h
new file mode 100644 (file)
index 0000000..7a94dab
--- /dev/null
@@ -0,0 +1,89 @@
+/*[clinic input]
+preserve
+[clinic start generated code]*/
+
+#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
+#  include "pycore_gc.h"          // PyGC_Head
+#  include "pycore_runtime.h"     // _Py_ID()
+#endif
+#include "pycore_modsupport.h"    // _PyArg_UnpackKeywords()
+
+static PyObject *
+interpolation_new_impl(PyTypeObject *type, PyObject *value,
+                       PyObject *expression, PyObject *conversion,
+                       PyObject *format_spec);
+
+static PyObject *
+interpolation_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
+{
+    PyObject *return_value = NULL;
+    #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
+
+    #define NUM_KEYWORDS 4
+    static struct {
+        PyGC_Head _this_is_not_used;
+        PyObject_VAR_HEAD
+        Py_hash_t ob_hash;
+        PyObject *ob_item[NUM_KEYWORDS];
+    } _kwtuple = {
+        .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS)
+        .ob_hash = -1,
+        .ob_item = { &_Py_ID(value), &_Py_ID(expression), &_Py_ID(conversion), &_Py_ID(format_spec), },
+    };
+    #undef NUM_KEYWORDS
+    #define KWTUPLE (&_kwtuple.ob_base.ob_base)
+
+    #else  // !Py_BUILD_CORE
+    #  define KWTUPLE NULL
+    #endif  // !Py_BUILD_CORE
+
+    static const char * const _keywords[] = {"value", "expression", "conversion", "format_spec", NULL};
+    static _PyArg_Parser _parser = {
+        .keywords = _keywords,
+        .fname = "Interpolation",
+        .kwtuple = KWTUPLE,
+    };
+    #undef KWTUPLE
+    PyObject *argsbuf[4];
+    PyObject * const *fastargs;
+    Py_ssize_t nargs = PyTuple_GET_SIZE(args);
+    Py_ssize_t noptargs = nargs + (kwargs ? PyDict_GET_SIZE(kwargs) : 0) - 2;
+    PyObject *value;
+    PyObject *expression;
+    PyObject *conversion = Py_None;
+    PyObject *format_spec = &_Py_STR(empty);
+
+    fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser,
+            /*minpos*/ 2, /*maxpos*/ 4, /*minkw*/ 0, /*varpos*/ 0, argsbuf);
+    if (!fastargs) {
+        goto exit;
+    }
+    value = fastargs[0];
+    if (!PyUnicode_Check(fastargs[1])) {
+        _PyArg_BadArgument("Interpolation", "argument 'expression'", "str", fastargs[1]);
+        goto exit;
+    }
+    expression = fastargs[1];
+    if (!noptargs) {
+        goto skip_optional_pos;
+    }
+    if (fastargs[2]) {
+        if (!_conversion_converter(fastargs[2], &conversion)) {
+            goto exit;
+        }
+        if (!--noptargs) {
+            goto skip_optional_pos;
+        }
+    }
+    if (!PyUnicode_Check(fastargs[3])) {
+        _PyArg_BadArgument("Interpolation", "argument 'format_spec'", "str", fastargs[3]);
+        goto exit;
+    }
+    format_spec = fastargs[3];
+skip_optional_pos:
+    return_value = interpolation_new_impl(type, value, expression, conversion, format_spec);
+
+exit:
+    return return_value;
+}
+/*[clinic end generated code: output=599742a5ccd6f060 input=a9049054013a1b77]*/
diff --git a/Objects/interpolationobject.c b/Objects/interpolationobject.c
new file mode 100644 (file)
index 0000000..aaea3b8
--- /dev/null
@@ -0,0 +1,229 @@
+/* t-string Interpolation object implementation */
+
+#include "Python.h"
+#include "pycore_initconfig.h"    // _PyStatus_OK
+#include "pycore_interpolation.h"
+#include "pycore_typeobject.h"    // _PyType_GetDict
+
+static int
+_conversion_converter(PyObject *arg, PyObject **conversion)
+{
+    if (arg == Py_None) {
+        return 1;
+    }
+
+    if (!PyUnicode_Check(arg)) {
+        PyErr_Format(PyExc_TypeError,
+            "Interpolation() argument 'conversion' must be str, not %T",
+            arg);
+        return 0;
+    }
+
+    Py_ssize_t len;
+    const char *conv_str = PyUnicode_AsUTF8AndSize(arg, &len);
+    if (len != 1 || !(conv_str[0] == 'a' || conv_str[0] == 'r' || conv_str[0] == 's')) {
+        PyErr_SetString(PyExc_ValueError,
+            "Interpolation() argument 'conversion' must be one of 's', 'a' or 'r'");
+        return 0;
+    }
+
+    *conversion = arg;
+    return 1;
+}
+
+#include "clinic/interpolationobject.c.h"
+
+/*[clinic input]
+class Interpolation "interpolationobject *" "&_PyInterpolation_Type"
+[clinic start generated code]*/
+/*[clinic end generated code: output=da39a3ee5e6b4b0d input=161c64a16f9c4544]*/
+
+typedef struct {
+    PyObject_HEAD
+    PyObject *value;
+    PyObject *expression;
+    PyObject *conversion;
+    PyObject *format_spec;
+} interpolationobject;
+
+#define interpolationobject_CAST(op) \
+    (assert(_PyInterpolation_CheckExact(op)), _Py_CAST(interpolationobject*, (op)))
+
+/*[clinic input]
+@classmethod
+Interpolation.__new__ as interpolation_new
+
+    value: object
+    expression: object(subclass_of='&PyUnicode_Type')
+    conversion: object(converter='_conversion_converter') = None
+    format_spec: object(subclass_of='&PyUnicode_Type', c_default='&_Py_STR(empty)') = ""
+[clinic start generated code]*/
+
+static PyObject *
+interpolation_new_impl(PyTypeObject *type, PyObject *value,
+                       PyObject *expression, PyObject *conversion,
+                       PyObject *format_spec)
+/*[clinic end generated code: output=6488e288765bc1a9 input=d91711024068528c]*/
+{
+    interpolationobject *self = PyObject_GC_New(interpolationobject, type);
+    if (!self) {
+        return NULL;
+    }
+
+    self->value = Py_NewRef(value);
+    self->expression = Py_NewRef(expression);
+    self->conversion = Py_NewRef(conversion);
+    self->format_spec = Py_NewRef(format_spec);
+    PyObject_GC_Track(self);
+    return (PyObject *) self;
+}
+
+static void
+interpolation_dealloc(PyObject *op)
+{
+    PyObject_GC_UnTrack(op);
+    Py_TYPE(op)->tp_clear(op);
+    Py_TYPE(op)->tp_free(op);
+}
+
+static int
+interpolation_clear(PyObject *op)
+{
+    interpolationobject *self = interpolationobject_CAST(op);
+    Py_CLEAR(self->value);
+    Py_CLEAR(self->expression);
+    Py_CLEAR(self->conversion);
+    Py_CLEAR(self->format_spec);
+    return 0;
+}
+
+static int
+interpolation_traverse(PyObject *op, visitproc visit, void *arg)
+{
+    interpolationobject *self = interpolationobject_CAST(op);
+    Py_VISIT(self->value);
+    Py_VISIT(self->expression);
+    Py_VISIT(self->conversion);
+    Py_VISIT(self->format_spec);
+    return 0;
+}
+
+static PyObject *
+interpolation_repr(PyObject *op)
+{
+    interpolationobject *self = interpolationobject_CAST(op);
+    return PyUnicode_FromFormat("%s(%R, %R, %R, %R)",
+                                _PyType_Name(Py_TYPE(self)), self->value, self->expression,
+                                self->conversion, self->format_spec);
+}
+
+static PyMemberDef interpolation_members[] = {
+    {"value", Py_T_OBJECT_EX, offsetof(interpolationobject, value), Py_READONLY, "Value"},
+    {"expression", Py_T_OBJECT_EX, offsetof(interpolationobject, expression), Py_READONLY, "Expression"},
+    {"conversion", Py_T_OBJECT_EX, offsetof(interpolationobject, conversion), Py_READONLY, "Conversion"},
+    {"format_spec", Py_T_OBJECT_EX, offsetof(interpolationobject, format_spec), Py_READONLY, "Format specifier"},
+    {NULL}
+};
+
+static PyObject*
+interpolation_reduce(PyObject *op, PyObject *Py_UNUSED(dummy))
+{
+    interpolationobject *self = interpolationobject_CAST(op);
+    return Py_BuildValue("(O(OOOO))", (PyObject *)Py_TYPE(op),
+                         self->value, self->expression,
+                         self->conversion, self->format_spec);
+}
+
+static PyMethodDef interpolation_methods[] = {
+    {"__reduce__", interpolation_reduce, METH_NOARGS,
+        PyDoc_STR("__reduce__() -> (cls, state)")},
+    {NULL, NULL},
+};
+
+PyTypeObject _PyInterpolation_Type = {
+    PyVarObject_HEAD_INIT(NULL, 0)
+    .tp_name = "string.templatelib.Interpolation",
+    .tp_doc = PyDoc_STR("Interpolation object"),
+    .tp_basicsize = sizeof(interpolationobject),
+    .tp_itemsize = 0,
+    .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
+    .tp_new = interpolation_new,
+    .tp_alloc = PyType_GenericAlloc,
+    .tp_dealloc = interpolation_dealloc,
+    .tp_clear = interpolation_clear,
+    .tp_free = PyObject_GC_Del,
+    .tp_repr = interpolation_repr,
+    .tp_members = interpolation_members,
+    .tp_methods = interpolation_methods,
+    .tp_traverse = interpolation_traverse,
+};
+
+PyStatus
+_PyInterpolation_InitTypes(PyInterpreterState *interp)
+{
+    PyObject *tuple = Py_BuildValue("(ssss)", "value", "expression", "conversion", "format_spec");
+    if (!tuple) {
+        goto error;
+    }
+
+    PyObject *dict = _PyType_GetDict(&_PyInterpolation_Type);
+    if (!dict) {
+        Py_DECREF(tuple);
+        goto error;
+    }
+
+    int status = PyDict_SetItemString(dict, "__match_args__", tuple);
+    Py_DECREF(tuple);
+    if (status < 0) {
+        goto error;
+    }
+    return _PyStatus_OK();
+
+error:
+    return _PyStatus_ERR("Can't initialize interpolation types");
+}
+
+PyObject *
+_PyInterpolation_Build(PyObject *value, PyObject *str, int conversion, PyObject *format_spec)
+{
+    interpolationobject *interpolation = PyObject_GC_New(interpolationobject, &_PyInterpolation_Type);
+    if (!interpolation) {
+        return NULL;
+    }
+
+    interpolation->value = Py_NewRef(value);
+    interpolation->expression = Py_NewRef(str);
+    interpolation->format_spec = Py_NewRef(format_spec);
+    interpolation->conversion = NULL;
+
+    if (conversion == 0) {
+        interpolation->conversion = Py_None;
+    }
+    else {
+        switch (conversion) {
+            case FVC_ASCII:
+                interpolation->conversion = _Py_LATIN1_CHR('a');
+                break;
+            case FVC_REPR:
+                interpolation->conversion = _Py_LATIN1_CHR('r');
+                break;
+            case FVC_STR:
+                interpolation->conversion = _Py_LATIN1_CHR('s');
+                break;
+            default:
+                PyErr_SetString(PyExc_SystemError,
+                    "Interpolation() argument 'conversion' must be one of 's', 'a' or 'r'");
+                Py_DECREF(interpolation);
+                return NULL;
+        }
+    }
+
+    PyObject_GC_Track(interpolation);
+    return (PyObject *) interpolation;
+}
+
+PyObject *
+_PyInterpolation_GetValueRef(PyObject *interpolation)
+{
+    return Py_NewRef(interpolationobject_CAST(interpolation)->value);
+}
index 99bb1d9c0bfad5d08888fa1643083a174fcc42fa..a33a4267d62d65e86c07634dd4d988968eb35cd6 100644 (file)
@@ -15,6 +15,7 @@
 #include "pycore_hamt.h"          // _PyHamtItems_Type
 #include "pycore_initconfig.h"    // _PyStatus_OK()
 #include "pycore_instruction_sequence.h" // _PyInstructionSequence_Type
+#include "pycore_interpolation.h" // _PyInterpolation_Type
 #include "pycore_list.h"          // _PyList_DebugMallocStats()
 #include "pycore_long.h"          // _PyLong_GetZero()
 #include "pycore_memoryobject.h"  // _PyManagedBuffer_Type
@@ -25,6 +26,7 @@
 #include "pycore_pymem.h"         // _PyMem_IsPtrFreed()
 #include "pycore_pystate.h"       // _PyThreadState_GET()
 #include "pycore_symtable.h"      // PySTEntry_Type
+#include "pycore_template.h"      // _PyTemplate_Type _PyTemplateIter_Type
 #include "pycore_tuple.h"         // _PyTuple_DebugMallocStats()
 #include "pycore_typeobject.h"    // _PyBufferWrapper_Type
 #include "pycore_typevarobject.h" // _PyTypeAlias_Type
@@ -2409,6 +2411,7 @@ static PyTypeObject* static_types[] = {
     &_PyHamt_CollisionNode_Type,
     &_PyHamt_Type,
     &_PyInstructionSequence_Type,
+    &_PyInterpolation_Type,
     &_PyLegacyEventHandler_Type,
     &_PyLineIterator,
     &_PyManagedBuffer_Type,
@@ -2418,6 +2421,8 @@ static PyTypeObject* static_types[] = {
     &_PyNone_Type,
     &_PyNotImplemented_Type,
     &_PyPositionsIterator,
+    &_PyTemplate_Type,
+    &_PyTemplateIter_Type,
     &_PyUnicodeASCIIIter_Type,
     &_PyUnion_Type,
 #ifdef _Py_TIER2
diff --git a/Objects/templateobject.c b/Objects/templateobject.c
new file mode 100644 (file)
index 0000000..7d35698
--- /dev/null
@@ -0,0 +1,483 @@
+/* t-string Template object implementation */
+
+#include "Python.h"
+#include "pycore_interpolation.h" // _PyInterpolation_CheckExact()
+#include "pycore_runtime.h"       // _Py_STR()
+#include "pycore_template.h"
+
+typedef struct {
+    PyObject_HEAD
+    PyObject *stringsiter;
+    PyObject *interpolationsiter;
+    int from_strings;
+} templateiterobject;
+
+#define templateiterobject_CAST(op) \
+    (assert(_PyTemplateIter_CheckExact(op)), _Py_CAST(templateiterobject*, (op)))
+
+static PyObject *
+templateiter_next(PyObject *op)
+{
+    templateiterobject *self = templateiterobject_CAST(op);
+    PyObject *item;
+    if (self->from_strings) {
+        item = PyIter_Next(self->stringsiter);
+        self->from_strings = 0;
+        if (PyUnicode_GET_LENGTH(item) == 0) {
+            Py_SETREF(item, PyIter_Next(self->interpolationsiter));
+            self->from_strings = 1;
+        }
+    } else {
+        item = PyIter_Next(self->interpolationsiter);
+        self->from_strings = 1;
+    }
+    return item;
+}
+
+static void
+templateiter_dealloc(PyObject *op)
+{
+    PyObject_GC_UnTrack(op);
+    Py_TYPE(op)->tp_clear(op);
+    Py_TYPE(op)->tp_free(op);
+}
+
+static int
+templateiter_clear(PyObject *op)
+{
+    templateiterobject *self = templateiterobject_CAST(op);
+    Py_CLEAR(self->stringsiter);
+    Py_CLEAR(self->interpolationsiter);
+    return 0;
+}
+
+static int
+templateiter_traverse(PyObject *op, visitproc visit, void *arg)
+{
+    templateiterobject *self = templateiterobject_CAST(op);
+    Py_VISIT(self->stringsiter);
+    Py_VISIT(self->interpolationsiter);
+    return 0;
+}
+
+PyTypeObject _PyTemplateIter_Type = {
+    PyVarObject_HEAD_INIT(NULL, 0)
+    .tp_name = "string.templatelib.TemplateIter",
+    .tp_doc = PyDoc_STR("Template iterator object"),
+    .tp_basicsize = sizeof(templateiterobject),
+    .tp_itemsize = 0,
+    .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
+    .tp_alloc = PyType_GenericAlloc,
+    .tp_dealloc = templateiter_dealloc,
+    .tp_clear = templateiter_clear,
+    .tp_free = PyObject_GC_Del,
+    .tp_traverse = templateiter_traverse,
+    .tp_iter = PyObject_SelfIter,
+    .tp_iternext = templateiter_next,
+};
+
+typedef struct {
+    PyObject_HEAD
+    PyObject *strings;
+    PyObject *interpolations;
+} templateobject;
+
+#define templateobject_CAST(op) \
+    (assert(_PyTemplate_CheckExact(op)), _Py_CAST(templateobject*, (op)))
+
+static PyObject *
+template_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+    if (kwds != NULL) {
+        PyErr_SetString(PyExc_TypeError, "Template.__new__ only accepts *args arguments");
+        return NULL;
+    }
+
+    Py_ssize_t argslen = PyTuple_GET_SIZE(args);
+    Py_ssize_t stringslen = 0;
+    Py_ssize_t interpolationslen = 0;
+    int last_was_str = 0;
+
+    for (Py_ssize_t i = 0; i < argslen; i++) {
+        PyObject *item = PyTuple_GET_ITEM(args, i);
+        if (PyUnicode_Check(item)) {
+            if (!last_was_str) {
+                stringslen++;
+            }
+            last_was_str = 1;
+        }
+        else if (_PyInterpolation_CheckExact(item)) {
+            if (!last_was_str) {
+                stringslen++;
+            }
+            interpolationslen++;
+            last_was_str = 0;
+        }
+        else {
+            PyErr_Format(
+                PyExc_TypeError,
+                "Template.__new__ *args need to be of type 'str' or 'Interpolation', got %T",
+                item);
+            return NULL;
+        }
+    }
+    if (!last_was_str) {
+        stringslen++;
+    }
+
+    PyObject *strings = PyTuple_New(stringslen);
+    if (!strings) {
+        return NULL;
+    }
+
+    PyObject *interpolations = PyTuple_New(interpolationslen);
+    if (!interpolations) {
+        Py_DECREF(strings);
+        return NULL;
+    }
+
+    last_was_str = 0;
+    Py_ssize_t stringsidx = 0, interpolationsidx = 0;
+    for (Py_ssize_t i = 0; i < argslen; i++) {
+        PyObject *item = PyTuple_GET_ITEM(args, i);
+        if (PyUnicode_Check(item)) {
+            if (last_was_str) {
+                PyObject *laststring = PyTuple_GET_ITEM(strings, stringsidx - 1);
+                PyObject *concat = PyUnicode_Concat(laststring, item);
+                Py_DECREF(laststring);
+                if (!concat) {
+                    Py_DECREF(strings);
+                    Py_DECREF(interpolations);
+                    return NULL;
+                }
+                PyTuple_SET_ITEM(strings, stringsidx - 1, concat);
+            }
+            else {
+                PyTuple_SET_ITEM(strings, stringsidx++, Py_NewRef(item));
+            }
+            last_was_str = 1;
+        }
+        else if (_PyInterpolation_CheckExact(item)) {
+            if (!last_was_str) {
+                _Py_DECLARE_STR(empty, "");
+                PyTuple_SET_ITEM(strings, stringsidx++, &_Py_STR(empty));
+            }
+            PyTuple_SET_ITEM(interpolations, interpolationsidx++, Py_NewRef(item));
+            last_was_str = 0;
+        }
+    }
+    if (!last_was_str) {
+        _Py_DECLARE_STR(empty, "");
+        PyTuple_SET_ITEM(strings, stringsidx++, &_Py_STR(empty));
+    }
+
+    PyObject *template = _PyTemplate_Build(strings, interpolations);
+    Py_DECREF(strings);
+    Py_DECREF(interpolations);
+    return template;
+}
+
+static void
+template_dealloc(PyObject *op)
+{
+    PyObject_GC_UnTrack(op);
+    Py_TYPE(op)->tp_clear(op);
+    Py_TYPE(op)->tp_free(op);
+}
+
+static int
+template_clear(PyObject *op)
+{
+    templateobject *self = templateobject_CAST(op);
+    Py_CLEAR(self->strings);
+    Py_CLEAR(self->interpolations);
+    return 0;
+}
+
+static int
+template_traverse(PyObject *op, visitproc visit, void *arg)
+{
+    templateobject *self = templateobject_CAST(op);
+    Py_VISIT(self->strings);
+    Py_VISIT(self->interpolations);
+    return 0;
+}
+
+static PyObject *
+template_repr(PyObject *op)
+{
+    templateobject *self = templateobject_CAST(op);
+    return PyUnicode_FromFormat("%s(strings=%R, interpolations=%R)",
+                                _PyType_Name(Py_TYPE(self)),
+                                self->strings,
+                                self->interpolations);
+}
+
+static PyObject *
+template_iter(PyObject *op)
+{
+    templateobject *self = templateobject_CAST(op);
+    templateiterobject *iter = PyObject_GC_New(templateiterobject, &_PyTemplateIter_Type);
+    if (iter == NULL) {
+        return NULL;
+    }
+
+    PyObject *stringsiter = PyObject_GetIter(self->strings);
+    if (stringsiter == NULL) {
+        Py_DECREF(iter);
+        return NULL;
+    }
+
+    PyObject *interpolationsiter = PyObject_GetIter(self->interpolations);
+    if (interpolationsiter == NULL) {
+        Py_DECREF(iter);
+        Py_DECREF(stringsiter);
+        return NULL;
+    }
+
+    iter->stringsiter = stringsiter;
+    iter->interpolationsiter = interpolationsiter;
+    iter->from_strings = 1;
+    PyObject_GC_Track(iter);
+    return (PyObject *)iter;
+}
+
+static PyObject *
+template_strings_append_str(PyObject *strings, PyObject *str)
+{
+    Py_ssize_t stringslen = PyTuple_GET_SIZE(strings);
+    PyObject *string = PyTuple_GET_ITEM(strings, stringslen - 1);
+    PyObject *concat = PyUnicode_Concat(string, str);
+    if (concat == NULL) {
+        return NULL;
+    }
+
+    PyObject *newstrings = PyTuple_New(stringslen);
+    if (newstrings == NULL) {
+        Py_DECREF(concat);
+        return NULL;
+    }
+
+    for (Py_ssize_t i = 0; i < stringslen - 1; i++) {
+        PyTuple_SET_ITEM(newstrings, i, Py_NewRef(PyTuple_GET_ITEM(strings, i)));
+    }
+    PyTuple_SET_ITEM(newstrings, stringslen - 1, concat);
+
+    return newstrings;
+}
+
+static PyObject *
+template_strings_prepend_str(PyObject *strings, PyObject *str)
+{
+    Py_ssize_t stringslen = PyTuple_GET_SIZE(strings);
+    PyObject *string = PyTuple_GET_ITEM(strings, 0);
+    PyObject *concat = PyUnicode_Concat(str, string);
+    if (concat == NULL) {
+        return NULL;
+    }
+
+    PyObject *newstrings = PyTuple_New(stringslen);
+    if (newstrings == NULL) {
+        Py_DECREF(concat);
+        return NULL;
+    }
+
+    PyTuple_SET_ITEM(newstrings, 0, concat);
+    for (Py_ssize_t i = 1; i < stringslen; i++) {
+        PyTuple_SET_ITEM(newstrings, i, Py_NewRef(PyTuple_GET_ITEM(strings, i)));
+    }
+
+    return newstrings;
+}
+
+static PyObject *
+template_strings_concat(PyObject *left, PyObject *right)
+{
+    Py_ssize_t left_stringslen = PyTuple_GET_SIZE(left);
+    PyObject *left_laststring = PyTuple_GET_ITEM(left, left_stringslen - 1);
+    Py_ssize_t right_stringslen = PyTuple_GET_SIZE(right);
+    PyObject *right_firststring = PyTuple_GET_ITEM(right, 0);
+
+    PyObject *concat = PyUnicode_Concat(left_laststring, right_firststring);
+    if (concat == NULL) {
+        return NULL;
+    }
+
+    PyObject *newstrings = PyTuple_New(left_stringslen + right_stringslen - 1);
+    if (newstrings == NULL) {
+        Py_DECREF(concat);
+        return NULL;
+    }
+
+    Py_ssize_t index = 0;
+    for (Py_ssize_t i = 0; i < left_stringslen - 1; i++) {
+        PyTuple_SET_ITEM(newstrings, index++, Py_NewRef(PyTuple_GET_ITEM(left, i)));
+    }
+    PyTuple_SET_ITEM(newstrings, index++, concat);
+    for (Py_ssize_t i = 1; i < right_stringslen; i++) {
+        PyTuple_SET_ITEM(newstrings, index++, Py_NewRef(PyTuple_GET_ITEM(right, i)));
+    }
+
+    return newstrings;
+}
+
+static PyObject *
+template_concat_templates(templateobject *self, templateobject *other)
+{
+    PyObject *newstrings = template_strings_concat(self->strings, other->strings);
+    if (newstrings == NULL) {
+        return NULL;
+    }
+
+    PyObject *newinterpolations = PySequence_Concat(self->interpolations, other->interpolations);
+    if (newinterpolations == NULL) {
+        Py_DECREF(newstrings);
+        return NULL;
+    }
+
+    PyObject *newtemplate = _PyTemplate_Build(newstrings, newinterpolations);
+    Py_DECREF(newstrings);
+    Py_DECREF(newinterpolations);
+    return newtemplate;
+}
+
+static PyObject *
+template_concat_template_str(templateobject *self, PyObject *other)
+{
+    PyObject *newstrings = template_strings_append_str(self->strings, other);
+    if (newstrings == NULL) {
+        return NULL;
+    }
+
+    PyObject *newtemplate = _PyTemplate_Build(newstrings, self->interpolations);
+    Py_DECREF(newstrings);
+    return newtemplate;
+}
+
+static PyObject *
+template_concat_str_template(templateobject *self, PyObject *other)
+{
+    PyObject *newstrings = template_strings_prepend_str(self->strings, other);
+    if (newstrings == NULL) {
+        return NULL;
+    }
+
+    PyObject *newtemplate = _PyTemplate_Build(newstrings, self->interpolations);
+    Py_DECREF(newstrings);
+    return newtemplate;
+}
+
+PyObject *
+_PyTemplate_Concat(PyObject *self, PyObject *other)
+{
+    if (_PyTemplate_CheckExact(self) && _PyTemplate_CheckExact(other)) {
+        return template_concat_templates((templateobject *) self, (templateobject *) other);
+    }
+    else if ((_PyTemplate_CheckExact(self)) && PyUnicode_Check(other)) {
+        return template_concat_template_str((templateobject *) self, other);
+    }
+    else if (PyUnicode_Check(self) && (_PyTemplate_CheckExact(other))) {
+        return template_concat_str_template((templateobject *) other, self);
+    }
+    else {
+        Py_RETURN_NOTIMPLEMENTED;
+    }
+}
+
+static PyObject *
+template_values_get(PyObject *op, void *Py_UNUSED(data))
+{
+    templateobject *self = templateobject_CAST(op);
+
+    Py_ssize_t len = PyTuple_GET_SIZE(self->interpolations);
+    PyObject *values = PyTuple_New(PyTuple_GET_SIZE(self->interpolations));
+    if (values == NULL) {
+        return NULL;
+    }
+
+    for (Py_ssize_t i = 0; i < len; i++) {
+        PyObject *item = PyTuple_GET_ITEM(self->interpolations, i);
+        PyTuple_SET_ITEM(values, i, _PyInterpolation_GetValueRef(item));
+    }
+
+    return values;
+}
+
+static PyMemberDef template_members[] = {
+    {"strings", Py_T_OBJECT_EX, offsetof(templateobject, strings), Py_READONLY, "Strings"},
+    {"interpolations", Py_T_OBJECT_EX, offsetof(templateobject, interpolations), Py_READONLY, "Interpolations"},
+    {NULL},
+};
+
+static PyGetSetDef template_getset[] = {
+    {"values", template_values_get, NULL,
+     PyDoc_STR("Values of interpolations"), NULL},
+    {NULL},
+};
+
+static PySequenceMethods template_as_sequence = {
+    .sq_concat = _PyTemplate_Concat,
+};
+
+static PyObject*
+template_reduce(PyObject *op, PyObject *Py_UNUSED(dummy))
+{
+    PyObject *mod = PyImport_ImportModule("string.templatelib");
+    if (mod == NULL) {
+        return NULL;
+    }
+    PyObject *func = PyObject_GetAttrString(mod, "_template_unpickle");
+    Py_DECREF(mod);
+    if (func == NULL) {
+        return NULL;
+    }
+
+    templateobject *self = templateobject_CAST(op);
+    PyObject *result = Py_BuildValue("O(OO)",
+                                     func,
+                                     self->strings,
+                                     self->interpolations);
+
+    Py_DECREF(func);
+    return result;
+}
+
+static PyMethodDef template_methods[] = {
+    {"__reduce__", template_reduce, METH_NOARGS, NULL},
+    {NULL, NULL},
+};
+
+PyTypeObject _PyTemplate_Type = {
+    PyVarObject_HEAD_INIT(NULL, 0)
+    .tp_name = "string.templatelib.Template",
+    .tp_doc = PyDoc_STR("Template object"),
+    .tp_basicsize = sizeof(templateobject),
+    .tp_itemsize = 0,
+    .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
+    .tp_as_sequence = &template_as_sequence,
+    .tp_new = template_new,
+    .tp_alloc = PyType_GenericAlloc,
+    .tp_dealloc = template_dealloc,
+    .tp_clear = template_clear,
+    .tp_free = PyObject_GC_Del,
+    .tp_repr = template_repr,
+    .tp_members = template_members,
+    .tp_methods = template_methods,
+    .tp_getset = template_getset,
+    .tp_iter = template_iter,
+    .tp_traverse = template_traverse,
+};
+
+PyObject *
+_PyTemplate_Build(PyObject *strings, PyObject *interpolations)
+{
+    templateobject *template = PyObject_GC_New(templateobject, &_PyTemplate_Type);
+    if (template == NULL) {
+        return NULL;
+    }
+
+    template->strings = Py_NewRef(strings);
+    template->interpolations = Py_NewRef(interpolations);
+    PyObject_GC_Track(template);
+    return (PyObject *) template;
+}
index b3287546dd92f0806540c915e186783694cf6ff1..462323a828ef4923e7c82ef7c349d904e6903724 100644 (file)
@@ -56,6 +56,7 @@ OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 #include "pycore_pyhash.h"        // _Py_HashSecret_t
 #include "pycore_pylifecycle.h"   // _Py_SetFileSystemEncoding()
 #include "pycore_pystate.h"       // _PyInterpreterState_GET()
+#include "pycore_template.h"      // _PyTemplate_Concat()
 #include "pycore_tuple.h"         // _PyTuple_FromArray()
 #include "pycore_ucnhash.h"       // _PyUnicode_Name_CAPI
 #include "pycore_unicodeobject.h" // struct _Py_unicode_state
@@ -11628,10 +11629,16 @@ PyUnicode_Concat(PyObject *left, PyObject *right)
         return NULL;
 
     if (!PyUnicode_Check(right)) {
-        PyErr_Format(PyExc_TypeError,
-                     "can only concatenate str (not \"%.200s\") to str",
-                     Py_TYPE(right)->tp_name);
-        return NULL;
+        if (_PyTemplate_CheckExact(right)) {
+            // str + tstring is implemented in the tstring type
+            return _PyTemplate_Concat(left, right);
+        }
+        else {
+            PyErr_Format(PyExc_TypeError,
+                "can only concatenate str (not \"%.200s\") to str",
+                Py_TYPE(right)->tp_name);
+            return NULL;
+        }
     }
 
     /* Shortcuts */
index 59d664dc1745c1c29c3df029d203d21a21004c9f..1f3f3170f3fa487381dbf8b0e1042ec2f27e45b3 100644 (file)
     <ClCompile Include="..\Objects\funcobject.c" />
     <ClCompile Include="..\Objects\genericaliasobject.c" />
     <ClCompile Include="..\Objects\genobject.c" />
+    <ClCompile Include="..\Objects\interpolationobject.c" />
     <ClCompile Include="..\Objects\iterobject.c" />
     <ClCompile Include="..\Objects\listobject.c" />
     <ClCompile Include="..\Objects\longobject.c" />
     <ClCompile Include="..\Objects\setobject.c" />
     <ClCompile Include="..\Objects\sliceobject.c" />
     <ClCompile Include="..\Objects\structseq.c" />
+    <ClCompile Include="..\Objects\templateobject.c" />
     <ClCompile Include="..\Objects\tupleobject.c" />
     <ClCompile Include="..\Objects\typeobject.c" />
     <ClCompile Include="..\Objects\typevarobject.c" />
index ce9b056ac2561c384be33e86b5dc9b80a91fb306..d2aafce058d05edf912f128b891e85dfbeaaa401 100644 (file)
     <ClInclude Include="..\Include\internal\pycore_interp_structs.h" />
     <ClInclude Include="..\Include\internal\pycore_interpframe.h" />
     <ClInclude Include="..\Include\internal\pycore_interpframe_structs.h" />
+    <ClInclude Include="..\Include\internal\pycore_interpolation.h" />
     <ClInclude Include="..\Include\internal\pycore_intrinsics.h" />
     <ClInclude Include="..\Include\internal\pycore_jit.h" />
     <ClInclude Include="..\Include\internal\pycore_list.h" />
     <ClInclude Include="..\Include\internal\pycore_structseq.h" />
     <ClInclude Include="..\Include\internal\pycore_sysmodule.h" />
     <ClInclude Include="..\Include\internal\pycore_symtable.h" />
+    <ClInclude Include="..\Include\internal\pycore_template.h" />
     <ClInclude Include="..\Include\internal\pycore_time.h" />
     <ClInclude Include="..\Include\internal\pycore_token.h" />
     <ClInclude Include="..\Include\internal\pycore_traceback.h" />
     <ClCompile Include="..\Objects\funcobject.c" />
     <ClCompile Include="..\Objects\genericaliasobject.c" />
     <ClCompile Include="..\Objects\genobject.c" />
+    <ClCompile Include="..\Objects\interpolationobject.c" />
     <ClCompile Include="..\Objects\iterobject.c" />
     <ClCompile Include="..\Objects\listobject.c" />
     <ClCompile Include="..\Objects\longobject.c" />
     <ClCompile Include="..\Objects\setobject.c" />
     <ClCompile Include="..\Objects\sliceobject.c" />
     <ClCompile Include="..\Objects\structseq.c" />
+    <ClCompile Include="..\Objects\templateobject.c" />
     <ClCompile Include="..\Objects\tupleobject.c" />
     <ClCompile Include="..\Objects\typeobject.c" />
     <ClCompile Include="..\Objects\typevarobject.c" />
index 2c2eac85941f70fa1afb563b4d0cc59e4df76f84..873f7d65a26a877203a40ba6fefd599400fc5c30 100644 (file)
     <ClInclude Include="..\Include\internal\pycore_interpframe_structs.h">
       <Filter>Include\internal</Filter>
     </ClInclude>
+    <ClInclude Include="..\Include\internal\pycore_interpolation.h">
+      <Filter>Include\internal</Filter>
+    </ClInclude>
     <ClInclude Include="..\Include\internal\pycore_intrinsics.h">
       <Filter>Include\cpython</Filter>
     </ClInclude>
     <ClInclude Include="..\Include\internal\pycore_stackref.h">
       <Filter>Include\internal</Filter>
     </ClInclude>
+    <ClInclude Include="..\Include\internal\pycore_template.h">
+      <Filter>Include\internal</Filter>
+    </ClInclude>
     <ClInclude Include="..\Include\internal\pycore_time.h">
       <Filter>Include\internal</Filter>
     </ClInclude>
index 80776ffe4493930d3221a7a24e7eeaa92c0f947b..db61d8b1f66467a98fc1d002308d92efc38fe30f 100644 (file)
@@ -78,7 +78,9 @@ module Python
          | Compare(expr left, cmpop* ops, expr* comparators)
          | Call(expr func, expr* args, keyword* keywords)
          | FormattedValue(expr value, int conversion, expr? format_spec)
+         | Interpolation(expr value, constant str, int conversion, expr? format_spec)
          | JoinedStr(expr* values)
+         | TemplateStr(expr* values)
          | Constant(constant value, string? kind)
 
          -- the following expression can appear in assignment context
index 6a825b1abd350494bdb8f840e5cd6a78cf1c8e96..3bcc0870882a29aafbb3b14697031d96f12b43d8 100644 (file)
@@ -965,9 +965,21 @@ _PyPegen_check_fstring_conversion(Parser *p, Token* conv_token, expr_ty conv)
     if (conv_token->lineno != conv->lineno || conv_token->end_col_offset != conv->col_offset) {
         return RAISE_SYNTAX_ERROR_KNOWN_RANGE(
             conv_token, conv,
-            "f-string: conversion type must come right after the exclamanation mark"
+            "%c-string: conversion type must come right after the exclamanation mark",
+            TOK_GET_STRING_PREFIX(p->tok)
         );
     }
+
+    Py_UCS4 first = PyUnicode_READ_CHAR(conv->v.Name.id, 0);
+    if (PyUnicode_GET_LENGTH(conv->v.Name.id) > 1 ||
+            !(first == 's' || first == 'r' || first == 'a')) {
+        RAISE_SYNTAX_ERROR_KNOWN_LOCATION(conv,
+                                            "%c-string: invalid conversion character %R: expected 's', 'r', or 'a'",
+                                            TOK_GET_STRING_PREFIX(p->tok),
+                                            conv->v.Name.id);
+        return NULL;
+    }
+
     return result_token_with_metadata(p, conv, conv_token->metadata);
 }
 
@@ -1070,6 +1082,9 @@ _PyPegen_get_expr_name(expr_ty e)
         case JoinedStr_kind:
         case FormattedValue_kind:
             return "f-string expression";
+        case TemplateStr_kind:
+        case Interpolation_kind:
+            return "t-string expression";
         case Constant_kind: {
             PyObject *value = e->v.Constant.value;
             if (value == Py_None) {
@@ -1279,20 +1294,13 @@ _PyPegen_decode_fstring_part(Parser* p, int is_raw, expr_ty constant, Token* tok
                            p->arena);
 }
 
-expr_ty
-_PyPegen_joined_str(Parser *p, Token* a, asdl_expr_seq* expr, Token*b) {
-
-    /* The parser might put multiple f-string values into an individual
-     * JoinedStr node at the top level due to stuff like f-string debugging
-     * expressions. This function flattens those and promotes them to the
-     * upper level. Only simplifies AST, but the compiler already takes care
-     * of the regular output, so this is not necessary if you are not going
-     * to expose the output AST to Python level. */
-
-    Py_ssize_t n_items = asdl_seq_LEN(expr);
+static asdl_expr_seq *
+_get_resized_exprs(Parser *p, Token *a, asdl_expr_seq *raw_expressions, Token *b, enum string_kind_t string_kind)
+{
+    Py_ssize_t n_items = asdl_seq_LEN(raw_expressions);
     Py_ssize_t total_items = n_items;
     for (Py_ssize_t i = 0; i < n_items; i++) {
-        expr_ty item = asdl_seq_GET(expr, i);
+        expr_ty item = asdl_seq_GET(raw_expressions, i);
         if (item->kind == JoinedStr_kind) {
             total_items += asdl_seq_LEN(item->v.JoinedStr.values) - 1;
         }
@@ -1311,17 +1319,19 @@ _PyPegen_joined_str(Parser *p, Token* a, asdl_expr_seq* expr, Token*b) {
 
     Py_ssize_t index = 0;
     for (Py_ssize_t i = 0; i < n_items; i++) {
-        expr_ty item = asdl_seq_GET(expr, i);
+        expr_ty item = asdl_seq_GET(raw_expressions, i);
 
         // This should correspond to a JoinedStr node of two elements
         // created _PyPegen_formatted_value. This situation can only be the result of
-        // a f-string debug expression where the first element is a constant with the text and the second
+        // a (f|t)-string debug expression where the first element is a constant with the text and the second
         // a formatted value with the expression.
         if (item->kind == JoinedStr_kind) {
             asdl_expr_seq *values = item->v.JoinedStr.values;
             if (asdl_seq_LEN(values) != 2) {
                 PyErr_Format(PyExc_SystemError,
-                             "unexpected JoinedStr node without debug data in f-string at line %d",
+                             string_kind == TSTRING
+                             ? "unexpected TemplateStr node without debug data in t-string at line %d"
+                             : "unexpected JoinedStr node without debug data in f-string at line %d",
                              item->lineno);
                 return NULL;
             }
@@ -1331,7 +1341,7 @@ _PyPegen_joined_str(Parser *p, Token* a, asdl_expr_seq* expr, Token*b) {
             asdl_seq_SET(seq, index++, first);
 
             expr_ty second = asdl_seq_GET(values, 1);
-            assert(second->kind == FormattedValue_kind);
+            assert((string_kind == TSTRING && second->kind == Interpolation_kind) || second->kind == FormattedValue_kind);
             asdl_seq_SET(seq, index++, second);
 
             continue;
@@ -1367,7 +1377,22 @@ _PyPegen_joined_str(Parser *p, Token* a, asdl_expr_seq* expr, Token*b) {
     else {
         resized_exprs = seq;
     }
+    return resized_exprs;
+}
 
+expr_ty
+_PyPegen_template_str(Parser *p, Token *a, asdl_expr_seq *raw_expressions, Token *b) {
+
+    asdl_expr_seq *resized_exprs = _get_resized_exprs(p, a, raw_expressions, b, TSTRING);
+    return _PyAST_TemplateStr(resized_exprs, a->lineno, a->col_offset,
+                              b->end_lineno, b->end_col_offset,
+                              p->arena);
+}
+
+expr_ty
+_PyPegen_joined_str(Parser *p, Token* a, asdl_expr_seq* raw_expressions, Token*b) {
+
+    asdl_expr_seq *resized_exprs = _get_resized_exprs(p, a, raw_expressions, b, FSTRING);
     return _PyAST_JoinedStr(resized_exprs, a->lineno, a->col_offset,
                             b->end_lineno, b->end_col_offset,
                             p->arena);
@@ -1434,138 +1459,239 @@ expr_ty _PyPegen_constant_from_string(Parser* p, Token* tok) {
     return _PyAST_Constant(s, kind, tok->lineno, tok->col_offset, tok->end_lineno, tok->end_col_offset, p->arena);
 }
 
-expr_ty _PyPegen_formatted_value(Parser *p, expr_ty expression, Token *debug, ResultTokenWithMetadata *conversion,
-                                 ResultTokenWithMetadata *format, Token *closing_brace, int lineno, int col_offset,
-                                 int end_lineno, int end_col_offset, PyArena *arena) {
-    int conversion_val = -1;
+static int
+_get_interpolation_conversion(Parser *p, Token *debug, ResultTokenWithMetadata *conversion,
+                              ResultTokenWithMetadata *format)
+{
     if (conversion != NULL) {
         expr_ty conversion_expr = (expr_ty) conversion->result;
         assert(conversion_expr->kind == Name_kind);
         Py_UCS4 first = PyUnicode_READ_CHAR(conversion_expr->v.Name.id, 0);
+        return Py_SAFE_DOWNCAST(first, Py_UCS4, int);
+    }
+    else if (debug && !format) {
+        /* If no conversion is specified, use !r for debug expressions */
+        return (int)'r';
+    }
+    return -1;
+}
 
-        if (PyUnicode_GET_LENGTH(conversion_expr->v.Name.id) > 1 ||
-            !(first == 's' || first == 'r' || first == 'a')) {
-            RAISE_SYNTAX_ERROR_KNOWN_LOCATION(conversion_expr,
-                                              "f-string: invalid conversion character %R: expected 's', 'r', or 'a'",
-                                              conversion_expr->v.Name.id);
-            return NULL;
+static PyObject *
+_strip_interpolation_expr(PyObject *exprstr)
+{
+    Py_ssize_t len = PyUnicode_GET_LENGTH(exprstr);
+
+    for (Py_ssize_t i = len - 1; i >= 0; i--) {
+        Py_UCS4 c = PyUnicode_READ_CHAR(exprstr, i);
+        if (_PyUnicode_IsWhitespace(c) || c == '=') {
+            len--;
+        }
+        else {
+            break;
         }
+    }
+
+    return PyUnicode_Substring(exprstr, 0, len);
+}
+
+expr_ty _PyPegen_interpolation(Parser *p, expr_ty expression, Token *debug, ResultTokenWithMetadata *conversion,
+                                 ResultTokenWithMetadata *format, Token *closing_brace, int lineno, int col_offset,
+                                 int end_lineno, int end_col_offset, PyArena *arena) {
 
-        conversion_val = Py_SAFE_DOWNCAST(first, Py_UCS4, int);
+    int conversion_val = _get_interpolation_conversion(p, debug, conversion, format);
+
+    /* Find the non whitespace token after the "=" */
+    int debug_end_line, debug_end_offset;
+    PyObject *debug_metadata;
+    constant exprstr;
+
+    if (conversion) {
+        debug_end_line = ((expr_ty) conversion->result)->lineno;
+        debug_end_offset = ((expr_ty) conversion->result)->col_offset;
+        debug_metadata = exprstr = conversion->metadata;
     }
-    else if (debug && !format) {
-        /* If no conversion is specified, use !r for debug expressions */
-        conversion_val = (int)'r';
+    else if (format) {
+        debug_end_line = ((expr_ty) format->result)->lineno;
+        debug_end_offset = ((expr_ty) format->result)->col_offset + 1;
+        debug_metadata = exprstr = format->metadata;
+    }
+    else {
+        debug_end_line = end_lineno;
+        debug_end_offset = end_col_offset;
+        debug_metadata = exprstr = closing_brace->metadata;
+    }
+
+    assert(exprstr != NULL);
+    PyObject *final_exprstr = _strip_interpolation_expr(exprstr);
+    if (!final_exprstr || _PyArena_AddPyObject(arena, final_exprstr) < 0) {
+        Py_XDECREF(final_exprstr);
+        return NULL;
+    }
+
+    expr_ty interpolation = _PyAST_Interpolation(
+        expression, final_exprstr, conversion_val, format ? (expr_ty) format->result : NULL,
+        lineno, col_offset, end_lineno,
+        end_col_offset, arena
+    );
+
+    if (!debug) {
+        return interpolation;
     }
 
+    expr_ty debug_text = _PyAST_Constant(debug_metadata, NULL, lineno, col_offset + 1, debug_end_line,
+                                            debug_end_offset - 1, p->arena);
+    if (!debug_text) {
+        return NULL;
+    }
+
+    asdl_expr_seq *values = _Py_asdl_expr_seq_new(2, arena);
+    asdl_seq_SET(values, 0, debug_text);
+    asdl_seq_SET(values, 1, interpolation);
+    return _PyAST_JoinedStr(values, lineno, col_offset, debug_end_line, debug_end_offset, p->arena);
+}
+
+expr_ty _PyPegen_formatted_value(Parser *p, expr_ty expression, Token *debug, ResultTokenWithMetadata *conversion,
+                                 ResultTokenWithMetadata *format, Token *closing_brace, int lineno, int col_offset,
+                                 int end_lineno, int end_col_offset, PyArena *arena) {
+    int conversion_val = _get_interpolation_conversion(p, debug, conversion, format);
+
     expr_ty formatted_value = _PyAST_FormattedValue(
         expression, conversion_val, format ? (expr_ty) format->result : NULL,
         lineno, col_offset, end_lineno,
         end_col_offset, arena
     );
 
-    if (debug) {
-        /* Find the non whitespace token after the "=" */
-        int debug_end_line, debug_end_offset;
-        PyObject *debug_metadata;
+    if (!debug) {
+        return formatted_value;
+    }
 
-        if (conversion) {
-            debug_end_line = ((expr_ty) conversion->result)->lineno;
-            debug_end_offset = ((expr_ty) conversion->result)->col_offset;
-            debug_metadata = conversion->metadata;
-        }
-        else if (format) {
-            debug_end_line = ((expr_ty) format->result)->lineno;
-            debug_end_offset = ((expr_ty) format->result)->col_offset + 1;
-            debug_metadata = format->metadata;
-        }
-        else {
-            debug_end_line = end_lineno;
-            debug_end_offset = end_col_offset;
-            debug_metadata = closing_brace->metadata;
-        }
-        expr_ty debug_text = _PyAST_Constant(debug_metadata, NULL, lineno, col_offset + 1, debug_end_line,
-                                             debug_end_offset - 1, p->arena);
-        if (!debug_text) {
-            return NULL;
-        }
+    /* Find the non whitespace token after the "=" */
+    int debug_end_line, debug_end_offset;
+    PyObject *debug_metadata;
 
-        asdl_expr_seq *values = _Py_asdl_expr_seq_new(2, arena);
-        if (values == NULL) {
-            return NULL;
-        }
-        asdl_seq_SET(values, 0, debug_text);
-        asdl_seq_SET(values, 1, formatted_value);
-        return _PyAST_JoinedStr(values, lineno, col_offset, debug_end_line, debug_end_offset, p->arena);
+    if (conversion) {
+        debug_end_line = ((expr_ty) conversion->result)->lineno;
+        debug_end_offset = ((expr_ty) conversion->result)->col_offset;
+        debug_metadata = conversion->metadata;
+    }
+    else if (format) {
+        debug_end_line = ((expr_ty) format->result)->lineno;
+        debug_end_offset = ((expr_ty) format->result)->col_offset + 1;
+        debug_metadata = format->metadata;
     }
     else {
-        return formatted_value;
+        debug_end_line = end_lineno;
+        debug_end_offset = end_col_offset;
+        debug_metadata = closing_brace->metadata;
+    }
+    expr_ty debug_text = _PyAST_Constant(debug_metadata, NULL, lineno, col_offset + 1, debug_end_line,
+                                            debug_end_offset - 1, p->arena);
+    if (!debug_text) {
+        return NULL;
     }
+
+    asdl_expr_seq *values = _Py_asdl_expr_seq_new(2, arena);
+    asdl_seq_SET(values, 0, debug_text);
+    asdl_seq_SET(values, 1, formatted_value);
+    return _PyAST_JoinedStr(values, lineno, col_offset, debug_end_line, debug_end_offset, p->arena);
 }
 
-expr_ty
-_PyPegen_concatenate_strings(Parser *p, asdl_expr_seq *strings,
-                             int lineno, int col_offset, int end_lineno,
-                             int end_col_offset, PyArena *arena)
+static expr_ty
+_build_concatenated_bytes(Parser *p, asdl_expr_seq *strings, int lineno,
+                        int col_offset, int end_lineno, int end_col_offset,
+                        PyArena *arena)
 {
     Py_ssize_t len = asdl_seq_LEN(strings);
     assert(len > 0);
 
-    int f_string_found = 0;
-    int unicode_string_found = 0;
-    int bytes_found = 0;
+    PyObject* res = Py_GetConstant(Py_CONSTANT_EMPTY_BYTES);
 
-    Py_ssize_t i = 0;
-    Py_ssize_t n_flattened_elements = 0;
-    for (i = 0; i < len; i++) {
+    /* Bytes literals never get a kind, but just for consistency
+        since they are represented as Constant nodes, we'll mirror
+        the same behavior as unicode strings for determining the
+        kind. */
+    PyObject* kind = asdl_seq_GET(strings, 0)->v.Constant.kind;
+    for (Py_ssize_t i = 0; i < len; i++) {
         expr_ty elem = asdl_seq_GET(strings, i);
-        switch(elem->kind) {
-            case Constant_kind:
-                if (PyBytes_CheckExact(elem->v.Constant.value)) {
-                    bytes_found = 1;
-                } else {
-                    unicode_string_found = 1;
-                }
-                n_flattened_elements++;
-                break;
-            case JoinedStr_kind:
-                n_flattened_elements += asdl_seq_LEN(elem->v.JoinedStr.values);
-                f_string_found = 1;
-                break;
-            default:
-                n_flattened_elements++;
-                f_string_found = 1;
-                break;
-        }
+        PyBytes_Concat(&res, elem->v.Constant.value);
     }
+    if (!res || _PyArena_AddPyObject(arena, res) < 0) {
+        Py_XDECREF(res);
+        return NULL;
+    }
+    return _PyAST_Constant(res, kind, lineno, col_offset, end_lineno, end_col_offset, p->arena);
+}
 
-    if ((unicode_string_found || f_string_found) && bytes_found) {
-        RAISE_SYNTAX_ERROR("cannot mix bytes and nonbytes literals");
+static expr_ty
+_build_concatenated_unicode(Parser *p, asdl_expr_seq *strings, int lineno,
+                        int col_offset, int end_lineno, int end_col_offset,
+                        PyArena *arena)
+{
+    Py_ssize_t len = asdl_seq_LEN(strings);
+    assert(len > 1);
+
+    expr_ty first = asdl_seq_GET(strings, 0);
+
+    /* When a string is getting concatenated, the kind of the string
+        is determined by the first string in the concatenation
+        sequence.
+
+        u"abc" "def" -> u"abcdef"
+        "abc" u"abc" ->  "abcabc" */
+    PyObject *kind = first->v.Constant.kind;
+
+    PyUnicodeWriter *writer = PyUnicodeWriter_Create(0);
+    if (writer == NULL) {
         return NULL;
     }
 
-    if (bytes_found) {
-        PyObject* res = Py_GetConstant(Py_CONSTANT_EMPTY_BYTES);
+    for (Py_ssize_t i = 0; i < len; i++) {
+        expr_ty current_elem = asdl_seq_GET(strings, i);
+        assert(current_elem->kind == Constant_kind);
 
-        /* Bytes literals never get a kind, but just for consistency
-           since they are represented as Constant nodes, we'll mirror
-           the same behavior as unicode strings for determining the
-           kind. */
-        PyObject* kind = asdl_seq_GET(strings, 0)->v.Constant.kind;
-        for (i = 0; i < len; i++) {
-            expr_ty elem = asdl_seq_GET(strings, i);
-            PyBytes_Concat(&res, elem->v.Constant.value);
-        }
-        if (!res || _PyArena_AddPyObject(arena, res) < 0) {
-            Py_XDECREF(res);
+        if (PyUnicodeWriter_WriteStr(writer,
+                                     current_elem->v.Constant.value)) {
+            PyUnicodeWriter_Discard(writer);
             return NULL;
         }
-        return _PyAST_Constant(res, kind, lineno, col_offset, end_lineno, end_col_offset, p->arena);
     }
 
-    if (!f_string_found && len == 1) {
-        return asdl_seq_GET(strings, 0);
+    PyObject *final = PyUnicodeWriter_Finish(writer);
+    if (final == NULL) {
+        return NULL;
     }
+    if (_PyArena_AddPyObject(p->arena, final) < 0) {
+        Py_DECREF(final);
+        return NULL;
+    }
+    return _PyAST_Constant(final, kind, lineno, col_offset,
+                           end_lineno, end_col_offset, arena);
+}
+
+static asdl_expr_seq *
+_build_concatenated_str(Parser *p, asdl_expr_seq *strings,
+                               int lineno, int col_offset, int end_lineno,
+                               int end_col_offset, PyArena *arena)
+{
+    Py_ssize_t len = asdl_seq_LEN(strings);
+    assert(len > 0);
+
+    Py_ssize_t n_flattened_elements = 0;
+    for (Py_ssize_t i = 0; i < len; i++) {
+        expr_ty elem = asdl_seq_GET(strings, i);
+        switch(elem->kind) {
+            case JoinedStr_kind:
+                n_flattened_elements += asdl_seq_LEN(elem->v.JoinedStr.values);
+                break;
+            case TemplateStr_kind:
+                n_flattened_elements += asdl_seq_LEN(elem->v.TemplateStr.values);
+                break;
+            default:
+                n_flattened_elements++;
+                break;
+        }
+    }
+
 
     asdl_expr_seq* flattened = _Py_asdl_expr_seq_new(n_flattened_elements, p->arena);
     if (flattened == NULL) {
@@ -1574,12 +1700,11 @@ _PyPegen_concatenate_strings(Parser *p, asdl_expr_seq *strings,
 
     /* build flattened list */
     Py_ssize_t current_pos = 0;
-    Py_ssize_t j = 0;
-    for (i = 0; i < len; i++) {
+    for (Py_ssize_t i = 0; i < len; i++) {
         expr_ty elem = asdl_seq_GET(strings, i);
         switch(elem->kind) {
             case JoinedStr_kind:
-                for (j = 0; j < asdl_seq_LEN(elem->v.JoinedStr.values); j++) {
+                for (Py_ssize_t j = 0; j < asdl_seq_LEN(elem->v.JoinedStr.values); j++) {
                     expr_ty subvalue = asdl_seq_GET(elem->v.JoinedStr.values, j);
                     if (subvalue == NULL) {
                         return NULL;
@@ -1587,6 +1712,15 @@ _PyPegen_concatenate_strings(Parser *p, asdl_expr_seq *strings,
                     asdl_seq_SET(flattened, current_pos++, subvalue);
                 }
                 break;
+            case TemplateStr_kind:
+                for (Py_ssize_t j = 0; j < asdl_seq_LEN(elem->v.TemplateStr.values); j++) {
+                    expr_ty subvalue = asdl_seq_GET(elem->v.TemplateStr.values, j);
+                    if (subvalue == NULL) {
+                        return NULL;
+                    }
+                    asdl_seq_SET(flattened, current_pos++, subvalue);
+                }
+                break;
             default:
                 asdl_seq_SET(flattened, current_pos++, elem);
                 break;
@@ -1596,13 +1730,13 @@ _PyPegen_concatenate_strings(Parser *p, asdl_expr_seq *strings,
     /* calculate folded element count */
     Py_ssize_t n_elements = 0;
     int prev_is_constant = 0;
-    for (i = 0; i < n_flattened_elements; i++) {
+    for (Py_ssize_t i = 0; i < n_flattened_elements; i++) {
         expr_ty elem = asdl_seq_GET(flattened, i);
 
         /* The concatenation of a FormattedValue and an empty Constant should
            lead to the FormattedValue itself. Thus, we will not take any empty
            constants into account, just as in `_PyPegen_joined_str` */
-        if (f_string_found && elem->kind == Constant_kind &&
+        if (elem->kind == Constant_kind &&
             PyUnicode_CheckExact(elem->v.Constant.value) &&
             PyUnicode_GET_LENGTH(elem->v.Constant.value) == 0)
             continue;
@@ -1620,7 +1754,7 @@ _PyPegen_concatenate_strings(Parser *p, asdl_expr_seq *strings,
 
     /* build folded list */
     current_pos = 0;
-    for (i = 0; i < n_flattened_elements; i++) {
+    for (Py_ssize_t i = 0; i < n_flattened_elements; i++) {
         expr_ty elem = asdl_seq_GET(flattened, i);
 
         /* if the current elem and the following are constants,
@@ -1643,6 +1777,7 @@ _PyPegen_concatenate_strings(Parser *p, asdl_expr_seq *strings,
                     return NULL;
                 }
                 expr_ty last_elem = elem;
+                Py_ssize_t j;
                 for (j = i; j < n_flattened_elements; j++) {
                     expr_ty current_elem = asdl_seq_GET(flattened, j);
                     if (current_elem->kind == Constant_kind) {
@@ -1676,8 +1811,7 @@ _PyPegen_concatenate_strings(Parser *p, asdl_expr_seq *strings,
             }
 
             /* Drop all empty contanst strings */
-            if (f_string_found &&
-                PyUnicode_CheckExact(elem->v.Constant.value) &&
+            if (PyUnicode_CheckExact(elem->v.Constant.value) &&
                 PyUnicode_GET_LENGTH(elem->v.Constant.value) == 0) {
                 continue;
             }
@@ -1686,17 +1820,97 @@ _PyPegen_concatenate_strings(Parser *p, asdl_expr_seq *strings,
         asdl_seq_SET(values, current_pos++, elem);
     }
 
-    if (!f_string_found) {
-        assert(n_elements == 1);
-        expr_ty elem = asdl_seq_GET(values, 0);
-        assert(elem->kind == Constant_kind);
-        return elem;
-    }
-
     assert(current_pos == n_elements);
+    return values;
+}
+
+static expr_ty
+_build_concatenated_joined_str(Parser *p, asdl_expr_seq *strings,
+                               int lineno, int col_offset, int end_lineno,
+                               int end_col_offset, PyArena *arena)
+{
+    asdl_expr_seq *values = _build_concatenated_str(p, strings, lineno,
+        col_offset, end_lineno, end_col_offset, arena);
     return _PyAST_JoinedStr(values, lineno, col_offset, end_lineno, end_col_offset, p->arena);
 }
 
+static expr_ty
+_build_concatenated_template_str(Parser *p, asdl_expr_seq *strings,
+                               int lineno, int col_offset, int end_lineno,
+                               int end_col_offset, PyArena *arena)
+{
+    asdl_expr_seq *values = _build_concatenated_str(p, strings, lineno,
+        col_offset, end_lineno, end_col_offset, arena);
+    return _PyAST_TemplateStr(values, lineno, col_offset, end_lineno,
+        end_col_offset, arena);
+}
+
+expr_ty
+_PyPegen_concatenate_strings(Parser *p, asdl_expr_seq *strings,
+                             int lineno, int col_offset, int end_lineno,
+                             int end_col_offset, PyArena *arena)
+{
+    Py_ssize_t len = asdl_seq_LEN(strings);
+    assert(len > 0);
+
+    int t_string_found = 0;
+    int f_string_found = 0;
+    int unicode_string_found = 0;
+    int bytes_found = 0;
+
+    Py_ssize_t i = 0;
+    for (i = 0; i < len; i++) {
+        expr_ty elem = asdl_seq_GET(strings, i);
+        switch(elem->kind) {
+            case Constant_kind:
+                if (PyBytes_CheckExact(elem->v.Constant.value)) {
+                    bytes_found = 1;
+                } else {
+                    unicode_string_found = 1;
+                }
+                break;
+            case JoinedStr_kind:
+                f_string_found = 1;
+                break;
+            case TemplateStr_kind:
+                t_string_found = 1;
+                break;
+            default:
+                f_string_found = 1;
+                break;
+        }
+    }
+
+    // Cannot mix unicode and bytes
+    if ((unicode_string_found || f_string_found || t_string_found) && bytes_found) {
+        RAISE_SYNTAX_ERROR("cannot mix bytes and nonbytes literals");
+        return NULL;
+    }
+
+    // If it's only bytes or only unicode string, do a simple concat
+    if (!f_string_found && !t_string_found) {
+        if (len == 1) {
+            return asdl_seq_GET(strings, 0);
+        }
+        else if (bytes_found) {
+            return _build_concatenated_bytes(p, strings, lineno, col_offset,
+                end_lineno, end_col_offset, arena);
+        }
+        else {
+            return _build_concatenated_unicode(p, strings, lineno, col_offset,
+                end_lineno, end_col_offset, arena);
+        }
+    }
+
+    if (t_string_found) {
+        return _build_concatenated_template_str(p, strings, lineno,
+            col_offset, end_lineno, end_col_offset, arena);
+    }
+
+    return _build_concatenated_joined_str(p, strings, lineno,
+        col_offset, end_lineno, end_col_offset, arena);
+}
+
 stmt_ty
 _PyPegen_checked_future_import(Parser *p, identifier module, asdl_alias_seq * names, int level,
                                           int lineno, int col_offset, int end_lineno, int end_col_offset,
index f6502bf8f7f2d1acb6854c5f0e3ac60af6ea5132..63aa1ea2ad4f60492429608ea82888a2b5e58bd8 100644 (file)
@@ -13,8 +13,8 @@ _PyLexer_remember_fstring_buffers(struct tok_state *tok)
 
     for (index = tok->tok_mode_stack_index; index >= 0; --index) {
         mode = &(tok->tok_mode_stack[index]);
-        mode->f_string_start_offset = mode->f_string_start - tok->buf;
-        mode->f_string_multi_line_start_offset = mode->f_string_multi_line_start - tok->buf;
+        mode->start_offset = mode->start - tok->buf;
+        mode->multi_line_start_offset = mode->multi_line_start - tok->buf;
     }
 }
 
@@ -27,8 +27,8 @@ _PyLexer_restore_fstring_buffers(struct tok_state *tok)
 
     for (index = tok->tok_mode_stack_index; index >= 0; --index) {
         mode = &(tok->tok_mode_stack[index]);
-        mode->f_string_start = tok->buf + mode->f_string_start_offset;
-        mode->f_string_multi_line_start = tok->buf + mode->f_string_multi_line_start_offset;
+        mode->start = tok->buf + mode->start_offset;
+        mode->multi_line_start = tok->buf + mode->multi_line_start_offset;
     }
 }
 
index e81867a5fad0158f34c94f03bad32ad667a9d141..ca01cab9937a85ce26bde784b4f6df3385b1a00f 100644 (file)
@@ -38,6 +38,9 @@ static inline tokenizer_mode* TOK_NEXT_MODE(struct tok_state* tok) {
 #define TOK_NEXT_MODE(tok) (&(tok->tok_mode_stack[++tok->tok_mode_stack_index]))
 #endif
 
+#define FTSTRING_MIDDLE(tok_mode) (tok_mode->string_kind == TSTRING ? TSTRING_MIDDLE : FSTRING_MIDDLE)
+#define FTSTRING_END(tok_mode) (tok_mode->string_kind == TSTRING ? TSTRING_END : FSTRING_END)
+#define TOK_GET_STRING_PREFIX(tok) (TOK_GET_MODE(tok)->string_kind == TSTRING ? 't' : 'f')
 #define MAKE_TOKEN(token_type) _PyLexer_token_setup(tok, token, token_type, p_start, p_end)
 #define MAKE_TYPE_COMMENT_TOKEN(token_type, col_offset, end_col_offset) (\
                 _PyLexer_type_comment_token_setup(tok, token, token_type, col_offset, end_col_offset, p_start, p_end))
@@ -108,12 +111,12 @@ tok_backup(struct tok_state *tok, int c)
 }
 
 static int
-set_fstring_expr(struct tok_state* tok, struct token *token, char c) {
+set_ftstring_expr(struct tok_state* tok, struct token *token, char c) {
     assert(token != NULL);
     assert(c == '}' || c == ':' || c == '!');
     tokenizer_mode *tok_mode = TOK_GET_MODE(tok);
 
-    if (!tok_mode->f_string_debug || token->metadata) {
+    if (!(tok_mode->in_debug || tok_mode->string_kind == TSTRING) || token->metadata) {
         return 0;
     }
     PyObject *res = NULL;
@@ -173,7 +176,7 @@ set_fstring_expr(struct tok_state* tok, struct token *token, char c) {
 }
 
 int
-_PyLexer_update_fstring_expr(struct tok_state *tok, char cur)
+_PyLexer_update_ftstring_expr(struct tok_state *tok, char cur)
 {
     assert(tok->cur != NULL);
 
@@ -643,13 +646,13 @@ tok_get_normal_mode(struct tok_state *tok, tokenizer_mode* current_tok, struct t
     nonascii = 0;
     if (is_potential_identifier_start(c)) {
         /* Process the various legal combinations of b"", r"", u"", and f"". */
-        int saw_b = 0, saw_r = 0, saw_u = 0, saw_f = 0;
+        int saw_b = 0, saw_r = 0, saw_u = 0, saw_f = 0, saw_t = 0;
         while (1) {
-            if (!(saw_b || saw_u || saw_f) && (c == 'b' || c == 'B'))
+            if (!(saw_b || saw_u || saw_f || saw_t) && (c == 'b' || c == 'B'))
                 saw_b = 1;
             /* Since this is a backwards compatibility support literal we don't
                want to support it in arbitrary order like byte literals. */
-            else if (!(saw_b || saw_u || saw_r || saw_f)
+            else if (!(saw_b || saw_u || saw_r || saw_f || saw_t)
                      && (c == 'u'|| c == 'U')) {
                 saw_u = 1;
             }
@@ -657,15 +660,18 @@ tok_get_normal_mode(struct tok_state *tok, tokenizer_mode* current_tok, struct t
             else if (!(saw_r || saw_u) && (c == 'r' || c == 'R')) {
                 saw_r = 1;
             }
-            else if (!(saw_f || saw_b || saw_u) && (c == 'f' || c == 'F')) {
+            else if (!(saw_f || saw_b || saw_u || saw_t) && (c == 'f' || c == 'F')) {
                 saw_f = 1;
             }
+            else if (!(saw_t || saw_b || saw_u || saw_f) && (c == 't' || c == 'T')) {
+                saw_t = 1;
+            }
             else {
                 break;
             }
             c = tok_nextc(tok);
             if (c == '"' || c == '\'') {
-                if (saw_f) {
+                if (saw_f || saw_t) {
                     goto f_string_quote;
                 }
                 goto letter_quote;
@@ -939,7 +945,9 @@ tok_get_normal_mode(struct tok_state *tok, tokenizer_mode* current_tok, struct t
     }
 
   f_string_quote:
-    if (((Py_TOLOWER(*tok->start) == 'f' || Py_TOLOWER(*tok->start) == 'r') && (c == '\'' || c == '"'))) {
+    if (((Py_TOLOWER(*tok->start) == 'f' || Py_TOLOWER(*tok->start) == 'r' || Py_TOLOWER(*tok->start) == 't')
+        && (c == '\'' || c == '"'))) {
+
         int quote = c;
         int quote_size = 1;             /* 1 or 3 */
 
@@ -971,39 +979,49 @@ tok_get_normal_mode(struct tok_state *tok, tokenizer_mode* current_tok, struct t
         p_start = tok->start;
         p_end = tok->cur;
         if (tok->tok_mode_stack_index + 1 >= MAXFSTRINGLEVEL) {
-            return MAKE_TOKEN(_PyTokenizer_syntaxerror(tok, "too many nested f-strings"));
+            return MAKE_TOKEN(_PyTokenizer_syntaxerror(tok, "too many nested f-strings or t-strings"));
         }
         tokenizer_mode *the_current_tok = TOK_NEXT_MODE(tok);
         the_current_tok->kind = TOK_FSTRING_MODE;
-        the_current_tok->f_string_quote = quote;
-        the_current_tok->f_string_quote_size = quote_size;
-        the_current_tok->f_string_start = tok->start;
-        the_current_tok->f_string_multi_line_start = tok->line_start;
-        the_current_tok->f_string_line_start = tok->lineno;
-        the_current_tok->f_string_start_offset = -1;
-        the_current_tok->f_string_multi_line_start_offset = -1;
+        the_current_tok->quote = quote;
+        the_current_tok->quote_size = quote_size;
+        the_current_tok->start = tok->start;
+        the_current_tok->multi_line_start = tok->line_start;
+        the_current_tok->first_line = tok->lineno;
+        the_current_tok->start_offset = -1;
+        the_current_tok->multi_line_start_offset = -1;
         the_current_tok->last_expr_buffer = NULL;
         the_current_tok->last_expr_size = 0;
         the_current_tok->last_expr_end = -1;
         the_current_tok->in_format_spec = 0;
-        the_current_tok->f_string_debug = 0;
+        the_current_tok->in_debug = 0;
 
+        enum string_kind_t string_kind = FSTRING;
         switch (*tok->start) {
+            case 'T':
+            case 't':
+                the_current_tok->raw = Py_TOLOWER(*(tok->start + 1)) == 'r';
+                string_kind = TSTRING;
+                break;
             case 'F':
             case 'f':
-                the_current_tok->f_string_raw = Py_TOLOWER(*(tok->start + 1)) == 'r';
+                the_current_tok->raw = Py_TOLOWER(*(tok->start + 1)) == 'r';
                 break;
             case 'R':
             case 'r':
-                the_current_tok->f_string_raw = 1;
+                the_current_tok->raw = 1;
+                if (Py_TOLOWER(*(tok->start + 1)) == 't') {
+                    string_kind = TSTRING;
+                }
                 break;
             default:
                 Py_UNREACHABLE();
         }
 
+        the_current_tok->string_kind = string_kind;
         the_current_tok->curly_bracket_depth = 0;
         the_current_tok->curly_bracket_expr_start_depth = -1;
-        return MAKE_TOKEN(FSTRING_START);
+        return string_kind == TSTRING ? MAKE_TOKEN(TSTRING_START) : MAKE_TOKEN(FSTRING_START);
     }
 
   letter_quote:
@@ -1063,9 +1081,10 @@ tok_get_normal_mode(struct tok_state *tok, tokenizer_mode* current_tok, struct t
                      * and if it is, then this must be a missing '}' token
                      * so raise the proper error */
                     tokenizer_mode *the_current_tok = TOK_GET_MODE(tok);
-                    if (the_current_tok->f_string_quote == quote &&
-                        the_current_tok->f_string_quote_size == quote_size) {
-                        return MAKE_TOKEN(_PyTokenizer_syntaxerror(tok, "f-string: expecting '}'", start));
+                    if (the_current_tok->quote == quote &&
+                        the_current_tok->quote_size == quote_size) {
+                        return MAKE_TOKEN(_PyTokenizer_syntaxerror(tok,
+                            "%c-string: expecting '}'", TOK_GET_STRING_PREFIX(tok)));
                     }
                 }
 
@@ -1136,12 +1155,12 @@ tok_get_normal_mode(struct tok_state *tok, tokenizer_mode* current_tok, struct t
         int cursor = current_tok->curly_bracket_depth - (c != '{');
         int in_format_spec = current_tok->in_format_spec;
          int cursor_in_format_with_debug =
-             cursor == 1 && (current_tok->f_string_debug || in_format_spec);
+             cursor == 1 && (current_tok->in_debug || in_format_spec);
          int cursor_valid = cursor == 0 || cursor_in_format_with_debug;
-        if ((cursor_valid) && !_PyLexer_update_fstring_expr(tok, c)) {
+        if ((cursor_valid) && !_PyLexer_update_ftstring_expr(tok, c)) {
             return MAKE_TOKEN(ENDMARKER);
         }
-        if ((cursor_valid) && c != '{' && set_fstring_expr(tok, token, c)) {
+        if ((cursor_valid) && c != '{' && set_ftstring_expr(tok, token, c)) {
             return MAKE_TOKEN(ERRORTOKEN);
         }
 
@@ -1194,7 +1213,8 @@ tok_get_normal_mode(struct tok_state *tok, tokenizer_mode* current_tok, struct t
     case ']':
     case '}':
         if (INSIDE_FSTRING(tok) && !current_tok->curly_bracket_depth && c == '}') {
-            return MAKE_TOKEN(_PyTokenizer_syntaxerror(tok, "f-string: single '}' is not allowed"));
+            return MAKE_TOKEN(_PyTokenizer_syntaxerror(tok,
+                "%c-string: single '}' is not allowed", TOK_GET_STRING_PREFIX(tok)));
         }
         if (!tok->tok_extra_tokens && !tok->level) {
             return MAKE_TOKEN(_PyTokenizer_syntaxerror(tok, "unmatched '%c'", c));
@@ -1214,7 +1234,8 @@ tok_get_normal_mode(struct tok_state *tok, tokenizer_mode* current_tok, struct t
                     assert(current_tok->curly_bracket_depth >= 0);
                     int previous_bracket = current_tok->curly_bracket_depth - 1;
                     if (previous_bracket == current_tok->curly_bracket_expr_start_depth) {
-                        return MAKE_TOKEN(_PyTokenizer_syntaxerror(tok, "f-string: unmatched '%c'", c));
+                        return MAKE_TOKEN(_PyTokenizer_syntaxerror(tok,
+                            "%c-string: unmatched '%c'", TOK_GET_STRING_PREFIX(tok), c));
                     }
                 }
                 if (tok->parenlinenostack[tok->level] != tok->lineno) {
@@ -1235,13 +1256,14 @@ tok_get_normal_mode(struct tok_state *tok, tokenizer_mode* current_tok, struct t
         if (INSIDE_FSTRING(tok)) {
             current_tok->curly_bracket_depth--;
             if (current_tok->curly_bracket_depth < 0) {
-                return MAKE_TOKEN(_PyTokenizer_syntaxerror(tok, "f-string: unmatched '%c'", c));
+                return MAKE_TOKEN(_PyTokenizer_syntaxerror(tok, "%c-string: unmatched '%c'",
+                    TOK_GET_STRING_PREFIX(tok), c));
             }
             if (c == '}' && current_tok->curly_bracket_depth == current_tok->curly_bracket_expr_start_depth) {
                 current_tok->curly_bracket_expr_start_depth--;
                 current_tok->kind = TOK_FSTRING_MODE;
                 current_tok->in_format_spec = 0;
-                current_tok->f_string_debug = 0;
+                current_tok->in_debug = 0;
             }
         }
         break;
@@ -1254,7 +1276,7 @@ tok_get_normal_mode(struct tok_state *tok, tokenizer_mode* current_tok, struct t
     }
 
     if( c == '=' && INSIDE_FSTRING_EXPR(current_tok)) {
-        current_tok->f_string_debug = 1;
+        current_tok->in_debug = 1;
     }
 
     /* Punctuation character */
@@ -1285,7 +1307,8 @@ tok_get_fstring_mode(struct tok_state *tok, tokenizer_mode* current_tok, struct
         if (peek1 != '{') {
             current_tok->curly_bracket_expr_start_depth++;
             if (current_tok->curly_bracket_expr_start_depth >= MAX_EXPR_NESTING) {
-                return MAKE_TOKEN(_PyTokenizer_syntaxerror(tok, "f-string: expressions nested too deeply"));
+                return MAKE_TOKEN(_PyTokenizer_syntaxerror(tok,
+                    "%c-string: expressions nested too deeply", TOK_GET_STRING_PREFIX(tok)));
             }
             TOK_GET_MODE(tok)->kind = TOK_REGULAR_MODE;
             return tok_get_normal_mode(tok, current_tok, token);
@@ -1296,9 +1319,9 @@ tok_get_fstring_mode(struct tok_state *tok, tokenizer_mode* current_tok, struct
     }
 
     // Check if we are at the end of the string
-    for (int i = 0; i < current_tok->f_string_quote_size; i++) {
+    for (int i = 0; i < current_tok->quote_size; i++) {
         int quote = tok_nextc(tok);
-        if (quote != current_tok->f_string_quote) {
+        if (quote != current_tok->quote) {
             tok_backup(tok, quote);
             goto f_string_middle;
         }
@@ -1314,14 +1337,14 @@ tok_get_fstring_mode(struct tok_state *tok, tokenizer_mode* current_tok, struct
     p_start = tok->start;
     p_end = tok->cur;
     tok->tok_mode_stack_index--;
-    return MAKE_TOKEN(FSTRING_END);
+    return MAKE_TOKEN(FTSTRING_END(current_tok));
 
 f_string_middle:
 
     // TODO: This is a bit of a hack, but it works for now. We need to find a better way to handle
     // this.
     tok->multi_line_start = tok->line_start;
-    while (end_quote_size != current_tok->f_string_quote_size) {
+    while (end_quote_size != current_tok->quote_size) {
         int c = tok_nextc(tok);
         if (tok->done == E_ERROR || tok->done == E_DECODE) {
             return MAKE_TOKEN(ERRORTOKEN);
@@ -1332,7 +1355,7 @@ f_string_middle:
                 INSIDE_FSTRING_EXPR(current_tok)
         );
 
-       if (c == EOF || (current_tok->f_string_quote_size == 1 && c == '\n')) {
+       if (c == EOF || (current_tok->quote_size == 1 && c == '\n')) {
             if (tok->decoding_erred) {
                 return MAKE_TOKEN(ERRORTOKEN);
             }
@@ -1341,7 +1364,7 @@ f_string_middle:
             // it means that the format spec ends here and we should
             // return to the regular mode.
             if (in_format_spec && c == '\n') {
-                if (current_tok->f_string_quote_size == 1) {
+                if (current_tok->quote_size == 1) {
                     return MAKE_TOKEN(
                         _PyTokenizer_syntaxerror(
                             tok,
@@ -1354,25 +1377,26 @@ f_string_middle:
                 current_tok->in_format_spec = 0;
                 p_start = tok->start;
                 p_end = tok->cur;
-                return MAKE_TOKEN(FSTRING_MIDDLE);
+                return MAKE_TOKEN(FTSTRING_MIDDLE(current_tok));
             }
 
             assert(tok->multi_line_start != NULL);
             // shift the tok_state's location into
             // the start of string, and report the error
             // from the initial quote character
-            tok->cur = (char *)current_tok->f_string_start;
+            tok->cur = (char *)current_tok->start;
             tok->cur++;
-            tok->line_start = current_tok->f_string_multi_line_start;
+            tok->line_start = current_tok->multi_line_start;
             int start = tok->lineno;
 
             tokenizer_mode *the_current_tok = TOK_GET_MODE(tok);
-            tok->lineno = the_current_tok->f_string_line_start;
+            tok->lineno = the_current_tok->first_line;
 
-            if (current_tok->f_string_quote_size == 3) {
+            if (current_tok->quote_size == 3) {
                 _PyTokenizer_syntaxerror(tok,
-                                    "unterminated triple-quoted f-string literal"
-                                    " (detected at line %d)", start);
+                                    "unterminated triple-quoted %c-string literal"
+                                    " (detected at line %d)",
+                                    TOK_GET_STRING_PREFIX(tok), start);
                 if (c != '\n') {
                     tok->done = E_EOFS;
                 }
@@ -1380,12 +1404,12 @@ f_string_middle:
             }
             else {
                 return MAKE_TOKEN(_PyTokenizer_syntaxerror(tok,
-                                    "unterminated f-string literal (detected at"
-                                    " line %d)", start));
+                                    "unterminated %c-string literal (detected at"
+                                    " line %d)", TOK_GET_STRING_PREFIX(tok), start));
             }
         }
 
-        if (c == current_tok->f_string_quote) {
+        if (c == current_tok->quote) {
             end_quote_size += 1;
             continue;
         } else {
@@ -1393,7 +1417,7 @@ f_string_middle:
         }
 
         if (c == '{') {
-            if (!_PyLexer_update_fstring_expr(tok, c)) {
+            if (!_PyLexer_update_ftstring_expr(tok, c)) {
                 return MAKE_TOKEN(ENDMARKER);
             }
             int peek = tok_nextc(tok);
@@ -1402,7 +1426,8 @@ f_string_middle:
                 tok_backup(tok, c);
                 current_tok->curly_bracket_expr_start_depth++;
                 if (current_tok->curly_bracket_expr_start_depth >= MAX_EXPR_NESTING) {
-                    return MAKE_TOKEN(_PyTokenizer_syntaxerror(tok, "f-string: expressions nested too deeply"));
+                    return MAKE_TOKEN(_PyTokenizer_syntaxerror(tok,
+                        "%c-string: expressions nested too deeply", TOK_GET_STRING_PREFIX(tok)));
                 }
                 TOK_GET_MODE(tok)->kind = TOK_REGULAR_MODE;
                 current_tok->in_format_spec = 0;
@@ -1412,12 +1437,12 @@ f_string_middle:
                 p_start = tok->start;
                 p_end = tok->cur - 1;
             }
-            return MAKE_TOKEN(FSTRING_MIDDLE);
+            return MAKE_TOKEN(FTSTRING_MIDDLE(current_tok));
         } else if (c == '}') {
             if (unicode_escape) {
                 p_start = tok->start;
                 p_end = tok->cur;
-                return MAKE_TOKEN(FSTRING_MIDDLE);
+                return MAKE_TOKEN(FTSTRING_MIDDLE(current_tok));
             }
             int peek = tok_nextc(tok);
 
@@ -1437,7 +1462,7 @@ f_string_middle:
                 p_start = tok->start;
                 p_end = tok->cur;
             }
-            return MAKE_TOKEN(FSTRING_MIDDLE);
+            return MAKE_TOKEN(FTSTRING_MIDDLE(current_tok));
         } else if (c == '\\') {
             int peek = tok_nextc(tok);
             if (peek == '\r') {
@@ -1447,7 +1472,7 @@ f_string_middle:
             // brace. We have to restore and return the control back
             // to the loop for the next iteration.
             if (peek == '{' || peek == '}') {
-                if (!current_tok->f_string_raw) {
+                if (!current_tok->raw) {
                     if (_PyTokenizer_warn_invalid_escape_sequence(tok, peek)) {
                         return MAKE_TOKEN(ERRORTOKEN);
                     }
@@ -1456,7 +1481,7 @@ f_string_middle:
                 continue;
             }
 
-            if (!current_tok->f_string_raw) {
+            if (!current_tok->raw) {
                 if (peek == 'N') {
                     /* Handle named unicode escapes (\N{BULLET}) */
                     peek = tok_nextc(tok);
@@ -1474,12 +1499,12 @@ f_string_middle:
 
     // Backup the f-string quotes to emit a final FSTRING_MIDDLE and
     // add the quotes to the FSTRING_END in the next tokenizer iteration.
-    for (int i = 0; i < current_tok->f_string_quote_size; i++) {
-        tok_backup(tok, current_tok->f_string_quote);
+    for (int i = 0; i < current_tok->quote_size; i++) {
+        tok_backup(tok, current_tok->quote);
     }
     p_start = tok->start;
     p_end = tok->cur;
-    return MAKE_TOKEN(FSTRING_MIDDLE);
+    return MAKE_TOKEN(FTSTRING_MIDDLE(current_tok));
 }
 
 static int
index 7f21bf56bba2d19dea3962fd06503bd8aada1590..1d97ac57b745b092f7ebada447d1cbc8e1cd63e5 100644 (file)
@@ -3,7 +3,7 @@
 
 #include "state.h"
 
-int _PyLexer_update_fstring_expr(struct tok_state *tok, char cur);
+int _PyLexer_update_ftstring_expr(struct tok_state *tok, char cur);
 
 int _PyTokenizer_Get(struct tok_state *, struct token *);
 
index 1665debea30b3017a9304780cdc9a1030d44dac8..2de9004fe084f2f5b6e761a995f8e33e5caadb40 100644 (file)
@@ -54,7 +54,7 @@ _PyTokenizer_tok_new(void)
     tok->tok_extra_tokens = 0;
     tok->comment_newline = 0;
     tok->implicit_newline = 0;
-    tok->tok_mode_stack[0] = (tokenizer_mode){.kind =TOK_REGULAR_MODE, .f_string_quote='\0', .f_string_quote_size = 0, .f_string_debug=0};
+    tok->tok_mode_stack[0] = (tokenizer_mode){.kind =TOK_REGULAR_MODE, .quote='\0', .quote_size = 0, .in_debug=0};
     tok->tok_mode_stack_index = 0;
 #ifdef Py_DEBUG
     tok->debug = _Py_GetConfig()->parser_debug;
index 9ed3babfdbfbf17634eb56bf5647bf55e49a9a5c..5e8cac7249b21c5b1798cb1bed9fca2ac48ac931 100644 (file)
@@ -36,6 +36,11 @@ enum tokenizer_mode_kind_t {
     TOK_FSTRING_MODE,
 };
 
+enum string_kind_t {
+    FSTRING,
+    TSTRING,
+};
+
 #define MAX_EXPR_NESTING 3
 
 typedef struct _tokenizer_mode {
@@ -44,21 +49,23 @@ typedef struct _tokenizer_mode {
     int curly_bracket_depth;
     int curly_bracket_expr_start_depth;
 
-    char f_string_quote;
-    int f_string_quote_size;
-    int f_string_raw;
-    const char* f_string_start;
-    const char* f_string_multi_line_start;
-    int f_string_line_start;
+    char quote;
+    int quote_size;
+    int raw;
+    const char* start;
+    const char* multi_line_start;
+    int first_line;
 
-    Py_ssize_t f_string_start_offset;
-    Py_ssize_t f_string_multi_line_start_offset;
+    Py_ssize_t start_offset;
+    Py_ssize_t multi_line_start_offset;
 
     Py_ssize_t last_expr_size;
     Py_ssize_t last_expr_end;
     char* last_expr_buffer;
-    int f_string_debug;
+    int in_debug;
     int in_format_spec;
+
+    enum string_kind_t string_kind;
 } tokenizer_mode;
 
 /* Tokenizer state */
index 0cc43cefa1b818950906e79ca2c9b51375f0e62d..ff5d8baf9204b62a2cc76aa5d02ac5c3418d3738 100644 (file)
@@ -21,54 +21,54 @@ static KeywordToken *reserved_keywords[] = {
     (KeywordToken[]) {{NULL, -1}},
     (KeywordToken[]) {{NULL, -1}},
     (KeywordToken[]) {
-        {"if", 677},
-        {"as", 675},
-        {"in", 690},
+        {"if", 678},
+        {"as", 676},
+        {"in", 691},
         {"or", 586},
         {"is", 594},
         {NULL, -1},
     },
     (KeywordToken[]) {
-        {"del", 622},
-        {"def", 694},
-        {"for", 689},
-        {"try", 651},
+        {"del", 623},
+        {"def", 695},
+        {"for", 690},
+        {"try", 652},
         {"and", 587},
-        {"not", 698},
+        {"not", 699},
         {NULL, -1},
     },
     (KeywordToken[]) {
-        {"from", 630},
+        {"from", 631},
         {"pass", 526},
-        {"with", 642},
-        {"elif", 682},
-        {"else", 681},
-        {"None", 620},
-        {"True", 619},
+        {"with", 643},
+        {"elif", 683},
+        {"else", 682},
+        {"None", 621},
+        {"True", 620},
         {NULL, -1},
     },
     (KeywordToken[]) {
         {"raise", 525},
         {"yield", 585},
         {"break", 527},
-        {"async", 693},
-        {"class", 696},
-        {"while", 684},
-        {"False", 621},
+        {"async", 694},
+        {"class", 697},
+        {"while", 685},
+        {"False", 622},
         {"await", 595},
         {NULL, -1},
     },
     (KeywordToken[]) {
         {"return", 522},
-        {"import", 631},
+        {"import", 632},
         {"assert", 532},
         {"global", 529},
-        {"except", 672},
-        {"lambda", 618},
+        {"except", 673},
+        {"lambda", 619},
         {NULL, -1},
     },
     (KeywordToken[]) {
-        {"finally", 668},
+        {"finally", 669},
         {NULL, -1},
     },
     (KeywordToken[]) {
@@ -247,272 +247,282 @@ static char *soft_keywords[] = {
 #define fstring_full_format_spec_type 1160
 #define fstring_format_spec_type 1161
 #define fstring_type 1162
-#define string_type 1163
-#define strings_type 1164
-#define list_type 1165
-#define tuple_type 1166
-#define set_type 1167
-#define dict_type 1168
-#define double_starred_kvpairs_type 1169
-#define double_starred_kvpair_type 1170
-#define kvpair_type 1171
-#define for_if_clauses_type 1172
-#define for_if_clause_type 1173
-#define listcomp_type 1174
-#define setcomp_type 1175
-#define genexp_type 1176
-#define dictcomp_type 1177
-#define arguments_type 1178
-#define args_type 1179
-#define kwargs_type 1180
-#define starred_expression_type 1181
-#define kwarg_or_starred_type 1182
-#define kwarg_or_double_starred_type 1183
-#define star_targets_type 1184
-#define star_targets_list_seq_type 1185
-#define star_targets_tuple_seq_type 1186
-#define star_target_type 1187
-#define target_with_star_atom_type 1188
-#define star_atom_type 1189
-#define single_target_type 1190
-#define single_subscript_attribute_target_type 1191
-#define t_primary_type 1192  // Left-recursive
-#define t_lookahead_type 1193
-#define del_targets_type 1194
-#define del_target_type 1195
-#define del_t_atom_type 1196
-#define type_expressions_type 1197
-#define func_type_comment_type 1198
-#define invalid_arguments_type 1199
-#define invalid_kwarg_type 1200
-#define expression_without_invalid_type 1201
-#define invalid_legacy_expression_type 1202
-#define invalid_type_param_type 1203
-#define invalid_expression_type 1204
-#define invalid_named_expression_type 1205
-#define invalid_assignment_type 1206
-#define invalid_ann_assign_target_type 1207
-#define invalid_del_stmt_type 1208
-#define invalid_block_type 1209
-#define invalid_comprehension_type 1210
-#define invalid_dict_comprehension_type 1211
-#define invalid_parameters_type 1212
-#define invalid_default_type 1213
-#define invalid_star_etc_type 1214
-#define invalid_kwds_type 1215
-#define invalid_parameters_helper_type 1216
-#define invalid_lambda_parameters_type 1217
-#define invalid_lambda_parameters_helper_type 1218
-#define invalid_lambda_star_etc_type 1219
-#define invalid_lambda_kwds_type 1220
-#define invalid_double_type_comments_type 1221
-#define invalid_with_item_type 1222
-#define invalid_for_if_clause_type 1223
-#define invalid_for_target_type 1224
-#define invalid_group_type 1225
-#define invalid_import_type 1226
-#define invalid_import_from_targets_type 1227
-#define invalid_with_stmt_type 1228
-#define invalid_with_stmt_indent_type 1229
-#define invalid_try_stmt_type 1230
-#define invalid_except_stmt_type 1231
-#define invalid_except_star_stmt_type 1232
-#define invalid_finally_stmt_type 1233
-#define invalid_except_stmt_indent_type 1234
-#define invalid_except_star_stmt_indent_type 1235
-#define invalid_match_stmt_type 1236
-#define invalid_case_block_type 1237
-#define invalid_as_pattern_type 1238
-#define invalid_class_pattern_type 1239
-#define invalid_class_argument_pattern_type 1240
-#define invalid_if_stmt_type 1241
-#define invalid_elif_stmt_type 1242
-#define invalid_else_stmt_type 1243
-#define invalid_while_stmt_type 1244
-#define invalid_for_stmt_type 1245
-#define invalid_def_raw_type 1246
-#define invalid_class_def_raw_type 1247
-#define invalid_double_starred_kvpairs_type 1248
-#define invalid_kvpair_type 1249
-#define invalid_starred_expression_unpacking_type 1250
-#define invalid_starred_expression_type 1251
-#define invalid_replacement_field_type 1252
-#define invalid_conversion_character_type 1253
-#define invalid_arithmetic_type 1254
-#define invalid_factor_type 1255
-#define invalid_type_params_type 1256
-#define _loop0_1_type 1257
-#define _loop1_2_type 1258
-#define _loop0_3_type 1259
-#define _gather_4_type 1260
-#define _tmp_5_type 1261
-#define _tmp_6_type 1262
-#define _tmp_7_type 1263
-#define _tmp_8_type 1264
-#define _tmp_9_type 1265
-#define _tmp_10_type 1266
-#define _tmp_11_type 1267
-#define _loop1_12_type 1268
-#define _tmp_13_type 1269
-#define _loop0_14_type 1270
-#define _gather_15_type 1271
-#define _tmp_16_type 1272
-#define _tmp_17_type 1273
-#define _loop0_18_type 1274
-#define _loop1_19_type 1275
-#define _loop0_20_type 1276
-#define _gather_21_type 1277
-#define _tmp_22_type 1278
-#define _loop0_23_type 1279
-#define _gather_24_type 1280
-#define _loop1_25_type 1281
-#define _tmp_26_type 1282
-#define _tmp_27_type 1283
-#define _loop0_28_type 1284
-#define _loop0_29_type 1285
-#define _loop1_30_type 1286
-#define _loop1_31_type 1287
-#define _loop0_32_type 1288
-#define _loop1_33_type 1289
-#define _loop0_34_type 1290
-#define _gather_35_type 1291
-#define _tmp_36_type 1292
-#define _loop1_37_type 1293
-#define _loop1_38_type 1294
-#define _loop1_39_type 1295
-#define _loop0_40_type 1296
-#define _gather_41_type 1297
-#define _tmp_42_type 1298
-#define _tmp_43_type 1299
-#define _loop0_44_type 1300
-#define _gather_45_type 1301
-#define _loop0_46_type 1302
-#define _gather_47_type 1303
-#define _tmp_48_type 1304
-#define _loop0_49_type 1305
-#define _gather_50_type 1306
-#define _loop0_51_type 1307
-#define _gather_52_type 1308
-#define _loop0_53_type 1309
-#define _gather_54_type 1310
-#define _loop1_55_type 1311
-#define _loop1_56_type 1312
-#define _loop0_57_type 1313
-#define _gather_58_type 1314
-#define _loop1_59_type 1315
-#define _loop1_60_type 1316
-#define _loop1_61_type 1317
-#define _tmp_62_type 1318
-#define _loop0_63_type 1319
-#define _gather_64_type 1320
-#define _tmp_65_type 1321
-#define _tmp_66_type 1322
-#define _tmp_67_type 1323
-#define _tmp_68_type 1324
-#define _tmp_69_type 1325
-#define _tmp_70_type 1326
-#define _loop0_71_type 1327
-#define _loop0_72_type 1328
-#define _loop1_73_type 1329
-#define _loop1_74_type 1330
-#define _loop0_75_type 1331
-#define _loop1_76_type 1332
-#define _loop0_77_type 1333
-#define _loop0_78_type 1334
-#define _loop1_79_type 1335
-#define _tmp_80_type 1336
-#define _loop0_81_type 1337
-#define _gather_82_type 1338
-#define _loop1_83_type 1339
-#define _loop0_84_type 1340
-#define _tmp_85_type 1341
-#define _loop0_86_type 1342
-#define _gather_87_type 1343
-#define _tmp_88_type 1344
-#define _loop0_89_type 1345
-#define _gather_90_type 1346
-#define _loop0_91_type 1347
-#define _gather_92_type 1348
-#define _loop0_93_type 1349
-#define _loop0_94_type 1350
-#define _gather_95_type 1351
-#define _loop1_96_type 1352
-#define _tmp_97_type 1353
-#define _loop0_98_type 1354
-#define _gather_99_type 1355
-#define _loop0_100_type 1356
-#define _gather_101_type 1357
-#define _tmp_102_type 1358
-#define _tmp_103_type 1359
-#define _loop0_104_type 1360
-#define _gather_105_type 1361
-#define _tmp_106_type 1362
-#define _tmp_107_type 1363
-#define _tmp_108_type 1364
-#define _tmp_109_type 1365
-#define _tmp_110_type 1366
-#define _loop1_111_type 1367
-#define _tmp_112_type 1368
-#define _tmp_113_type 1369
-#define _tmp_114_type 1370
-#define _tmp_115_type 1371
-#define _tmp_116_type 1372
-#define _loop0_117_type 1373
-#define _loop0_118_type 1374
-#define _tmp_119_type 1375
-#define _tmp_120_type 1376
-#define _tmp_121_type 1377
-#define _tmp_122_type 1378
-#define _tmp_123_type 1379
-#define _tmp_124_type 1380
-#define _tmp_125_type 1381
-#define _tmp_126_type 1382
-#define _tmp_127_type 1383
-#define _loop0_128_type 1384
-#define _gather_129_type 1385
-#define _tmp_130_type 1386
-#define _tmp_131_type 1387
-#define _tmp_132_type 1388
-#define _tmp_133_type 1389
-#define _loop0_134_type 1390
-#define _gather_135_type 1391
-#define _loop0_136_type 1392
-#define _gather_137_type 1393
-#define _loop0_138_type 1394
-#define _gather_139_type 1395
-#define _tmp_140_type 1396
-#define _loop0_141_type 1397
-#define _tmp_142_type 1398
-#define _tmp_143_type 1399
-#define _tmp_144_type 1400
-#define _tmp_145_type 1401
-#define _tmp_146_type 1402
-#define _tmp_147_type 1403
-#define _tmp_148_type 1404
-#define _tmp_149_type 1405
-#define _tmp_150_type 1406
-#define _tmp_151_type 1407
-#define _tmp_152_type 1408
-#define _tmp_153_type 1409
-#define _tmp_154_type 1410
-#define _tmp_155_type 1411
-#define _tmp_156_type 1412
-#define _tmp_157_type 1413
-#define _tmp_158_type 1414
-#define _tmp_159_type 1415
-#define _tmp_160_type 1416
-#define _tmp_161_type 1417
-#define _tmp_162_type 1418
-#define _tmp_163_type 1419
-#define _tmp_164_type 1420
-#define _tmp_165_type 1421
-#define _tmp_166_type 1422
-#define _tmp_167_type 1423
-#define _loop0_168_type 1424
-#define _tmp_169_type 1425
-#define _tmp_170_type 1426
-#define _tmp_171_type 1427
-#define _tmp_172_type 1428
+#define tstring_format_spec_replacement_field_type 1163
+#define tstring_format_spec_type 1164
+#define tstring_full_format_spec_type 1165
+#define tstring_replacement_field_type 1166
+#define tstring_middle_type 1167
+#define tstring_type 1168
+#define string_type 1169
+#define strings_type 1170
+#define list_type 1171
+#define tuple_type 1172
+#define set_type 1173
+#define dict_type 1174
+#define double_starred_kvpairs_type 1175
+#define double_starred_kvpair_type 1176
+#define kvpair_type 1177
+#define for_if_clauses_type 1178
+#define for_if_clause_type 1179
+#define listcomp_type 1180
+#define setcomp_type 1181
+#define genexp_type 1182
+#define dictcomp_type 1183
+#define arguments_type 1184
+#define args_type 1185
+#define kwargs_type 1186
+#define starred_expression_type 1187
+#define kwarg_or_starred_type 1188
+#define kwarg_or_double_starred_type 1189
+#define star_targets_type 1190
+#define star_targets_list_seq_type 1191
+#define star_targets_tuple_seq_type 1192
+#define star_target_type 1193
+#define target_with_star_atom_type 1194
+#define star_atom_type 1195
+#define single_target_type 1196
+#define single_subscript_attribute_target_type 1197
+#define t_primary_type 1198  // Left-recursive
+#define t_lookahead_type 1199
+#define del_targets_type 1200
+#define del_target_type 1201
+#define del_t_atom_type 1202
+#define type_expressions_type 1203
+#define func_type_comment_type 1204
+#define invalid_arguments_type 1205
+#define invalid_kwarg_type 1206
+#define expression_without_invalid_type 1207
+#define invalid_legacy_expression_type 1208
+#define invalid_type_param_type 1209
+#define invalid_expression_type 1210
+#define invalid_named_expression_type 1211
+#define invalid_assignment_type 1212
+#define invalid_ann_assign_target_type 1213
+#define invalid_del_stmt_type 1214
+#define invalid_block_type 1215
+#define invalid_comprehension_type 1216
+#define invalid_dict_comprehension_type 1217
+#define invalid_parameters_type 1218
+#define invalid_default_type 1219
+#define invalid_star_etc_type 1220
+#define invalid_kwds_type 1221
+#define invalid_parameters_helper_type 1222
+#define invalid_lambda_parameters_type 1223
+#define invalid_lambda_parameters_helper_type 1224
+#define invalid_lambda_star_etc_type 1225
+#define invalid_lambda_kwds_type 1226
+#define invalid_double_type_comments_type 1227
+#define invalid_with_item_type 1228
+#define invalid_for_if_clause_type 1229
+#define invalid_for_target_type 1230
+#define invalid_group_type 1231
+#define invalid_import_type 1232
+#define invalid_import_from_targets_type 1233
+#define invalid_with_stmt_type 1234
+#define invalid_with_stmt_indent_type 1235
+#define invalid_try_stmt_type 1236
+#define invalid_except_stmt_type 1237
+#define invalid_except_star_stmt_type 1238
+#define invalid_finally_stmt_type 1239
+#define invalid_except_stmt_indent_type 1240
+#define invalid_except_star_stmt_indent_type 1241
+#define invalid_match_stmt_type 1242
+#define invalid_case_block_type 1243
+#define invalid_as_pattern_type 1244
+#define invalid_class_pattern_type 1245
+#define invalid_class_argument_pattern_type 1246
+#define invalid_if_stmt_type 1247
+#define invalid_elif_stmt_type 1248
+#define invalid_else_stmt_type 1249
+#define invalid_while_stmt_type 1250
+#define invalid_for_stmt_type 1251
+#define invalid_def_raw_type 1252
+#define invalid_class_def_raw_type 1253
+#define invalid_double_starred_kvpairs_type 1254
+#define invalid_kvpair_type 1255
+#define invalid_starred_expression_unpacking_type 1256
+#define invalid_starred_expression_type 1257
+#define invalid_fstring_replacement_field_type 1258
+#define invalid_fstring_conversion_character_type 1259
+#define invalid_tstring_replacement_field_type 1260
+#define invalid_tstring_conversion_character_type 1261
+#define invalid_arithmetic_type 1262
+#define invalid_factor_type 1263
+#define invalid_type_params_type 1264
+#define _loop0_1_type 1265
+#define _loop1_2_type 1266
+#define _loop0_3_type 1267
+#define _gather_4_type 1268
+#define _tmp_5_type 1269
+#define _tmp_6_type 1270
+#define _tmp_7_type 1271
+#define _tmp_8_type 1272
+#define _tmp_9_type 1273
+#define _tmp_10_type 1274
+#define _tmp_11_type 1275
+#define _loop1_12_type 1276
+#define _tmp_13_type 1277
+#define _loop0_14_type 1278
+#define _gather_15_type 1279
+#define _tmp_16_type 1280
+#define _tmp_17_type 1281
+#define _loop0_18_type 1282
+#define _loop1_19_type 1283
+#define _loop0_20_type 1284
+#define _gather_21_type 1285
+#define _tmp_22_type 1286
+#define _loop0_23_type 1287
+#define _gather_24_type 1288
+#define _loop1_25_type 1289
+#define _tmp_26_type 1290
+#define _tmp_27_type 1291
+#define _loop0_28_type 1292
+#define _loop0_29_type 1293
+#define _loop1_30_type 1294
+#define _loop1_31_type 1295
+#define _loop0_32_type 1296
+#define _loop1_33_type 1297
+#define _loop0_34_type 1298
+#define _gather_35_type 1299
+#define _tmp_36_type 1300
+#define _loop1_37_type 1301
+#define _loop1_38_type 1302
+#define _loop1_39_type 1303
+#define _loop0_40_type 1304
+#define _gather_41_type 1305
+#define _tmp_42_type 1306
+#define _tmp_43_type 1307
+#define _tmp_44_type 1308
+#define _loop0_45_type 1309
+#define _gather_46_type 1310
+#define _loop0_47_type 1311
+#define _gather_48_type 1312
+#define _tmp_49_type 1313
+#define _loop0_50_type 1314
+#define _gather_51_type 1315
+#define _loop0_52_type 1316
+#define _gather_53_type 1317
+#define _loop0_54_type 1318
+#define _gather_55_type 1319
+#define _loop1_56_type 1320
+#define _loop1_57_type 1321
+#define _loop0_58_type 1322
+#define _gather_59_type 1323
+#define _loop1_60_type 1324
+#define _loop1_61_type 1325
+#define _loop1_62_type 1326
+#define _tmp_63_type 1327
+#define _loop0_64_type 1328
+#define _gather_65_type 1329
+#define _tmp_66_type 1330
+#define _tmp_67_type 1331
+#define _tmp_68_type 1332
+#define _tmp_69_type 1333
+#define _tmp_70_type 1334
+#define _loop0_71_type 1335
+#define _loop0_72_type 1336
+#define _loop1_73_type 1337
+#define _loop1_74_type 1338
+#define _loop0_75_type 1339
+#define _loop1_76_type 1340
+#define _loop0_77_type 1341
+#define _loop0_78_type 1342
+#define _loop0_79_type 1343
+#define _loop0_80_type 1344
+#define _loop1_81_type 1345
+#define _tmp_82_type 1346
+#define _loop0_83_type 1347
+#define _gather_84_type 1348
+#define _loop1_85_type 1349
+#define _loop0_86_type 1350
+#define _tmp_87_type 1351
+#define _loop0_88_type 1352
+#define _gather_89_type 1353
+#define _tmp_90_type 1354
+#define _loop0_91_type 1355
+#define _gather_92_type 1356
+#define _loop0_93_type 1357
+#define _gather_94_type 1358
+#define _loop0_95_type 1359
+#define _loop0_96_type 1360
+#define _gather_97_type 1361
+#define _loop1_98_type 1362
+#define _tmp_99_type 1363
+#define _loop0_100_type 1364
+#define _gather_101_type 1365
+#define _loop0_102_type 1366
+#define _gather_103_type 1367
+#define _tmp_104_type 1368
+#define _tmp_105_type 1369
+#define _loop0_106_type 1370
+#define _gather_107_type 1371
+#define _tmp_108_type 1372
+#define _tmp_109_type 1373
+#define _tmp_110_type 1374
+#define _tmp_111_type 1375
+#define _tmp_112_type 1376
+#define _loop1_113_type 1377
+#define _tmp_114_type 1378
+#define _tmp_115_type 1379
+#define _tmp_116_type 1380
+#define _tmp_117_type 1381
+#define _tmp_118_type 1382
+#define _loop0_119_type 1383
+#define _loop0_120_type 1384
+#define _tmp_121_type 1385
+#define _tmp_122_type 1386
+#define _tmp_123_type 1387
+#define _tmp_124_type 1388
+#define _tmp_125_type 1389
+#define _tmp_126_type 1390
+#define _tmp_127_type 1391
+#define _tmp_128_type 1392
+#define _tmp_129_type 1393
+#define _loop0_130_type 1394
+#define _gather_131_type 1395
+#define _tmp_132_type 1396
+#define _tmp_133_type 1397
+#define _tmp_134_type 1398
+#define _tmp_135_type 1399
+#define _loop0_136_type 1400
+#define _gather_137_type 1401
+#define _loop0_138_type 1402
+#define _gather_139_type 1403
+#define _loop0_140_type 1404
+#define _gather_141_type 1405
+#define _tmp_142_type 1406
+#define _loop0_143_type 1407
+#define _tmp_144_type 1408
+#define _tmp_145_type 1409
+#define _tmp_146_type 1410
+#define _tmp_147_type 1411
+#define _tmp_148_type 1412
+#define _tmp_149_type 1413
+#define _tmp_150_type 1414
+#define _tmp_151_type 1415
+#define _tmp_152_type 1416
+#define _tmp_153_type 1417
+#define _tmp_154_type 1418
+#define _tmp_155_type 1419
+#define _tmp_156_type 1420
+#define _tmp_157_type 1421
+#define _tmp_158_type 1422
+#define _tmp_159_type 1423
+#define _tmp_160_type 1424
+#define _tmp_161_type 1425
+#define _tmp_162_type 1426
+#define _tmp_163_type 1427
+#define _tmp_164_type 1428
+#define _tmp_165_type 1429
+#define _tmp_166_type 1430
+#define _tmp_167_type 1431
+#define _tmp_168_type 1432
+#define _tmp_169_type 1433
+#define _loop0_170_type 1434
+#define _tmp_171_type 1435
+#define _tmp_172_type 1436
+#define _tmp_173_type 1437
+#define _tmp_174_type 1438
 
 static mod_ty file_rule(Parser *p);
 static mod_ty interactive_rule(Parser *p);
@@ -677,6 +687,12 @@ static ResultTokenWithMetadata* fstring_conversion_rule(Parser *p);
 static ResultTokenWithMetadata* fstring_full_format_spec_rule(Parser *p);
 static expr_ty fstring_format_spec_rule(Parser *p);
 static expr_ty fstring_rule(Parser *p);
+static expr_ty tstring_format_spec_replacement_field_rule(Parser *p);
+static expr_ty tstring_format_spec_rule(Parser *p);
+static ResultTokenWithMetadata* tstring_full_format_spec_rule(Parser *p);
+static expr_ty tstring_replacement_field_rule(Parser *p);
+static expr_ty tstring_middle_rule(Parser *p);
+static expr_ty tstring_rule(Parser *p);
 static expr_ty string_rule(Parser *p);
 static expr_ty strings_rule(Parser *p);
 static expr_ty list_rule(Parser *p);
@@ -766,8 +782,10 @@ static void *invalid_double_starred_kvpairs_rule(Parser *p);
 static void *invalid_kvpair_rule(Parser *p);
 static void *invalid_starred_expression_unpacking_rule(Parser *p);
 static void *invalid_starred_expression_rule(Parser *p);
-static void *invalid_replacement_field_rule(Parser *p);
-static void *invalid_conversion_character_rule(Parser *p);
+static void *invalid_fstring_replacement_field_rule(Parser *p);
+static void *invalid_fstring_conversion_character_rule(Parser *p);
+static void *invalid_tstring_replacement_field_rule(Parser *p);
+static void *invalid_tstring_conversion_character_rule(Parser *p);
 static void *invalid_arithmetic_rule(Parser *p);
 static void *invalid_factor_rule(Parser *p);
 static void *invalid_type_params_rule(Parser *p);
@@ -814,28 +832,28 @@ static asdl_seq *_loop0_40_rule(Parser *p);
 static asdl_seq *_gather_41_rule(Parser *p);
 static void *_tmp_42_rule(Parser *p);
 static void *_tmp_43_rule(Parser *p);
-static asdl_seq *_loop0_44_rule(Parser *p);
-static asdl_seq *_gather_45_rule(Parser *p);
-static asdl_seq *_loop0_46_rule(Parser *p);
-static asdl_seq *_gather_47_rule(Parser *p);
-static void *_tmp_48_rule(Parser *p);
-static asdl_seq *_loop0_49_rule(Parser *p);
-static asdl_seq *_gather_50_rule(Parser *p);
-static asdl_seq *_loop0_51_rule(Parser *p);
-static asdl_seq *_gather_52_rule(Parser *p);
-static asdl_seq *_loop0_53_rule(Parser *p);
-static asdl_seq *_gather_54_rule(Parser *p);
-static asdl_seq *_loop1_55_rule(Parser *p);
+static void *_tmp_44_rule(Parser *p);
+static asdl_seq *_loop0_45_rule(Parser *p);
+static asdl_seq *_gather_46_rule(Parser *p);
+static asdl_seq *_loop0_47_rule(Parser *p);
+static asdl_seq *_gather_48_rule(Parser *p);
+static void *_tmp_49_rule(Parser *p);
+static asdl_seq *_loop0_50_rule(Parser *p);
+static asdl_seq *_gather_51_rule(Parser *p);
+static asdl_seq *_loop0_52_rule(Parser *p);
+static asdl_seq *_gather_53_rule(Parser *p);
+static asdl_seq *_loop0_54_rule(Parser *p);
+static asdl_seq *_gather_55_rule(Parser *p);
 static asdl_seq *_loop1_56_rule(Parser *p);
-static asdl_seq *_loop0_57_rule(Parser *p);
-static asdl_seq *_gather_58_rule(Parser *p);
-static asdl_seq *_loop1_59_rule(Parser *p);
+static asdl_seq *_loop1_57_rule(Parser *p);
+static asdl_seq *_loop0_58_rule(Parser *p);
+static asdl_seq *_gather_59_rule(Parser *p);
 static asdl_seq *_loop1_60_rule(Parser *p);
 static asdl_seq *_loop1_61_rule(Parser *p);
-static void *_tmp_62_rule(Parser *p);
-static asdl_seq *_loop0_63_rule(Parser *p);
-static asdl_seq *_gather_64_rule(Parser *p);
-static void *_tmp_65_rule(Parser *p);
+static asdl_seq *_loop1_62_rule(Parser *p);
+static void *_tmp_63_rule(Parser *p);
+static asdl_seq *_loop0_64_rule(Parser *p);
+static asdl_seq *_gather_65_rule(Parser *p);
 static void *_tmp_66_rule(Parser *p);
 static void *_tmp_67_rule(Parser *p);
 static void *_tmp_68_rule(Parser *p);
@@ -849,48 +867,48 @@ static asdl_seq *_loop0_75_rule(Parser *p);
 static asdl_seq *_loop1_76_rule(Parser *p);
 static asdl_seq *_loop0_77_rule(Parser *p);
 static asdl_seq *_loop0_78_rule(Parser *p);
-static asdl_seq *_loop1_79_rule(Parser *p);
-static void *_tmp_80_rule(Parser *p);
-static asdl_seq *_loop0_81_rule(Parser *p);
-static asdl_seq *_gather_82_rule(Parser *p);
-static asdl_seq *_loop1_83_rule(Parser *p);
-static asdl_seq *_loop0_84_rule(Parser *p);
-static void *_tmp_85_rule(Parser *p);
+static asdl_seq *_loop0_79_rule(Parser *p);
+static asdl_seq *_loop0_80_rule(Parser *p);
+static asdl_seq *_loop1_81_rule(Parser *p);
+static void *_tmp_82_rule(Parser *p);
+static asdl_seq *_loop0_83_rule(Parser *p);
+static asdl_seq *_gather_84_rule(Parser *p);
+static asdl_seq *_loop1_85_rule(Parser *p);
 static asdl_seq *_loop0_86_rule(Parser *p);
-static asdl_seq *_gather_87_rule(Parser *p);
-static void *_tmp_88_rule(Parser *p);
-static asdl_seq *_loop0_89_rule(Parser *p);
-static asdl_seq *_gather_90_rule(Parser *p);
+static void *_tmp_87_rule(Parser *p);
+static asdl_seq *_loop0_88_rule(Parser *p);
+static asdl_seq *_gather_89_rule(Parser *p);
+static void *_tmp_90_rule(Parser *p);
 static asdl_seq *_loop0_91_rule(Parser *p);
 static asdl_seq *_gather_92_rule(Parser *p);
 static asdl_seq *_loop0_93_rule(Parser *p);
-static asdl_seq *_loop0_94_rule(Parser *p);
-static asdl_seq *_gather_95_rule(Parser *p);
-static asdl_seq *_loop1_96_rule(Parser *p);
-static void *_tmp_97_rule(Parser *p);
-static asdl_seq *_loop0_98_rule(Parser *p);
-static asdl_seq *_gather_99_rule(Parser *p);
+static asdl_seq *_gather_94_rule(Parser *p);
+static asdl_seq *_loop0_95_rule(Parser *p);
+static asdl_seq *_loop0_96_rule(Parser *p);
+static asdl_seq *_gather_97_rule(Parser *p);
+static asdl_seq *_loop1_98_rule(Parser *p);
+static void *_tmp_99_rule(Parser *p);
 static asdl_seq *_loop0_100_rule(Parser *p);
 static asdl_seq *_gather_101_rule(Parser *p);
-static void *_tmp_102_rule(Parser *p);
-static void *_tmp_103_rule(Parser *p);
-static asdl_seq *_loop0_104_rule(Parser *p);
-static asdl_seq *_gather_105_rule(Parser *p);
-static void *_tmp_106_rule(Parser *p);
-static void *_tmp_107_rule(Parser *p);
+static asdl_seq *_loop0_102_rule(Parser *p);
+static asdl_seq *_gather_103_rule(Parser *p);
+static void *_tmp_104_rule(Parser *p);
+static void *_tmp_105_rule(Parser *p);
+static asdl_seq *_loop0_106_rule(Parser *p);
+static asdl_seq *_gather_107_rule(Parser *p);
 static void *_tmp_108_rule(Parser *p);
 static void *_tmp_109_rule(Parser *p);
 static void *_tmp_110_rule(Parser *p);
-static asdl_seq *_loop1_111_rule(Parser *p);
+static void *_tmp_111_rule(Parser *p);
 static void *_tmp_112_rule(Parser *p);
-static void *_tmp_113_rule(Parser *p);
+static asdl_seq *_loop1_113_rule(Parser *p);
 static void *_tmp_114_rule(Parser *p);
 static void *_tmp_115_rule(Parser *p);
 static void *_tmp_116_rule(Parser *p);
-static asdl_seq *_loop0_117_rule(Parser *p);
-static asdl_seq *_loop0_118_rule(Parser *p);
-static void *_tmp_119_rule(Parser *p);
-static void *_tmp_120_rule(Parser *p);
+static void *_tmp_117_rule(Parser *p);
+static void *_tmp_118_rule(Parser *p);
+static asdl_seq *_loop0_119_rule(Parser *p);
+static asdl_seq *_loop0_120_rule(Parser *p);
 static void *_tmp_121_rule(Parser *p);
 static void *_tmp_122_rule(Parser *p);
 static void *_tmp_123_rule(Parser *p);
@@ -898,22 +916,22 @@ static void *_tmp_124_rule(Parser *p);
 static void *_tmp_125_rule(Parser *p);
 static void *_tmp_126_rule(Parser *p);
 static void *_tmp_127_rule(Parser *p);
-static asdl_seq *_loop0_128_rule(Parser *p);
-static asdl_seq *_gather_129_rule(Parser *p);
-static void *_tmp_130_rule(Parser *p);
-static void *_tmp_131_rule(Parser *p);
+static void *_tmp_128_rule(Parser *p);
+static void *_tmp_129_rule(Parser *p);
+static asdl_seq *_loop0_130_rule(Parser *p);
+static asdl_seq *_gather_131_rule(Parser *p);
 static void *_tmp_132_rule(Parser *p);
 static void *_tmp_133_rule(Parser *p);
-static asdl_seq *_loop0_134_rule(Parser *p);
-static asdl_seq *_gather_135_rule(Parser *p);
+static void *_tmp_134_rule(Parser *p);
+static void *_tmp_135_rule(Parser *p);
 static asdl_seq *_loop0_136_rule(Parser *p);
 static asdl_seq *_gather_137_rule(Parser *p);
 static asdl_seq *_loop0_138_rule(Parser *p);
 static asdl_seq *_gather_139_rule(Parser *p);
-static void *_tmp_140_rule(Parser *p);
-static asdl_seq *_loop0_141_rule(Parser *p);
+static asdl_seq *_loop0_140_rule(Parser *p);
+static asdl_seq *_gather_141_rule(Parser *p);
 static void *_tmp_142_rule(Parser *p);
-static void *_tmp_143_rule(Parser *p);
+static asdl_seq *_loop0_143_rule(Parser *p);
 static void *_tmp_144_rule(Parser *p);
 static void *_tmp_145_rule(Parser *p);
 static void *_tmp_146_rule(Parser *p);
@@ -938,11 +956,13 @@ static void *_tmp_164_rule(Parser *p);
 static void *_tmp_165_rule(Parser *p);
 static void *_tmp_166_rule(Parser *p);
 static void *_tmp_167_rule(Parser *p);
-static asdl_seq *_loop0_168_rule(Parser *p);
+static void *_tmp_168_rule(Parser *p);
 static void *_tmp_169_rule(Parser *p);
-static void *_tmp_170_rule(Parser *p);
+static asdl_seq *_loop0_170_rule(Parser *p);
 static void *_tmp_171_rule(Parser *p);
 static void *_tmp_172_rule(Parser *p);
+static void *_tmp_173_rule(Parser *p);
+static void *_tmp_174_rule(Parser *p);
 
 
 // file: statements? $
@@ -1712,7 +1732,7 @@ simple_stmt_rule(Parser *p)
         D(fprintf(stderr, "%*c> simple_stmt[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "&'del' del_stmt"));
         stmt_ty del_stmt_var;
         if (
-            _PyPegen_lookahead_with_int(1, _PyPegen_expect_token, p, 622)  // token='del'
+            _PyPegen_lookahead_with_int(1, _PyPegen_expect_token, p, 623)  // token='del'
             &&
             (del_stmt_var = del_stmt_rule(p))  // del_stmt
         )
@@ -1908,7 +1928,7 @@ compound_stmt_rule(Parser *p)
         D(fprintf(stderr, "%*c> compound_stmt[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "&'if' if_stmt"));
         stmt_ty if_stmt_var;
         if (
-            _PyPegen_lookahead_with_int(1, _PyPegen_expect_token, p, 677)  // token='if'
+            _PyPegen_lookahead_with_int(1, _PyPegen_expect_token, p, 678)  // token='if'
             &&
             (if_stmt_var = if_stmt_rule(p))  // if_stmt
         )
@@ -1992,7 +2012,7 @@ compound_stmt_rule(Parser *p)
         D(fprintf(stderr, "%*c> compound_stmt[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "&'try' try_stmt"));
         stmt_ty try_stmt_var;
         if (
-            _PyPegen_lookahead_with_int(1, _PyPegen_expect_token, p, 651)  // token='try'
+            _PyPegen_lookahead_with_int(1, _PyPegen_expect_token, p, 652)  // token='try'
             &&
             (try_stmt_var = try_stmt_rule(p))  // try_stmt
         )
@@ -2013,7 +2033,7 @@ compound_stmt_rule(Parser *p)
         D(fprintf(stderr, "%*c> compound_stmt[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "&'while' while_stmt"));
         stmt_ty while_stmt_var;
         if (
-            _PyPegen_lookahead_with_int(1, _PyPegen_expect_token, p, 684)  // token='while'
+            _PyPegen_lookahead_with_int(1, _PyPegen_expect_token, p, 685)  // token='while'
             &&
             (while_stmt_var = while_stmt_rule(p))  // while_stmt
         )
@@ -3181,7 +3201,7 @@ del_stmt_rule(Parser *p)
         Token * _keyword;
         asdl_expr_seq* a;
         if (
-            (_keyword = _PyPegen_expect_token(p, 622))  // token='del'
+            (_keyword = _PyPegen_expect_token(p, 623))  // token='del'
             &&
             (a = del_targets_rule(p))  // del_targets
             &&
@@ -3470,7 +3490,7 @@ import_name_rule(Parser *p)
         Token * _keyword;
         asdl_alias_seq* a;
         if (
-            (_keyword = _PyPegen_expect_token(p, 631))  // token='import'
+            (_keyword = _PyPegen_expect_token(p, 632))  // token='import'
             &&
             (a = dotted_as_names_rule(p))  // dotted_as_names
         )
@@ -3539,13 +3559,13 @@ import_from_rule(Parser *p)
         expr_ty b;
         asdl_alias_seq* c;
         if (
-            (_keyword = _PyPegen_expect_token(p, 630))  // token='from'
+            (_keyword = _PyPegen_expect_token(p, 631))  // token='from'
             &&
             (a = _loop0_18_rule(p))  // (('.' | '...'))*
             &&
             (b = dotted_name_rule(p))  // dotted_name
             &&
-            (_keyword_1 = _PyPegen_expect_token(p, 631))  // token='import'
+            (_keyword_1 = _PyPegen_expect_token(p, 632))  // token='import'
             &&
             (c = import_from_targets_rule(p))  // import_from_targets
         )
@@ -3583,11 +3603,11 @@ import_from_rule(Parser *p)
         asdl_seq * a;
         asdl_alias_seq* b;
         if (
-            (_keyword = _PyPegen_expect_token(p, 630))  // token='from'
+            (_keyword = _PyPegen_expect_token(p, 631))  // token='from'
             &&
             (a = _loop1_19_rule(p))  // (('.' | '...'))+
             &&
-            (_keyword_1 = _PyPegen_expect_token(p, 631))  // token='import'
+            (_keyword_1 = _PyPegen_expect_token(p, 632))  // token='import'
             &&
             (b = import_from_targets_rule(p))  // import_from_targets
         )
@@ -4336,7 +4356,7 @@ class_def_raw_rule(Parser *p)
         asdl_stmt_seq* c;
         void *t;
         if (
-            (_keyword = _PyPegen_expect_token(p, 696))  // token='class'
+            (_keyword = _PyPegen_expect_token(p, 697))  // token='class'
             &&
             (a = _PyPegen_name_token(p))  // NAME
             &&
@@ -4503,7 +4523,7 @@ function_def_raw_rule(Parser *p)
         void *t;
         void *tc;
         if (
-            (_keyword = _PyPegen_expect_token(p, 694))  // token='def'
+            (_keyword = _PyPegen_expect_token(p, 695))  // token='def'
             &&
             (n = _PyPegen_name_token(p))  // NAME
             &&
@@ -4564,9 +4584,9 @@ function_def_raw_rule(Parser *p)
         void *t;
         void *tc;
         if (
-            (_keyword = _PyPegen_expect_token(p, 693))  // token='async'
+            (_keyword = _PyPegen_expect_token(p, 694))  // token='async'
             &&
-            (_keyword_1 = _PyPegen_expect_token(p, 694))  // token='def'
+            (_keyword_1 = _PyPegen_expect_token(p, 695))  // token='def'
             &&
             (n = _PyPegen_name_token(p))  // NAME
             &&
@@ -5904,7 +5924,7 @@ if_stmt_rule(Parser *p)
         asdl_stmt_seq* b;
         stmt_ty c;
         if (
-            (_keyword = _PyPegen_expect_token(p, 677))  // token='if'
+            (_keyword = _PyPegen_expect_token(p, 678))  // token='if'
             &&
             (a = named_expression_rule(p))  // named_expression
             &&
@@ -5949,7 +5969,7 @@ if_stmt_rule(Parser *p)
         asdl_stmt_seq* b;
         void *c;
         if (
-            (_keyword = _PyPegen_expect_token(p, 677))  // token='if'
+            (_keyword = _PyPegen_expect_token(p, 678))  // token='if'
             &&
             (a = named_expression_rule(p))  // named_expression
             &&
@@ -6044,7 +6064,7 @@ elif_stmt_rule(Parser *p)
         asdl_stmt_seq* b;
         stmt_ty c;
         if (
-            (_keyword = _PyPegen_expect_token(p, 682))  // token='elif'
+            (_keyword = _PyPegen_expect_token(p, 683))  // token='elif'
             &&
             (a = named_expression_rule(p))  // named_expression
             &&
@@ -6089,7 +6109,7 @@ elif_stmt_rule(Parser *p)
         asdl_stmt_seq* b;
         void *c;
         if (
-            (_keyword = _PyPegen_expect_token(p, 682))  // token='elif'
+            (_keyword = _PyPegen_expect_token(p, 683))  // token='elif'
             &&
             (a = named_expression_rule(p))  // named_expression
             &&
@@ -6170,7 +6190,7 @@ else_block_rule(Parser *p)
         Token * _literal;
         asdl_stmt_seq* b;
         if (
-            (_keyword = _PyPegen_expect_token(p, 681))  // token='else'
+            (_keyword = _PyPegen_expect_token(p, 682))  // token='else'
             &&
             (_literal = _PyPegen_expect_forced_token(p, 11, ":"))  // forced_token=':'
             &&
@@ -6249,7 +6269,7 @@ while_stmt_rule(Parser *p)
         asdl_stmt_seq* b;
         void *c;
         if (
-            (_keyword = _PyPegen_expect_token(p, 684))  // token='while'
+            (_keyword = _PyPegen_expect_token(p, 685))  // token='while'
             &&
             (a = named_expression_rule(p))  // named_expression
             &&
@@ -6349,11 +6369,11 @@ for_stmt_rule(Parser *p)
         expr_ty t;
         void *tc;
         if (
-            (_keyword = _PyPegen_expect_token(p, 689))  // token='for'
+            (_keyword = _PyPegen_expect_token(p, 690))  // token='for'
             &&
             (t = star_targets_rule(p))  // star_targets
             &&
-            (_keyword_1 = _PyPegen_expect_token(p, 690))  // token='in'
+            (_keyword_1 = _PyPegen_expect_token(p, 691))  // token='in'
             &&
             (_cut_var = 1)
             &&
@@ -6411,13 +6431,13 @@ for_stmt_rule(Parser *p)
         expr_ty t;
         void *tc;
         if (
-            (_keyword = _PyPegen_expect_token(p, 693))  // token='async'
+            (_keyword = _PyPegen_expect_token(p, 694))  // token='async'
             &&
-            (_keyword_1 = _PyPegen_expect_token(p, 689))  // token='for'
+            (_keyword_1 = _PyPegen_expect_token(p, 690))  // token='for'
             &&
             (t = star_targets_rule(p))  // star_targets
             &&
-            (_keyword_2 = _PyPegen_expect_token(p, 690))  // token='in'
+            (_keyword_2 = _PyPegen_expect_token(p, 691))  // token='in'
             &&
             (_cut_var = 1)
             &&
@@ -6546,7 +6566,7 @@ with_stmt_rule(Parser *p)
         asdl_stmt_seq* b;
         void *tc;
         if (
-            (_keyword = _PyPegen_expect_token(p, 642))  // token='with'
+            (_keyword = _PyPegen_expect_token(p, 643))  // token='with'
             &&
             (_literal = _PyPegen_expect_token(p, 7))  // token='('
             &&
@@ -6597,7 +6617,7 @@ with_stmt_rule(Parser *p)
         asdl_stmt_seq* b;
         void *tc;
         if (
-            (_keyword = _PyPegen_expect_token(p, 642))  // token='with'
+            (_keyword = _PyPegen_expect_token(p, 643))  // token='with'
             &&
             (a = (asdl_withitem_seq*)_gather_35_rule(p))  // ','.with_item+
             &&
@@ -6646,9 +6666,9 @@ with_stmt_rule(Parser *p)
         asdl_withitem_seq* a;
         asdl_stmt_seq* b;
         if (
-            (_keyword = _PyPegen_expect_token(p, 693))  // token='async'
+            (_keyword = _PyPegen_expect_token(p, 694))  // token='async'
             &&
-            (_keyword_1 = _PyPegen_expect_token(p, 642))  // token='with'
+            (_keyword_1 = _PyPegen_expect_token(p, 643))  // token='with'
             &&
             (_literal = _PyPegen_expect_token(p, 7))  // token='('
             &&
@@ -6698,9 +6718,9 @@ with_stmt_rule(Parser *p)
         asdl_stmt_seq* b;
         void *tc;
         if (
-            (_keyword = _PyPegen_expect_token(p, 693))  // token='async'
+            (_keyword = _PyPegen_expect_token(p, 694))  // token='async'
             &&
-            (_keyword_1 = _PyPegen_expect_token(p, 642))  // token='with'
+            (_keyword_1 = _PyPegen_expect_token(p, 643))  // token='with'
             &&
             (a = (asdl_withitem_seq*)_gather_35_rule(p))  // ','.with_item+
             &&
@@ -6786,7 +6806,7 @@ with_item_rule(Parser *p)
         if (
             (e = expression_rule(p))  // expression
             &&
-            (_keyword = _PyPegen_expect_token(p, 675))  // token='as'
+            (_keyword = _PyPegen_expect_token(p, 676))  // token='as'
             &&
             (t = star_target_rule(p))  // star_target
             &&
@@ -6911,7 +6931,7 @@ try_stmt_rule(Parser *p)
         asdl_stmt_seq* b;
         asdl_stmt_seq* f;
         if (
-            (_keyword = _PyPegen_expect_token(p, 651))  // token='try'
+            (_keyword = _PyPegen_expect_token(p, 652))  // token='try'
             &&
             (_literal = _PyPegen_expect_forced_token(p, 11, ":"))  // forced_token=':'
             &&
@@ -6955,7 +6975,7 @@ try_stmt_rule(Parser *p)
         asdl_excepthandler_seq* ex;
         void *f;
         if (
-            (_keyword = _PyPegen_expect_token(p, 651))  // token='try'
+            (_keyword = _PyPegen_expect_token(p, 652))  // token='try'
             &&
             (_literal = _PyPegen_expect_forced_token(p, 11, ":"))  // forced_token=':'
             &&
@@ -7003,7 +7023,7 @@ try_stmt_rule(Parser *p)
         asdl_excepthandler_seq* ex;
         void *f;
         if (
-            (_keyword = _PyPegen_expect_token(p, 651))  // token='try'
+            (_keyword = _PyPegen_expect_token(p, 652))  // token='try'
             &&
             (_literal = _PyPegen_expect_forced_token(p, 11, ":"))  // forced_token=':'
             &&
@@ -7101,7 +7121,7 @@ except_block_rule(Parser *p)
         asdl_stmt_seq* b;
         expr_ty e;
         if (
-            (_keyword = _PyPegen_expect_token(p, 672))  // token='except'
+            (_keyword = _PyPegen_expect_token(p, 673))  // token='except'
             &&
             (e = expressions_rule(p))  // expressions
             &&
@@ -7145,11 +7165,11 @@ except_block_rule(Parser *p)
         expr_ty e;
         expr_ty t;
         if (
-            (_keyword = _PyPegen_expect_token(p, 672))  // token='except'
+            (_keyword = _PyPegen_expect_token(p, 673))  // token='except'
             &&
             (e = expression_rule(p))  // expression
             &&
-            (_keyword_1 = _PyPegen_expect_token(p, 675))  // token='as'
+            (_keyword_1 = _PyPegen_expect_token(p, 676))  // token='as'
             &&
             (t = _PyPegen_name_token(p))  // NAME
             &&
@@ -7190,7 +7210,7 @@ except_block_rule(Parser *p)
         Token * _literal;
         asdl_stmt_seq* b;
         if (
-            (_keyword = _PyPegen_expect_token(p, 672))  // token='except'
+            (_keyword = _PyPegen_expect_token(p, 673))  // token='except'
             &&
             (_literal = _PyPegen_expect_token(p, 11))  // token=':'
             &&
@@ -7301,7 +7321,7 @@ except_star_block_rule(Parser *p)
         asdl_stmt_seq* b;
         expr_ty e;
         if (
-            (_keyword = _PyPegen_expect_token(p, 672))  // token='except'
+            (_keyword = _PyPegen_expect_token(p, 673))  // token='except'
             &&
             (_literal = _PyPegen_expect_token(p, 16))  // token='*'
             &&
@@ -7348,13 +7368,13 @@ except_star_block_rule(Parser *p)
         expr_ty e;
         expr_ty t;
         if (
-            (_keyword = _PyPegen_expect_token(p, 672))  // token='except'
+            (_keyword = _PyPegen_expect_token(p, 673))  // token='except'
             &&
             (_literal = _PyPegen_expect_token(p, 16))  // token='*'
             &&
             (e = expression_rule(p))  // expression
             &&
-            (_keyword_1 = _PyPegen_expect_token(p, 675))  // token='as'
+            (_keyword_1 = _PyPegen_expect_token(p, 676))  // token='as'
             &&
             (t = _PyPegen_name_token(p))  // NAME
             &&
@@ -7452,7 +7472,7 @@ finally_block_rule(Parser *p)
         Token * _literal;
         asdl_stmt_seq* a;
         if (
-            (_keyword = _PyPegen_expect_token(p, 668))  // token='finally'
+            (_keyword = _PyPegen_expect_token(p, 669))  // token='finally'
             &&
             (_literal = _PyPegen_expect_forced_token(p, 11, ":"))  // forced_token=':'
             &&
@@ -7760,7 +7780,7 @@ guard_rule(Parser *p)
         Token * _keyword;
         expr_ty guard;
         if (
-            (_keyword = _PyPegen_expect_token(p, 677))  // token='if'
+            (_keyword = _PyPegen_expect_token(p, 678))  // token='if'
             &&
             (guard = named_expression_rule(p))  // named_expression
         )
@@ -7955,7 +7975,7 @@ as_pattern_rule(Parser *p)
         if (
             (pattern = or_pattern_rule(p))  // or_pattern
             &&
-            (_keyword = _PyPegen_expect_token(p, 675))  // token='as'
+            (_keyword = _PyPegen_expect_token(p, 676))  // token='as'
             &&
             (target = pattern_capture_target_rule(p))  // pattern_capture_target
         )
@@ -8389,7 +8409,7 @@ literal_pattern_rule(Parser *p)
         D(fprintf(stderr, "%*c> literal_pattern[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'None'"));
         Token * _keyword;
         if (
-            (_keyword = _PyPegen_expect_token(p, 620))  // token='None'
+            (_keyword = _PyPegen_expect_token(p, 621))  // token='None'
         )
         {
             D(fprintf(stderr, "%*c+ literal_pattern[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'None'"));
@@ -8422,7 +8442,7 @@ literal_pattern_rule(Parser *p)
         D(fprintf(stderr, "%*c> literal_pattern[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'True'"));
         Token * _keyword;
         if (
-            (_keyword = _PyPegen_expect_token(p, 619))  // token='True'
+            (_keyword = _PyPegen_expect_token(p, 620))  // token='True'
         )
         {
             D(fprintf(stderr, "%*c+ literal_pattern[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'True'"));
@@ -8455,7 +8475,7 @@ literal_pattern_rule(Parser *p)
         D(fprintf(stderr, "%*c> literal_pattern[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'False'"));
         Token * _keyword;
         if (
-            (_keyword = _PyPegen_expect_token(p, 621))  // token='False'
+            (_keyword = _PyPegen_expect_token(p, 622))  // token='False'
         )
         {
             D(fprintf(stderr, "%*c+ literal_pattern[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'False'"));
@@ -8489,7 +8509,7 @@ literal_pattern_rule(Parser *p)
 // literal_expr:
 //     | signed_number !('+' | '-')
 //     | complex_number
-//     | strings
+//     | &(STRING | FSTRING_START | TSTRING_START) strings
 //     | 'None'
 //     | 'True'
 //     | 'False'
@@ -8554,24 +8574,26 @@ literal_expr_rule(Parser *p)
         D(fprintf(stderr, "%*c%s literal_expr[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "complex_number"));
     }
-    { // strings
+    { // &(STRING | FSTRING_START | TSTRING_START) strings
         if (p->error_indicator) {
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> literal_expr[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "strings"));
+        D(fprintf(stderr, "%*c> literal_expr[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "&(STRING | FSTRING_START | TSTRING_START) strings"));
         expr_ty strings_var;
         if (
+            _PyPegen_lookahead(1, (void *(*)(Parser *)) _tmp_43_rule, p)
+            &&
             (strings_var = strings_rule(p))  // strings
         )
         {
-            D(fprintf(stderr, "%*c+ literal_expr[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "strings"));
+            D(fprintf(stderr, "%*c+ literal_expr[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "&(STRING | FSTRING_START | TSTRING_START) strings"));
             _res = strings_var;
             goto done;
         }
         p->mark = _mark;
         D(fprintf(stderr, "%*c%s literal_expr[%d-%d]: %s failed!\n", p->level, ' ',
-                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "strings"));
+                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "&(STRING | FSTRING_START | TSTRING_START) strings"));
     }
     { // 'None'
         if (p->error_indicator) {
@@ -8581,7 +8603,7 @@ literal_expr_rule(Parser *p)
         D(fprintf(stderr, "%*c> literal_expr[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'None'"));
         Token * _keyword;
         if (
-            (_keyword = _PyPegen_expect_token(p, 620))  // token='None'
+            (_keyword = _PyPegen_expect_token(p, 621))  // token='None'
         )
         {
             D(fprintf(stderr, "%*c+ literal_expr[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'None'"));
@@ -8614,7 +8636,7 @@ literal_expr_rule(Parser *p)
         D(fprintf(stderr, "%*c> literal_expr[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'True'"));
         Token * _keyword;
         if (
-            (_keyword = _PyPegen_expect_token(p, 619))  // token='True'
+            (_keyword = _PyPegen_expect_token(p, 620))  // token='True'
         )
         {
             D(fprintf(stderr, "%*c+ literal_expr[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'True'"));
@@ -8647,7 +8669,7 @@ literal_expr_rule(Parser *p)
         D(fprintf(stderr, "%*c> literal_expr[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'False'"));
         Token * _keyword;
         if (
-            (_keyword = _PyPegen_expect_token(p, 621))  // token='False'
+            (_keyword = _PyPegen_expect_token(p, 622))  // token='False'
         )
         {
             D(fprintf(stderr, "%*c+ literal_expr[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'False'"));
@@ -9124,7 +9146,7 @@ pattern_capture_target_rule(Parser *p)
             &&
             (name = _PyPegen_name_token(p))  // NAME
             &&
-            _PyPegen_lookahead(0, (void *(*)(Parser *)) _tmp_43_rule, p)
+            _PyPegen_lookahead(0, (void *(*)(Parser *)) _tmp_44_rule, p)
         )
         {
             D(fprintf(stderr, "%*c+ pattern_capture_target[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "!\"_\" NAME !('.' | '(' | '=')"));
@@ -9239,7 +9261,7 @@ value_pattern_rule(Parser *p)
         if (
             (attr = attr_rule(p))  // attr
             &&
-            _PyPegen_lookahead(0, (void *(*)(Parser *)) _tmp_43_rule, p)
+            _PyPegen_lookahead(0, (void *(*)(Parser *)) _tmp_44_rule, p)
         )
         {
             D(fprintf(stderr, "%*c+ value_pattern[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "attr !('.' | '(' | '=')"));
@@ -9658,7 +9680,7 @@ maybe_sequence_pattern_rule(Parser *p)
         UNUSED(_opt_var); // Silence compiler warnings
         asdl_seq * patterns;
         if (
-            (patterns = _gather_45_rule(p))  // ','.maybe_star_pattern+
+            (patterns = _gather_46_rule(p))  // ','.maybe_star_pattern+
             &&
             (_opt_var = _PyPegen_expect_token(p, 12), !p->error_indicator)  // ','?
         )
@@ -10066,13 +10088,13 @@ items_pattern_rule(Parser *p)
             return NULL;
         }
         D(fprintf(stderr, "%*c> items_pattern[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','.key_value_pattern+"));
-        asdl_seq * _gather_47_var;
+        asdl_seq * _gather_48_var;
         if (
-            (_gather_47_var = _gather_47_rule(p))  // ','.key_value_pattern+
+            (_gather_48_var = _gather_48_rule(p))  // ','.key_value_pattern+
         )
         {
             D(fprintf(stderr, "%*c+ items_pattern[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','.key_value_pattern+"));
-            _res = _gather_47_var;
+            _res = _gather_48_var;
             goto done;
         }
         p->mark = _mark;
@@ -10108,7 +10130,7 @@ key_value_pattern_rule(Parser *p)
         void *key;
         pattern_ty pattern;
         if (
-            (key = _tmp_48_rule(p))  // literal_expr | attr
+            (key = _tmp_49_rule(p))  // literal_expr | attr
             &&
             (_literal = _PyPegen_expect_token(p, 11))  // token=':'
             &&
@@ -10436,7 +10458,7 @@ positional_patterns_rule(Parser *p)
         D(fprintf(stderr, "%*c> positional_patterns[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','.pattern+"));
         asdl_pattern_seq* args;
         if (
-            (args = (asdl_pattern_seq*)_gather_50_rule(p))  // ','.pattern+
+            (args = (asdl_pattern_seq*)_gather_51_rule(p))  // ','.pattern+
         )
         {
             D(fprintf(stderr, "%*c+ positional_patterns[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','.pattern+"));
@@ -10477,13 +10499,13 @@ keyword_patterns_rule(Parser *p)
             return NULL;
         }
         D(fprintf(stderr, "%*c> keyword_patterns[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','.keyword_pattern+"));
-        asdl_seq * _gather_52_var;
+        asdl_seq * _gather_53_var;
         if (
-            (_gather_52_var = _gather_52_rule(p))  // ','.keyword_pattern+
+            (_gather_53_var = _gather_53_rule(p))  // ','.keyword_pattern+
         )
         {
             D(fprintf(stderr, "%*c+ keyword_patterns[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','.keyword_pattern+"));
-            _res = _gather_52_var;
+            _res = _gather_53_var;
             goto done;
         }
         p->mark = _mark;
@@ -10709,7 +10731,7 @@ type_param_seq_rule(Parser *p)
         UNUSED(_opt_var); // Silence compiler warnings
         asdl_type_param_seq* a;
         if (
-            (a = (asdl_type_param_seq*)_gather_54_rule(p))  // ','.type_param+
+            (a = (asdl_type_param_seq*)_gather_55_rule(p))  // ','.type_param+
             &&
             (_opt_var = _PyPegen_expect_token(p, 12), !p->error_indicator)  // ','?
         )
@@ -11079,7 +11101,7 @@ expressions_rule(Parser *p)
         if (
             (a = expression_rule(p))  // expression
             &&
-            (b = _loop1_55_rule(p))  // ((',' expression))+
+            (b = _loop1_56_rule(p))  // ((',' expression))+
             &&
             (_opt_var = _PyPegen_expect_token(p, 12), !p->error_indicator)  // ','?
         )
@@ -11250,11 +11272,11 @@ expression_rule(Parser *p)
         if (
             (a = disjunction_rule(p))  // disjunction
             &&
-            (_keyword = _PyPegen_expect_token(p, 677))  // token='if'
+            (_keyword = _PyPegen_expect_token(p, 678))  // token='if'
             &&
             (b = disjunction_rule(p))  // disjunction
             &&
-            (_keyword_1 = _PyPegen_expect_token(p, 681))  // token='else'
+            (_keyword_1 = _PyPegen_expect_token(p, 682))  // token='else'
             &&
             (c = expression_rule(p))  // expression
         )
@@ -11360,7 +11382,7 @@ yield_expr_rule(Parser *p)
         if (
             (_keyword = _PyPegen_expect_token(p, 585))  // token='yield'
             &&
-            (_keyword_1 = _PyPegen_expect_token(p, 630))  // token='from'
+            (_keyword_1 = _PyPegen_expect_token(p, 631))  // token='from'
             &&
             (a = expression_rule(p))  // expression
         )
@@ -11467,7 +11489,7 @@ star_expressions_rule(Parser *p)
         if (
             (a = star_expression_rule(p))  // star_expression
             &&
-            (b = _loop1_56_rule(p))  // ((',' star_expression))+
+            (b = _loop1_57_rule(p))  // ((',' star_expression))+
             &&
             (_opt_var = _PyPegen_expect_token(p, 12), !p->error_indicator)  // ','?
         )
@@ -11666,7 +11688,7 @@ star_named_expressions_rule(Parser *p)
         UNUSED(_opt_var); // Silence compiler warnings
         asdl_expr_seq* a;
         if (
-            (a = (asdl_expr_seq*)_gather_58_rule(p))  // ','.star_named_expression+
+            (a = (asdl_expr_seq*)_gather_59_rule(p))  // ','.star_named_expression+
             &&
             (_opt_var = _PyPegen_expect_token(p, 12), !p->error_indicator)  // ','?
         )
@@ -11962,7 +11984,7 @@ disjunction_rule(Parser *p)
         if (
             (a = conjunction_rule(p))  // conjunction
             &&
-            (b = _loop1_59_rule(p))  // (('or' conjunction))+
+            (b = _loop1_60_rule(p))  // (('or' conjunction))+
         )
         {
             D(fprintf(stderr, "%*c+ disjunction[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "conjunction (('or' conjunction))+"));
@@ -12050,7 +12072,7 @@ conjunction_rule(Parser *p)
         if (
             (a = inversion_rule(p))  // inversion
             &&
-            (b = _loop1_60_rule(p))  // (('and' inversion))+
+            (b = _loop1_61_rule(p))  // (('and' inversion))+
         )
         {
             D(fprintf(stderr, "%*c+ conjunction[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "inversion (('and' inversion))+"));
@@ -12136,7 +12158,7 @@ inversion_rule(Parser *p)
         Token * _keyword;
         expr_ty a;
         if (
-            (_keyword = _PyPegen_expect_token(p, 698))  // token='not'
+            (_keyword = _PyPegen_expect_token(p, 699))  // token='not'
             &&
             (a = inversion_rule(p))  // inversion
         )
@@ -12222,7 +12244,7 @@ comparison_rule(Parser *p)
         if (
             (a = bitwise_or_rule(p))  // bitwise_or
             &&
-            (b = _loop1_61_rule(p))  // compare_op_bitwise_or_pair+
+            (b = _loop1_62_rule(p))  // compare_op_bitwise_or_pair+
         )
         {
             D(fprintf(stderr, "%*c+ comparison[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "bitwise_or compare_op_bitwise_or_pair+"));
@@ -12556,10 +12578,10 @@ noteq_bitwise_or_rule(Parser *p)
             return NULL;
         }
         D(fprintf(stderr, "%*c> noteq_bitwise_or[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('!=') bitwise_or"));
-        void *_tmp_62_var;
+        void *_tmp_63_var;
         expr_ty a;
         if (
-            (_tmp_62_var = _tmp_62_rule(p))  // '!='
+            (_tmp_63_var = _tmp_63_rule(p))  // '!='
             &&
             (a = bitwise_or_rule(p))  // bitwise_or
         )
@@ -12790,9 +12812,9 @@ notin_bitwise_or_rule(Parser *p)
         Token * _keyword_1;
         expr_ty a;
         if (
-            (_keyword = _PyPegen_expect_token(p, 698))  // token='not'
+            (_keyword = _PyPegen_expect_token(p, 699))  // token='not'
             &&
-            (_keyword_1 = _PyPegen_expect_token(p, 690))  // token='in'
+            (_keyword_1 = _PyPegen_expect_token(p, 691))  // token='in'
             &&
             (a = bitwise_or_rule(p))  // bitwise_or
         )
@@ -12838,7 +12860,7 @@ in_bitwise_or_rule(Parser *p)
         Token * _keyword;
         expr_ty a;
         if (
-            (_keyword = _PyPegen_expect_token(p, 690))  // token='in'
+            (_keyword = _PyPegen_expect_token(p, 691))  // token='in'
             &&
             (a = bitwise_or_rule(p))  // bitwise_or
         )
@@ -12887,7 +12909,7 @@ isnot_bitwise_or_rule(Parser *p)
         if (
             (_keyword = _PyPegen_expect_token(p, 594))  // token='is'
             &&
-            (_keyword_1 = _PyPegen_expect_token(p, 698))  // token='not'
+            (_keyword_1 = _PyPegen_expect_token(p, 699))  // token='not'
             &&
             (a = bitwise_or_rule(p))  // bitwise_or
         )
@@ -14607,7 +14629,7 @@ slices_rule(Parser *p)
         UNUSED(_opt_var); // Silence compiler warnings
         asdl_expr_seq* a;
         if (
-            (a = (asdl_expr_seq*)_gather_64_rule(p))  // ','.(slice | starred_expression)+
+            (a = (asdl_expr_seq*)_gather_65_rule(p))  // ','.(slice | starred_expression)+
             &&
             (_opt_var = _PyPegen_expect_token(p, 12), !p->error_indicator)  // ','?
         )
@@ -14679,7 +14701,7 @@ slice_rule(Parser *p)
             &&
             (b = expression_rule(p), !p->error_indicator)  // expression?
             &&
-            (c = _tmp_65_rule(p), !p->error_indicator)  // [':' expression?]
+            (c = _tmp_66_rule(p), !p->error_indicator)  // [':' expression?]
         )
         {
             D(fprintf(stderr, "%*c+ slice[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression? ':' expression? [':' expression?]"));
@@ -14739,7 +14761,7 @@ slice_rule(Parser *p)
 //     | 'True'
 //     | 'False'
 //     | 'None'
-//     | &(STRING | FSTRING_START) strings
+//     | &(STRING | FSTRING_START | TSTRING_START) strings
 //     | NUMBER
 //     | &'(' (tuple | group | genexp)
 //     | &'[' (list | listcomp)
@@ -14793,7 +14815,7 @@ atom_rule(Parser *p)
         D(fprintf(stderr, "%*c> atom[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'True'"));
         Token * _keyword;
         if (
-            (_keyword = _PyPegen_expect_token(p, 619))  // token='True'
+            (_keyword = _PyPegen_expect_token(p, 620))  // token='True'
         )
         {
             D(fprintf(stderr, "%*c+ atom[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'True'"));
@@ -14826,7 +14848,7 @@ atom_rule(Parser *p)
         D(fprintf(stderr, "%*c> atom[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'False'"));
         Token * _keyword;
         if (
-            (_keyword = _PyPegen_expect_token(p, 621))  // token='False'
+            (_keyword = _PyPegen_expect_token(p, 622))  // token='False'
         )
         {
             D(fprintf(stderr, "%*c+ atom[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'False'"));
@@ -14859,7 +14881,7 @@ atom_rule(Parser *p)
         D(fprintf(stderr, "%*c> atom[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'None'"));
         Token * _keyword;
         if (
-            (_keyword = _PyPegen_expect_token(p, 620))  // token='None'
+            (_keyword = _PyPegen_expect_token(p, 621))  // token='None'
         )
         {
             D(fprintf(stderr, "%*c+ atom[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'None'"));
@@ -14884,26 +14906,26 @@ atom_rule(Parser *p)
         D(fprintf(stderr, "%*c%s atom[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'None'"));
     }
-    { // &(STRING | FSTRING_START) strings
+    { // &(STRING | FSTRING_START | TSTRING_START) strings
         if (p->error_indicator) {
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> atom[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "&(STRING | FSTRING_START) strings"));
+        D(fprintf(stderr, "%*c> atom[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "&(STRING | FSTRING_START | TSTRING_START) strings"));
         expr_ty strings_var;
         if (
-            _PyPegen_lookahead(1, (void *(*)(Parser *)) _tmp_66_rule, p)
+            _PyPegen_lookahead(1, (void *(*)(Parser *)) _tmp_43_rule, p)
             &&
             (strings_var = strings_rule(p))  // strings
         )
         {
-            D(fprintf(stderr, "%*c+ atom[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "&(STRING | FSTRING_START) strings"));
+            D(fprintf(stderr, "%*c+ atom[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "&(STRING | FSTRING_START | TSTRING_START) strings"));
             _res = strings_var;
             goto done;
         }
         p->mark = _mark;
         D(fprintf(stderr, "%*c%s atom[%d-%d]: %s failed!\n", p->level, ' ',
-                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "&(STRING | FSTRING_START) strings"));
+                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "&(STRING | FSTRING_START | TSTRING_START) strings"));
     }
     { // NUMBER
         if (p->error_indicator) {
@@ -15127,7 +15149,7 @@ lambdef_rule(Parser *p)
         void *a;
         expr_ty b;
         if (
-            (_keyword = _PyPegen_expect_token(p, 618))  // token='lambda'
+            (_keyword = _PyPegen_expect_token(p, 619))  // token='lambda'
             &&
             (a = lambda_params_rule(p), !p->error_indicator)  // lambda_params?
             &&
@@ -16105,7 +16127,7 @@ fstring_middle_rule(Parser *p)
 
 // fstring_replacement_field:
 //     | '{' annotated_rhs '='? fstring_conversion? fstring_full_format_spec? '}'
-//     | invalid_replacement_field
+//     | invalid_fstring_replacement_field
 static expr_ty
 fstring_replacement_field_rule(Parser *p)
 {
@@ -16175,24 +16197,24 @@ fstring_replacement_field_rule(Parser *p)
         D(fprintf(stderr, "%*c%s fstring_replacement_field[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'{' annotated_rhs '='? fstring_conversion? fstring_full_format_spec? '}'"));
     }
-    if (p->call_invalid_rules) { // invalid_replacement_field
+    if (p->call_invalid_rules) { // invalid_fstring_replacement_field
         if (p->error_indicator) {
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> fstring_replacement_field[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "invalid_replacement_field"));
-        void *invalid_replacement_field_var;
+        D(fprintf(stderr, "%*c> fstring_replacement_field[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "invalid_fstring_replacement_field"));
+        void *invalid_fstring_replacement_field_var;
         if (
-            (invalid_replacement_field_var = invalid_replacement_field_rule(p))  // invalid_replacement_field
+            (invalid_fstring_replacement_field_var = invalid_fstring_replacement_field_rule(p))  // invalid_fstring_replacement_field
         )
         {
-            D(fprintf(stderr, "%*c+ fstring_replacement_field[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "invalid_replacement_field"));
-            _res = invalid_replacement_field_var;
+            D(fprintf(stderr, "%*c+ fstring_replacement_field[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "invalid_fstring_replacement_field"));
+            _res = invalid_fstring_replacement_field_var;
             goto done;
         }
         p->mark = _mark;
         D(fprintf(stderr, "%*c%s fstring_replacement_field[%d-%d]: %s failed!\n", p->level, ' ',
-                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "invalid_replacement_field"));
+                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "invalid_fstring_replacement_field"));
     }
     _res = NULL;
   done:
@@ -16421,6 +16443,442 @@ fstring_rule(Parser *p)
     return _res;
 }
 
+// tstring_format_spec_replacement_field:
+//     | '{' annotated_rhs '='? fstring_conversion? tstring_full_format_spec? '}'
+//     | invalid_tstring_replacement_field
+static expr_ty
+tstring_format_spec_replacement_field_rule(Parser *p)
+{
+    if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
+        _Pypegen_stack_overflow(p);
+    }
+    if (p->error_indicator) {
+        p->level--;
+        return NULL;
+    }
+    expr_ty _res = NULL;
+    int _mark = p->mark;
+    if (p->mark == p->fill && _PyPegen_fill_token(p) < 0) {
+        p->error_indicator = 1;
+        p->level--;
+        return NULL;
+    }
+    int _start_lineno = p->tokens[_mark]->lineno;
+    UNUSED(_start_lineno); // Only used by EXTRA macro
+    int _start_col_offset = p->tokens[_mark]->col_offset;
+    UNUSED(_start_col_offset); // Only used by EXTRA macro
+    { // '{' annotated_rhs '='? fstring_conversion? tstring_full_format_spec? '}'
+        if (p->error_indicator) {
+            p->level--;
+            return NULL;
+        }
+        D(fprintf(stderr, "%*c> tstring_format_spec_replacement_field[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{' annotated_rhs '='? fstring_conversion? tstring_full_format_spec? '}'"));
+        Token * _literal;
+        expr_ty a;
+        void *conversion;
+        void *debug_expr;
+        void *format;
+        Token * rbrace;
+        if (
+            (_literal = _PyPegen_expect_token(p, 25))  // token='{'
+            &&
+            (a = annotated_rhs_rule(p))  // annotated_rhs
+            &&
+            (debug_expr = _PyPegen_expect_token(p, 22), !p->error_indicator)  // '='?
+            &&
+            (conversion = fstring_conversion_rule(p), !p->error_indicator)  // fstring_conversion?
+            &&
+            (format = tstring_full_format_spec_rule(p), !p->error_indicator)  // tstring_full_format_spec?
+            &&
+            (rbrace = _PyPegen_expect_token(p, 26))  // token='}'
+        )
+        {
+            D(fprintf(stderr, "%*c+ tstring_format_spec_replacement_field[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' annotated_rhs '='? fstring_conversion? tstring_full_format_spec? '}'"));
+            Token *_token = _PyPegen_get_last_nonnwhitespace_token(p);
+            if (_token == NULL) {
+                p->level--;
+                return NULL;
+            }
+            int _end_lineno = _token->end_lineno;
+            UNUSED(_end_lineno); // Only used by EXTRA macro
+            int _end_col_offset = _token->end_col_offset;
+            UNUSED(_end_col_offset); // Only used by EXTRA macro
+            _res = _PyPegen_formatted_value ( p , a , debug_expr , conversion , format , rbrace , EXTRA );
+            if (_res == NULL && PyErr_Occurred()) {
+                p->error_indicator = 1;
+                p->level--;
+                return NULL;
+            }
+            goto done;
+        }
+        p->mark = _mark;
+        D(fprintf(stderr, "%*c%s tstring_format_spec_replacement_field[%d-%d]: %s failed!\n", p->level, ' ',
+                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'{' annotated_rhs '='? fstring_conversion? tstring_full_format_spec? '}'"));
+    }
+    if (p->call_invalid_rules) { // invalid_tstring_replacement_field
+        if (p->error_indicator) {
+            p->level--;
+            return NULL;
+        }
+        D(fprintf(stderr, "%*c> tstring_format_spec_replacement_field[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "invalid_tstring_replacement_field"));
+        void *invalid_tstring_replacement_field_var;
+        if (
+            (invalid_tstring_replacement_field_var = invalid_tstring_replacement_field_rule(p))  // invalid_tstring_replacement_field
+        )
+        {
+            D(fprintf(stderr, "%*c+ tstring_format_spec_replacement_field[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "invalid_tstring_replacement_field"));
+            _res = invalid_tstring_replacement_field_var;
+            goto done;
+        }
+        p->mark = _mark;
+        D(fprintf(stderr, "%*c%s tstring_format_spec_replacement_field[%d-%d]: %s failed!\n", p->level, ' ',
+                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "invalid_tstring_replacement_field"));
+    }
+    _res = NULL;
+  done:
+    p->level--;
+    return _res;
+}
+
+// tstring_format_spec: TSTRING_MIDDLE | tstring_format_spec_replacement_field
+static expr_ty
+tstring_format_spec_rule(Parser *p)
+{
+    if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
+        _Pypegen_stack_overflow(p);
+    }
+    if (p->error_indicator) {
+        p->level--;
+        return NULL;
+    }
+    expr_ty _res = NULL;
+    int _mark = p->mark;
+    { // TSTRING_MIDDLE
+        if (p->error_indicator) {
+            p->level--;
+            return NULL;
+        }
+        D(fprintf(stderr, "%*c> tstring_format_spec[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "TSTRING_MIDDLE"));
+        Token * t;
+        if (
+            (t = _PyPegen_expect_token(p, TSTRING_MIDDLE))  // token='TSTRING_MIDDLE'
+        )
+        {
+            D(fprintf(stderr, "%*c+ tstring_format_spec[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "TSTRING_MIDDLE"));
+            _res = _PyPegen_decoded_constant_from_token ( p , t );
+            if (_res == NULL && PyErr_Occurred()) {
+                p->error_indicator = 1;
+                p->level--;
+                return NULL;
+            }
+            goto done;
+        }
+        p->mark = _mark;
+        D(fprintf(stderr, "%*c%s tstring_format_spec[%d-%d]: %s failed!\n", p->level, ' ',
+                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "TSTRING_MIDDLE"));
+    }
+    { // tstring_format_spec_replacement_field
+        if (p->error_indicator) {
+            p->level--;
+            return NULL;
+        }
+        D(fprintf(stderr, "%*c> tstring_format_spec[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "tstring_format_spec_replacement_field"));
+        expr_ty tstring_format_spec_replacement_field_var;
+        if (
+            (tstring_format_spec_replacement_field_var = tstring_format_spec_replacement_field_rule(p))  // tstring_format_spec_replacement_field
+        )
+        {
+            D(fprintf(stderr, "%*c+ tstring_format_spec[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "tstring_format_spec_replacement_field"));
+            _res = tstring_format_spec_replacement_field_var;
+            goto done;
+        }
+        p->mark = _mark;
+        D(fprintf(stderr, "%*c%s tstring_format_spec[%d-%d]: %s failed!\n", p->level, ' ',
+                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "tstring_format_spec_replacement_field"));
+    }
+    _res = NULL;
+  done:
+    p->level--;
+    return _res;
+}
+
+// tstring_full_format_spec: ':' tstring_format_spec*
+static ResultTokenWithMetadata*
+tstring_full_format_spec_rule(Parser *p)
+{
+    if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
+        _Pypegen_stack_overflow(p);
+    }
+    if (p->error_indicator) {
+        p->level--;
+        return NULL;
+    }
+    ResultTokenWithMetadata* _res = NULL;
+    int _mark = p->mark;
+    if (p->mark == p->fill && _PyPegen_fill_token(p) < 0) {
+        p->error_indicator = 1;
+        p->level--;
+        return NULL;
+    }
+    int _start_lineno = p->tokens[_mark]->lineno;
+    UNUSED(_start_lineno); // Only used by EXTRA macro
+    int _start_col_offset = p->tokens[_mark]->col_offset;
+    UNUSED(_start_col_offset); // Only used by EXTRA macro
+    { // ':' tstring_format_spec*
+        if (p->error_indicator) {
+            p->level--;
+            return NULL;
+        }
+        D(fprintf(stderr, "%*c> tstring_full_format_spec[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':' tstring_format_spec*"));
+        Token * colon;
+        asdl_seq * spec;
+        if (
+            (colon = _PyPegen_expect_token(p, 11))  // token=':'
+            &&
+            (spec = _loop0_79_rule(p))  // tstring_format_spec*
+        )
+        {
+            D(fprintf(stderr, "%*c+ tstring_full_format_spec[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':' tstring_format_spec*"));
+            Token *_token = _PyPegen_get_last_nonnwhitespace_token(p);
+            if (_token == NULL) {
+                p->level--;
+                return NULL;
+            }
+            int _end_lineno = _token->end_lineno;
+            UNUSED(_end_lineno); // Only used by EXTRA macro
+            int _end_col_offset = _token->end_col_offset;
+            UNUSED(_end_col_offset); // Only used by EXTRA macro
+            _res = _PyPegen_setup_full_format_spec ( p , colon , ( asdl_expr_seq* ) spec , EXTRA );
+            if (_res == NULL && PyErr_Occurred()) {
+                p->error_indicator = 1;
+                p->level--;
+                return NULL;
+            }
+            goto done;
+        }
+        p->mark = _mark;
+        D(fprintf(stderr, "%*c%s tstring_full_format_spec[%d-%d]: %s failed!\n", p->level, ' ',
+                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "':' tstring_format_spec*"));
+    }
+    _res = NULL;
+  done:
+    p->level--;
+    return _res;
+}
+
+// tstring_replacement_field:
+//     | '{' annotated_rhs '='? fstring_conversion? tstring_full_format_spec? '}'
+//     | invalid_tstring_replacement_field
+static expr_ty
+tstring_replacement_field_rule(Parser *p)
+{
+    if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
+        _Pypegen_stack_overflow(p);
+    }
+    if (p->error_indicator) {
+        p->level--;
+        return NULL;
+    }
+    expr_ty _res = NULL;
+    int _mark = p->mark;
+    if (p->mark == p->fill && _PyPegen_fill_token(p) < 0) {
+        p->error_indicator = 1;
+        p->level--;
+        return NULL;
+    }
+    int _start_lineno = p->tokens[_mark]->lineno;
+    UNUSED(_start_lineno); // Only used by EXTRA macro
+    int _start_col_offset = p->tokens[_mark]->col_offset;
+    UNUSED(_start_col_offset); // Only used by EXTRA macro
+    { // '{' annotated_rhs '='? fstring_conversion? tstring_full_format_spec? '}'
+        if (p->error_indicator) {
+            p->level--;
+            return NULL;
+        }
+        D(fprintf(stderr, "%*c> tstring_replacement_field[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{' annotated_rhs '='? fstring_conversion? tstring_full_format_spec? '}'"));
+        Token * _literal;
+        expr_ty a;
+        void *conversion;
+        void *debug_expr;
+        void *format;
+        Token * rbrace;
+        if (
+            (_literal = _PyPegen_expect_token(p, 25))  // token='{'
+            &&
+            (a = annotated_rhs_rule(p))  // annotated_rhs
+            &&
+            (debug_expr = _PyPegen_expect_token(p, 22), !p->error_indicator)  // '='?
+            &&
+            (conversion = fstring_conversion_rule(p), !p->error_indicator)  // fstring_conversion?
+            &&
+            (format = tstring_full_format_spec_rule(p), !p->error_indicator)  // tstring_full_format_spec?
+            &&
+            (rbrace = _PyPegen_expect_token(p, 26))  // token='}'
+        )
+        {
+            D(fprintf(stderr, "%*c+ tstring_replacement_field[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' annotated_rhs '='? fstring_conversion? tstring_full_format_spec? '}'"));
+            Token *_token = _PyPegen_get_last_nonnwhitespace_token(p);
+            if (_token == NULL) {
+                p->level--;
+                return NULL;
+            }
+            int _end_lineno = _token->end_lineno;
+            UNUSED(_end_lineno); // Only used by EXTRA macro
+            int _end_col_offset = _token->end_col_offset;
+            UNUSED(_end_col_offset); // Only used by EXTRA macro
+            _res = _PyPegen_interpolation ( p , a , debug_expr , conversion , format , rbrace , EXTRA );
+            if (_res == NULL && PyErr_Occurred()) {
+                p->error_indicator = 1;
+                p->level--;
+                return NULL;
+            }
+            goto done;
+        }
+        p->mark = _mark;
+        D(fprintf(stderr, "%*c%s tstring_replacement_field[%d-%d]: %s failed!\n", p->level, ' ',
+                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'{' annotated_rhs '='? fstring_conversion? tstring_full_format_spec? '}'"));
+    }
+    if (p->call_invalid_rules) { // invalid_tstring_replacement_field
+        if (p->error_indicator) {
+            p->level--;
+            return NULL;
+        }
+        D(fprintf(stderr, "%*c> tstring_replacement_field[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "invalid_tstring_replacement_field"));
+        void *invalid_tstring_replacement_field_var;
+        if (
+            (invalid_tstring_replacement_field_var = invalid_tstring_replacement_field_rule(p))  // invalid_tstring_replacement_field
+        )
+        {
+            D(fprintf(stderr, "%*c+ tstring_replacement_field[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "invalid_tstring_replacement_field"));
+            _res = invalid_tstring_replacement_field_var;
+            goto done;
+        }
+        p->mark = _mark;
+        D(fprintf(stderr, "%*c%s tstring_replacement_field[%d-%d]: %s failed!\n", p->level, ' ',
+                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "invalid_tstring_replacement_field"));
+    }
+    _res = NULL;
+  done:
+    p->level--;
+    return _res;
+}
+
+// tstring_middle: tstring_replacement_field | TSTRING_MIDDLE
+static expr_ty
+tstring_middle_rule(Parser *p)
+{
+    if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
+        _Pypegen_stack_overflow(p);
+    }
+    if (p->error_indicator) {
+        p->level--;
+        return NULL;
+    }
+    expr_ty _res = NULL;
+    int _mark = p->mark;
+    { // tstring_replacement_field
+        if (p->error_indicator) {
+            p->level--;
+            return NULL;
+        }
+        D(fprintf(stderr, "%*c> tstring_middle[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "tstring_replacement_field"));
+        expr_ty tstring_replacement_field_var;
+        if (
+            (tstring_replacement_field_var = tstring_replacement_field_rule(p))  // tstring_replacement_field
+        )
+        {
+            D(fprintf(stderr, "%*c+ tstring_middle[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "tstring_replacement_field"));
+            _res = tstring_replacement_field_var;
+            goto done;
+        }
+        p->mark = _mark;
+        D(fprintf(stderr, "%*c%s tstring_middle[%d-%d]: %s failed!\n", p->level, ' ',
+                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "tstring_replacement_field"));
+    }
+    { // TSTRING_MIDDLE
+        if (p->error_indicator) {
+            p->level--;
+            return NULL;
+        }
+        D(fprintf(stderr, "%*c> tstring_middle[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "TSTRING_MIDDLE"));
+        Token * t;
+        if (
+            (t = _PyPegen_expect_token(p, TSTRING_MIDDLE))  // token='TSTRING_MIDDLE'
+        )
+        {
+            D(fprintf(stderr, "%*c+ tstring_middle[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "TSTRING_MIDDLE"));
+            _res = _PyPegen_constant_from_token ( p , t );
+            if (_res == NULL && PyErr_Occurred()) {
+                p->error_indicator = 1;
+                p->level--;
+                return NULL;
+            }
+            goto done;
+        }
+        p->mark = _mark;
+        D(fprintf(stderr, "%*c%s tstring_middle[%d-%d]: %s failed!\n", p->level, ' ',
+                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "TSTRING_MIDDLE"));
+    }
+    _res = NULL;
+  done:
+    p->level--;
+    return _res;
+}
+
+// tstring: TSTRING_START tstring_middle* TSTRING_END
+static expr_ty
+tstring_rule(Parser *p)
+{
+    if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
+        _Pypegen_stack_overflow(p);
+    }
+    if (p->error_indicator) {
+        p->level--;
+        return NULL;
+    }
+    expr_ty _res = NULL;
+    if (_PyPegen_is_memoized(p, tstring_type, &_res)) {
+        p->level--;
+        return _res;
+    }
+    int _mark = p->mark;
+    { // TSTRING_START tstring_middle* TSTRING_END
+        if (p->error_indicator) {
+            p->level--;
+            return NULL;
+        }
+        D(fprintf(stderr, "%*c> tstring[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "TSTRING_START tstring_middle* TSTRING_END"));
+        Token * a;
+        asdl_seq * b;
+        Token * c;
+        if (
+            (a = _PyPegen_expect_token(p, TSTRING_START))  // token='TSTRING_START'
+            &&
+            (b = _loop0_80_rule(p))  // tstring_middle*
+            &&
+            (c = _PyPegen_expect_token(p, TSTRING_END))  // token='TSTRING_END'
+        )
+        {
+            D(fprintf(stderr, "%*c+ tstring[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "TSTRING_START tstring_middle* TSTRING_END"));
+            _res = _PyPegen_template_str ( p , a , ( asdl_expr_seq* ) b , c );
+            if (_res == NULL && PyErr_Occurred()) {
+                p->error_indicator = 1;
+                p->level--;
+                return NULL;
+            }
+            goto done;
+        }
+        p->mark = _mark;
+        D(fprintf(stderr, "%*c%s tstring[%d-%d]: %s failed!\n", p->level, ' ',
+                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "TSTRING_START tstring_middle* TSTRING_END"));
+    }
+    _res = NULL;
+  done:
+    _PyPegen_insert_memo(p, _mark, tstring_type, _res);
+    p->level--;
+    return _res;
+}
+
 // string: STRING
 static expr_ty
 string_rule(Parser *p)
@@ -16464,7 +16922,7 @@ string_rule(Parser *p)
     return _res;
 }
 
-// strings: ((fstring | string))+
+// strings: ((fstring | string | tstring))+
 static expr_ty
 strings_rule(Parser *p)
 {
@@ -16490,18 +16948,18 @@ strings_rule(Parser *p)
     UNUSED(_start_lineno); // Only used by EXTRA macro
     int _start_col_offset = p->tokens[_mark]->col_offset;
     UNUSED(_start_col_offset); // Only used by EXTRA macro
-    { // ((fstring | string))+
+    { // ((fstring | string | tstring))+
         if (p->error_indicator) {
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> strings[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "((fstring | string))+"));
+        D(fprintf(stderr, "%*c> strings[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "((fstring | string | tstring))+"));
         asdl_expr_seq* a;
         if (
-            (a = (asdl_expr_seq*)_loop1_79_rule(p))  // ((fstring | string))+
+            (a = (asdl_expr_seq*)_loop1_81_rule(p))  // ((fstring | string | tstring))+
         )
         {
-            D(fprintf(stderr, "%*c+ strings[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "((fstring | string))+"));
+            D(fprintf(stderr, "%*c+ strings[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "((fstring | string | tstring))+"));
             Token *_token = _PyPegen_get_last_nonnwhitespace_token(p);
             if (_token == NULL) {
                 p->level--;
@@ -16521,7 +16979,7 @@ strings_rule(Parser *p)
         }
         p->mark = _mark;
         D(fprintf(stderr, "%*c%s strings[%d-%d]: %s failed!\n", p->level, ' ',
-                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "((fstring | string))+"));
+                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "((fstring | string | tstring))+"));
     }
     _res = NULL;
   done:
@@ -16631,7 +17089,7 @@ tuple_rule(Parser *p)
         if (
             (_literal = _PyPegen_expect_token(p, 7))  // token='('
             &&
-            (a = _tmp_80_rule(p), !p->error_indicator)  // [star_named_expression ',' star_named_expressions?]
+            (a = _tmp_82_rule(p), !p->error_indicator)  // [star_named_expression ',' star_named_expressions?]
             &&
             (_literal_1 = _PyPegen_expect_token(p, 8))  // token=')'
         )
@@ -16846,7 +17304,7 @@ double_starred_kvpairs_rule(Parser *p)
         UNUSED(_opt_var); // Silence compiler warnings
         asdl_seq * a;
         if (
-            (a = _gather_82_rule(p))  // ','.double_starred_kvpair+
+            (a = _gather_84_rule(p))  // ','.double_starred_kvpair+
             &&
             (_opt_var = _PyPegen_expect_token(p, 12), !p->error_indicator)  // ','?
         )
@@ -17005,7 +17463,7 @@ for_if_clauses_rule(Parser *p)
         D(fprintf(stderr, "%*c> for_if_clauses[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "for_if_clause+"));
         asdl_comprehension_seq* a;
         if (
-            (a = (asdl_comprehension_seq*)_loop1_83_rule(p))  // for_if_clause+
+            (a = (asdl_comprehension_seq*)_loop1_85_rule(p))  // for_if_clause+
         )
         {
             D(fprintf(stderr, "%*c+ for_if_clauses[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "for_if_clause+"));
@@ -17058,19 +17516,19 @@ for_if_clause_rule(Parser *p)
         expr_ty b;
         asdl_expr_seq* c;
         if (
-            (_keyword = _PyPegen_expect_token(p, 693))  // token='async'
+            (_keyword = _PyPegen_expect_token(p, 694))  // token='async'
             &&
-            (_keyword_1 = _PyPegen_expect_token(p, 689))  // token='for'
+            (_keyword_1 = _PyPegen_expect_token(p, 690))  // token='for'
             &&
             (a = star_targets_rule(p))  // star_targets
             &&
-            (_keyword_2 = _PyPegen_expect_token(p, 690))  // token='in'
+            (_keyword_2 = _PyPegen_expect_token(p, 691))  // token='in'
             &&
             (_cut_var = 1)
             &&
             (b = disjunction_rule(p))  // disjunction
             &&
-            (c = (asdl_expr_seq*)_loop0_84_rule(p))  // (('if' disjunction))*
+            (c = (asdl_expr_seq*)_loop0_86_rule(p))  // (('if' disjunction))*
         )
         {
             D(fprintf(stderr, "%*c+ for_if_clause[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'async' 'for' star_targets 'in' ~ disjunction (('if' disjunction))*"));
@@ -17103,17 +17561,17 @@ for_if_clause_rule(Parser *p)
         expr_ty b;
         asdl_expr_seq* c;
         if (
-            (_keyword = _PyPegen_expect_token(p, 689))  // token='for'
+            (_keyword = _PyPegen_expect_token(p, 690))  // token='for'
             &&
             (a = star_targets_rule(p))  // star_targets
             &&
-            (_keyword_1 = _PyPegen_expect_token(p, 690))  // token='in'
+            (_keyword_1 = _PyPegen_expect_token(p, 691))  // token='in'
             &&
             (_cut_var = 1)
             &&
             (b = disjunction_rule(p))  // disjunction
             &&
-            (c = (asdl_expr_seq*)_loop0_84_rule(p))  // (('if' disjunction))*
+            (c = (asdl_expr_seq*)_loop0_86_rule(p))  // (('if' disjunction))*
         )
         {
             D(fprintf(stderr, "%*c+ for_if_clause[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'for' star_targets 'in' ~ disjunction (('if' disjunction))*"));
@@ -17392,7 +17850,7 @@ genexp_rule(Parser *p)
         if (
             (_literal = _PyPegen_expect_token(p, 7))  // token='('
             &&
-            (a = _tmp_85_rule(p))  // assignment_expression | expression !':='
+            (a = _tmp_87_rule(p))  // assignment_expression | expression !':='
             &&
             (b = for_if_clauses_rule(p))  // for_if_clauses
             &&
@@ -17641,9 +18099,9 @@ args_rule(Parser *p)
         asdl_expr_seq* a;
         void *b;
         if (
-            (a = (asdl_expr_seq*)_gather_87_rule(p))  // ','.(starred_expression | (assignment_expression | expression !':=') !'=')+
+            (a = (asdl_expr_seq*)_gather_89_rule(p))  // ','.(starred_expression | (assignment_expression | expression !':=') !'=')+
             &&
-            (b = _tmp_88_rule(p), !p->error_indicator)  // [',' kwargs]
+            (b = _tmp_90_rule(p), !p->error_indicator)  // [',' kwargs]
         )
         {
             D(fprintf(stderr, "%*c+ args[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','.(starred_expression | (assignment_expression | expression !':=') !'=')+ [',' kwargs]"));
@@ -17733,11 +18191,11 @@ kwargs_rule(Parser *p)
         asdl_seq * a;
         asdl_seq * b;
         if (
-            (a = _gather_90_rule(p))  // ','.kwarg_or_starred+
+            (a = _gather_92_rule(p))  // ','.kwarg_or_starred+
             &&
             (_literal = _PyPegen_expect_token(p, 12))  // token=','
             &&
-            (b = _gather_92_rule(p))  // ','.kwarg_or_double_starred+
+            (b = _gather_94_rule(p))  // ','.kwarg_or_double_starred+
         )
         {
             D(fprintf(stderr, "%*c+ kwargs[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','.kwarg_or_starred+ ',' ','.kwarg_or_double_starred+"));
@@ -17759,13 +18217,13 @@ kwargs_rule(Parser *p)
             return NULL;
         }
         D(fprintf(stderr, "%*c> kwargs[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','.kwarg_or_starred+"));
-        asdl_seq * _gather_90_var;
+        asdl_seq * _gather_92_var;
         if (
-            (_gather_90_var = _gather_90_rule(p))  // ','.kwarg_or_starred+
+            (_gather_92_var = _gather_92_rule(p))  // ','.kwarg_or_starred+
         )
         {
             D(fprintf(stderr, "%*c+ kwargs[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','.kwarg_or_starred+"));
-            _res = _gather_90_var;
+            _res = _gather_92_var;
             goto done;
         }
         p->mark = _mark;
@@ -17778,13 +18236,13 @@ kwargs_rule(Parser *p)
             return NULL;
         }
         D(fprintf(stderr, "%*c> kwargs[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','.kwarg_or_double_starred+"));
-        asdl_seq * _gather_92_var;
+        asdl_seq * _gather_94_var;
         if (
-            (_gather_92_var = _gather_92_rule(p))  // ','.kwarg_or_double_starred+
+            (_gather_94_var = _gather_94_rule(p))  // ','.kwarg_or_double_starred+
         )
         {
             D(fprintf(stderr, "%*c+ kwargs[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','.kwarg_or_double_starred+"));
-            _res = _gather_92_var;
+            _res = _gather_94_var;
             goto done;
         }
         p->mark = _mark;
@@ -18195,7 +18653,7 @@ star_targets_rule(Parser *p)
         if (
             (a = star_target_rule(p))  // star_target
             &&
-            (b = _loop0_93_rule(p))  // ((',' star_target))*
+            (b = _loop0_95_rule(p))  // ((',' star_target))*
             &&
             (_opt_var = _PyPegen_expect_token(p, 12), !p->error_indicator)  // ','?
         )
@@ -18251,7 +18709,7 @@ star_targets_list_seq_rule(Parser *p)
         UNUSED(_opt_var); // Silence compiler warnings
         asdl_expr_seq* a;
         if (
-            (a = (asdl_expr_seq*)_gather_95_rule(p))  // ','.star_target+
+            (a = (asdl_expr_seq*)_gather_97_rule(p))  // ','.star_target+
             &&
             (_opt_var = _PyPegen_expect_token(p, 12), !p->error_indicator)  // ','?
         )
@@ -18301,7 +18759,7 @@ star_targets_tuple_seq_rule(Parser *p)
         if (
             (a = star_target_rule(p))  // star_target
             &&
-            (b = _loop1_96_rule(p))  // ((',' star_target))+
+            (b = _loop1_98_rule(p))  // ((',' star_target))+
             &&
             (_opt_var = _PyPegen_expect_token(p, 12), !p->error_indicator)  // ','?
         )
@@ -18389,7 +18847,7 @@ star_target_rule(Parser *p)
         if (
             (_literal = _PyPegen_expect_token(p, 16))  // token='*'
             &&
-            (a = _tmp_97_rule(p))  // !'*' star_target
+            (a = _tmp_99_rule(p))  // !'*' star_target
         )
         {
             D(fprintf(stderr, "%*c+ star_target[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'*' (!'*' star_target)"));
@@ -19312,7 +19770,7 @@ del_targets_rule(Parser *p)
         UNUSED(_opt_var); // Silence compiler warnings
         asdl_expr_seq* a;
         if (
-            (a = (asdl_expr_seq*)_gather_99_rule(p))  // ','.del_target+
+            (a = (asdl_expr_seq*)_gather_101_rule(p))  // ','.del_target+
             &&
             (_opt_var = _PyPegen_expect_token(p, 12), !p->error_indicator)  // ','?
         )
@@ -19670,7 +20128,7 @@ type_expressions_rule(Parser *p)
         expr_ty b;
         expr_ty c;
         if (
-            (a = _gather_101_rule(p))  // ','.expression+
+            (a = _gather_103_rule(p))  // ','.expression+
             &&
             (_literal = _PyPegen_expect_token(p, 12))  // token=','
             &&
@@ -19709,7 +20167,7 @@ type_expressions_rule(Parser *p)
         asdl_seq * a;
         expr_ty b;
         if (
-            (a = _gather_101_rule(p))  // ','.expression+
+            (a = _gather_103_rule(p))  // ','.expression+
             &&
             (_literal = _PyPegen_expect_token(p, 12))  // token=','
             &&
@@ -19742,7 +20200,7 @@ type_expressions_rule(Parser *p)
         asdl_seq * a;
         expr_ty b;
         if (
-            (a = _gather_101_rule(p))  // ','.expression+
+            (a = _gather_103_rule(p))  // ','.expression+
             &&
             (_literal = _PyPegen_expect_token(p, 12))  // token=','
             &&
@@ -19862,7 +20320,7 @@ type_expressions_rule(Parser *p)
         D(fprintf(stderr, "%*c> type_expressions[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','.expression+"));
         asdl_expr_seq* a;
         if (
-            (a = (asdl_expr_seq*)_gather_101_rule(p))  // ','.expression+
+            (a = (asdl_expr_seq*)_gather_103_rule(p))  // ','.expression+
         )
         {
             D(fprintf(stderr, "%*c+ type_expressions[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','.expression+"));
@@ -19913,7 +20371,7 @@ func_type_comment_rule(Parser *p)
             &&
             (t = _PyPegen_expect_token(p, TYPE_COMMENT))  // token='TYPE_COMMENT'
             &&
-            _PyPegen_lookahead(1, (void *(*)(Parser *)) _tmp_102_rule, p)
+            _PyPegen_lookahead(1, (void *(*)(Parser *)) _tmp_104_rule, p)
         )
         {
             D(fprintf(stderr, "%*c+ func_type_comment[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "NEWLINE TYPE_COMMENT &(NEWLINE INDENT)"));
@@ -19999,15 +20457,15 @@ invalid_arguments_rule(Parser *p)
             return NULL;
         }
         D(fprintf(stderr, "%*c> invalid_arguments[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "((','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs) | kwargs) ',' ','.(starred_expression !'=')+"));
-        asdl_seq * _gather_105_var;
-        void *_tmp_103_var;
+        asdl_seq * _gather_107_var;
+        void *_tmp_105_var;
         Token * a;
         if (
-            (_tmp_103_var = _tmp_103_rule(p))  // (','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs) | kwargs
+            (_tmp_105_var = _tmp_105_rule(p))  // (','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs) | kwargs
             &&
             (a = _PyPegen_expect_token(p, 12))  // token=','
             &&
-            (_gather_105_var = _gather_105_rule(p))  // ','.(starred_expression !'=')+
+            (_gather_107_var = _gather_107_rule(p))  // ','.(starred_expression !'=')+
         )
         {
             D(fprintf(stderr, "%*c+ invalid_arguments[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "((','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs) | kwargs) ',' ','.(starred_expression !'=')+"));
@@ -20041,7 +20499,7 @@ invalid_arguments_rule(Parser *p)
             &&
             (_literal = _PyPegen_expect_token(p, 12))  // token=','
             &&
-            (_opt_var = _tmp_106_rule(p), !p->error_indicator)  // [args | expression for_if_clauses]
+            (_opt_var = _tmp_108_rule(p), !p->error_indicator)  // [args | expression for_if_clauses]
         )
         {
             D(fprintf(stderr, "%*c+ invalid_arguments[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression for_if_clauses ',' [args | expression for_if_clauses]"));
@@ -20101,13 +20559,13 @@ invalid_arguments_rule(Parser *p)
         expr_ty a;
         Token * b;
         if (
-            (_opt_var = _tmp_107_rule(p), !p->error_indicator)  // [(args ',')]
+            (_opt_var = _tmp_109_rule(p), !p->error_indicator)  // [(args ',')]
             &&
             (a = _PyPegen_name_token(p))  // NAME
             &&
             (b = _PyPegen_expect_token(p, 22))  // token='='
             &&
-            _PyPegen_lookahead(1, (void *(*)(Parser *)) _tmp_108_rule, p)
+            _PyPegen_lookahead(1, (void *(*)(Parser *)) _tmp_110_rule, p)
         )
         {
             D(fprintf(stderr, "%*c+ invalid_arguments[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "[(args ',')] NAME '=' &(',' | ')')"));
@@ -20245,7 +20703,7 @@ invalid_kwarg_rule(Parser *p)
         Token* a;
         Token * b;
         if (
-            (a = (Token*)_tmp_109_rule(p))  // 'True' | 'False' | 'None'
+            (a = (Token*)_tmp_111_rule(p))  // 'True' | 'False' | 'None'
             &&
             (b = _PyPegen_expect_token(p, 22))  // token='='
         )
@@ -20305,7 +20763,7 @@ invalid_kwarg_rule(Parser *p)
         expr_ty a;
         Token * b;
         if (
-            _PyPegen_lookahead(0, (void *(*)(Parser *)) _tmp_110_rule, p)
+            _PyPegen_lookahead(0, (void *(*)(Parser *)) _tmp_112_rule, p)
             &&
             (a = expression_rule(p))  // expression
             &&
@@ -20408,11 +20866,11 @@ expression_without_invalid_rule(Parser *p)
         if (
             (a = disjunction_rule(p))  // disjunction
             &&
-            (_keyword = _PyPegen_expect_token(p, 677))  // token='if'
+            (_keyword = _PyPegen_expect_token(p, 678))  // token='if'
             &&
             (b = disjunction_rule(p))  // disjunction
             &&
-            (_keyword_1 = _PyPegen_expect_token(p, 681))  // token='else'
+            (_keyword_1 = _PyPegen_expect_token(p, 682))  // token='else'
             &&
             (c = expression_rule(p))  // expression
         )
@@ -20628,6 +21086,7 @@ invalid_type_param_rule(Parser *p)
 //     | disjunction 'if' disjunction 'else' !expression
 //     | (pass_stmt | break_stmt | continue_stmt) 'if' disjunction 'else' simple_stmt
 //     | 'lambda' lambda_params? ':' &FSTRING_MIDDLE
+//     | 'lambda' lambda_params? ':' &TSTRING_MIDDLE
 static void *
 invalid_expression_rule(Parser *p)
 {
@@ -20652,7 +21111,7 @@ invalid_expression_rule(Parser *p)
         if (
             (string_var = _PyPegen_string_token(p))  // STRING
             &&
-            (a = _loop1_111_rule(p))  // ((!STRING expression_without_invalid))+
+            (a = _loop1_113_rule(p))  // ((!STRING expression_without_invalid))+
             &&
             (string_var_1 = _PyPegen_string_token(p))  // STRING
         )
@@ -20679,7 +21138,7 @@ invalid_expression_rule(Parser *p)
         expr_ty a;
         expr_ty b;
         if (
-            _PyPegen_lookahead(0, (void *(*)(Parser *)) _tmp_112_rule, p)
+            _PyPegen_lookahead(0, (void *(*)(Parser *)) _tmp_114_rule, p)
             &&
             (a = disjunction_rule(p))  // disjunction
             &&
@@ -20711,11 +21170,11 @@ invalid_expression_rule(Parser *p)
         if (
             (a = disjunction_rule(p))  // disjunction
             &&
-            (_keyword = _PyPegen_expect_token(p, 677))  // token='if'
+            (_keyword = _PyPegen_expect_token(p, 678))  // token='if'
             &&
             (b = disjunction_rule(p))  // disjunction
             &&
-            _PyPegen_lookahead(0, (void *(*)(Parser *)) _tmp_113_rule, p)
+            _PyPegen_lookahead(0, (void *(*)(Parser *)) _tmp_115_rule, p)
         )
         {
             D(fprintf(stderr, "%*c+ invalid_expression[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "disjunction 'if' disjunction !('else' | ':')"));
@@ -20744,11 +21203,11 @@ invalid_expression_rule(Parser *p)
         if (
             (a = disjunction_rule(p))  // disjunction
             &&
-            (_keyword = _PyPegen_expect_token(p, 677))  // token='if'
+            (_keyword = _PyPegen_expect_token(p, 678))  // token='if'
             &&
             (b = disjunction_rule(p))  // disjunction
             &&
-            (_keyword_1 = _PyPegen_expect_token(p, 681))  // token='else'
+            (_keyword_1 = _PyPegen_expect_token(p, 682))  // token='else'
             &&
             _PyPegen_lookahead(0, (void *(*)(Parser *)) expression_rule, p)
         )
@@ -20778,13 +21237,13 @@ invalid_expression_rule(Parser *p)
         expr_ty b;
         stmt_ty c;
         if (
-            (a = (stmt_ty)_tmp_114_rule(p))  // pass_stmt | break_stmt | continue_stmt
+            (a = (stmt_ty)_tmp_116_rule(p))  // pass_stmt | break_stmt | continue_stmt
             &&
-            (_keyword = _PyPegen_expect_token(p, 677))  // token='if'
+            (_keyword = _PyPegen_expect_token(p, 678))  // token='if'
             &&
             (b = disjunction_rule(p))  // disjunction
             &&
-            (_keyword_1 = _PyPegen_expect_token(p, 681))  // token='else'
+            (_keyword_1 = _PyPegen_expect_token(p, 682))  // token='else'
             &&
             (c = simple_stmt_rule(p))  // simple_stmt
         )
@@ -20813,7 +21272,7 @@ invalid_expression_rule(Parser *p)
         Token * a;
         Token * b;
         if (
-            (a = _PyPegen_expect_token(p, 618))  // token='lambda'
+            (a = _PyPegen_expect_token(p, 619))  // token='lambda'
             &&
             (_opt_var = lambda_params_rule(p), !p->error_indicator)  // lambda_params?
             &&
@@ -20835,6 +21294,39 @@ invalid_expression_rule(Parser *p)
         D(fprintf(stderr, "%*c%s invalid_expression[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'lambda' lambda_params? ':' &FSTRING_MIDDLE"));
     }
+    { // 'lambda' lambda_params? ':' &TSTRING_MIDDLE
+        if (p->error_indicator) {
+            p->level--;
+            return NULL;
+        }
+        D(fprintf(stderr, "%*c> invalid_expression[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'lambda' lambda_params? ':' &TSTRING_MIDDLE"));
+        void *_opt_var;
+        UNUSED(_opt_var); // Silence compiler warnings
+        Token * a;
+        Token * b;
+        if (
+            (a = _PyPegen_expect_token(p, 619))  // token='lambda'
+            &&
+            (_opt_var = lambda_params_rule(p), !p->error_indicator)  // lambda_params?
+            &&
+            (b = _PyPegen_expect_token(p, 11))  // token=':'
+            &&
+            _PyPegen_lookahead_with_int(1, _PyPegen_expect_token, p, TSTRING_MIDDLE)  // token=TSTRING_MIDDLE
+        )
+        {
+            D(fprintf(stderr, "%*c+ invalid_expression[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'lambda' lambda_params? ':' &TSTRING_MIDDLE"));
+            _res = RAISE_SYNTAX_ERROR_KNOWN_RANGE ( a , b , "t-string: lambda expressions are not allowed without parentheses" );
+            if (_res == NULL && PyErr_Occurred()) {
+                p->error_indicator = 1;
+                p->level--;
+                return NULL;
+            }
+            goto done;
+        }
+        p->mark = _mark;
+        D(fprintf(stderr, "%*c%s invalid_expression[%d-%d]: %s failed!\n", p->level, ' ',
+                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'lambda' lambda_params? ':' &TSTRING_MIDDLE"));
+    }
     _res = NULL;
   done:
     p->level--;
@@ -20907,7 +21399,7 @@ invalid_named_expression_rule(Parser *p)
             &&
             (b = bitwise_or_rule(p))  // bitwise_or
             &&
-            _PyPegen_lookahead(0, (void *(*)(Parser *)) _tmp_115_rule, p)
+            _PyPegen_lookahead(0, (void *(*)(Parser *)) _tmp_117_rule, p)
         )
         {
             D(fprintf(stderr, "%*c+ invalid_named_expression[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "NAME '=' bitwise_or !('=' | ':=')"));
@@ -20933,7 +21425,7 @@ invalid_named_expression_rule(Parser *p)
         Token * b;
         expr_ty bitwise_or_var;
         if (
-            _PyPegen_lookahead(0, (void *(*)(Parser *)) _tmp_116_rule, p)
+            _PyPegen_lookahead(0, (void *(*)(Parser *)) _tmp_118_rule, p)
             &&
             (a = bitwise_or_rule(p))  // bitwise_or
             &&
@@ -20941,7 +21433,7 @@ invalid_named_expression_rule(Parser *p)
             &&
             (bitwise_or_var = bitwise_or_rule(p))  // bitwise_or
             &&
-            _PyPegen_lookahead(0, (void *(*)(Parser *)) _tmp_115_rule, p)
+            _PyPegen_lookahead(0, (void *(*)(Parser *)) _tmp_117_rule, p)
         )
         {
             D(fprintf(stderr, "%*c+ invalid_named_expression[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "!(list | tuple | genexp | 'True' | 'None' | 'False') bitwise_or '=' bitwise_or !('=' | ':=')"));
@@ -21021,7 +21513,7 @@ invalid_assignment_rule(Parser *p)
         D(fprintf(stderr, "%*c> invalid_assignment[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_named_expression ',' star_named_expressions* ':' expression"));
         Token * _literal;
         Token * _literal_1;
-        asdl_seq * _loop0_117_var;
+        asdl_seq * _loop0_119_var;
         expr_ty a;
         expr_ty expression_var;
         if (
@@ -21029,7 +21521,7 @@ invalid_assignment_rule(Parser *p)
             &&
             (_literal = _PyPegen_expect_token(p, 12))  // token=','
             &&
-            (_loop0_117_var = _loop0_117_rule(p))  // star_named_expressions*
+            (_loop0_119_var = _loop0_119_rule(p))  // star_named_expressions*
             &&
             (_literal_1 = _PyPegen_expect_token(p, 11))  // token=':'
             &&
@@ -21086,10 +21578,10 @@ invalid_assignment_rule(Parser *p)
         }
         D(fprintf(stderr, "%*c> invalid_assignment[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "((star_targets '='))* star_expressions '='"));
         Token * _literal;
-        asdl_seq * _loop0_118_var;
+        asdl_seq * _loop0_120_var;
         expr_ty a;
         if (
-            (_loop0_118_var = _loop0_118_rule(p))  // ((star_targets '='))*
+            (_loop0_120_var = _loop0_120_rule(p))  // ((star_targets '='))*
             &&
             (a = star_expressions_rule(p))  // star_expressions
             &&
@@ -21116,10 +21608,10 @@ invalid_assignment_rule(Parser *p)
         }
         D(fprintf(stderr, "%*c> invalid_assignment[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "((star_targets '='))* yield_expr '='"));
         Token * _literal;
-        asdl_seq * _loop0_118_var;
+        asdl_seq * _loop0_120_var;
         expr_ty a;
         if (
-            (_loop0_118_var = _loop0_118_rule(p))  // ((star_targets '='))*
+            (_loop0_120_var = _loop0_120_rule(p))  // ((star_targets '='))*
             &&
             (a = yield_expr_rule(p))  // yield_expr
             &&
@@ -21284,7 +21776,7 @@ invalid_del_stmt_rule(Parser *p)
         Token * _keyword;
         expr_ty a;
         if (
-            (_keyword = _PyPegen_expect_token(p, 622))  // token='del'
+            (_keyword = _PyPegen_expect_token(p, 623))  // token='del'
             &&
             (a = star_expressions_rule(p))  // star_expressions
         )
@@ -21375,11 +21867,11 @@ invalid_comprehension_rule(Parser *p)
             return NULL;
         }
         D(fprintf(stderr, "%*c> invalid_comprehension[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('[' | '(' | '{') starred_expression for_if_clauses"));
-        void *_tmp_119_var;
+        void *_tmp_121_var;
         expr_ty a;
         asdl_comprehension_seq* for_if_clauses_var;
         if (
-            (_tmp_119_var = _tmp_119_rule(p))  // '[' | '(' | '{'
+            (_tmp_121_var = _tmp_121_rule(p))  // '[' | '(' | '{'
             &&
             (a = starred_expression_rule(p))  // starred_expression
             &&
@@ -21406,12 +21898,12 @@ invalid_comprehension_rule(Parser *p)
         }
         D(fprintf(stderr, "%*c> invalid_comprehension[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('[' | '{') star_named_expression ',' star_named_expressions for_if_clauses"));
         Token * _literal;
-        void *_tmp_120_var;
+        void *_tmp_122_var;
         expr_ty a;
         asdl_expr_seq* b;
         asdl_comprehension_seq* for_if_clauses_var;
         if (
-            (_tmp_120_var = _tmp_120_rule(p))  // '[' | '{'
+            (_tmp_122_var = _tmp_122_rule(p))  // '[' | '{'
             &&
             (a = star_named_expression_rule(p))  // star_named_expression
             &&
@@ -21441,12 +21933,12 @@ invalid_comprehension_rule(Parser *p)
             return NULL;
         }
         D(fprintf(stderr, "%*c> invalid_comprehension[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('[' | '{') star_named_expression ',' for_if_clauses"));
-        void *_tmp_120_var;
+        void *_tmp_122_var;
         expr_ty a;
         Token * b;
         asdl_comprehension_seq* for_if_clauses_var;
         if (
-            (_tmp_120_var = _tmp_120_rule(p))  // '[' | '{'
+            (_tmp_122_var = _tmp_122_rule(p))  // '[' | '{'
             &&
             (a = star_named_expression_rule(p))  // star_named_expression
             &&
@@ -21582,10 +22074,10 @@ invalid_parameters_rule(Parser *p)
         }
         D(fprintf(stderr, "%*c> invalid_parameters[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(slash_no_default | slash_with_default) param_maybe_default* '/'"));
         asdl_seq * _loop0_32_var;
-        void *_tmp_121_var;
+        void *_tmp_123_var;
         Token * a;
         if (
-            (_tmp_121_var = _tmp_121_rule(p))  // slash_no_default | slash_with_default
+            (_tmp_123_var = _tmp_123_rule(p))  // slash_no_default | slash_with_default
             &&
             (_loop0_32_var = _loop0_32_rule(p))  // param_maybe_default*
             &&
@@ -21687,16 +22179,16 @@ invalid_parameters_rule(Parser *p)
         asdl_seq * _loop0_32_var_1;
         void *_opt_var;
         UNUSED(_opt_var); // Silence compiler warnings
-        void *_tmp_122_var;
+        void *_tmp_124_var;
         Token * a;
         if (
-            (_opt_var = _tmp_121_rule(p), !p->error_indicator)  // [(slash_no_default | slash_with_default)]
+            (_opt_var = _tmp_123_rule(p), !p->error_indicator)  // [(slash_no_default | slash_with_default)]
             &&
             (_loop0_32_var = _loop0_32_rule(p))  // param_maybe_default*
             &&
             (_literal = _PyPegen_expect_token(p, 16))  // token='*'
             &&
-            (_tmp_122_var = _tmp_122_rule(p))  // ',' | param_no_default
+            (_tmp_124_var = _tmp_124_rule(p))  // ',' | param_no_default
             &&
             (_loop0_32_var_1 = _loop0_32_rule(p))  // param_maybe_default*
             &&
@@ -21775,7 +22267,7 @@ invalid_default_rule(Parser *p)
         if (
             (a = _PyPegen_expect_token(p, 22))  // token='='
             &&
-            _PyPegen_lookahead(1, (void *(*)(Parser *)) _tmp_123_rule, p)
+            _PyPegen_lookahead(1, (void *(*)(Parser *)) _tmp_125_rule, p)
         )
         {
             D(fprintf(stderr, "%*c+ invalid_default[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'=' &(')' | ',')"));
@@ -21820,12 +22312,12 @@ invalid_star_etc_rule(Parser *p)
             return NULL;
         }
         D(fprintf(stderr, "%*c> invalid_star_etc[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'*' (')' | ',' (')' | '**'))"));
-        void *_tmp_124_var;
+        void *_tmp_126_var;
         Token * a;
         if (
             (a = _PyPegen_expect_token(p, 16))  // token='*'
             &&
-            (_tmp_124_var = _tmp_124_rule(p))  // ')' | ',' (')' | '**')
+            (_tmp_126_var = _tmp_126_rule(p))  // ')' | ',' (')' | '**')
         )
         {
             D(fprintf(stderr, "%*c+ invalid_star_etc[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'*' (')' | ',' (')' | '**'))"));
@@ -21909,19 +22401,19 @@ invalid_star_etc_rule(Parser *p)
         D(fprintf(stderr, "%*c> invalid_star_etc[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'*' (param_no_default | ',') param_maybe_default* '*' (param_no_default | ',')"));
         Token * _literal;
         asdl_seq * _loop0_32_var;
-        void *_tmp_125_var;
-        void *_tmp_125_var_1;
+        void *_tmp_127_var;
+        void *_tmp_127_var_1;
         Token * a;
         if (
             (_literal = _PyPegen_expect_token(p, 16))  // token='*'
             &&
-            (_tmp_125_var = _tmp_125_rule(p))  // param_no_default | ','
+            (_tmp_127_var = _tmp_127_rule(p))  // param_no_default | ','
             &&
             (_loop0_32_var = _loop0_32_rule(p))  // param_maybe_default*
             &&
             (a = _PyPegen_expect_token(p, 16))  // token='*'
             &&
-            (_tmp_125_var_1 = _tmp_125_rule(p))  // param_no_default | ','
+            (_tmp_127_var_1 = _tmp_127_rule(p))  // param_no_default | ','
         )
         {
             D(fprintf(stderr, "%*c+ invalid_star_etc[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'*' (param_no_default | ',') param_maybe_default* '*' (param_no_default | ',')"));
@@ -22036,7 +22528,7 @@ invalid_kwds_rule(Parser *p)
             &&
             (_literal_1 = _PyPegen_expect_token(p, 12))  // token=','
             &&
-            (a = (Token*)_tmp_126_rule(p))  // '*' | '**' | '/'
+            (a = (Token*)_tmp_128_rule(p))  // '*' | '**' | '/'
         )
         {
             D(fprintf(stderr, "%*c+ invalid_kwds[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'**' param ',' ('*' | '**' | '/')"));
@@ -22173,10 +22665,10 @@ invalid_lambda_parameters_rule(Parser *p)
         }
         D(fprintf(stderr, "%*c> invalid_lambda_parameters[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(lambda_slash_no_default | lambda_slash_with_default) lambda_param_maybe_default* '/'"));
         asdl_seq * _loop0_75_var;
-        void *_tmp_127_var;
+        void *_tmp_129_var;
         Token * a;
         if (
-            (_tmp_127_var = _tmp_127_rule(p))  // lambda_slash_no_default | lambda_slash_with_default
+            (_tmp_129_var = _tmp_129_rule(p))  // lambda_slash_no_default | lambda_slash_with_default
             &&
             (_loop0_75_var = _loop0_75_rule(p))  // lambda_param_maybe_default*
             &&
@@ -22236,7 +22728,7 @@ invalid_lambda_parameters_rule(Parser *p)
             return NULL;
         }
         D(fprintf(stderr, "%*c> invalid_lambda_parameters[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_no_default* '(' ','.lambda_param+ ','? ')'"));
-        asdl_seq * _gather_129_var;
+        asdl_seq * _gather_131_var;
         asdl_seq * _loop0_71_var;
         void *_opt_var;
         UNUSED(_opt_var); // Silence compiler warnings
@@ -22247,7 +22739,7 @@ invalid_lambda_parameters_rule(Parser *p)
             &&
             (a = _PyPegen_expect_token(p, 7))  // token='('
             &&
-            (_gather_129_var = _gather_129_rule(p))  // ','.lambda_param+
+            (_gather_131_var = _gather_131_rule(p))  // ','.lambda_param+
             &&
             (_opt_var = _PyPegen_expect_token(p, 12), !p->error_indicator)  // ','?
             &&
@@ -22278,16 +22770,16 @@ invalid_lambda_parameters_rule(Parser *p)
         asdl_seq * _loop0_75_var_1;
         void *_opt_var;
         UNUSED(_opt_var); // Silence compiler warnings
-        void *_tmp_130_var;
+        void *_tmp_132_var;
         Token * a;
         if (
-            (_opt_var = _tmp_127_rule(p), !p->error_indicator)  // [(lambda_slash_no_default | lambda_slash_with_default)]
+            (_opt_var = _tmp_129_rule(p), !p->error_indicator)  // [(lambda_slash_no_default | lambda_slash_with_default)]
             &&
             (_loop0_75_var = _loop0_75_rule(p))  // lambda_param_maybe_default*
             &&
             (_literal = _PyPegen_expect_token(p, 16))  // token='*'
             &&
-            (_tmp_130_var = _tmp_130_rule(p))  // ',' | lambda_param_no_default
+            (_tmp_132_var = _tmp_132_rule(p))  // ',' | lambda_param_no_default
             &&
             (_loop0_75_var_1 = _loop0_75_rule(p))  // lambda_param_maybe_default*
             &&
@@ -22430,11 +22922,11 @@ invalid_lambda_star_etc_rule(Parser *p)
         }
         D(fprintf(stderr, "%*c> invalid_lambda_star_etc[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'*' (':' | ',' (':' | '**'))"));
         Token * _literal;
-        void *_tmp_131_var;
+        void *_tmp_133_var;
         if (
             (_literal = _PyPegen_expect_token(p, 16))  // token='*'
             &&
-            (_tmp_131_var = _tmp_131_rule(p))  // ':' | ',' (':' | '**')
+            (_tmp_133_var = _tmp_133_rule(p))  // ':' | ',' (':' | '**')
         )
         {
             D(fprintf(stderr, "%*c+ invalid_lambda_star_etc[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'*' (':' | ',' (':' | '**'))"));
@@ -22488,19 +22980,19 @@ invalid_lambda_star_etc_rule(Parser *p)
         D(fprintf(stderr, "%*c> invalid_lambda_star_etc[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'*' (lambda_param_no_default | ',') lambda_param_maybe_default* '*' (lambda_param_no_default | ',')"));
         Token * _literal;
         asdl_seq * _loop0_75_var;
-        void *_tmp_132_var;
-        void *_tmp_132_var_1;
+        void *_tmp_134_var;
+        void *_tmp_134_var_1;
         Token * a;
         if (
             (_literal = _PyPegen_expect_token(p, 16))  // token='*'
             &&
-            (_tmp_132_var = _tmp_132_rule(p))  // lambda_param_no_default | ','
+            (_tmp_134_var = _tmp_134_rule(p))  // lambda_param_no_default | ','
             &&
             (_loop0_75_var = _loop0_75_rule(p))  // lambda_param_maybe_default*
             &&
             (a = _PyPegen_expect_token(p, 16))  // token='*'
             &&
-            (_tmp_132_var_1 = _tmp_132_rule(p))  // lambda_param_no_default | ','
+            (_tmp_134_var_1 = _tmp_134_rule(p))  // lambda_param_no_default | ','
         )
         {
             D(fprintf(stderr, "%*c+ invalid_lambda_star_etc[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'*' (lambda_param_no_default | ',') lambda_param_maybe_default* '*' (lambda_param_no_default | ',')"));
@@ -22618,7 +23110,7 @@ invalid_lambda_kwds_rule(Parser *p)
             &&
             (_literal_1 = _PyPegen_expect_token(p, 12))  // token=','
             &&
-            (a = (Token*)_tmp_126_rule(p))  // '*' | '**' | '/'
+            (a = (Token*)_tmp_128_rule(p))  // '*' | '**' | '/'
         )
         {
             D(fprintf(stderr, "%*c+ invalid_lambda_kwds[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'**' lambda_param ',' ('*' | '**' | '/')"));
@@ -22720,7 +23212,7 @@ invalid_with_item_rule(Parser *p)
         if (
             (expression_var = expression_rule(p))  // expression
             &&
-            (_keyword = _PyPegen_expect_token(p, 675))  // token='as'
+            (_keyword = _PyPegen_expect_token(p, 676))  // token='as'
             &&
             (a = expression_rule(p))  // expression
             &&
@@ -22768,15 +23260,15 @@ invalid_for_if_clause_rule(Parser *p)
         Token * _keyword;
         void *_opt_var;
         UNUSED(_opt_var); // Silence compiler warnings
-        void *_tmp_133_var;
+        void *_tmp_135_var;
         if (
-            (_opt_var = _PyPegen_expect_token(p, 693), !p->error_indicator)  // 'async'?
+            (_opt_var = _PyPegen_expect_token(p, 694), !p->error_indicator)  // 'async'?
             &&
-            (_keyword = _PyPegen_expect_token(p, 689))  // token='for'
+            (_keyword = _PyPegen_expect_token(p, 690))  // token='for'
             &&
-            (_tmp_133_var = _tmp_133_rule(p))  // bitwise_or ((',' bitwise_or))* ','?
+            (_tmp_135_var = _tmp_135_rule(p))  // bitwise_or ((',' bitwise_or))* ','?
             &&
-            _PyPegen_lookahead_with_int(0, _PyPegen_expect_token, p, 690)  // token='in'
+            _PyPegen_lookahead_with_int(0, _PyPegen_expect_token, p, 691)  // token='in'
         )
         {
             D(fprintf(stderr, "%*c+ invalid_for_if_clause[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'async'? 'for' (bitwise_or ((',' bitwise_or))* ','?) !'in'"));
@@ -22822,9 +23314,9 @@ invalid_for_target_rule(Parser *p)
         UNUSED(_opt_var); // Silence compiler warnings
         expr_ty a;
         if (
-            (_opt_var = _PyPegen_expect_token(p, 693), !p->error_indicator)  // 'async'?
+            (_opt_var = _PyPegen_expect_token(p, 694), !p->error_indicator)  // 'async'?
             &&
-            (_keyword = _PyPegen_expect_token(p, 689))  // token='for'
+            (_keyword = _PyPegen_expect_token(p, 690))  // token='for'
             &&
             (a = star_expressions_rule(p))  // star_expressions
         )
@@ -22949,16 +23441,16 @@ invalid_import_rule(Parser *p)
             return NULL;
         }
         D(fprintf(stderr, "%*c> invalid_import[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'import' ','.dotted_name+ 'from' dotted_name"));
-        asdl_seq * _gather_135_var;
+        asdl_seq * _gather_137_var;
         Token * _keyword;
         Token * a;
         expr_ty dotted_name_var;
         if (
-            (a = _PyPegen_expect_token(p, 631))  // token='import'
+            (a = _PyPegen_expect_token(p, 632))  // token='import'
             &&
-            (_gather_135_var = _gather_135_rule(p))  // ','.dotted_name+
+            (_gather_137_var = _gather_137_rule(p))  // ','.dotted_name+
             &&
-            (_keyword = _PyPegen_expect_token(p, 630))  // token='from'
+            (_keyword = _PyPegen_expect_token(p, 631))  // token='from'
             &&
             (dotted_name_var = dotted_name_rule(p))  // dotted_name
         )
@@ -22985,7 +23477,7 @@ invalid_import_rule(Parser *p)
         Token * _keyword;
         Token * token;
         if (
-            (_keyword = _PyPegen_expect_token(p, 631))  // token='import'
+            (_keyword = _PyPegen_expect_token(p, 632))  // token='import'
             &&
             (token = _PyPegen_expect_token(p, NEWLINE))  // token='NEWLINE'
         )
@@ -23103,17 +23595,17 @@ invalid_with_stmt_rule(Parser *p)
             return NULL;
         }
         D(fprintf(stderr, "%*c> invalid_with_stmt[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'async'? 'with' ','.(expression ['as' star_target])+ NEWLINE"));
-        asdl_seq * _gather_137_var;
+        asdl_seq * _gather_139_var;
         Token * _keyword;
         void *_opt_var;
         UNUSED(_opt_var); // Silence compiler warnings
         Token * newline_var;
         if (
-            (_opt_var = _PyPegen_expect_token(p, 693), !p->error_indicator)  // 'async'?
+            (_opt_var = _PyPegen_expect_token(p, 694), !p->error_indicator)  // 'async'?
             &&
-            (_keyword = _PyPegen_expect_token(p, 642))  // token='with'
+            (_keyword = _PyPegen_expect_token(p, 643))  // token='with'
             &&
-            (_gather_137_var = _gather_137_rule(p))  // ','.(expression ['as' star_target])+
+            (_gather_139_var = _gather_139_rule(p))  // ','.(expression ['as' star_target])+
             &&
             (newline_var = _PyPegen_expect_token(p, NEWLINE))  // token='NEWLINE'
         )
@@ -23137,7 +23629,7 @@ invalid_with_stmt_rule(Parser *p)
             return NULL;
         }
         D(fprintf(stderr, "%*c> invalid_with_stmt[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'async'? 'with' '(' ','.(expressions ['as' star_target])+ ','? ')' NEWLINE"));
-        asdl_seq * _gather_139_var;
+        asdl_seq * _gather_141_var;
         Token * _keyword;
         Token * _literal;
         Token * _literal_1;
@@ -23147,13 +23639,13 @@ invalid_with_stmt_rule(Parser *p)
         UNUSED(_opt_var_1); // Silence compiler warnings
         Token * newline_var;
         if (
-            (_opt_var = _PyPegen_expect_token(p, 693), !p->error_indicator)  // 'async'?
+            (_opt_var = _PyPegen_expect_token(p, 694), !p->error_indicator)  // 'async'?
             &&
-            (_keyword = _PyPegen_expect_token(p, 642))  // token='with'
+            (_keyword = _PyPegen_expect_token(p, 643))  // token='with'
             &&
             (_literal = _PyPegen_expect_token(p, 7))  // token='('
             &&
-            (_gather_139_var = _gather_139_rule(p))  // ','.(expressions ['as' star_target])+
+            (_gather_141_var = _gather_141_rule(p))  // ','.(expressions ['as' star_target])+
             &&
             (_opt_var_1 = _PyPegen_expect_token(p, 12), !p->error_indicator)  // ','?
             &&
@@ -23202,18 +23694,18 @@ invalid_with_stmt_indent_rule(Parser *p)
             return NULL;
         }
         D(fprintf(stderr, "%*c> invalid_with_stmt_indent[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'async'? 'with' ','.(expression ['as' star_target])+ ':' NEWLINE !INDENT"));
-        asdl_seq * _gather_137_var;
+        asdl_seq * _gather_139_var;
         Token * _literal;
         void *_opt_var;
         UNUSED(_opt_var); // Silence compiler warnings
         Token * a;
         Token * newline_var;
         if (
-            (_opt_var = _PyPegen_expect_token(p, 693), !p->error_indicator)  // 'async'?
+            (_opt_var = _PyPegen_expect_token(p, 694), !p->error_indicator)  // 'async'?
             &&
-            (a = _PyPegen_expect_token(p, 642))  // token='with'
+            (a = _PyPegen_expect_token(p, 643))  // token='with'
             &&
-            (_gather_137_var = _gather_137_rule(p))  // ','.(expression ['as' star_target])+
+            (_gather_139_var = _gather_139_rule(p))  // ','.(expression ['as' star_target])+
             &&
             (_literal = _PyPegen_expect_token(p, 11))  // token=':'
             &&
@@ -23241,7 +23733,7 @@ invalid_with_stmt_indent_rule(Parser *p)
             return NULL;
         }
         D(fprintf(stderr, "%*c> invalid_with_stmt_indent[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'async'? 'with' '(' ','.(expressions ['as' star_target])+ ','? ')' ':' NEWLINE !INDENT"));
-        asdl_seq * _gather_139_var;
+        asdl_seq * _gather_141_var;
         Token * _literal;
         Token * _literal_1;
         Token * _literal_2;
@@ -23252,13 +23744,13 @@ invalid_with_stmt_indent_rule(Parser *p)
         Token * a;
         Token * newline_var;
         if (
-            (_opt_var = _PyPegen_expect_token(p, 693), !p->error_indicator)  // 'async'?
+            (_opt_var = _PyPegen_expect_token(p, 694), !p->error_indicator)  // 'async'?
             &&
-            (a = _PyPegen_expect_token(p, 642))  // token='with'
+            (a = _PyPegen_expect_token(p, 643))  // token='with'
             &&
             (_literal = _PyPegen_expect_token(p, 7))  // token='('
             &&
-            (_gather_139_var = _gather_139_rule(p))  // ','.(expressions ['as' star_target])+
+            (_gather_141_var = _gather_141_rule(p))  // ','.(expressions ['as' star_target])+
             &&
             (_opt_var_1 = _PyPegen_expect_token(p, 12), !p->error_indicator)  // ','?
             &&
@@ -23317,7 +23809,7 @@ invalid_try_stmt_rule(Parser *p)
         Token * a;
         Token * newline_var;
         if (
-            (a = _PyPegen_expect_token(p, 651))  // token='try'
+            (a = _PyPegen_expect_token(p, 652))  // token='try'
             &&
             (_literal = _PyPegen_expect_token(p, 11))  // token=':'
             &&
@@ -23349,13 +23841,13 @@ invalid_try_stmt_rule(Parser *p)
         Token * _literal;
         asdl_stmt_seq* block_var;
         if (
-            (_keyword = _PyPegen_expect_token(p, 651))  // token='try'
+            (_keyword = _PyPegen_expect_token(p, 652))  // token='try'
             &&
             (_literal = _PyPegen_expect_token(p, 11))  // token=':'
             &&
             (block_var = block_rule(p))  // block
             &&
-            _PyPegen_lookahead(0, (void *(*)(Parser *)) _tmp_140_rule, p)
+            _PyPegen_lookahead(0, (void *(*)(Parser *)) _tmp_142_rule, p)
         )
         {
             D(fprintf(stderr, "%*c+ invalid_try_stmt[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'try' ':' block !('except' | 'finally')"));
@@ -23380,7 +23872,7 @@ invalid_try_stmt_rule(Parser *p)
         Token * _keyword;
         Token * _literal;
         Token * _literal_1;
-        asdl_seq * _loop0_141_var;
+        asdl_seq * _loop0_143_var;
         asdl_seq * _loop1_37_var;
         void *_opt_var;
         UNUSED(_opt_var); // Silence compiler warnings
@@ -23388,15 +23880,15 @@ invalid_try_stmt_rule(Parser *p)
         Token * b;
         expr_ty expression_var;
         if (
-            (_keyword = _PyPegen_expect_token(p, 651))  // token='try'
+            (_keyword = _PyPegen_expect_token(p, 652))  // token='try'
             &&
             (_literal = _PyPegen_expect_token(p, 11))  // token=':'
             &&
-            (_loop0_141_var = _loop0_141_rule(p))  // block*
+            (_loop0_143_var = _loop0_143_rule(p))  // block*
             &&
             (_loop1_37_var = _loop1_37_rule(p))  // except_block+
             &&
-            (a = _PyPegen_expect_token(p, 672))  // token='except'
+            (a = _PyPegen_expect_token(p, 673))  // token='except'
             &&
             (b = _PyPegen_expect_token(p, 16))  // token='*'
             &&
@@ -23429,23 +23921,23 @@ invalid_try_stmt_rule(Parser *p)
         Token * _keyword;
         Token * _literal;
         Token * _literal_1;
-        asdl_seq * _loop0_141_var;
+        asdl_seq * _loop0_143_var;
         asdl_seq * _loop1_38_var;
         void *_opt_var;
         UNUSED(_opt_var); // Silence compiler warnings
         Token * a;
         if (
-            (_keyword = _PyPegen_expect_token(p, 651))  // token='try'
+            (_keyword = _PyPegen_expect_token(p, 652))  // token='try'
             &&
             (_literal = _PyPegen_expect_token(p, 11))  // token=':'
             &&
-            (_loop0_141_var = _loop0_141_rule(p))  // block*
+            (_loop0_143_var = _loop0_143_rule(p))  // block*
             &&
             (_loop1_38_var = _loop1_38_rule(p))  // except_star_block+
             &&
-            (a = _PyPegen_expect_token(p, 672))  // token='except'
+            (a = _PyPegen_expect_token(p, 673))  // token='except'
             &&
-            (_opt_var = _tmp_142_rule(p), !p->error_indicator)  // [expression ['as' NAME]]
+            (_opt_var = _tmp_144_rule(p), !p->error_indicator)  // [expression ['as' NAME]]
             &&
             (_literal_1 = _PyPegen_expect_token(p, 11))  // token=':'
         )
@@ -23500,7 +23992,7 @@ invalid_except_stmt_rule(Parser *p)
         expr_ty expressions_var;
         expr_ty name_var;
         if (
-            (_keyword = _PyPegen_expect_token(p, 672))  // token='except'
+            (_keyword = _PyPegen_expect_token(p, 673))  // token='except'
             &&
             (a = expression_rule(p))  // expression
             &&
@@ -23508,7 +24000,7 @@ invalid_except_stmt_rule(Parser *p)
             &&
             (expressions_var = expressions_rule(p))  // expressions
             &&
-            (_keyword_1 = _PyPegen_expect_token(p, 675))  // token='as'
+            (_keyword_1 = _PyPegen_expect_token(p, 676))  // token='as'
             &&
             (name_var = _PyPegen_name_token(p))  // NAME
             &&
@@ -23540,7 +24032,7 @@ invalid_except_stmt_rule(Parser *p)
         expr_ty expression_var;
         Token * newline_var;
         if (
-            (a = _PyPegen_expect_token(p, 672))  // token='except'
+            (a = _PyPegen_expect_token(p, 673))  // token='except'
             &&
             (expression_var = expression_rule(p))  // expression
             &&
@@ -23571,7 +24063,7 @@ invalid_except_stmt_rule(Parser *p)
         Token * a;
         Token * newline_var;
         if (
-            (a = _PyPegen_expect_token(p, 672))  // token='except'
+            (a = _PyPegen_expect_token(p, 673))  // token='except'
             &&
             (newline_var = _PyPegen_expect_token(p, NEWLINE))  // token='NEWLINE'
         )
@@ -23600,11 +24092,11 @@ invalid_except_stmt_rule(Parser *p)
         expr_ty a;
         expr_ty expression_var;
         if (
-            (_keyword = _PyPegen_expect_token(p, 672))  // token='except'
+            (_keyword = _PyPegen_expect_token(p, 673))  // token='except'
             &&
             (expression_var = expression_rule(p))  // expression
             &&
-            (_keyword_1 = _PyPegen_expect_token(p, 675))  // token='as'
+            (_keyword_1 = _PyPegen_expect_token(p, 676))  // token='as'
             &&
             (a = expression_rule(p))  // expression
         )
@@ -23660,7 +24152,7 @@ invalid_except_star_stmt_rule(Parser *p)
         expr_ty expressions_var;
         expr_ty name_var;
         if (
-            (_keyword = _PyPegen_expect_token(p, 672))  // token='except'
+            (_keyword = _PyPegen_expect_token(p, 673))  // token='except'
             &&
             (_literal = _PyPegen_expect_token(p, 16))  // token='*'
             &&
@@ -23670,7 +24162,7 @@ invalid_except_star_stmt_rule(Parser *p)
             &&
             (expressions_var = expressions_rule(p))  // expressions
             &&
-            (_keyword_1 = _PyPegen_expect_token(p, 675))  // token='as'
+            (_keyword_1 = _PyPegen_expect_token(p, 676))  // token='as'
             &&
             (name_var = _PyPegen_name_token(p))  // NAME
             &&
@@ -23703,7 +24195,7 @@ invalid_except_star_stmt_rule(Parser *p)
         expr_ty expression_var;
         Token * newline_var;
         if (
-            (a = _PyPegen_expect_token(p, 672))  // token='except'
+            (a = _PyPegen_expect_token(p, 673))  // token='except'
             &&
             (_literal = _PyPegen_expect_token(p, 16))  // token='*'
             &&
@@ -23734,14 +24226,14 @@ invalid_except_star_stmt_rule(Parser *p)
         }
         D(fprintf(stderr, "%*c> invalid_except_star_stmt[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'except' '*' (NEWLINE | ':')"));
         Token * _literal;
-        void *_tmp_143_var;
+        void *_tmp_145_var;
         Token * a;
         if (
-            (a = _PyPegen_expect_token(p, 672))  // token='except'
+            (a = _PyPegen_expect_token(p, 673))  // token='except'
             &&
             (_literal = _PyPegen_expect_token(p, 16))  // token='*'
             &&
-            (_tmp_143_var = _tmp_143_rule(p))  // NEWLINE | ':'
+            (_tmp_145_var = _tmp_145_rule(p))  // NEWLINE | ':'
         )
         {
             D(fprintf(stderr, "%*c+ invalid_except_star_stmt[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'except' '*' (NEWLINE | ':')"));
@@ -23769,13 +24261,13 @@ invalid_except_star_stmt_rule(Parser *p)
         expr_ty a;
         expr_ty expression_var;
         if (
-            (_keyword = _PyPegen_expect_token(p, 672))  // token='except'
+            (_keyword = _PyPegen_expect_token(p, 673))  // token='except'
             &&
             (_literal = _PyPegen_expect_token(p, 16))  // token='*'
             &&
             (expression_var = expression_rule(p))  // expression
             &&
-            (_keyword_1 = _PyPegen_expect_token(p, 675))  // token='as'
+            (_keyword_1 = _PyPegen_expect_token(p, 676))  // token='as'
             &&
             (a = expression_rule(p))  // expression
         )
@@ -23822,7 +24314,7 @@ invalid_finally_stmt_rule(Parser *p)
         Token * a;
         Token * newline_var;
         if (
-            (a = _PyPegen_expect_token(p, 668))  // token='finally'
+            (a = _PyPegen_expect_token(p, 669))  // token='finally'
             &&
             (_literal = _PyPegen_expect_token(p, 11))  // token=':'
             &&
@@ -23878,7 +24370,7 @@ invalid_except_stmt_indent_rule(Parser *p)
         expr_ty expression_var;
         Token * newline_var;
         if (
-            (a = _PyPegen_expect_token(p, 672))  // token='except'
+            (a = _PyPegen_expect_token(p, 673))  // token='except'
             &&
             (expression_var = expression_rule(p))  // expression
             &&
@@ -23914,7 +24406,7 @@ invalid_except_stmt_indent_rule(Parser *p)
         Token * a;
         Token * newline_var;
         if (
-            (a = _PyPegen_expect_token(p, 672))  // token='except'
+            (a = _PyPegen_expect_token(p, 673))  // token='except'
             &&
             (_literal = _PyPegen_expect_token(p, 11))  // token=':'
             &&
@@ -23970,7 +24462,7 @@ invalid_except_star_stmt_indent_rule(Parser *p)
         expr_ty expression_var;
         Token * newline_var;
         if (
-            (a = _PyPegen_expect_token(p, 672))  // token='except'
+            (a = _PyPegen_expect_token(p, 673))  // token='except'
             &&
             (_literal = _PyPegen_expect_token(p, 16))  // token='*'
             &&
@@ -24209,7 +24701,7 @@ invalid_as_pattern_rule(Parser *p)
         if (
             (or_pattern_var = or_pattern_rule(p))  // or_pattern
             &&
-            (_keyword = _PyPegen_expect_token(p, 675))  // token='as'
+            (_keyword = _PyPegen_expect_token(p, 676))  // token='as'
             &&
             (a = _PyPegen_expect_soft_keyword(p, "_"))  // soft_keyword='"_"'
         )
@@ -24239,7 +24731,7 @@ invalid_as_pattern_rule(Parser *p)
         if (
             (or_pattern_var = or_pattern_rule(p))  // or_pattern
             &&
-            (_keyword = _PyPegen_expect_token(p, 675))  // token='as'
+            (_keyword = _PyPegen_expect_token(p, 676))  // token='as'
             &&
             (a = expression_rule(p))  // expression
         )
@@ -24338,7 +24830,7 @@ invalid_class_argument_pattern_rule(Parser *p)
         asdl_pattern_seq* a;
         asdl_seq* keyword_patterns_var;
         if (
-            (_opt_var = _tmp_144_rule(p), !p->error_indicator)  // [positional_patterns ',']
+            (_opt_var = _tmp_146_rule(p), !p->error_indicator)  // [positional_patterns ',']
             &&
             (keyword_patterns_var = keyword_patterns_rule(p))  // keyword_patterns
             &&
@@ -24391,7 +24883,7 @@ invalid_if_stmt_rule(Parser *p)
         expr_ty named_expression_var;
         Token * newline_var;
         if (
-            (_keyword = _PyPegen_expect_token(p, 677))  // token='if'
+            (_keyword = _PyPegen_expect_token(p, 678))  // token='if'
             &&
             (named_expression_var = named_expression_rule(p))  // named_expression
             &&
@@ -24422,7 +24914,7 @@ invalid_if_stmt_rule(Parser *p)
         expr_ty a_1;
         Token * newline_var;
         if (
-            (a = _PyPegen_expect_token(p, 677))  // token='if'
+            (a = _PyPegen_expect_token(p, 678))  // token='if'
             &&
             (a_1 = named_expression_rule(p))  // named_expression
             &&
@@ -24477,7 +24969,7 @@ invalid_elif_stmt_rule(Parser *p)
         expr_ty named_expression_var;
         Token * newline_var;
         if (
-            (_keyword = _PyPegen_expect_token(p, 682))  // token='elif'
+            (_keyword = _PyPegen_expect_token(p, 683))  // token='elif'
             &&
             (named_expression_var = named_expression_rule(p))  // named_expression
             &&
@@ -24508,7 +25000,7 @@ invalid_elif_stmt_rule(Parser *p)
         expr_ty named_expression_var;
         Token * newline_var;
         if (
-            (a = _PyPegen_expect_token(p, 682))  // token='elif'
+            (a = _PyPegen_expect_token(p, 683))  // token='elif'
             &&
             (named_expression_var = named_expression_rule(p))  // named_expression
             &&
@@ -24561,7 +25053,7 @@ invalid_else_stmt_rule(Parser *p)
         Token * a;
         Token * newline_var;
         if (
-            (a = _PyPegen_expect_token(p, 681))  // token='else'
+            (a = _PyPegen_expect_token(p, 682))  // token='else'
             &&
             (_literal = _PyPegen_expect_token(p, 11))  // token=':'
             &&
@@ -24594,13 +25086,13 @@ invalid_else_stmt_rule(Parser *p)
         Token * _literal;
         asdl_stmt_seq* block_var;
         if (
-            (_keyword = _PyPegen_expect_token(p, 681))  // token='else'
+            (_keyword = _PyPegen_expect_token(p, 682))  // token='else'
             &&
             (_literal = _PyPegen_expect_token(p, 11))  // token=':'
             &&
             (block_var = block_rule(p))  // block
             &&
-            (_keyword_1 = _PyPegen_expect_token(p, 682))  // token='elif'
+            (_keyword_1 = _PyPegen_expect_token(p, 683))  // token='elif'
         )
         {
             D(fprintf(stderr, "%*c+ invalid_else_stmt[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'else' ':' block 'elif'"));
@@ -24647,7 +25139,7 @@ invalid_while_stmt_rule(Parser *p)
         expr_ty named_expression_var;
         Token * newline_var;
         if (
-            (_keyword = _PyPegen_expect_token(p, 684))  // token='while'
+            (_keyword = _PyPegen_expect_token(p, 685))  // token='while'
             &&
             (named_expression_var = named_expression_rule(p))  // named_expression
             &&
@@ -24678,7 +25170,7 @@ invalid_while_stmt_rule(Parser *p)
         expr_ty named_expression_var;
         Token * newline_var;
         if (
-            (a = _PyPegen_expect_token(p, 684))  // token='while'
+            (a = _PyPegen_expect_token(p, 685))  // token='while'
             &&
             (named_expression_var = named_expression_rule(p))  // named_expression
             &&
@@ -24737,13 +25229,13 @@ invalid_for_stmt_rule(Parser *p)
         expr_ty star_expressions_var;
         expr_ty star_targets_var;
         if (
-            (_opt_var = _PyPegen_expect_token(p, 693), !p->error_indicator)  // 'async'?
+            (_opt_var = _PyPegen_expect_token(p, 694), !p->error_indicator)  // 'async'?
             &&
-            (_keyword = _PyPegen_expect_token(p, 689))  // token='for'
+            (_keyword = _PyPegen_expect_token(p, 690))  // token='for'
             &&
             (star_targets_var = star_targets_rule(p))  // star_targets
             &&
-            (_keyword_1 = _PyPegen_expect_token(p, 690))  // token='in'
+            (_keyword_1 = _PyPegen_expect_token(p, 691))  // token='in'
             &&
             (star_expressions_var = star_expressions_rule(p))  // star_expressions
             &&
@@ -24778,13 +25270,13 @@ invalid_for_stmt_rule(Parser *p)
         expr_ty star_expressions_var;
         expr_ty star_targets_var;
         if (
-            (_opt_var = _PyPegen_expect_token(p, 693), !p->error_indicator)  // 'async'?
+            (_opt_var = _PyPegen_expect_token(p, 694), !p->error_indicator)  // 'async'?
             &&
-            (a = _PyPegen_expect_token(p, 689))  // token='for'
+            (a = _PyPegen_expect_token(p, 690))  // token='for'
             &&
             (star_targets_var = star_targets_rule(p))  // star_targets
             &&
-            (_keyword = _PyPegen_expect_token(p, 690))  // token='in'
+            (_keyword = _PyPegen_expect_token(p, 691))  // token='in'
             &&
             (star_expressions_var = star_expressions_rule(p))  // star_expressions
             &&
@@ -24850,9 +25342,9 @@ invalid_def_raw_rule(Parser *p)
         expr_ty name_var;
         Token * newline_var;
         if (
-            (_opt_var = _PyPegen_expect_token(p, 693), !p->error_indicator)  // 'async'?
+            (_opt_var = _PyPegen_expect_token(p, 694), !p->error_indicator)  // 'async'?
             &&
-            (a = _PyPegen_expect_token(p, 694))  // token='def'
+            (a = _PyPegen_expect_token(p, 695))  // token='def'
             &&
             (name_var = _PyPegen_name_token(p))  // NAME
             &&
@@ -24909,9 +25401,9 @@ invalid_def_raw_rule(Parser *p)
         asdl_stmt_seq* block_var;
         expr_ty name_var;
         if (
-            (_opt_var = _PyPegen_expect_token(p, 693), !p->error_indicator)  // 'async'?
+            (_opt_var = _PyPegen_expect_token(p, 694), !p->error_indicator)  // 'async'?
             &&
-            (_keyword = _PyPegen_expect_token(p, 694))  // token='def'
+            (_keyword = _PyPegen_expect_token(p, 695))  // token='def'
             &&
             (name_var = _PyPegen_name_token(p))  // NAME
             &&
@@ -24975,7 +25467,7 @@ invalid_class_def_raw_rule(Parser *p)
         expr_ty name_var;
         Token * newline_var;
         if (
-            (_keyword = _PyPegen_expect_token(p, 696))  // token='class'
+            (_keyword = _PyPegen_expect_token(p, 697))  // token='class'
             &&
             (name_var = _PyPegen_name_token(p))  // NAME
             &&
@@ -25014,7 +25506,7 @@ invalid_class_def_raw_rule(Parser *p)
         expr_ty name_var;
         Token * newline_var;
         if (
-            (a = _PyPegen_expect_token(p, 696))  // token='class'
+            (a = _PyPegen_expect_token(p, 697))  // token='class'
             &&
             (name_var = _PyPegen_name_token(p))  // NAME
             &&
@@ -25070,11 +25562,11 @@ invalid_double_starred_kvpairs_rule(Parser *p)
             return NULL;
         }
         D(fprintf(stderr, "%*c> invalid_double_starred_kvpairs[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','.double_starred_kvpair+ ',' invalid_kvpair"));
-        asdl_seq * _gather_82_var;
+        asdl_seq * _gather_84_var;
         Token * _literal;
         void *invalid_kvpair_var;
         if (
-            (_gather_82_var = _gather_82_rule(p))  // ','.double_starred_kvpair+
+            (_gather_84_var = _gather_84_rule(p))  // ','.double_starred_kvpair+
             &&
             (_literal = _PyPegen_expect_token(p, 12))  // token=','
             &&
@@ -25082,7 +25574,7 @@ invalid_double_starred_kvpairs_rule(Parser *p)
         )
         {
             D(fprintf(stderr, "%*c+ invalid_double_starred_kvpairs[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','.double_starred_kvpair+ ',' invalid_kvpair"));
-            _res = _PyPegen_dummy_name(p, _gather_82_var, _literal, invalid_kvpair_var);
+            _res = _PyPegen_dummy_name(p, _gather_84_var, _literal, invalid_kvpair_var);
             goto done;
         }
         p->mark = _mark;
@@ -25135,7 +25627,7 @@ invalid_double_starred_kvpairs_rule(Parser *p)
             &&
             (a = _PyPegen_expect_token(p, 11))  // token=':'
             &&
-            _PyPegen_lookahead(1, (void *(*)(Parser *)) _tmp_145_rule, p)
+            _PyPegen_lookahead(1, (void *(*)(Parser *)) _tmp_147_rule, p)
         )
         {
             D(fprintf(stderr, "%*c+ invalid_double_starred_kvpairs[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression ':' &('}' | ',')"));
@@ -25245,7 +25737,7 @@ invalid_kvpair_rule(Parser *p)
             &&
             (a = _PyPegen_expect_token(p, 11))  // token=':'
             &&
-            _PyPegen_lookahead(1, (void *(*)(Parser *)) _tmp_145_rule, p)
+            _PyPegen_lookahead(1, (void *(*)(Parser *)) _tmp_147_rule, p)
         )
         {
             D(fprintf(stderr, "%*c+ invalid_kvpair[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression ':' &('}' | ',')"));
@@ -25362,7 +25854,7 @@ invalid_starred_expression_rule(Parser *p)
     return _res;
 }
 
-// invalid_replacement_field:
+// invalid_fstring_replacement_field:
 //     | '{' '='
 //     | '{' '!'
 //     | '{' ':'
@@ -25370,12 +25862,12 @@ invalid_starred_expression_rule(Parser *p)
 //     | '{' !annotated_rhs
 //     | '{' annotated_rhs !('=' | '!' | ':' | '}')
 //     | '{' annotated_rhs '=' !('!' | ':' | '}')
-//     | '{' annotated_rhs '='? invalid_conversion_character
+//     | '{' annotated_rhs '='? invalid_fstring_conversion_character
 //     | '{' annotated_rhs '='? ['!' NAME] !(':' | '}')
 //     | '{' annotated_rhs '='? ['!' NAME] ':' fstring_format_spec* !'}'
 //     | '{' annotated_rhs '='? ['!' NAME] !'}'
 static void *
-invalid_replacement_field_rule(Parser *p)
+invalid_fstring_replacement_field_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -25391,7 +25883,7 @@ invalid_replacement_field_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> invalid_replacement_field[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{' '='"));
+        D(fprintf(stderr, "%*c> invalid_fstring_replacement_field[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{' '='"));
         Token * _literal;
         Token * a;
         if (
@@ -25400,7 +25892,7 @@ invalid_replacement_field_rule(Parser *p)
             (a = _PyPegen_expect_token(p, 22))  // token='='
         )
         {
-            D(fprintf(stderr, "%*c+ invalid_replacement_field[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' '='"));
+            D(fprintf(stderr, "%*c+ invalid_fstring_replacement_field[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' '='"));
             _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( a , "f-string: valid expression required before '='" );
             if (_res == NULL && PyErr_Occurred()) {
                 p->error_indicator = 1;
@@ -25410,7 +25902,7 @@ invalid_replacement_field_rule(Parser *p)
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s invalid_replacement_field[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s invalid_fstring_replacement_field[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'{' '='"));
     }
     { // '{' '!'
@@ -25418,7 +25910,7 @@ invalid_replacement_field_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> invalid_replacement_field[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{' '!'"));
+        D(fprintf(stderr, "%*c> invalid_fstring_replacement_field[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{' '!'"));
         Token * _literal;
         Token * a;
         if (
@@ -25427,7 +25919,7 @@ invalid_replacement_field_rule(Parser *p)
             (a = _PyPegen_expect_token(p, 54))  // token='!'
         )
         {
-            D(fprintf(stderr, "%*c+ invalid_replacement_field[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' '!'"));
+            D(fprintf(stderr, "%*c+ invalid_fstring_replacement_field[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' '!'"));
             _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( a , "f-string: valid expression required before '!'" );
             if (_res == NULL && PyErr_Occurred()) {
                 p->error_indicator = 1;
@@ -25437,7 +25929,7 @@ invalid_replacement_field_rule(Parser *p)
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s invalid_replacement_field[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s invalid_fstring_replacement_field[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'{' '!'"));
     }
     { // '{' ':'
@@ -25445,7 +25937,7 @@ invalid_replacement_field_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> invalid_replacement_field[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{' ':'"));
+        D(fprintf(stderr, "%*c> invalid_fstring_replacement_field[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{' ':'"));
         Token * _literal;
         Token * a;
         if (
@@ -25454,7 +25946,7 @@ invalid_replacement_field_rule(Parser *p)
             (a = _PyPegen_expect_token(p, 11))  // token=':'
         )
         {
-            D(fprintf(stderr, "%*c+ invalid_replacement_field[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' ':'"));
+            D(fprintf(stderr, "%*c+ invalid_fstring_replacement_field[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' ':'"));
             _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( a , "f-string: valid expression required before ':'" );
             if (_res == NULL && PyErr_Occurred()) {
                 p->error_indicator = 1;
@@ -25464,7 +25956,7 @@ invalid_replacement_field_rule(Parser *p)
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s invalid_replacement_field[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s invalid_fstring_replacement_field[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'{' ':'"));
     }
     { // '{' '}'
@@ -25472,7 +25964,7 @@ invalid_replacement_field_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> invalid_replacement_field[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{' '}'"));
+        D(fprintf(stderr, "%*c> invalid_fstring_replacement_field[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{' '}'"));
         Token * _literal;
         Token * a;
         if (
@@ -25481,7 +25973,7 @@ invalid_replacement_field_rule(Parser *p)
             (a = _PyPegen_expect_token(p, 26))  // token='}'
         )
         {
-            D(fprintf(stderr, "%*c+ invalid_replacement_field[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' '}'"));
+            D(fprintf(stderr, "%*c+ invalid_fstring_replacement_field[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' '}'"));
             _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( a , "f-string: valid expression required before '}'" );
             if (_res == NULL && PyErr_Occurred()) {
                 p->error_indicator = 1;
@@ -25491,7 +25983,7 @@ invalid_replacement_field_rule(Parser *p)
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s invalid_replacement_field[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s invalid_fstring_replacement_field[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'{' '}'"));
     }
     { // '{' !annotated_rhs
@@ -25499,7 +25991,7 @@ invalid_replacement_field_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> invalid_replacement_field[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{' !annotated_rhs"));
+        D(fprintf(stderr, "%*c> invalid_fstring_replacement_field[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{' !annotated_rhs"));
         Token * _literal;
         if (
             (_literal = _PyPegen_expect_token(p, 25))  // token='{'
@@ -25507,7 +25999,7 @@ invalid_replacement_field_rule(Parser *p)
             _PyPegen_lookahead(0, (void *(*)(Parser *)) annotated_rhs_rule, p)
         )
         {
-            D(fprintf(stderr, "%*c+ invalid_replacement_field[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' !annotated_rhs"));
+            D(fprintf(stderr, "%*c+ invalid_fstring_replacement_field[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' !annotated_rhs"));
             _res = RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN ( "f-string: expecting a valid expression after '{'" );
             if (_res == NULL && PyErr_Occurred()) {
                 p->error_indicator = 1;
@@ -25517,7 +26009,7 @@ invalid_replacement_field_rule(Parser *p)
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s invalid_replacement_field[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s invalid_fstring_replacement_field[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'{' !annotated_rhs"));
     }
     { // '{' annotated_rhs !('=' | '!' | ':' | '}')
@@ -25525,7 +26017,7 @@ invalid_replacement_field_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> invalid_replacement_field[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{' annotated_rhs !('=' | '!' | ':' | '}')"));
+        D(fprintf(stderr, "%*c> invalid_fstring_replacement_field[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{' annotated_rhs !('=' | '!' | ':' | '}')"));
         Token * _literal;
         expr_ty annotated_rhs_var;
         if (
@@ -25533,10 +26025,10 @@ invalid_replacement_field_rule(Parser *p)
             &&
             (annotated_rhs_var = annotated_rhs_rule(p))  // annotated_rhs
             &&
-            _PyPegen_lookahead(0, (void *(*)(Parser *)) _tmp_146_rule, p)
+            _PyPegen_lookahead(0, (void *(*)(Parser *)) _tmp_148_rule, p)
         )
         {
-            D(fprintf(stderr, "%*c+ invalid_replacement_field[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' annotated_rhs !('=' | '!' | ':' | '}')"));
+            D(fprintf(stderr, "%*c+ invalid_fstring_replacement_field[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' annotated_rhs !('=' | '!' | ':' | '}')"));
             _res = PyErr_Occurred ( ) ? NULL : RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN ( "f-string: expecting '=', or '!', or ':', or '}'" );
             if (_res == NULL && PyErr_Occurred()) {
                 p->error_indicator = 1;
@@ -25546,7 +26038,7 @@ invalid_replacement_field_rule(Parser *p)
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s invalid_replacement_field[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s invalid_fstring_replacement_field[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'{' annotated_rhs !('=' | '!' | ':' | '}')"));
     }
     { // '{' annotated_rhs '=' !('!' | ':' | '}')
@@ -25554,7 +26046,7 @@ invalid_replacement_field_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> invalid_replacement_field[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{' annotated_rhs '=' !('!' | ':' | '}')"));
+        D(fprintf(stderr, "%*c> invalid_fstring_replacement_field[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{' annotated_rhs '=' !('!' | ':' | '}')"));
         Token * _literal;
         Token * _literal_1;
         expr_ty annotated_rhs_var;
@@ -25565,10 +26057,10 @@ invalid_replacement_field_rule(Parser *p)
             &&
             (_literal_1 = _PyPegen_expect_token(p, 22))  // token='='
             &&
-            _PyPegen_lookahead(0, (void *(*)(Parser *)) _tmp_147_rule, p)
+            _PyPegen_lookahead(0, (void *(*)(Parser *)) _tmp_149_rule, p)
         )
         {
-            D(fprintf(stderr, "%*c+ invalid_replacement_field[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' annotated_rhs '=' !('!' | ':' | '}')"));
+            D(fprintf(stderr, "%*c+ invalid_fstring_replacement_field[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' annotated_rhs '=' !('!' | ':' | '}')"));
             _res = PyErr_Occurred ( ) ? NULL : RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN ( "f-string: expecting '!', or ':', or '}'" );
             if (_res == NULL && PyErr_Occurred()) {
                 p->error_indicator = 1;
@@ -25578,20 +26070,20 @@ invalid_replacement_field_rule(Parser *p)
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s invalid_replacement_field[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s invalid_fstring_replacement_field[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'{' annotated_rhs '=' !('!' | ':' | '}')"));
     }
-    { // '{' annotated_rhs '='? invalid_conversion_character
+    { // '{' annotated_rhs '='? invalid_fstring_conversion_character
         if (p->error_indicator) {
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> invalid_replacement_field[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{' annotated_rhs '='? invalid_conversion_character"));
+        D(fprintf(stderr, "%*c> invalid_fstring_replacement_field[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{' annotated_rhs '='? invalid_fstring_conversion_character"));
         Token * _literal;
         void *_opt_var;
         UNUSED(_opt_var); // Silence compiler warnings
         expr_ty annotated_rhs_var;
-        void *invalid_conversion_character_var;
+        void *invalid_fstring_conversion_character_var;
         if (
             (_literal = _PyPegen_expect_token(p, 25))  // token='{'
             &&
@@ -25599,23 +26091,23 @@ invalid_replacement_field_rule(Parser *p)
             &&
             (_opt_var = _PyPegen_expect_token(p, 22), !p->error_indicator)  // '='?
             &&
-            (invalid_conversion_character_var = invalid_conversion_character_rule(p))  // invalid_conversion_character
+            (invalid_fstring_conversion_character_var = invalid_fstring_conversion_character_rule(p))  // invalid_fstring_conversion_character
         )
         {
-            D(fprintf(stderr, "%*c+ invalid_replacement_field[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' annotated_rhs '='? invalid_conversion_character"));
-            _res = _PyPegen_dummy_name(p, _literal, annotated_rhs_var, _opt_var, invalid_conversion_character_var);
+            D(fprintf(stderr, "%*c+ invalid_fstring_replacement_field[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' annotated_rhs '='? invalid_fstring_conversion_character"));
+            _res = _PyPegen_dummy_name(p, _literal, annotated_rhs_var, _opt_var, invalid_fstring_conversion_character_var);
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s invalid_replacement_field[%d-%d]: %s failed!\n", p->level, ' ',
-                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'{' annotated_rhs '='? invalid_conversion_character"));
+        D(fprintf(stderr, "%*c%s invalid_fstring_replacement_field[%d-%d]: %s failed!\n", p->level, ' ',
+                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'{' annotated_rhs '='? invalid_fstring_conversion_character"));
     }
     { // '{' annotated_rhs '='? ['!' NAME] !(':' | '}')
         if (p->error_indicator) {
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> invalid_replacement_field[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{' annotated_rhs '='? ['!' NAME] !(':' | '}')"));
+        D(fprintf(stderr, "%*c> invalid_fstring_replacement_field[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{' annotated_rhs '='? ['!' NAME] !(':' | '}')"));
         Token * _literal;
         void *_opt_var;
         UNUSED(_opt_var); // Silence compiler warnings
@@ -25629,12 +26121,12 @@ invalid_replacement_field_rule(Parser *p)
             &&
             (_opt_var = _PyPegen_expect_token(p, 22), !p->error_indicator)  // '='?
             &&
-            (_opt_var_1 = _tmp_148_rule(p), !p->error_indicator)  // ['!' NAME]
+            (_opt_var_1 = _tmp_150_rule(p), !p->error_indicator)  // ['!' NAME]
             &&
-            _PyPegen_lookahead(0, (void *(*)(Parser *)) _tmp_149_rule, p)
+            _PyPegen_lookahead(0, (void *(*)(Parser *)) _tmp_151_rule, p)
         )
         {
-            D(fprintf(stderr, "%*c+ invalid_replacement_field[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' annotated_rhs '='? ['!' NAME] !(':' | '}')"));
+            D(fprintf(stderr, "%*c+ invalid_fstring_replacement_field[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' annotated_rhs '='? ['!' NAME] !(':' | '}')"));
             _res = PyErr_Occurred ( ) ? NULL : RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN ( "f-string: expecting ':' or '}'" );
             if (_res == NULL && PyErr_Occurred()) {
                 p->error_indicator = 1;
@@ -25644,7 +26136,7 @@ invalid_replacement_field_rule(Parser *p)
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s invalid_replacement_field[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s invalid_fstring_replacement_field[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'{' annotated_rhs '='? ['!' NAME] !(':' | '}')"));
     }
     { // '{' annotated_rhs '='? ['!' NAME] ':' fstring_format_spec* !'}'
@@ -25652,7 +26144,7 @@ invalid_replacement_field_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> invalid_replacement_field[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{' annotated_rhs '='? ['!' NAME] ':' fstring_format_spec* !'}'"));
+        D(fprintf(stderr, "%*c> invalid_fstring_replacement_field[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{' annotated_rhs '='? ['!' NAME] ':' fstring_format_spec* !'}'"));
         Token * _literal;
         Token * _literal_1;
         asdl_seq * _loop0_77_var;
@@ -25668,7 +26160,7 @@ invalid_replacement_field_rule(Parser *p)
             &&
             (_opt_var = _PyPegen_expect_token(p, 22), !p->error_indicator)  // '='?
             &&
-            (_opt_var_1 = _tmp_148_rule(p), !p->error_indicator)  // ['!' NAME]
+            (_opt_var_1 = _tmp_150_rule(p), !p->error_indicator)  // ['!' NAME]
             &&
             (_literal_1 = _PyPegen_expect_token(p, 11))  // token=':'
             &&
@@ -25677,7 +26169,7 @@ invalid_replacement_field_rule(Parser *p)
             _PyPegen_lookahead_with_int(0, _PyPegen_expect_token, p, 26)  // token='}'
         )
         {
-            D(fprintf(stderr, "%*c+ invalid_replacement_field[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' annotated_rhs '='? ['!' NAME] ':' fstring_format_spec* !'}'"));
+            D(fprintf(stderr, "%*c+ invalid_fstring_replacement_field[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' annotated_rhs '='? ['!' NAME] ':' fstring_format_spec* !'}'"));
             _res = PyErr_Occurred ( ) ? NULL : RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN ( "f-string: expecting '}', or format specs" );
             if (_res == NULL && PyErr_Occurred()) {
                 p->error_indicator = 1;
@@ -25687,7 +26179,7 @@ invalid_replacement_field_rule(Parser *p)
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s invalid_replacement_field[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s invalid_fstring_replacement_field[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'{' annotated_rhs '='? ['!' NAME] ':' fstring_format_spec* !'}'"));
     }
     { // '{' annotated_rhs '='? ['!' NAME] !'}'
@@ -25695,7 +26187,7 @@ invalid_replacement_field_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> invalid_replacement_field[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{' annotated_rhs '='? ['!' NAME] !'}'"));
+        D(fprintf(stderr, "%*c> invalid_fstring_replacement_field[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{' annotated_rhs '='? ['!' NAME] !'}'"));
         Token * _literal;
         void *_opt_var;
         UNUSED(_opt_var); // Silence compiler warnings
@@ -25709,12 +26201,12 @@ invalid_replacement_field_rule(Parser *p)
             &&
             (_opt_var = _PyPegen_expect_token(p, 22), !p->error_indicator)  // '='?
             &&
-            (_opt_var_1 = _tmp_148_rule(p), !p->error_indicator)  // ['!' NAME]
+            (_opt_var_1 = _tmp_150_rule(p), !p->error_indicator)  // ['!' NAME]
             &&
             _PyPegen_lookahead_with_int(0, _PyPegen_expect_token, p, 26)  // token='}'
         )
         {
-            D(fprintf(stderr, "%*c+ invalid_replacement_field[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' annotated_rhs '='? ['!' NAME] !'}'"));
+            D(fprintf(stderr, "%*c+ invalid_fstring_replacement_field[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' annotated_rhs '='? ['!' NAME] !'}'"));
             _res = PyErr_Occurred ( ) ? NULL : RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN ( "f-string: expecting '}'" );
             if (_res == NULL && PyErr_Occurred()) {
                 p->error_indicator = 1;
@@ -25724,7 +26216,7 @@ invalid_replacement_field_rule(Parser *p)
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s invalid_replacement_field[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s invalid_fstring_replacement_field[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'{' annotated_rhs '='? ['!' NAME] !'}'"));
     }
     _res = NULL;
@@ -25733,9 +26225,9 @@ invalid_replacement_field_rule(Parser *p)
     return _res;
 }
 
-// invalid_conversion_character: '!' &(':' | '}') | '!' !NAME
+// invalid_fstring_conversion_character: '!' &(':' | '}') | '!' !NAME
 static void *
-invalid_conversion_character_rule(Parser *p)
+invalid_fstring_conversion_character_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -25751,15 +26243,15 @@ invalid_conversion_character_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> invalid_conversion_character[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'!' &(':' | '}')"));
+        D(fprintf(stderr, "%*c> invalid_fstring_conversion_character[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'!' &(':' | '}')"));
         Token * _literal;
         if (
             (_literal = _PyPegen_expect_token(p, 54))  // token='!'
             &&
-            _PyPegen_lookahead(1, (void *(*)(Parser *)) _tmp_149_rule, p)
+            _PyPegen_lookahead(1, (void *(*)(Parser *)) _tmp_151_rule, p)
         )
         {
-            D(fprintf(stderr, "%*c+ invalid_conversion_character[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'!' &(':' | '}')"));
+            D(fprintf(stderr, "%*c+ invalid_fstring_conversion_character[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'!' &(':' | '}')"));
             _res = RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN ( "f-string: missing conversion character" );
             if (_res == NULL && PyErr_Occurred()) {
                 p->error_indicator = 1;
@@ -25769,7 +26261,7 @@ invalid_conversion_character_rule(Parser *p)
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s invalid_conversion_character[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s invalid_fstring_conversion_character[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'!' &(':' | '}')"));
     }
     { // '!' !NAME
@@ -25777,7 +26269,7 @@ invalid_conversion_character_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> invalid_conversion_character[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'!' !NAME"));
+        D(fprintf(stderr, "%*c> invalid_fstring_conversion_character[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'!' !NAME"));
         Token * _literal;
         if (
             (_literal = _PyPegen_expect_token(p, 54))  // token='!'
@@ -25785,7 +26277,7 @@ invalid_conversion_character_rule(Parser *p)
             _PyPegen_lookahead_with_name(0, _PyPegen_name_token, p)
         )
         {
-            D(fprintf(stderr, "%*c+ invalid_conversion_character[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'!' !NAME"));
+            D(fprintf(stderr, "%*c+ invalid_fstring_conversion_character[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'!' !NAME"));
             _res = RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN ( "f-string: invalid conversion character" );
             if (_res == NULL && PyErr_Occurred()) {
                 p->error_indicator = 1;
@@ -25795,7 +26287,449 @@ invalid_conversion_character_rule(Parser *p)
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s invalid_conversion_character[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s invalid_fstring_conversion_character[%d-%d]: %s failed!\n", p->level, ' ',
+                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'!' !NAME"));
+    }
+    _res = NULL;
+  done:
+    p->level--;
+    return _res;
+}
+
+// invalid_tstring_replacement_field:
+//     | '{' '='
+//     | '{' '!'
+//     | '{' ':'
+//     | '{' '}'
+//     | '{' !annotated_rhs
+//     | '{' annotated_rhs !('=' | '!' | ':' | '}')
+//     | '{' annotated_rhs '=' !('!' | ':' | '}')
+//     | '{' annotated_rhs '='? invalid_tstring_conversion_character
+//     | '{' annotated_rhs '='? ['!' NAME] !(':' | '}')
+//     | '{' annotated_rhs '='? ['!' NAME] ':' fstring_format_spec* !'}'
+//     | '{' annotated_rhs '='? ['!' NAME] !'}'
+static void *
+invalid_tstring_replacement_field_rule(Parser *p)
+{
+    if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
+        _Pypegen_stack_overflow(p);
+    }
+    if (p->error_indicator) {
+        p->level--;
+        return NULL;
+    }
+    void * _res = NULL;
+    int _mark = p->mark;
+    { // '{' '='
+        if (p->error_indicator) {
+            p->level--;
+            return NULL;
+        }
+        D(fprintf(stderr, "%*c> invalid_tstring_replacement_field[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{' '='"));
+        Token * _literal;
+        Token * a;
+        if (
+            (_literal = _PyPegen_expect_token(p, 25))  // token='{'
+            &&
+            (a = _PyPegen_expect_token(p, 22))  // token='='
+        )
+        {
+            D(fprintf(stderr, "%*c+ invalid_tstring_replacement_field[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' '='"));
+            _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( a , "t-string: valid expression required before '='" );
+            if (_res == NULL && PyErr_Occurred()) {
+                p->error_indicator = 1;
+                p->level--;
+                return NULL;
+            }
+            goto done;
+        }
+        p->mark = _mark;
+        D(fprintf(stderr, "%*c%s invalid_tstring_replacement_field[%d-%d]: %s failed!\n", p->level, ' ',
+                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'{' '='"));
+    }
+    { // '{' '!'
+        if (p->error_indicator) {
+            p->level--;
+            return NULL;
+        }
+        D(fprintf(stderr, "%*c> invalid_tstring_replacement_field[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{' '!'"));
+        Token * _literal;
+        Token * a;
+        if (
+            (_literal = _PyPegen_expect_token(p, 25))  // token='{'
+            &&
+            (a = _PyPegen_expect_token(p, 54))  // token='!'
+        )
+        {
+            D(fprintf(stderr, "%*c+ invalid_tstring_replacement_field[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' '!'"));
+            _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( a , "t-string: valid expression required before '!'" );
+            if (_res == NULL && PyErr_Occurred()) {
+                p->error_indicator = 1;
+                p->level--;
+                return NULL;
+            }
+            goto done;
+        }
+        p->mark = _mark;
+        D(fprintf(stderr, "%*c%s invalid_tstring_replacement_field[%d-%d]: %s failed!\n", p->level, ' ',
+                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'{' '!'"));
+    }
+    { // '{' ':'
+        if (p->error_indicator) {
+            p->level--;
+            return NULL;
+        }
+        D(fprintf(stderr, "%*c> invalid_tstring_replacement_field[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{' ':'"));
+        Token * _literal;
+        Token * a;
+        if (
+            (_literal = _PyPegen_expect_token(p, 25))  // token='{'
+            &&
+            (a = _PyPegen_expect_token(p, 11))  // token=':'
+        )
+        {
+            D(fprintf(stderr, "%*c+ invalid_tstring_replacement_field[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' ':'"));
+            _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( a , "t-string: valid expression required before ':'" );
+            if (_res == NULL && PyErr_Occurred()) {
+                p->error_indicator = 1;
+                p->level--;
+                return NULL;
+            }
+            goto done;
+        }
+        p->mark = _mark;
+        D(fprintf(stderr, "%*c%s invalid_tstring_replacement_field[%d-%d]: %s failed!\n", p->level, ' ',
+                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'{' ':'"));
+    }
+    { // '{' '}'
+        if (p->error_indicator) {
+            p->level--;
+            return NULL;
+        }
+        D(fprintf(stderr, "%*c> invalid_tstring_replacement_field[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{' '}'"));
+        Token * _literal;
+        Token * a;
+        if (
+            (_literal = _PyPegen_expect_token(p, 25))  // token='{'
+            &&
+            (a = _PyPegen_expect_token(p, 26))  // token='}'
+        )
+        {
+            D(fprintf(stderr, "%*c+ invalid_tstring_replacement_field[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' '}'"));
+            _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( a , "t-string: valid expression required before '}'" );
+            if (_res == NULL && PyErr_Occurred()) {
+                p->error_indicator = 1;
+                p->level--;
+                return NULL;
+            }
+            goto done;
+        }
+        p->mark = _mark;
+        D(fprintf(stderr, "%*c%s invalid_tstring_replacement_field[%d-%d]: %s failed!\n", p->level, ' ',
+                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'{' '}'"));
+    }
+    { // '{' !annotated_rhs
+        if (p->error_indicator) {
+            p->level--;
+            return NULL;
+        }
+        D(fprintf(stderr, "%*c> invalid_tstring_replacement_field[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{' !annotated_rhs"));
+        Token * _literal;
+        if (
+            (_literal = _PyPegen_expect_token(p, 25))  // token='{'
+            &&
+            _PyPegen_lookahead(0, (void *(*)(Parser *)) annotated_rhs_rule, p)
+        )
+        {
+            D(fprintf(stderr, "%*c+ invalid_tstring_replacement_field[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' !annotated_rhs"));
+            _res = RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN ( "t-string: expecting a valid expression after '{'" );
+            if (_res == NULL && PyErr_Occurred()) {
+                p->error_indicator = 1;
+                p->level--;
+                return NULL;
+            }
+            goto done;
+        }
+        p->mark = _mark;
+        D(fprintf(stderr, "%*c%s invalid_tstring_replacement_field[%d-%d]: %s failed!\n", p->level, ' ',
+                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'{' !annotated_rhs"));
+    }
+    { // '{' annotated_rhs !('=' | '!' | ':' | '}')
+        if (p->error_indicator) {
+            p->level--;
+            return NULL;
+        }
+        D(fprintf(stderr, "%*c> invalid_tstring_replacement_field[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{' annotated_rhs !('=' | '!' | ':' | '}')"));
+        Token * _literal;
+        expr_ty annotated_rhs_var;
+        if (
+            (_literal = _PyPegen_expect_token(p, 25))  // token='{'
+            &&
+            (annotated_rhs_var = annotated_rhs_rule(p))  // annotated_rhs
+            &&
+            _PyPegen_lookahead(0, (void *(*)(Parser *)) _tmp_148_rule, p)
+        )
+        {
+            D(fprintf(stderr, "%*c+ invalid_tstring_replacement_field[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' annotated_rhs !('=' | '!' | ':' | '}')"));
+            _res = PyErr_Occurred ( ) ? NULL : RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN ( "t-string: expecting '=', or '!', or ':', or '}'" );
+            if (_res == NULL && PyErr_Occurred()) {
+                p->error_indicator = 1;
+                p->level--;
+                return NULL;
+            }
+            goto done;
+        }
+        p->mark = _mark;
+        D(fprintf(stderr, "%*c%s invalid_tstring_replacement_field[%d-%d]: %s failed!\n", p->level, ' ',
+                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'{' annotated_rhs !('=' | '!' | ':' | '}')"));
+    }
+    { // '{' annotated_rhs '=' !('!' | ':' | '}')
+        if (p->error_indicator) {
+            p->level--;
+            return NULL;
+        }
+        D(fprintf(stderr, "%*c> invalid_tstring_replacement_field[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{' annotated_rhs '=' !('!' | ':' | '}')"));
+        Token * _literal;
+        Token * _literal_1;
+        expr_ty annotated_rhs_var;
+        if (
+            (_literal = _PyPegen_expect_token(p, 25))  // token='{'
+            &&
+            (annotated_rhs_var = annotated_rhs_rule(p))  // annotated_rhs
+            &&
+            (_literal_1 = _PyPegen_expect_token(p, 22))  // token='='
+            &&
+            _PyPegen_lookahead(0, (void *(*)(Parser *)) _tmp_149_rule, p)
+        )
+        {
+            D(fprintf(stderr, "%*c+ invalid_tstring_replacement_field[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' annotated_rhs '=' !('!' | ':' | '}')"));
+            _res = PyErr_Occurred ( ) ? NULL : RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN ( "t-string: expecting '!', or ':', or '}'" );
+            if (_res == NULL && PyErr_Occurred()) {
+                p->error_indicator = 1;
+                p->level--;
+                return NULL;
+            }
+            goto done;
+        }
+        p->mark = _mark;
+        D(fprintf(stderr, "%*c%s invalid_tstring_replacement_field[%d-%d]: %s failed!\n", p->level, ' ',
+                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'{' annotated_rhs '=' !('!' | ':' | '}')"));
+    }
+    { // '{' annotated_rhs '='? invalid_tstring_conversion_character
+        if (p->error_indicator) {
+            p->level--;
+            return NULL;
+        }
+        D(fprintf(stderr, "%*c> invalid_tstring_replacement_field[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{' annotated_rhs '='? invalid_tstring_conversion_character"));
+        Token * _literal;
+        void *_opt_var;
+        UNUSED(_opt_var); // Silence compiler warnings
+        expr_ty annotated_rhs_var;
+        void *invalid_tstring_conversion_character_var;
+        if (
+            (_literal = _PyPegen_expect_token(p, 25))  // token='{'
+            &&
+            (annotated_rhs_var = annotated_rhs_rule(p))  // annotated_rhs
+            &&
+            (_opt_var = _PyPegen_expect_token(p, 22), !p->error_indicator)  // '='?
+            &&
+            (invalid_tstring_conversion_character_var = invalid_tstring_conversion_character_rule(p))  // invalid_tstring_conversion_character
+        )
+        {
+            D(fprintf(stderr, "%*c+ invalid_tstring_replacement_field[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' annotated_rhs '='? invalid_tstring_conversion_character"));
+            _res = _PyPegen_dummy_name(p, _literal, annotated_rhs_var, _opt_var, invalid_tstring_conversion_character_var);
+            goto done;
+        }
+        p->mark = _mark;
+        D(fprintf(stderr, "%*c%s invalid_tstring_replacement_field[%d-%d]: %s failed!\n", p->level, ' ',
+                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'{' annotated_rhs '='? invalid_tstring_conversion_character"));
+    }
+    { // '{' annotated_rhs '='? ['!' NAME] !(':' | '}')
+        if (p->error_indicator) {
+            p->level--;
+            return NULL;
+        }
+        D(fprintf(stderr, "%*c> invalid_tstring_replacement_field[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{' annotated_rhs '='? ['!' NAME] !(':' | '}')"));
+        Token * _literal;
+        void *_opt_var;
+        UNUSED(_opt_var); // Silence compiler warnings
+        void *_opt_var_1;
+        UNUSED(_opt_var_1); // Silence compiler warnings
+        expr_ty annotated_rhs_var;
+        if (
+            (_literal = _PyPegen_expect_token(p, 25))  // token='{'
+            &&
+            (annotated_rhs_var = annotated_rhs_rule(p))  // annotated_rhs
+            &&
+            (_opt_var = _PyPegen_expect_token(p, 22), !p->error_indicator)  // '='?
+            &&
+            (_opt_var_1 = _tmp_150_rule(p), !p->error_indicator)  // ['!' NAME]
+            &&
+            _PyPegen_lookahead(0, (void *(*)(Parser *)) _tmp_151_rule, p)
+        )
+        {
+            D(fprintf(stderr, "%*c+ invalid_tstring_replacement_field[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' annotated_rhs '='? ['!' NAME] !(':' | '}')"));
+            _res = PyErr_Occurred ( ) ? NULL : RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN ( "t-string: expecting ':' or '}'" );
+            if (_res == NULL && PyErr_Occurred()) {
+                p->error_indicator = 1;
+                p->level--;
+                return NULL;
+            }
+            goto done;
+        }
+        p->mark = _mark;
+        D(fprintf(stderr, "%*c%s invalid_tstring_replacement_field[%d-%d]: %s failed!\n", p->level, ' ',
+                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'{' annotated_rhs '='? ['!' NAME] !(':' | '}')"));
+    }
+    { // '{' annotated_rhs '='? ['!' NAME] ':' fstring_format_spec* !'}'
+        if (p->error_indicator) {
+            p->level--;
+            return NULL;
+        }
+        D(fprintf(stderr, "%*c> invalid_tstring_replacement_field[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{' annotated_rhs '='? ['!' NAME] ':' fstring_format_spec* !'}'"));
+        Token * _literal;
+        Token * _literal_1;
+        asdl_seq * _loop0_77_var;
+        void *_opt_var;
+        UNUSED(_opt_var); // Silence compiler warnings
+        void *_opt_var_1;
+        UNUSED(_opt_var_1); // Silence compiler warnings
+        expr_ty annotated_rhs_var;
+        if (
+            (_literal = _PyPegen_expect_token(p, 25))  // token='{'
+            &&
+            (annotated_rhs_var = annotated_rhs_rule(p))  // annotated_rhs
+            &&
+            (_opt_var = _PyPegen_expect_token(p, 22), !p->error_indicator)  // '='?
+            &&
+            (_opt_var_1 = _tmp_150_rule(p), !p->error_indicator)  // ['!' NAME]
+            &&
+            (_literal_1 = _PyPegen_expect_token(p, 11))  // token=':'
+            &&
+            (_loop0_77_var = _loop0_77_rule(p))  // fstring_format_spec*
+            &&
+            _PyPegen_lookahead_with_int(0, _PyPegen_expect_token, p, 26)  // token='}'
+        )
+        {
+            D(fprintf(stderr, "%*c+ invalid_tstring_replacement_field[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' annotated_rhs '='? ['!' NAME] ':' fstring_format_spec* !'}'"));
+            _res = PyErr_Occurred ( ) ? NULL : RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN ( "t-string: expecting '}', or format specs" );
+            if (_res == NULL && PyErr_Occurred()) {
+                p->error_indicator = 1;
+                p->level--;
+                return NULL;
+            }
+            goto done;
+        }
+        p->mark = _mark;
+        D(fprintf(stderr, "%*c%s invalid_tstring_replacement_field[%d-%d]: %s failed!\n", p->level, ' ',
+                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'{' annotated_rhs '='? ['!' NAME] ':' fstring_format_spec* !'}'"));
+    }
+    { // '{' annotated_rhs '='? ['!' NAME] !'}'
+        if (p->error_indicator) {
+            p->level--;
+            return NULL;
+        }
+        D(fprintf(stderr, "%*c> invalid_tstring_replacement_field[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{' annotated_rhs '='? ['!' NAME] !'}'"));
+        Token * _literal;
+        void *_opt_var;
+        UNUSED(_opt_var); // Silence compiler warnings
+        void *_opt_var_1;
+        UNUSED(_opt_var_1); // Silence compiler warnings
+        expr_ty annotated_rhs_var;
+        if (
+            (_literal = _PyPegen_expect_token(p, 25))  // token='{'
+            &&
+            (annotated_rhs_var = annotated_rhs_rule(p))  // annotated_rhs
+            &&
+            (_opt_var = _PyPegen_expect_token(p, 22), !p->error_indicator)  // '='?
+            &&
+            (_opt_var_1 = _tmp_150_rule(p), !p->error_indicator)  // ['!' NAME]
+            &&
+            _PyPegen_lookahead_with_int(0, _PyPegen_expect_token, p, 26)  // token='}'
+        )
+        {
+            D(fprintf(stderr, "%*c+ invalid_tstring_replacement_field[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' annotated_rhs '='? ['!' NAME] !'}'"));
+            _res = PyErr_Occurred ( ) ? NULL : RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN ( "t-string: expecting '}'" );
+            if (_res == NULL && PyErr_Occurred()) {
+                p->error_indicator = 1;
+                p->level--;
+                return NULL;
+            }
+            goto done;
+        }
+        p->mark = _mark;
+        D(fprintf(stderr, "%*c%s invalid_tstring_replacement_field[%d-%d]: %s failed!\n", p->level, ' ',
+                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'{' annotated_rhs '='? ['!' NAME] !'}'"));
+    }
+    _res = NULL;
+  done:
+    p->level--;
+    return _res;
+}
+
+// invalid_tstring_conversion_character: '!' &(':' | '}') | '!' !NAME
+static void *
+invalid_tstring_conversion_character_rule(Parser *p)
+{
+    if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
+        _Pypegen_stack_overflow(p);
+    }
+    if (p->error_indicator) {
+        p->level--;
+        return NULL;
+    }
+    void * _res = NULL;
+    int _mark = p->mark;
+    { // '!' &(':' | '}')
+        if (p->error_indicator) {
+            p->level--;
+            return NULL;
+        }
+        D(fprintf(stderr, "%*c> invalid_tstring_conversion_character[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'!' &(':' | '}')"));
+        Token * _literal;
+        if (
+            (_literal = _PyPegen_expect_token(p, 54))  // token='!'
+            &&
+            _PyPegen_lookahead(1, (void *(*)(Parser *)) _tmp_151_rule, p)
+        )
+        {
+            D(fprintf(stderr, "%*c+ invalid_tstring_conversion_character[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'!' &(':' | '}')"));
+            _res = RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN ( "t-string: missing conversion character" );
+            if (_res == NULL && PyErr_Occurred()) {
+                p->error_indicator = 1;
+                p->level--;
+                return NULL;
+            }
+            goto done;
+        }
+        p->mark = _mark;
+        D(fprintf(stderr, "%*c%s invalid_tstring_conversion_character[%d-%d]: %s failed!\n", p->level, ' ',
+                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'!' &(':' | '}')"));
+    }
+    { // '!' !NAME
+        if (p->error_indicator) {
+            p->level--;
+            return NULL;
+        }
+        D(fprintf(stderr, "%*c> invalid_tstring_conversion_character[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'!' !NAME"));
+        Token * _literal;
+        if (
+            (_literal = _PyPegen_expect_token(p, 54))  // token='!'
+            &&
+            _PyPegen_lookahead_with_name(0, _PyPegen_name_token, p)
+        )
+        {
+            D(fprintf(stderr, "%*c+ invalid_tstring_conversion_character[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'!' !NAME"));
+            _res = RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN ( "t-string: invalid conversion character" );
+            if (_res == NULL && PyErr_Occurred()) {
+                p->error_indicator = 1;
+                p->level--;
+                return NULL;
+            }
+            goto done;
+        }
+        p->mark = _mark;
+        D(fprintf(stderr, "%*c%s invalid_tstring_conversion_character[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'!' !NAME"));
     }
     _res = NULL;
@@ -25823,16 +26757,16 @@ invalid_arithmetic_rule(Parser *p)
             return NULL;
         }
         D(fprintf(stderr, "%*c> invalid_arithmetic[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "sum ('+' | '-' | '*' | '/' | '%' | '//' | '@') 'not' inversion"));
-        void *_tmp_150_var;
+        void *_tmp_152_var;
         Token * a;
         expr_ty b;
         expr_ty sum_var;
         if (
             (sum_var = sum_rule(p))  // sum
             &&
-            (_tmp_150_var = _tmp_150_rule(p))  // '+' | '-' | '*' | '/' | '%' | '//' | '@'
+            (_tmp_152_var = _tmp_152_rule(p))  // '+' | '-' | '*' | '/' | '%' | '//' | '@'
             &&
-            (a = _PyPegen_expect_token(p, 698))  // token='not'
+            (a = _PyPegen_expect_token(p, 699))  // token='not'
             &&
             (b = inversion_rule(p))  // inversion
         )
@@ -25875,13 +26809,13 @@ invalid_factor_rule(Parser *p)
             return NULL;
         }
         D(fprintf(stderr, "%*c> invalid_factor[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('+' | '-' | '~') 'not' factor"));
-        void *_tmp_151_var;
+        void *_tmp_153_var;
         Token * a;
         expr_ty b;
         if (
-            (_tmp_151_var = _tmp_151_rule(p))  // '+' | '-' | '~'
+            (_tmp_153_var = _tmp_153_rule(p))  // '+' | '-' | '~'
             &&
-            (a = _PyPegen_expect_token(p, 698))  // token='not'
+            (a = _PyPegen_expect_token(p, 699))  // token='not'
             &&
             (b = factor_rule(p))  // factor
         )
@@ -26228,7 +27162,7 @@ _tmp_5_rule(Parser *p)
         D(fprintf(stderr, "%*c> _tmp_5[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'import'"));
         Token * _keyword;
         if (
-            (_keyword = _PyPegen_expect_token(p, 631))  // token='import'
+            (_keyword = _PyPegen_expect_token(p, 632))  // token='import'
         )
         {
             D(fprintf(stderr, "%*c+ _tmp_5[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'import'"));
@@ -26247,7 +27181,7 @@ _tmp_5_rule(Parser *p)
         D(fprintf(stderr, "%*c> _tmp_5[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'from'"));
         Token * _keyword;
         if (
-            (_keyword = _PyPegen_expect_token(p, 630))  // token='from'
+            (_keyword = _PyPegen_expect_token(p, 631))  // token='from'
         )
         {
             D(fprintf(stderr, "%*c+ _tmp_5[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'from'"));
@@ -26285,7 +27219,7 @@ _tmp_6_rule(Parser *p)
         D(fprintf(stderr, "%*c> _tmp_6[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'def'"));
         Token * _keyword;
         if (
-            (_keyword = _PyPegen_expect_token(p, 694))  // token='def'
+            (_keyword = _PyPegen_expect_token(p, 695))  // token='def'
         )
         {
             D(fprintf(stderr, "%*c+ _tmp_6[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'def'"));
@@ -26323,7 +27257,7 @@ _tmp_6_rule(Parser *p)
         D(fprintf(stderr, "%*c> _tmp_6[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'async'"));
         Token * _keyword;
         if (
-            (_keyword = _PyPegen_expect_token(p, 693))  // token='async'
+            (_keyword = _PyPegen_expect_token(p, 694))  // token='async'
         )
         {
             D(fprintf(stderr, "%*c+ _tmp_6[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'async'"));
@@ -26361,7 +27295,7 @@ _tmp_7_rule(Parser *p)
         D(fprintf(stderr, "%*c> _tmp_7[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'class'"));
         Token * _keyword;
         if (
-            (_keyword = _PyPegen_expect_token(p, 696))  // token='class'
+            (_keyword = _PyPegen_expect_token(p, 697))  // token='class'
         )
         {
             D(fprintf(stderr, "%*c+ _tmp_7[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'class'"));
@@ -26418,7 +27352,7 @@ _tmp_8_rule(Parser *p)
         D(fprintf(stderr, "%*c> _tmp_8[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'with'"));
         Token * _keyword;
         if (
-            (_keyword = _PyPegen_expect_token(p, 642))  // token='with'
+            (_keyword = _PyPegen_expect_token(p, 643))  // token='with'
         )
         {
             D(fprintf(stderr, "%*c+ _tmp_8[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'with'"));
@@ -26437,7 +27371,7 @@ _tmp_8_rule(Parser *p)
         D(fprintf(stderr, "%*c> _tmp_8[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'async'"));
         Token * _keyword;
         if (
-            (_keyword = _PyPegen_expect_token(p, 693))  // token='async'
+            (_keyword = _PyPegen_expect_token(p, 694))  // token='async'
         )
         {
             D(fprintf(stderr, "%*c+ _tmp_8[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'async'"));
@@ -26475,7 +27409,7 @@ _tmp_9_rule(Parser *p)
         D(fprintf(stderr, "%*c> _tmp_9[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'for'"));
         Token * _keyword;
         if (
-            (_keyword = _PyPegen_expect_token(p, 689))  // token='for'
+            (_keyword = _PyPegen_expect_token(p, 690))  // token='for'
         )
         {
             D(fprintf(stderr, "%*c+ _tmp_9[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'for'"));
@@ -26494,7 +27428,7 @@ _tmp_9_rule(Parser *p)
         D(fprintf(stderr, "%*c> _tmp_9[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'async'"));
         Token * _keyword;
         if (
-            (_keyword = _PyPegen_expect_token(p, 693))  // token='async'
+            (_keyword = _PyPegen_expect_token(p, 694))  // token='async'
         )
         {
             D(fprintf(stderr, "%*c+ _tmp_9[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'async'"));
@@ -26653,12 +27587,12 @@ _loop1_12_rule(Parser *p)
             return NULL;
         }
         D(fprintf(stderr, "%*c> _loop1_12[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(star_targets '=')"));
-        void *_tmp_152_var;
+        void *_tmp_154_var;
         while (
-            (_tmp_152_var = _tmp_152_rule(p))  // star_targets '='
+            (_tmp_154_var = _tmp_154_rule(p))  // star_targets '='
         )
         {
-            _res = _tmp_152_var;
+            _res = _tmp_154_var;
             if (_n == _children_capacity) {
                 _children_capacity *= 2;
                 void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *));
@@ -26719,7 +27653,7 @@ _tmp_13_rule(Parser *p)
         Token * _keyword;
         expr_ty z;
         if (
-            (_keyword = _PyPegen_expect_token(p, 630))  // token='from'
+            (_keyword = _PyPegen_expect_token(p, 631))  // token='from'
             &&
             (z = expression_rule(p))  // expression
         )
@@ -26991,12 +27925,12 @@ _loop0_18_rule(Parser *p)
             return NULL;
         }
         D(fprintf(stderr, "%*c> _loop0_18[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('.' | '...')"));
-        void *_tmp_153_var;
+        void *_tmp_155_var;
         while (
-            (_tmp_153_var = _tmp_153_rule(p))  // '.' | '...'
+            (_tmp_155_var = _tmp_155_rule(p))  // '.' | '...'
         )
         {
-            _res = _tmp_153_var;
+            _res = _tmp_155_var;
             if (_n == _children_capacity) {
                 _children_capacity *= 2;
                 void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *));
@@ -27058,12 +27992,12 @@ _loop1_19_rule(Parser *p)
             return NULL;
         }
         D(fprintf(stderr, "%*c> _loop1_19[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('.' | '...')"));
-        void *_tmp_153_var;
+        void *_tmp_155_var;
         while (
-            (_tmp_153_var = _tmp_153_rule(p))  // '.' | '...'
+            (_tmp_155_var = _tmp_155_rule(p))  // '.' | '...'
         )
         {
-            _res = _tmp_153_var;
+            _res = _tmp_155_var;
             if (_n == _children_capacity) {
                 _children_capacity *= 2;
                 void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *));
@@ -27241,7 +28175,7 @@ _tmp_22_rule(Parser *p)
         Token * _keyword;
         expr_ty z;
         if (
-            (_keyword = _PyPegen_expect_token(p, 675))  // token='as'
+            (_keyword = _PyPegen_expect_token(p, 676))  // token='as'
             &&
             (z = _PyPegen_name_token(p))  // NAME
         )
@@ -27410,12 +28344,12 @@ _loop1_25_rule(Parser *p)
             return NULL;
         }
         D(fprintf(stderr, "%*c> _loop1_25[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('@' named_expression NEWLINE)"));
-        void *_tmp_154_var;
+        void *_tmp_156_var;
         while (
-            (_tmp_154_var = _tmp_154_rule(p))  // '@' named_expression NEWLINE
+            (_tmp_156_var = _tmp_156_rule(p))  // '@' named_expression NEWLINE
         )
         {
-            _res = _tmp_154_var;
+            _res = _tmp_156_var;
             if (_n == _children_capacity) {
                 _children_capacity *= 2;
                 void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *));
@@ -28549,9 +29483,85 @@ _tmp_42_rule(Parser *p)
     return _res;
 }
 
-// _tmp_43: '.' | '(' | '='
+// _tmp_43: STRING | FSTRING_START | TSTRING_START
 static void *
 _tmp_43_rule(Parser *p)
+{
+    if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
+        _Pypegen_stack_overflow(p);
+    }
+    if (p->error_indicator) {
+        p->level--;
+        return NULL;
+    }
+    void * _res = NULL;
+    int _mark = p->mark;
+    { // STRING
+        if (p->error_indicator) {
+            p->level--;
+            return NULL;
+        }
+        D(fprintf(stderr, "%*c> _tmp_43[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "STRING"));
+        expr_ty string_var;
+        if (
+            (string_var = _PyPegen_string_token(p))  // STRING
+        )
+        {
+            D(fprintf(stderr, "%*c+ _tmp_43[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "STRING"));
+            _res = string_var;
+            goto done;
+        }
+        p->mark = _mark;
+        D(fprintf(stderr, "%*c%s _tmp_43[%d-%d]: %s failed!\n", p->level, ' ',
+                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "STRING"));
+    }
+    { // FSTRING_START
+        if (p->error_indicator) {
+            p->level--;
+            return NULL;
+        }
+        D(fprintf(stderr, "%*c> _tmp_43[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "FSTRING_START"));
+        Token * fstring_start_var;
+        if (
+            (fstring_start_var = _PyPegen_expect_token(p, FSTRING_START))  // token='FSTRING_START'
+        )
+        {
+            D(fprintf(stderr, "%*c+ _tmp_43[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "FSTRING_START"));
+            _res = fstring_start_var;
+            goto done;
+        }
+        p->mark = _mark;
+        D(fprintf(stderr, "%*c%s _tmp_43[%d-%d]: %s failed!\n", p->level, ' ',
+                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "FSTRING_START"));
+    }
+    { // TSTRING_START
+        if (p->error_indicator) {
+            p->level--;
+            return NULL;
+        }
+        D(fprintf(stderr, "%*c> _tmp_43[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "TSTRING_START"));
+        Token * tstring_start_var;
+        if (
+            (tstring_start_var = _PyPegen_expect_token(p, TSTRING_START))  // token='TSTRING_START'
+        )
+        {
+            D(fprintf(stderr, "%*c+ _tmp_43[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "TSTRING_START"));
+            _res = tstring_start_var;
+            goto done;
+        }
+        p->mark = _mark;
+        D(fprintf(stderr, "%*c%s _tmp_43[%d-%d]: %s failed!\n", p->level, ' ',
+                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "TSTRING_START"));
+    }
+    _res = NULL;
+  done:
+    p->level--;
+    return _res;
+}
+
+// _tmp_44: '.' | '(' | '='
+static void *
+_tmp_44_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -28567,18 +29577,18 @@ _tmp_43_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_43[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'.'"));
+        D(fprintf(stderr, "%*c> _tmp_44[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'.'"));
         Token * _literal;
         if (
             (_literal = _PyPegen_expect_token(p, 23))  // token='.'
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_43[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'.'"));
+            D(fprintf(stderr, "%*c+ _tmp_44[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'.'"));
             _res = _literal;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_43[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_44[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'.'"));
     }
     { // '('
@@ -28586,18 +29596,18 @@ _tmp_43_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_43[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'('"));
+        D(fprintf(stderr, "%*c> _tmp_44[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'('"));
         Token * _literal;
         if (
             (_literal = _PyPegen_expect_token(p, 7))  // token='('
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_43[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'('"));
+            D(fprintf(stderr, "%*c+ _tmp_44[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'('"));
             _res = _literal;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_43[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_44[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'('"));
     }
     { // '='
@@ -28605,18 +29615,18 @@ _tmp_43_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_43[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'='"));
+        D(fprintf(stderr, "%*c> _tmp_44[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'='"));
         Token * _literal;
         if (
             (_literal = _PyPegen_expect_token(p, 22))  // token='='
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_43[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'='"));
+            D(fprintf(stderr, "%*c+ _tmp_44[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'='"));
             _res = _literal;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_43[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_44[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'='"));
     }
     _res = NULL;
@@ -28625,9 +29635,9 @@ _tmp_43_rule(Parser *p)
     return _res;
 }
 
-// _loop0_44: ',' maybe_star_pattern
+// _loop0_45: ',' maybe_star_pattern
 static asdl_seq *
-_loop0_44_rule(Parser *p)
+_loop0_45_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -28652,7 +29662,7 @@ _loop0_44_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _loop0_44[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' maybe_star_pattern"));
+        D(fprintf(stderr, "%*c> _loop0_45[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' maybe_star_pattern"));
         Token * _literal;
         pattern_ty elem;
         while (
@@ -28684,7 +29694,7 @@ _loop0_44_rule(Parser *p)
             _mark = p->mark;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _loop0_44[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _loop0_45[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' maybe_star_pattern"));
     }
     asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena);
@@ -28701,9 +29711,9 @@ _loop0_44_rule(Parser *p)
     return _seq;
 }
 
-// _gather_45: maybe_star_pattern _loop0_44
+// _gather_46: maybe_star_pattern _loop0_45
 static asdl_seq *
-_gather_45_rule(Parser *p)
+_gather_46_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -28714,27 +29724,27 @@ _gather_45_rule(Parser *p)
     }
     asdl_seq * _res = NULL;
     int _mark = p->mark;
-    { // maybe_star_pattern _loop0_44
+    { // maybe_star_pattern _loop0_45
         if (p->error_indicator) {
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _gather_45[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "maybe_star_pattern _loop0_44"));
+        D(fprintf(stderr, "%*c> _gather_46[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "maybe_star_pattern _loop0_45"));
         pattern_ty elem;
         asdl_seq * seq;
         if (
             (elem = maybe_star_pattern_rule(p))  // maybe_star_pattern
             &&
-            (seq = _loop0_44_rule(p))  // _loop0_44
+            (seq = _loop0_45_rule(p))  // _loop0_45
         )
         {
-            D(fprintf(stderr, "%*c+ _gather_45[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "maybe_star_pattern _loop0_44"));
+            D(fprintf(stderr, "%*c+ _gather_46[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "maybe_star_pattern _loop0_45"));
             _res = _PyPegen_seq_insert_in_front(p, elem, seq);
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _gather_45[%d-%d]: %s failed!\n", p->level, ' ',
-                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "maybe_star_pattern _loop0_44"));
+        D(fprintf(stderr, "%*c%s _gather_46[%d-%d]: %s failed!\n", p->level, ' ',
+                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "maybe_star_pattern _loop0_45"));
     }
     _res = NULL;
   done:
@@ -28742,9 +29752,9 @@ _gather_45_rule(Parser *p)
     return _res;
 }
 
-// _loop0_46: ',' key_value_pattern
+// _loop0_47: ',' key_value_pattern
 static asdl_seq *
-_loop0_46_rule(Parser *p)
+_loop0_47_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -28769,7 +29779,7 @@ _loop0_46_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _loop0_46[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' key_value_pattern"));
+        D(fprintf(stderr, "%*c> _loop0_47[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' key_value_pattern"));
         Token * _literal;
         KeyPatternPair* elem;
         while (
@@ -28801,7 +29811,7 @@ _loop0_46_rule(Parser *p)
             _mark = p->mark;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _loop0_46[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _loop0_47[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' key_value_pattern"));
     }
     asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena);
@@ -28818,9 +29828,9 @@ _loop0_46_rule(Parser *p)
     return _seq;
 }
 
-// _gather_47: key_value_pattern _loop0_46
+// _gather_48: key_value_pattern _loop0_47
 static asdl_seq *
-_gather_47_rule(Parser *p)
+_gather_48_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -28831,27 +29841,27 @@ _gather_47_rule(Parser *p)
     }
     asdl_seq * _res = NULL;
     int _mark = p->mark;
-    { // key_value_pattern _loop0_46
+    { // key_value_pattern _loop0_47
         if (p->error_indicator) {
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _gather_47[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "key_value_pattern _loop0_46"));
+        D(fprintf(stderr, "%*c> _gather_48[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "key_value_pattern _loop0_47"));
         KeyPatternPair* elem;
         asdl_seq * seq;
         if (
             (elem = key_value_pattern_rule(p))  // key_value_pattern
             &&
-            (seq = _loop0_46_rule(p))  // _loop0_46
+            (seq = _loop0_47_rule(p))  // _loop0_47
         )
         {
-            D(fprintf(stderr, "%*c+ _gather_47[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "key_value_pattern _loop0_46"));
+            D(fprintf(stderr, "%*c+ _gather_48[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "key_value_pattern _loop0_47"));
             _res = _PyPegen_seq_insert_in_front(p, elem, seq);
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _gather_47[%d-%d]: %s failed!\n", p->level, ' ',
-                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "key_value_pattern _loop0_46"));
+        D(fprintf(stderr, "%*c%s _gather_48[%d-%d]: %s failed!\n", p->level, ' ',
+                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "key_value_pattern _loop0_47"));
     }
     _res = NULL;
   done:
@@ -28859,9 +29869,9 @@ _gather_47_rule(Parser *p)
     return _res;
 }
 
-// _tmp_48: literal_expr | attr
+// _tmp_49: literal_expr | attr
 static void *
-_tmp_48_rule(Parser *p)
+_tmp_49_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -28877,18 +29887,18 @@ _tmp_48_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_48[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "literal_expr"));
+        D(fprintf(stderr, "%*c> _tmp_49[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "literal_expr"));
         expr_ty literal_expr_var;
         if (
             (literal_expr_var = literal_expr_rule(p))  // literal_expr
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_48[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "literal_expr"));
+            D(fprintf(stderr, "%*c+ _tmp_49[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "literal_expr"));
             _res = literal_expr_var;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_48[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_49[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "literal_expr"));
     }
     { // attr
@@ -28896,18 +29906,18 @@ _tmp_48_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_48[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "attr"));
+        D(fprintf(stderr, "%*c> _tmp_49[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "attr"));
         expr_ty attr_var;
         if (
             (attr_var = attr_rule(p))  // attr
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_48[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "attr"));
+            D(fprintf(stderr, "%*c+ _tmp_49[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "attr"));
             _res = attr_var;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_48[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_49[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "attr"));
     }
     _res = NULL;
@@ -28916,9 +29926,9 @@ _tmp_48_rule(Parser *p)
     return _res;
 }
 
-// _loop0_49: ',' pattern
+// _loop0_50: ',' pattern
 static asdl_seq *
-_loop0_49_rule(Parser *p)
+_loop0_50_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -28943,7 +29953,7 @@ _loop0_49_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _loop0_49[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' pattern"));
+        D(fprintf(stderr, "%*c> _loop0_50[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' pattern"));
         Token * _literal;
         pattern_ty elem;
         while (
@@ -28975,7 +29985,7 @@ _loop0_49_rule(Parser *p)
             _mark = p->mark;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _loop0_49[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _loop0_50[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' pattern"));
     }
     asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena);
@@ -28992,9 +30002,9 @@ _loop0_49_rule(Parser *p)
     return _seq;
 }
 
-// _gather_50: pattern _loop0_49
+// _gather_51: pattern _loop0_50
 static asdl_seq *
-_gather_50_rule(Parser *p)
+_gather_51_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -29005,27 +30015,27 @@ _gather_50_rule(Parser *p)
     }
     asdl_seq * _res = NULL;
     int _mark = p->mark;
-    { // pattern _loop0_49
+    { // pattern _loop0_50
         if (p->error_indicator) {
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _gather_50[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "pattern _loop0_49"));
+        D(fprintf(stderr, "%*c> _gather_51[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "pattern _loop0_50"));
         pattern_ty elem;
         asdl_seq * seq;
         if (
             (elem = pattern_rule(p))  // pattern
             &&
-            (seq = _loop0_49_rule(p))  // _loop0_49
+            (seq = _loop0_50_rule(p))  // _loop0_50
         )
         {
-            D(fprintf(stderr, "%*c+ _gather_50[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "pattern _loop0_49"));
+            D(fprintf(stderr, "%*c+ _gather_51[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "pattern _loop0_50"));
             _res = _PyPegen_seq_insert_in_front(p, elem, seq);
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _gather_50[%d-%d]: %s failed!\n", p->level, ' ',
-                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "pattern _loop0_49"));
+        D(fprintf(stderr, "%*c%s _gather_51[%d-%d]: %s failed!\n", p->level, ' ',
+                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "pattern _loop0_50"));
     }
     _res = NULL;
   done:
@@ -29033,9 +30043,9 @@ _gather_50_rule(Parser *p)
     return _res;
 }
 
-// _loop0_51: ',' keyword_pattern
+// _loop0_52: ',' keyword_pattern
 static asdl_seq *
-_loop0_51_rule(Parser *p)
+_loop0_52_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -29060,7 +30070,7 @@ _loop0_51_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _loop0_51[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' keyword_pattern"));
+        D(fprintf(stderr, "%*c> _loop0_52[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' keyword_pattern"));
         Token * _literal;
         KeyPatternPair* elem;
         while (
@@ -29092,7 +30102,7 @@ _loop0_51_rule(Parser *p)
             _mark = p->mark;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _loop0_51[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _loop0_52[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' keyword_pattern"));
     }
     asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena);
@@ -29109,9 +30119,9 @@ _loop0_51_rule(Parser *p)
     return _seq;
 }
 
-// _gather_52: keyword_pattern _loop0_51
+// _gather_53: keyword_pattern _loop0_52
 static asdl_seq *
-_gather_52_rule(Parser *p)
+_gather_53_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -29122,27 +30132,27 @@ _gather_52_rule(Parser *p)
     }
     asdl_seq * _res = NULL;
     int _mark = p->mark;
-    { // keyword_pattern _loop0_51
+    { // keyword_pattern _loop0_52
         if (p->error_indicator) {
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _gather_52[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "keyword_pattern _loop0_51"));
+        D(fprintf(stderr, "%*c> _gather_53[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "keyword_pattern _loop0_52"));
         KeyPatternPair* elem;
         asdl_seq * seq;
         if (
             (elem = keyword_pattern_rule(p))  // keyword_pattern
             &&
-            (seq = _loop0_51_rule(p))  // _loop0_51
+            (seq = _loop0_52_rule(p))  // _loop0_52
         )
         {
-            D(fprintf(stderr, "%*c+ _gather_52[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "keyword_pattern _loop0_51"));
+            D(fprintf(stderr, "%*c+ _gather_53[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "keyword_pattern _loop0_52"));
             _res = _PyPegen_seq_insert_in_front(p, elem, seq);
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _gather_52[%d-%d]: %s failed!\n", p->level, ' ',
-                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "keyword_pattern _loop0_51"));
+        D(fprintf(stderr, "%*c%s _gather_53[%d-%d]: %s failed!\n", p->level, ' ',
+                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "keyword_pattern _loop0_52"));
     }
     _res = NULL;
   done:
@@ -29150,9 +30160,9 @@ _gather_52_rule(Parser *p)
     return _res;
 }
 
-// _loop0_53: ',' type_param
+// _loop0_54: ',' type_param
 static asdl_seq *
-_loop0_53_rule(Parser *p)
+_loop0_54_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -29177,7 +30187,7 @@ _loop0_53_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _loop0_53[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' type_param"));
+        D(fprintf(stderr, "%*c> _loop0_54[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' type_param"));
         Token * _literal;
         type_param_ty elem;
         while (
@@ -29209,7 +30219,7 @@ _loop0_53_rule(Parser *p)
             _mark = p->mark;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _loop0_53[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _loop0_54[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' type_param"));
     }
     asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena);
@@ -29226,9 +30236,9 @@ _loop0_53_rule(Parser *p)
     return _seq;
 }
 
-// _gather_54: type_param _loop0_53
+// _gather_55: type_param _loop0_54
 static asdl_seq *
-_gather_54_rule(Parser *p)
+_gather_55_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -29239,27 +30249,27 @@ _gather_54_rule(Parser *p)
     }
     asdl_seq * _res = NULL;
     int _mark = p->mark;
-    { // type_param _loop0_53
+    { // type_param _loop0_54
         if (p->error_indicator) {
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _gather_54[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "type_param _loop0_53"));
+        D(fprintf(stderr, "%*c> _gather_55[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "type_param _loop0_54"));
         type_param_ty elem;
         asdl_seq * seq;
         if (
             (elem = type_param_rule(p))  // type_param
             &&
-            (seq = _loop0_53_rule(p))  // _loop0_53
+            (seq = _loop0_54_rule(p))  // _loop0_54
         )
         {
-            D(fprintf(stderr, "%*c+ _gather_54[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "type_param _loop0_53"));
+            D(fprintf(stderr, "%*c+ _gather_55[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "type_param _loop0_54"));
             _res = _PyPegen_seq_insert_in_front(p, elem, seq);
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _gather_54[%d-%d]: %s failed!\n", p->level, ' ',
-                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "type_param _loop0_53"));
+        D(fprintf(stderr, "%*c%s _gather_55[%d-%d]: %s failed!\n", p->level, ' ',
+                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "type_param _loop0_54"));
     }
     _res = NULL;
   done:
@@ -29267,9 +30277,9 @@ _gather_54_rule(Parser *p)
     return _res;
 }
 
-// _loop1_55: (',' expression)
+// _loop1_56: (',' expression)
 static asdl_seq *
-_loop1_55_rule(Parser *p)
+_loop1_56_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -29294,7 +30304,7 @@ _loop1_55_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _loop1_55[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(',' expression)"));
+        D(fprintf(stderr, "%*c> _loop1_56[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(',' expression)"));
         void *_tmp_17_var;
         while (
             (_tmp_17_var = _tmp_17_rule(p))  // ',' expression
@@ -29317,7 +30327,7 @@ _loop1_55_rule(Parser *p)
             _mark = p->mark;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _loop1_55[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _loop1_56[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(',' expression)"));
     }
     if (_n == 0 || p->error_indicator) {
@@ -29339,9 +30349,9 @@ _loop1_55_rule(Parser *p)
     return _seq;
 }
 
-// _loop1_56: (',' star_expression)
+// _loop1_57: (',' star_expression)
 static asdl_seq *
-_loop1_56_rule(Parser *p)
+_loop1_57_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -29366,13 +30376,13 @@ _loop1_56_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _loop1_56[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(',' star_expression)"));
-        void *_tmp_155_var;
+        D(fprintf(stderr, "%*c> _loop1_57[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(',' star_expression)"));
+        void *_tmp_157_var;
         while (
-            (_tmp_155_var = _tmp_155_rule(p))  // ',' star_expression
+            (_tmp_157_var = _tmp_157_rule(p))  // ',' star_expression
         )
         {
-            _res = _tmp_155_var;
+            _res = _tmp_157_var;
             if (_n == _children_capacity) {
                 _children_capacity *= 2;
                 void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *));
@@ -29389,7 +30399,7 @@ _loop1_56_rule(Parser *p)
             _mark = p->mark;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _loop1_56[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _loop1_57[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(',' star_expression)"));
     }
     if (_n == 0 || p->error_indicator) {
@@ -29411,9 +30421,9 @@ _loop1_56_rule(Parser *p)
     return _seq;
 }
 
-// _loop0_57: ',' star_named_expression
+// _loop0_58: ',' star_named_expression
 static asdl_seq *
-_loop0_57_rule(Parser *p)
+_loop0_58_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -29438,7 +30448,7 @@ _loop0_57_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _loop0_57[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' star_named_expression"));
+        D(fprintf(stderr, "%*c> _loop0_58[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' star_named_expression"));
         Token * _literal;
         expr_ty elem;
         while (
@@ -29470,7 +30480,7 @@ _loop0_57_rule(Parser *p)
             _mark = p->mark;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _loop0_57[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _loop0_58[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' star_named_expression"));
     }
     asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena);
@@ -29487,9 +30497,9 @@ _loop0_57_rule(Parser *p)
     return _seq;
 }
 
-// _gather_58: star_named_expression _loop0_57
+// _gather_59: star_named_expression _loop0_58
 static asdl_seq *
-_gather_58_rule(Parser *p)
+_gather_59_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -29500,27 +30510,27 @@ _gather_58_rule(Parser *p)
     }
     asdl_seq * _res = NULL;
     int _mark = p->mark;
-    { // star_named_expression _loop0_57
+    { // star_named_expression _loop0_58
         if (p->error_indicator) {
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _gather_58[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_named_expression _loop0_57"));
+        D(fprintf(stderr, "%*c> _gather_59[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_named_expression _loop0_58"));
         expr_ty elem;
         asdl_seq * seq;
         if (
             (elem = star_named_expression_rule(p))  // star_named_expression
             &&
-            (seq = _loop0_57_rule(p))  // _loop0_57
+            (seq = _loop0_58_rule(p))  // _loop0_58
         )
         {
-            D(fprintf(stderr, "%*c+ _gather_58[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_named_expression _loop0_57"));
+            D(fprintf(stderr, "%*c+ _gather_59[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_named_expression _loop0_58"));
             _res = _PyPegen_seq_insert_in_front(p, elem, seq);
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _gather_58[%d-%d]: %s failed!\n", p->level, ' ',
-                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "star_named_expression _loop0_57"));
+        D(fprintf(stderr, "%*c%s _gather_59[%d-%d]: %s failed!\n", p->level, ' ',
+                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "star_named_expression _loop0_58"));
     }
     _res = NULL;
   done:
@@ -29528,9 +30538,9 @@ _gather_58_rule(Parser *p)
     return _res;
 }
 
-// _loop1_59: ('or' conjunction)
+// _loop1_60: ('or' conjunction)
 static asdl_seq *
-_loop1_59_rule(Parser *p)
+_loop1_60_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -29555,13 +30565,13 @@ _loop1_59_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _loop1_59[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('or' conjunction)"));
-        void *_tmp_156_var;
+        D(fprintf(stderr, "%*c> _loop1_60[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('or' conjunction)"));
+        void *_tmp_158_var;
         while (
-            (_tmp_156_var = _tmp_156_rule(p))  // 'or' conjunction
+            (_tmp_158_var = _tmp_158_rule(p))  // 'or' conjunction
         )
         {
-            _res = _tmp_156_var;
+            _res = _tmp_158_var;
             if (_n == _children_capacity) {
                 _children_capacity *= 2;
                 void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *));
@@ -29578,7 +30588,7 @@ _loop1_59_rule(Parser *p)
             _mark = p->mark;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _loop1_59[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _loop1_60[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "('or' conjunction)"));
     }
     if (_n == 0 || p->error_indicator) {
@@ -29600,9 +30610,9 @@ _loop1_59_rule(Parser *p)
     return _seq;
 }
 
-// _loop1_60: ('and' inversion)
+// _loop1_61: ('and' inversion)
 static asdl_seq *
-_loop1_60_rule(Parser *p)
+_loop1_61_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -29627,13 +30637,13 @@ _loop1_60_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _loop1_60[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('and' inversion)"));
-        void *_tmp_157_var;
+        D(fprintf(stderr, "%*c> _loop1_61[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('and' inversion)"));
+        void *_tmp_159_var;
         while (
-            (_tmp_157_var = _tmp_157_rule(p))  // 'and' inversion
+            (_tmp_159_var = _tmp_159_rule(p))  // 'and' inversion
         )
         {
-            _res = _tmp_157_var;
+            _res = _tmp_159_var;
             if (_n == _children_capacity) {
                 _children_capacity *= 2;
                 void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *));
@@ -29650,7 +30660,7 @@ _loop1_60_rule(Parser *p)
             _mark = p->mark;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _loop1_60[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _loop1_61[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "('and' inversion)"));
     }
     if (_n == 0 || p->error_indicator) {
@@ -29672,9 +30682,9 @@ _loop1_60_rule(Parser *p)
     return _seq;
 }
 
-// _loop1_61: compare_op_bitwise_or_pair
+// _loop1_62: compare_op_bitwise_or_pair
 static asdl_seq *
-_loop1_61_rule(Parser *p)
+_loop1_62_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -29699,7 +30709,7 @@ _loop1_61_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _loop1_61[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "compare_op_bitwise_or_pair"));
+        D(fprintf(stderr, "%*c> _loop1_62[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "compare_op_bitwise_or_pair"));
         CmpopExprPair* compare_op_bitwise_or_pair_var;
         while (
             (compare_op_bitwise_or_pair_var = compare_op_bitwise_or_pair_rule(p))  // compare_op_bitwise_or_pair
@@ -29722,7 +30732,7 @@ _loop1_61_rule(Parser *p)
             _mark = p->mark;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _loop1_61[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _loop1_62[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "compare_op_bitwise_or_pair"));
     }
     if (_n == 0 || p->error_indicator) {
@@ -29744,9 +30754,9 @@ _loop1_61_rule(Parser *p)
     return _seq;
 }
 
-// _tmp_62: '!='
+// _tmp_63: '!='
 static void *
-_tmp_62_rule(Parser *p)
+_tmp_63_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -29762,13 +30772,13 @@ _tmp_62_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_62[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'!='"));
+        D(fprintf(stderr, "%*c> _tmp_63[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'!='"));
         Token * tok;
         if (
             (tok = _PyPegen_expect_token(p, 28))  // token='!='
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_62[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'!='"));
+            D(fprintf(stderr, "%*c+ _tmp_63[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'!='"));
             _res = _PyPegen_check_barry_as_flufl ( p , tok ) ? NULL : tok;
             if (_res == NULL && PyErr_Occurred()) {
                 p->error_indicator = 1;
@@ -29778,7 +30788,7 @@ _tmp_62_rule(Parser *p)
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_62[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_63[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'!='"));
     }
     _res = NULL;
@@ -29787,9 +30797,9 @@ _tmp_62_rule(Parser *p)
     return _res;
 }
 
-// _loop0_63: ',' (slice | starred_expression)
+// _loop0_64: ',' (slice | starred_expression)
 static asdl_seq *
-_loop0_63_rule(Parser *p)
+_loop0_64_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -29814,13 +30824,13 @@ _loop0_63_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _loop0_63[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' (slice | starred_expression)"));
+        D(fprintf(stderr, "%*c> _loop0_64[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' (slice | starred_expression)"));
         Token * _literal;
         void *elem;
         while (
             (_literal = _PyPegen_expect_token(p, 12))  // token=','
             &&
-            (elem = _tmp_158_rule(p))  // slice | starred_expression
+            (elem = _tmp_160_rule(p))  // slice | starred_expression
         )
         {
             _res = elem;
@@ -29846,7 +30856,7 @@ _loop0_63_rule(Parser *p)
             _mark = p->mark;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _loop0_63[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _loop0_64[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' (slice | starred_expression)"));
     }
     asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena);
@@ -29863,9 +30873,9 @@ _loop0_63_rule(Parser *p)
     return _seq;
 }
 
-// _gather_64: (slice | starred_expression) _loop0_63
+// _gather_65: (slice | starred_expression) _loop0_64
 static asdl_seq *
-_gather_64_rule(Parser *p)
+_gather_65_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -29876,27 +30886,27 @@ _gather_64_rule(Parser *p)
     }
     asdl_seq * _res = NULL;
     int _mark = p->mark;
-    { // (slice | starred_expression) _loop0_63
+    { // (slice | starred_expression) _loop0_64
         if (p->error_indicator) {
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _gather_64[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(slice | starred_expression) _loop0_63"));
+        D(fprintf(stderr, "%*c> _gather_65[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(slice | starred_expression) _loop0_64"));
         void *elem;
         asdl_seq * seq;
         if (
-            (elem = _tmp_158_rule(p))  // slice | starred_expression
+            (elem = _tmp_160_rule(p))  // slice | starred_expression
             &&
-            (seq = _loop0_63_rule(p))  // _loop0_63
+            (seq = _loop0_64_rule(p))  // _loop0_64
         )
         {
-            D(fprintf(stderr, "%*c+ _gather_64[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "(slice | starred_expression) _loop0_63"));
+            D(fprintf(stderr, "%*c+ _gather_65[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "(slice | starred_expression) _loop0_64"));
             _res = _PyPegen_seq_insert_in_front(p, elem, seq);
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _gather_64[%d-%d]: %s failed!\n", p->level, ' ',
-                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(slice | starred_expression) _loop0_63"));
+        D(fprintf(stderr, "%*c%s _gather_65[%d-%d]: %s failed!\n", p->level, ' ',
+                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(slice | starred_expression) _loop0_64"));
     }
     _res = NULL;
   done:
@@ -29904,9 +30914,9 @@ _gather_64_rule(Parser *p)
     return _res;
 }
 
-// _tmp_65: ':' expression?
+// _tmp_66: ':' expression?
 static void *
-_tmp_65_rule(Parser *p)
+_tmp_66_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -29922,7 +30932,7 @@ _tmp_65_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_65[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':' expression?"));
+        D(fprintf(stderr, "%*c> _tmp_66[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':' expression?"));
         Token * _literal;
         void *d;
         if (
@@ -29931,7 +30941,7 @@ _tmp_65_rule(Parser *p)
             (d = expression_rule(p), !p->error_indicator)  // expression?
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_65[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':' expression?"));
+            D(fprintf(stderr, "%*c+ _tmp_66[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':' expression?"));
             _res = d;
             if (_res == NULL && PyErr_Occurred()) {
                 p->error_indicator = 1;
@@ -29941,65 +30951,8 @@ _tmp_65_rule(Parser *p)
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_65[%d-%d]: %s failed!\n", p->level, ' ',
-                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "':' expression?"));
-    }
-    _res = NULL;
-  done:
-    p->level--;
-    return _res;
-}
-
-// _tmp_66: STRING | FSTRING_START
-static void *
-_tmp_66_rule(Parser *p)
-{
-    if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
-        _Pypegen_stack_overflow(p);
-    }
-    if (p->error_indicator) {
-        p->level--;
-        return NULL;
-    }
-    void * _res = NULL;
-    int _mark = p->mark;
-    { // STRING
-        if (p->error_indicator) {
-            p->level--;
-            return NULL;
-        }
-        D(fprintf(stderr, "%*c> _tmp_66[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "STRING"));
-        expr_ty string_var;
-        if (
-            (string_var = _PyPegen_string_token(p))  // STRING
-        )
-        {
-            D(fprintf(stderr, "%*c+ _tmp_66[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "STRING"));
-            _res = string_var;
-            goto done;
-        }
-        p->mark = _mark;
         D(fprintf(stderr, "%*c%s _tmp_66[%d-%d]: %s failed!\n", p->level, ' ',
-                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "STRING"));
-    }
-    { // FSTRING_START
-        if (p->error_indicator) {
-            p->level--;
-            return NULL;
-        }
-        D(fprintf(stderr, "%*c> _tmp_66[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "FSTRING_START"));
-        Token * fstring_start_var;
-        if (
-            (fstring_start_var = _PyPegen_expect_token(p, FSTRING_START))  // token='FSTRING_START'
-        )
-        {
-            D(fprintf(stderr, "%*c+ _tmp_66[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "FSTRING_START"));
-            _res = fstring_start_var;
-            goto done;
-        }
-        p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_66[%d-%d]: %s failed!\n", p->level, ' ',
-                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "FSTRING_START"));
+                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "':' expression?"));
     }
     _res = NULL;
   done:
@@ -30843,9 +31796,9 @@ _loop0_78_rule(Parser *p)
     return _seq;
 }
 
-// _loop1_79: (fstring | string)
+// _loop0_79: tstring_format_spec
 static asdl_seq *
-_loop1_79_rule(Parser *p)
+_loop0_79_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -30865,18 +31818,152 @@ _loop1_79_rule(Parser *p)
     }
     Py_ssize_t _children_capacity = 1;
     Py_ssize_t _n = 0;
-    { // (fstring | string)
+    { // tstring_format_spec
         if (p->error_indicator) {
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _loop1_79[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(fstring | string)"));
-        void *_tmp_159_var;
+        D(fprintf(stderr, "%*c> _loop0_79[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "tstring_format_spec"));
+        expr_ty tstring_format_spec_var;
         while (
-            (_tmp_159_var = _tmp_159_rule(p))  // fstring | string
+            (tstring_format_spec_var = tstring_format_spec_rule(p))  // tstring_format_spec
         )
         {
-            _res = _tmp_159_var;
+            _res = tstring_format_spec_var;
+            if (_n == _children_capacity) {
+                _children_capacity *= 2;
+                void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *));
+                if (!_new_children) {
+                    PyMem_Free(_children);
+                    p->error_indicator = 1;
+                    PyErr_NoMemory();
+                    p->level--;
+                    return NULL;
+                }
+                _children = _new_children;
+            }
+            _children[_n++] = _res;
+            _mark = p->mark;
+        }
+        p->mark = _mark;
+        D(fprintf(stderr, "%*c%s _loop0_79[%d-%d]: %s failed!\n", p->level, ' ',
+                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "tstring_format_spec"));
+    }
+    asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena);
+    if (!_seq) {
+        PyMem_Free(_children);
+        p->error_indicator = 1;
+        PyErr_NoMemory();
+        p->level--;
+        return NULL;
+    }
+    for (Py_ssize_t i = 0; i < _n; i++) asdl_seq_SET_UNTYPED(_seq, i, _children[i]);
+    PyMem_Free(_children);
+    p->level--;
+    return _seq;
+}
+
+// _loop0_80: tstring_middle
+static asdl_seq *
+_loop0_80_rule(Parser *p)
+{
+    if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
+        _Pypegen_stack_overflow(p);
+    }
+    if (p->error_indicator) {
+        p->level--;
+        return NULL;
+    }
+    void *_res = NULL;
+    int _mark = p->mark;
+    void **_children = PyMem_Malloc(sizeof(void *));
+    if (!_children) {
+        p->error_indicator = 1;
+        PyErr_NoMemory();
+        p->level--;
+        return NULL;
+    }
+    Py_ssize_t _children_capacity = 1;
+    Py_ssize_t _n = 0;
+    { // tstring_middle
+        if (p->error_indicator) {
+            p->level--;
+            return NULL;
+        }
+        D(fprintf(stderr, "%*c> _loop0_80[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "tstring_middle"));
+        expr_ty tstring_middle_var;
+        while (
+            (tstring_middle_var = tstring_middle_rule(p))  // tstring_middle
+        )
+        {
+            _res = tstring_middle_var;
+            if (_n == _children_capacity) {
+                _children_capacity *= 2;
+                void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *));
+                if (!_new_children) {
+                    PyMem_Free(_children);
+                    p->error_indicator = 1;
+                    PyErr_NoMemory();
+                    p->level--;
+                    return NULL;
+                }
+                _children = _new_children;
+            }
+            _children[_n++] = _res;
+            _mark = p->mark;
+        }
+        p->mark = _mark;
+        D(fprintf(stderr, "%*c%s _loop0_80[%d-%d]: %s failed!\n", p->level, ' ',
+                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "tstring_middle"));
+    }
+    asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena);
+    if (!_seq) {
+        PyMem_Free(_children);
+        p->error_indicator = 1;
+        PyErr_NoMemory();
+        p->level--;
+        return NULL;
+    }
+    for (Py_ssize_t i = 0; i < _n; i++) asdl_seq_SET_UNTYPED(_seq, i, _children[i]);
+    PyMem_Free(_children);
+    p->level--;
+    return _seq;
+}
+
+// _loop1_81: (fstring | string | tstring)
+static asdl_seq *
+_loop1_81_rule(Parser *p)
+{
+    if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
+        _Pypegen_stack_overflow(p);
+    }
+    if (p->error_indicator) {
+        p->level--;
+        return NULL;
+    }
+    void *_res = NULL;
+    int _mark = p->mark;
+    void **_children = PyMem_Malloc(sizeof(void *));
+    if (!_children) {
+        p->error_indicator = 1;
+        PyErr_NoMemory();
+        p->level--;
+        return NULL;
+    }
+    Py_ssize_t _children_capacity = 1;
+    Py_ssize_t _n = 0;
+    { // (fstring | string | tstring)
+        if (p->error_indicator) {
+            p->level--;
+            return NULL;
+        }
+        D(fprintf(stderr, "%*c> _loop1_81[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(fstring | string | tstring)"));
+        void *_tmp_161_var;
+        while (
+            (_tmp_161_var = _tmp_161_rule(p))  // fstring | string | tstring
+        )
+        {
+            _res = _tmp_161_var;
             if (_n == _children_capacity) {
                 _children_capacity *= 2;
                 void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *));
@@ -30893,8 +31980,8 @@ _loop1_79_rule(Parser *p)
             _mark = p->mark;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _loop1_79[%d-%d]: %s failed!\n", p->level, ' ',
-                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(fstring | string)"));
+        D(fprintf(stderr, "%*c%s _loop1_81[%d-%d]: %s failed!\n", p->level, ' ',
+                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(fstring | string | tstring)"));
     }
     if (_n == 0 || p->error_indicator) {
         PyMem_Free(_children);
@@ -30915,9 +32002,9 @@ _loop1_79_rule(Parser *p)
     return _seq;
 }
 
-// _tmp_80: star_named_expression ',' star_named_expressions?
+// _tmp_82: star_named_expression ',' star_named_expressions?
 static void *
-_tmp_80_rule(Parser *p)
+_tmp_82_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -30933,7 +32020,7 @@ _tmp_80_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_80[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_named_expression ',' star_named_expressions?"));
+        D(fprintf(stderr, "%*c> _tmp_82[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_named_expression ',' star_named_expressions?"));
         Token * _literal;
         expr_ty y;
         void *z;
@@ -30945,7 +32032,7 @@ _tmp_80_rule(Parser *p)
             (z = star_named_expressions_rule(p), !p->error_indicator)  // star_named_expressions?
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_80[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_named_expression ',' star_named_expressions?"));
+            D(fprintf(stderr, "%*c+ _tmp_82[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_named_expression ',' star_named_expressions?"));
             _res = _PyPegen_seq_insert_in_front ( p , y , z );
             if (_res == NULL && PyErr_Occurred()) {
                 p->error_indicator = 1;
@@ -30955,7 +32042,7 @@ _tmp_80_rule(Parser *p)
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_80[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_82[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "star_named_expression ',' star_named_expressions?"));
     }
     _res = NULL;
@@ -30964,9 +32051,9 @@ _tmp_80_rule(Parser *p)
     return _res;
 }
 
-// _loop0_81: ',' double_starred_kvpair
+// _loop0_83: ',' double_starred_kvpair
 static asdl_seq *
-_loop0_81_rule(Parser *p)
+_loop0_83_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -30991,7 +32078,7 @@ _loop0_81_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _loop0_81[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' double_starred_kvpair"));
+        D(fprintf(stderr, "%*c> _loop0_83[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' double_starred_kvpair"));
         Token * _literal;
         KeyValuePair* elem;
         while (
@@ -31023,7 +32110,7 @@ _loop0_81_rule(Parser *p)
             _mark = p->mark;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _loop0_81[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _loop0_83[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' double_starred_kvpair"));
     }
     asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena);
@@ -31040,9 +32127,9 @@ _loop0_81_rule(Parser *p)
     return _seq;
 }
 
-// _gather_82: double_starred_kvpair _loop0_81
+// _gather_84: double_starred_kvpair _loop0_83
 static asdl_seq *
-_gather_82_rule(Parser *p)
+_gather_84_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -31053,27 +32140,27 @@ _gather_82_rule(Parser *p)
     }
     asdl_seq * _res = NULL;
     int _mark = p->mark;
-    { // double_starred_kvpair _loop0_81
+    { // double_starred_kvpair _loop0_83
         if (p->error_indicator) {
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _gather_82[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "double_starred_kvpair _loop0_81"));
+        D(fprintf(stderr, "%*c> _gather_84[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "double_starred_kvpair _loop0_83"));
         KeyValuePair* elem;
         asdl_seq * seq;
         if (
             (elem = double_starred_kvpair_rule(p))  // double_starred_kvpair
             &&
-            (seq = _loop0_81_rule(p))  // _loop0_81
+            (seq = _loop0_83_rule(p))  // _loop0_83
         )
         {
-            D(fprintf(stderr, "%*c+ _gather_82[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "double_starred_kvpair _loop0_81"));
+            D(fprintf(stderr, "%*c+ _gather_84[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "double_starred_kvpair _loop0_83"));
             _res = _PyPegen_seq_insert_in_front(p, elem, seq);
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _gather_82[%d-%d]: %s failed!\n", p->level, ' ',
-                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "double_starred_kvpair _loop0_81"));
+        D(fprintf(stderr, "%*c%s _gather_84[%d-%d]: %s failed!\n", p->level, ' ',
+                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "double_starred_kvpair _loop0_83"));
     }
     _res = NULL;
   done:
@@ -31081,9 +32168,9 @@ _gather_82_rule(Parser *p)
     return _res;
 }
 
-// _loop1_83: for_if_clause
+// _loop1_85: for_if_clause
 static asdl_seq *
-_loop1_83_rule(Parser *p)
+_loop1_85_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -31108,7 +32195,7 @@ _loop1_83_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _loop1_83[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "for_if_clause"));
+        D(fprintf(stderr, "%*c> _loop1_85[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "for_if_clause"));
         comprehension_ty for_if_clause_var;
         while (
             (for_if_clause_var = for_if_clause_rule(p))  // for_if_clause
@@ -31131,7 +32218,7 @@ _loop1_83_rule(Parser *p)
             _mark = p->mark;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _loop1_83[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _loop1_85[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "for_if_clause"));
     }
     if (_n == 0 || p->error_indicator) {
@@ -31153,9 +32240,9 @@ _loop1_83_rule(Parser *p)
     return _seq;
 }
 
-// _loop0_84: ('if' disjunction)
+// _loop0_86: ('if' disjunction)
 static asdl_seq *
-_loop0_84_rule(Parser *p)
+_loop0_86_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -31180,13 +32267,13 @@ _loop0_84_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _loop0_84[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('if' disjunction)"));
-        void *_tmp_160_var;
+        D(fprintf(stderr, "%*c> _loop0_86[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('if' disjunction)"));
+        void *_tmp_162_var;
         while (
-            (_tmp_160_var = _tmp_160_rule(p))  // 'if' disjunction
+            (_tmp_162_var = _tmp_162_rule(p))  // 'if' disjunction
         )
         {
-            _res = _tmp_160_var;
+            _res = _tmp_162_var;
             if (_n == _children_capacity) {
                 _children_capacity *= 2;
                 void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *));
@@ -31203,7 +32290,7 @@ _loop0_84_rule(Parser *p)
             _mark = p->mark;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _loop0_84[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _loop0_86[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "('if' disjunction)"));
     }
     asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena);
@@ -31220,9 +32307,9 @@ _loop0_84_rule(Parser *p)
     return _seq;
 }
 
-// _tmp_85: assignment_expression | expression !':='
+// _tmp_87: assignment_expression | expression !':='
 static void *
-_tmp_85_rule(Parser *p)
+_tmp_87_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -31238,18 +32325,18 @@ _tmp_85_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_85[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "assignment_expression"));
+        D(fprintf(stderr, "%*c> _tmp_87[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "assignment_expression"));
         expr_ty assignment_expression_var;
         if (
             (assignment_expression_var = assignment_expression_rule(p))  // assignment_expression
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_85[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "assignment_expression"));
+            D(fprintf(stderr, "%*c+ _tmp_87[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "assignment_expression"));
             _res = assignment_expression_var;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_85[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_87[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "assignment_expression"));
     }
     { // expression !':='
@@ -31257,7 +32344,7 @@ _tmp_85_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_85[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression !':='"));
+        D(fprintf(stderr, "%*c> _tmp_87[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression !':='"));
         expr_ty expression_var;
         if (
             (expression_var = expression_rule(p))  // expression
@@ -31265,12 +32352,12 @@ _tmp_85_rule(Parser *p)
             _PyPegen_lookahead_with_int(0, _PyPegen_expect_token, p, 53)  // token=':='
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_85[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression !':='"));
+            D(fprintf(stderr, "%*c+ _tmp_87[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression !':='"));
             _res = expression_var;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_85[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_87[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "expression !':='"));
     }
     _res = NULL;
@@ -31279,9 +32366,9 @@ _tmp_85_rule(Parser *p)
     return _res;
 }
 
-// _loop0_86: ',' (starred_expression | (assignment_expression | expression !':=') !'=')
+// _loop0_88: ',' (starred_expression | (assignment_expression | expression !':=') !'=')
 static asdl_seq *
-_loop0_86_rule(Parser *p)
+_loop0_88_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -31306,13 +32393,13 @@ _loop0_86_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _loop0_86[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' (starred_expression | (assignment_expression | expression !':=') !'=')"));
+        D(fprintf(stderr, "%*c> _loop0_88[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' (starred_expression | (assignment_expression | expression !':=') !'=')"));
         Token * _literal;
         void *elem;
         while (
             (_literal = _PyPegen_expect_token(p, 12))  // token=','
             &&
-            (elem = _tmp_161_rule(p))  // starred_expression | (assignment_expression | expression !':=') !'='
+            (elem = _tmp_163_rule(p))  // starred_expression | (assignment_expression | expression !':=') !'='
         )
         {
             _res = elem;
@@ -31338,7 +32425,7 @@ _loop0_86_rule(Parser *p)
             _mark = p->mark;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _loop0_86[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _loop0_88[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' (starred_expression | (assignment_expression | expression !':=') !'=')"));
     }
     asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena);
@@ -31355,10 +32442,10 @@ _loop0_86_rule(Parser *p)
     return _seq;
 }
 
-// _gather_87:
-//     | (starred_expression | (assignment_expression | expression !':=') !'=') _loop0_86
+// _gather_89:
+//     | (starred_expression | (assignment_expression | expression !':=') !'=') _loop0_88
 static asdl_seq *
-_gather_87_rule(Parser *p)
+_gather_89_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -31369,27 +32456,27 @@ _gather_87_rule(Parser *p)
     }
     asdl_seq * _res = NULL;
     int _mark = p->mark;
-    { // (starred_expression | (assignment_expression | expression !':=') !'=') _loop0_86
+    { // (starred_expression | (assignment_expression | expression !':=') !'=') _loop0_88
         if (p->error_indicator) {
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _gather_87[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(starred_expression | (assignment_expression | expression !':=') !'=') _loop0_86"));
+        D(fprintf(stderr, "%*c> _gather_89[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(starred_expression | (assignment_expression | expression !':=') !'=') _loop0_88"));
         void *elem;
         asdl_seq * seq;
         if (
-            (elem = _tmp_161_rule(p))  // starred_expression | (assignment_expression | expression !':=') !'='
+            (elem = _tmp_163_rule(p))  // starred_expression | (assignment_expression | expression !':=') !'='
             &&
-            (seq = _loop0_86_rule(p))  // _loop0_86
+            (seq = _loop0_88_rule(p))  // _loop0_88
         )
         {
-            D(fprintf(stderr, "%*c+ _gather_87[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "(starred_expression | (assignment_expression | expression !':=') !'=') _loop0_86"));
+            D(fprintf(stderr, "%*c+ _gather_89[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "(starred_expression | (assignment_expression | expression !':=') !'=') _loop0_88"));
             _res = _PyPegen_seq_insert_in_front(p, elem, seq);
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _gather_87[%d-%d]: %s failed!\n", p->level, ' ',
-                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(starred_expression | (assignment_expression | expression !':=') !'=') _loop0_86"));
+        D(fprintf(stderr, "%*c%s _gather_89[%d-%d]: %s failed!\n", p->level, ' ',
+                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(starred_expression | (assignment_expression | expression !':=') !'=') _loop0_88"));
     }
     _res = NULL;
   done:
@@ -31397,9 +32484,9 @@ _gather_87_rule(Parser *p)
     return _res;
 }
 
-// _tmp_88: ',' kwargs
+// _tmp_90: ',' kwargs
 static void *
-_tmp_88_rule(Parser *p)
+_tmp_90_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -31415,7 +32502,7 @@ _tmp_88_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_88[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' kwargs"));
+        D(fprintf(stderr, "%*c> _tmp_90[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' kwargs"));
         Token * _literal;
         asdl_seq* k;
         if (
@@ -31424,7 +32511,7 @@ _tmp_88_rule(Parser *p)
             (k = kwargs_rule(p))  // kwargs
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_88[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' kwargs"));
+            D(fprintf(stderr, "%*c+ _tmp_90[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' kwargs"));
             _res = k;
             if (_res == NULL && PyErr_Occurred()) {
                 p->error_indicator = 1;
@@ -31434,7 +32521,7 @@ _tmp_88_rule(Parser *p)
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_88[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_90[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' kwargs"));
     }
     _res = NULL;
@@ -31443,9 +32530,9 @@ _tmp_88_rule(Parser *p)
     return _res;
 }
 
-// _loop0_89: ',' kwarg_or_starred
+// _loop0_91: ',' kwarg_or_starred
 static asdl_seq *
-_loop0_89_rule(Parser *p)
+_loop0_91_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -31470,7 +32557,7 @@ _loop0_89_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _loop0_89[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' kwarg_or_starred"));
+        D(fprintf(stderr, "%*c> _loop0_91[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' kwarg_or_starred"));
         Token * _literal;
         KeywordOrStarred* elem;
         while (
@@ -31502,7 +32589,7 @@ _loop0_89_rule(Parser *p)
             _mark = p->mark;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _loop0_89[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _loop0_91[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' kwarg_or_starred"));
     }
     asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena);
@@ -31519,9 +32606,9 @@ _loop0_89_rule(Parser *p)
     return _seq;
 }
 
-// _gather_90: kwarg_or_starred _loop0_89
+// _gather_92: kwarg_or_starred _loop0_91
 static asdl_seq *
-_gather_90_rule(Parser *p)
+_gather_92_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -31532,27 +32619,27 @@ _gather_90_rule(Parser *p)
     }
     asdl_seq * _res = NULL;
     int _mark = p->mark;
-    { // kwarg_or_starred _loop0_89
+    { // kwarg_or_starred _loop0_91
         if (p->error_indicator) {
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _gather_90[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "kwarg_or_starred _loop0_89"));
+        D(fprintf(stderr, "%*c> _gather_92[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "kwarg_or_starred _loop0_91"));
         KeywordOrStarred* elem;
         asdl_seq * seq;
         if (
             (elem = kwarg_or_starred_rule(p))  // kwarg_or_starred
             &&
-            (seq = _loop0_89_rule(p))  // _loop0_89
+            (seq = _loop0_91_rule(p))  // _loop0_91
         )
         {
-            D(fprintf(stderr, "%*c+ _gather_90[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "kwarg_or_starred _loop0_89"));
+            D(fprintf(stderr, "%*c+ _gather_92[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "kwarg_or_starred _loop0_91"));
             _res = _PyPegen_seq_insert_in_front(p, elem, seq);
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _gather_90[%d-%d]: %s failed!\n", p->level, ' ',
-                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "kwarg_or_starred _loop0_89"));
+        D(fprintf(stderr, "%*c%s _gather_92[%d-%d]: %s failed!\n", p->level, ' ',
+                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "kwarg_or_starred _loop0_91"));
     }
     _res = NULL;
   done:
@@ -31560,9 +32647,9 @@ _gather_90_rule(Parser *p)
     return _res;
 }
 
-// _loop0_91: ',' kwarg_or_double_starred
+// _loop0_93: ',' kwarg_or_double_starred
 static asdl_seq *
-_loop0_91_rule(Parser *p)
+_loop0_93_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -31587,7 +32674,7 @@ _loop0_91_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _loop0_91[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' kwarg_or_double_starred"));
+        D(fprintf(stderr, "%*c> _loop0_93[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' kwarg_or_double_starred"));
         Token * _literal;
         KeywordOrStarred* elem;
         while (
@@ -31619,7 +32706,7 @@ _loop0_91_rule(Parser *p)
             _mark = p->mark;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _loop0_91[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _loop0_93[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' kwarg_or_double_starred"));
     }
     asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena);
@@ -31636,9 +32723,9 @@ _loop0_91_rule(Parser *p)
     return _seq;
 }
 
-// _gather_92: kwarg_or_double_starred _loop0_91
+// _gather_94: kwarg_or_double_starred _loop0_93
 static asdl_seq *
-_gather_92_rule(Parser *p)
+_gather_94_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -31649,27 +32736,27 @@ _gather_92_rule(Parser *p)
     }
     asdl_seq * _res = NULL;
     int _mark = p->mark;
-    { // kwarg_or_double_starred _loop0_91
+    { // kwarg_or_double_starred _loop0_93
         if (p->error_indicator) {
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _gather_92[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "kwarg_or_double_starred _loop0_91"));
+        D(fprintf(stderr, "%*c> _gather_94[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "kwarg_or_double_starred _loop0_93"));
         KeywordOrStarred* elem;
         asdl_seq * seq;
         if (
             (elem = kwarg_or_double_starred_rule(p))  // kwarg_or_double_starred
             &&
-            (seq = _loop0_91_rule(p))  // _loop0_91
+            (seq = _loop0_93_rule(p))  // _loop0_93
         )
         {
-            D(fprintf(stderr, "%*c+ _gather_92[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "kwarg_or_double_starred _loop0_91"));
+            D(fprintf(stderr, "%*c+ _gather_94[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "kwarg_or_double_starred _loop0_93"));
             _res = _PyPegen_seq_insert_in_front(p, elem, seq);
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _gather_92[%d-%d]: %s failed!\n", p->level, ' ',
-                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "kwarg_or_double_starred _loop0_91"));
+        D(fprintf(stderr, "%*c%s _gather_94[%d-%d]: %s failed!\n", p->level, ' ',
+                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "kwarg_or_double_starred _loop0_93"));
     }
     _res = NULL;
   done:
@@ -31677,9 +32764,9 @@ _gather_92_rule(Parser *p)
     return _res;
 }
 
-// _loop0_93: (',' star_target)
+// _loop0_95: (',' star_target)
 static asdl_seq *
-_loop0_93_rule(Parser *p)
+_loop0_95_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -31704,13 +32791,13 @@ _loop0_93_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _loop0_93[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(',' star_target)"));
-        void *_tmp_162_var;
+        D(fprintf(stderr, "%*c> _loop0_95[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(',' star_target)"));
+        void *_tmp_164_var;
         while (
-            (_tmp_162_var = _tmp_162_rule(p))  // ',' star_target
+            (_tmp_164_var = _tmp_164_rule(p))  // ',' star_target
         )
         {
-            _res = _tmp_162_var;
+            _res = _tmp_164_var;
             if (_n == _children_capacity) {
                 _children_capacity *= 2;
                 void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *));
@@ -31727,7 +32814,7 @@ _loop0_93_rule(Parser *p)
             _mark = p->mark;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _loop0_93[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _loop0_95[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(',' star_target)"));
     }
     asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena);
@@ -31744,9 +32831,9 @@ _loop0_93_rule(Parser *p)
     return _seq;
 }
 
-// _loop0_94: ',' star_target
+// _loop0_96: ',' star_target
 static asdl_seq *
-_loop0_94_rule(Parser *p)
+_loop0_96_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -31771,7 +32858,7 @@ _loop0_94_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _loop0_94[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' star_target"));
+        D(fprintf(stderr, "%*c> _loop0_96[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' star_target"));
         Token * _literal;
         expr_ty elem;
         while (
@@ -31803,7 +32890,7 @@ _loop0_94_rule(Parser *p)
             _mark = p->mark;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _loop0_94[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _loop0_96[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' star_target"));
     }
     asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena);
@@ -31820,9 +32907,9 @@ _loop0_94_rule(Parser *p)
     return _seq;
 }
 
-// _gather_95: star_target _loop0_94
+// _gather_97: star_target _loop0_96
 static asdl_seq *
-_gather_95_rule(Parser *p)
+_gather_97_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -31833,27 +32920,27 @@ _gather_95_rule(Parser *p)
     }
     asdl_seq * _res = NULL;
     int _mark = p->mark;
-    { // star_target _loop0_94
+    { // star_target _loop0_96
         if (p->error_indicator) {
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _gather_95[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_target _loop0_94"));
+        D(fprintf(stderr, "%*c> _gather_97[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_target _loop0_96"));
         expr_ty elem;
         asdl_seq * seq;
         if (
             (elem = star_target_rule(p))  // star_target
             &&
-            (seq = _loop0_94_rule(p))  // _loop0_94
+            (seq = _loop0_96_rule(p))  // _loop0_96
         )
         {
-            D(fprintf(stderr, "%*c+ _gather_95[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_target _loop0_94"));
+            D(fprintf(stderr, "%*c+ _gather_97[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_target _loop0_96"));
             _res = _PyPegen_seq_insert_in_front(p, elem, seq);
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _gather_95[%d-%d]: %s failed!\n", p->level, ' ',
-                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "star_target _loop0_94"));
+        D(fprintf(stderr, "%*c%s _gather_97[%d-%d]: %s failed!\n", p->level, ' ',
+                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "star_target _loop0_96"));
     }
     _res = NULL;
   done:
@@ -31861,9 +32948,9 @@ _gather_95_rule(Parser *p)
     return _res;
 }
 
-// _loop1_96: (',' star_target)
+// _loop1_98: (',' star_target)
 static asdl_seq *
-_loop1_96_rule(Parser *p)
+_loop1_98_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -31888,13 +32975,13 @@ _loop1_96_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _loop1_96[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(',' star_target)"));
-        void *_tmp_162_var;
+        D(fprintf(stderr, "%*c> _loop1_98[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(',' star_target)"));
+        void *_tmp_164_var;
         while (
-            (_tmp_162_var = _tmp_162_rule(p))  // ',' star_target
+            (_tmp_164_var = _tmp_164_rule(p))  // ',' star_target
         )
         {
-            _res = _tmp_162_var;
+            _res = _tmp_164_var;
             if (_n == _children_capacity) {
                 _children_capacity *= 2;
                 void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *));
@@ -31911,7 +32998,7 @@ _loop1_96_rule(Parser *p)
             _mark = p->mark;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _loop1_96[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _loop1_98[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(',' star_target)"));
     }
     if (_n == 0 || p->error_indicator) {
@@ -31933,9 +33020,9 @@ _loop1_96_rule(Parser *p)
     return _seq;
 }
 
-// _tmp_97: !'*' star_target
+// _tmp_99: !'*' star_target
 static void *
-_tmp_97_rule(Parser *p)
+_tmp_99_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -31951,7 +33038,7 @@ _tmp_97_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_97[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "!'*' star_target"));
+        D(fprintf(stderr, "%*c> _tmp_99[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "!'*' star_target"));
         expr_ty star_target_var;
         if (
             _PyPegen_lookahead_with_int(0, _PyPegen_expect_token, p, 16)  // token='*'
@@ -31959,12 +33046,12 @@ _tmp_97_rule(Parser *p)
             (star_target_var = star_target_rule(p))  // star_target
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_97[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "!'*' star_target"));
+            D(fprintf(stderr, "%*c+ _tmp_99[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "!'*' star_target"));
             _res = star_target_var;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_97[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_99[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "!'*' star_target"));
     }
     _res = NULL;
@@ -31973,9 +33060,9 @@ _tmp_97_rule(Parser *p)
     return _res;
 }
 
-// _loop0_98: ',' del_target
+// _loop0_100: ',' del_target
 static asdl_seq *
-_loop0_98_rule(Parser *p)
+_loop0_100_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -32000,7 +33087,7 @@ _loop0_98_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _loop0_98[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' del_target"));
+        D(fprintf(stderr, "%*c> _loop0_100[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' del_target"));
         Token * _literal;
         expr_ty elem;
         while (
@@ -32032,7 +33119,7 @@ _loop0_98_rule(Parser *p)
             _mark = p->mark;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _loop0_98[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _loop0_100[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' del_target"));
     }
     asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena);
@@ -32049,9 +33136,9 @@ _loop0_98_rule(Parser *p)
     return _seq;
 }
 
-// _gather_99: del_target _loop0_98
+// _gather_101: del_target _loop0_100
 static asdl_seq *
-_gather_99_rule(Parser *p)
+_gather_101_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -32062,27 +33149,27 @@ _gather_99_rule(Parser *p)
     }
     asdl_seq * _res = NULL;
     int _mark = p->mark;
-    { // del_target _loop0_98
+    { // del_target _loop0_100
         if (p->error_indicator) {
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _gather_99[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "del_target _loop0_98"));
+        D(fprintf(stderr, "%*c> _gather_101[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "del_target _loop0_100"));
         expr_ty elem;
         asdl_seq * seq;
         if (
             (elem = del_target_rule(p))  // del_target
             &&
-            (seq = _loop0_98_rule(p))  // _loop0_98
+            (seq = _loop0_100_rule(p))  // _loop0_100
         )
         {
-            D(fprintf(stderr, "%*c+ _gather_99[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "del_target _loop0_98"));
+            D(fprintf(stderr, "%*c+ _gather_101[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "del_target _loop0_100"));
             _res = _PyPegen_seq_insert_in_front(p, elem, seq);
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _gather_99[%d-%d]: %s failed!\n", p->level, ' ',
-                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "del_target _loop0_98"));
+        D(fprintf(stderr, "%*c%s _gather_101[%d-%d]: %s failed!\n", p->level, ' ',
+                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "del_target _loop0_100"));
     }
     _res = NULL;
   done:
@@ -32090,9 +33177,9 @@ _gather_99_rule(Parser *p)
     return _res;
 }
 
-// _loop0_100: ',' expression
+// _loop0_102: ',' expression
 static asdl_seq *
-_loop0_100_rule(Parser *p)
+_loop0_102_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -32117,7 +33204,7 @@ _loop0_100_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _loop0_100[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' expression"));
+        D(fprintf(stderr, "%*c> _loop0_102[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' expression"));
         Token * _literal;
         expr_ty elem;
         while (
@@ -32149,7 +33236,7 @@ _loop0_100_rule(Parser *p)
             _mark = p->mark;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _loop0_100[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _loop0_102[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' expression"));
     }
     asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena);
@@ -32166,9 +33253,9 @@ _loop0_100_rule(Parser *p)
     return _seq;
 }
 
-// _gather_101: expression _loop0_100
+// _gather_103: expression _loop0_102
 static asdl_seq *
-_gather_101_rule(Parser *p)
+_gather_103_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -32179,27 +33266,27 @@ _gather_101_rule(Parser *p)
     }
     asdl_seq * _res = NULL;
     int _mark = p->mark;
-    { // expression _loop0_100
+    { // expression _loop0_102
         if (p->error_indicator) {
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _gather_101[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression _loop0_100"));
+        D(fprintf(stderr, "%*c> _gather_103[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression _loop0_102"));
         expr_ty elem;
         asdl_seq * seq;
         if (
             (elem = expression_rule(p))  // expression
             &&
-            (seq = _loop0_100_rule(p))  // _loop0_100
+            (seq = _loop0_102_rule(p))  // _loop0_102
         )
         {
-            D(fprintf(stderr, "%*c+ _gather_101[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression _loop0_100"));
+            D(fprintf(stderr, "%*c+ _gather_103[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression _loop0_102"));
             _res = _PyPegen_seq_insert_in_front(p, elem, seq);
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _gather_101[%d-%d]: %s failed!\n", p->level, ' ',
-                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "expression _loop0_100"));
+        D(fprintf(stderr, "%*c%s _gather_103[%d-%d]: %s failed!\n", p->level, ' ',
+                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "expression _loop0_102"));
     }
     _res = NULL;
   done:
@@ -32207,9 +33294,9 @@ _gather_101_rule(Parser *p)
     return _res;
 }
 
-// _tmp_102: NEWLINE INDENT
+// _tmp_104: NEWLINE INDENT
 static void *
-_tmp_102_rule(Parser *p)
+_tmp_104_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -32225,7 +33312,7 @@ _tmp_102_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_102[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "NEWLINE INDENT"));
+        D(fprintf(stderr, "%*c> _tmp_104[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "NEWLINE INDENT"));
         Token * indent_var;
         Token * newline_var;
         if (
@@ -32234,12 +33321,12 @@ _tmp_102_rule(Parser *p)
             (indent_var = _PyPegen_expect_token(p, INDENT))  // token='INDENT'
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_102[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "NEWLINE INDENT"));
+            D(fprintf(stderr, "%*c+ _tmp_104[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "NEWLINE INDENT"));
             _res = _PyPegen_dummy_name(p, newline_var, indent_var);
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_102[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_104[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "NEWLINE INDENT"));
     }
     _res = NULL;
@@ -32248,11 +33335,11 @@ _tmp_102_rule(Parser *p)
     return _res;
 }
 
-// _tmp_103:
+// _tmp_105:
 //     | (','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs)
 //     | kwargs
 static void *
-_tmp_103_rule(Parser *p)
+_tmp_105_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -32268,18 +33355,18 @@ _tmp_103_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_103[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs)"));
-        void *_tmp_163_var;
+        D(fprintf(stderr, "%*c> _tmp_105[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs)"));
+        void *_tmp_165_var;
         if (
-            (_tmp_163_var = _tmp_163_rule(p))  // ','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs
+            (_tmp_165_var = _tmp_165_rule(p))  // ','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_103[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "(','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs)"));
-            _res = _tmp_163_var;
+            D(fprintf(stderr, "%*c+ _tmp_105[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "(','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs)"));
+            _res = _tmp_165_var;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_103[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_105[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs)"));
     }
     { // kwargs
@@ -32287,18 +33374,18 @@ _tmp_103_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_103[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "kwargs"));
+        D(fprintf(stderr, "%*c> _tmp_105[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "kwargs"));
         asdl_seq* kwargs_var;
         if (
             (kwargs_var = kwargs_rule(p))  // kwargs
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_103[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "kwargs"));
+            D(fprintf(stderr, "%*c+ _tmp_105[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "kwargs"));
             _res = kwargs_var;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_103[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_105[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "kwargs"));
     }
     _res = NULL;
@@ -32307,9 +33394,9 @@ _tmp_103_rule(Parser *p)
     return _res;
 }
 
-// _loop0_104: ',' (starred_expression !'=')
+// _loop0_106: ',' (starred_expression !'=')
 static asdl_seq *
-_loop0_104_rule(Parser *p)
+_loop0_106_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -32334,13 +33421,13 @@ _loop0_104_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _loop0_104[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' (starred_expression !'=')"));
+        D(fprintf(stderr, "%*c> _loop0_106[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' (starred_expression !'=')"));
         Token * _literal;
         void *elem;
         while (
             (_literal = _PyPegen_expect_token(p, 12))  // token=','
             &&
-            (elem = _tmp_164_rule(p))  // starred_expression !'='
+            (elem = _tmp_166_rule(p))  // starred_expression !'='
         )
         {
             _res = elem;
@@ -32366,7 +33453,7 @@ _loop0_104_rule(Parser *p)
             _mark = p->mark;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _loop0_104[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _loop0_106[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' (starred_expression !'=')"));
     }
     asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena);
@@ -32383,9 +33470,9 @@ _loop0_104_rule(Parser *p)
     return _seq;
 }
 
-// _gather_105: (starred_expression !'=') _loop0_104
+// _gather_107: (starred_expression !'=') _loop0_106
 static asdl_seq *
-_gather_105_rule(Parser *p)
+_gather_107_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -32396,27 +33483,27 @@ _gather_105_rule(Parser *p)
     }
     asdl_seq * _res = NULL;
     int _mark = p->mark;
-    { // (starred_expression !'=') _loop0_104
+    { // (starred_expression !'=') _loop0_106
         if (p->error_indicator) {
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _gather_105[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(starred_expression !'=') _loop0_104"));
+        D(fprintf(stderr, "%*c> _gather_107[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(starred_expression !'=') _loop0_106"));
         void *elem;
         asdl_seq * seq;
         if (
-            (elem = _tmp_164_rule(p))  // starred_expression !'='
+            (elem = _tmp_166_rule(p))  // starred_expression !'='
             &&
-            (seq = _loop0_104_rule(p))  // _loop0_104
+            (seq = _loop0_106_rule(p))  // _loop0_106
         )
         {
-            D(fprintf(stderr, "%*c+ _gather_105[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "(starred_expression !'=') _loop0_104"));
+            D(fprintf(stderr, "%*c+ _gather_107[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "(starred_expression !'=') _loop0_106"));
             _res = _PyPegen_seq_insert_in_front(p, elem, seq);
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _gather_105[%d-%d]: %s failed!\n", p->level, ' ',
-                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(starred_expression !'=') _loop0_104"));
+        D(fprintf(stderr, "%*c%s _gather_107[%d-%d]: %s failed!\n", p->level, ' ',
+                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(starred_expression !'=') _loop0_106"));
     }
     _res = NULL;
   done:
@@ -32424,9 +33511,9 @@ _gather_105_rule(Parser *p)
     return _res;
 }
 
-// _tmp_106: args | expression for_if_clauses
+// _tmp_108: args | expression for_if_clauses
 static void *
-_tmp_106_rule(Parser *p)
+_tmp_108_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -32442,18 +33529,18 @@ _tmp_106_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_106[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "args"));
+        D(fprintf(stderr, "%*c> _tmp_108[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "args"));
         expr_ty args_var;
         if (
             (args_var = args_rule(p))  // args
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_106[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "args"));
+            D(fprintf(stderr, "%*c+ _tmp_108[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "args"));
             _res = args_var;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_106[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_108[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "args"));
     }
     { // expression for_if_clauses
@@ -32461,7 +33548,7 @@ _tmp_106_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_106[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression for_if_clauses"));
+        D(fprintf(stderr, "%*c> _tmp_108[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression for_if_clauses"));
         expr_ty expression_var;
         asdl_comprehension_seq* for_if_clauses_var;
         if (
@@ -32470,12 +33557,12 @@ _tmp_106_rule(Parser *p)
             (for_if_clauses_var = for_if_clauses_rule(p))  // for_if_clauses
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_106[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression for_if_clauses"));
+            D(fprintf(stderr, "%*c+ _tmp_108[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression for_if_clauses"));
             _res = _PyPegen_dummy_name(p, expression_var, for_if_clauses_var);
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_106[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_108[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "expression for_if_clauses"));
     }
     _res = NULL;
@@ -32484,9 +33571,9 @@ _tmp_106_rule(Parser *p)
     return _res;
 }
 
-// _tmp_107: args ','
+// _tmp_109: args ','
 static void *
-_tmp_107_rule(Parser *p)
+_tmp_109_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -32502,7 +33589,7 @@ _tmp_107_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_107[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "args ','"));
+        D(fprintf(stderr, "%*c> _tmp_109[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "args ','"));
         Token * _literal;
         expr_ty args_var;
         if (
@@ -32511,12 +33598,12 @@ _tmp_107_rule(Parser *p)
             (_literal = _PyPegen_expect_token(p, 12))  // token=','
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_107[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "args ','"));
+            D(fprintf(stderr, "%*c+ _tmp_109[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "args ','"));
             _res = _PyPegen_dummy_name(p, args_var, _literal);
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_107[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_109[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "args ','"));
     }
     _res = NULL;
@@ -32525,9 +33612,9 @@ _tmp_107_rule(Parser *p)
     return _res;
 }
 
-// _tmp_108: ',' | ')'
+// _tmp_110: ',' | ')'
 static void *
-_tmp_108_rule(Parser *p)
+_tmp_110_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -32543,18 +33630,18 @@ _tmp_108_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_108[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','"));
+        D(fprintf(stderr, "%*c> _tmp_110[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','"));
         Token * _literal;
         if (
             (_literal = _PyPegen_expect_token(p, 12))  // token=','
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_108[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','"));
+            D(fprintf(stderr, "%*c+ _tmp_110[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','"));
             _res = _literal;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_108[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_110[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "','"));
     }
     { // ')'
@@ -32562,18 +33649,18 @@ _tmp_108_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_108[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "')'"));
+        D(fprintf(stderr, "%*c> _tmp_110[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "')'"));
         Token * _literal;
         if (
             (_literal = _PyPegen_expect_token(p, 8))  // token=')'
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_108[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "')'"));
+            D(fprintf(stderr, "%*c+ _tmp_110[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "')'"));
             _res = _literal;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_108[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_110[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "')'"));
     }
     _res = NULL;
@@ -32582,9 +33669,9 @@ _tmp_108_rule(Parser *p)
     return _res;
 }
 
-// _tmp_109: 'True' | 'False' | 'None'
+// _tmp_111: 'True' | 'False' | 'None'
 static void *
-_tmp_109_rule(Parser *p)
+_tmp_111_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -32600,18 +33687,18 @@ _tmp_109_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_109[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'True'"));
+        D(fprintf(stderr, "%*c> _tmp_111[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'True'"));
         Token * _keyword;
         if (
-            (_keyword = _PyPegen_expect_token(p, 619))  // token='True'
+            (_keyword = _PyPegen_expect_token(p, 620))  // token='True'
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_109[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'True'"));
+            D(fprintf(stderr, "%*c+ _tmp_111[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'True'"));
             _res = _keyword;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_109[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_111[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'True'"));
     }
     { // 'False'
@@ -32619,18 +33706,18 @@ _tmp_109_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_109[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'False'"));
+        D(fprintf(stderr, "%*c> _tmp_111[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'False'"));
         Token * _keyword;
         if (
-            (_keyword = _PyPegen_expect_token(p, 621))  // token='False'
+            (_keyword = _PyPegen_expect_token(p, 622))  // token='False'
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_109[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'False'"));
+            D(fprintf(stderr, "%*c+ _tmp_111[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'False'"));
             _res = _keyword;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_109[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_111[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'False'"));
     }
     { // 'None'
@@ -32638,18 +33725,18 @@ _tmp_109_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_109[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'None'"));
+        D(fprintf(stderr, "%*c> _tmp_111[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'None'"));
         Token * _keyword;
         if (
-            (_keyword = _PyPegen_expect_token(p, 620))  // token='None'
+            (_keyword = _PyPegen_expect_token(p, 621))  // token='None'
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_109[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'None'"));
+            D(fprintf(stderr, "%*c+ _tmp_111[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'None'"));
             _res = _keyword;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_109[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_111[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'None'"));
     }
     _res = NULL;
@@ -32658,9 +33745,9 @@ _tmp_109_rule(Parser *p)
     return _res;
 }
 
-// _tmp_110: NAME '='
+// _tmp_112: NAME '='
 static void *
-_tmp_110_rule(Parser *p)
+_tmp_112_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -32676,7 +33763,7 @@ _tmp_110_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_110[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "NAME '='"));
+        D(fprintf(stderr, "%*c> _tmp_112[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "NAME '='"));
         Token * _literal;
         expr_ty name_var;
         if (
@@ -32685,12 +33772,12 @@ _tmp_110_rule(Parser *p)
             (_literal = _PyPegen_expect_token(p, 22))  // token='='
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_110[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "NAME '='"));
+            D(fprintf(stderr, "%*c+ _tmp_112[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "NAME '='"));
             _res = _PyPegen_dummy_name(p, name_var, _literal);
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_110[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_112[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "NAME '='"));
     }
     _res = NULL;
@@ -32699,9 +33786,9 @@ _tmp_110_rule(Parser *p)
     return _res;
 }
 
-// _loop1_111: (!STRING expression_without_invalid)
+// _loop1_113: (!STRING expression_without_invalid)
 static asdl_seq *
-_loop1_111_rule(Parser *p)
+_loop1_113_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -32726,13 +33813,13 @@ _loop1_111_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _loop1_111[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(!STRING expression_without_invalid)"));
-        void *_tmp_165_var;
+        D(fprintf(stderr, "%*c> _loop1_113[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(!STRING expression_without_invalid)"));
+        void *_tmp_167_var;
         while (
-            (_tmp_165_var = _tmp_165_rule(p))  // !STRING expression_without_invalid
+            (_tmp_167_var = _tmp_167_rule(p))  // !STRING expression_without_invalid
         )
         {
-            _res = _tmp_165_var;
+            _res = _tmp_167_var;
             if (_n == _children_capacity) {
                 _children_capacity *= 2;
                 void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *));
@@ -32749,7 +33836,7 @@ _loop1_111_rule(Parser *p)
             _mark = p->mark;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _loop1_111[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _loop1_113[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(!STRING expression_without_invalid)"));
     }
     if (_n == 0 || p->error_indicator) {
@@ -32771,9 +33858,9 @@ _loop1_111_rule(Parser *p)
     return _seq;
 }
 
-// _tmp_112: NAME STRING | SOFT_KEYWORD
+// _tmp_114: NAME STRING | SOFT_KEYWORD
 static void *
-_tmp_112_rule(Parser *p)
+_tmp_114_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -32789,7 +33876,7 @@ _tmp_112_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_112[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "NAME STRING"));
+        D(fprintf(stderr, "%*c> _tmp_114[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "NAME STRING"));
         expr_ty name_var;
         expr_ty string_var;
         if (
@@ -32798,12 +33885,12 @@ _tmp_112_rule(Parser *p)
             (string_var = _PyPegen_string_token(p))  // STRING
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_112[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "NAME STRING"));
+            D(fprintf(stderr, "%*c+ _tmp_114[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "NAME STRING"));
             _res = _PyPegen_dummy_name(p, name_var, string_var);
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_112[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_114[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "NAME STRING"));
     }
     { // SOFT_KEYWORD
@@ -32811,18 +33898,18 @@ _tmp_112_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_112[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "SOFT_KEYWORD"));
+        D(fprintf(stderr, "%*c> _tmp_114[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "SOFT_KEYWORD"));
         expr_ty soft_keyword_var;
         if (
             (soft_keyword_var = _PyPegen_soft_keyword_token(p))  // SOFT_KEYWORD
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_112[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "SOFT_KEYWORD"));
+            D(fprintf(stderr, "%*c+ _tmp_114[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "SOFT_KEYWORD"));
             _res = soft_keyword_var;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_112[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_114[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "SOFT_KEYWORD"));
     }
     _res = NULL;
@@ -32831,9 +33918,9 @@ _tmp_112_rule(Parser *p)
     return _res;
 }
 
-// _tmp_113: 'else' | ':'
+// _tmp_115: 'else' | ':'
 static void *
-_tmp_113_rule(Parser *p)
+_tmp_115_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -32849,18 +33936,18 @@ _tmp_113_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_113[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'else'"));
+        D(fprintf(stderr, "%*c> _tmp_115[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'else'"));
         Token * _keyword;
         if (
-            (_keyword = _PyPegen_expect_token(p, 681))  // token='else'
+            (_keyword = _PyPegen_expect_token(p, 682))  // token='else'
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_113[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'else'"));
+            D(fprintf(stderr, "%*c+ _tmp_115[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'else'"));
             _res = _keyword;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_113[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_115[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'else'"));
     }
     { // ':'
@@ -32868,18 +33955,18 @@ _tmp_113_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_113[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':'"));
+        D(fprintf(stderr, "%*c> _tmp_115[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':'"));
         Token * _literal;
         if (
             (_literal = _PyPegen_expect_token(p, 11))  // token=':'
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_113[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':'"));
+            D(fprintf(stderr, "%*c+ _tmp_115[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':'"));
             _res = _literal;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_113[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_115[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "':'"));
     }
     _res = NULL;
@@ -32888,9 +33975,9 @@ _tmp_113_rule(Parser *p)
     return _res;
 }
 
-// _tmp_114: pass_stmt | break_stmt | continue_stmt
+// _tmp_116: pass_stmt | break_stmt | continue_stmt
 static void *
-_tmp_114_rule(Parser *p)
+_tmp_116_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -32906,18 +33993,18 @@ _tmp_114_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_114[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "pass_stmt"));
+        D(fprintf(stderr, "%*c> _tmp_116[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "pass_stmt"));
         stmt_ty pass_stmt_var;
         if (
             (pass_stmt_var = pass_stmt_rule(p))  // pass_stmt
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_114[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "pass_stmt"));
+            D(fprintf(stderr, "%*c+ _tmp_116[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "pass_stmt"));
             _res = pass_stmt_var;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_114[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_116[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "pass_stmt"));
     }
     { // break_stmt
@@ -32925,18 +34012,18 @@ _tmp_114_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_114[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "break_stmt"));
+        D(fprintf(stderr, "%*c> _tmp_116[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "break_stmt"));
         stmt_ty break_stmt_var;
         if (
             (break_stmt_var = break_stmt_rule(p))  // break_stmt
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_114[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "break_stmt"));
+            D(fprintf(stderr, "%*c+ _tmp_116[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "break_stmt"));
             _res = break_stmt_var;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_114[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_116[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "break_stmt"));
     }
     { // continue_stmt
@@ -32944,18 +34031,18 @@ _tmp_114_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_114[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "continue_stmt"));
+        D(fprintf(stderr, "%*c> _tmp_116[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "continue_stmt"));
         stmt_ty continue_stmt_var;
         if (
             (continue_stmt_var = continue_stmt_rule(p))  // continue_stmt
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_114[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "continue_stmt"));
+            D(fprintf(stderr, "%*c+ _tmp_116[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "continue_stmt"));
             _res = continue_stmt_var;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_114[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_116[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "continue_stmt"));
     }
     _res = NULL;
@@ -32964,9 +34051,9 @@ _tmp_114_rule(Parser *p)
     return _res;
 }
 
-// _tmp_115: '=' | ':='
+// _tmp_117: '=' | ':='
 static void *
-_tmp_115_rule(Parser *p)
+_tmp_117_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -32982,18 +34069,18 @@ _tmp_115_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_115[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'='"));
+        D(fprintf(stderr, "%*c> _tmp_117[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'='"));
         Token * _literal;
         if (
             (_literal = _PyPegen_expect_token(p, 22))  // token='='
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_115[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'='"));
+            D(fprintf(stderr, "%*c+ _tmp_117[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'='"));
             _res = _literal;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_115[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_117[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'='"));
     }
     { // ':='
@@ -33001,18 +34088,18 @@ _tmp_115_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_115[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':='"));
+        D(fprintf(stderr, "%*c> _tmp_117[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':='"));
         Token * _literal;
         if (
             (_literal = _PyPegen_expect_token(p, 53))  // token=':='
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_115[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':='"));
+            D(fprintf(stderr, "%*c+ _tmp_117[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':='"));
             _res = _literal;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_115[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_117[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "':='"));
     }
     _res = NULL;
@@ -33021,9 +34108,9 @@ _tmp_115_rule(Parser *p)
     return _res;
 }
 
-// _tmp_116: list | tuple | genexp | 'True' | 'None' | 'False'
+// _tmp_118: list | tuple | genexp | 'True' | 'None' | 'False'
 static void *
-_tmp_116_rule(Parser *p)
+_tmp_118_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -33039,18 +34126,18 @@ _tmp_116_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_116[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "list"));
+        D(fprintf(stderr, "%*c> _tmp_118[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "list"));
         expr_ty list_var;
         if (
             (list_var = list_rule(p))  // list
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_116[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "list"));
+            D(fprintf(stderr, "%*c+ _tmp_118[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "list"));
             _res = list_var;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_116[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_118[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "list"));
     }
     { // tuple
@@ -33058,18 +34145,18 @@ _tmp_116_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_116[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "tuple"));
+        D(fprintf(stderr, "%*c> _tmp_118[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "tuple"));
         expr_ty tuple_var;
         if (
             (tuple_var = tuple_rule(p))  // tuple
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_116[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "tuple"));
+            D(fprintf(stderr, "%*c+ _tmp_118[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "tuple"));
             _res = tuple_var;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_116[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_118[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "tuple"));
     }
     { // genexp
@@ -33077,18 +34164,18 @@ _tmp_116_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_116[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "genexp"));
+        D(fprintf(stderr, "%*c> _tmp_118[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "genexp"));
         expr_ty genexp_var;
         if (
             (genexp_var = genexp_rule(p))  // genexp
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_116[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "genexp"));
+            D(fprintf(stderr, "%*c+ _tmp_118[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "genexp"));
             _res = genexp_var;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_116[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_118[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "genexp"));
     }
     { // 'True'
@@ -33096,18 +34183,18 @@ _tmp_116_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_116[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'True'"));
+        D(fprintf(stderr, "%*c> _tmp_118[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'True'"));
         Token * _keyword;
         if (
-            (_keyword = _PyPegen_expect_token(p, 619))  // token='True'
+            (_keyword = _PyPegen_expect_token(p, 620))  // token='True'
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_116[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'True'"));
+            D(fprintf(stderr, "%*c+ _tmp_118[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'True'"));
             _res = _keyword;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_116[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_118[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'True'"));
     }
     { // 'None'
@@ -33115,18 +34202,18 @@ _tmp_116_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_116[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'None'"));
+        D(fprintf(stderr, "%*c> _tmp_118[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'None'"));
         Token * _keyword;
         if (
-            (_keyword = _PyPegen_expect_token(p, 620))  // token='None'
+            (_keyword = _PyPegen_expect_token(p, 621))  // token='None'
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_116[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'None'"));
+            D(fprintf(stderr, "%*c+ _tmp_118[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'None'"));
             _res = _keyword;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_116[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_118[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'None'"));
     }
     { // 'False'
@@ -33134,18 +34221,18 @@ _tmp_116_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_116[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'False'"));
+        D(fprintf(stderr, "%*c> _tmp_118[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'False'"));
         Token * _keyword;
         if (
-            (_keyword = _PyPegen_expect_token(p, 621))  // token='False'
+            (_keyword = _PyPegen_expect_token(p, 622))  // token='False'
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_116[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'False'"));
+            D(fprintf(stderr, "%*c+ _tmp_118[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'False'"));
             _res = _keyword;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_116[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_118[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'False'"));
     }
     _res = NULL;
@@ -33154,9 +34241,9 @@ _tmp_116_rule(Parser *p)
     return _res;
 }
 
-// _loop0_117: star_named_expressions
+// _loop0_119: star_named_expressions
 static asdl_seq *
-_loop0_117_rule(Parser *p)
+_loop0_119_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -33181,7 +34268,7 @@ _loop0_117_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _loop0_117[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_named_expressions"));
+        D(fprintf(stderr, "%*c> _loop0_119[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_named_expressions"));
         asdl_expr_seq* star_named_expressions_var;
         while (
             (star_named_expressions_var = star_named_expressions_rule(p))  // star_named_expressions
@@ -33204,7 +34291,7 @@ _loop0_117_rule(Parser *p)
             _mark = p->mark;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _loop0_117[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _loop0_119[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "star_named_expressions"));
     }
     asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena);
@@ -33221,9 +34308,9 @@ _loop0_117_rule(Parser *p)
     return _seq;
 }
 
-// _loop0_118: (star_targets '=')
+// _loop0_120: (star_targets '=')
 static asdl_seq *
-_loop0_118_rule(Parser *p)
+_loop0_120_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -33248,13 +34335,13 @@ _loop0_118_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _loop0_118[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(star_targets '=')"));
-        void *_tmp_152_var;
+        D(fprintf(stderr, "%*c> _loop0_120[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(star_targets '=')"));
+        void *_tmp_154_var;
         while (
-            (_tmp_152_var = _tmp_152_rule(p))  // star_targets '='
+            (_tmp_154_var = _tmp_154_rule(p))  // star_targets '='
         )
         {
-            _res = _tmp_152_var;
+            _res = _tmp_154_var;
             if (_n == _children_capacity) {
                 _children_capacity *= 2;
                 void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *));
@@ -33271,7 +34358,7 @@ _loop0_118_rule(Parser *p)
             _mark = p->mark;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _loop0_118[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _loop0_120[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(star_targets '=')"));
     }
     asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena);
@@ -33288,9 +34375,9 @@ _loop0_118_rule(Parser *p)
     return _seq;
 }
 
-// _tmp_119: '[' | '(' | '{'
+// _tmp_121: '[' | '(' | '{'
 static void *
-_tmp_119_rule(Parser *p)
+_tmp_121_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -33306,18 +34393,18 @@ _tmp_119_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_119[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'['"));
+        D(fprintf(stderr, "%*c> _tmp_121[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'['"));
         Token * _literal;
         if (
             (_literal = _PyPegen_expect_token(p, 9))  // token='['
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_119[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'['"));
+            D(fprintf(stderr, "%*c+ _tmp_121[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'['"));
             _res = _literal;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_119[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_121[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'['"));
     }
     { // '('
@@ -33325,18 +34412,18 @@ _tmp_119_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_119[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'('"));
+        D(fprintf(stderr, "%*c> _tmp_121[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'('"));
         Token * _literal;
         if (
             (_literal = _PyPegen_expect_token(p, 7))  // token='('
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_119[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'('"));
+            D(fprintf(stderr, "%*c+ _tmp_121[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'('"));
             _res = _literal;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_119[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_121[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'('"));
     }
     { // '{'
@@ -33344,18 +34431,18 @@ _tmp_119_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_119[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{'"));
+        D(fprintf(stderr, "%*c> _tmp_121[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{'"));
         Token * _literal;
         if (
             (_literal = _PyPegen_expect_token(p, 25))  // token='{'
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_119[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{'"));
+            D(fprintf(stderr, "%*c+ _tmp_121[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{'"));
             _res = _literal;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_119[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_121[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'{'"));
     }
     _res = NULL;
@@ -33364,9 +34451,9 @@ _tmp_119_rule(Parser *p)
     return _res;
 }
 
-// _tmp_120: '[' | '{'
+// _tmp_122: '[' | '{'
 static void *
-_tmp_120_rule(Parser *p)
+_tmp_122_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -33382,18 +34469,18 @@ _tmp_120_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_120[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'['"));
+        D(fprintf(stderr, "%*c> _tmp_122[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'['"));
         Token * _literal;
         if (
             (_literal = _PyPegen_expect_token(p, 9))  // token='['
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_120[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'['"));
+            D(fprintf(stderr, "%*c+ _tmp_122[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'['"));
             _res = _literal;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_120[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_122[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'['"));
     }
     { // '{'
@@ -33401,18 +34488,18 @@ _tmp_120_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_120[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{'"));
+        D(fprintf(stderr, "%*c> _tmp_122[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{'"));
         Token * _literal;
         if (
             (_literal = _PyPegen_expect_token(p, 25))  // token='{'
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_120[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{'"));
+            D(fprintf(stderr, "%*c+ _tmp_122[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{'"));
             _res = _literal;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_120[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_122[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'{'"));
     }
     _res = NULL;
@@ -33421,9 +34508,9 @@ _tmp_120_rule(Parser *p)
     return _res;
 }
 
-// _tmp_121: slash_no_default | slash_with_default
+// _tmp_123: slash_no_default | slash_with_default
 static void *
-_tmp_121_rule(Parser *p)
+_tmp_123_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -33439,18 +34526,18 @@ _tmp_121_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_121[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "slash_no_default"));
+        D(fprintf(stderr, "%*c> _tmp_123[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "slash_no_default"));
         asdl_arg_seq* slash_no_default_var;
         if (
             (slash_no_default_var = slash_no_default_rule(p))  // slash_no_default
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_121[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "slash_no_default"));
+            D(fprintf(stderr, "%*c+ _tmp_123[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "slash_no_default"));
             _res = slash_no_default_var;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_121[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_123[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "slash_no_default"));
     }
     { // slash_with_default
@@ -33458,18 +34545,18 @@ _tmp_121_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_121[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "slash_with_default"));
+        D(fprintf(stderr, "%*c> _tmp_123[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "slash_with_default"));
         SlashWithDefault* slash_with_default_var;
         if (
             (slash_with_default_var = slash_with_default_rule(p))  // slash_with_default
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_121[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "slash_with_default"));
+            D(fprintf(stderr, "%*c+ _tmp_123[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "slash_with_default"));
             _res = slash_with_default_var;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_121[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_123[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "slash_with_default"));
     }
     _res = NULL;
@@ -33478,9 +34565,9 @@ _tmp_121_rule(Parser *p)
     return _res;
 }
 
-// _tmp_122: ',' | param_no_default
+// _tmp_124: ',' | param_no_default
 static void *
-_tmp_122_rule(Parser *p)
+_tmp_124_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -33496,18 +34583,18 @@ _tmp_122_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_122[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','"));
+        D(fprintf(stderr, "%*c> _tmp_124[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','"));
         Token * _literal;
         if (
             (_literal = _PyPegen_expect_token(p, 12))  // token=','
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_122[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','"));
+            D(fprintf(stderr, "%*c+ _tmp_124[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','"));
             _res = _literal;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_122[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_124[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "','"));
     }
     { // param_no_default
@@ -33515,18 +34602,18 @@ _tmp_122_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_122[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "param_no_default"));
+        D(fprintf(stderr, "%*c> _tmp_124[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "param_no_default"));
         arg_ty param_no_default_var;
         if (
             (param_no_default_var = param_no_default_rule(p))  // param_no_default
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_122[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "param_no_default"));
+            D(fprintf(stderr, "%*c+ _tmp_124[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "param_no_default"));
             _res = param_no_default_var;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_122[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_124[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "param_no_default"));
     }
     _res = NULL;
@@ -33535,9 +34622,9 @@ _tmp_122_rule(Parser *p)
     return _res;
 }
 
-// _tmp_123: ')' | ','
+// _tmp_125: ')' | ','
 static void *
-_tmp_123_rule(Parser *p)
+_tmp_125_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -33553,18 +34640,18 @@ _tmp_123_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_123[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "')'"));
+        D(fprintf(stderr, "%*c> _tmp_125[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "')'"));
         Token * _literal;
         if (
             (_literal = _PyPegen_expect_token(p, 8))  // token=')'
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_123[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "')'"));
+            D(fprintf(stderr, "%*c+ _tmp_125[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "')'"));
             _res = _literal;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_123[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_125[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "')'"));
     }
     { // ','
@@ -33572,18 +34659,18 @@ _tmp_123_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_123[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','"));
+        D(fprintf(stderr, "%*c> _tmp_125[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','"));
         Token * _literal;
         if (
             (_literal = _PyPegen_expect_token(p, 12))  // token=','
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_123[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','"));
+            D(fprintf(stderr, "%*c+ _tmp_125[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','"));
             _res = _literal;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_123[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_125[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "','"));
     }
     _res = NULL;
@@ -33592,9 +34679,9 @@ _tmp_123_rule(Parser *p)
     return _res;
 }
 
-// _tmp_124: ')' | ',' (')' | '**')
+// _tmp_126: ')' | ',' (')' | '**')
 static void *
-_tmp_124_rule(Parser *p)
+_tmp_126_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -33610,18 +34697,18 @@ _tmp_124_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_124[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "')'"));
+        D(fprintf(stderr, "%*c> _tmp_126[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "')'"));
         Token * _literal;
         if (
             (_literal = _PyPegen_expect_token(p, 8))  // token=')'
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_124[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "')'"));
+            D(fprintf(stderr, "%*c+ _tmp_126[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "')'"));
             _res = _literal;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_124[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_126[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "')'"));
     }
     { // ',' (')' | '**')
@@ -33629,21 +34716,21 @@ _tmp_124_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_124[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' (')' | '**')"));
+        D(fprintf(stderr, "%*c> _tmp_126[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' (')' | '**')"));
         Token * _literal;
-        void *_tmp_166_var;
+        void *_tmp_168_var;
         if (
             (_literal = _PyPegen_expect_token(p, 12))  // token=','
             &&
-            (_tmp_166_var = _tmp_166_rule(p))  // ')' | '**'
+            (_tmp_168_var = _tmp_168_rule(p))  // ')' | '**'
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_124[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' (')' | '**')"));
-            _res = _PyPegen_dummy_name(p, _literal, _tmp_166_var);
+            D(fprintf(stderr, "%*c+ _tmp_126[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' (')' | '**')"));
+            _res = _PyPegen_dummy_name(p, _literal, _tmp_168_var);
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_124[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_126[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' (')' | '**')"));
     }
     _res = NULL;
@@ -33652,9 +34739,9 @@ _tmp_124_rule(Parser *p)
     return _res;
 }
 
-// _tmp_125: param_no_default | ','
+// _tmp_127: param_no_default | ','
 static void *
-_tmp_125_rule(Parser *p)
+_tmp_127_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -33670,18 +34757,18 @@ _tmp_125_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_125[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "param_no_default"));
+        D(fprintf(stderr, "%*c> _tmp_127[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "param_no_default"));
         arg_ty param_no_default_var;
         if (
             (param_no_default_var = param_no_default_rule(p))  // param_no_default
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_125[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "param_no_default"));
+            D(fprintf(stderr, "%*c+ _tmp_127[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "param_no_default"));
             _res = param_no_default_var;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_125[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_127[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "param_no_default"));
     }
     { // ','
@@ -33689,18 +34776,18 @@ _tmp_125_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_125[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','"));
+        D(fprintf(stderr, "%*c> _tmp_127[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','"));
         Token * _literal;
         if (
             (_literal = _PyPegen_expect_token(p, 12))  // token=','
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_125[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','"));
+            D(fprintf(stderr, "%*c+ _tmp_127[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','"));
             _res = _literal;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_125[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_127[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "','"));
     }
     _res = NULL;
@@ -33709,9 +34796,9 @@ _tmp_125_rule(Parser *p)
     return _res;
 }
 
-// _tmp_126: '*' | '**' | '/'
+// _tmp_128: '*' | '**' | '/'
 static void *
-_tmp_126_rule(Parser *p)
+_tmp_128_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -33727,18 +34814,18 @@ _tmp_126_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_126[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'*'"));
+        D(fprintf(stderr, "%*c> _tmp_128[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'*'"));
         Token * _literal;
         if (
             (_literal = _PyPegen_expect_token(p, 16))  // token='*'
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_126[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'*'"));
+            D(fprintf(stderr, "%*c+ _tmp_128[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'*'"));
             _res = _literal;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_126[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_128[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'*'"));
     }
     { // '**'
@@ -33746,18 +34833,18 @@ _tmp_126_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_126[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'**'"));
+        D(fprintf(stderr, "%*c> _tmp_128[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'**'"));
         Token * _literal;
         if (
             (_literal = _PyPegen_expect_token(p, 35))  // token='**'
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_126[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'**'"));
+            D(fprintf(stderr, "%*c+ _tmp_128[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'**'"));
             _res = _literal;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_126[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_128[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'**'"));
     }
     { // '/'
@@ -33765,18 +34852,18 @@ _tmp_126_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_126[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'/'"));
+        D(fprintf(stderr, "%*c> _tmp_128[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'/'"));
         Token * _literal;
         if (
             (_literal = _PyPegen_expect_token(p, 17))  // token='/'
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_126[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'/'"));
+            D(fprintf(stderr, "%*c+ _tmp_128[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'/'"));
             _res = _literal;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_126[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_128[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'/'"));
     }
     _res = NULL;
@@ -33785,9 +34872,9 @@ _tmp_126_rule(Parser *p)
     return _res;
 }
 
-// _tmp_127: lambda_slash_no_default | lambda_slash_with_default
+// _tmp_129: lambda_slash_no_default | lambda_slash_with_default
 static void *
-_tmp_127_rule(Parser *p)
+_tmp_129_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -33803,18 +34890,18 @@ _tmp_127_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_127[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_slash_no_default"));
+        D(fprintf(stderr, "%*c> _tmp_129[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_slash_no_default"));
         asdl_arg_seq* lambda_slash_no_default_var;
         if (
             (lambda_slash_no_default_var = lambda_slash_no_default_rule(p))  // lambda_slash_no_default
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_127[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_slash_no_default"));
+            D(fprintf(stderr, "%*c+ _tmp_129[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_slash_no_default"));
             _res = lambda_slash_no_default_var;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_127[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_129[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "lambda_slash_no_default"));
     }
     { // lambda_slash_with_default
@@ -33822,18 +34909,18 @@ _tmp_127_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_127[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_slash_with_default"));
+        D(fprintf(stderr, "%*c> _tmp_129[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_slash_with_default"));
         SlashWithDefault* lambda_slash_with_default_var;
         if (
             (lambda_slash_with_default_var = lambda_slash_with_default_rule(p))  // lambda_slash_with_default
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_127[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_slash_with_default"));
+            D(fprintf(stderr, "%*c+ _tmp_129[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_slash_with_default"));
             _res = lambda_slash_with_default_var;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_127[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_129[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "lambda_slash_with_default"));
     }
     _res = NULL;
@@ -33842,9 +34929,9 @@ _tmp_127_rule(Parser *p)
     return _res;
 }
 
-// _loop0_128: ',' lambda_param
+// _loop0_130: ',' lambda_param
 static asdl_seq *
-_loop0_128_rule(Parser *p)
+_loop0_130_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -33869,7 +34956,7 @@ _loop0_128_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _loop0_128[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' lambda_param"));
+        D(fprintf(stderr, "%*c> _loop0_130[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' lambda_param"));
         Token * _literal;
         arg_ty elem;
         while (
@@ -33901,7 +34988,7 @@ _loop0_128_rule(Parser *p)
             _mark = p->mark;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _loop0_128[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _loop0_130[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' lambda_param"));
     }
     asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena);
@@ -33918,9 +35005,9 @@ _loop0_128_rule(Parser *p)
     return _seq;
 }
 
-// _gather_129: lambda_param _loop0_128
+// _gather_131: lambda_param _loop0_130
 static asdl_seq *
-_gather_129_rule(Parser *p)
+_gather_131_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -33931,27 +35018,27 @@ _gather_129_rule(Parser *p)
     }
     asdl_seq * _res = NULL;
     int _mark = p->mark;
-    { // lambda_param _loop0_128
+    { // lambda_param _loop0_130
         if (p->error_indicator) {
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _gather_129[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param _loop0_128"));
+        D(fprintf(stderr, "%*c> _gather_131[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param _loop0_130"));
         arg_ty elem;
         asdl_seq * seq;
         if (
             (elem = lambda_param_rule(p))  // lambda_param
             &&
-            (seq = _loop0_128_rule(p))  // _loop0_128
+            (seq = _loop0_130_rule(p))  // _loop0_130
         )
         {
-            D(fprintf(stderr, "%*c+ _gather_129[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_param _loop0_128"));
+            D(fprintf(stderr, "%*c+ _gather_131[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_param _loop0_130"));
             _res = _PyPegen_seq_insert_in_front(p, elem, seq);
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _gather_129[%d-%d]: %s failed!\n", p->level, ' ',
-                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "lambda_param _loop0_128"));
+        D(fprintf(stderr, "%*c%s _gather_131[%d-%d]: %s failed!\n", p->level, ' ',
+                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "lambda_param _loop0_130"));
     }
     _res = NULL;
   done:
@@ -33959,9 +35046,9 @@ _gather_129_rule(Parser *p)
     return _res;
 }
 
-// _tmp_130: ',' | lambda_param_no_default
+// _tmp_132: ',' | lambda_param_no_default
 static void *
-_tmp_130_rule(Parser *p)
+_tmp_132_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -33977,18 +35064,18 @@ _tmp_130_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_130[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','"));
+        D(fprintf(stderr, "%*c> _tmp_132[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','"));
         Token * _literal;
         if (
             (_literal = _PyPegen_expect_token(p, 12))  // token=','
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_130[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','"));
+            D(fprintf(stderr, "%*c+ _tmp_132[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','"));
             _res = _literal;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_130[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_132[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "','"));
     }
     { // lambda_param_no_default
@@ -33996,18 +35083,18 @@ _tmp_130_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_130[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_no_default"));
+        D(fprintf(stderr, "%*c> _tmp_132[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_no_default"));
         arg_ty lambda_param_no_default_var;
         if (
             (lambda_param_no_default_var = lambda_param_no_default_rule(p))  // lambda_param_no_default
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_130[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_param_no_default"));
+            D(fprintf(stderr, "%*c+ _tmp_132[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_param_no_default"));
             _res = lambda_param_no_default_var;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_130[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_132[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "lambda_param_no_default"));
     }
     _res = NULL;
@@ -34016,9 +35103,9 @@ _tmp_130_rule(Parser *p)
     return _res;
 }
 
-// _tmp_131: ':' | ',' (':' | '**')
+// _tmp_133: ':' | ',' (':' | '**')
 static void *
-_tmp_131_rule(Parser *p)
+_tmp_133_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -34034,18 +35121,18 @@ _tmp_131_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_131[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':'"));
+        D(fprintf(stderr, "%*c> _tmp_133[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':'"));
         Token * _literal;
         if (
             (_literal = _PyPegen_expect_token(p, 11))  // token=':'
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_131[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':'"));
+            D(fprintf(stderr, "%*c+ _tmp_133[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':'"));
             _res = _literal;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_131[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_133[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "':'"));
     }
     { // ',' (':' | '**')
@@ -34053,21 +35140,21 @@ _tmp_131_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_131[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' (':' | '**')"));
+        D(fprintf(stderr, "%*c> _tmp_133[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' (':' | '**')"));
         Token * _literal;
-        void *_tmp_167_var;
+        void *_tmp_169_var;
         if (
             (_literal = _PyPegen_expect_token(p, 12))  // token=','
             &&
-            (_tmp_167_var = _tmp_167_rule(p))  // ':' | '**'
+            (_tmp_169_var = _tmp_169_rule(p))  // ':' | '**'
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_131[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' (':' | '**')"));
-            _res = _PyPegen_dummy_name(p, _literal, _tmp_167_var);
+            D(fprintf(stderr, "%*c+ _tmp_133[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' (':' | '**')"));
+            _res = _PyPegen_dummy_name(p, _literal, _tmp_169_var);
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_131[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_133[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' (':' | '**')"));
     }
     _res = NULL;
@@ -34076,9 +35163,9 @@ _tmp_131_rule(Parser *p)
     return _res;
 }
 
-// _tmp_132: lambda_param_no_default | ','
+// _tmp_134: lambda_param_no_default | ','
 static void *
-_tmp_132_rule(Parser *p)
+_tmp_134_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -34094,18 +35181,18 @@ _tmp_132_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_132[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_no_default"));
+        D(fprintf(stderr, "%*c> _tmp_134[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_no_default"));
         arg_ty lambda_param_no_default_var;
         if (
             (lambda_param_no_default_var = lambda_param_no_default_rule(p))  // lambda_param_no_default
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_132[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_param_no_default"));
+            D(fprintf(stderr, "%*c+ _tmp_134[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_param_no_default"));
             _res = lambda_param_no_default_var;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_132[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_134[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "lambda_param_no_default"));
     }
     { // ','
@@ -34113,18 +35200,18 @@ _tmp_132_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_132[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','"));
+        D(fprintf(stderr, "%*c> _tmp_134[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','"));
         Token * _literal;
         if (
             (_literal = _PyPegen_expect_token(p, 12))  // token=','
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_132[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','"));
+            D(fprintf(stderr, "%*c+ _tmp_134[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','"));
             _res = _literal;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_132[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_134[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "','"));
     }
     _res = NULL;
@@ -34133,9 +35220,9 @@ _tmp_132_rule(Parser *p)
     return _res;
 }
 
-// _tmp_133: bitwise_or ((',' bitwise_or))* ','?
+// _tmp_135: bitwise_or ((',' bitwise_or))* ','?
 static void *
-_tmp_133_rule(Parser *p)
+_tmp_135_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -34151,25 +35238,25 @@ _tmp_133_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_133[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "bitwise_or ((',' bitwise_or))* ','?"));
-        asdl_seq * _loop0_168_var;
+        D(fprintf(stderr, "%*c> _tmp_135[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "bitwise_or ((',' bitwise_or))* ','?"));
+        asdl_seq * _loop0_170_var;
         void *_opt_var;
         UNUSED(_opt_var); // Silence compiler warnings
         expr_ty bitwise_or_var;
         if (
             (bitwise_or_var = bitwise_or_rule(p))  // bitwise_or
             &&
-            (_loop0_168_var = _loop0_168_rule(p))  // ((',' bitwise_or))*
+            (_loop0_170_var = _loop0_170_rule(p))  // ((',' bitwise_or))*
             &&
             (_opt_var = _PyPegen_expect_token(p, 12), !p->error_indicator)  // ','?
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_133[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "bitwise_or ((',' bitwise_or))* ','?"));
-            _res = _PyPegen_dummy_name(p, bitwise_or_var, _loop0_168_var, _opt_var);
+            D(fprintf(stderr, "%*c+ _tmp_135[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "bitwise_or ((',' bitwise_or))* ','?"));
+            _res = _PyPegen_dummy_name(p, bitwise_or_var, _loop0_170_var, _opt_var);
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_133[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_135[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "bitwise_or ((',' bitwise_or))* ','?"));
     }
     _res = NULL;
@@ -34178,9 +35265,9 @@ _tmp_133_rule(Parser *p)
     return _res;
 }
 
-// _loop0_134: ',' dotted_name
+// _loop0_136: ',' dotted_name
 static asdl_seq *
-_loop0_134_rule(Parser *p)
+_loop0_136_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -34205,7 +35292,7 @@ _loop0_134_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _loop0_134[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' dotted_name"));
+        D(fprintf(stderr, "%*c> _loop0_136[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' dotted_name"));
         Token * _literal;
         expr_ty elem;
         while (
@@ -34237,7 +35324,7 @@ _loop0_134_rule(Parser *p)
             _mark = p->mark;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _loop0_134[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _loop0_136[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' dotted_name"));
     }
     asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena);
@@ -34254,9 +35341,9 @@ _loop0_134_rule(Parser *p)
     return _seq;
 }
 
-// _gather_135: dotted_name _loop0_134
+// _gather_137: dotted_name _loop0_136
 static asdl_seq *
-_gather_135_rule(Parser *p)
+_gather_137_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -34267,27 +35354,27 @@ _gather_135_rule(Parser *p)
     }
     asdl_seq * _res = NULL;
     int _mark = p->mark;
-    { // dotted_name _loop0_134
+    { // dotted_name _loop0_136
         if (p->error_indicator) {
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _gather_135[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "dotted_name _loop0_134"));
+        D(fprintf(stderr, "%*c> _gather_137[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "dotted_name _loop0_136"));
         expr_ty elem;
         asdl_seq * seq;
         if (
             (elem = dotted_name_rule(p))  // dotted_name
             &&
-            (seq = _loop0_134_rule(p))  // _loop0_134
+            (seq = _loop0_136_rule(p))  // _loop0_136
         )
         {
-            D(fprintf(stderr, "%*c+ _gather_135[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "dotted_name _loop0_134"));
+            D(fprintf(stderr, "%*c+ _gather_137[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "dotted_name _loop0_136"));
             _res = _PyPegen_seq_insert_in_front(p, elem, seq);
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _gather_135[%d-%d]: %s failed!\n", p->level, ' ',
-                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "dotted_name _loop0_134"));
+        D(fprintf(stderr, "%*c%s _gather_137[%d-%d]: %s failed!\n", p->level, ' ',
+                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "dotted_name _loop0_136"));
     }
     _res = NULL;
   done:
@@ -34295,9 +35382,9 @@ _gather_135_rule(Parser *p)
     return _res;
 }
 
-// _loop0_136: ',' (expression ['as' star_target])
+// _loop0_138: ',' (expression ['as' star_target])
 static asdl_seq *
-_loop0_136_rule(Parser *p)
+_loop0_138_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -34322,13 +35409,13 @@ _loop0_136_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _loop0_136[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' (expression ['as' star_target])"));
+        D(fprintf(stderr, "%*c> _loop0_138[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' (expression ['as' star_target])"));
         Token * _literal;
         void *elem;
         while (
             (_literal = _PyPegen_expect_token(p, 12))  // token=','
             &&
-            (elem = _tmp_169_rule(p))  // expression ['as' star_target]
+            (elem = _tmp_171_rule(p))  // expression ['as' star_target]
         )
         {
             _res = elem;
@@ -34354,7 +35441,7 @@ _loop0_136_rule(Parser *p)
             _mark = p->mark;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _loop0_136[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _loop0_138[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' (expression ['as' star_target])"));
     }
     asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena);
@@ -34371,9 +35458,9 @@ _loop0_136_rule(Parser *p)
     return _seq;
 }
 
-// _gather_137: (expression ['as' star_target]) _loop0_136
+// _gather_139: (expression ['as' star_target]) _loop0_138
 static asdl_seq *
-_gather_137_rule(Parser *p)
+_gather_139_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -34384,27 +35471,27 @@ _gather_137_rule(Parser *p)
     }
     asdl_seq * _res = NULL;
     int _mark = p->mark;
-    { // (expression ['as' star_target]) _loop0_136
+    { // (expression ['as' star_target]) _loop0_138
         if (p->error_indicator) {
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _gather_137[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(expression ['as' star_target]) _loop0_136"));
+        D(fprintf(stderr, "%*c> _gather_139[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(expression ['as' star_target]) _loop0_138"));
         void *elem;
         asdl_seq * seq;
         if (
-            (elem = _tmp_169_rule(p))  // expression ['as' star_target]
+            (elem = _tmp_171_rule(p))  // expression ['as' star_target]
             &&
-            (seq = _loop0_136_rule(p))  // _loop0_136
+            (seq = _loop0_138_rule(p))  // _loop0_138
         )
         {
-            D(fprintf(stderr, "%*c+ _gather_137[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "(expression ['as' star_target]) _loop0_136"));
+            D(fprintf(stderr, "%*c+ _gather_139[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "(expression ['as' star_target]) _loop0_138"));
             _res = _PyPegen_seq_insert_in_front(p, elem, seq);
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _gather_137[%d-%d]: %s failed!\n", p->level, ' ',
-                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(expression ['as' star_target]) _loop0_136"));
+        D(fprintf(stderr, "%*c%s _gather_139[%d-%d]: %s failed!\n", p->level, ' ',
+                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(expression ['as' star_target]) _loop0_138"));
     }
     _res = NULL;
   done:
@@ -34412,9 +35499,9 @@ _gather_137_rule(Parser *p)
     return _res;
 }
 
-// _loop0_138: ',' (expressions ['as' star_target])
+// _loop0_140: ',' (expressions ['as' star_target])
 static asdl_seq *
-_loop0_138_rule(Parser *p)
+_loop0_140_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -34439,13 +35526,13 @@ _loop0_138_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _loop0_138[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' (expressions ['as' star_target])"));
+        D(fprintf(stderr, "%*c> _loop0_140[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' (expressions ['as' star_target])"));
         Token * _literal;
         void *elem;
         while (
             (_literal = _PyPegen_expect_token(p, 12))  // token=','
             &&
-            (elem = _tmp_170_rule(p))  // expressions ['as' star_target]
+            (elem = _tmp_172_rule(p))  // expressions ['as' star_target]
         )
         {
             _res = elem;
@@ -34471,7 +35558,7 @@ _loop0_138_rule(Parser *p)
             _mark = p->mark;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _loop0_138[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _loop0_140[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' (expressions ['as' star_target])"));
     }
     asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena);
@@ -34488,9 +35575,9 @@ _loop0_138_rule(Parser *p)
     return _seq;
 }
 
-// _gather_139: (expressions ['as' star_target]) _loop0_138
+// _gather_141: (expressions ['as' star_target]) _loop0_140
 static asdl_seq *
-_gather_139_rule(Parser *p)
+_gather_141_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -34501,27 +35588,27 @@ _gather_139_rule(Parser *p)
     }
     asdl_seq * _res = NULL;
     int _mark = p->mark;
-    { // (expressions ['as' star_target]) _loop0_138
+    { // (expressions ['as' star_target]) _loop0_140
         if (p->error_indicator) {
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _gather_139[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(expressions ['as' star_target]) _loop0_138"));
+        D(fprintf(stderr, "%*c> _gather_141[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(expressions ['as' star_target]) _loop0_140"));
         void *elem;
         asdl_seq * seq;
         if (
-            (elem = _tmp_170_rule(p))  // expressions ['as' star_target]
+            (elem = _tmp_172_rule(p))  // expressions ['as' star_target]
             &&
-            (seq = _loop0_138_rule(p))  // _loop0_138
+            (seq = _loop0_140_rule(p))  // _loop0_140
         )
         {
-            D(fprintf(stderr, "%*c+ _gather_139[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "(expressions ['as' star_target]) _loop0_138"));
+            D(fprintf(stderr, "%*c+ _gather_141[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "(expressions ['as' star_target]) _loop0_140"));
             _res = _PyPegen_seq_insert_in_front(p, elem, seq);
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _gather_139[%d-%d]: %s failed!\n", p->level, ' ',
-                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(expressions ['as' star_target]) _loop0_138"));
+        D(fprintf(stderr, "%*c%s _gather_141[%d-%d]: %s failed!\n", p->level, ' ',
+                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(expressions ['as' star_target]) _loop0_140"));
     }
     _res = NULL;
   done:
@@ -34529,9 +35616,9 @@ _gather_139_rule(Parser *p)
     return _res;
 }
 
-// _tmp_140: 'except' | 'finally'
+// _tmp_142: 'except' | 'finally'
 static void *
-_tmp_140_rule(Parser *p)
+_tmp_142_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -34547,18 +35634,18 @@ _tmp_140_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_140[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'except'"));
+        D(fprintf(stderr, "%*c> _tmp_142[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'except'"));
         Token * _keyword;
         if (
-            (_keyword = _PyPegen_expect_token(p, 672))  // token='except'
+            (_keyword = _PyPegen_expect_token(p, 673))  // token='except'
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_140[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'except'"));
+            D(fprintf(stderr, "%*c+ _tmp_142[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'except'"));
             _res = _keyword;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_140[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_142[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'except'"));
     }
     { // 'finally'
@@ -34566,18 +35653,18 @@ _tmp_140_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_140[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'finally'"));
+        D(fprintf(stderr, "%*c> _tmp_142[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'finally'"));
         Token * _keyword;
         if (
-            (_keyword = _PyPegen_expect_token(p, 668))  // token='finally'
+            (_keyword = _PyPegen_expect_token(p, 669))  // token='finally'
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_140[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'finally'"));
+            D(fprintf(stderr, "%*c+ _tmp_142[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'finally'"));
             _res = _keyword;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_140[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_142[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'finally'"));
     }
     _res = NULL;
@@ -34586,9 +35673,9 @@ _tmp_140_rule(Parser *p)
     return _res;
 }
 
-// _loop0_141: block
+// _loop0_143: block
 static asdl_seq *
-_loop0_141_rule(Parser *p)
+_loop0_143_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -34613,7 +35700,7 @@ _loop0_141_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _loop0_141[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "block"));
+        D(fprintf(stderr, "%*c> _loop0_143[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "block"));
         asdl_stmt_seq* block_var;
         while (
             (block_var = block_rule(p))  // block
@@ -34636,7 +35723,7 @@ _loop0_141_rule(Parser *p)
             _mark = p->mark;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _loop0_141[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _loop0_143[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "block"));
     }
     asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena);
@@ -34653,9 +35740,9 @@ _loop0_141_rule(Parser *p)
     return _seq;
 }
 
-// _tmp_142: expression ['as' NAME]
+// _tmp_144: expression ['as' NAME]
 static void *
-_tmp_142_rule(Parser *p)
+_tmp_144_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -34671,7 +35758,7 @@ _tmp_142_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_142[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression ['as' NAME]"));
+        D(fprintf(stderr, "%*c> _tmp_144[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression ['as' NAME]"));
         void *_opt_var;
         UNUSED(_opt_var); // Silence compiler warnings
         expr_ty expression_var;
@@ -34681,12 +35768,12 @@ _tmp_142_rule(Parser *p)
             (_opt_var = _tmp_22_rule(p), !p->error_indicator)  // ['as' NAME]
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_142[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression ['as' NAME]"));
+            D(fprintf(stderr, "%*c+ _tmp_144[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression ['as' NAME]"));
             _res = _PyPegen_dummy_name(p, expression_var, _opt_var);
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_142[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_144[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "expression ['as' NAME]"));
     }
     _res = NULL;
@@ -34695,9 +35782,9 @@ _tmp_142_rule(Parser *p)
     return _res;
 }
 
-// _tmp_143: NEWLINE | ':'
+// _tmp_145: NEWLINE | ':'
 static void *
-_tmp_143_rule(Parser *p)
+_tmp_145_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -34713,18 +35800,18 @@ _tmp_143_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_143[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "NEWLINE"));
+        D(fprintf(stderr, "%*c> _tmp_145[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "NEWLINE"));
         Token * newline_var;
         if (
             (newline_var = _PyPegen_expect_token(p, NEWLINE))  // token='NEWLINE'
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_143[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "NEWLINE"));
+            D(fprintf(stderr, "%*c+ _tmp_145[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "NEWLINE"));
             _res = newline_var;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_143[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_145[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "NEWLINE"));
     }
     { // ':'
@@ -34732,18 +35819,18 @@ _tmp_143_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_143[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':'"));
+        D(fprintf(stderr, "%*c> _tmp_145[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':'"));
         Token * _literal;
         if (
             (_literal = _PyPegen_expect_token(p, 11))  // token=':'
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_143[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':'"));
+            D(fprintf(stderr, "%*c+ _tmp_145[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':'"));
             _res = _literal;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_143[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_145[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "':'"));
     }
     _res = NULL;
@@ -34752,9 +35839,9 @@ _tmp_143_rule(Parser *p)
     return _res;
 }
 
-// _tmp_144: positional_patterns ','
+// _tmp_146: positional_patterns ','
 static void *
-_tmp_144_rule(Parser *p)
+_tmp_146_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -34770,7 +35857,7 @@ _tmp_144_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_144[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "positional_patterns ','"));
+        D(fprintf(stderr, "%*c> _tmp_146[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "positional_patterns ','"));
         Token * _literal;
         asdl_pattern_seq* positional_patterns_var;
         if (
@@ -34779,12 +35866,12 @@ _tmp_144_rule(Parser *p)
             (_literal = _PyPegen_expect_token(p, 12))  // token=','
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_144[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "positional_patterns ','"));
+            D(fprintf(stderr, "%*c+ _tmp_146[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "positional_patterns ','"));
             _res = _PyPegen_dummy_name(p, positional_patterns_var, _literal);
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_144[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_146[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "positional_patterns ','"));
     }
     _res = NULL;
@@ -34793,9 +35880,9 @@ _tmp_144_rule(Parser *p)
     return _res;
 }
 
-// _tmp_145: '}' | ','
+// _tmp_147: '}' | ','
 static void *
-_tmp_145_rule(Parser *p)
+_tmp_147_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -34811,18 +35898,18 @@ _tmp_145_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_145[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'}'"));
+        D(fprintf(stderr, "%*c> _tmp_147[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'}'"));
         Token * _literal;
         if (
             (_literal = _PyPegen_expect_token(p, 26))  // token='}'
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_145[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'}'"));
+            D(fprintf(stderr, "%*c+ _tmp_147[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'}'"));
             _res = _literal;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_145[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_147[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'}'"));
     }
     { // ','
@@ -34830,18 +35917,18 @@ _tmp_145_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_145[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','"));
+        D(fprintf(stderr, "%*c> _tmp_147[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','"));
         Token * _literal;
         if (
             (_literal = _PyPegen_expect_token(p, 12))  // token=','
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_145[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','"));
+            D(fprintf(stderr, "%*c+ _tmp_147[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','"));
             _res = _literal;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_145[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_147[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "','"));
     }
     _res = NULL;
@@ -34850,9 +35937,9 @@ _tmp_145_rule(Parser *p)
     return _res;
 }
 
-// _tmp_146: '=' | '!' | ':' | '}'
+// _tmp_148: '=' | '!' | ':' | '}'
 static void *
-_tmp_146_rule(Parser *p)
+_tmp_148_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -34868,18 +35955,18 @@ _tmp_146_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_146[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'='"));
+        D(fprintf(stderr, "%*c> _tmp_148[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'='"));
         Token * _literal;
         if (
             (_literal = _PyPegen_expect_token(p, 22))  // token='='
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_146[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'='"));
+            D(fprintf(stderr, "%*c+ _tmp_148[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'='"));
             _res = _literal;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_146[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_148[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'='"));
     }
     { // '!'
@@ -34887,18 +35974,18 @@ _tmp_146_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_146[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'!'"));
+        D(fprintf(stderr, "%*c> _tmp_148[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'!'"));
         Token * _literal;
         if (
             (_literal = _PyPegen_expect_token(p, 54))  // token='!'
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_146[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'!'"));
+            D(fprintf(stderr, "%*c+ _tmp_148[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'!'"));
             _res = _literal;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_146[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_148[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'!'"));
     }
     { // ':'
@@ -34906,18 +35993,18 @@ _tmp_146_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_146[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':'"));
+        D(fprintf(stderr, "%*c> _tmp_148[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':'"));
         Token * _literal;
         if (
             (_literal = _PyPegen_expect_token(p, 11))  // token=':'
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_146[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':'"));
+            D(fprintf(stderr, "%*c+ _tmp_148[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':'"));
             _res = _literal;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_146[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_148[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "':'"));
     }
     { // '}'
@@ -34925,18 +36012,18 @@ _tmp_146_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_146[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'}'"));
+        D(fprintf(stderr, "%*c> _tmp_148[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'}'"));
         Token * _literal;
         if (
             (_literal = _PyPegen_expect_token(p, 26))  // token='}'
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_146[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'}'"));
+            D(fprintf(stderr, "%*c+ _tmp_148[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'}'"));
             _res = _literal;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_146[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_148[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'}'"));
     }
     _res = NULL;
@@ -34945,9 +36032,9 @@ _tmp_146_rule(Parser *p)
     return _res;
 }
 
-// _tmp_147: '!' | ':' | '}'
+// _tmp_149: '!' | ':' | '}'
 static void *
-_tmp_147_rule(Parser *p)
+_tmp_149_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -34963,18 +36050,18 @@ _tmp_147_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_147[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'!'"));
+        D(fprintf(stderr, "%*c> _tmp_149[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'!'"));
         Token * _literal;
         if (
             (_literal = _PyPegen_expect_token(p, 54))  // token='!'
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_147[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'!'"));
+            D(fprintf(stderr, "%*c+ _tmp_149[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'!'"));
             _res = _literal;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_147[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_149[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'!'"));
     }
     { // ':'
@@ -34982,18 +36069,18 @@ _tmp_147_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_147[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':'"));
+        D(fprintf(stderr, "%*c> _tmp_149[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':'"));
         Token * _literal;
         if (
             (_literal = _PyPegen_expect_token(p, 11))  // token=':'
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_147[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':'"));
+            D(fprintf(stderr, "%*c+ _tmp_149[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':'"));
             _res = _literal;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_147[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_149[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "':'"));
     }
     { // '}'
@@ -35001,18 +36088,18 @@ _tmp_147_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_147[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'}'"));
+        D(fprintf(stderr, "%*c> _tmp_149[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'}'"));
         Token * _literal;
         if (
             (_literal = _PyPegen_expect_token(p, 26))  // token='}'
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_147[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'}'"));
+            D(fprintf(stderr, "%*c+ _tmp_149[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'}'"));
             _res = _literal;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_147[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_149[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'}'"));
     }
     _res = NULL;
@@ -35021,9 +36108,9 @@ _tmp_147_rule(Parser *p)
     return _res;
 }
 
-// _tmp_148: '!' NAME
+// _tmp_150: '!' NAME
 static void *
-_tmp_148_rule(Parser *p)
+_tmp_150_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -35039,7 +36126,7 @@ _tmp_148_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_148[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'!' NAME"));
+        D(fprintf(stderr, "%*c> _tmp_150[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'!' NAME"));
         Token * _literal;
         expr_ty name_var;
         if (
@@ -35048,12 +36135,12 @@ _tmp_148_rule(Parser *p)
             (name_var = _PyPegen_name_token(p))  // NAME
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_148[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'!' NAME"));
+            D(fprintf(stderr, "%*c+ _tmp_150[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'!' NAME"));
             _res = _PyPegen_dummy_name(p, _literal, name_var);
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_148[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_150[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'!' NAME"));
     }
     _res = NULL;
@@ -35062,9 +36149,9 @@ _tmp_148_rule(Parser *p)
     return _res;
 }
 
-// _tmp_149: ':' | '}'
+// _tmp_151: ':' | '}'
 static void *
-_tmp_149_rule(Parser *p)
+_tmp_151_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -35080,18 +36167,18 @@ _tmp_149_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_149[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':'"));
+        D(fprintf(stderr, "%*c> _tmp_151[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':'"));
         Token * _literal;
         if (
             (_literal = _PyPegen_expect_token(p, 11))  // token=':'
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_149[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':'"));
+            D(fprintf(stderr, "%*c+ _tmp_151[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':'"));
             _res = _literal;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_149[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_151[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "':'"));
     }
     { // '}'
@@ -35099,18 +36186,18 @@ _tmp_149_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_149[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'}'"));
+        D(fprintf(stderr, "%*c> _tmp_151[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'}'"));
         Token * _literal;
         if (
             (_literal = _PyPegen_expect_token(p, 26))  // token='}'
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_149[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'}'"));
+            D(fprintf(stderr, "%*c+ _tmp_151[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'}'"));
             _res = _literal;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_149[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_151[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'}'"));
     }
     _res = NULL;
@@ -35119,9 +36206,9 @@ _tmp_149_rule(Parser *p)
     return _res;
 }
 
-// _tmp_150: '+' | '-' | '*' | '/' | '%' | '//' | '@'
+// _tmp_152: '+' | '-' | '*' | '/' | '%' | '//' | '@'
 static void *
-_tmp_150_rule(Parser *p)
+_tmp_152_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -35137,18 +36224,18 @@ _tmp_150_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_150[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'+'"));
+        D(fprintf(stderr, "%*c> _tmp_152[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'+'"));
         Token * _literal;
         if (
             (_literal = _PyPegen_expect_token(p, 14))  // token='+'
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_150[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'+'"));
+            D(fprintf(stderr, "%*c+ _tmp_152[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'+'"));
             _res = _literal;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_150[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_152[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'+'"));
     }
     { // '-'
@@ -35156,18 +36243,18 @@ _tmp_150_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_150[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'-'"));
+        D(fprintf(stderr, "%*c> _tmp_152[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'-'"));
         Token * _literal;
         if (
             (_literal = _PyPegen_expect_token(p, 15))  // token='-'
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_150[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'-'"));
+            D(fprintf(stderr, "%*c+ _tmp_152[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'-'"));
             _res = _literal;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_150[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_152[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'-'"));
     }
     { // '*'
@@ -35175,18 +36262,18 @@ _tmp_150_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_150[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'*'"));
+        D(fprintf(stderr, "%*c> _tmp_152[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'*'"));
         Token * _literal;
         if (
             (_literal = _PyPegen_expect_token(p, 16))  // token='*'
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_150[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'*'"));
+            D(fprintf(stderr, "%*c+ _tmp_152[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'*'"));
             _res = _literal;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_150[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_152[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'*'"));
     }
     { // '/'
@@ -35194,18 +36281,18 @@ _tmp_150_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_150[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'/'"));
+        D(fprintf(stderr, "%*c> _tmp_152[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'/'"));
         Token * _literal;
         if (
             (_literal = _PyPegen_expect_token(p, 17))  // token='/'
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_150[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'/'"));
+            D(fprintf(stderr, "%*c+ _tmp_152[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'/'"));
             _res = _literal;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_150[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_152[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'/'"));
     }
     { // '%'
@@ -35213,18 +36300,18 @@ _tmp_150_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_150[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'%'"));
+        D(fprintf(stderr, "%*c> _tmp_152[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'%'"));
         Token * _literal;
         if (
             (_literal = _PyPegen_expect_token(p, 24))  // token='%'
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_150[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'%'"));
+            D(fprintf(stderr, "%*c+ _tmp_152[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'%'"));
             _res = _literal;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_150[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_152[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'%'"));
     }
     { // '//'
@@ -35232,18 +36319,18 @@ _tmp_150_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_150[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'//'"));
+        D(fprintf(stderr, "%*c> _tmp_152[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'//'"));
         Token * _literal;
         if (
             (_literal = _PyPegen_expect_token(p, 47))  // token='//'
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_150[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'//'"));
+            D(fprintf(stderr, "%*c+ _tmp_152[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'//'"));
             _res = _literal;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_150[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_152[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'//'"));
     }
     { // '@'
@@ -35251,18 +36338,18 @@ _tmp_150_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_150[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'@'"));
+        D(fprintf(stderr, "%*c> _tmp_152[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'@'"));
         Token * _literal;
         if (
             (_literal = _PyPegen_expect_token(p, 49))  // token='@'
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_150[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'@'"));
+            D(fprintf(stderr, "%*c+ _tmp_152[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'@'"));
             _res = _literal;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_150[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_152[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'@'"));
     }
     _res = NULL;
@@ -35271,9 +36358,9 @@ _tmp_150_rule(Parser *p)
     return _res;
 }
 
-// _tmp_151: '+' | '-' | '~'
+// _tmp_153: '+' | '-' | '~'
 static void *
-_tmp_151_rule(Parser *p)
+_tmp_153_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -35289,18 +36376,18 @@ _tmp_151_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_151[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'+'"));
+        D(fprintf(stderr, "%*c> _tmp_153[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'+'"));
         Token * _literal;
         if (
             (_literal = _PyPegen_expect_token(p, 14))  // token='+'
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_151[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'+'"));
+            D(fprintf(stderr, "%*c+ _tmp_153[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'+'"));
             _res = _literal;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_151[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_153[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'+'"));
     }
     { // '-'
@@ -35308,18 +36395,18 @@ _tmp_151_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_151[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'-'"));
+        D(fprintf(stderr, "%*c> _tmp_153[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'-'"));
         Token * _literal;
         if (
             (_literal = _PyPegen_expect_token(p, 15))  // token='-'
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_151[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'-'"));
+            D(fprintf(stderr, "%*c+ _tmp_153[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'-'"));
             _res = _literal;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_151[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_153[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'-'"));
     }
     { // '~'
@@ -35327,18 +36414,18 @@ _tmp_151_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_151[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'~'"));
+        D(fprintf(stderr, "%*c> _tmp_153[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'~'"));
         Token * _literal;
         if (
             (_literal = _PyPegen_expect_token(p, 31))  // token='~'
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_151[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'~'"));
+            D(fprintf(stderr, "%*c+ _tmp_153[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'~'"));
             _res = _literal;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_151[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_153[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'~'"));
     }
     _res = NULL;
@@ -35347,9 +36434,9 @@ _tmp_151_rule(Parser *p)
     return _res;
 }
 
-// _tmp_152: star_targets '='
+// _tmp_154: star_targets '='
 static void *
-_tmp_152_rule(Parser *p)
+_tmp_154_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -35365,7 +36452,7 @@ _tmp_152_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_152[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_targets '='"));
+        D(fprintf(stderr, "%*c> _tmp_154[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_targets '='"));
         Token * _literal;
         expr_ty z;
         if (
@@ -35374,7 +36461,7 @@ _tmp_152_rule(Parser *p)
             (_literal = _PyPegen_expect_token(p, 22))  // token='='
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_152[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_targets '='"));
+            D(fprintf(stderr, "%*c+ _tmp_154[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_targets '='"));
             _res = z;
             if (_res == NULL && PyErr_Occurred()) {
                 p->error_indicator = 1;
@@ -35384,7 +36471,7 @@ _tmp_152_rule(Parser *p)
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_152[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_154[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "star_targets '='"));
     }
     _res = NULL;
@@ -35393,9 +36480,9 @@ _tmp_152_rule(Parser *p)
     return _res;
 }
 
-// _tmp_153: '.' | '...'
+// _tmp_155: '.' | '...'
 static void *
-_tmp_153_rule(Parser *p)
+_tmp_155_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -35411,18 +36498,18 @@ _tmp_153_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_153[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'.'"));
+        D(fprintf(stderr, "%*c> _tmp_155[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'.'"));
         Token * _literal;
         if (
             (_literal = _PyPegen_expect_token(p, 23))  // token='.'
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_153[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'.'"));
+            D(fprintf(stderr, "%*c+ _tmp_155[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'.'"));
             _res = _literal;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_153[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_155[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'.'"));
     }
     { // '...'
@@ -35430,18 +36517,18 @@ _tmp_153_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_153[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'...'"));
+        D(fprintf(stderr, "%*c> _tmp_155[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'...'"));
         Token * _literal;
         if (
             (_literal = _PyPegen_expect_token(p, 52))  // token='...'
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_153[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'...'"));
+            D(fprintf(stderr, "%*c+ _tmp_155[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'...'"));
             _res = _literal;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_153[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_155[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'...'"));
     }
     _res = NULL;
@@ -35450,9 +36537,9 @@ _tmp_153_rule(Parser *p)
     return _res;
 }
 
-// _tmp_154: '@' named_expression NEWLINE
+// _tmp_156: '@' named_expression NEWLINE
 static void *
-_tmp_154_rule(Parser *p)
+_tmp_156_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -35468,7 +36555,7 @@ _tmp_154_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_154[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'@' named_expression NEWLINE"));
+        D(fprintf(stderr, "%*c> _tmp_156[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'@' named_expression NEWLINE"));
         Token * _literal;
         expr_ty f;
         Token * newline_var;
@@ -35480,7 +36567,7 @@ _tmp_154_rule(Parser *p)
             (newline_var = _PyPegen_expect_token(p, NEWLINE))  // token='NEWLINE'
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_154[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'@' named_expression NEWLINE"));
+            D(fprintf(stderr, "%*c+ _tmp_156[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'@' named_expression NEWLINE"));
             _res = f;
             if (_res == NULL && PyErr_Occurred()) {
                 p->error_indicator = 1;
@@ -35490,7 +36577,7 @@ _tmp_154_rule(Parser *p)
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_154[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_156[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'@' named_expression NEWLINE"));
     }
     _res = NULL;
@@ -35499,9 +36586,9 @@ _tmp_154_rule(Parser *p)
     return _res;
 }
 
-// _tmp_155: ',' star_expression
+// _tmp_157: ',' star_expression
 static void *
-_tmp_155_rule(Parser *p)
+_tmp_157_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -35517,7 +36604,7 @@ _tmp_155_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_155[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' star_expression"));
+        D(fprintf(stderr, "%*c> _tmp_157[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' star_expression"));
         Token * _literal;
         expr_ty c;
         if (
@@ -35526,7 +36613,7 @@ _tmp_155_rule(Parser *p)
             (c = star_expression_rule(p))  // star_expression
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_155[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' star_expression"));
+            D(fprintf(stderr, "%*c+ _tmp_157[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' star_expression"));
             _res = c;
             if (_res == NULL && PyErr_Occurred()) {
                 p->error_indicator = 1;
@@ -35536,7 +36623,7 @@ _tmp_155_rule(Parser *p)
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_155[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_157[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' star_expression"));
     }
     _res = NULL;
@@ -35545,9 +36632,9 @@ _tmp_155_rule(Parser *p)
     return _res;
 }
 
-// _tmp_156: 'or' conjunction
+// _tmp_158: 'or' conjunction
 static void *
-_tmp_156_rule(Parser *p)
+_tmp_158_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -35563,7 +36650,7 @@ _tmp_156_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_156[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'or' conjunction"));
+        D(fprintf(stderr, "%*c> _tmp_158[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'or' conjunction"));
         Token * _keyword;
         expr_ty c;
         if (
@@ -35572,7 +36659,7 @@ _tmp_156_rule(Parser *p)
             (c = conjunction_rule(p))  // conjunction
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_156[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'or' conjunction"));
+            D(fprintf(stderr, "%*c+ _tmp_158[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'or' conjunction"));
             _res = c;
             if (_res == NULL && PyErr_Occurred()) {
                 p->error_indicator = 1;
@@ -35582,7 +36669,7 @@ _tmp_156_rule(Parser *p)
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_156[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_158[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'or' conjunction"));
     }
     _res = NULL;
@@ -35591,9 +36678,9 @@ _tmp_156_rule(Parser *p)
     return _res;
 }
 
-// _tmp_157: 'and' inversion
+// _tmp_159: 'and' inversion
 static void *
-_tmp_157_rule(Parser *p)
+_tmp_159_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -35609,7 +36696,7 @@ _tmp_157_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_157[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'and' inversion"));
+        D(fprintf(stderr, "%*c> _tmp_159[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'and' inversion"));
         Token * _keyword;
         expr_ty c;
         if (
@@ -35618,7 +36705,7 @@ _tmp_157_rule(Parser *p)
             (c = inversion_rule(p))  // inversion
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_157[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'and' inversion"));
+            D(fprintf(stderr, "%*c+ _tmp_159[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'and' inversion"));
             _res = c;
             if (_res == NULL && PyErr_Occurred()) {
                 p->error_indicator = 1;
@@ -35628,7 +36715,7 @@ _tmp_157_rule(Parser *p)
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_157[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_159[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'and' inversion"));
     }
     _res = NULL;
@@ -35637,9 +36724,9 @@ _tmp_157_rule(Parser *p)
     return _res;
 }
 
-// _tmp_158: slice | starred_expression
+// _tmp_160: slice | starred_expression
 static void *
-_tmp_158_rule(Parser *p)
+_tmp_160_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -35655,18 +36742,18 @@ _tmp_158_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_158[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "slice"));
+        D(fprintf(stderr, "%*c> _tmp_160[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "slice"));
         expr_ty slice_var;
         if (
             (slice_var = slice_rule(p))  // slice
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_158[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "slice"));
+            D(fprintf(stderr, "%*c+ _tmp_160[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "slice"));
             _res = slice_var;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_158[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_160[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "slice"));
     }
     { // starred_expression
@@ -35674,18 +36761,18 @@ _tmp_158_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_158[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "starred_expression"));
+        D(fprintf(stderr, "%*c> _tmp_160[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "starred_expression"));
         expr_ty starred_expression_var;
         if (
             (starred_expression_var = starred_expression_rule(p))  // starred_expression
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_158[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "starred_expression"));
+            D(fprintf(stderr, "%*c+ _tmp_160[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "starred_expression"));
             _res = starred_expression_var;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_158[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_160[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "starred_expression"));
     }
     _res = NULL;
@@ -35694,9 +36781,9 @@ _tmp_158_rule(Parser *p)
     return _res;
 }
 
-// _tmp_159: fstring | string
+// _tmp_161: fstring | string | tstring
 static void *
-_tmp_159_rule(Parser *p)
+_tmp_161_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -35712,18 +36799,18 @@ _tmp_159_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_159[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "fstring"));
+        D(fprintf(stderr, "%*c> _tmp_161[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "fstring"));
         expr_ty fstring_var;
         if (
             (fstring_var = fstring_rule(p))  // fstring
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_159[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "fstring"));
+            D(fprintf(stderr, "%*c+ _tmp_161[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "fstring"));
             _res = fstring_var;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_159[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_161[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "fstring"));
     }
     { // string
@@ -35731,29 +36818,48 @@ _tmp_159_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_159[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "string"));
+        D(fprintf(stderr, "%*c> _tmp_161[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "string"));
         expr_ty string_var;
         if (
             (string_var = string_rule(p))  // string
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_159[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "string"));
+            D(fprintf(stderr, "%*c+ _tmp_161[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "string"));
             _res = string_var;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_159[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_161[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "string"));
     }
+    { // tstring
+        if (p->error_indicator) {
+            p->level--;
+            return NULL;
+        }
+        D(fprintf(stderr, "%*c> _tmp_161[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "tstring"));
+        expr_ty tstring_var;
+        if (
+            (tstring_var = tstring_rule(p))  // tstring
+        )
+        {
+            D(fprintf(stderr, "%*c+ _tmp_161[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "tstring"));
+            _res = tstring_var;
+            goto done;
+        }
+        p->mark = _mark;
+        D(fprintf(stderr, "%*c%s _tmp_161[%d-%d]: %s failed!\n", p->level, ' ',
+                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "tstring"));
+    }
     _res = NULL;
   done:
     p->level--;
     return _res;
 }
 
-// _tmp_160: 'if' disjunction
+// _tmp_162: 'if' disjunction
 static void *
-_tmp_160_rule(Parser *p)
+_tmp_162_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -35769,16 +36875,16 @@ _tmp_160_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_160[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'if' disjunction"));
+        D(fprintf(stderr, "%*c> _tmp_162[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'if' disjunction"));
         Token * _keyword;
         expr_ty z;
         if (
-            (_keyword = _PyPegen_expect_token(p, 677))  // token='if'
+            (_keyword = _PyPegen_expect_token(p, 678))  // token='if'
             &&
             (z = disjunction_rule(p))  // disjunction
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_160[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'if' disjunction"));
+            D(fprintf(stderr, "%*c+ _tmp_162[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'if' disjunction"));
             _res = z;
             if (_res == NULL && PyErr_Occurred()) {
                 p->error_indicator = 1;
@@ -35788,7 +36894,7 @@ _tmp_160_rule(Parser *p)
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_160[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_162[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'if' disjunction"));
     }
     _res = NULL;
@@ -35797,9 +36903,9 @@ _tmp_160_rule(Parser *p)
     return _res;
 }
 
-// _tmp_161: starred_expression | (assignment_expression | expression !':=') !'='
+// _tmp_163: starred_expression | (assignment_expression | expression !':=') !'='
 static void *
-_tmp_161_rule(Parser *p)
+_tmp_163_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -35815,18 +36921,18 @@ _tmp_161_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_161[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "starred_expression"));
+        D(fprintf(stderr, "%*c> _tmp_163[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "starred_expression"));
         expr_ty starred_expression_var;
         if (
             (starred_expression_var = starred_expression_rule(p))  // starred_expression
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_161[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "starred_expression"));
+            D(fprintf(stderr, "%*c+ _tmp_163[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "starred_expression"));
             _res = starred_expression_var;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_161[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_163[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "starred_expression"));
     }
     { // (assignment_expression | expression !':=') !'='
@@ -35834,20 +36940,20 @@ _tmp_161_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_161[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(assignment_expression | expression !':=') !'='"));
-        void *_tmp_85_var;
+        D(fprintf(stderr, "%*c> _tmp_163[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(assignment_expression | expression !':=') !'='"));
+        void *_tmp_87_var;
         if (
-            (_tmp_85_var = _tmp_85_rule(p))  // assignment_expression | expression !':='
+            (_tmp_87_var = _tmp_87_rule(p))  // assignment_expression | expression !':='
             &&
             _PyPegen_lookahead_with_int(0, _PyPegen_expect_token, p, 22)  // token='='
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_161[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "(assignment_expression | expression !':=') !'='"));
-            _res = _tmp_85_var;
+            D(fprintf(stderr, "%*c+ _tmp_163[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "(assignment_expression | expression !':=') !'='"));
+            _res = _tmp_87_var;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_161[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_163[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(assignment_expression | expression !':=') !'='"));
     }
     _res = NULL;
@@ -35856,9 +36962,9 @@ _tmp_161_rule(Parser *p)
     return _res;
 }
 
-// _tmp_162: ',' star_target
+// _tmp_164: ',' star_target
 static void *
-_tmp_162_rule(Parser *p)
+_tmp_164_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -35874,7 +36980,7 @@ _tmp_162_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_162[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' star_target"));
+        D(fprintf(stderr, "%*c> _tmp_164[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' star_target"));
         Token * _literal;
         expr_ty c;
         if (
@@ -35883,7 +36989,7 @@ _tmp_162_rule(Parser *p)
             (c = star_target_rule(p))  // star_target
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_162[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' star_target"));
+            D(fprintf(stderr, "%*c+ _tmp_164[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' star_target"));
             _res = c;
             if (_res == NULL && PyErr_Occurred()) {
                 p->error_indicator = 1;
@@ -35893,7 +36999,7 @@ _tmp_162_rule(Parser *p)
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_162[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_164[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' star_target"));
     }
     _res = NULL;
@@ -35902,10 +37008,10 @@ _tmp_162_rule(Parser *p)
     return _res;
 }
 
-// _tmp_163:
+// _tmp_165:
 //     | ','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs
 static void *
-_tmp_163_rule(Parser *p)
+_tmp_165_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -35921,24 +37027,24 @@ _tmp_163_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_163[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs"));
-        asdl_seq * _gather_87_var;
+        D(fprintf(stderr, "%*c> _tmp_165[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs"));
+        asdl_seq * _gather_89_var;
         Token * _literal;
         asdl_seq* kwargs_var;
         if (
-            (_gather_87_var = _gather_87_rule(p))  // ','.(starred_expression | (assignment_expression | expression !':=') !'=')+
+            (_gather_89_var = _gather_89_rule(p))  // ','.(starred_expression | (assignment_expression | expression !':=') !'=')+
             &&
             (_literal = _PyPegen_expect_token(p, 12))  // token=','
             &&
             (kwargs_var = kwargs_rule(p))  // kwargs
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_163[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs"));
-            _res = _PyPegen_dummy_name(p, _gather_87_var, _literal, kwargs_var);
+            D(fprintf(stderr, "%*c+ _tmp_165[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs"));
+            _res = _PyPegen_dummy_name(p, _gather_89_var, _literal, kwargs_var);
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_163[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_165[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs"));
     }
     _res = NULL;
@@ -35947,9 +37053,9 @@ _tmp_163_rule(Parser *p)
     return _res;
 }
 
-// _tmp_164: starred_expression !'='
+// _tmp_166: starred_expression !'='
 static void *
-_tmp_164_rule(Parser *p)
+_tmp_166_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -35965,7 +37071,7 @@ _tmp_164_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_164[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "starred_expression !'='"));
+        D(fprintf(stderr, "%*c> _tmp_166[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "starred_expression !'='"));
         expr_ty starred_expression_var;
         if (
             (starred_expression_var = starred_expression_rule(p))  // starred_expression
@@ -35973,12 +37079,12 @@ _tmp_164_rule(Parser *p)
             _PyPegen_lookahead_with_int(0, _PyPegen_expect_token, p, 22)  // token='='
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_164[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "starred_expression !'='"));
+            D(fprintf(stderr, "%*c+ _tmp_166[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "starred_expression !'='"));
             _res = starred_expression_var;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_164[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_166[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "starred_expression !'='"));
     }
     _res = NULL;
@@ -35987,9 +37093,9 @@ _tmp_164_rule(Parser *p)
     return _res;
 }
 
-// _tmp_165: !STRING expression_without_invalid
+// _tmp_167: !STRING expression_without_invalid
 static void *
-_tmp_165_rule(Parser *p)
+_tmp_167_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -36005,7 +37111,7 @@ _tmp_165_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_165[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "!STRING expression_without_invalid"));
+        D(fprintf(stderr, "%*c> _tmp_167[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "!STRING expression_without_invalid"));
         expr_ty expression_without_invalid_var;
         if (
             _PyPegen_lookahead(0, (void *(*)(Parser *)) _PyPegen_string_token, p)
@@ -36013,12 +37119,12 @@ _tmp_165_rule(Parser *p)
             (expression_without_invalid_var = expression_without_invalid_rule(p))  // expression_without_invalid
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_165[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "!STRING expression_without_invalid"));
+            D(fprintf(stderr, "%*c+ _tmp_167[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "!STRING expression_without_invalid"));
             _res = expression_without_invalid_var;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_165[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_167[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "!STRING expression_without_invalid"));
     }
     _res = NULL;
@@ -36027,9 +37133,9 @@ _tmp_165_rule(Parser *p)
     return _res;
 }
 
-// _tmp_166: ')' | '**'
+// _tmp_168: ')' | '**'
 static void *
-_tmp_166_rule(Parser *p)
+_tmp_168_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -36045,18 +37151,18 @@ _tmp_166_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_166[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "')'"));
+        D(fprintf(stderr, "%*c> _tmp_168[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "')'"));
         Token * _literal;
         if (
             (_literal = _PyPegen_expect_token(p, 8))  // token=')'
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_166[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "')'"));
+            D(fprintf(stderr, "%*c+ _tmp_168[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "')'"));
             _res = _literal;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_166[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_168[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "')'"));
     }
     { // '**'
@@ -36064,18 +37170,18 @@ _tmp_166_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_166[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'**'"));
+        D(fprintf(stderr, "%*c> _tmp_168[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'**'"));
         Token * _literal;
         if (
             (_literal = _PyPegen_expect_token(p, 35))  // token='**'
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_166[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'**'"));
+            D(fprintf(stderr, "%*c+ _tmp_168[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'**'"));
             _res = _literal;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_166[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_168[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'**'"));
     }
     _res = NULL;
@@ -36084,9 +37190,9 @@ _tmp_166_rule(Parser *p)
     return _res;
 }
 
-// _tmp_167: ':' | '**'
+// _tmp_169: ':' | '**'
 static void *
-_tmp_167_rule(Parser *p)
+_tmp_169_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -36102,18 +37208,18 @@ _tmp_167_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_167[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':'"));
+        D(fprintf(stderr, "%*c> _tmp_169[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':'"));
         Token * _literal;
         if (
             (_literal = _PyPegen_expect_token(p, 11))  // token=':'
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_167[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':'"));
+            D(fprintf(stderr, "%*c+ _tmp_169[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':'"));
             _res = _literal;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_167[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_169[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "':'"));
     }
     { // '**'
@@ -36121,18 +37227,18 @@ _tmp_167_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_167[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'**'"));
+        D(fprintf(stderr, "%*c> _tmp_169[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'**'"));
         Token * _literal;
         if (
             (_literal = _PyPegen_expect_token(p, 35))  // token='**'
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_167[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'**'"));
+            D(fprintf(stderr, "%*c+ _tmp_169[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'**'"));
             _res = _literal;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_167[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_169[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'**'"));
     }
     _res = NULL;
@@ -36141,9 +37247,9 @@ _tmp_167_rule(Parser *p)
     return _res;
 }
 
-// _loop0_168: (',' bitwise_or)
+// _loop0_170: (',' bitwise_or)
 static asdl_seq *
-_loop0_168_rule(Parser *p)
+_loop0_170_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -36168,13 +37274,13 @@ _loop0_168_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _loop0_168[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(',' bitwise_or)"));
-        void *_tmp_171_var;
+        D(fprintf(stderr, "%*c> _loop0_170[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(',' bitwise_or)"));
+        void *_tmp_173_var;
         while (
-            (_tmp_171_var = _tmp_171_rule(p))  // ',' bitwise_or
+            (_tmp_173_var = _tmp_173_rule(p))  // ',' bitwise_or
         )
         {
-            _res = _tmp_171_var;
+            _res = _tmp_173_var;
             if (_n == _children_capacity) {
                 _children_capacity *= 2;
                 void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *));
@@ -36191,7 +37297,7 @@ _loop0_168_rule(Parser *p)
             _mark = p->mark;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _loop0_168[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _loop0_170[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(',' bitwise_or)"));
     }
     asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena);
@@ -36208,9 +37314,9 @@ _loop0_168_rule(Parser *p)
     return _seq;
 }
 
-// _tmp_169: expression ['as' star_target]
+// _tmp_171: expression ['as' star_target]
 static void *
-_tmp_169_rule(Parser *p)
+_tmp_171_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -36226,22 +37332,22 @@ _tmp_169_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_169[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression ['as' star_target]"));
+        D(fprintf(stderr, "%*c> _tmp_171[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression ['as' star_target]"));
         void *_opt_var;
         UNUSED(_opt_var); // Silence compiler warnings
         expr_ty expression_var;
         if (
             (expression_var = expression_rule(p))  // expression
             &&
-            (_opt_var = _tmp_172_rule(p), !p->error_indicator)  // ['as' star_target]
+            (_opt_var = _tmp_174_rule(p), !p->error_indicator)  // ['as' star_target]
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_169[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression ['as' star_target]"));
+            D(fprintf(stderr, "%*c+ _tmp_171[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression ['as' star_target]"));
             _res = _PyPegen_dummy_name(p, expression_var, _opt_var);
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_169[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_171[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "expression ['as' star_target]"));
     }
     _res = NULL;
@@ -36250,9 +37356,9 @@ _tmp_169_rule(Parser *p)
     return _res;
 }
 
-// _tmp_170: expressions ['as' star_target]
+// _tmp_172: expressions ['as' star_target]
 static void *
-_tmp_170_rule(Parser *p)
+_tmp_172_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -36268,22 +37374,22 @@ _tmp_170_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_170[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expressions ['as' star_target]"));
+        D(fprintf(stderr, "%*c> _tmp_172[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expressions ['as' star_target]"));
         void *_opt_var;
         UNUSED(_opt_var); // Silence compiler warnings
         expr_ty expressions_var;
         if (
             (expressions_var = expressions_rule(p))  // expressions
             &&
-            (_opt_var = _tmp_172_rule(p), !p->error_indicator)  // ['as' star_target]
+            (_opt_var = _tmp_174_rule(p), !p->error_indicator)  // ['as' star_target]
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_170[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expressions ['as' star_target]"));
+            D(fprintf(stderr, "%*c+ _tmp_172[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expressions ['as' star_target]"));
             _res = _PyPegen_dummy_name(p, expressions_var, _opt_var);
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_170[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_172[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "expressions ['as' star_target]"));
     }
     _res = NULL;
@@ -36292,9 +37398,9 @@ _tmp_170_rule(Parser *p)
     return _res;
 }
 
-// _tmp_171: ',' bitwise_or
+// _tmp_173: ',' bitwise_or
 static void *
-_tmp_171_rule(Parser *p)
+_tmp_173_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -36310,7 +37416,7 @@ _tmp_171_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_171[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' bitwise_or"));
+        D(fprintf(stderr, "%*c> _tmp_173[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' bitwise_or"));
         Token * _literal;
         expr_ty bitwise_or_var;
         if (
@@ -36319,12 +37425,12 @@ _tmp_171_rule(Parser *p)
             (bitwise_or_var = bitwise_or_rule(p))  // bitwise_or
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_171[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' bitwise_or"));
+            D(fprintf(stderr, "%*c+ _tmp_173[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' bitwise_or"));
             _res = _PyPegen_dummy_name(p, _literal, bitwise_or_var);
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_171[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_173[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' bitwise_or"));
     }
     _res = NULL;
@@ -36333,9 +37439,9 @@ _tmp_171_rule(Parser *p)
     return _res;
 }
 
-// _tmp_172: 'as' star_target
+// _tmp_174: 'as' star_target
 static void *
-_tmp_172_rule(Parser *p)
+_tmp_174_rule(Parser *p)
 {
     if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {
         _Pypegen_stack_overflow(p);
@@ -36351,21 +37457,21 @@ _tmp_172_rule(Parser *p)
             p->level--;
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_172[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'as' star_target"));
+        D(fprintf(stderr, "%*c> _tmp_174[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'as' star_target"));
         Token * _keyword;
         expr_ty star_target_var;
         if (
-            (_keyword = _PyPegen_expect_token(p, 675))  // token='as'
+            (_keyword = _PyPegen_expect_token(p, 676))  // token='as'
             &&
             (star_target_var = star_target_rule(p))  // star_target
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_172[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'as' star_target"));
+            D(fprintf(stderr, "%*c+ _tmp_174[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'as' star_target"));
             _res = _PyPegen_dummy_name(p, _keyword, star_target_var);
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_172[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_174[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'as' star_target"));
     }
     _res = NULL;
index e219aa0359e5789bbeb31754a2fc86709d092c64..1862fd7407ec3ca255143de8edc6daea2e6f6b38 100644 (file)
@@ -5,6 +5,8 @@
 #include <pycore_ast.h>
 #include <pycore_token.h>
 
+#include "lexer/state.h"
+
 #if 0
 #define PyPARSE_YIELD_IS_KEYWORD        0x0001
 #endif
@@ -24,6 +26,9 @@
 
 #define CURRENT_POS (-5)
 
+#define TOK_GET_MODE(tok) (&(tok->tok_mode_stack[tok->tok_mode_stack_index]))
+#define TOK_GET_STRING_PREFIX(tok) (TOK_GET_MODE(tok)->string_kind == TSTRING ? 't' : 'f')
+
 typedef struct _memo {
     int type;
     void *node;
@@ -327,6 +332,10 @@ StarEtc *_PyPegen_star_etc(Parser *, arg_ty, asdl_seq *, arg_ty);
 arguments_ty _PyPegen_make_arguments(Parser *, asdl_arg_seq *, SlashWithDefault *,
                                      asdl_arg_seq *, asdl_seq *, StarEtc *);
 arguments_ty _PyPegen_empty_arguments(Parser *);
+expr_ty _PyPegen_template_str(Parser *p, Token *a, asdl_expr_seq *raw_expressions, Token *b);
+expr_ty _PyPegen_joined_str(Parser *p, Token *a, asdl_expr_seq *raw_expressions, Token *b);
+expr_ty _PyPegen_interpolation(Parser *, expr_ty, Token *, ResultTokenWithMetadata *, ResultTokenWithMetadata *, Token *,
+                                 int, int, int, int, PyArena *);
 expr_ty _PyPegen_formatted_value(Parser *, expr_ty, Token *, ResultTokenWithMetadata *, ResultTokenWithMetadata *, Token *,
                                  int, int, int, int, PyArena *);
 AugOperator *_PyPegen_augoperator(Parser*, operator_ty type);
@@ -371,9 +380,6 @@ void *_PyPegen_run_parser(Parser *);
 mod_ty _PyPegen_run_parser_from_string(const char *, int, PyObject *, PyCompilerFlags *, PyArena *);
 asdl_stmt_seq *_PyPegen_interactive_exit(Parser *);
 
-// TODO: move to the correct place in this file
-expr_ty _PyPegen_joined_str(Parser *p, Token* a, asdl_expr_seq* expr, Token*b);
-
 // Generated function in parse.c - function definition in python.gram
 void *_PyPegen_parse(Parser *);
 
index 9b557e94d004312169aca81e4ad59ec888e535e7..d3631b114c5a3ca00eb753dfe6a6ae97a5be02df 100644 (file)
@@ -19,7 +19,8 @@ warn_invalid_escape_sequence(Parser *p, const char* buffer, const char *first_in
         return 0;
     }
     unsigned char c = (unsigned char)*first_invalid_escape;
-    if ((t->type == FSTRING_MIDDLE || t->type == FSTRING_END) && (c == '{' || c == '}')) {
+    if ((t->type == FSTRING_MIDDLE || t->type == FSTRING_END || t->type == TSTRING_MIDDLE || t->type == TSTRING_END)
+            && (c == '{' || c == '}')) {
         // in this case the tokenizer has already emitted a warning,
         // see Parser/tokenizer/helpers.c:warn_invalid_escape_sequence
         return 0;
index 4f163f21609a0a0dc25e82947f35c4bf31f020f5..a6e7ce35dfad855762b0390c8422f4b5576a51ef 100644 (file)
@@ -68,6 +68,9 @@ const char * const _PyParser_TokenNames[] = {
     "FSTRING_START",
     "FSTRING_MIDDLE",
     "FSTRING_END",
+    "TSTRING_START",
+    "TSTRING_MIDDLE",
+    "TSTRING_END",
     "COMMENT",
     "NL",
     "<ERRORTOKEN>",
index 15fc9348f8ae562bd33b95b8a9f8495feddf4708..01e473f58a07779c4770fb4b61426943a8f4086f 100644 (file)
@@ -275,7 +275,7 @@ tok_underflow_interactive(struct tok_state *tok) {
         return 0;
     }
 
-    if (tok->tok_mode_stack_index && !_PyLexer_update_fstring_expr(tok, 0)) {
+    if (tok->tok_mode_stack_index && !_PyLexer_update_ftstring_expr(tok, 0)) {
         return 0;
     }
     return 1;
@@ -322,7 +322,7 @@ tok_underflow_file(struct tok_state *tok) {
         tok->implicit_newline = 1;
     }
 
-    if (tok->tok_mode_stack_index && !_PyLexer_update_fstring_expr(tok, 0)) {
+    if (tok->tok_mode_stack_index && !_PyLexer_update_ftstring_expr(tok, 0)) {
         return 0;
     }
 
index a2637af9902dd3bcc6311b32aed9e457001f4844..22f84c77a12b47fe9f68f8b69a1649afa1a2a938 100644 (file)
@@ -90,7 +90,7 @@ tok_underflow_readline(struct tok_state* tok) {
         tok->implicit_newline = 1;
     }
 
-    if (tok->tok_mode_stack_index && !_PyLexer_update_fstring_expr(tok, 0)) {
+    if (tok->tok_mode_stack_index && !_PyLexer_update_ftstring_expr(tok, 0)) {
         return 0;
     }
 
index 8cedee31e08a00c48fe89858ee088e9983941812..b7d23f57018525494c0157012ce4a0ad01008757 100644 (file)
@@ -1,19 +1,19 @@
 // Auto-generated by Programs/freeze_test_frozenmain.py
 unsigned char M_test_frozenmain[] = {
     227,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0,
-    0,0,0,0,0,243,184,0,0,0,128,0,92,0,80,1,
-    71,0,114,0,92,0,80,1,71,1,114,1,91,2,32,0,
-    80,2,50,1,0,0,0,0,0,0,30,0,91,2,32,0,
-    80,3,91,0,78,6,0,0,0,0,0,0,0,0,0,0,
-    0,0,0,0,0,0,0,0,50,2,0,0,0,0,0,0,
-    30,0,91,1,78,8,0,0,0,0,0,0,0,0,0,0,
-    0,0,0,0,0,0,0,0,32,0,50,0,0,0,0,0,
-    0,0,80,4,43,26,0,0,0,0,0,0,0,0,0,0,
-    114,5,80,7,15,0,68,24,0,0,114,6,91,2,32,0,
-    80,5,91,6,11,0,80,6,91,5,91,6,43,26,0,0,
-    0,0,0,0,0,0,0,0,11,0,48,4,50,1,0,0,
-    0,0,0,0,30,0,73,26,0,0,8,0,29,0,80,1,
-    34,0,41,8,233,0,0,0,0,78,122,18,70,114,111,122,
+    0,0,0,0,0,243,184,0,0,0,128,0,94,0,82,1,
+    73,0,116,0,94,0,82,1,73,1,116,1,93,2,33,0,
+    82,2,52,1,0,0,0,0,0,0,31,0,93,2,33,0,
+    82,3,93,0,80,6,0,0,0,0,0,0,0,0,0,0,
+    0,0,0,0,0,0,0,0,52,2,0,0,0,0,0,0,
+    31,0,93,1,80,8,0,0,0,0,0,0,0,0,0,0,
+    0,0,0,0,0,0,0,0,33,0,52,0,0,0,0,0,
+    0,0,82,4,44,26,0,0,0,0,0,0,0,0,0,0,
+    116,5,82,7,16,0,70,24,0,0,116,6,93,2,33,0,
+    82,5,93,6,12,0,82,6,93,5,93,6,44,26,0,0,
+    0,0,0,0,0,0,0,0,12,0,50,4,52,1,0,0,
+    0,0,0,0,31,0,75,26,0,0,9,0,30,0,82,1,
+    35,0,41,8,233,0,0,0,0,78,122,18,70,114,111,122,
     101,110,32,72,101,108,108,111,32,87,111,114,108,100,122,8,
     115,121,115,46,97,114,103,118,218,6,99,111,110,102,105,103,
     122,7,99,111,110,102,105,103,32,122,2,58,32,41,5,218,
index 98dcdc4ed0be7062b79e6207094d5ad34b5869ad..94d9a76d28306e5991757376b66c7678ee4a62cb 100644 (file)
@@ -92,6 +92,7 @@ void _PyAST_Fini(PyInterpreterState *interp)
     Py_CLEAR(state->In_singleton);
     Py_CLEAR(state->In_type);
     Py_CLEAR(state->Interactive_type);
+    Py_CLEAR(state->Interpolation_type);
     Py_CLEAR(state->Invert_singleton);
     Py_CLEAR(state->Invert_type);
     Py_CLEAR(state->IsNot_singleton);
@@ -154,6 +155,7 @@ void _PyAST_Fini(PyInterpreterState *interp)
     Py_CLEAR(state->Sub_singleton);
     Py_CLEAR(state->Sub_type);
     Py_CLEAR(state->Subscript_type);
+    Py_CLEAR(state->TemplateStr_type);
     Py_CLEAR(state->TryStar_type);
     Py_CLEAR(state->Try_type);
     Py_CLEAR(state->Tuple_type);
@@ -259,6 +261,7 @@ void _PyAST_Fini(PyInterpreterState *interp)
     Py_CLEAR(state->slice);
     Py_CLEAR(state->step);
     Py_CLEAR(state->stmt_type);
+    Py_CLEAR(state->str);
     Py_CLEAR(state->subject);
     Py_CLEAR(state->tag);
     Py_CLEAR(state->target);
@@ -357,6 +360,7 @@ static int init_identifiers(struct ast_state *state)
     if ((state->simple = PyUnicode_InternFromString("simple")) == NULL) return -1;
     if ((state->slice = PyUnicode_InternFromString("slice")) == NULL) return -1;
     if ((state->step = PyUnicode_InternFromString("step")) == NULL) return -1;
+    if ((state->str = PyUnicode_InternFromString("str")) == NULL) return -1;
     if ((state->subject = PyUnicode_InternFromString("subject")) == NULL) return -1;
     if ((state->tag = PyUnicode_InternFromString("tag")) == NULL) return -1;
     if ((state->target = PyUnicode_InternFromString("target")) == NULL) return -1;
@@ -619,9 +623,18 @@ static const char * const FormattedValue_fields[]={
     "conversion",
     "format_spec",
 };
+static const char * const Interpolation_fields[]={
+    "value",
+    "str",
+    "conversion",
+    "format_spec",
+};
 static const char * const JoinedStr_fields[]={
     "values",
 };
+static const char * const TemplateStr_fields[]={
+    "values",
+};
 static const char * const Constant_fields[]={
     "value",
     "kind",
@@ -3174,6 +3187,70 @@ add_ast_annotations(struct ast_state *state)
         return 0;
     }
     Py_DECREF(FormattedValue_annotations);
+    PyObject *Interpolation_annotations = PyDict_New();
+    if (!Interpolation_annotations) return 0;
+    {
+        PyObject *type = state->expr_type;
+        Py_INCREF(type);
+        cond = PyDict_SetItemString(Interpolation_annotations, "value", type)
+                                    == 0;
+        Py_DECREF(type);
+        if (!cond) {
+            Py_DECREF(Interpolation_annotations);
+            return 0;
+        }
+    }
+    {
+        PyObject *type = (PyObject *)&PyBaseObject_Type;
+        Py_INCREF(type);
+        cond = PyDict_SetItemString(Interpolation_annotations, "str", type) ==
+                                    0;
+        Py_DECREF(type);
+        if (!cond) {
+            Py_DECREF(Interpolation_annotations);
+            return 0;
+        }
+    }
+    {
+        PyObject *type = (PyObject *)&PyLong_Type;
+        Py_INCREF(type);
+        cond = PyDict_SetItemString(Interpolation_annotations, "conversion",
+                                    type) == 0;
+        Py_DECREF(type);
+        if (!cond) {
+            Py_DECREF(Interpolation_annotations);
+            return 0;
+        }
+    }
+    {
+        PyObject *type = state->expr_type;
+        type = _Py_union_type_or(type, Py_None);
+        cond = type != NULL;
+        if (!cond) {
+            Py_DECREF(Interpolation_annotations);
+            return 0;
+        }
+        cond = PyDict_SetItemString(Interpolation_annotations, "format_spec",
+                                    type) == 0;
+        Py_DECREF(type);
+        if (!cond) {
+            Py_DECREF(Interpolation_annotations);
+            return 0;
+        }
+    }
+    cond = PyObject_SetAttrString(state->Interpolation_type, "_field_types",
+                                  Interpolation_annotations) == 0;
+    if (!cond) {
+        Py_DECREF(Interpolation_annotations);
+        return 0;
+    }
+    cond = PyObject_SetAttrString(state->Interpolation_type, "__annotations__",
+                                  Interpolation_annotations) == 0;
+    if (!cond) {
+        Py_DECREF(Interpolation_annotations);
+        return 0;
+    }
+    Py_DECREF(Interpolation_annotations);
     PyObject *JoinedStr_annotations = PyDict_New();
     if (!JoinedStr_annotations) return 0;
     {
@@ -3204,6 +3281,37 @@ add_ast_annotations(struct ast_state *state)
         return 0;
     }
     Py_DECREF(JoinedStr_annotations);
+    PyObject *TemplateStr_annotations = PyDict_New();
+    if (!TemplateStr_annotations) return 0;
+    {
+        PyObject *type = state->expr_type;
+        type = Py_GenericAlias((PyObject *)&PyList_Type, type);
+        cond = type != NULL;
+        if (!cond) {
+            Py_DECREF(TemplateStr_annotations);
+            return 0;
+        }
+        cond = PyDict_SetItemString(TemplateStr_annotations, "values", type) ==
+                                    0;
+        Py_DECREF(type);
+        if (!cond) {
+            Py_DECREF(TemplateStr_annotations);
+            return 0;
+        }
+    }
+    cond = PyObject_SetAttrString(state->TemplateStr_type, "_field_types",
+                                  TemplateStr_annotations) == 0;
+    if (!cond) {
+        Py_DECREF(TemplateStr_annotations);
+        return 0;
+    }
+    cond = PyObject_SetAttrString(state->TemplateStr_type, "__annotations__",
+                                  TemplateStr_annotations) == 0;
+    if (!cond) {
+        Py_DECREF(TemplateStr_annotations);
+        return 0;
+    }
+    Py_DECREF(TemplateStr_annotations);
     PyObject *Constant_annotations = PyDict_New();
     if (!Constant_annotations) return 0;
     {
@@ -6266,7 +6374,9 @@ init_types(void *arg)
         "     | Compare(expr left, cmpop* ops, expr* comparators)\n"
         "     | Call(expr func, expr* args, keyword* keywords)\n"
         "     | FormattedValue(expr value, int conversion, expr? format_spec)\n"
+        "     | Interpolation(expr value, constant str, int conversion, expr? format_spec)\n"
         "     | JoinedStr(expr* values)\n"
+        "     | TemplateStr(expr* values)\n"
         "     | Constant(constant value, string? kind)\n"
         "     | Attribute(expr value, identifier attr, expr_context ctx)\n"
         "     | Subscript(expr value, expr slice, expr_context ctx)\n"
@@ -6361,10 +6471,22 @@ init_types(void *arg)
     if (PyObject_SetAttr(state->FormattedValue_type, state->format_spec,
         Py_None) == -1)
         return -1;
+    state->Interpolation_type = make_type(state, "Interpolation",
+                                          state->expr_type,
+                                          Interpolation_fields, 4,
+        "Interpolation(expr value, constant str, int conversion, expr? format_spec)");
+    if (!state->Interpolation_type) return -1;
+    if (PyObject_SetAttr(state->Interpolation_type, state->format_spec,
+        Py_None) == -1)
+        return -1;
     state->JoinedStr_type = make_type(state, "JoinedStr", state->expr_type,
                                       JoinedStr_fields, 1,
         "JoinedStr(expr* values)");
     if (!state->JoinedStr_type) return -1;
+    state->TemplateStr_type = make_type(state, "TemplateStr", state->expr_type,
+                                        TemplateStr_fields, 1,
+        "TemplateStr(expr* values)");
+    if (!state->TemplateStr_type) return -1;
     state->Constant_type = make_type(state, "Constant", state->expr_type,
                                      Constant_fields, 2,
         "Constant(constant value, string? kind)");
@@ -8038,6 +8160,37 @@ _PyAST_FormattedValue(expr_ty value, int conversion, expr_ty format_spec, int
     return p;
 }
 
+expr_ty
+_PyAST_Interpolation(expr_ty value, constant str, int conversion, expr_ty
+                     format_spec, int lineno, int col_offset, int end_lineno,
+                     int end_col_offset, PyArena *arena)
+{
+    expr_ty p;
+    if (!value) {
+        PyErr_SetString(PyExc_ValueError,
+                        "field 'value' is required for Interpolation");
+        return NULL;
+    }
+    if (!str) {
+        PyErr_SetString(PyExc_ValueError,
+                        "field 'str' is required for Interpolation");
+        return NULL;
+    }
+    p = (expr_ty)_PyArena_Malloc(arena, sizeof(*p));
+    if (!p)
+        return NULL;
+    p->kind = Interpolation_kind;
+    p->v.Interpolation.value = value;
+    p->v.Interpolation.str = str;
+    p->v.Interpolation.conversion = conversion;
+    p->v.Interpolation.format_spec = format_spec;
+    p->lineno = lineno;
+    p->col_offset = col_offset;
+    p->end_lineno = end_lineno;
+    p->end_col_offset = end_col_offset;
+    return p;
+}
+
 expr_ty
 _PyAST_JoinedStr(asdl_expr_seq * values, int lineno, int col_offset, int
                  end_lineno, int end_col_offset, PyArena *arena)
@@ -8055,6 +8208,23 @@ _PyAST_JoinedStr(asdl_expr_seq * values, int lineno, int col_offset, int
     return p;
 }
 
+expr_ty
+_PyAST_TemplateStr(asdl_expr_seq * values, int lineno, int col_offset, int
+                   end_lineno, int end_col_offset, PyArena *arena)
+{
+    expr_ty p;
+    p = (expr_ty)_PyArena_Malloc(arena, sizeof(*p));
+    if (!p)
+        return NULL;
+    p->kind = TemplateStr_kind;
+    p->v.TemplateStr.values = values;
+    p->lineno = lineno;
+    p->col_offset = col_offset;
+    p->end_lineno = end_lineno;
+    p->end_col_offset = end_col_offset;
+    return p;
+}
+
 expr_ty
 _PyAST_Constant(constant value, string kind, int lineno, int col_offset, int
                 end_lineno, int end_col_offset, PyArena *arena)
@@ -9674,6 +9844,31 @@ ast2obj_expr(struct ast_state *state, void* _o)
             goto failed;
         Py_DECREF(value);
         break;
+    case Interpolation_kind:
+        tp = (PyTypeObject *)state->Interpolation_type;
+        result = PyType_GenericNew(tp, NULL, NULL);
+        if (!result) goto failed;
+        value = ast2obj_expr(state, o->v.Interpolation.value);
+        if (!value) goto failed;
+        if (PyObject_SetAttr(result, state->value, value) == -1)
+            goto failed;
+        Py_DECREF(value);
+        value = ast2obj_constant(state, o->v.Interpolation.str);
+        if (!value) goto failed;
+        if (PyObject_SetAttr(result, state->str, value) == -1)
+            goto failed;
+        Py_DECREF(value);
+        value = ast2obj_int(state, o->v.Interpolation.conversion);
+        if (!value) goto failed;
+        if (PyObject_SetAttr(result, state->conversion, value) == -1)
+            goto failed;
+        Py_DECREF(value);
+        value = ast2obj_expr(state, o->v.Interpolation.format_spec);
+        if (!value) goto failed;
+        if (PyObject_SetAttr(result, state->format_spec, value) == -1)
+            goto failed;
+        Py_DECREF(value);
+        break;
     case JoinedStr_kind:
         tp = (PyTypeObject *)state->JoinedStr_type;
         result = PyType_GenericNew(tp, NULL, NULL);
@@ -9685,6 +9880,17 @@ ast2obj_expr(struct ast_state *state, void* _o)
             goto failed;
         Py_DECREF(value);
         break;
+    case TemplateStr_kind:
+        tp = (PyTypeObject *)state->TemplateStr_type;
+        result = PyType_GenericNew(tp, NULL, NULL);
+        if (!result) goto failed;
+        value = ast2obj_list(state, (asdl_seq*)o->v.TemplateStr.values,
+                             ast2obj_expr);
+        if (!value) goto failed;
+        if (PyObject_SetAttr(result, state->values, value) == -1)
+            goto failed;
+        Py_DECREF(value);
+        break;
     case Constant_kind:
         tp = (PyTypeObject *)state->Constant_type;
         result = PyType_GenericNew(tp, NULL, NULL);
@@ -14793,6 +14999,91 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena*
         if (*out == NULL) goto failed;
         return 0;
     }
+    tp = state->Interpolation_type;
+    isinstance = PyObject_IsInstance(obj, tp);
+    if (isinstance == -1) {
+        return -1;
+    }
+    if (isinstance) {
+        expr_ty value;
+        constant str;
+        int conversion;
+        expr_ty format_spec;
+
+        if (PyObject_GetOptionalAttr(obj, state->value, &tmp) < 0) {
+            return -1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"value\" missing from Interpolation");
+            return -1;
+        }
+        else {
+            int res;
+            if (_Py_EnterRecursiveCall(" while traversing 'Interpolation' node")) {
+                goto failed;
+            }
+            res = obj2ast_expr(state, tmp, &value, arena);
+            _Py_LeaveRecursiveCall();
+            if (res != 0) goto failed;
+            Py_CLEAR(tmp);
+        }
+        if (PyObject_GetOptionalAttr(obj, state->str, &tmp) < 0) {
+            return -1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"str\" missing from Interpolation");
+            return -1;
+        }
+        else {
+            int res;
+            if (_Py_EnterRecursiveCall(" while traversing 'Interpolation' node")) {
+                goto failed;
+            }
+            res = obj2ast_constant(state, tmp, &str, arena);
+            _Py_LeaveRecursiveCall();
+            if (res != 0) goto failed;
+            Py_CLEAR(tmp);
+        }
+        if (PyObject_GetOptionalAttr(obj, state->conversion, &tmp) < 0) {
+            return -1;
+        }
+        if (tmp == NULL) {
+            PyErr_SetString(PyExc_TypeError, "required field \"conversion\" missing from Interpolation");
+            return -1;
+        }
+        else {
+            int res;
+            if (_Py_EnterRecursiveCall(" while traversing 'Interpolation' node")) {
+                goto failed;
+            }
+            res = obj2ast_int(state, tmp, &conversion, arena);
+            _Py_LeaveRecursiveCall();
+            if (res != 0) goto failed;
+            Py_CLEAR(tmp);
+        }
+        if (PyObject_GetOptionalAttr(obj, state->format_spec, &tmp) < 0) {
+            return -1;
+        }
+        if (tmp == NULL || tmp == Py_None) {
+            Py_CLEAR(tmp);
+            format_spec = NULL;
+        }
+        else {
+            int res;
+            if (_Py_EnterRecursiveCall(" while traversing 'Interpolation' node")) {
+                goto failed;
+            }
+            res = obj2ast_expr(state, tmp, &format_spec, arena);
+            _Py_LeaveRecursiveCall();
+            if (res != 0) goto failed;
+            Py_CLEAR(tmp);
+        }
+        *out = _PyAST_Interpolation(value, str, conversion, format_spec,
+                                    lineno, col_offset, end_lineno,
+                                    end_col_offset, arena);
+        if (*out == NULL) goto failed;
+        return 0;
+    }
     tp = state->JoinedStr_type;
     isinstance = PyObject_IsInstance(obj, tp);
     if (isinstance == -1) {
@@ -14844,6 +15135,57 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena*
         if (*out == NULL) goto failed;
         return 0;
     }
+    tp = state->TemplateStr_type;
+    isinstance = PyObject_IsInstance(obj, tp);
+    if (isinstance == -1) {
+        return -1;
+    }
+    if (isinstance) {
+        asdl_expr_seq* values;
+
+        if (PyObject_GetOptionalAttr(obj, state->values, &tmp) < 0) {
+            return -1;
+        }
+        if (tmp == NULL) {
+            tmp = PyList_New(0);
+            if (tmp == NULL) {
+                return -1;
+            }
+        }
+        {
+            int res;
+            Py_ssize_t len;
+            Py_ssize_t i;
+            if (!PyList_Check(tmp)) {
+                PyErr_Format(PyExc_TypeError, "TemplateStr field \"values\" must be a list, not a %.200s", _PyType_Name(Py_TYPE(tmp)));
+                goto failed;
+            }
+            len = PyList_GET_SIZE(tmp);
+            values = _Py_asdl_expr_seq_new(len, arena);
+            if (values == NULL) goto failed;
+            for (i = 0; i < len; i++) {
+                expr_ty val;
+                PyObject *tmp2 = Py_NewRef(PyList_GET_ITEM(tmp, i));
+                if (_Py_EnterRecursiveCall(" while traversing 'TemplateStr' node")) {
+                    goto failed;
+                }
+                res = obj2ast_expr(state, tmp2, &val, arena);
+                _Py_LeaveRecursiveCall();
+                Py_DECREF(tmp2);
+                if (res != 0) goto failed;
+                if (len != PyList_GET_SIZE(tmp)) {
+                    PyErr_SetString(PyExc_RuntimeError, "TemplateStr field \"values\" changed size during iteration");
+                    goto failed;
+                }
+                asdl_seq_SET(values, i, val);
+            }
+            Py_CLEAR(tmp);
+        }
+        *out = _PyAST_TemplateStr(values, lineno, col_offset, end_lineno,
+                                  end_col_offset, arena);
+        if (*out == NULL) goto failed;
+        return 0;
+    }
     tp = state->Constant_type;
     isinstance = PyObject_IsInstance(obj, tp);
     if (isinstance == -1) {
@@ -17794,9 +18136,16 @@ astmodule_exec(PyObject *m)
         < 0) {
         return -1;
     }
+    if (PyModule_AddObjectRef(m, "Interpolation", state->Interpolation_type) <
+        0) {
+        return -1;
+    }
     if (PyModule_AddObjectRef(m, "JoinedStr", state->JoinedStr_type) < 0) {
         return -1;
     }
+    if (PyModule_AddObjectRef(m, "TemplateStr", state->TemplateStr_type) < 0) {
+        return -1;
+    }
     if (PyModule_AddObjectRef(m, "Constant", state->Constant_type) < 0) {
         return -1;
     }
index 7ee884357b41e84a56f60f8adeeb39dcd07119cd..e01dd0de51e5965a14ebc87f769361d7400242b1 100644 (file)
@@ -345,6 +345,9 @@ validate_expr(expr_ty exp, expr_context_ty ctx)
     case JoinedStr_kind:
         ret = validate_exprs(exp->v.JoinedStr.values, Load, 0);
         break;
+    case TemplateStr_kind:
+        ret = validate_exprs(exp->v.TemplateStr.values, Load, 0);
+        break;
     case FormattedValue_kind:
         if (validate_expr(exp->v.FormattedValue.value, Load) == 0)
             return 0;
@@ -354,6 +357,15 @@ validate_expr(expr_ty exp, expr_context_ty ctx)
         }
         ret = 1;
         break;
+    case Interpolation_kind:
+        if (validate_expr(exp->v.Interpolation.value, Load) == 0)
+            return 0;
+        if (exp->v.Interpolation.format_spec) {
+            ret = validate_expr(exp->v.Interpolation.format_spec, Load);
+            break;
+        }
+        ret = 1;
+        break;
     case Attribute_kind:
         ret = validate_expr(exp->v.Attribute.value, Load);
         break;
@@ -512,6 +524,7 @@ validate_pattern_match_value(expr_ty exp)
             }
             break;
         case JoinedStr_kind:
+        case TemplateStr_kind:
             // Handled in the later stages
             return 1;
         default:
index 807de98ab5e42b6366c4e0b8f55c38118c10be64..4d5e5589ac06cadf116a5ffcd2440c931860419c 100644 (file)
@@ -558,9 +558,16 @@ astfold_expr(expr_ty node_, PyArena *ctx_, _PyASTOptimizeState *state)
         CALL(astfold_expr, expr_ty, node_->v.FormattedValue.value);
         CALL_OPT(astfold_expr, expr_ty, node_->v.FormattedValue.format_spec);
         break;
+    case Interpolation_kind:
+        CALL(astfold_expr, expr_ty, node_->v.Interpolation.value);
+        CALL_OPT(astfold_expr, expr_ty, node_->v.Interpolation.format_spec);
+        break;
     case JoinedStr_kind:
         CALL_SEQ(astfold_expr, expr, node_->v.JoinedStr.values);
         break;
+    case TemplateStr_kind:
+        CALL_SEQ(astfold_expr, expr, node_->v.TemplateStr.values);
+        break;
     case Attribute_kind:
         CALL(astfold_expr, expr_ty, node_->v.Attribute.value);
         break;
index f3c669c33eb07ccdc68c458807a2a56bc6190d93..c121ec096aebf45d920112a9e7405eb390839a3e 100644 (file)
@@ -18,8 +18,12 @@ expr_as_unicode(expr_ty e, int level);
 static int
 append_ast_expr(PyUnicodeWriter *writer, expr_ty e, int level);
 static int
+append_templatestr(PyUnicodeWriter *writer, expr_ty e);
+static int
 append_joinedstr(PyUnicodeWriter *writer, expr_ty e, bool is_format_spec);
 static int
+append_interpolation(PyUnicodeWriter *writer, expr_ty e);
+static int
 append_formattedvalue(PyUnicodeWriter *writer, expr_ty e);
 static int
 append_ast_slice(PyUnicodeWriter *writer, expr_ty e);
@@ -621,11 +625,15 @@ append_fstring_element(PyUnicodeWriter *writer, expr_ty e, bool is_format_spec)
         return append_fstring_unicode(writer, e->v.Constant.value);
     case JoinedStr_kind:
         return append_joinedstr(writer, e, is_format_spec);
+    case TemplateStr_kind:
+        return append_templatestr(writer, e);
     case FormattedValue_kind:
         return append_formattedvalue(writer, e);
+    case Interpolation_kind:
+        return append_interpolation(writer, e);
     default:
         PyErr_SetString(PyExc_SystemError,
-                        "unknown expression kind inside f-string");
+                        "unknown expression kind inside f-string or t-string");
         return -1;
     }
 }
@@ -633,7 +641,7 @@ append_fstring_element(PyUnicodeWriter *writer, expr_ty e, bool is_format_spec)
 /* Build body separately to enable wrapping the entire stream of Strs,
    Constants and FormattedValues in one opening and one closing quote. */
 static PyObject *
-build_fstring_body(asdl_expr_seq *values, bool is_format_spec)
+build_ftstring_body(asdl_expr_seq *values, bool is_format_spec)
 {
     PyUnicodeWriter *body_writer = PyUnicodeWriter_Create(256);
     if (body_writer == NULL) {
@@ -654,11 +662,99 @@ build_fstring_body(asdl_expr_seq *values, bool is_format_spec)
     return PyUnicodeWriter_Finish(body_writer);
 }
 
+static int
+_write_values_subarray(PyUnicodeWriter *writer, asdl_expr_seq *values, Py_ssize_t first_idx,
+                       Py_ssize_t last_idx, char prefix, PyArena *arena)
+{
+    int result = -1;
+
+    asdl_expr_seq *new_values = _Py_asdl_expr_seq_new(last_idx - first_idx + 1, arena);
+    if (!new_values) {
+        return result;
+    }
+
+    Py_ssize_t j = 0;
+    for (Py_ssize_t i = first_idx; i <= last_idx; ++i) {
+        asdl_seq_SET(new_values, j++, asdl_seq_GET(values, i));
+    }
+
+    PyObject *body = build_ftstring_body(new_values, false);
+    if (!body) {
+        return result;
+    }
+
+    if (-1 != append_char(writer, prefix) &&
+        -1 != append_repr(writer, body))
+    {
+        result = 0;
+    }
+    Py_DECREF(body);
+    return result;
+}
+
+static int
+append_templatestr(PyUnicodeWriter *writer, expr_ty e)
+{
+    PyArena *arena = _PyArena_New();
+    if (!arena) {
+        return -1;
+    }
+
+    Py_ssize_t last_idx = 0;
+    Py_ssize_t len = asdl_seq_LEN(e->v.TemplateStr.values);
+    for (Py_ssize_t i = 0; i < len; i++) {
+        expr_ty value = asdl_seq_GET(e->v.TemplateStr.values, i);
+
+        // Handle implicit concat of t-strings with f-strings
+        if (value->kind == FormattedValue_kind) {
+            if (i > last_idx) {
+                // Create a new TemplateStr with the values between last_idx and i
+                // and append it to the writer.
+                if (_write_values_subarray(writer, e->v.TemplateStr.values,
+                        last_idx, i - 1, 't', arena) == -1) {
+                    goto error;
+                }
+
+                if (append_charp(writer, " ") == -1) {
+                    goto error;
+                }
+            }
+
+            // Append the FormattedValue to the writer.
+            if (_write_values_subarray(writer, e->v.TemplateStr.values,
+                    i, i, 'f', arena) == -1) {
+                goto error;
+            }
+
+            if (i + 1 < len) {
+                if (append_charp(writer, " ") == -1) {
+                    goto error;
+                }
+            }
+
+            last_idx = i + 1;
+        }
+    }
+
+    if (last_idx < len) {
+        if (_write_values_subarray(writer, e->v.TemplateStr.values,
+                last_idx, len - 1, 't', arena) == -1) {
+            goto error;
+        }
+    }
+
+    return 0;
+
+error:
+    _PyArena_Free(arena);
+    return -1;
+}
+
 static int
 append_joinedstr(PyUnicodeWriter *writer, expr_ty e, bool is_format_spec)
 {
     int result = -1;
-    PyObject *body = build_fstring_body(e->v.JoinedStr.values, is_format_spec);
+    PyObject *body = build_ftstring_body(e->v.JoinedStr.values, is_format_spec);
     if (!body) {
         return -1;
     }
@@ -678,13 +774,12 @@ append_joinedstr(PyUnicodeWriter *writer, expr_ty e, bool is_format_spec)
 }
 
 static int
-append_formattedvalue(PyUnicodeWriter *writer, expr_ty e)
+append_interpolation_value(PyUnicodeWriter *writer, expr_ty e)
 {
-    const char *conversion;
     const char *outer_brace = "{";
     /* Grammar allows PR_TUPLE, but use >PR_TEST for adding parenthesis
        around a lambda with ':' */
-    PyObject *temp_fv_str = expr_as_unicode(e->v.FormattedValue.value, PR_TEST + 1);
+    PyObject *temp_fv_str = expr_as_unicode(e, PR_TEST + 1);
     if (!temp_fv_str) {
         return -1;
     }
@@ -702,35 +797,81 @@ append_formattedvalue(PyUnicodeWriter *writer, expr_ty e)
         return -1;
     }
     Py_DECREF(temp_fv_str);
+    return 0;
+}
 
-    if (e->v.FormattedValue.conversion > 0) {
-        switch (e->v.FormattedValue.conversion) {
-        case 'a':
-            conversion = "!a";
-            break;
-        case 'r':
-            conversion = "!r";
-            break;
-        case 's':
-            conversion = "!s";
-            break;
-        default:
-            PyErr_SetString(PyExc_SystemError,
-                            "unknown f-value conversion kind");
-            return -1;
-        }
-        APPEND_STR(conversion);
+static int
+append_interpolation_conversion(PyUnicodeWriter *writer, int conversion)
+{
+    if (conversion < 0) {
+        return 0;
     }
-    if (e->v.FormattedValue.format_spec) {
+
+    const char *conversion_str;
+    switch (conversion) {
+    case 'a':
+        conversion_str = "!a";
+        break;
+    case 'r':
+        conversion_str = "!r";
+        break;
+    case 's':
+        conversion_str = "!s";
+        break;
+    default:
+        PyErr_SetString(PyExc_SystemError,
+                        "unknown f-value conversion kind");
+        return -1;
+    }
+    APPEND_STR(conversion_str);
+    return 0;
+}
+
+static int
+append_interpolation_format_spec(PyUnicodeWriter *writer, expr_ty e)
+{
+    if (e) {
         if (-1 == PyUnicodeWriter_WriteChar(writer, ':') ||
-            -1 == append_fstring_element(writer,
-                                         e->v.FormattedValue.format_spec,
-                                         true
-                                        ))
+            -1 == append_fstring_element(writer, e, true))
         {
             return -1;
         }
     }
+    return 0;
+}
+
+static int
+append_interpolation(PyUnicodeWriter *writer, expr_ty e)
+{
+    if (-1 == append_interpolation_value(writer, e->v.Interpolation.value)) {
+        return -1;
+    }
+
+    if (-1 == append_interpolation_conversion(writer, e->v.Interpolation.conversion)) {
+        return -1;
+    }
+
+    if (-1 == append_interpolation_format_spec(writer, e->v.Interpolation.format_spec)) {
+        return -1;
+    }
+
+    APPEND_STR_FINISH("}");
+}
+
+static int
+append_formattedvalue(PyUnicodeWriter *writer, expr_ty e)
+{
+    if (-1 == append_interpolation_value(writer, e->v.FormattedValue.value)) {
+        return -1;
+    }
+
+    if (-1 == append_interpolation_conversion(writer, e->v.FormattedValue.conversion)) {
+        return -1;
+    }
+
+    if (-1 == append_interpolation_format_spec(writer, e->v.FormattedValue.format_spec)) {
+        return -1;
+    }
 
     APPEND_CHAR_FINISH('}');
 }
@@ -901,8 +1042,12 @@ append_ast_expr(PyUnicodeWriter *writer, expr_ty e, int level)
         return append_ast_constant(writer, e->v.Constant.value);
     case JoinedStr_kind:
         return append_joinedstr(writer, e, false);
+    case TemplateStr_kind:
+        return append_templatestr(writer, e);
     case FormattedValue_kind:
         return append_formattedvalue(writer, e);
+    case Interpolation_kind:
+        return append_interpolation(writer, e);
     /* The following exprs can be assignment targets. */
     case Attribute_kind:
         return append_ast_attribute(writer, e);
index 7ae687f1bfaad3fe5cd5ae9c984f65f4699ec9e9..b30fa0899106de5044bad7d77f7c3e0ca3a99013 100644 (file)
@@ -16,6 +16,7 @@
 #include "pycore_emscripten_signal.h"  // _Py_CHECK_EMSCRIPTEN_SIGNALS
 #include "pycore_function.h"
 #include "pycore_instruments.h"
+#include "pycore_interpolation.h" // _PyInterpolation_Build()
 #include "pycore_intrinsics.h"
 #include "pycore_long.h"          // _PyLong_GetZero()
 #include "pycore_moduleobject.h"  // PyModuleObject
@@ -30,6 +31,7 @@
 #include "pycore_setobject.h"     // _PySet_NextEntry()
 #include "pycore_sliceobject.h"   // _PyBuildSlice_ConsumeRefs
 #include "pycore_stackref.h"
+#include "pycore_template.h"      // _PyTemplate_Build()
 #include "pycore_tuple.h"         // _PyTuple_ITEMS()
 #include "pycore_typeobject.h"    // _PySuper_Lookup()
 
@@ -1903,6 +1905,40 @@ dummy_func(
             str = PyStackRef_FromPyObjectSteal(str_o);
         }
 
+        inst(BUILD_INTERPOLATION, (value, str, format[oparg & 1] -- interpolation)) {
+            PyObject *value_o = PyStackRef_AsPyObjectBorrow(value);
+            PyObject *str_o = PyStackRef_AsPyObjectBorrow(str);
+            int conversion = oparg >> 2;
+            PyObject *format_o;
+            if (oparg & 1) {
+                format_o = PyStackRef_AsPyObjectBorrow(format[0]);
+            }
+            else {
+                format_o = &_Py_STR(empty);
+            }
+            PyObject *interpolation_o = _PyInterpolation_Build(value_o, str_o, conversion, format_o);
+            if (oparg & 1) {
+                PyStackRef_CLOSE(format[0]);
+            }
+            else {
+                DEAD(format);
+            }
+            PyStackRef_CLOSE(str);
+            PyStackRef_CLOSE(value);
+            ERROR_IF(interpolation_o == NULL);
+            interpolation = PyStackRef_FromPyObjectSteal(interpolation_o);
+        }
+
+        inst(BUILD_TEMPLATE, (strings, interpolations -- template)) {
+            PyObject *strings_o = PyStackRef_AsPyObjectBorrow(strings);
+            PyObject *interpolations_o = PyStackRef_AsPyObjectBorrow(interpolations);
+            PyObject *template_o = _PyTemplate_Build(strings_o, interpolations_o);
+            PyStackRef_CLOSE(interpolations);
+            PyStackRef_CLOSE(strings);
+            ERROR_IF(template_o == NULL);
+            template = PyStackRef_FromPyObjectSteal(template_o);
+        }
+
         inst(BUILD_TUPLE, (values[oparg] -- tup)) {
             PyObject *tup_o = _PyTuple_FromStackRefStealOnSuccess(values, oparg);
             if (tup_o == NULL) {
index 19a1c9529dd9aa856ac912f5cdf3ce71d17c1e52..c3a7a27f2917cae95f2336d880fad2f0f0904d13 100644 (file)
@@ -19,6 +19,7 @@
 #include "pycore_import.h"        // _PyImport_IsDefaultImportFunc()
 #include "pycore_instruments.h"
 #include "pycore_interpframe.h"   // _PyFrame_SetStackPointer()
+#include "pycore_interpolation.h" // _PyInterpolation_Build()
 #include "pycore_intrinsics.h"
 #include "pycore_jit.h"
 #include "pycore_list.h"          // _PyList_GetItemRef()
@@ -36,6 +37,7 @@
 #include "pycore_setobject.h"     // _PySet_Update()
 #include "pycore_sliceobject.h"   // _PyBuildSlice_ConsumeRefs
 #include "pycore_sysmodule.h"     // _PySys_GetOptionalAttrString()
+#include "pycore_template.h"      // _PyTemplate_Build()
 #include "pycore_traceback.h"     // _PyTraceBack_FromFrame
 #include "pycore_tuple.h"         // _PyTuple_ITEMS()
 #include "pycore_uop_ids.h"       // Uops
index a7e412f7032c4556d4fd09061fe94fdfb769eeab..c7662538408f5d589a44a90394e105ee4d97613e 100644 (file)
@@ -3607,7 +3607,9 @@ infer_type(expr_ty e)
     case Lambda_kind:
         return &PyFunction_Type;
     case JoinedStr_kind:
+    case TemplateStr_kind:
     case FormattedValue_kind:
+    case Interpolation_kind:
         return &PyUnicode_Type;
     case Constant_kind:
         return Py_TYPE(e->v.Constant.value);
@@ -3630,7 +3632,9 @@ check_caller(compiler *c, expr_ty e)
     case SetComp_kind:
     case GeneratorExp_kind:
     case JoinedStr_kind:
-    case FormattedValue_kind: {
+    case TemplateStr_kind:
+    case FormattedValue_kind:
+    case Interpolation_kind: {
         location loc = LOC(e);
         return _PyCompile_Warn(c, loc, "'%.200s' object is not callable; "
                                        "perhaps you missed a comma?",
@@ -3693,7 +3697,9 @@ check_index(compiler *c, expr_ty e, expr_ty s)
     case List_kind:
     case ListComp_kind:
     case JoinedStr_kind:
-    case FormattedValue_kind: {
+    case TemplateStr_kind:
+    case FormattedValue_kind:
+    case Interpolation_kind: {
         location loc = LOC(e);
         return _PyCompile_Warn(c, loc, "%.200s indices must be integers "
                                        "or slices, not %.200s; "
@@ -4043,6 +4049,59 @@ codegen_call(compiler *c, expr_ty e)
     return ret;
 }
 
+static int
+codegen_template_str(compiler *c, expr_ty e)
+{
+    location loc = LOC(e);
+    expr_ty value;
+
+    Py_ssize_t value_count = asdl_seq_LEN(e->v.TemplateStr.values);
+    int last_was_interpolation = 1;
+    Py_ssize_t stringslen = 0;
+    for (Py_ssize_t i = 0; i < value_count; i++) {
+        value = asdl_seq_GET(e->v.TemplateStr.values, i);
+        if (value->kind == Interpolation_kind) {
+            if (last_was_interpolation) {
+                ADDOP_LOAD_CONST(c, loc, Py_NewRef(&_Py_STR(empty)));
+                stringslen++;
+            }
+            last_was_interpolation = 1;
+        }
+        else {
+            VISIT(c, expr, value);
+            Py_ssize_t j;
+            for (j = i + 1; j < value_count; j++) {
+                value = asdl_seq_GET(e->v.TemplateStr.values, j);
+                if (value->kind == Interpolation_kind) {
+                    break;
+                }
+                VISIT(c, expr, value);
+                ADDOP_INPLACE(c, loc, Add);
+            }
+            i = j - 1;
+            stringslen++;
+            last_was_interpolation = 0;
+        }
+    }
+    if (last_was_interpolation) {
+        ADDOP_LOAD_CONST(c, loc, Py_NewRef(&_Py_STR(empty)));
+        stringslen++;
+    }
+    ADDOP_I(c, loc, BUILD_TUPLE, stringslen);
+
+    Py_ssize_t interpolationslen = 0;
+    for (Py_ssize_t i = 0; i < value_count; i++) {
+        value = asdl_seq_GET(e->v.TemplateStr.values, i);
+        if (value->kind == Interpolation_kind) {
+            VISIT(c, expr, value);
+            interpolationslen++;
+        }
+    }
+    ADDOP_I(c, loc, BUILD_TUPLE, interpolationslen);
+    ADDOP(c, loc, BUILD_TEMPLATE);
+    return SUCCESS;
+}
+
 static int
 codegen_joined_str(compiler *c, expr_ty e)
 {
@@ -4072,24 +4131,41 @@ codegen_joined_str(compiler *c, expr_ty e)
     return SUCCESS;
 }
 
-/* Used to implement f-strings. Format a single value. */
 static int
-codegen_formatted_value(compiler *c, expr_ty e)
+codegen_interpolation(compiler *c, expr_ty e)
 {
-    /* Our oparg encodes 2 pieces of information: the conversion
-       character, and whether or not a format_spec was provided.
+    location loc = LOC(e);
 
-       Convert the conversion char to 3 bits:
-           : 000  0x0  FVC_NONE   The default if nothing specified.
-       !s  : 001  0x1  FVC_STR
-       !r  : 010  0x2  FVC_REPR
-       !a  : 011  0x3  FVC_ASCII
+    VISIT(c, expr, e->v.Interpolation.value);
+    ADDOP_LOAD_CONST(c, loc, e->v.Interpolation.str);
 
-       next bit is whether or not we have a format spec:
-       yes : 100  0x4
-       no  : 000  0x0
-    */
+    int oparg = 2;
+    if (e->v.Interpolation.format_spec) {
+        oparg++;
+        VISIT(c, expr, e->v.Interpolation.format_spec);
+    }
+
+    int conversion = e->v.Interpolation.conversion;
+    if (conversion != -1) {
+        switch (conversion) {
+        case 's': oparg |= FVC_STR << 2;   break;
+        case 'r': oparg |= FVC_REPR << 2;  break;
+        case 'a': oparg |= FVC_ASCII << 2; break;
+        default:
+            PyErr_Format(PyExc_SystemError,
+                     "Unrecognized conversion character %d", conversion);
+            return ERROR;
+        }
+    }
+
+    ADDOP_I(c, loc, BUILD_INTERPOLATION, oparg);
+    return SUCCESS;
+}
 
+/* Used to implement f-strings. Format a single value. */
+static int
+codegen_formatted_value(compiler *c, expr_ty e)
+{
     int conversion = e->v.FormattedValue.conversion;
     int oparg;
 
@@ -5182,8 +5258,12 @@ codegen_visit_expr(compiler *c, expr_ty e)
         break;
     case JoinedStr_kind:
         return codegen_joined_str(c, e);
+    case TemplateStr_kind:
+        return codegen_template_str(c, e);
     case FormattedValue_kind:
         return codegen_formatted_value(c, e);
+    case Interpolation_kind:
+        return codegen_interpolation(c, e);
     /* The following exprs can be assignment targets. */
     case Attribute_kind:
         if (e->v.Attribute.ctx == Load) {
index 4e57906c84a1541f0a02713eae7e91db51355be6..b37a87542fb9c72a6e4c9a32310ab7c2caacacc3 100644 (file)
             break;
         }
 
+        case _BUILD_INTERPOLATION: {
+            _PyStackRef *format;
+            _PyStackRef str;
+            _PyStackRef value;
+            _PyStackRef interpolation;
+            oparg = CURRENT_OPARG();
+            format = &stack_pointer[-(oparg & 1)];
+            str = stack_pointer[-1 - (oparg & 1)];
+            value = stack_pointer[-2 - (oparg & 1)];
+            PyObject *value_o = PyStackRef_AsPyObjectBorrow(value);
+            PyObject *str_o = PyStackRef_AsPyObjectBorrow(str);
+            int conversion = oparg >> 2;
+            PyObject *format_o;
+            if (oparg & 1) {
+                format_o = PyStackRef_AsPyObjectBorrow(format[0]);
+            }
+            else {
+                format_o = &_Py_STR(empty);
+            }
+            _PyFrame_SetStackPointer(frame, stack_pointer);
+            PyObject *interpolation_o = _PyInterpolation_Build(value_o, str_o, conversion, format_o);
+            stack_pointer = _PyFrame_GetStackPointer(frame);
+            if (oparg & 1) {
+                stack_pointer += -(oparg & 1);
+                assert(WITHIN_STACK_BOUNDS());
+                _PyFrame_SetStackPointer(frame, stack_pointer);
+                PyStackRef_CLOSE(format[0]);
+                stack_pointer = _PyFrame_GetStackPointer(frame);
+            }
+            else {
+                stack_pointer += -(oparg & 1);
+            }
+            stack_pointer += -1;
+            assert(WITHIN_STACK_BOUNDS());
+            _PyFrame_SetStackPointer(frame, stack_pointer);
+            PyStackRef_CLOSE(str);
+            stack_pointer = _PyFrame_GetStackPointer(frame);
+            stack_pointer += -1;
+            assert(WITHIN_STACK_BOUNDS());
+            _PyFrame_SetStackPointer(frame, stack_pointer);
+            PyStackRef_CLOSE(value);
+            stack_pointer = _PyFrame_GetStackPointer(frame);
+            if (interpolation_o == NULL) {
+                JUMP_TO_ERROR();
+            }
+            interpolation = PyStackRef_FromPyObjectSteal(interpolation_o);
+            stack_pointer[0] = interpolation;
+            stack_pointer += 1;
+            assert(WITHIN_STACK_BOUNDS());
+            break;
+        }
+
+        case _BUILD_TEMPLATE: {
+            _PyStackRef interpolations;
+            _PyStackRef strings;
+            _PyStackRef template;
+            interpolations = stack_pointer[-1];
+            strings = stack_pointer[-2];
+            PyObject *strings_o = PyStackRef_AsPyObjectBorrow(strings);
+            PyObject *interpolations_o = PyStackRef_AsPyObjectBorrow(interpolations);
+            _PyFrame_SetStackPointer(frame, stack_pointer);
+            PyObject *template_o = _PyTemplate_Build(strings_o, interpolations_o);
+            stack_pointer = _PyFrame_GetStackPointer(frame);
+            stack_pointer += -1;
+            assert(WITHIN_STACK_BOUNDS());
+            _PyFrame_SetStackPointer(frame, stack_pointer);
+            PyStackRef_CLOSE(interpolations);
+            stack_pointer = _PyFrame_GetStackPointer(frame);
+            stack_pointer += -1;
+            assert(WITHIN_STACK_BOUNDS());
+            _PyFrame_SetStackPointer(frame, stack_pointer);
+            PyStackRef_CLOSE(strings);
+            stack_pointer = _PyFrame_GetStackPointer(frame);
+            if (template_o == NULL) {
+                JUMP_TO_ERROR();
+            }
+            template = PyStackRef_FromPyObjectSteal(template_o);
+            stack_pointer[0] = template;
+            stack_pointer += 1;
+            assert(WITHIN_STACK_BOUNDS());
+            break;
+        }
+
         case _BUILD_TUPLE: {
             _PyStackRef *values;
             _PyStackRef tup;
index 88ab86a774f0ff16cabb1c090e0a9fc68df74323..89bfc069080a951e22f20f426bf8d4450940e55b 100644 (file)
             DISPATCH();
         }
 
+        TARGET(BUILD_INTERPOLATION) {
+            #if Py_TAIL_CALL_INTERP
+            int opcode = BUILD_INTERPOLATION;
+            (void)(opcode);
+            #endif
+            frame->instr_ptr = next_instr;
+            next_instr += 1;
+            INSTRUCTION_STATS(BUILD_INTERPOLATION);
+            _PyStackRef value;
+            _PyStackRef str;
+            _PyStackRef *format;
+            _PyStackRef interpolation;
+            format = &stack_pointer[-(oparg & 1)];
+            str = stack_pointer[-1 - (oparg & 1)];
+            value = stack_pointer[-2 - (oparg & 1)];
+            PyObject *value_o = PyStackRef_AsPyObjectBorrow(value);
+            PyObject *str_o = PyStackRef_AsPyObjectBorrow(str);
+            int conversion = oparg >> 2;
+            PyObject *format_o;
+            if (oparg & 1) {
+                format_o = PyStackRef_AsPyObjectBorrow(format[0]);
+            }
+            else {
+                format_o = &_Py_STR(empty);
+            }
+            _PyFrame_SetStackPointer(frame, stack_pointer);
+            PyObject *interpolation_o = _PyInterpolation_Build(value_o, str_o, conversion, format_o);
+            stack_pointer = _PyFrame_GetStackPointer(frame);
+            if (oparg & 1) {
+                stack_pointer += -(oparg & 1);
+                assert(WITHIN_STACK_BOUNDS());
+                _PyFrame_SetStackPointer(frame, stack_pointer);
+                PyStackRef_CLOSE(format[0]);
+                stack_pointer = _PyFrame_GetStackPointer(frame);
+            }
+            else {
+                stack_pointer += -(oparg & 1);
+            }
+            stack_pointer += -1;
+            assert(WITHIN_STACK_BOUNDS());
+            _PyFrame_SetStackPointer(frame, stack_pointer);
+            PyStackRef_CLOSE(str);
+            stack_pointer = _PyFrame_GetStackPointer(frame);
+            stack_pointer += -1;
+            assert(WITHIN_STACK_BOUNDS());
+            _PyFrame_SetStackPointer(frame, stack_pointer);
+            PyStackRef_CLOSE(value);
+            stack_pointer = _PyFrame_GetStackPointer(frame);
+            if (interpolation_o == NULL) {
+                JUMP_TO_LABEL(error);
+            }
+            interpolation = PyStackRef_FromPyObjectSteal(interpolation_o);
+            stack_pointer[0] = interpolation;
+            stack_pointer += 1;
+            assert(WITHIN_STACK_BOUNDS());
+            DISPATCH();
+        }
+
         TARGET(BUILD_LIST) {
             #if Py_TAIL_CALL_INTERP
             int opcode = BUILD_LIST;
             DISPATCH();
         }
 
+        TARGET(BUILD_TEMPLATE) {
+            #if Py_TAIL_CALL_INTERP
+            int opcode = BUILD_TEMPLATE;
+            (void)(opcode);
+            #endif
+            frame->instr_ptr = next_instr;
+            next_instr += 1;
+            INSTRUCTION_STATS(BUILD_TEMPLATE);
+            _PyStackRef strings;
+            _PyStackRef interpolations;
+            _PyStackRef template;
+            interpolations = stack_pointer[-1];
+            strings = stack_pointer[-2];
+            PyObject *strings_o = PyStackRef_AsPyObjectBorrow(strings);
+            PyObject *interpolations_o = PyStackRef_AsPyObjectBorrow(interpolations);
+            _PyFrame_SetStackPointer(frame, stack_pointer);
+            PyObject *template_o = _PyTemplate_Build(strings_o, interpolations_o);
+            stack_pointer = _PyFrame_GetStackPointer(frame);
+            stack_pointer += -1;
+            assert(WITHIN_STACK_BOUNDS());
+            _PyFrame_SetStackPointer(frame, stack_pointer);
+            PyStackRef_CLOSE(interpolations);
+            stack_pointer = _PyFrame_GetStackPointer(frame);
+            stack_pointer += -1;
+            assert(WITHIN_STACK_BOUNDS());
+            _PyFrame_SetStackPointer(frame, stack_pointer);
+            PyStackRef_CLOSE(strings);
+            stack_pointer = _PyFrame_GetStackPointer(frame);
+            if (template_o == NULL) {
+                JUMP_TO_LABEL(error);
+            }
+            template = PyStackRef_FromPyObjectSteal(template_o);
+            stack_pointer[0] = template;
+            stack_pointer += 1;
+            assert(WITHIN_STACK_BOUNDS());
+            DISPATCH();
+        }
+
         TARGET(BUILD_TUPLE) {
             #if Py_TAIL_CALL_INTERP
             int opcode = BUILD_TUPLE;
index 8a91d2f62a4627bb2ae53454090f280a5487d852..e232cc1f7d9250695242a8d8e71109c1ed2599bd 100644 (file)
@@ -12,6 +12,7 @@
 #include "pycore_frame.h"
 #include "pycore_function.h"
 #include "pycore_interpframe.h"
+#include "pycore_interpolation.h"
 #include "pycore_intrinsics.h"
 #include "pycore_list.h"
 #include "pycore_long.h"
@@ -21,6 +22,7 @@
 #include "pycore_pyerrors.h"
 #include "pycore_setobject.h"
 #include "pycore_sliceobject.h"
+#include "pycore_template.h"
 #include "pycore_tuple.h"
 #include "pycore_unicodeobject.h"
 
index 5b9fb794c6bddc962850033fc326132dbd409773..debaab98f00a8e289500ccab9bc0f82c3c893458 100644 (file)
@@ -2,8 +2,9 @@
 static void *opcode_targets[256] = {
     &&TARGET_CACHE,
     &&TARGET_BINARY_SLICE,
-    &&TARGET_CALL_FUNCTION_EX,
+    &&TARGET_BUILD_TEMPLATE,
     &&TARGET_BINARY_OP_INPLACE_ADD_UNICODE,
+    &&TARGET_CALL_FUNCTION_EX,
     &&TARGET_CHECK_EG_MATCH,
     &&TARGET_CHECK_EXC_MATCH,
     &&TARGET_CLEANUP_THROW,
@@ -16,8 +17,8 @@ static void *opcode_targets[256] = {
     &&TARGET_GET_AITER,
     &&TARGET_GET_ANEXT,
     &&TARGET_GET_ITER,
-    &&TARGET_GET_LEN,
     &&TARGET_RESERVED,
+    &&TARGET_GET_LEN,
     &&TARGET_GET_YIELD_FROM_ITER,
     &&TARGET_INTERPRETER_EXIT,
     &&TARGET_LOAD_BUILD_CLASS,
@@ -44,6 +45,7 @@ static void *opcode_targets[256] = {
     &&TARGET_UNARY_NOT,
     &&TARGET_WITH_EXCEPT_START,
     &&TARGET_BINARY_OP,
+    &&TARGET_BUILD_INTERPOLATION,
     &&TARGET_BUILD_LIST,
     &&TARGET_BUILD_MAP,
     &&TARGET_BUILD_SET,
@@ -126,8 +128,6 @@ static void *opcode_targets[256] = {
     &&_unknown_opcode,
     &&_unknown_opcode,
     &&_unknown_opcode,
-    &&_unknown_opcode,
-    &&_unknown_opcode,
     &&TARGET_RESUME,
     &&TARGET_BINARY_OP_ADD_FLOAT,
     &&TARGET_BINARY_OP_ADD_INT,
@@ -285,11 +285,13 @@ Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_BINARY_OP_SUBSCR_TUPLE_INT(TAIL_
 Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_BINARY_OP_SUBTRACT_FLOAT(TAIL_CALL_PARAMS);
 Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_BINARY_OP_SUBTRACT_INT(TAIL_CALL_PARAMS);
 Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_BINARY_SLICE(TAIL_CALL_PARAMS);
+Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_BUILD_INTERPOLATION(TAIL_CALL_PARAMS);
 Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_BUILD_LIST(TAIL_CALL_PARAMS);
 Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_BUILD_MAP(TAIL_CALL_PARAMS);
 Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_BUILD_SET(TAIL_CALL_PARAMS);
 Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_BUILD_SLICE(TAIL_CALL_PARAMS);
 Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_BUILD_STRING(TAIL_CALL_PARAMS);
+Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_BUILD_TEMPLATE(TAIL_CALL_PARAMS);
 Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_BUILD_TUPLE(TAIL_CALL_PARAMS);
 Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_CACHE(TAIL_CALL_PARAMS);
 Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_CALL(TAIL_CALL_PARAMS);
@@ -521,11 +523,13 @@ static py_tail_call_funcptr INSTRUCTION_TABLE[256] = {
     [BINARY_OP_SUBTRACT_FLOAT] = _TAIL_CALL_BINARY_OP_SUBTRACT_FLOAT,
     [BINARY_OP_SUBTRACT_INT] = _TAIL_CALL_BINARY_OP_SUBTRACT_INT,
     [BINARY_SLICE] = _TAIL_CALL_BINARY_SLICE,
+    [BUILD_INTERPOLATION] = _TAIL_CALL_BUILD_INTERPOLATION,
     [BUILD_LIST] = _TAIL_CALL_BUILD_LIST,
     [BUILD_MAP] = _TAIL_CALL_BUILD_MAP,
     [BUILD_SET] = _TAIL_CALL_BUILD_SET,
     [BUILD_SLICE] = _TAIL_CALL_BUILD_SLICE,
     [BUILD_STRING] = _TAIL_CALL_BUILD_STRING,
+    [BUILD_TEMPLATE] = _TAIL_CALL_BUILD_TEMPLATE,
     [BUILD_TUPLE] = _TAIL_CALL_BUILD_TUPLE,
     [CACHE] = _TAIL_CALL_CACHE,
     [CALL] = _TAIL_CALL_CALL,
@@ -729,8 +733,6 @@ static py_tail_call_funcptr INSTRUCTION_TABLE[256] = {
     [UNPACK_SEQUENCE_TWO_TUPLE] = _TAIL_CALL_UNPACK_SEQUENCE_TWO_TUPLE,
     [WITH_EXCEPT_START] = _TAIL_CALL_WITH_EXCEPT_START,
     [YIELD_VALUE] = _TAIL_CALL_YIELD_VALUE,
-    [119] = _TAIL_CALL_UNKNOWN_OPCODE,
-    [120] = _TAIL_CALL_UNKNOWN_OPCODE,
     [121] = _TAIL_CALL_UNKNOWN_OPCODE,
     [122] = _TAIL_CALL_UNKNOWN_OPCODE,
     [123] = _TAIL_CALL_UNKNOWN_OPCODE,
index 679240b6efa3154d802d231b4417531bc90d7afe..b8391c59aaf94551e70d0d49d970084706234aae 100644 (file)
             break;
         }
 
+        case _BUILD_INTERPOLATION: {
+            JitOptSymbol *interpolation;
+            interpolation = sym_new_not_null(ctx);
+            stack_pointer[-2 - (oparg & 1)] = interpolation;
+            stack_pointer += -1 - (oparg & 1);
+            assert(WITHIN_STACK_BOUNDS());
+            break;
+        }
+
+        case _BUILD_TEMPLATE: {
+            JitOptSymbol *template;
+            template = sym_new_not_null(ctx);
+            stack_pointer[-2] = template;
+            stack_pointer += -1;
+            assert(WITHIN_STACK_BOUNDS());
+            break;
+        }
+
         case _BUILD_TUPLE: {
             JitOptSymbol **values;
             JitOptSymbol *tup;
index 0871e147169328465191079c8c2cf1429e0d126c..9eaf493a652aedfafb1a2a607796568d7c5ee0cd 100644 (file)
@@ -13,6 +13,7 @@
 #include "pycore_freelist.h"      // _PyObject_ClearFreeLists()
 #include "pycore_global_objects_fini_generated.h"  // _PyStaticObjects_CheckRefcnt()
 #include "pycore_initconfig.h"    // _PyStatus_OK()
+#include "pycore_interpolation.h" // _PyInterpolation_InitTypes()
 #include "pycore_long.h"          // _PyLong_InitTypes()
 #include "pycore_object.h"        // _PyDebug_PrintTotalRefs()
 #include "pycore_obmalloc.h"      // _PyMem_init_obmalloc()
@@ -754,6 +755,11 @@ pycore_init_types(PyInterpreterState *interp)
         return status;
     }
 
+    status = _PyInterpolation_InitTypes(interp);
+    if (_PyStatus_EXCEPTION(status)) {
+        return status;
+    }
+
     return _PyStatus_OK();
 }
 
index 2f13f35072aa5ef7cfbc80deba744ec6338e63f0..f633e281019720fe6a97a3e5f21f156bdea98347 100644 (file)
@@ -2510,9 +2510,17 @@ symtable_visit_expr(struct symtable *st, expr_ty e)
         if (e->v.FormattedValue.format_spec)
             VISIT(st, expr, e->v.FormattedValue.format_spec);
         break;
+    case Interpolation_kind:
+        VISIT(st, expr, e->v.Interpolation.value);
+        if (e->v.Interpolation.format_spec)
+            VISIT(st, expr, e->v.Interpolation.format_spec);
+        break;
     case JoinedStr_kind:
         VISIT_SEQ(st, expr, e->v.JoinedStr.values);
         break;
+    case TemplateStr_kind:
+        VISIT_SEQ(st, expr, e->v.TemplateStr.values);
+        break;
     case Constant_kind:
         /* Nothing to do here. */
         break;
index 7316333dcd452ec31bcc9b84e7c032b5dcbbcc6a..a64806763f3fce3fe4290ba7f093a2781f809523 100755 (executable)
@@ -87,7 +87,8 @@ extern "C" {
                                  (x) == INDENT    || \\
                                  (x) == DEDENT)
 #define ISSTRINGLIT(x)          ((x) == STRING           || \\
-                                 (x) == FSTRING_MIDDLE)
+                                 (x) == FSTRING_MIDDLE   || \\
+                                 (x) == TSTRING_MIDDLE)
 
 
 // Export these 4 symbols for 'test_peg_generator'
index d509489176b9453819330b818ccd5fc7061ddba0..2077534ccf412868a5ab3119d1c7eee1b229e4af 100644 (file)
@@ -794,6 +794,7 @@ Objects/genobject.c:_PyAsyncGenASend_Type                        PyTypeObject _P
 Objects/genobject.c:_PyAsyncGenAThrow_Type                       PyTypeObject _PyAsyncGenAThrow_Type
 Objects/genobject.c:_PyAsyncGenWrappedValue_Type                 PyTypeObject _PyAsyncGenWrappedValue_Type
 Objects/genobject.c:_PyCoroWrapper_Type                          PyTypeObject _PyCoroWrapper_Type
+Objects/interpolationobject.c:_PyInterpolation_Type              PyTypeObject _PyInterpolation_Type
 Objects/interpreteridobject.c:_PyInterpreterID_Type              PyTypeObject _PyInterpreterID_Type
 Objects/iterobject.c:PyCallIter_Type                             PyTypeObject PyCallIter_Type
 Objects/iterobject.c:PySeqIter_Type                              PyTypeObject PySeqIter_Type
@@ -827,6 +828,8 @@ Objects/sliceobject.c:PyEllipsis_Type                            PyTypeObject Py
 Objects/sliceobject.c:PySlice_Type                               PyTypeObject PySlice_Type
 Objects/stringlib/unicode_format.h:PyFieldNameIter_Type          static PyTypeObject PyFieldNameIter_Type
 Objects/stringlib/unicode_format.h:PyFormatterIter_Type          static PyTypeObject PyFormatterIter_Type
+Objects/templateobject.c:_PyTemplateIter_Type                    PyTypeObject _PyTemplateIter_Type
+Objects/templateobject.c:_PyTemplate_Type                        PyTypeObject _PyTemplate_Type
 Objects/tupleobject.c:PyTupleIter_Type                           PyTypeObject PyTupleIter_Type
 Objects/tupleobject.c:PyTuple_Type                               PyTypeObject PyTuple_Type
 Objects/typeobject.c:PyBaseObject_Type                           PyTypeObject PyBaseObject_Type
index 54954cfb5f83ffa46c6dc17c158fd95cf4924af5..3c3cb2f9c86f16beba37ede9a1a06e986a6a2e11 100644 (file)
@@ -55,6 +55,7 @@ Objects/genobject.c   -       _PyAsyncGenASend_Type   -
 Objects/genobject.c    -       _PyAsyncGenAThrow_Type  -
 Objects/genobject.c    -       _PyAsyncGenWrappedValue_Type    -
 Objects/genobject.c    -       _PyCoroWrapper_Type     -
+Objects/interpolationobject.c  -       _PyInterpolation_Type   -
 Objects/iterobject.c   -       PyCallIter_Type -
 Objects/iterobject.c   -       PySeqIter_Type  -
 Objects/iterobject.c   -       _PyAnextAwaitable_Type  -
@@ -86,6 +87,8 @@ Objects/setobject.c   -       PySetIter_Type  -
 Objects/setobject.c    -       PySet_Type      -
 Objects/sliceobject.c  -       PyEllipsis_Type -
 Objects/sliceobject.c  -       PySlice_Type    -
+Objects/templateobject.c       -       _PyTemplateIter_Type    -
+Objects/templateobject.c       -       _PyTemplate_Type        -
 Objects/tupleobject.c  -       PyTupleIter_Type        -
 Objects/tupleobject.c  -       PyTuple_Type    -
 Objects/typeobject.c   -       _PyBufferWrapper_Type   -
index bc18e702eeaa3ba44042f765f8688b80363c1a4a..68cf75942d85c9decf5e52474a08d0f9d1113992 100644 (file)
@@ -13,6 +13,7 @@
 #include "pycore_function.h"
 #include "pycore_genobject.h"
 #include "pycore_interpframe.h"
+#include "pycore_interpolation.h"
 #include "pycore_intrinsics.h"
 #include "pycore_jit.h"
 #include "pycore_list.h"
@@ -25,6 +26,7 @@
 #include "pycore_setobject.h"
 #include "pycore_sliceobject.h"
 #include "pycore_stackref.h"
+#include "pycore_template.h"
 #include "pycore_tuple.h"
 #include "pycore_unicodeobject.h"