@@ -442,15 +442,23 @@ try_stmt[stmt_ty]:
442442except_block[excepthandler_ty]:
443443 | invalid_except_stmt_indent
444444 | 'except' e=expressions ':' b=block {
445- _PyAST_ExceptHandler(e, NULL, b, EXTRA) }
445+ CHECK_VERSION(
446+ excepthandler_ty,
447+ 14,
448+ "except expressions without parentheses",
449+ _PyAST_ExceptHandler(e, NULL, b, EXTRA)) }
446450 | 'except' e=expression 'as' t=NAME ':' b=block {
447451 _PyAST_ExceptHandler(e, ((expr_ty) t)->v.Name.id, b, EXTRA) }
448452 | 'except' ':' b=block { _PyAST_ExceptHandler(NULL, NULL, b, EXTRA) }
449453 | invalid_except_stmt
450454except_star_block[excepthandler_ty]:
451455 | invalid_except_star_stmt_indent
452456 | 'except' '*' e=expressions ':' b=block {
453- _PyAST_ExceptHandler(e, NULL, b, EXTRA) }
457+ CHECK_VERSION(
458+ excepthandler_ty,
459+ 14,
460+ "except expressions without parentheses",
461+ _PyAST_ExceptHandler(e, NULL, b, EXTRA)) }
454462 | 'except' '*' e=expression 'as' t=NAME ':' b=block {
455463 _PyAST_ExceptHandler(e, ((expr_ty) t)->v.Name.id, b, EXTRA) }
456464 | invalid_except_star_stmt
@@ -519,7 +527,7 @@ literal_pattern[pattern_ty]:
519527literal_expr[expr_ty]:
520528 | signed_number !('+' | '-')
521529 | complex_number
522- | strings
530+ | &(STRING|FSTRING_START|TSTRING_START) strings
523531 | 'None' { _PyAST_Constant(Py_None, NULL, EXTRA) }
524532 | 'True' { _PyAST_Constant(Py_True, NULL, EXTRA) }
525533 | 'False' { _PyAST_Constant(Py_False, NULL, EXTRA) }
@@ -859,7 +867,7 @@ atom[expr_ty]:
859867 | 'True' { _PyAST_Constant(Py_True, NULL, EXTRA) }
860868 | 'False' { _PyAST_Constant(Py_False, NULL, EXTRA) }
861869 | 'None' { _PyAST_Constant(Py_None, NULL, EXTRA) }
862- | &(STRING|FSTRING_START) strings
870+ | &(STRING|FSTRING_START|TSTRING_START ) strings
863871 | NUMBER
864872 | &'(' (tuple | group | genexp)
865873 | &'[' (list | listcomp)
@@ -935,7 +943,7 @@ fstring_middle[expr_ty]:
935943fstring_replacement_field[expr_ty]:
936944 | '{' a=annotated_rhs debug_expr='='? conversion=[fstring_conversion] format=[fstring_full_format_spec] rbrace='}' {
937945 _PyPegen_formatted_value(p, a, debug_expr, conversion, format, rbrace, EXTRA) }
938- | invalid_replacement_field
946+ | invalid_fstring_replacement_field
939947fstring_conversion[ResultTokenWithMetadata*]:
940948 | conv_token="!" conv=NAME { _PyPegen_check_fstring_conversion(p, conv_token, conv) }
941949fstring_full_format_spec[ResultTokenWithMetadata*]:
@@ -946,8 +954,27 @@ fstring_format_spec[expr_ty]:
946954fstring[expr_ty]:
947955 | a=FSTRING_START b=fstring_middle* c=FSTRING_END { _PyPegen_joined_str(p, a, (asdl_expr_seq*)b, c) }
948956
957+ tstring_format_spec_replacement_field[expr_ty]:
958+ | '{' a=annotated_rhs debug_expr='='? conversion=[fstring_conversion] format=[tstring_full_format_spec] rbrace='}' {
959+ _PyPegen_formatted_value(p, a, debug_expr, conversion, format, rbrace, EXTRA) }
960+ | invalid_tstring_replacement_field
961+ tstring_format_spec[expr_ty]:
962+ | t=TSTRING_MIDDLE { _PyPegen_decoded_constant_from_token(p, t) }
963+ | tstring_format_spec_replacement_field
964+ tstring_full_format_spec[ResultTokenWithMetadata*]:
965+ | colon=':' spec=tstring_format_spec* { _PyPegen_setup_full_format_spec(p, colon, (asdl_expr_seq *) spec, EXTRA) }
966+ tstring_replacement_field[expr_ty]:
967+ | '{' a=annotated_rhs debug_expr='='? conversion=[fstring_conversion] format=[tstring_full_format_spec] rbrace='}' {
968+ _PyPegen_interpolation(p, a, debug_expr, conversion, format, rbrace, EXTRA) }
969+ | invalid_tstring_replacement_field
970+ tstring_middle[expr_ty]:
971+ | tstring_replacement_field
972+ | t=TSTRING_MIDDLE { _PyPegen_constant_from_token(p, t) }
973+ tstring[expr_ty] (memo):
974+ | a=TSTRING_START b=tstring_middle* c=TSTRING_END { _PyPegen_template_str(p, a, (asdl_expr_seq*)b, c) }
975+
949976string[expr_ty]: s[Token*]=STRING { _PyPegen_constant_from_string(p, s) }
950- strings[expr_ty] (memo): a[asdl_expr_seq*]=(fstring|string)+ { _PyPegen_concatenate_strings(p, a, EXTRA) }
977+ strings[expr_ty] (memo): a[asdl_expr_seq*]=(fstring|string|tstring )+ { _PyPegen_concatenate_strings(p, a, EXTRA) }
951978
952979list[expr_ty]:
953980 | '[' a=[star_named_expressions] ']' { _PyAST_List(a, Load, EXTRA) }
@@ -1212,6 +1239,8 @@ invalid_expression:
12121239 RAISE_SYNTAX_ERROR_KNOWN_LOCATION (a, "expected expression before 'if', but statement is given") }
12131240 | a='lambda' [lambda_params] b=':' &FSTRING_MIDDLE {
12141241 RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, b, "f-string: lambda expressions are not allowed without parentheses") }
1242+ | a='lambda' [lambda_params] b=':' &TSTRING_MIDDLE {
1243+ RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, b, "t-string: lambda expressions are not allowed without parentheses") }
12151244
12161245invalid_named_expression(memo):
12171246 | a=expression ':=' expression {
@@ -1454,28 +1483,50 @@ invalid_starred_expression_unpacking:
14541483invalid_starred_expression:
14551484 | '*' { RAISE_SYNTAX_ERROR("Invalid star expression") }
14561485
1457- invalid_replacement_field :
1486+ invalid_fstring_replacement_field :
14581487 | '{' a='=' { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "f-string: valid expression required before '='") }
14591488 | '{' a='!' { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "f-string: valid expression required before '!'") }
14601489 | '{' a=':' { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "f-string: valid expression required before ':'") }
14611490 | '{' a='}' { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "f-string: valid expression required before '}'") }
1462- | '{' !annotated_rhs { RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN("f-string: expecting a valid expression after '{'")}
1491+ | '{' !annotated_rhs { RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN("f-string: expecting a valid expression after '{'") }
14631492 | '{' annotated_rhs !('=' | '!' | ':' | '}') {
14641493 PyErr_Occurred() ? NULL : RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN("f-string: expecting '=', or '!', or ':', or '}'") }
14651494 | '{' annotated_rhs '=' !('!' | ':' | '}') {
14661495 PyErr_Occurred() ? NULL : RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN("f-string: expecting '!', or ':', or '}'") }
1467- | '{' annotated_rhs '='? invalid_conversion_character
1496+ | '{' annotated_rhs '='? invalid_fstring_conversion_character
14681497 | '{' annotated_rhs '='? ['!' NAME] !(':' | '}') {
14691498 PyErr_Occurred() ? NULL : RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN("f-string: expecting ':' or '}'") }
14701499 | '{' annotated_rhs '='? ['!' NAME] ':' fstring_format_spec* !'}' {
14711500 PyErr_Occurred() ? NULL : RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN("f-string: expecting '}', or format specs") }
14721501 | '{' annotated_rhs '='? ['!' NAME] !'}' {
14731502 PyErr_Occurred() ? NULL : RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN("f-string: expecting '}'") }
14741503
1475- invalid_conversion_character :
1504+ invalid_fstring_conversion_character :
14761505 | '!' &(':' | '}') { RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN("f-string: missing conversion character") }
14771506 | '!' !NAME { RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN("f-string: invalid conversion character") }
14781507
1508+ invalid_tstring_replacement_field:
1509+ | '{' a='=' { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "t-string: valid expression required before '='") }
1510+ | '{' a='!' { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "t-string: valid expression required before '!'") }
1511+ | '{' a=':' { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "t-string: valid expression required before ':'") }
1512+ | '{' a='}' { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "t-string: valid expression required before '}'") }
1513+ | '{' !annotated_rhs { RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN("t-string: expecting a valid expression after '{'") }
1514+ | '{' annotated_rhs !('=' | '!' | ':' | '}') {
1515+ PyErr_Occurred() ? NULL : RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN("t-string: expecting '=', or '!', or ':', or '}'") }
1516+ | '{' annotated_rhs '=' !('!' | ':' | '}') {
1517+ PyErr_Occurred() ? NULL : RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN("t-string: expecting '!', or ':', or '}'") }
1518+ | '{' annotated_rhs '='? invalid_tstring_conversion_character
1519+ | '{' annotated_rhs '='? ['!' NAME] !(':' | '}') {
1520+ PyErr_Occurred() ? NULL : RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN("t-string: expecting ':' or '}'") }
1521+ | '{' annotated_rhs '='? ['!' NAME] ':' fstring_format_spec* !'}' {
1522+ PyErr_Occurred() ? NULL : RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN("t-string: expecting '}', or format specs") }
1523+ | '{' annotated_rhs '='? ['!' NAME] !'}' {
1524+ PyErr_Occurred() ? NULL : RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN("t-string: expecting '}'") }
1525+
1526+ invalid_tstring_conversion_character:
1527+ | '!' &(':' | '}') { RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN("t-string: missing conversion character") }
1528+ | '!' !NAME { RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN("t-string: invalid conversion character") }
1529+
14791530invalid_arithmetic:
14801531 | sum ('+'|'-'|'*'|'/'|'%'|'//'|'@') a='not' b=inversion { RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, b, "'not' after an operator must be parenthesized") }
14811532invalid_factor:
0 commit comments