Skip to content

Commit 97998dd

Browse files
Add dollar-declaration () syntax and assignment-aware ternary (?:) operator
1 parent 04a1fcf commit 97998dd

2 files changed

Lines changed: 39 additions & 3 deletions

File tree

Grammar/Tokens

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ RARROW '->'
5959
ELLIPSIS '...'
6060
COLONEQUAL ':='
6161
EXCLAMATION '!'
62+
DOLLAR '$'
63+
TERNARY '?'
6264

6365
OP
6466
TYPE_IGNORE

Grammar/python.gram

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ simple_stmts[asdl_stmt_seq*]:
119119
# NOTE: assignment MUST precede expression, else parsing a simple assignment
120120
# will throw a SyntaxError.
121121
simple_stmt[stmt_ty] (memo):
122+
| dollar_stmt
122123
| assignment
123124
| &"type" type_alias
124125
| &('import' | 'from' | "lazy") import_stmt
@@ -134,6 +135,20 @@ simple_stmt[stmt_ty] (memo):
134135
| &'global' global_stmt
135136
| &'nonlocal' nonlocal_stmt
136137

138+
dollar_stmt[stmt_ty]:
139+
| '$' n=NAME {
140+
_PyAST_Assign(
141+
CHECK(asdl_expr_seq*,
142+
_PyPegen_singleton_seq(
143+
p,
144+
_PyAST_Name(n->v.Name.id, Store, EXTRA)
145+
)
146+
),
147+
_PyAST_Constant(Py_None, NULL, EXTRA),
148+
NULL,
149+
EXTRA
150+
)
151+
}
137152
compound_stmt[stmt_ty]:
138153
| &('def' | '@' | 'async') function_def
139154
| &'if' if_stmt
@@ -717,13 +732,25 @@ expression[expr_ty] (memo):
717732
| invalid_if_expression
718733
| invalid_expression
719734
| invalid_legacy_expression
735+
| question_if_expression
720736
| if_expression
721737
| disjunction
722738
| lambdef
723739

740+
# IMPORTANT:
741+
# The 'no' branch must be 'expression' (which includes question_if_expression
742+
# as one of its alternatives), NOT question_if_expression directly.
743+
# question_if_expression only matches "X ? Y : Z" shape; using it directly
744+
# for 'no' forces the false-branch to ALSO be a ternary, breaking simple
745+
# cases like "12 > 2 ? 5 : 4" where 4 is just a plain expression.
746+
question_if_expression[expr_ty]:
747+
| cond=disjunction '?' yes=expression ':' no=expression {
748+
_PyAST_IfExp(cond, yes, no, EXTRA)
749+
}
724750
if_expression[expr_ty]:
725-
| a=disjunction 'if' b=disjunction 'else' c=expression { _PyAST_IfExp(b, a, c, EXTRA) }
726-
751+
| a=disjunction 'if' b=disjunction 'else' c=expression {
752+
_PyAST_IfExp(b, a, c, EXTRA)
753+
}
727754
yield_expr[expr_ty]:
728755
| 'yield' 'from' a=expression { _PyAST_YieldFrom(a, EXTRA) }
729756
| 'yield' a=[star_expressions] { _PyAST_Yield(a, EXTRA) }
@@ -1239,11 +1266,17 @@ invalid_kwarg:
12391266
| a='**' expression '=' b=expression {
12401267
RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, b, "cannot assign to keyword argument unpacking") }
12411268

1242-
# IMPORTANT: Note that the "_without_invalid" suffix causes the rule to not call invalid rules under it
1269+
# IMPORTANT:
1270+
# expression_without_invalid must ALSO recognize question_if_expression,
1271+
# otherwise the invalid_* rules (which run BEFORE question_if_expression
1272+
# in the main `expression` rule) will treat '?' as a syntax error before
1273+
# ever letting question_if_expression get a chance to parse it.
12431274
expression_without_invalid[expr_ty]:
12441275
| a=disjunction 'if' b=disjunction 'else' c=expression { _PyAST_IfExp(b, a, c, EXTRA) }
1276+
| question_if_expression
12451277
| disjunction
12461278
| lambdef
1279+
12471280
invalid_legacy_expression:
12481281
| a=NAME !'(' b=star_expressions {
12491282
_PyPegen_check_legacy_stmt(p, a) ? RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, b,
@@ -1659,3 +1692,4 @@ invalid_bitwise_or:
16591692
? RAISE_SYNTAX_ERROR_KNOWN_RANGE(b, c, "invalid syntax. Maybe you meant 'or' or '|' instead of '||'?")
16601693
: NULL
16611694
}
1695+

0 commit comments

Comments
 (0)