bpo-43892: Make match patterns explicit in the AST (GH-25585)

Co-authored-by: Brandt Bucher <brandtbucher@gmail.com>
This commit is contained in:
Nick Coghlan 2021-04-29 15:58:44 +10:00 committed by GitHub
parent e52ab42ced
commit 1e7b858575
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 3460 additions and 1377 deletions

View File

@ -1245,23 +1245,28 @@ Control flow
type_ignores=[])
Pattern matching
^^^^^^^^^^^^^^^^
.. class:: Match(subject, cases)
A ``match`` statement. ``subject`` holds the subject of the match (the object
that is being matched against the cases) and ``cases`` contains an iterable of
:class:`match_case` nodes with the different cases.
.. class:: match_case(pattern, guard, body)
A single case pattern in a ``match`` statement. ``pattern`` contains the
match pattern that will be used to match the subject against. Notice that
the meaning of the :class:`AST` nodes in this attribute have a different
meaning than in other places, as they represent patterns to match against.
The ``guard`` attribute contains an expression that will be evaluated if
the pattern matches the subject. If the pattern matches and the ``guard`` condition
is truthy, the body of the case shall be executed. ``body`` contains a list
of nodes to execute if the guard is truthy.
A single case pattern in a ``match`` statement. ``pattern`` contains the
match pattern that the subject will be matched against. Note that the
:class:`AST` nodes produced for patterns differ from those produced for
expressions, even when they share the same syntax.
The ``guard`` attribute contains an expression that will be evaluated if
the pattern matches the subject.
``body`` contains a list of nodes to execute if the pattern matches and
the result of evaluating the guard expression is truthy.
.. doctest::
@ -1278,10 +1283,9 @@ Control flow
subject=Name(id='x', ctx=Load()),
cases=[
match_case(
pattern=List(
elts=[
Name(id='x', ctx=Store())],
ctx=Load()),
pattern=MatchSequence(
patterns=[
MatchAs(name='x')]),
guard=Compare(
left=Name(id='x', ctx=Load()),
ops=[
@ -1292,10 +1296,244 @@ Control flow
Expr(
value=Constant(value=Ellipsis))]),
match_case(
pattern=Call(
func=Name(id='tuple', ctx=Load()),
args=[],
keywords=[]),
pattern=MatchClass(
cls=Name(id='tuple', ctx=Load()),
patterns=[],
kwd_attrs=[],
kwd_patterns=[]),
body=[
Expr(
value=Constant(value=Ellipsis))])])],
type_ignores=[])
.. class:: MatchValue(value)
A match literal or value pattern that compares by equality. ``value`` is
an expression node. Permitted value nodes are restricted as described in
the match statement documentation. This pattern succeeds if the match
subject is equal to the evaluated value.
.. doctest::
>>> print(ast.dump(ast.parse("""
... match x:
... case "Relevant":
... ...
... """), indent=4))
Module(
body=[
Match(
subject=Name(id='x', ctx=Load()),
cases=[
match_case(
pattern=MatchValue(
value=Constant(value='Relevant')),
body=[
Expr(
value=Constant(value=Ellipsis))])])],
type_ignores=[])
.. class:: MatchSingleton(value)
A match literal pattern that compares by identity. ``value`` is the
singleton to be compared against: ``None``, ``True``, or ``False``. This
pattern succeeds if the match subject is the given constant.
.. doctest::
>>> print(ast.dump(ast.parse("""
... match x:
... case None:
... ...
... """), indent=4))
Module(
body=[
Match(
subject=Name(id='x', ctx=Load()),
cases=[
match_case(
pattern=MatchSingleton(value=None),
body=[
Expr(
value=Constant(value=Ellipsis))])])],
type_ignores=[])
.. class:: MatchSequence(patterns)
A match sequence pattern. ``patterns`` contains the patterns to be matched
against the subject elements if the subject is a sequence. Matches a variable
length sequence if one of the subpatterns is a ``MatchStar`` node, otherwise
matches a fixed length sequence.
.. doctest::
>>> print(ast.dump(ast.parse("""
... match x:
... case [1, 2]:
... ...
... """), indent=4))
Module(
body=[
Match(
subject=Name(id='x', ctx=Load()),
cases=[
match_case(
pattern=MatchSequence(
patterns=[
MatchValue(
value=Constant(value=1)),
MatchValue(
value=Constant(value=2))]),
body=[
Expr(
value=Constant(value=Ellipsis))])])],
type_ignores=[])
.. class:: MatchStar(name)
Matches the rest of the sequence in a variable length match sequence pattern.
If ``name`` is not ``None``, a list containing the remaining sequence
elements is bound to that name if the overall sequence pattern is successful.
.. doctest::
>>> print(ast.dump(ast.parse("""
... match x:
... case [1, 2, *rest]:
... ...
... case [*_]:
... ...
... """), indent=4))
Module(
body=[
Match(
subject=Name(id='x', ctx=Load()),
cases=[
match_case(
pattern=MatchSequence(
patterns=[
MatchValue(
value=Constant(value=1)),
MatchValue(
value=Constant(value=2)),
MatchStar(name='rest')]),
body=[
Expr(
value=Constant(value=Ellipsis))]),
match_case(
pattern=MatchSequence(
patterns=[
MatchStar()]),
body=[
Expr(
value=Constant(value=Ellipsis))])])],
type_ignores=[])
.. class:: MatchMapping(keys, patterns, rest)
A match mapping pattern. ``keys`` is a sequence of expression nodes.
``patterns`` is a corresponding sequence of pattern nodes. ``rest`` is an
optional name that can be specified to capture the remaining mapping elements.
Permitted key expressions are restricted as described in the match statement
documentation.
This pattern succeeds if the subject is a mapping, all evaluated key
expressions are present in the mapping, and the value corresponding to each
key matches the corresponding subpattern. If ``rest`` is not ``None``, a dict
containing the remaining mapping elements is bound to that name if the overall
mapping pattern is successful.
.. doctest::
>>> print(ast.dump(ast.parse("""
... match x:
... case {1: _, 2: _}:
... ...
... case {**rest}:
... ...
... """), indent=4))
Module(
body=[
Match(
subject=Name(id='x', ctx=Load()),
cases=[
match_case(
pattern=MatchMapping(
keys=[
Constant(value=1),
Constant(value=2)],
patterns=[
MatchAs(),
MatchAs()]),
body=[
Expr(
value=Constant(value=Ellipsis))]),
match_case(
pattern=MatchMapping(keys=[], patterns=[], rest='rest'),
body=[
Expr(
value=Constant(value=Ellipsis))])])],
type_ignores=[])
.. class:: MatchClass(cls, patterns, kwd_attrs, kwd_patterns)
A match class pattern. ``cls`` is an expression giving the nominal class to
be matched. ``patterns`` is a sequence of pattern nodes to be matched against
the class defined sequence of pattern matching attributes. ``kwd_attrs`` is a
sequence of additional attributes to be matched (specified as keyword arguments
in the class pattern), ``kwd_patterns`` are the corresponding patterns
(specified as keyword values in the class pattern).
This pattern succeeds if the subject is an instance of the nominated class,
all positional patterns match the corresponding class-defined attributes, and
any specified keyword attributes match their corresponding pattern.
Note: classes may define a property that returns self in order to match a
pattern node against the instance being matched. Several builtin types are
also matched that way, as described in the match statement documentation.
.. doctest::
>>> print(ast.dump(ast.parse("""
... match x:
... case Point2D(0, 0):
... ...
... case Point3D(x=0, y=0, z=0):
... ...
... """), indent=4))
Module(
body=[
Match(
subject=Name(id='x', ctx=Load()),
cases=[
match_case(
pattern=MatchClass(
cls=Name(id='Point2D', ctx=Load()),
patterns=[
MatchValue(
value=Constant(value=0)),
MatchValue(
value=Constant(value=0))],
kwd_attrs=[],
kwd_patterns=[]),
body=[
Expr(
value=Constant(value=Ellipsis))]),
match_case(
pattern=MatchClass(
cls=Name(id='Point3D', ctx=Load()),
patterns=[],
kwd_attrs=[
'x',
'y',
'z'],
kwd_patterns=[
MatchValue(
value=Constant(value=0)),
MatchValue(
value=Constant(value=0)),
MatchValue(
value=Constant(value=0))]),
body=[
Expr(
value=Constant(value=Ellipsis))])])],
@ -1303,10 +1541,14 @@ Control flow
.. class:: MatchAs(pattern, name)
A match "as-pattern". The as-pattern matches whatever pattern is on its
left-hand side, but also binds the value to a name. ``pattern`` contains
the match pattern that will be used to match the subject agsinst. The ``name``
attribute contains the name that will be binded if the pattern is successful.
A match "as-pattern", capture pattern or wildcard pattern. ``pattern``
contains the match pattern that the subject will be matched against.
If the pattern is ``None``, the node represents a capture pattern (i.e a
bare name) and will always succeed.
The ``name`` attribute contains the name that will be bound if the pattern
is successful. If ``name`` is ``None``, ``pattern`` must also be ``None``
and the node represents the wildcard pattern.
.. doctest::
@ -1314,6 +1556,8 @@ Control flow
... match x:
... case [x] as y:
... ...
... case _:
... ...
... """), indent=4))
Module(
body=[
@ -1322,24 +1566,27 @@ Control flow
cases=[
match_case(
pattern=MatchAs(
pattern=List(
elts=[
Name(id='x', ctx=Store())],
ctx=Load()),
pattern=MatchSequence(
patterns=[
MatchAs(name='x')]),
name='y'),
body=[
Expr(
value=Constant(value=Ellipsis))]),
match_case(
pattern=MatchAs(),
body=[
Expr(
value=Constant(value=Ellipsis))])])],
type_ignores=[])
.. class:: MatchOr(patterns)
A match "or-pattern". An or-pattern matches each of its subpatterns in turn
to the subject, until one succeeds. The or-pattern is then deemed to
succeed. If none of the subpatterns succeed the or-pattern fails. The
``patterns`` attribute contains a list of match patterns nodes that will be
matched against the subject.
A match "or-pattern". An or-pattern matches each of its subpatterns in turn
to the subject, until one succeeds. The or-pattern is then deemed to
succeed. If none of the subpatterns succeed the or-pattern fails. The
``patterns`` attribute contains a list of match pattern nodes that will be
matched against the subject.
.. doctest::
@ -1356,11 +1603,10 @@ Control flow
match_case(
pattern=MatchOr(
patterns=[
List(
elts=[
Name(id='x', ctx=Store())],
ctx=Load()),
Name(id='y', ctx=Store())]),
MatchSequence(
patterns=[
MatchAs(name='x')]),
MatchAs(name='y')]),
body=[
Expr(
value=Constant(value=Ellipsis))])])],

View File

@ -234,20 +234,20 @@ case_block[match_case_ty]:
_PyAST_match_case(pattern, guard, body, p->arena) }
guard[expr_ty]: 'if' guard=named_expression { guard }
patterns[expr_ty]:
| values[asdl_expr_seq*]=open_sequence_pattern {
_PyAST_Tuple(values, Load, EXTRA) }
patterns[pattern_ty]:
| patterns[asdl_pattern_seq*]=open_sequence_pattern {
_PyAST_MatchSequence(patterns, EXTRA) }
| pattern
pattern[expr_ty]:
pattern[pattern_ty]:
| as_pattern
| or_pattern
as_pattern[expr_ty]:
| pattern=or_pattern 'as' target=capture_pattern {
as_pattern[pattern_ty]:
| pattern=or_pattern 'as' target=pattern_capture_target {
_PyAST_MatchAs(pattern, target->v.Name.id, EXTRA) }
or_pattern[expr_ty]:
| patterns[asdl_expr_seq*]='|'.closed_pattern+ {
or_pattern[pattern_ty]:
| patterns[asdl_pattern_seq*]='|'.closed_pattern+ {
asdl_seq_LEN(patterns) == 1 ? asdl_seq_GET(patterns, 0) : _PyAST_MatchOr(patterns, EXTRA) }
closed_pattern[expr_ty]:
closed_pattern[pattern_ty]:
| literal_pattern
| capture_pattern
| wildcard_pattern
@ -257,27 +257,47 @@ closed_pattern[expr_ty]:
| mapping_pattern
| class_pattern
literal_pattern[expr_ty]:
# Literal patterns are used for equality and identity constraints
literal_pattern[pattern_ty]:
| value=signed_number !('+' | '-') { _PyAST_MatchValue(value, EXTRA) }
| value=complex_number { _PyAST_MatchValue(value, EXTRA) }
| value=strings { _PyAST_MatchValue(value, EXTRA) }
| 'None' { _PyAST_MatchSingleton(Py_None, EXTRA) }
| 'True' { _PyAST_MatchSingleton(Py_True, EXTRA) }
| 'False' { _PyAST_MatchSingleton(Py_False, EXTRA) }
# Literal expressions are used to restrict permitted mapping pattern keys
literal_expr[expr_ty]:
| signed_number !('+' | '-')
| real=signed_number '+' imag=NUMBER { _PyAST_BinOp(real, Add, imag, EXTRA) }
| real=signed_number '-' imag=NUMBER { _PyAST_BinOp(real, Sub, imag, EXTRA) }
| complex_number
| strings
| 'None' { _PyAST_Constant(Py_None, NULL, EXTRA) }
| 'True' { _PyAST_Constant(Py_True, NULL, EXTRA) }
| 'False' { _PyAST_Constant(Py_False, NULL, EXTRA) }
complex_number[expr_ty]:
| real=signed_number '+' imag=imaginary_number { _PyAST_BinOp(real, Add, imag, EXTRA) }
| real=signed_number '-' imag=imaginary_number { _PyAST_BinOp(real, Sub, imag, EXTRA) }
signed_number[expr_ty]:
| NUMBER
| '-' number=NUMBER { _PyAST_UnaryOp(USub, number, EXTRA) }
capture_pattern[expr_ty]:
imaginary_number[expr_ty]:
| imag=NUMBER { _PyPegen_ensure_imaginary(p, imag) }
capture_pattern[pattern_ty]:
| target=pattern_capture_target { _PyAST_MatchAs(NULL, target->v.Name.id, EXTRA) }
pattern_capture_target[expr_ty]:
| !"_" name=NAME !('.' | '(' | '=') {
_PyPegen_set_expr_context(p, name, Store) }
wildcard_pattern[expr_ty]:
| "_" { _PyAST_Name(CHECK(PyObject*, _PyPegen_new_identifier(p, "_")), Store, EXTRA) }
wildcard_pattern[pattern_ty]:
| "_" { _PyAST_MatchAs(NULL, NULL, EXTRA) }
value_pattern[expr_ty]:
| attr=attr !('.' | '(' | '=') { attr }
value_pattern[pattern_ty]:
| attr=attr !('.' | '(' | '=') { _PyAST_MatchValue(attr, EXTRA) }
attr[expr_ty]:
| value=name_or_attr '.' attr=NAME {
_PyAST_Attribute(value, attr->v.Name.id, Load, EXTRA) }
@ -285,50 +305,77 @@ name_or_attr[expr_ty]:
| attr
| NAME
group_pattern[expr_ty]:
group_pattern[pattern_ty]:
| '(' pattern=pattern ')' { pattern }
sequence_pattern[expr_ty]:
| '[' values=maybe_sequence_pattern? ']' { _PyAST_List(values, Load, EXTRA) }
| '(' values=open_sequence_pattern? ')' { _PyAST_Tuple(values, Load, EXTRA) }
sequence_pattern[pattern_ty]:
| '[' patterns=maybe_sequence_pattern? ']' { _PyAST_MatchSequence(patterns, EXTRA) }
| '(' patterns=open_sequence_pattern? ')' { _PyAST_MatchSequence(patterns, EXTRA) }
open_sequence_pattern[asdl_seq*]:
| value=maybe_star_pattern ',' values=maybe_sequence_pattern? {
_PyPegen_seq_insert_in_front(p, value, values) }
| pattern=maybe_star_pattern ',' patterns=maybe_sequence_pattern? {
_PyPegen_seq_insert_in_front(p, pattern, patterns) }
maybe_sequence_pattern[asdl_seq*]:
| values=','.maybe_star_pattern+ ','? { values }
maybe_star_pattern[expr_ty]:
| patterns=','.maybe_star_pattern+ ','? { patterns }
maybe_star_pattern[pattern_ty]:
| star_pattern
| pattern
star_pattern[expr_ty]:
| '*' value=(capture_pattern | wildcard_pattern) {
_PyAST_Starred(value, Store, EXTRA) }
star_pattern[pattern_ty]:
| '*' target=pattern_capture_target {
_PyAST_MatchStar(target->v.Name.id, EXTRA) }
| '*' wildcard_pattern {
_PyAST_MatchStar(NULL, EXTRA) }
mapping_pattern[expr_ty]:
| '{' items=items_pattern? '}' {
_PyAST_Dict(CHECK(asdl_expr_seq*, _PyPegen_get_keys(p, items)), CHECK(asdl_expr_seq*, _PyPegen_get_values(p, items)), EXTRA) }
mapping_pattern[pattern_ty]:
| '{' '}' {
_PyAST_MatchMapping(NULL, NULL, NULL, EXTRA) }
| '{' rest=double_star_pattern ','? '}' {
_PyAST_MatchMapping(NULL, NULL, rest->v.Name.id, EXTRA) }
| '{' items=items_pattern ',' rest=double_star_pattern ','? '}' {
_PyAST_MatchMapping(
CHECK(asdl_expr_seq*, _PyPegen_get_pattern_keys(p, items)),
CHECK(asdl_pattern_seq*, _PyPegen_get_patterns(p, items)),
rest->v.Name.id,
EXTRA) }
| '{' items=items_pattern ','? '}' {
_PyAST_MatchMapping(
CHECK(asdl_expr_seq*, _PyPegen_get_pattern_keys(p, items)),
CHECK(asdl_pattern_seq*, _PyPegen_get_patterns(p, items)),
NULL,
EXTRA) }
items_pattern[asdl_seq*]:
| items=','.key_value_pattern+ ','? { items }
key_value_pattern[KeyValuePair*]:
| key=(literal_pattern | value_pattern) ':' value=pattern {
_PyPegen_key_value_pair(p, key, value) }
| double_star_pattern
double_star_pattern[KeyValuePair*]:
| '**' value=capture_pattern { _PyPegen_key_value_pair(p, NULL, value) }
| items=','.key_value_pattern+ { items }
key_value_pattern[KeyPatternPair*]:
| key=(literal_expr | attr) ':' pattern=pattern {
_PyPegen_key_pattern_pair(p, key, pattern) }
double_star_pattern[expr_ty]:
| '**' target=pattern_capture_target { target }
class_pattern[expr_ty]:
| func=name_or_attr '(' ')' { _PyAST_Call(func, NULL, NULL, EXTRA) }
| func=name_or_attr '(' args=positional_patterns ','? ')' {
_PyAST_Call(func, args, NULL, EXTRA) }
| func=name_or_attr '(' keywords=keyword_patterns ','? ')' {
_PyAST_Call(func, NULL, keywords, EXTRA) }
| func=name_or_attr '(' args=positional_patterns ',' keywords=keyword_patterns ','? ')' {
_PyAST_Call(func, args, keywords, EXTRA) }
positional_patterns[asdl_expr_seq*]:
| args[asdl_expr_seq*]=','.pattern+ { args }
keyword_patterns[asdl_keyword_seq*]:
| keywords[asdl_keyword_seq*]=','.keyword_pattern+ { keywords }
keyword_pattern[keyword_ty]:
| arg=NAME '=' value=pattern { _PyAST_keyword(arg->v.Name.id, value, EXTRA) }
class_pattern[pattern_ty]:
| cls=name_or_attr '(' ')' {
_PyAST_MatchClass(cls, NULL, NULL, NULL, EXTRA) }
| cls=name_or_attr '(' patterns=positional_patterns ','? ')' {
_PyAST_MatchClass(cls, patterns, NULL, NULL, EXTRA) }
| cls=name_or_attr '(' keywords=keyword_patterns ','? ')' {
_PyAST_MatchClass(
cls, NULL,
CHECK(asdl_identifier_seq*, _PyPegen_map_names_to_ids(p,
CHECK(asdl_expr_seq*, _PyPegen_get_pattern_keys(p, keywords)))),
CHECK(asdl_pattern_seq*, _PyPegen_get_patterns(p, keywords)),
EXTRA) }
| cls=name_or_attr '(' patterns=positional_patterns ',' keywords=keyword_patterns ','? ')' {
_PyAST_MatchClass(
cls,
patterns,
CHECK(asdl_identifier_seq*, _PyPegen_map_names_to_ids(p,
CHECK(asdl_expr_seq*, _PyPegen_get_pattern_keys(p, keywords)))),
CHECK(asdl_pattern_seq*, _PyPegen_get_patterns(p, keywords)),
EXTRA) }
positional_patterns[asdl_pattern_seq*]:
| args[asdl_pattern_seq*]=','.pattern+ { args }
keyword_patterns[asdl_seq*]:
| keywords[asdl_seq*]=','.keyword_pattern+ { keywords }
keyword_pattern[KeyPatternPair*]:
| arg=NAME '=' value=pattern { _PyPegen_key_pattern_pair(p, arg, value) }
return_stmt[stmt_ty]:
| 'return' a=[star_expressions] { _PyAST_Return(a, EXTRA) }

View File

@ -47,6 +47,8 @@ typedef struct _withitem *withitem_ty;
typedef struct _match_case *match_case_ty;
typedef struct _pattern *pattern_ty;
typedef struct _type_ignore *type_ignore_ty;
@ -130,6 +132,13 @@ typedef struct {
asdl_match_case_seq *_Py_asdl_match_case_seq_new(Py_ssize_t size, PyArena
*arena);
typedef struct {
_ASDL_SEQ_HEAD
pattern_ty typed_elements[1];
} asdl_pattern_seq;
asdl_pattern_seq *_Py_asdl_pattern_seq_new(Py_ssize_t size, PyArena *arena);
typedef struct {
_ASDL_SEQ_HEAD
type_ignore_ty typed_elements[1];
@ -327,8 +336,7 @@ enum _expr_kind {BoolOp_kind=1, NamedExpr_kind=2, BinOp_kind=3, UnaryOp_kind=4,
YieldFrom_kind=15, Compare_kind=16, Call_kind=17,
FormattedValue_kind=18, JoinedStr_kind=19, Constant_kind=20,
Attribute_kind=21, Subscript_kind=22, Starred_kind=23,
Name_kind=24, List_kind=25, Tuple_kind=26, Slice_kind=27,
MatchAs_kind=28, MatchOr_kind=29};
Name_kind=24, List_kind=25, Tuple_kind=26, Slice_kind=27};
struct _expr {
enum _expr_kind kind;
union {
@ -471,15 +479,6 @@ struct _expr {
expr_ty step;
} Slice;
struct {
expr_ty pattern;
identifier name;
} MatchAs;
struct {
asdl_expr_seq *patterns;
} MatchOr;
} v;
int lineno;
int col_offset;
@ -555,11 +554,63 @@ struct _withitem {
};
struct _match_case {
expr_ty pattern;
pattern_ty pattern;
expr_ty guard;
asdl_stmt_seq *body;
};
enum _pattern_kind {MatchValue_kind=1, MatchSingleton_kind=2,
MatchSequence_kind=3, MatchMapping_kind=4,
MatchClass_kind=5, MatchStar_kind=6, MatchAs_kind=7,
MatchOr_kind=8};
struct _pattern {
enum _pattern_kind kind;
union {
struct {
expr_ty value;
} MatchValue;
struct {
constant value;
} MatchSingleton;
struct {
asdl_pattern_seq *patterns;
} MatchSequence;
struct {
asdl_expr_seq *keys;
asdl_pattern_seq *patterns;
identifier rest;
} MatchMapping;
struct {
expr_ty cls;
asdl_pattern_seq *patterns;
asdl_identifier_seq *kwd_attrs;
asdl_pattern_seq *kwd_patterns;
} MatchClass;
struct {
identifier name;
} MatchStar;
struct {
pattern_ty pattern;
identifier name;
} MatchAs;
struct {
asdl_pattern_seq *patterns;
} MatchOr;
} v;
int lineno;
int col_offset;
int end_lineno;
int end_col_offset;
};
enum _type_ignore_kind {TypeIgnore_kind=1};
struct _type_ignore {
enum _type_ignore_kind kind;
@ -733,11 +784,6 @@ expr_ty _PyAST_Tuple(asdl_expr_seq * elts, expr_context_ty ctx, int lineno, int
expr_ty _PyAST_Slice(expr_ty lower, expr_ty upper, expr_ty step, int lineno,
int col_offset, int end_lineno, int end_col_offset,
PyArena *arena);
expr_ty _PyAST_MatchAs(expr_ty pattern, identifier name, int lineno, int
col_offset, int end_lineno, int end_col_offset, PyArena
*arena);
expr_ty _PyAST_MatchOr(asdl_expr_seq * patterns, int lineno, int col_offset,
int end_lineno, int end_col_offset, PyArena *arena);
comprehension_ty _PyAST_comprehension(expr_ty target, expr_ty iter,
asdl_expr_seq * ifs, int is_async,
PyArena *arena);
@ -760,8 +806,32 @@ alias_ty _PyAST_alias(identifier name, identifier asname, int lineno, int
*arena);
withitem_ty _PyAST_withitem(expr_ty context_expr, expr_ty optional_vars,
PyArena *arena);
match_case_ty _PyAST_match_case(expr_ty pattern, expr_ty guard, asdl_stmt_seq *
body, PyArena *arena);
match_case_ty _PyAST_match_case(pattern_ty pattern, expr_ty guard,
asdl_stmt_seq * body, PyArena *arena);
pattern_ty _PyAST_MatchValue(expr_ty value, int lineno, int col_offset, int
end_lineno, int end_col_offset, PyArena *arena);
pattern_ty _PyAST_MatchSingleton(constant value, int lineno, int col_offset,
int end_lineno, int end_col_offset, PyArena
*arena);
pattern_ty _PyAST_MatchSequence(asdl_pattern_seq * patterns, int lineno, int
col_offset, int end_lineno, int end_col_offset,
PyArena *arena);
pattern_ty _PyAST_MatchMapping(asdl_expr_seq * keys, asdl_pattern_seq *
patterns, identifier rest, int lineno, int
col_offset, int end_lineno, int end_col_offset,
PyArena *arena);
pattern_ty _PyAST_MatchClass(expr_ty cls, asdl_pattern_seq * patterns,
asdl_identifier_seq * kwd_attrs, asdl_pattern_seq
* kwd_patterns, int lineno, int col_offset, int
end_lineno, int end_col_offset, PyArena *arena);
pattern_ty _PyAST_MatchStar(identifier name, int lineno, int col_offset, int
end_lineno, int end_col_offset, PyArena *arena);
pattern_ty _PyAST_MatchAs(pattern_ty pattern, identifier name, int lineno, int
col_offset, int end_lineno, int end_col_offset,
PyArena *arena);
pattern_ty _PyAST_MatchOr(asdl_pattern_seq * patterns, int lineno, int
col_offset, int end_lineno, int end_col_offset,
PyArena *arena);
type_ignore_ty _PyAST_TypeIgnore(int lineno, string tag, PyArena *arena);

View File

@ -92,7 +92,13 @@ struct ast_state {
PyObject *MatMult_singleton;
PyObject *MatMult_type;
PyObject *MatchAs_type;
PyObject *MatchClass_type;
PyObject *MatchMapping_type;
PyObject *MatchOr_type;
PyObject *MatchSequence_type;
PyObject *MatchSingleton_type;
PyObject *MatchStar_type;
PyObject *MatchValue_type;
PyObject *Match_type;
PyObject *Mod_singleton;
PyObject *Mod_type;
@ -159,6 +165,7 @@ struct ast_state {
PyObject *boolop_type;
PyObject *cases;
PyObject *cause;
PyObject *cls;
PyObject *cmpop_type;
PyObject *col_offset;
PyObject *comparators;
@ -194,6 +201,8 @@ struct ast_state {
PyObject *kind;
PyObject *kw_defaults;
PyObject *kwarg;
PyObject *kwd_attrs;
PyObject *kwd_patterns;
PyObject *kwonlyargs;
PyObject *left;
PyObject *level;
@ -212,8 +221,10 @@ struct ast_state {
PyObject *optional_vars;
PyObject *orelse;
PyObject *pattern;
PyObject *pattern_type;
PyObject *patterns;
PyObject *posonlyargs;
PyObject *rest;
PyObject *returns;
PyObject *right;
PyObject *simple;

View File

@ -32,7 +32,6 @@ struct symtable {
the symbol table */
int recursion_depth; /* current recursion depth */
int recursion_limit; /* recursion limit */
int in_pattern; /* whether we are currently in a pattern */
};
typedef struct _symtable_entry {

View File

@ -798,6 +798,9 @@ class _Unparser(NodeVisitor):
else:
super().visit(node)
# Note: as visit() resets the output text, do NOT rely on
# NodeVisitor.generic_visit to handle any nodes (as it calls back in to
# the subclass visit() method, which resets self._source to an empty list)
def visit(self, node):
"""Outputs a source code string that, if converted back to an ast
(using ast.parse) will generate an AST equivalent to *node*"""
@ -1587,11 +1590,79 @@ class _Unparser(NodeVisitor):
with self.block():
self.traverse(node.body)
def visit_MatchValue(self, node):
self.traverse(node.value)
def visit_MatchSingleton(self, node):
self._write_constant(node.value)
def visit_MatchSequence(self, node):
with self.delimit("[", "]"):
self.interleave(
lambda: self.write(", "), self.traverse, node.patterns
)
def visit_MatchStar(self, node):
name = node.name
if name is None:
name = "_"
self.write(f"*{name}")
def visit_MatchMapping(self, node):
def write_key_pattern_pair(pair):
k, p = pair
self.traverse(k)
self.write(": ")
self.traverse(p)
with self.delimit("{", "}"):
keys = node.keys
self.interleave(
lambda: self.write(", "),
write_key_pattern_pair,
zip(keys, node.patterns, strict=True),
)
rest = node.rest
if rest is not None:
if keys:
self.write(", ")
self.write(f"**{rest}")
def visit_MatchClass(self, node):
self.set_precedence(_Precedence.ATOM, node.cls)
self.traverse(node.cls)
with self.delimit("(", ")"):
patterns = node.patterns
self.interleave(
lambda: self.write(", "), self.traverse, patterns
)
attrs = node.kwd_attrs
if attrs:
def write_attr_pattern(pair):
attr, pattern = pair
self.write(f"{attr}=")
self.traverse(pattern)
if patterns:
self.write(", ")
self.interleave(
lambda: self.write(", "),
write_attr_pattern,
zip(attrs, node.kwd_patterns, strict=True),
)
def visit_MatchAs(self, node):
with self.require_parens(_Precedence.TEST, node):
self.set_precedence(_Precedence.BOR, node.pattern)
self.traverse(node.pattern)
self.write(f" as {node.name}")
name = node.name
pattern = node.pattern
if name is None:
self.write("_")
elif pattern is None:
self.write(node.name)
else:
with self.require_parens(_Precedence.TEST, node):
self.set_precedence(_Precedence.BOR, node.pattern)
self.traverse(node.pattern)
self.write(f" as {node.name}")
def visit_MatchOr(self, node):
with self.require_parens(_Precedence.BOR, node):

View File

@ -2857,6 +2857,22 @@ class TestPatma(unittest.TestCase):
self.assertIs(y, None)
self.assertIs(z, None)
@no_perf
def test_patma_283(self):
self.assert_syntax_error("""
match ...:
case {0+0: _}:
pass
""")
@no_perf
def test_patma_284(self):
self.assert_syntax_error("""
match ...:
case {f"": _}:
pass
""")
class PerfPatma(TestPatma):

View File

@ -518,7 +518,8 @@ class DirectoryTestCase(ASTTestCase):
lib_dir = pathlib.Path(__file__).parent / ".."
test_directories = (lib_dir, lib_dir / "test")
run_always_files = {"test_grammar.py", "test_syntax.py", "test_compile.py",
"test_ast.py", "test_asdl_parser.py", "test_fstring.py"}
"test_ast.py", "test_asdl_parser.py", "test_fstring.py",
"test_patma.py"}
_files_to_test = None

View File

@ -0,0 +1,5 @@
Match patterns now use new dedicated AST nodes (``MatchValue``,
``MatchSingleton``, ``MatchSequence``, ``MatchStar``, ``MatchMapping``,
``MatchClass``) rather than reusing expression AST nodes. ``MatchAs`` and
``MatchOr`` are now defined as pattern nodes rather than as expression nodes.
Patch by Nick Coghlan.

View File

@ -89,10 +89,6 @@ module Python
-- can appear only in Subscript
| Slice(expr? lower, expr? upper, expr? step)
-- only used in patterns
| MatchAs(expr pattern, identifier name)
| MatchOr(expr* patterns)
-- col_offset is the byte offset in the utf8 string the parser uses
attributes (int lineno, int col_offset, int? end_lineno, int? end_col_offset)
@ -128,7 +124,21 @@ module Python
withitem = (expr context_expr, expr? optional_vars)
match_case = (expr pattern, expr? guard, stmt* body)
match_case = (pattern pattern, expr? guard, stmt* body)
pattern = MatchValue(expr value)
| MatchSingleton(constant value)
| MatchSequence(pattern* patterns)
| MatchMapping(expr* keys, pattern* patterns, identifier? rest)
| MatchClass(expr cls, pattern* patterns, identifier* kwd_attrs, pattern* kwd_patterns)
| MatchStar(identifier? name)
-- The optional "rest" MatchMapping parameter handles capturing extra mapping keys
| MatchAs(pattern? pattern, identifier? name)
| MatchOr(pattern* patterns)
attributes (int lineno, int col_offset, int end_lineno, int end_col_offset)
type_ignore = TypeIgnore(int lineno, string tag)
}

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
#include <Python.h>
#include "pycore_ast.h" // _PyAST_Validate()
#include "pycore_ast.h" // _PyAST_Validate(),
#include <errcode.h>
#include "tokenizer.h"
@ -1818,6 +1818,51 @@ _PyPegen_get_values(Parser *p, asdl_seq *seq)
return new_seq;
}
/* Constructs a KeyPatternPair that is used when parsing mapping & class patterns */
KeyPatternPair *
_PyPegen_key_pattern_pair(Parser *p, expr_ty key, pattern_ty pattern)
{
KeyPatternPair *a = _PyArena_Malloc(p->arena, sizeof(KeyPatternPair));
if (!a) {
return NULL;
}
a->key = key;
a->pattern = pattern;
return a;
}
/* Extracts all keys from an asdl_seq* of KeyPatternPair*'s */
asdl_expr_seq *
_PyPegen_get_pattern_keys(Parser *p, asdl_seq *seq)
{
Py_ssize_t len = asdl_seq_LEN(seq);
asdl_expr_seq *new_seq = _Py_asdl_expr_seq_new(len, p->arena);
if (!new_seq) {
return NULL;
}
for (Py_ssize_t i = 0; i < len; i++) {
KeyPatternPair *pair = asdl_seq_GET_UNTYPED(seq, i);
asdl_seq_SET(new_seq, i, pair->key);
}
return new_seq;
}
/* Extracts all patterns from an asdl_seq* of KeyPatternPair*'s */
asdl_pattern_seq *
_PyPegen_get_patterns(Parser *p, asdl_seq *seq)
{
Py_ssize_t len = asdl_seq_LEN(seq);
asdl_pattern_seq *new_seq = _Py_asdl_pattern_seq_new(len, p->arena);
if (!new_seq) {
return NULL;
}
for (Py_ssize_t i = 0; i < len; i++) {
KeyPatternPair *pair = asdl_seq_GET_UNTYPED(seq, i);
asdl_seq_SET(new_seq, i, pair->pattern);
}
return new_seq;
}
/* Constructs a NameDefaultPair */
NameDefaultPair *
_PyPegen_name_default_pair(Parser *p, arg_ty arg, expr_ty value, Token *tc)
@ -2303,6 +2348,16 @@ error:
return NULL;
}
expr_ty
_PyPegen_ensure_imaginary(Parser *p, expr_ty exp)
{
if (exp->kind != Constant_kind || !PyComplex_CheckExact(exp->v.Constant.value)) {
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(exp, "Imaginary number required in complex literal");
return NULL;
}
return exp;
}
mod_ty
_PyPegen_make_module(Parser *p, asdl_stmt_seq *a) {
asdl_type_ignore_seq *type_ignores = NULL;

View File

@ -86,6 +86,11 @@ typedef struct {
expr_ty value;
} KeyValuePair;
typedef struct {
expr_ty key;
pattern_ty pattern;
} KeyPatternPair;
typedef struct {
arg_ty arg;
expr_ty value;
@ -259,6 +264,9 @@ expr_ty _PyPegen_set_expr_context(Parser *, expr_ty, expr_context_ty);
KeyValuePair *_PyPegen_key_value_pair(Parser *, expr_ty, expr_ty);
asdl_expr_seq *_PyPegen_get_keys(Parser *, asdl_seq *);
asdl_expr_seq *_PyPegen_get_values(Parser *, asdl_seq *);
KeyPatternPair *_PyPegen_key_pattern_pair(Parser *, expr_ty, pattern_ty);
asdl_expr_seq *_PyPegen_get_pattern_keys(Parser *, asdl_seq *);
asdl_pattern_seq *_PyPegen_get_patterns(Parser *, asdl_seq *);
NameDefaultPair *_PyPegen_name_default_pair(Parser *, arg_ty, expr_ty, Token *);
SlashWithDefault *_PyPegen_slash_with_default(Parser *, asdl_arg_seq *, asdl_seq *);
StarEtc *_PyPegen_star_etc(Parser *, arg_ty, asdl_seq *, arg_ty);
@ -275,6 +283,7 @@ expr_ty _PyPegen_collect_call_seqs(Parser *, asdl_expr_seq *, asdl_seq *,
int lineno, int col_offset, int end_lineno,
int end_col_offset, PyArena *arena);
expr_ty _PyPegen_concatenate_strings(Parser *p, asdl_seq *);
expr_ty _PyPegen_ensure_imaginary(Parser *p, expr_ty);
asdl_seq *_PyPegen_join_sequences(Parser *, asdl_seq *, asdl_seq *);
int _PyPegen_check_barry_as_flufl(Parser *, Token *);
mod_ty _PyPegen_make_module(Parser *, asdl_stmt_seq *);

1160
Python/Python-ast.c generated

File diff suppressed because it is too large Load Diff

View File

@ -7,6 +7,7 @@
#include "pycore_pystate.h" // _PyThreadState_GET()
#include <assert.h>
#include <stdbool.h>
struct validator {
int recursion_depth; /* current recursion depth */
@ -18,6 +19,7 @@ static int validate_exprs(struct validator *, asdl_expr_seq*, expr_context_ty, i
static int _validate_nonempty_seq(asdl_seq *, const char *, const char *);
static int validate_stmt(struct validator *, stmt_ty);
static int validate_expr(struct validator *, expr_ty, expr_context_ty);
static int validate_pattern(struct validator *, pattern_ty);
static int
validate_name(PyObject *name)
@ -88,9 +90,9 @@ expr_context_name(expr_context_ty ctx)
return "Store";
case Del:
return "Del";
default:
Py_UNREACHABLE();
// No default case so compiler emits warning for unhandled cases
}
Py_UNREACHABLE();
}
static int
@ -180,7 +182,7 @@ validate_constant(struct validator *state, PyObject *value)
static int
validate_expr(struct validator *state, expr_ty exp, expr_context_ty ctx)
{
int ret;
int ret = -1;
if (++state->recursion_depth > state->recursion_limit) {
PyErr_SetString(PyExc_RecursionError,
"maximum recursion depth exceeded during compilation");
@ -351,33 +353,215 @@ validate_expr(struct validator *state, expr_ty exp, expr_context_ty ctx)
case NamedExpr_kind:
ret = validate_expr(state, exp->v.NamedExpr.value, Load);
break;
case MatchAs_kind:
PyErr_SetString(PyExc_ValueError,
"MatchAs is only valid in match_case patterns");
return 0;
case MatchOr_kind:
PyErr_SetString(PyExc_ValueError,
"MatchOr is only valid in match_case patterns");
return 0;
/* This last case doesn't have any checking. */
case Name_kind:
ret = 1;
break;
default:
// No default case so compiler emits warning for unhandled cases
}
if (ret < 0) {
PyErr_SetString(PyExc_SystemError, "unexpected expression");
return 0;
ret = 0;
}
state->recursion_depth--;
return ret;
}
// Note: the ensure_literal_* functions are only used to validate a restricted
// set of non-recursive literals that have already been checked with
// validate_expr, so they don't accept the validator state
static int
validate_pattern(expr_ty p)
ensure_literal_number(expr_ty exp, bool allow_real, bool allow_imaginary)
{
// Coming soon (thanks Batuhan)!
assert(exp->kind == Constant_kind);
PyObject *value = exp->v.Constant.value;
return (allow_real && PyFloat_CheckExact(value)) ||
(allow_real && PyLong_CheckExact(value)) ||
(allow_imaginary && PyComplex_CheckExact(value));
}
static int
ensure_literal_negative(expr_ty exp, bool allow_real, bool allow_imaginary)
{
assert(exp->kind == UnaryOp_kind);
// Must be negation ...
if (exp->v.UnaryOp.op != USub) {
return 0;
}
// ... of a constant ...
expr_ty operand = exp->v.UnaryOp.operand;
if (operand->kind != Constant_kind) {
return 0;
}
// ... number
return ensure_literal_number(operand, allow_real, allow_imaginary);
}
static int
ensure_literal_complex(expr_ty exp)
{
assert(exp->kind == BinOp_kind);
expr_ty left = exp->v.BinOp.left;
expr_ty right = exp->v.BinOp.right;
// Ensure op is addition or subtraction
if (exp->v.BinOp.op != Add && exp->v.BinOp.op != Sub) {
return 0;
}
// Check LHS is a real number (potentially signed)
switch (left->kind)
{
case Constant_kind:
if (!ensure_literal_number(left, /*real=*/true, /*imaginary=*/false)) {
return 0;
}
break;
case UnaryOp_kind:
if (!ensure_literal_negative(left, /*real=*/true, /*imaginary=*/false)) {
return 0;
}
break;
default:
return 0;
}
// Check RHS is an imaginary number (no separate sign allowed)
switch (right->kind)
{
case Constant_kind:
if (!ensure_literal_number(right, /*real=*/false, /*imaginary=*/true)) {
return 0;
}
break;
default:
return 0;
}
return 1;
}
static int
validate_pattern_match_value(struct validator *state, expr_ty exp)
{
if (!validate_expr(state, exp, Load)) {
return 0;
}
switch (exp->kind)
{
case Constant_kind:
case Attribute_kind:
// Constants and attribute lookups are always permitted
return 1;
case UnaryOp_kind:
// Negated numbers are permitted (whether real or imaginary)
// Compiler will complain if AST folding doesn't create a constant
if (ensure_literal_negative(exp, /*real=*/true, /*imaginary=*/true)) {
return 1;
}
break;
case BinOp_kind:
// Complex literals are permitted
// Compiler will complain if AST folding doesn't create a constant
if (ensure_literal_complex(exp)) {
return 1;
}
break;
default:
break;
}
PyErr_SetString(PyExc_SyntaxError,
"patterns may only match literals and attribute lookups");
return 0;
}
static int
validate_pattern(struct validator *state, pattern_ty p)
{
int ret = -1;
if (++state->recursion_depth > state->recursion_limit) {
PyErr_SetString(PyExc_RecursionError,
"maximum recursion depth exceeded during compilation");
return 0;
}
// Coming soon: https://bugs.python.org/issue43897 (thanks Batuhan)!
// TODO: Ensure no subnodes use "_" as an ordinary identifier
switch (p->kind) {
case MatchValue_kind:
ret = validate_pattern_match_value(state, p->v.MatchValue.value);
break;
case MatchSingleton_kind:
// TODO: Check constant is specifically None, True, or False
ret = validate_constant(state, p->v.MatchSingleton.value);
break;
case MatchSequence_kind:
// TODO: Validate all subpatterns
// return validate_patterns(state, p->v.MatchSequence.patterns);
ret = 1;
break;
case MatchMapping_kind:
// TODO: check "rest" target name is valid
if (asdl_seq_LEN(p->v.MatchMapping.keys) != asdl_seq_LEN(p->v.MatchMapping.patterns)) {
PyErr_SetString(PyExc_ValueError,
"MatchMapping doesn't have the same number of keys as patterns");
return 0;
}
// null_ok=0 for key expressions, as rest-of-mapping is captured in "rest"
// TODO: replace with more restrictive expression validator, as per MatchValue above
if (!validate_exprs(state, p->v.MatchMapping.keys, Load, /*null_ok=*/ 0)) {
return 0;
}
// TODO: Validate all subpatterns
// ret = validate_patterns(state, p->v.MatchMapping.patterns);
ret = 1;
break;
case MatchClass_kind:
if (asdl_seq_LEN(p->v.MatchClass.kwd_attrs) != asdl_seq_LEN(p->v.MatchClass.kwd_patterns)) {
PyErr_SetString(PyExc_ValueError,
"MatchClass doesn't have the same number of keyword attributes as patterns");
return 0;
}
// TODO: Restrict cls lookup to being a name or attribute
if (!validate_expr(state, p->v.MatchClass.cls, Load)) {
return 0;
}
// TODO: Validate all subpatterns
// return validate_patterns(state, p->v.MatchClass.patterns) &&
// validate_patterns(state, p->v.MatchClass.kwd_patterns);
ret = 1;
break;
case MatchStar_kind:
// TODO: check target name is valid
ret = 1;
break;
case MatchAs_kind:
// TODO: check target name is valid
if (p->v.MatchAs.pattern == NULL) {
ret = 1;
}
else if (p->v.MatchAs.name == NULL) {
PyErr_SetString(PyExc_ValueError,
"MatchAs must specify a target name if a pattern is given");
return 0;
}
else {
ret = validate_pattern(state, p->v.MatchAs.pattern);
}
break;
case MatchOr_kind:
// TODO: Validate all subpatterns
// return validate_patterns(state, p->v.MatchOr.patterns);
ret = 1;
break;
// No default case, so the compiler will emit a warning if new pattern
// kinds are added without being handled here
}
if (ret < 0) {
PyErr_SetString(PyExc_SystemError, "unexpected pattern");
ret = 0;
}
state->recursion_depth--;
return ret;
}
static int
_validate_nonempty_seq(asdl_seq *seq, const char *what, const char *owner)
{
@ -404,7 +588,7 @@ validate_body(struct validator *state, asdl_stmt_seq *body, const char *owner)
static int
validate_stmt(struct validator *state, stmt_ty stmt)
{
int ret;
int ret = -1;
Py_ssize_t i;
if (++state->recursion_depth > state->recursion_limit) {
PyErr_SetString(PyExc_RecursionError,
@ -502,7 +686,7 @@ validate_stmt(struct validator *state, stmt_ty stmt)
}
for (i = 0; i < asdl_seq_LEN(stmt->v.Match.cases); i++) {
match_case_ty m = asdl_seq_GET(stmt->v.Match.cases, i);
if (!validate_pattern(m->pattern)
if (!validate_pattern(state, m->pattern)
|| (m->guard && !validate_expr(state, m->guard, Load))
|| !validate_body(state, m->body, "match_case")) {
return 0;
@ -582,9 +766,11 @@ validate_stmt(struct validator *state, stmt_ty stmt)
case Continue_kind:
ret = 1;
break;
default:
// No default case so compiler emits warning for unhandled cases
}
if (ret < 0) {
PyErr_SetString(PyExc_SystemError, "unexpected statement");
return 0;
ret = 0;
}
state->recursion_depth--;
return ret;
@ -635,7 +821,7 @@ validate_exprs(struct validator *state, asdl_expr_seq *exprs, expr_context_ty ct
int
_PyAST_Validate(mod_ty mod)
{
int res = 0;
int res = -1;
struct validator state;
PyThreadState *tstate;
int recursion_limit = Py_GetRecursionLimit();
@ -663,10 +849,16 @@ _PyAST_Validate(mod_ty mod)
case Expression_kind:
res = validate_expr(&state, mod->v.Expression.body, Load);
break;
default:
PyErr_SetString(PyExc_SystemError, "impossible module node");
res = 0;
case FunctionType_kind:
res = validate_exprs(&state, mod->v.FunctionType.argtypes, Load, /*null_ok=*/0) &&
validate_expr(&state, mod->v.FunctionType.returns, Load);
break;
// No default case so compiler emits warning for unhandled cases
}
if (res < 0) {
PyErr_SetString(PyExc_SystemError, "impossible module node");
return 0;
}
/* Check that the recursion depth counting balanced correctly */

View File

@ -411,7 +411,7 @@ static int astfold_arg(arg_ty node_, PyArena *ctx_, _PyASTOptimizeState *state);
static int astfold_withitem(withitem_ty node_, PyArena *ctx_, _PyASTOptimizeState *state);
static int astfold_excepthandler(excepthandler_ty node_, PyArena *ctx_, _PyASTOptimizeState *state);
static int astfold_match_case(match_case_ty node_, PyArena *ctx_, _PyASTOptimizeState *state);
static int astfold_pattern(expr_ty node_, PyArena *ctx_, _PyASTOptimizeState *state);
static int astfold_pattern(pattern_ty node_, PyArena *ctx_, _PyASTOptimizeState *state);
#define CALL(FUNC, TYPE, ARG) \
if (!FUNC((ARG), ctx_, state)) \
@ -602,10 +602,6 @@ astfold_expr(expr_ty node_, PyArena *ctx_, _PyASTOptimizeState *state)
case Constant_kind:
// Already a constant, nothing further to do
break;
case MatchAs_kind:
case MatchOr_kind:
// These can't occur outside of patterns.
Py_UNREACHABLE();
// No default case, so the compiler will emit a warning if new expression
// kinds are added without being handled here
}
@ -797,112 +793,48 @@ astfold_withitem(withitem_ty node_, PyArena *ctx_, _PyASTOptimizeState *state)
}
static int
astfold_pattern_negative(expr_ty node_, PyArena *ctx_, _PyASTOptimizeState *state)
astfold_pattern(pattern_ty node_, PyArena *ctx_, _PyASTOptimizeState *state)
{
assert(node_->kind == UnaryOp_kind);
assert(node_->v.UnaryOp.op == USub);
assert(node_->v.UnaryOp.operand->kind == Constant_kind);
PyObject *value = node_->v.UnaryOp.operand->v.Constant.value;
assert(PyComplex_CheckExact(value) ||
PyFloat_CheckExact(value) ||
PyLong_CheckExact(value));
PyObject *negated = PyNumber_Negative(value);
if (negated == NULL) {
// Currently, this is really only used to form complex/negative numeric
// constants in MatchValue and MatchMapping nodes
// We still recurse into all subexpressions and subpatterns anyway
if (++state->recursion_depth > state->recursion_limit) {
PyErr_SetString(PyExc_RecursionError,
"maximum recursion depth exceeded during compilation");
return 0;
}
assert(PyComplex_CheckExact(negated) ||
PyFloat_CheckExact(negated) ||
PyLong_CheckExact(negated));
return make_const(node_, negated, ctx_);
}
static int
astfold_pattern_complex(expr_ty node_, PyArena *ctx_, _PyASTOptimizeState *state)
{
expr_ty left = node_->v.BinOp.left;
expr_ty right = node_->v.BinOp.right;
if (left->kind == UnaryOp_kind) {
CALL(astfold_pattern_negative, expr_ty, left);
}
assert(left->kind = Constant_kind);
assert(right->kind = Constant_kind);
// LHS must be real, RHS must be imaginary:
if (!(PyFloat_CheckExact(left->v.Constant.value) ||
PyLong_CheckExact(left->v.Constant.value)) ||
!PyComplex_CheckExact(right->v.Constant.value))
{
// Not actually valid, but it's the compiler's job to complain:
return 1;
}
PyObject *new;
if (node_->v.BinOp.op == Add) {
new = PyNumber_Add(left->v.Constant.value, right->v.Constant.value);
}
else {
assert(node_->v.BinOp.op == Sub);
new = PyNumber_Subtract(left->v.Constant.value, right->v.Constant.value);
}
if (new == NULL) {
return 0;
}
assert(PyComplex_CheckExact(new));
return make_const(node_, new, ctx_);
}
static int
astfold_pattern_keyword(keyword_ty node_, PyArena *ctx_, _PyASTOptimizeState *state)
{
CALL(astfold_pattern, expr_ty, node_->value);
return 1;
}
static int
astfold_pattern(expr_ty node_, PyArena *ctx_, _PyASTOptimizeState *state)
{
// Don't blindly optimize the pattern as an expr; it plays by its own rules!
// Currently, this is only used to form complex/negative numeric constants.
switch (node_->kind) {
case Attribute_kind:
case MatchValue_kind:
CALL(astfold_expr, expr_ty, node_->v.MatchValue.value);
break;
case BinOp_kind:
CALL(astfold_pattern_complex, expr_ty, node_);
case MatchSingleton_kind:
break;
case Call_kind:
CALL_SEQ(astfold_pattern, expr, node_->v.Call.args);
CALL_SEQ(astfold_pattern_keyword, keyword, node_->v.Call.keywords);
case MatchSequence_kind:
CALL_SEQ(astfold_pattern, pattern, node_->v.MatchSequence.patterns);
break;
case Constant_kind:
case MatchMapping_kind:
CALL_SEQ(astfold_expr, expr, node_->v.MatchMapping.keys);
CALL_SEQ(astfold_pattern, pattern, node_->v.MatchMapping.patterns);
break;
case Dict_kind:
CALL_SEQ(astfold_pattern, expr, node_->v.Dict.keys);
CALL_SEQ(astfold_pattern, expr, node_->v.Dict.values);
case MatchClass_kind:
CALL(astfold_expr, expr_ty, node_->v.MatchClass.cls);
CALL_SEQ(astfold_pattern, pattern, node_->v.MatchClass.patterns);
CALL_SEQ(astfold_pattern, pattern, node_->v.MatchClass.kwd_patterns);
break;
// Not actually valid, but it's the compiler's job to complain:
case JoinedStr_kind:
break;
case List_kind:
CALL_SEQ(astfold_pattern, expr, node_->v.List.elts);
case MatchStar_kind:
break;
case MatchAs_kind:
CALL(astfold_pattern, expr_ty, node_->v.MatchAs.pattern);
if (node_->v.MatchAs.pattern) {
CALL(astfold_pattern, expr_ty, node_->v.MatchAs.pattern);
}
break;
case MatchOr_kind:
CALL_SEQ(astfold_pattern, expr, node_->v.MatchOr.patterns);
CALL_SEQ(astfold_pattern, pattern, node_->v.MatchOr.patterns);
break;
case Name_kind:
break;
case Starred_kind:
CALL(astfold_pattern, expr_ty, node_->v.Starred.value);
break;
case Tuple_kind:
CALL_SEQ(astfold_pattern, expr, node_->v.Tuple.elts);
break;
case UnaryOp_kind:
CALL(astfold_pattern_negative, expr_ty, node_);
break;
default:
Py_UNREACHABLE();
// No default case, so the compiler will emit a warning if new pattern
// kinds are added without being handled here
}
state->recursion_depth--;
return 1;
}

View File

@ -3,6 +3,11 @@
#include <float.h> // DBL_MAX_10_EXP
#include <stdbool.h>
/* This limited unparser is used to convert annotations back to strings
* during compilation rather than being a full AST unparser.
* See ast.unparse for a full unparser (written in Python)
*/
static PyObject *_str_open_br;
static PyObject *_str_dbl_open_br;
static PyObject *_str_close_br;
@ -912,11 +917,11 @@ append_ast_expr(_PyUnicodeWriter *writer, expr_ty e, int level)
return append_ast_tuple(writer, e, level);
case NamedExpr_kind:
return append_named_expr(writer, e, level);
default:
PyErr_SetString(PyExc_SystemError,
"unknown expression kind");
return -1;
// No default so compiler emits a warning for unhandled cases
}
PyErr_SetString(PyExc_SystemError,
"unknown expression kind");
return -1;
}
static int

View File

@ -831,11 +831,7 @@ builtin_compile_impl(PyObject *module, PyObject *source, PyObject *filename,
if (arena == NULL)
goto error;
mod = PyAST_obj2mod(source, arena, compile_mode);
if (mod == NULL) {
_PyArena_Free(arena);
goto error;
}
if (!_PyAST_Validate(mod)) {
if (mod == NULL || !_PyAST_Validate(mod)) {
_PyArena_Free(arena);
goto error;
}

View File

@ -280,9 +280,9 @@ static int compiler_async_comprehension_generator(
int depth,
expr_ty elt, expr_ty val, int type);
static int compiler_pattern(struct compiler *, expr_ty, pattern_context *);
static int compiler_pattern(struct compiler *, pattern_ty, pattern_context *);
static int compiler_match(struct compiler *, stmt_ty);
static int compiler_pattern_subpattern(struct compiler *, expr_ty,
static int compiler_pattern_subpattern(struct compiler *, pattern_ty,
pattern_context *);
static PyCodeObject *assemble(struct compiler *, int addNone);
@ -5263,10 +5263,6 @@ compiler_visit_expr1(struct compiler *c, expr_ty e)
return compiler_list(c, e);
case Tuple_kind:
return compiler_tuple(c, e);
case MatchAs_kind:
case MatchOr_kind:
// Can only occur in patterns, which are handled elsewhere.
Py_UNREACHABLE();
}
return 1;
}
@ -5600,16 +5596,22 @@ compiler_slice(struct compiler *c, expr_ty s)
// that it's much easier to smooth out any redundant pushing, popping, and
// jumping in the peephole optimizer than to detect or predict it here.
#define WILDCARD_CHECK(N) \
((N)->kind == Name_kind && \
_PyUnicode_EqualToASCIIString((N)->v.Name.id, "_"))
((N)->kind == MatchAs_kind && !(N)->v.MatchAs.name)
#define WILDCARD_STAR_CHECK(N) \
((N)->kind == MatchStar_kind && !(N)->v.MatchStar.name)
// Limit permitted subexpressions, even if the parser & AST validator let them through
#define MATCH_VALUE_EXPR(N) \
((N)->kind == Constant_kind || (N)->kind == Attribute_kind)
static int
pattern_helper_store_name(struct compiler *c, identifier n, pattern_context *pc)
{
assert(!_PyUnicode_EqualToASCIIString(n, "_"));
if (forbidden_name(c, n, Store)) {
return 0;
}
// Can't assign to the same name twice:
if (pc->stores == NULL) {
RETURN_IF_FALSE(pc->stores = PySet_New(NULL));
@ -5631,16 +5633,43 @@ pattern_helper_store_name(struct compiler *c, identifier n, pattern_context *pc)
static int
pattern_helper_sequence_unpack(struct compiler *c, asdl_expr_seq *values,
pattern_unpack_helper(struct compiler *c, asdl_pattern_seq *elts)
{
Py_ssize_t n = asdl_seq_LEN(elts);
int seen_star = 0;
for (Py_ssize_t i = 0; i < n; i++) {
pattern_ty elt = asdl_seq_GET(elts, i);
if (elt->kind == MatchStar_kind && !seen_star) {
if ((i >= (1 << 8)) ||
(n-i-1 >= (INT_MAX >> 8)))
return compiler_error(c,
"too many expressions in "
"star-unpacking sequence pattern");
ADDOP_I(c, UNPACK_EX, (i + ((n-i-1) << 8)));
seen_star = 1;
}
else if (elt->kind == MatchStar_kind) {
return compiler_error(c,
"multiple starred expressions in sequence pattern");
}
}
if (!seen_star) {
ADDOP_I(c, UNPACK_SEQUENCE, n);
}
return 1;
}
static int
pattern_helper_sequence_unpack(struct compiler *c, asdl_pattern_seq *patterns,
Py_ssize_t star, pattern_context *pc)
{
RETURN_IF_FALSE(unpack_helper(c, values));
RETURN_IF_FALSE(pattern_unpack_helper(c, patterns));
// We've now got a bunch of new subjects on the stack. If any of them fail
// to match, we need to pop everything else off, then finally push False.
// fails is an array of blocks that correspond to the necessary amount of
// popping for each element:
basicblock **fails;
Py_ssize_t size = asdl_seq_LEN(values);
Py_ssize_t size = asdl_seq_LEN(patterns);
fails = (basicblock **)PyObject_Malloc(sizeof(basicblock*) * size);
if (fails == NULL) {
PyErr_NoMemory();
@ -5655,12 +5684,9 @@ pattern_helper_sequence_unpack(struct compiler *c, asdl_expr_seq *values,
}
}
for (Py_ssize_t i = 0; i < size; i++) {
expr_ty value = asdl_seq_GET(values, i);
if (i == star) {
assert(value->kind == Starred_kind);
value = value->v.Starred.value;
}
if (!compiler_pattern_subpattern(c, value, pc) ||
pattern_ty pattern = asdl_seq_GET(patterns, i);
assert((i == star) == (pattern->kind == MatchStar_kind));
if (!compiler_pattern_subpattern(c, pattern, pc) ||
!compiler_addop_j(c, POP_JUMP_IF_FALSE, fails[i]) ||
compiler_next_block(c) == NULL)
{
@ -5703,21 +5729,20 @@ error:
// UNPACK_SEQUENCE / UNPACK_EX. This is more efficient for patterns with a
// starred wildcard like [first, *_] / [first, *_, last] / [*_, last] / etc.
static int
pattern_helper_sequence_subscr(struct compiler *c, asdl_expr_seq *values,
pattern_helper_sequence_subscr(struct compiler *c, asdl_pattern_seq *patterns,
Py_ssize_t star, pattern_context *pc)
{
basicblock *end, *fail_pop_1;
RETURN_IF_FALSE(end = compiler_new_block(c));
RETURN_IF_FALSE(fail_pop_1 = compiler_new_block(c));
Py_ssize_t size = asdl_seq_LEN(values);
Py_ssize_t size = asdl_seq_LEN(patterns);
for (Py_ssize_t i = 0; i < size; i++) {
expr_ty value = asdl_seq_GET(values, i);
if (WILDCARD_CHECK(value)) {
pattern_ty pattern = asdl_seq_GET(patterns, i);
if (WILDCARD_CHECK(pattern)) {
continue;
}
if (i == star) {
assert(value->kind == Starred_kind);
assert(WILDCARD_CHECK(value->v.Starred.value));
assert(WILDCARD_STAR_CHECK(pattern));
continue;
}
ADDOP(c, DUP_TOP);
@ -5732,7 +5757,7 @@ pattern_helper_sequence_subscr(struct compiler *c, asdl_expr_seq *values,
ADDOP(c, BINARY_SUBTRACT);
}
ADDOP(c, BINARY_SUBSCR);
RETURN_IF_FALSE(compiler_pattern_subpattern(c, value, pc));
RETURN_IF_FALSE(compiler_pattern_subpattern(c, pattern, pc));
ADDOP_JUMP(c, POP_JUMP_IF_FALSE, fail_pop_1);
NEXT_BLOCK(c);
}
@ -5746,10 +5771,9 @@ pattern_helper_sequence_subscr(struct compiler *c, asdl_expr_seq *values,
return 1;
}
// Like compiler_pattern, but turn off checks for irrefutability.
static int
compiler_pattern_subpattern(struct compiler *c, expr_ty p, pattern_context *pc)
compiler_pattern_subpattern(struct compiler *c, pattern_ty p, pattern_context *pc)
{
int allow_irrefutable = pc->allow_irrefutable;
pc->allow_irrefutable = 1;
@ -5758,11 +5782,43 @@ compiler_pattern_subpattern(struct compiler *c, expr_ty p, pattern_context *pc)
return 1;
}
static int
compiler_pattern_capture(struct compiler *c, identifier n, pattern_context *pc)
{
RETURN_IF_FALSE(pattern_helper_store_name(c, n, pc));
ADDOP_LOAD_CONST(c, Py_True);
return 1;
}
static int
compiler_pattern_as(struct compiler *c, expr_ty p, pattern_context *pc)
compiler_pattern_wildcard(struct compiler *c, pattern_ty p, pattern_context *pc)
{
assert(p->kind == MatchAs_kind);
if (!pc->allow_irrefutable) {
// Whoops, can't have a wildcard here!
const char *e = "wildcard makes remaining patterns unreachable";
return compiler_error(c, e);
}
ADDOP(c, POP_TOP);
ADDOP_LOAD_CONST(c, Py_True);
return 1;
}
static int
compiler_pattern_as(struct compiler *c, pattern_ty p, pattern_context *pc)
{
assert(p->kind == MatchAs_kind);
if (p->v.MatchAs.name == NULL) {
return compiler_pattern_wildcard(c, p, pc);
}
if (p->v.MatchAs.pattern == NULL) {
if (!pc->allow_irrefutable) {
// Whoops, can't have a name capture here!
const char *e = "name capture %R makes remaining patterns unreachable";
return compiler_error(c, e, p->v.MatchAs.name);
}
return compiler_pattern_capture(c, p->v.MatchAs.name, pc);
}
basicblock *end, *fail_pop_1;
RETURN_IF_FALSE(end = compiler_new_block(c));
RETURN_IF_FALSE(fail_pop_1 = compiler_new_block(c));
@ -5782,71 +5838,101 @@ compiler_pattern_as(struct compiler *c, expr_ty p, pattern_context *pc)
return 1;
}
static int
compiler_pattern_capture(struct compiler *c, expr_ty p, pattern_context *pc)
compiler_pattern_star(struct compiler *c, pattern_ty p, pattern_context *pc)
{
assert(p->kind == Name_kind);
assert(p->v.Name.ctx == Store);
assert(!WILDCARD_CHECK(p));
assert(p->kind == MatchStar_kind);
if (!pc->allow_irrefutable) {
// Whoops, can't have a name capture here!
const char *e = "name capture %R makes remaining patterns unreachable";
return compiler_error(c, e, p->v.Name.id);
// Whoops, can't have a star capture here!
const char *e = "star captures are only allowed as part of sequence patterns";
return compiler_error(c, e);
}
RETURN_IF_FALSE(pattern_helper_store_name(c, p->v.Name.id, pc));
ADDOP_LOAD_CONST(c, Py_True);
return 1;
return compiler_pattern_capture(c, p->v.MatchStar.name, pc);
}
static int
validate_kwd_attrs(struct compiler *c, asdl_identifier_seq *attrs, asdl_pattern_seq* patterns)
{
// Any errors will point to the pattern rather than the arg name as the
// parser is only supplying identifiers rather than Name or keyword nodes
Py_ssize_t nattrs = asdl_seq_LEN(attrs);
for (Py_ssize_t i = 0; i < nattrs; i++) {
identifier attr = ((identifier)asdl_seq_GET(attrs, i));
c->u->u_col_offset = ((pattern_ty) asdl_seq_GET(patterns, i))->col_offset;
if (forbidden_name(c, attr, Store)) {
return -1;
}
for (Py_ssize_t j = i + 1; j < nattrs; j++) {
identifier other = ((identifier)asdl_seq_GET(attrs, j));
if (!PyUnicode_Compare(attr, other)) {
c->u->u_col_offset = ((pattern_ty) asdl_seq_GET(patterns, j))->col_offset;
compiler_error(c, "attribute name repeated in class pattern: %U", attr);
return -1;
}
}
}
return 0;
}
static int
compiler_pattern_class(struct compiler *c, expr_ty p, pattern_context *pc)
compiler_pattern_class(struct compiler *c, pattern_ty p, pattern_context *pc)
{
asdl_expr_seq *args = p->v.Call.args;
asdl_keyword_seq *kwargs = p->v.Call.keywords;
Py_ssize_t nargs = asdl_seq_LEN(args);
Py_ssize_t nkwargs = asdl_seq_LEN(kwargs);
if (INT_MAX < nargs || INT_MAX < nargs + nkwargs - 1) {
const char *e = "too many sub-patterns in class pattern %R";
return compiler_error(c, e, p->v.Call.func);
assert(p->kind == MatchClass_kind);
asdl_pattern_seq *patterns = p->v.MatchClass.patterns;
asdl_identifier_seq *kwd_attrs = p->v.MatchClass.kwd_attrs;
asdl_pattern_seq *kwd_patterns = p->v.MatchClass.kwd_patterns;
Py_ssize_t nargs = asdl_seq_LEN(patterns);
Py_ssize_t nattrs = asdl_seq_LEN(kwd_attrs);
Py_ssize_t nkwd_patterns = asdl_seq_LEN(kwd_patterns);
if (nattrs != nkwd_patterns) {
// AST validator shouldn't let this happen, but if it does,
// just fail, don't crash out of the interpreter
const char * e = "kwd_attrs (%d) / kwd_patterns (%d) length mismatch in class pattern";
return compiler_error(c, e, nattrs, nkwd_patterns);
}
if (INT_MAX < nargs || INT_MAX < nargs + nattrs - 1) {
const char *e = "too many sub-patterns in class pattern %R";
return compiler_error(c, e, p->v.MatchClass.cls);
}
if (nattrs) {
RETURN_IF_FALSE(!validate_kwd_attrs(c, kwd_attrs, kwd_patterns));
c->u->u_col_offset = p->col_offset; // validate_kwd_attrs moves this
}
RETURN_IF_FALSE(!validate_keywords(c, kwargs));
basicblock *end, *fail_pop_1;
RETURN_IF_FALSE(end = compiler_new_block(c));
RETURN_IF_FALSE(fail_pop_1 = compiler_new_block(c));
VISIT(c, expr, p->v.Call.func);
PyObject *kwnames;
RETURN_IF_FALSE(kwnames = PyTuple_New(nkwargs));
VISIT(c, expr, p->v.MatchClass.cls);
PyObject *attr_names;
RETURN_IF_FALSE(attr_names = PyTuple_New(nattrs));
Py_ssize_t i;
for (i = 0; i < nkwargs; i++) {
PyObject *name = ((keyword_ty) asdl_seq_GET(kwargs, i))->arg;
for (i = 0; i < nattrs; i++) {
PyObject *name = asdl_seq_GET(kwd_attrs, i);
Py_INCREF(name);
PyTuple_SET_ITEM(kwnames, i, name);
PyTuple_SET_ITEM(attr_names, i, name);
}
ADDOP_LOAD_CONST_NEW(c, kwnames);
ADDOP_LOAD_CONST_NEW(c, attr_names);
ADDOP_I(c, MATCH_CLASS, nargs);
ADDOP_JUMP(c, POP_JUMP_IF_FALSE, fail_pop_1);
NEXT_BLOCK(c);
// TOS is now a tuple of (nargs + nkwargs) attributes.
for (i = 0; i < nargs + nkwargs; i++) {
expr_ty arg;
for (i = 0; i < nargs + nattrs; i++) {
pattern_ty pattern;
if (i < nargs) {
// Positional:
arg = asdl_seq_GET(args, i);
pattern = asdl_seq_GET(patterns, i);
}
else {
// Keyword:
arg = ((keyword_ty) asdl_seq_GET(kwargs, i - nargs))->value;
pattern = asdl_seq_GET(kwd_patterns, i - nargs);
}
if (WILDCARD_CHECK(arg)) {
if (WILDCARD_CHECK(pattern)) {
continue;
}
// Get the i-th attribute, and match it against the i-th pattern:
ADDOP(c, DUP_TOP);
ADDOP_LOAD_CONST_NEW(c, PyLong_FromSsize_t(i));
ADDOP(c, BINARY_SUBSCR);
RETURN_IF_FALSE(compiler_pattern_subpattern(c, arg, pc));
RETURN_IF_FALSE(compiler_pattern_subpattern(c, pattern, pc));
ADDOP_JUMP(c, POP_JUMP_IF_FALSE, fail_pop_1);
NEXT_BLOCK(c);
}
@ -5861,36 +5947,30 @@ compiler_pattern_class(struct compiler *c, expr_ty p, pattern_context *pc)
return 1;
}
static int
compiler_pattern_literal(struct compiler *c, expr_ty p, pattern_context *pc)
{
assert(p->kind == Constant_kind);
PyObject *v = p->v.Constant.value;
ADDOP_LOAD_CONST(c, v);
// Literal True, False, and None are compared by identity. All others use
// equality:
ADDOP_COMPARE(c, (v == Py_None || PyBool_Check(v)) ? Is : Eq);
return 1;
}
static int
compiler_pattern_mapping(struct compiler *c, expr_ty p, pattern_context *pc)
compiler_pattern_mapping(struct compiler *c, pattern_ty p, pattern_context *pc)
{
assert(p->kind == MatchMapping_kind);
basicblock *end, *fail_pop_1, *fail_pop_3;
RETURN_IF_FALSE(end = compiler_new_block(c));
RETURN_IF_FALSE(fail_pop_1 = compiler_new_block(c));
RETURN_IF_FALSE(fail_pop_3 = compiler_new_block(c));
asdl_expr_seq *keys = p->v.Dict.keys;
asdl_expr_seq *values = p->v.Dict.values;
Py_ssize_t size = asdl_seq_LEN(values);
// A starred pattern will be a keyless value. It is guaranteed to be last:
int star = size ? !asdl_seq_GET(keys, size - 1) : 0;
asdl_expr_seq *keys = p->v.MatchMapping.keys;
asdl_pattern_seq *patterns = p->v.MatchMapping.patterns;
Py_ssize_t size = asdl_seq_LEN(keys);
Py_ssize_t npatterns = asdl_seq_LEN(patterns);
if (size != npatterns) {
// AST validator shouldn't let this happen, but if it does,
// just fail, don't crash out of the interpreter
const char * e = "keys (%d) / patterns (%d) length mismatch in mapping pattern";
return compiler_error(c, e, size, npatterns);
}
// We have a double-star target if "rest" is set
PyObject *star_target = p->v.MatchMapping.rest;
ADDOP(c, MATCH_MAPPING);
ADDOP_JUMP(c, POP_JUMP_IF_FALSE, fail_pop_1);
NEXT_BLOCK(c);
if (!size) {
if (!size && !star_target) {
// If the pattern is just "{}", we're done!
ADDOP(c, POP_TOP);
ADDOP_LOAD_CONST(c, Py_True);
@ -5901,53 +5981,57 @@ compiler_pattern_mapping(struct compiler *c, expr_ty p, pattern_context *pc)
compiler_use_next_block(c, end);
return 1;
}
if (size - star) {
if (size) {
// If the pattern has any keys in it, perform a length check:
ADDOP(c, GET_LEN);
ADDOP_LOAD_CONST_NEW(c, PyLong_FromSsize_t(size - star));
ADDOP_LOAD_CONST_NEW(c, PyLong_FromSsize_t(size));
ADDOP_COMPARE(c, GtE);
ADDOP_JUMP(c, POP_JUMP_IF_FALSE, fail_pop_1);
NEXT_BLOCK(c);
}
if (INT_MAX < size - star - 1) {
if (INT_MAX < size - 1) {
return compiler_error(c, "too many sub-patterns in mapping pattern");
}
// Collect all of the keys into a tuple for MATCH_KEYS and
// COPY_DICT_WITHOUT_KEYS. They can either be dotted names or literals:
for (Py_ssize_t i = 0; i < size - star; i++) {
for (Py_ssize_t i = 0; i < size; i++) {
expr_ty key = asdl_seq_GET(keys, i);
if (key == NULL) {
const char *e = "can't use starred name here "
"(consider moving to end)";
const char *e = "can't use NULL keys in MatchMapping "
"(set 'rest' parameter instead)";
c->u->u_col_offset = ((pattern_ty) asdl_seq_GET(patterns, i))->col_offset;
return compiler_error(c, e);
}
if (!MATCH_VALUE_EXPR(key)) {
const char *e = "mapping pattern keys may only match literals and attribute lookups";
return compiler_error(c, e);
}
VISIT(c, expr, key);
}
ADDOP_I(c, BUILD_TUPLE, size - star);
ADDOP_I(c, BUILD_TUPLE, size);
ADDOP(c, MATCH_KEYS);
ADDOP_JUMP(c, POP_JUMP_IF_FALSE, fail_pop_3);
NEXT_BLOCK(c);
// So far so good. There's now a tuple of values on the stack to match
// sub-patterns against:
for (Py_ssize_t i = 0; i < size - star; i++) {
expr_ty value = asdl_seq_GET(values, i);
if (WILDCARD_CHECK(value)) {
for (Py_ssize_t i = 0; i < size; i++) {
pattern_ty pattern = asdl_seq_GET(patterns, i);
if (WILDCARD_CHECK(pattern)) {
continue;
}
ADDOP(c, DUP_TOP);
ADDOP_LOAD_CONST_NEW(c, PyLong_FromSsize_t(i));
ADDOP(c, BINARY_SUBSCR);
RETURN_IF_FALSE(compiler_pattern_subpattern(c, value, pc));
RETURN_IF_FALSE(compiler_pattern_subpattern(c, pattern, pc));
ADDOP_JUMP(c, POP_JUMP_IF_FALSE, fail_pop_3);
NEXT_BLOCK(c);
}
// If we get this far, it's a match! We're done with that tuple of values.
ADDOP(c, POP_TOP);
if (star) {
// If we had a starred name, bind a dict of remaining items to it:
if (star_target) {
// If we have a starred name, bind a dict of remaining items to it:
ADDOP(c, COPY_DICT_WITHOUT_KEYS);
PyObject *id = asdl_seq_GET(values, size - 1)->v.Name.id;
RETURN_IF_FALSE(pattern_helper_store_name(c, id, pc));
RETURN_IF_FALSE(pattern_helper_store_name(c, star_target, pc));
}
else {
// Otherwise, we don't care about this tuple of keys anymore:
@ -5970,9 +6054,8 @@ compiler_pattern_mapping(struct compiler *c, expr_ty p, pattern_context *pc)
return 1;
}
static int
compiler_pattern_or(struct compiler *c, expr_ty p, pattern_context *pc)
compiler_pattern_or(struct compiler *c, pattern_ty p, pattern_context *pc)
{
assert(p->kind == MatchOr_kind);
// control is the set of names bound by the first alternative. If all of the
@ -5988,7 +6071,7 @@ compiler_pattern_or(struct compiler *c, expr_ty p, pattern_context *pc)
int allow_irrefutable = pc->allow_irrefutable;
for (Py_ssize_t i = 0; i < size; i++) {
// NOTE: Can't use our nice returning macros in here: they'll leak sets!
expr_ty alt = asdl_seq_GET(p->v.MatchOr.patterns, i);
pattern_ty alt = asdl_seq_GET(p->v.MatchOr.patterns, i);
pc->stores = PySet_New(stores_init);
// An irrefutable sub-pattern must be last, if it is allowed at all:
int is_last = i == size - 1;
@ -6044,28 +6127,28 @@ fail:
static int
compiler_pattern_sequence(struct compiler *c, expr_ty p, pattern_context *pc)
compiler_pattern_sequence(struct compiler *c, pattern_ty p, pattern_context *pc)
{
assert(p->kind == List_kind || p->kind == Tuple_kind);
asdl_expr_seq *values = (p->kind == Tuple_kind) ? p->v.Tuple.elts
: p->v.List.elts;
Py_ssize_t size = asdl_seq_LEN(values);
assert(p->kind == MatchSequence_kind);
asdl_pattern_seq *patterns = p->v.MatchSequence.patterns;
Py_ssize_t size = asdl_seq_LEN(patterns);
Py_ssize_t star = -1;
int only_wildcard = 1;
int star_wildcard = 0;
// Find a starred name, if it exists. There may be at most one:
for (Py_ssize_t i = 0; i < size; i++) {
expr_ty value = asdl_seq_GET(values, i);
if (value->kind == Starred_kind) {
value = value->v.Starred.value;
pattern_ty pattern = asdl_seq_GET(patterns, i);
if (pattern->kind == MatchStar_kind) {
if (star >= 0) {
const char *e = "multiple starred names in sequence pattern";
return compiler_error(c, e);
}
star_wildcard = WILDCARD_CHECK(value);
star_wildcard = WILDCARD_STAR_CHECK(pattern);
only_wildcard &= star_wildcard;
star = i;
continue;
}
only_wildcard &= WILDCARD_CHECK(value);
only_wildcard &= WILDCARD_CHECK(pattern);
}
basicblock *end, *fail_pop_1;
RETURN_IF_FALSE(end = compiler_new_block(c));
@ -6095,10 +6178,10 @@ compiler_pattern_sequence(struct compiler *c, expr_ty p, pattern_context *pc)
ADDOP_LOAD_CONST(c, Py_True);
}
else if (star_wildcard) {
RETURN_IF_FALSE(pattern_helper_sequence_subscr(c, values, star, pc));
RETURN_IF_FALSE(pattern_helper_sequence_subscr(c, patterns, star, pc));
}
else {
RETURN_IF_FALSE(pattern_helper_sequence_unpack(c, values, star, pc));
RETURN_IF_FALSE(pattern_helper_sequence_unpack(c, patterns, star, pc));
}
ADDOP_JUMP(c, JUMP_FORWARD, end);
compiler_use_next_block(c, fail_pop_1);
@ -6108,72 +6191,57 @@ compiler_pattern_sequence(struct compiler *c, expr_ty p, pattern_context *pc)
return 1;
}
static int
compiler_pattern_value(struct compiler *c, expr_ty p, pattern_context *pc)
compiler_pattern_value(struct compiler *c, pattern_ty p, pattern_context *pc)
{
assert(p->kind == Attribute_kind);
assert(p->v.Attribute.ctx == Load);
VISIT(c, expr, p);
assert(p->kind == MatchValue_kind);
expr_ty value = p->v.MatchValue.value;
if (!MATCH_VALUE_EXPR(value)) {
const char *e = "patterns may only match literals and attribute lookups";
return compiler_error(c, e);
}
VISIT(c, expr, value);
ADDOP_COMPARE(c, Eq);
return 1;
}
static int
compiler_pattern_wildcard(struct compiler *c, expr_ty p, pattern_context *pc)
compiler_pattern_constant(struct compiler *c, pattern_ty p, pattern_context *pc)
{
assert(p->kind == Name_kind);
assert(p->v.Name.ctx == Store);
assert(WILDCARD_CHECK(p));
if (!pc->allow_irrefutable) {
// Whoops, can't have a wildcard here!
const char *e = "wildcard makes remaining patterns unreachable";
return compiler_error(c, e);
}
ADDOP(c, POP_TOP);
ADDOP_LOAD_CONST(c, Py_True);
assert(p->kind == MatchSingleton_kind);
ADDOP_LOAD_CONST(c, p->v.MatchSingleton.value);
ADDOP_COMPARE(c, Is);
return 1;
}
static int
compiler_pattern(struct compiler *c, expr_ty p, pattern_context *pc)
compiler_pattern(struct compiler *c, pattern_ty p, pattern_context *pc)
{
SET_LOC(c, p);
switch (p->kind) {
case Attribute_kind:
case MatchValue_kind:
return compiler_pattern_value(c, p, pc);
case BinOp_kind:
// Because we allow "2+2j", things like "2+2" make it this far:
return compiler_error(c, "patterns cannot include operators");
case Call_kind:
return compiler_pattern_class(c, p, pc);
case Constant_kind:
return compiler_pattern_literal(c, p, pc);
case Dict_kind:
return compiler_pattern_mapping(c, p, pc);
case JoinedStr_kind:
// Because we allow strings, f-strings make it this far:
return compiler_error(c, "patterns cannot include f-strings");
case List_kind:
case Tuple_kind:
case MatchSingleton_kind:
return compiler_pattern_constant(c, p, pc);
case MatchSequence_kind:
return compiler_pattern_sequence(c, p, pc);
case MatchMapping_kind:
return compiler_pattern_mapping(c, p, pc);
case MatchClass_kind:
return compiler_pattern_class(c, p, pc);
case MatchStar_kind:
return compiler_pattern_star(c, p, pc);
case MatchAs_kind:
return compiler_pattern_as(c, p, pc);
case MatchOr_kind:
return compiler_pattern_or(c, p, pc);
case Name_kind:
if (WILDCARD_CHECK(p)) {
return compiler_pattern_wildcard(c, p, pc);
}
return compiler_pattern_capture(c, p, pc);
default:
Py_UNREACHABLE();
}
// AST validator shouldn't let this happen, but if it does,
// just fail, don't crash out of the interpreter
const char *e = "invalid match pattern node in AST (kind=%d)";
return compiler_error(c, e, p->kind);
}
static int
compiler_match(struct compiler *c, stmt_ty s)
{
@ -6181,7 +6249,7 @@ compiler_match(struct compiler *c, stmt_ty s)
basicblock *next, *end;
RETURN_IF_FALSE(end = compiler_new_block(c));
Py_ssize_t cases = asdl_seq_LEN(s->v.Match.cases);
assert(cases);
assert(cases > 0);
pattern_context pc;
// We use pc.stores to track:
// - Repeated name assignments in the same pattern.
@ -6235,9 +6303,8 @@ compiler_match(struct compiler *c, stmt_ty s)
return 1;
}
#undef WILDCARD_CHECK
#undef WILDCARD_STAR_CHECK
/* End of the compiler section, beginning of the assembler section */

View File

@ -214,6 +214,7 @@ static int symtable_implicit_arg(struct symtable *st, int pos);
static int symtable_visit_annotations(struct symtable *st, arguments_ty, expr_ty);
static int symtable_visit_withitem(struct symtable *st, withitem_ty item);
static int symtable_visit_match_case(struct symtable *st, match_case_ty m);
static int symtable_visit_pattern(struct symtable *st, pattern_ty s);
static identifier top = NULL, lambda = NULL, genexpr = NULL,
@ -246,7 +247,6 @@ symtable_new(void)
goto fail;
st->st_cur = NULL;
st->st_private = NULL;
st->in_pattern = 0;
return st;
fail:
_PySymtable_Free(st);
@ -1676,13 +1676,6 @@ symtable_visit_expr(struct symtable *st, expr_ty e)
VISIT(st, expr, e->v.Slice.step)
break;
case Name_kind:
// Don't make "_" a local when used in a pattern:
if (st->in_pattern &&
e->v.Name.ctx == Store &&
_PyUnicode_EqualToASCIIString(e->v.Name.id, "_"))
{
break;
}
if (!symtable_add_def(st, e->v.Name.id,
e->v.Name.ctx == Load ? USE : DEF_LOCAL))
VISIT_QUIT(st, 0);
@ -1702,12 +1695,55 @@ symtable_visit_expr(struct symtable *st, expr_ty e)
case Tuple_kind:
VISIT_SEQ(st, expr, e->v.Tuple.elts);
break;
}
VISIT_QUIT(st, 1);
}
static int
symtable_visit_pattern(struct symtable *st, pattern_ty p)
{
if (++st->recursion_depth > st->recursion_limit) {
PyErr_SetString(PyExc_RecursionError,
"maximum recursion depth exceeded during compilation");
VISIT_QUIT(st, 0);
}
switch (p->kind) {
case MatchValue_kind:
VISIT(st, expr, p->v.MatchValue.value);
break;
case MatchSingleton_kind:
/* Nothing to do here. */
break;
case MatchSequence_kind:
VISIT_SEQ(st, pattern, p->v.MatchSequence.patterns);
break;
case MatchStar_kind:
if (p->v.MatchStar.name) {
symtable_add_def(st, p->v.MatchStar.name, DEF_LOCAL);
}
break;
case MatchMapping_kind:
VISIT_SEQ(st, expr, p->v.MatchMapping.keys);
VISIT_SEQ(st, pattern, p->v.MatchMapping.patterns);
if (p->v.MatchMapping.rest) {
symtable_add_def(st, p->v.MatchMapping.rest, DEF_LOCAL);
}
break;
case MatchClass_kind:
VISIT(st, expr, p->v.MatchClass.cls);
VISIT_SEQ(st, pattern, p->v.MatchClass.patterns);
VISIT_SEQ(st, pattern, p->v.MatchClass.kwd_patterns);
break;
case MatchAs_kind:
VISIT(st, expr, e->v.MatchAs.pattern);
symtable_add_def(st, e->v.MatchAs.name, DEF_LOCAL);
if (p->v.MatchAs.pattern) {
VISIT(st, pattern, p->v.MatchAs.pattern);
}
if (p->v.MatchAs.name) {
symtable_add_def(st, p->v.MatchAs.name, DEF_LOCAL);
}
break;
case MatchOr_kind:
VISIT_SEQ(st, expr, e->v.MatchOr.patterns);
VISIT_SEQ(st, pattern, p->v.MatchOr.patterns);
break;
}
VISIT_QUIT(st, 1);
@ -1830,11 +1866,7 @@ symtable_visit_withitem(struct symtable *st, withitem_ty item)
static int
symtable_visit_match_case(struct symtable *st, match_case_ty m)
{
assert(!st->in_pattern);
st->in_pattern = 1;
VISIT(st, expr, m->pattern);
assert(st->in_pattern);
st->in_pattern = 0;
VISIT(st, pattern, m->pattern);
if (m->guard) {
VISIT(st, expr, m->guard);
}