@@ -519,7 +519,7 @@ literal_pattern[pattern_ty]:
519
519
literal_expr[expr_ty]:
520
520
| signed_number !('+' | '-')
521
521
| complex_number
522
- | strings
522
+ | &(STRING|FSTRING_START|TSTRING_START) strings
523
523
| 'None' { _PyAST_Constant(Py_None, NULL, EXTRA) }
524
524
| 'True' { _PyAST_Constant(Py_True, NULL, EXTRA) }
525
525
| 'False' { _PyAST_Constant(Py_False, NULL, EXTRA) }
@@ -859,7 +859,7 @@ atom[expr_ty]:
859
859
| 'True' { _PyAST_Constant(Py_True, NULL, EXTRA) }
860
860
| 'False' { _PyAST_Constant(Py_False, NULL, EXTRA) }
861
861
| 'None' { _PyAST_Constant(Py_None, NULL, EXTRA) }
862
- | &(STRING|FSTRING_START) strings
862
+ | &(STRING|FSTRING_START|TSTRING_START ) strings
863
863
| NUMBER
864
864
| &'(' (tuple | group | genexp)
865
865
| &'[' (list | listcomp)
@@ -935,7 +935,7 @@ fstring_middle[expr_ty]:
935
935
fstring_replacement_field[expr_ty]:
936
936
| '{' a=annotated_rhs debug_expr='='? conversion=[fstring_conversion] format=[fstring_full_format_spec] rbrace='}' {
937
937
_PyPegen_formatted_value(p, a, debug_expr, conversion, format, rbrace, EXTRA) }
938
- | invalid_replacement_field
938
+ | invalid_fstring_replacement_field
939
939
fstring_conversion[ResultTokenWithMetadata*]:
940
940
| conv_token="!" conv=NAME { _PyPegen_check_fstring_conversion(p, conv_token, conv) }
941
941
fstring_full_format_spec[ResultTokenWithMetadata*]:
@@ -946,8 +946,27 @@ fstring_format_spec[expr_ty]:
946
946
fstring[expr_ty]:
947
947
| a=FSTRING_START b=fstring_middle* c=FSTRING_END { _PyPegen_joined_str(p, a, (asdl_expr_seq*)b, c) }
948
948
949
+ tstring_format_spec_replacement_field[expr_ty]:
950
+ | '{' a=annotated_rhs debug_expr='='? conversion=[fstring_conversion] format=[tstring_full_format_spec] rbrace='}' {
951
+ _PyPegen_formatted_value(p, a, debug_expr, conversion, format, rbrace, EXTRA) }
952
+ | invalid_tstring_replacement_field
953
+ tstring_format_spec[expr_ty]:
954
+ | t=TSTRING_MIDDLE { _PyPegen_decoded_constant_from_token(p, t) }
955
+ | tstring_format_spec_replacement_field
956
+ tstring_full_format_spec[ResultTokenWithMetadata*]:
957
+ | colon=':' spec=tstring_format_spec* { _PyPegen_setup_full_format_spec(p, colon, (asdl_expr_seq *) spec, EXTRA) }
958
+ tstring_replacement_field[expr_ty]:
959
+ | '{' a=annotated_rhs debug_expr='='? conversion=[fstring_conversion] format=[tstring_full_format_spec] rbrace='}' {
960
+ _PyPegen_interpolation(p, a, debug_expr, conversion, format, rbrace, EXTRA) }
961
+ | invalid_tstring_replacement_field
962
+ tstring_middle[expr_ty]:
963
+ | tstring_replacement_field
964
+ | t=TSTRING_MIDDLE { _PyPegen_constant_from_token(p, t) }
965
+ tstring[expr_ty] (memo):
966
+ | a=TSTRING_START b=tstring_middle* c=TSTRING_END { _PyPegen_template_str(p, a, (asdl_expr_seq*)b, c) }
967
+
949
968
string[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) }
969
+ strings[expr_ty] (memo): a[asdl_expr_seq*]=(fstring|string|tstring )+ { _PyPegen_concatenate_strings(p, a, EXTRA) }
951
970
952
971
list[expr_ty]:
953
972
| '[' a=[star_named_expressions] ']' { _PyAST_List(a, Load, EXTRA) }
@@ -1212,6 +1231,8 @@ invalid_expression:
1212
1231
RAISE_SYNTAX_ERROR_KNOWN_LOCATION (a, "expected expression before 'if', but statement is given") }
1213
1232
| a='lambda' [lambda_params] b=':' &FSTRING_MIDDLE {
1214
1233
RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, b, "f-string: lambda expressions are not allowed without parentheses") }
1234
+ | a='lambda' [lambda_params] b=':' &TSTRING_MIDDLE {
1235
+ RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, b, "t-string: lambda expressions are not allowed without parentheses") }
1215
1236
1216
1237
invalid_named_expression(memo):
1217
1238
| a=expression ':=' expression {
@@ -1454,28 +1475,50 @@ invalid_starred_expression_unpacking:
1454
1475
invalid_starred_expression:
1455
1476
| '*' { RAISE_SYNTAX_ERROR("Invalid star expression") }
1456
1477
1457
- invalid_replacement_field :
1478
+ invalid_fstring_replacement_field :
1458
1479
| '{' a='=' { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "f-string: valid expression required before '='") }
1459
1480
| '{' a='!' { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "f-string: valid expression required before '!'") }
1460
1481
| '{' a=':' { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "f-string: valid expression required before ':'") }
1461
1482
| '{' 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 '{'")}
1483
+ | '{' !annotated_rhs { RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN("f-string: expecting a valid expression after '{'") }
1463
1484
| '{' annotated_rhs !('=' | '!' | ':' | '}') {
1464
1485
PyErr_Occurred() ? NULL : RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN("f-string: expecting '=', or '!', or ':', or '}'") }
1465
1486
| '{' annotated_rhs '=' !('!' | ':' | '}') {
1466
1487
PyErr_Occurred() ? NULL : RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN("f-string: expecting '!', or ':', or '}'") }
1467
- | '{' annotated_rhs '='? invalid_conversion_character
1488
+ | '{' annotated_rhs '='? invalid_fstring_conversion_character
1468
1489
| '{' annotated_rhs '='? ['!' NAME] !(':' | '}') {
1469
1490
PyErr_Occurred() ? NULL : RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN("f-string: expecting ':' or '}'") }
1470
1491
| '{' annotated_rhs '='? ['!' NAME] ':' fstring_format_spec* !'}' {
1471
1492
PyErr_Occurred() ? NULL : RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN("f-string: expecting '}', or format specs") }
1472
1493
| '{' annotated_rhs '='? ['!' NAME] !'}' {
1473
1494
PyErr_Occurred() ? NULL : RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN("f-string: expecting '}'") }
1474
1495
1475
- invalid_conversion_character :
1496
+ invalid_fstring_conversion_character :
1476
1497
| '!' &(':' | '}') { RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN("f-string: missing conversion character") }
1477
1498
| '!' !NAME { RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN("f-string: invalid conversion character") }
1478
1499
1500
+ invalid_tstring_replacement_field:
1501
+ | '{' a='=' { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "t-string: valid expression required before '='") }
1502
+ | '{' a='!' { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "t-string: valid expression required before '!'") }
1503
+ | '{' a=':' { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "t-string: valid expression required before ':'") }
1504
+ | '{' a='}' { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "t-string: valid expression required before '}'") }
1505
+ | '{' !annotated_rhs { RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN("t-string: expecting a valid expression after '{'") }
1506
+ | '{' annotated_rhs !('=' | '!' | ':' | '}') {
1507
+ PyErr_Occurred() ? NULL : RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN("t-string: expecting '=', or '!', or ':', or '}'") }
1508
+ | '{' annotated_rhs '=' !('!' | ':' | '}') {
1509
+ PyErr_Occurred() ? NULL : RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN("t-string: expecting '!', or ':', or '}'") }
1510
+ | '{' annotated_rhs '='? invalid_tstring_conversion_character
1511
+ | '{' annotated_rhs '='? ['!' NAME] !(':' | '}') {
1512
+ PyErr_Occurred() ? NULL : RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN("t-string: expecting ':' or '}'") }
1513
+ | '{' annotated_rhs '='? ['!' NAME] ':' fstring_format_spec* !'}' {
1514
+ PyErr_Occurred() ? NULL : RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN("t-string: expecting '}', or format specs") }
1515
+ | '{' annotated_rhs '='? ['!' NAME] !'}' {
1516
+ PyErr_Occurred() ? NULL : RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN("t-string: expecting '}'") }
1517
+
1518
+ invalid_tstring_conversion_character:
1519
+ | '!' &(':' | '}') { RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN("t-string: missing conversion character") }
1520
+ | '!' !NAME { RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN("t-string: invalid conversion character") }
1521
+
1479
1522
invalid_arithmetic:
1480
1523
| sum ('+'|'-'|'*'|'/'|'%'|'//'|'@') a='not' b=inversion { RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, b, "'not' after an operator must be parenthesized") }
1481
1524
invalid_factor:
0 commit comments