Skip to content

Commit 145bf26

Browse files
brandtbuchergvanrossumviridiapablogsal
authored
bpo-42128: Structural Pattern Matching (PEP 634) (GH-22917)
Co-authored-by: Guido van Rossum <[email protected]> Co-authored-by: Talin <[email protected]> Co-authored-by: Pablo Galindo <[email protected]>
1 parent cc02b4f commit 145bf26

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+16242
-7982
lines changed

Doc/library/dis.rst

+56
Original file line numberDiff line numberDiff line change
@@ -752,6 +752,49 @@ iterations of the loop.
752752
.. versionadded:: 3.2
753753

754754

755+
.. opcode:: COPY_DICT_WITHOUT_KEYS
756+
757+
TOS is a tuple of mapping keys, and TOS1 is the match subject. Replace TOS
758+
with a :class:`dict` formed from the items of TOS1, but without any of the
759+
keys in TOS.
760+
761+
.. versionadded:: 3.10
762+
763+
764+
.. opcode:: GET_LEN
765+
766+
Push ``len(TOS)`` onto the stack.
767+
768+
.. versionadded:: 3.10
769+
770+
771+
.. opcode:: MATCH_MAPPING
772+
773+
If TOS is an instance of :class:`collections.abc.Mapping`, push ``True`` onto
774+
the stack. Otherwise, push ``False``.
775+
776+
.. versionadded:: 3.10
777+
778+
779+
.. opcode:: MATCH_SEQUENCE
780+
781+
If TOS is an instance of :class:`collections.abc.Sequence` and is *not* an
782+
instance of :class:`str`/:class:`bytes`/:class:`bytearray`, push ``True``
783+
onto the stack. Otherwise, push ``False``.
784+
785+
.. versionadded:: 3.10
786+
787+
788+
.. opcode:: MATCH_KEYS
789+
790+
TOS is a tuple of mapping keys, and TOS1 is the match subject. If TOS1
791+
contains all of the keys in TOS, push a :class:`tuple` containing the
792+
corresponding values, followed by ``True``. Otherwise, push ``None``,
793+
followed by ``False``.
794+
795+
.. versionadded:: 3.10
796+
797+
755798
All of the following opcodes use their arguments.
756799

757800
.. opcode:: STORE_NAME (namei)
@@ -1192,6 +1235,19 @@ All of the following opcodes use their arguments.
11921235
.. versionadded:: 3.6
11931236

11941237

1238+
.. opcode:: MATCH_CLASS (count)
1239+
1240+
TOS is a tuple of keyword attribute names, TOS1 is the class being matched
1241+
against, and TOS2 is the match subject. *count* is the number of positional
1242+
sub-patterns.
1243+
1244+
Pop TOS. If TOS2 is an instance of TOS1 and has the positional and keyword
1245+
attributes required by *count* and TOS, set TOS to ``True`` and TOS1 to a
1246+
tuple of extracted attributes. Otherwise, set TOS to ``False``.
1247+
1248+
.. versionadded:: 3.10
1249+
1250+
11951251
.. opcode:: HAVE_ARGUMENT
11961252

11971253
This is not really an opcode. It identifies the dividing line between

Doc/tools/susp-ignored.csv

+1
Original file line numberDiff line numberDiff line change
@@ -365,3 +365,4 @@ whatsnew/changelog,,::,default::DeprecationWarning
365365
library/importlib.metadata,,:main,"EntryPoint(name='wheel', value='wheel.cli:main', group='console_scripts')"
366366
library/importlib.metadata,,`,loading the metadata for packages for the indicated ``context``.
367367
library/re,,`,"`"
368+
library/dis,,:TOS1,TOS[x:TOS1 - 1 - y]

Grammar/python.gram

+110-1
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ compound_stmt[stmt_ty]:
8585
| &('for' | ASYNC) for_stmt
8686
| &'try' try_stmt
8787
| &'while' while_stmt
88+
| match_stmt
8889

8990
# NOTE: annotated_rhs may start with 'yield'; yield_expr must start with 'yield'
9091
assignment[stmt_ty]:
@@ -207,6 +208,114 @@ except_block[excepthandler_ty]:
207208
| invalid_except_block
208209
finally_block[asdl_stmt_seq*]: 'finally' ':' a=block { a }
209210

211+
match_stmt[stmt_ty]:
212+
| "match" subject=subject_expr ':' NEWLINE INDENT cases[asdl_match_case_seq*]=case_block+ DEDENT {
213+
CHECK_VERSION(stmt_ty, 10, "Pattern matching is", _Py_Match(subject, cases, EXTRA)) }
214+
subject_expr[expr_ty]:
215+
| value=star_named_expression ',' values=star_named_expressions? {
216+
_Py_Tuple(CHECK(asdl_expr_seq*, _PyPegen_seq_insert_in_front(p, value, values)), Load, EXTRA) }
217+
| named_expression
218+
case_block[match_case_ty]:
219+
| "case" pattern=patterns guard=guard? ':' body=block {
220+
_Py_match_case(pattern, guard, body, p->arena) }
221+
guard[expr_ty]: 'if' guard=named_expression { guard }
222+
223+
patterns[expr_ty]:
224+
| values[asdl_expr_seq*]=open_sequence_pattern {
225+
_Py_Tuple(values, Load, EXTRA) }
226+
| pattern
227+
pattern[expr_ty]:
228+
| as_pattern
229+
| or_pattern
230+
as_pattern[expr_ty]:
231+
| pattern=or_pattern 'as' target=capture_pattern {
232+
_Py_MatchAs(pattern, target->v.Name.id, EXTRA) }
233+
or_pattern[expr_ty]:
234+
| patterns[asdl_expr_seq*]='|'.closed_pattern+ {
235+
asdl_seq_LEN(patterns) == 1 ? asdl_seq_GET(patterns, 0) : _Py_MatchOr(patterns, EXTRA) }
236+
closed_pattern[expr_ty]:
237+
| literal_pattern
238+
| capture_pattern
239+
| wildcard_pattern
240+
| value_pattern
241+
| group_pattern
242+
| sequence_pattern
243+
| mapping_pattern
244+
| class_pattern
245+
246+
literal_pattern[expr_ty]:
247+
| signed_number !('+' | '-')
248+
| real=signed_number '+' imag=NUMBER { _Py_BinOp(real, Add, imag, EXTRA) }
249+
| real=signed_number '-' imag=NUMBER { _Py_BinOp(real, Sub, imag, EXTRA) }
250+
| strings
251+
| 'None' { _Py_Constant(Py_None, NULL, EXTRA) }
252+
| 'True' { _Py_Constant(Py_True, NULL, EXTRA) }
253+
| 'False' { _Py_Constant(Py_False, NULL, EXTRA) }
254+
signed_number[expr_ty]:
255+
| NUMBER
256+
| '-' number=NUMBER { _Py_UnaryOp(USub, number, EXTRA) }
257+
258+
capture_pattern[expr_ty]:
259+
| !"_" name=NAME !('.' | '(' | '=') {
260+
_PyPegen_set_expr_context(p, name, Store) }
261+
262+
wildcard_pattern[expr_ty]:
263+
| "_" { _Py_Name(CHECK(PyObject*, _PyPegen_new_identifier(p, "_")), Store, EXTRA) }
264+
265+
value_pattern[expr_ty]:
266+
| attr=attr !('.' | '(' | '=') { attr }
267+
attr[expr_ty]:
268+
| value=name_or_attr '.' attr=NAME {
269+
_Py_Attribute(value, attr->v.Name.id, Load, EXTRA) }
270+
name_or_attr[expr_ty]:
271+
| attr
272+
| NAME
273+
274+
group_pattern[expr_ty]:
275+
| '(' pattern=pattern ')' { pattern }
276+
277+
sequence_pattern[expr_ty]:
278+
| '[' values=maybe_sequence_pattern? ']' { _Py_List(values, Load, EXTRA) }
279+
| '(' values=open_sequence_pattern? ')' { _Py_Tuple(values, Load, EXTRA) }
280+
open_sequence_pattern[asdl_seq*]:
281+
| value=maybe_star_pattern ',' values=maybe_sequence_pattern? {
282+
_PyPegen_seq_insert_in_front(p, value, values) }
283+
maybe_sequence_pattern[asdl_seq*]:
284+
| values=','.maybe_star_pattern+ ','? { values }
285+
maybe_star_pattern[expr_ty]:
286+
| star_pattern
287+
| pattern
288+
star_pattern[expr_ty]:
289+
| '*' value=(capture_pattern | wildcard_pattern) {
290+
_Py_Starred(value, Store, EXTRA) }
291+
292+
mapping_pattern[expr_ty]:
293+
| '{' items=items_pattern? '}' {
294+
_Py_Dict(CHECK(asdl_expr_seq*, _PyPegen_get_keys(p, items)), CHECK(asdl_expr_seq*, _PyPegen_get_values(p, items)), EXTRA) }
295+
items_pattern[asdl_seq*]:
296+
| items=','.key_value_pattern+ ','? { items }
297+
key_value_pattern[KeyValuePair*]:
298+
| key=(literal_pattern | value_pattern) ':' value=pattern {
299+
_PyPegen_key_value_pair(p, key, value) }
300+
| double_star_pattern
301+
double_star_pattern[KeyValuePair*]:
302+
| '**' value=capture_pattern { _PyPegen_key_value_pair(p, NULL, value) }
303+
304+
class_pattern[expr_ty]:
305+
| func=name_or_attr '(' ')' { _Py_Call(func, NULL, NULL, EXTRA) }
306+
| func=name_or_attr '(' args=positional_patterns ','? ')' {
307+
_Py_Call(func, args, NULL, EXTRA) }
308+
| func=name_or_attr '(' keywords=keyword_patterns ','? ')' {
309+
_Py_Call(func, NULL, keywords, EXTRA) }
310+
| func=name_or_attr '(' args=positional_patterns ',' keywords=keyword_patterns ','? ')' {
311+
_Py_Call(func, args, keywords, EXTRA) }
312+
positional_patterns[asdl_expr_seq*]:
313+
| args[asdl_expr_seq*]=','.pattern+ { args }
314+
keyword_patterns[asdl_keyword_seq*]:
315+
| keywords[asdl_keyword_seq*]=','.keyword_pattern+ { keywords }
316+
keyword_pattern[keyword_ty]:
317+
| arg=NAME '=' value=pattern { _Py_keyword(arg->v.Name.id, value, EXTRA) }
318+
210319
return_stmt[stmt_ty]:
211320
| 'return' a=[star_expressions] { _Py_Return(a, EXTRA) }
212321

@@ -676,7 +785,7 @@ invalid_assignment:
676785
RAISE_SYNTAX_ERROR_INVALID_TARGET(STAR_TARGETS, a) }
677786
| (star_targets '=')* a=yield_expr '=' { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "assignment to yield expression not possible") }
678787
| a=star_expressions augassign (yield_expr | star_expressions) {
679-
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(
788+
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(
680789
a,
681790
"'%s' is an illegal expression for augmented assignment",
682791
_PyPegen_get_expr_name(a)

Include/Python-ast.h

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

Include/internal/pycore_ast.h

+10
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,9 @@ struct ast_state {
9191
PyObject *Lt_type;
9292
PyObject *MatMult_singleton;
9393
PyObject *MatMult_type;
94+
PyObject *MatchAs_type;
95+
PyObject *MatchOr_type;
96+
PyObject *Match_type;
9497
PyObject *Mod_singleton;
9598
PyObject *Mod_type;
9699
PyObject *Module_type;
@@ -137,6 +140,7 @@ struct ast_state {
137140
PyObject *Yield_type;
138141
PyObject *__dict__;
139142
PyObject *__doc__;
143+
PyObject *__match_args__;
140144
PyObject *__module__;
141145
PyObject *_attributes;
142146
PyObject *_fields;
@@ -153,6 +157,7 @@ struct ast_state {
153157
PyObject *bases;
154158
PyObject *body;
155159
PyObject *boolop_type;
160+
PyObject *cases;
156161
PyObject *cause;
157162
PyObject *cmpop_type;
158163
PyObject *col_offset;
@@ -175,6 +180,7 @@ struct ast_state {
175180
PyObject *format_spec;
176181
PyObject *func;
177182
PyObject *generators;
183+
PyObject *guard;
178184
PyObject *handlers;
179185
PyObject *id;
180186
PyObject *ifs;
@@ -193,6 +199,7 @@ struct ast_state {
193199
PyObject *level;
194200
PyObject *lineno;
195201
PyObject *lower;
202+
PyObject *match_case_type;
196203
PyObject *mod_type;
197204
PyObject *module;
198205
PyObject *msg;
@@ -204,13 +211,16 @@ struct ast_state {
204211
PyObject *ops;
205212
PyObject *optional_vars;
206213
PyObject *orelse;
214+
PyObject *pattern;
215+
PyObject *patterns;
207216
PyObject *posonlyargs;
208217
PyObject *returns;
209218
PyObject *right;
210219
PyObject *simple;
211220
PyObject *slice;
212221
PyObject *step;
213222
PyObject *stmt_type;
223+
PyObject *subject;
214224
PyObject *tag;
215225
PyObject *target;
216226
PyObject *targets;

0 commit comments

Comments
 (0)