Skip to content

Commit 1e7b858

Browse files
bpo-43892: Make match patterns explicit in the AST (GH-25585)
Co-authored-by: Brandt Bucher <[email protected]>
1 parent e52ab42 commit 1e7b858

20 files changed

+3598
-1515
lines changed

Doc/library/ast.rst

+282-36
Large diffs are not rendered by default.

Grammar/python.gram

+101-54
Original file line numberDiff line numberDiff line change
@@ -234,20 +234,20 @@ case_block[match_case_ty]:
234234
_PyAST_match_case(pattern, guard, body, p->arena) }
235235
guard[expr_ty]: 'if' guard=named_expression { guard }
236236

237-
patterns[expr_ty]:
238-
| values[asdl_expr_seq*]=open_sequence_pattern {
239-
_PyAST_Tuple(values, Load, EXTRA) }
237+
patterns[pattern_ty]:
238+
| patterns[asdl_pattern_seq*]=open_sequence_pattern {
239+
_PyAST_MatchSequence(patterns, EXTRA) }
240240
| pattern
241-
pattern[expr_ty]:
241+
pattern[pattern_ty]:
242242
| as_pattern
243243
| or_pattern
244-
as_pattern[expr_ty]:
245-
| pattern=or_pattern 'as' target=capture_pattern {
244+
as_pattern[pattern_ty]:
245+
| pattern=or_pattern 'as' target=pattern_capture_target {
246246
_PyAST_MatchAs(pattern, target->v.Name.id, EXTRA) }
247-
or_pattern[expr_ty]:
248-
| patterns[asdl_expr_seq*]='|'.closed_pattern+ {
247+
or_pattern[pattern_ty]:
248+
| patterns[asdl_pattern_seq*]='|'.closed_pattern+ {
249249
asdl_seq_LEN(patterns) == 1 ? asdl_seq_GET(patterns, 0) : _PyAST_MatchOr(patterns, EXTRA) }
250-
closed_pattern[expr_ty]:
250+
closed_pattern[pattern_ty]:
251251
| literal_pattern
252252
| capture_pattern
253253
| wildcard_pattern
@@ -257,78 +257,125 @@ closed_pattern[expr_ty]:
257257
| mapping_pattern
258258
| class_pattern
259259

260-
literal_pattern[expr_ty]:
260+
# Literal patterns are used for equality and identity constraints
261+
literal_pattern[pattern_ty]:
262+
| value=signed_number !('+' | '-') { _PyAST_MatchValue(value, EXTRA) }
263+
| value=complex_number { _PyAST_MatchValue(value, EXTRA) }
264+
| value=strings { _PyAST_MatchValue(value, EXTRA) }
265+
| 'None' { _PyAST_MatchSingleton(Py_None, EXTRA) }
266+
| 'True' { _PyAST_MatchSingleton(Py_True, EXTRA) }
267+
| 'False' { _PyAST_MatchSingleton(Py_False, EXTRA) }
268+
269+
# Literal expressions are used to restrict permitted mapping pattern keys
270+
literal_expr[expr_ty]:
261271
| signed_number !('+' | '-')
262-
| real=signed_number '+' imag=NUMBER { _PyAST_BinOp(real, Add, imag, EXTRA) }
263-
| real=signed_number '-' imag=NUMBER { _PyAST_BinOp(real, Sub, imag, EXTRA) }
272+
| complex_number
264273
| strings
265274
| 'None' { _PyAST_Constant(Py_None, NULL, EXTRA) }
266275
| 'True' { _PyAST_Constant(Py_True, NULL, EXTRA) }
267276
| 'False' { _PyAST_Constant(Py_False, NULL, EXTRA) }
277+
278+
complex_number[expr_ty]:
279+
| real=signed_number '+' imag=imaginary_number { _PyAST_BinOp(real, Add, imag, EXTRA) }
280+
| real=signed_number '-' imag=imaginary_number { _PyAST_BinOp(real, Sub, imag, EXTRA) }
281+
268282
signed_number[expr_ty]:
269283
| NUMBER
270284
| '-' number=NUMBER { _PyAST_UnaryOp(USub, number, EXTRA) }
271285

272-
capture_pattern[expr_ty]:
286+
imaginary_number[expr_ty]:
287+
| imag=NUMBER { _PyPegen_ensure_imaginary(p, imag) }
288+
289+
capture_pattern[pattern_ty]:
290+
| target=pattern_capture_target { _PyAST_MatchAs(NULL, target->v.Name.id, EXTRA) }
291+
292+
pattern_capture_target[expr_ty]:
273293
| !"_" name=NAME !('.' | '(' | '=') {
274294
_PyPegen_set_expr_context(p, name, Store) }
275295

276-
wildcard_pattern[expr_ty]:
277-
| "_" { _PyAST_Name(CHECK(PyObject*, _PyPegen_new_identifier(p, "_")), Store, EXTRA) }
296+
wildcard_pattern[pattern_ty]:
297+
| "_" { _PyAST_MatchAs(NULL, NULL, EXTRA) }
278298

279-
value_pattern[expr_ty]:
280-
| attr=attr !('.' | '(' | '=') { attr }
299+
value_pattern[pattern_ty]:
300+
| attr=attr !('.' | '(' | '=') { _PyAST_MatchValue(attr, EXTRA) }
281301
attr[expr_ty]:
282302
| value=name_or_attr '.' attr=NAME {
283303
_PyAST_Attribute(value, attr->v.Name.id, Load, EXTRA) }
284304
name_or_attr[expr_ty]:
285305
| attr
286306
| NAME
287307

288-
group_pattern[expr_ty]:
308+
group_pattern[pattern_ty]:
289309
| '(' pattern=pattern ')' { pattern }
290310

291-
sequence_pattern[expr_ty]:
292-
| '[' values=maybe_sequence_pattern? ']' { _PyAST_List(values, Load, EXTRA) }
293-
| '(' values=open_sequence_pattern? ')' { _PyAST_Tuple(values, Load, EXTRA) }
311+
sequence_pattern[pattern_ty]:
312+
| '[' patterns=maybe_sequence_pattern? ']' { _PyAST_MatchSequence(patterns, EXTRA) }
313+
| '(' patterns=open_sequence_pattern? ')' { _PyAST_MatchSequence(patterns, EXTRA) }
294314
open_sequence_pattern[asdl_seq*]:
295-
| value=maybe_star_pattern ',' values=maybe_sequence_pattern? {
296-
_PyPegen_seq_insert_in_front(p, value, values) }
315+
| pattern=maybe_star_pattern ',' patterns=maybe_sequence_pattern? {
316+
_PyPegen_seq_insert_in_front(p, pattern, patterns) }
297317
maybe_sequence_pattern[asdl_seq*]:
298-
| values=','.maybe_star_pattern+ ','? { values }
299-
maybe_star_pattern[expr_ty]:
318+
| patterns=','.maybe_star_pattern+ ','? { patterns }
319+
maybe_star_pattern[pattern_ty]:
300320
| star_pattern
301321
| pattern
302-
star_pattern[expr_ty]:
303-
| '*' value=(capture_pattern | wildcard_pattern) {
304-
_PyAST_Starred(value, Store, EXTRA) }
305-
306-
mapping_pattern[expr_ty]:
307-
| '{' items=items_pattern? '}' {
308-
_PyAST_Dict(CHECK(asdl_expr_seq*, _PyPegen_get_keys(p, items)), CHECK(asdl_expr_seq*, _PyPegen_get_values(p, items)), EXTRA) }
322+
star_pattern[pattern_ty]:
323+
| '*' target=pattern_capture_target {
324+
_PyAST_MatchStar(target->v.Name.id, EXTRA) }
325+
| '*' wildcard_pattern {
326+
_PyAST_MatchStar(NULL, EXTRA) }
327+
328+
mapping_pattern[pattern_ty]:
329+
| '{' '}' {
330+
_PyAST_MatchMapping(NULL, NULL, NULL, EXTRA) }
331+
| '{' rest=double_star_pattern ','? '}' {
332+
_PyAST_MatchMapping(NULL, NULL, rest->v.Name.id, EXTRA) }
333+
| '{' items=items_pattern ',' rest=double_star_pattern ','? '}' {
334+
_PyAST_MatchMapping(
335+
CHECK(asdl_expr_seq*, _PyPegen_get_pattern_keys(p, items)),
336+
CHECK(asdl_pattern_seq*, _PyPegen_get_patterns(p, items)),
337+
rest->v.Name.id,
338+
EXTRA) }
339+
| '{' items=items_pattern ','? '}' {
340+
_PyAST_MatchMapping(
341+
CHECK(asdl_expr_seq*, _PyPegen_get_pattern_keys(p, items)),
342+
CHECK(asdl_pattern_seq*, _PyPegen_get_patterns(p, items)),
343+
NULL,
344+
EXTRA) }
309345
items_pattern[asdl_seq*]:
310-
| items=','.key_value_pattern+ ','? { items }
311-
key_value_pattern[KeyValuePair*]:
312-
| key=(literal_pattern | value_pattern) ':' value=pattern {
313-
_PyPegen_key_value_pair(p, key, value) }
314-
| double_star_pattern
315-
double_star_pattern[KeyValuePair*]:
316-
| '**' value=capture_pattern { _PyPegen_key_value_pair(p, NULL, value) }
317-
318-
class_pattern[expr_ty]:
319-
| func=name_or_attr '(' ')' { _PyAST_Call(func, NULL, NULL, EXTRA) }
320-
| func=name_or_attr '(' args=positional_patterns ','? ')' {
321-
_PyAST_Call(func, args, NULL, EXTRA) }
322-
| func=name_or_attr '(' keywords=keyword_patterns ','? ')' {
323-
_PyAST_Call(func, NULL, keywords, EXTRA) }
324-
| func=name_or_attr '(' args=positional_patterns ',' keywords=keyword_patterns ','? ')' {
325-
_PyAST_Call(func, args, keywords, EXTRA) }
326-
positional_patterns[asdl_expr_seq*]:
327-
| args[asdl_expr_seq*]=','.pattern+ { args }
328-
keyword_patterns[asdl_keyword_seq*]:
329-
| keywords[asdl_keyword_seq*]=','.keyword_pattern+ { keywords }
330-
keyword_pattern[keyword_ty]:
331-
| arg=NAME '=' value=pattern { _PyAST_keyword(arg->v.Name.id, value, EXTRA) }
346+
| items=','.key_value_pattern+ { items }
347+
key_value_pattern[KeyPatternPair*]:
348+
| key=(literal_expr | attr) ':' pattern=pattern {
349+
_PyPegen_key_pattern_pair(p, key, pattern) }
350+
double_star_pattern[expr_ty]:
351+
| '**' target=pattern_capture_target { target }
352+
353+
class_pattern[pattern_ty]:
354+
| cls=name_or_attr '(' ')' {
355+
_PyAST_MatchClass(cls, NULL, NULL, NULL, EXTRA) }
356+
| cls=name_or_attr '(' patterns=positional_patterns ','? ')' {
357+
_PyAST_MatchClass(cls, patterns, NULL, NULL, EXTRA) }
358+
| cls=name_or_attr '(' keywords=keyword_patterns ','? ')' {
359+
_PyAST_MatchClass(
360+
cls, NULL,
361+
CHECK(asdl_identifier_seq*, _PyPegen_map_names_to_ids(p,
362+
CHECK(asdl_expr_seq*, _PyPegen_get_pattern_keys(p, keywords)))),
363+
CHECK(asdl_pattern_seq*, _PyPegen_get_patterns(p, keywords)),
364+
EXTRA) }
365+
| cls=name_or_attr '(' patterns=positional_patterns ',' keywords=keyword_patterns ','? ')' {
366+
_PyAST_MatchClass(
367+
cls,
368+
patterns,
369+
CHECK(asdl_identifier_seq*, _PyPegen_map_names_to_ids(p,
370+
CHECK(asdl_expr_seq*, _PyPegen_get_pattern_keys(p, keywords)))),
371+
CHECK(asdl_pattern_seq*, _PyPegen_get_patterns(p, keywords)),
372+
EXTRA) }
373+
positional_patterns[asdl_pattern_seq*]:
374+
| args[asdl_pattern_seq*]=','.pattern+ { args }
375+
keyword_patterns[asdl_seq*]:
376+
| keywords[asdl_seq*]=','.keyword_pattern+ { keywords }
377+
keyword_pattern[KeyPatternPair*]:
378+
| arg=NAME '=' value=pattern { _PyPegen_key_pattern_pair(p, arg, value) }
332379

333380
return_stmt[stmt_ty]:
334381
| 'return' a=[star_expressions] { _PyAST_Return(a, EXTRA) }

Include/internal/pycore_ast.h

+89-19
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)