mirror of
https://github.com/python/cpython.git
synced 2025-08-04 00:48:58 +00:00
bpo-42128: Structural Pattern Matching (PEP 634) (GH-22917)
Co-authored-by: Guido van Rossum <guido@python.org> Co-authored-by: Talin <viridia@gmail.com> Co-authored-by: Pablo Galindo <pablogsal@gmail.com>
This commit is contained in:
parent
cc02b4f2e8
commit
145bf269df
43 changed files with 10867 additions and 2607 deletions
739
Python/compile.c
739
Python/compile.c
|
@ -202,6 +202,11 @@ struct compiler {
|
|||
PyArena *c_arena; /* pointer to memory allocation arena */
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
PyObject *stores;
|
||||
int allow_irrefutable;
|
||||
} pattern_context;
|
||||
|
||||
static int compiler_enter_scope(struct compiler *, identifier, int, void *, int);
|
||||
static void compiler_free(struct compiler *);
|
||||
static basicblock *compiler_new_block(struct compiler *);
|
||||
|
@ -210,7 +215,7 @@ static int compiler_addop(struct compiler *, int);
|
|||
static int compiler_addop_i(struct compiler *, int, Py_ssize_t);
|
||||
static int compiler_addop_j(struct compiler *, int, basicblock *);
|
||||
static int compiler_addop_j_noline(struct compiler *, int, basicblock *);
|
||||
static int compiler_error(struct compiler *, const char *);
|
||||
static int compiler_error(struct compiler *, const char *, ...);
|
||||
static int compiler_warn(struct compiler *, const char *, ...);
|
||||
static int compiler_nameop(struct compiler *, identifier, expr_context_ty);
|
||||
|
||||
|
@ -248,6 +253,11 @@ 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_match(struct compiler *, stmt_ty);
|
||||
static int compiler_pattern_subpattern(struct compiler *, expr_ty,
|
||||
pattern_context *);
|
||||
|
||||
static PyCodeObject *assemble(struct compiler *, int addNone);
|
||||
static PyObject *__doc__, *__annotations__;
|
||||
|
||||
|
@ -1150,6 +1160,16 @@ stack_effect(int opcode, int oparg, int jump)
|
|||
case DICT_MERGE:
|
||||
case DICT_UPDATE:
|
||||
return -1;
|
||||
case COPY_DICT_WITHOUT_KEYS:
|
||||
return 0;
|
||||
case MATCH_CLASS:
|
||||
return -1;
|
||||
case GET_LEN:
|
||||
case MATCH_MAPPING:
|
||||
case MATCH_SEQUENCE:
|
||||
return 1;
|
||||
case MATCH_KEYS:
|
||||
return 2;
|
||||
default:
|
||||
return PY_INVALID_STACK_EFFECT;
|
||||
}
|
||||
|
@ -1580,6 +1600,11 @@ compiler_addop_j_noline(struct compiler *c, int opcode, basicblock *b)
|
|||
} \
|
||||
}
|
||||
|
||||
#define RETURN_IF_FALSE(X) \
|
||||
if (!(X)) { \
|
||||
return 0; \
|
||||
}
|
||||
|
||||
/* Search if variable annotations are present statically in a block. */
|
||||
|
||||
static int
|
||||
|
@ -3414,6 +3439,8 @@ compiler_visit_stmt(struct compiler *c, stmt_ty s)
|
|||
return compiler_while(c, s);
|
||||
case If_kind:
|
||||
return compiler_if(c, s);
|
||||
case Match_kind:
|
||||
return compiler_match(c, s);
|
||||
case Raise_kind:
|
||||
n = 0;
|
||||
if (s->v.Raise.exc) {
|
||||
|
@ -3758,12 +3785,11 @@ starunpack_helper(struct compiler *c, asdl_expr_seq *elts, int pushed,
|
|||
}
|
||||
|
||||
static int
|
||||
assignment_helper(struct compiler *c, asdl_expr_seq *elts)
|
||||
unpack_helper(struct compiler *c, asdl_expr_seq *elts)
|
||||
{
|
||||
Py_ssize_t n = asdl_seq_LEN(elts);
|
||||
Py_ssize_t i;
|
||||
int seen_star = 0;
|
||||
for (i = 0; i < n; i++) {
|
||||
for (Py_ssize_t i = 0; i < n; i++) {
|
||||
expr_ty elt = asdl_seq_GET(elts, i);
|
||||
if (elt->kind == Starred_kind && !seen_star) {
|
||||
if ((i >= (1 << 8)) ||
|
||||
|
@ -3782,7 +3808,15 @@ assignment_helper(struct compiler *c, asdl_expr_seq *elts)
|
|||
if (!seen_star) {
|
||||
ADDOP_I(c, UNPACK_SEQUENCE, n);
|
||||
}
|
||||
for (i = 0; i < n; i++) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
assignment_helper(struct compiler *c, asdl_expr_seq *elts)
|
||||
{
|
||||
Py_ssize_t n = asdl_seq_LEN(elts);
|
||||
RETURN_IF_FALSE(unpack_helper(c, elts));
|
||||
for (Py_ssize_t i = 0; i < n; i++) {
|
||||
expr_ty elt = asdl_seq_GET(elts, i);
|
||||
VISIT(c, expr, elt->kind != Starred_kind ? elt : elt->v.Starred.value);
|
||||
}
|
||||
|
@ -4132,13 +4166,8 @@ validate_keywords(struct compiler *c, asdl_keyword_seq *keywords)
|
|||
for (Py_ssize_t j = i + 1; j < nkeywords; j++) {
|
||||
keyword_ty other = ((keyword_ty)asdl_seq_GET(keywords, j));
|
||||
if (other->arg && !PyUnicode_Compare(key->arg, other->arg)) {
|
||||
PyObject *msg = PyUnicode_FromFormat("keyword argument repeated: %U", key->arg);
|
||||
if (msg == NULL) {
|
||||
return -1;
|
||||
}
|
||||
c->u->u_col_offset = other->col_offset;
|
||||
compiler_error(c, PyUnicode_AsUTF8(msg));
|
||||
Py_DECREF(msg);
|
||||
compiler_error(c, "keyword argument repeated: %U", key->arg);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
@ -5119,6 +5148,10 @@ 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;
|
||||
}
|
||||
|
@ -5305,28 +5338,34 @@ compiler_annassign(struct compiler *c, stmt_ty s)
|
|||
*/
|
||||
|
||||
static int
|
||||
compiler_error(struct compiler *c, const char *errstr)
|
||||
compiler_error(struct compiler *c, const char *format, ...)
|
||||
{
|
||||
PyObject *loc;
|
||||
PyObject *u = NULL, *v = NULL;
|
||||
|
||||
loc = PyErr_ProgramTextObject(c->c_filename, c->u->u_lineno);
|
||||
if (!loc) {
|
||||
va_list vargs;
|
||||
#ifdef HAVE_STDARG_PROTOTYPES
|
||||
va_start(vargs, format);
|
||||
#else
|
||||
va_start(vargs);
|
||||
#endif
|
||||
PyObject *msg = PyUnicode_FromFormatV(format, vargs);
|
||||
va_end(vargs);
|
||||
if (msg == NULL) {
|
||||
return 0;
|
||||
}
|
||||
PyObject *loc = PyErr_ProgramTextObject(c->c_filename, c->u->u_lineno);
|
||||
if (loc == NULL) {
|
||||
Py_INCREF(Py_None);
|
||||
loc = Py_None;
|
||||
}
|
||||
u = Py_BuildValue("(OiiO)", c->c_filename, c->u->u_lineno,
|
||||
c->u->u_col_offset + 1, loc);
|
||||
if (!u)
|
||||
PyObject *args = Py_BuildValue("O(OiiO)", msg, c->c_filename,
|
||||
c->u->u_lineno, c->u->u_col_offset + 1, loc);
|
||||
Py_DECREF(msg);
|
||||
if (args == NULL) {
|
||||
goto exit;
|
||||
v = Py_BuildValue("(zO)", errstr, u);
|
||||
if (!v)
|
||||
goto exit;
|
||||
PyErr_SetObject(PyExc_SyntaxError, v);
|
||||
}
|
||||
PyErr_SetObject(PyExc_SyntaxError, args);
|
||||
exit:
|
||||
Py_DECREF(loc);
|
||||
Py_XDECREF(u);
|
||||
Py_XDECREF(v);
|
||||
Py_XDECREF(args);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -5421,6 +5460,654 @@ compiler_slice(struct compiler *c, expr_ty s)
|
|||
return 1;
|
||||
}
|
||||
|
||||
|
||||
// PEP 634: Structural Pattern Matching
|
||||
|
||||
// To keep things simple, all compiler_pattern_* routines follow the convention
|
||||
// of replacing TOS (the subject for the given pattern) with either True (match)
|
||||
// or False (no match). We do this even for irrefutable patterns; the idea is
|
||||
// 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, "_"))
|
||||
|
||||
|
||||
static int
|
||||
pattern_helper_store_name(struct compiler *c, identifier n, pattern_context *pc)
|
||||
{
|
||||
assert(!_PyUnicode_EqualToASCIIString(n, "_"));
|
||||
// Can't assign to the same name twice:
|
||||
if (pc->stores == NULL) {
|
||||
RETURN_IF_FALSE(pc->stores = PySet_New(NULL));
|
||||
}
|
||||
else {
|
||||
int duplicate = PySet_Contains(pc->stores, n);
|
||||
if (duplicate < 0) {
|
||||
return 0;
|
||||
}
|
||||
if (duplicate) {
|
||||
const char *e = "multiple assignments to name %R in pattern";
|
||||
return compiler_error(c, e, n);
|
||||
}
|
||||
}
|
||||
RETURN_IF_FALSE(!PySet_Add(pc->stores, n));
|
||||
RETURN_IF_FALSE(compiler_nameop(c, n, Store));
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
pattern_helper_sequence_unpack(struct compiler *c, asdl_expr_seq *values,
|
||||
Py_ssize_t star, pattern_context *pc)
|
||||
{
|
||||
RETURN_IF_FALSE(unpack_helper(c, values));
|
||||
// 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);
|
||||
fails = (basicblock **)PyObject_Malloc(sizeof(basicblock*) * size);
|
||||
if (fails == NULL) {
|
||||
PyErr_NoMemory();
|
||||
return 0;
|
||||
}
|
||||
// NOTE: Can't use our nice returning macros anymore: they'll leak memory!
|
||||
// goto error on error.
|
||||
for (Py_ssize_t i = 0; i < size; i++) {
|
||||
fails[i] = compiler_new_block(c);
|
||||
if (fails[i] == NULL) {
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
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) ||
|
||||
!compiler_addop_j(c, POP_JUMP_IF_FALSE, fails[i]) ||
|
||||
compiler_next_block(c) == NULL)
|
||||
{
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
// Success!
|
||||
basicblock *end = compiler_new_block(c);
|
||||
if (end == NULL ||
|
||||
!compiler_addop_load_const(c, Py_True) ||
|
||||
!compiler_addop_j(c, JUMP_FORWARD, end))
|
||||
{
|
||||
goto error;
|
||||
}
|
||||
// This is where we handle failed sub-patterns. For a sequence pattern like
|
||||
// [a, b, c, d], this will look like:
|
||||
// fails[0]: POP_TOP
|
||||
// fails[1]: POP_TOP
|
||||
// fails[2]: POP_TOP
|
||||
// fails[3]: LOAD_CONST False
|
||||
for (Py_ssize_t i = 0; i < size - 1; i++) {
|
||||
compiler_use_next_block(c, fails[i]);
|
||||
if (!compiler_addop(c, POP_TOP)) {
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
compiler_use_next_block(c, fails[size - 1]);
|
||||
if (!compiler_addop_load_const(c, Py_False)) {
|
||||
goto error;
|
||||
}
|
||||
compiler_use_next_block(c, end);
|
||||
PyObject_Free(fails);
|
||||
return 1;
|
||||
error:
|
||||
PyObject_Free(fails);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Like pattern_helper_sequence_unpack, but uses BINARY_SUBSCR instead of
|
||||
// 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,
|
||||
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);
|
||||
for (Py_ssize_t i = 0; i < size; i++) {
|
||||
expr_ty value = asdl_seq_GET(values, i);
|
||||
if (WILDCARD_CHECK(value)) {
|
||||
continue;
|
||||
}
|
||||
if (i == star) {
|
||||
assert(value->kind == Starred_kind);
|
||||
assert(WILDCARD_CHECK(value->v.Starred.value));
|
||||
continue;
|
||||
}
|
||||
ADDOP(c, DUP_TOP);
|
||||
if (i < star) {
|
||||
ADDOP_LOAD_CONST_NEW(c, PyLong_FromSsize_t(i));
|
||||
}
|
||||
else {
|
||||
// The subject may not support negative indexing! Compute a
|
||||
// nonnegative index:
|
||||
ADDOP(c, GET_LEN);
|
||||
ADDOP_LOAD_CONST_NEW(c, PyLong_FromSsize_t(size - i));
|
||||
ADDOP(c, BINARY_SUBTRACT);
|
||||
}
|
||||
ADDOP(c, BINARY_SUBSCR);
|
||||
RETURN_IF_FALSE(compiler_pattern_subpattern(c, value, pc));
|
||||
ADDOP_JUMP(c, POP_JUMP_IF_FALSE, fail_pop_1);
|
||||
NEXT_BLOCK(c);
|
||||
}
|
||||
ADDOP(c, POP_TOP);
|
||||
ADDOP_LOAD_CONST(c, Py_True);
|
||||
ADDOP_JUMP(c, JUMP_FORWARD, end);
|
||||
compiler_use_next_block(c, fail_pop_1);
|
||||
ADDOP(c, POP_TOP);
|
||||
ADDOP_LOAD_CONST(c, Py_False);
|
||||
compiler_use_next_block(c, end);
|
||||
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)
|
||||
{
|
||||
int allow_irrefutable = pc->allow_irrefutable;
|
||||
pc->allow_irrefutable = 1;
|
||||
RETURN_IF_FALSE(compiler_pattern(c, p, pc));
|
||||
pc->allow_irrefutable = allow_irrefutable;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
compiler_pattern_as(struct compiler *c, expr_ty p, pattern_context *pc)
|
||||
{
|
||||
assert(p->kind == MatchAs_kind);
|
||||
basicblock *end, *fail_pop_1;
|
||||
RETURN_IF_FALSE(end = compiler_new_block(c));
|
||||
RETURN_IF_FALSE(fail_pop_1 = compiler_new_block(c));
|
||||
// Need to make a copy for (possibly) storing later:
|
||||
ADDOP(c, DUP_TOP);
|
||||
RETURN_IF_FALSE(compiler_pattern(c, p->v.MatchAs.pattern, pc));
|
||||
ADDOP_JUMP(c, POP_JUMP_IF_FALSE, fail_pop_1);
|
||||
NEXT_BLOCK(c);
|
||||
RETURN_IF_FALSE(pattern_helper_store_name(c, p->v.MatchAs.name, pc));
|
||||
ADDOP_LOAD_CONST(c, Py_True);
|
||||
ADDOP_JUMP(c, JUMP_FORWARD, end);
|
||||
compiler_use_next_block(c, fail_pop_1);
|
||||
// Need to pop that unused copy from before:
|
||||
ADDOP(c, POP_TOP);
|
||||
ADDOP_LOAD_CONST(c, Py_False);
|
||||
compiler_use_next_block(c, end);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
compiler_pattern_capture(struct compiler *c, expr_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 name capture here!
|
||||
const char *e = "name capture %R makes remaining patterns unreachable";
|
||||
return compiler_error(c, e, p->v.Name.id);
|
||||
}
|
||||
RETURN_IF_FALSE(pattern_helper_store_name(c, p->v.Name.id, pc));
|
||||
ADDOP_LOAD_CONST(c, Py_True);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
compiler_pattern_class(struct compiler *c, expr_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);
|
||||
}
|
||||
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));
|
||||
Py_ssize_t i;
|
||||
for (i = 0; i < nkwargs; i++) {
|
||||
PyObject *name = ((keyword_ty) asdl_seq_GET(kwargs, i))->arg;
|
||||
Py_INCREF(name);
|
||||
PyTuple_SET_ITEM(kwnames, i, name);
|
||||
}
|
||||
ADDOP_LOAD_CONST_NEW(c, kwnames);
|
||||
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;
|
||||
if (i < nargs) {
|
||||
// Positional:
|
||||
arg = asdl_seq_GET(args, i);
|
||||
}
|
||||
else {
|
||||
// Keyword:
|
||||
arg = ((keyword_ty) asdl_seq_GET(kwargs, i - nargs))->value;
|
||||
}
|
||||
if (WILDCARD_CHECK(arg)) {
|
||||
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));
|
||||
ADDOP_JUMP(c, POP_JUMP_IF_FALSE, fail_pop_1);
|
||||
NEXT_BLOCK(c);
|
||||
}
|
||||
// Success! Pop the tuple of attributes:
|
||||
ADDOP(c, POP_TOP);
|
||||
ADDOP_LOAD_CONST(c, Py_True);
|
||||
ADDOP_JUMP(c, JUMP_FORWARD, end);
|
||||
compiler_use_next_block(c, fail_pop_1);
|
||||
ADDOP(c, POP_TOP);
|
||||
ADDOP_LOAD_CONST(c, Py_False);
|
||||
compiler_use_next_block(c, end);
|
||||
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)
|
||||
{
|
||||
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 guranteed to be last:
|
||||
int star = size ? !asdl_seq_GET(keys, size - 1) : 0;
|
||||
ADDOP(c, MATCH_MAPPING);
|
||||
ADDOP_JUMP(c, POP_JUMP_IF_FALSE, fail_pop_1);
|
||||
NEXT_BLOCK(c);
|
||||
if (!size) {
|
||||
// If the pattern is just "{}", we're done!
|
||||
ADDOP(c, POP_TOP);
|
||||
ADDOP_LOAD_CONST(c, Py_True);
|
||||
ADDOP_JUMP(c, JUMP_FORWARD, end);
|
||||
compiler_use_next_block(c, fail_pop_1);
|
||||
ADDOP(c, POP_TOP);
|
||||
ADDOP_LOAD_CONST(c, Py_False);
|
||||
compiler_use_next_block(c, end);
|
||||
return 1;
|
||||
}
|
||||
if (size - star) {
|
||||
// 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_COMPARE(c, GtE);
|
||||
ADDOP_JUMP(c, POP_JUMP_IF_FALSE, fail_pop_1);
|
||||
NEXT_BLOCK(c);
|
||||
}
|
||||
if (INT_MAX < size - star - 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++) {
|
||||
expr_ty key = asdl_seq_GET(keys, i);
|
||||
if (key == NULL) {
|
||||
const char *e = "can't use starred name here "
|
||||
"(consider moving to end)";
|
||||
return compiler_error(c, e);
|
||||
}
|
||||
VISIT(c, expr, key);
|
||||
}
|
||||
ADDOP_I(c, BUILD_TUPLE, size - star);
|
||||
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)) {
|
||||
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));
|
||||
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:
|
||||
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));
|
||||
}
|
||||
else {
|
||||
// Otherwise, we don't care about this tuple of keys anymore:
|
||||
ADDOP(c, POP_TOP);
|
||||
}
|
||||
// Pop the subject:
|
||||
ADDOP(c, POP_TOP);
|
||||
ADDOP_LOAD_CONST(c, Py_True);
|
||||
ADDOP_JUMP(c, JUMP_FORWARD, end);
|
||||
// The top two items are a tuple of values or None, followed by a tuple of
|
||||
// keys. Pop them both:
|
||||
compiler_use_next_block(c, fail_pop_3);
|
||||
ADDOP(c, POP_TOP);
|
||||
ADDOP(c, POP_TOP);
|
||||
compiler_use_next_block(c, fail_pop_1);
|
||||
// Pop the subject:
|
||||
ADDOP(c, POP_TOP);
|
||||
ADDOP_LOAD_CONST(c, Py_False);
|
||||
compiler_use_next_block(c, end);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
compiler_pattern_or(struct compiler *c, expr_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
|
||||
// others bind the same names (they should), then this becomes pc->stores.
|
||||
PyObject *control = NULL;
|
||||
basicblock *end, *pass_pop_1;
|
||||
RETURN_IF_FALSE(end = compiler_new_block(c));
|
||||
RETURN_IF_FALSE(pass_pop_1 = compiler_new_block(c));
|
||||
Py_ssize_t size = asdl_seq_LEN(p->v.MatchOr.patterns);
|
||||
assert(size > 1);
|
||||
// We're going to be messing with pc. Keep the original info handy:
|
||||
PyObject *stores_init = pc->stores;
|
||||
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);
|
||||
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;
|
||||
pc->allow_irrefutable = allow_irrefutable && is_last;
|
||||
SET_LOC(c, alt);
|
||||
if (pc->stores == NULL ||
|
||||
// Only copy the subject if we're *not* on the last alternative:
|
||||
(!is_last && !compiler_addop(c, DUP_TOP)) ||
|
||||
!compiler_pattern(c, alt, pc) ||
|
||||
// Only jump if we're *not* on the last alternative:
|
||||
(!is_last && !compiler_addop_j(c, POP_JUMP_IF_TRUE, pass_pop_1)) ||
|
||||
!compiler_next_block(c))
|
||||
{
|
||||
goto fail;
|
||||
}
|
||||
if (!i) {
|
||||
// If this is the first alternative, save its stores as a "control"
|
||||
// for the others (they can't bind a different set of names):
|
||||
control = pc->stores;
|
||||
continue;
|
||||
}
|
||||
if (PySet_GET_SIZE(pc->stores) || PySet_GET_SIZE(control)) {
|
||||
// Otherwise, check to see if we differ from the control set:
|
||||
PyObject *diff = PyNumber_InPlaceXor(pc->stores, control);
|
||||
if (diff == NULL) {
|
||||
goto fail;
|
||||
}
|
||||
if (PySet_GET_SIZE(diff)) {
|
||||
// The names differ! Raise.
|
||||
Py_DECREF(diff);
|
||||
compiler_error(c, "alternative patterns bind different names");
|
||||
goto fail;
|
||||
}
|
||||
Py_DECREF(diff);
|
||||
}
|
||||
Py_DECREF(pc->stores);
|
||||
}
|
||||
Py_XDECREF(stores_init);
|
||||
// Update pc->stores and restore pc->allow_irrefutable:
|
||||
pc->stores = control;
|
||||
pc->allow_irrefutable = allow_irrefutable;
|
||||
ADDOP_JUMP(c, JUMP_FORWARD, end);
|
||||
compiler_use_next_block(c, pass_pop_1);
|
||||
ADDOP(c, POP_TOP);
|
||||
ADDOP_LOAD_CONST(c, Py_True);
|
||||
compiler_use_next_block(c, end);
|
||||
return 1;
|
||||
fail:
|
||||
Py_XDECREF(stores_init);
|
||||
Py_XDECREF(control);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
compiler_pattern_sequence(struct compiler *c, expr_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);
|
||||
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;
|
||||
if (star >= 0) {
|
||||
const char *e = "multiple starred names in sequence pattern";
|
||||
return compiler_error(c, e);
|
||||
}
|
||||
star_wildcard = WILDCARD_CHECK(value);
|
||||
star = i;
|
||||
}
|
||||
only_wildcard &= WILDCARD_CHECK(value);
|
||||
}
|
||||
basicblock *end, *fail_pop_1;
|
||||
RETURN_IF_FALSE(end = compiler_new_block(c));
|
||||
RETURN_IF_FALSE(fail_pop_1 = compiler_new_block(c));
|
||||
ADDOP(c, MATCH_SEQUENCE);
|
||||
ADDOP_JUMP(c, POP_JUMP_IF_FALSE, fail_pop_1);
|
||||
NEXT_BLOCK(c);
|
||||
if (star < 0) {
|
||||
// No star: len(subject) == size
|
||||
ADDOP(c, GET_LEN);
|
||||
ADDOP_LOAD_CONST_NEW(c, PyLong_FromSsize_t(size));
|
||||
ADDOP_COMPARE(c, Eq);
|
||||
ADDOP_JUMP(c, POP_JUMP_IF_FALSE, fail_pop_1);
|
||||
NEXT_BLOCK(c);
|
||||
}
|
||||
else if (size > 1) {
|
||||
// Star: len(subject) >= size - 1
|
||||
ADDOP(c, GET_LEN);
|
||||
ADDOP_LOAD_CONST_NEW(c, PyLong_FromSsize_t(size - 1));
|
||||
ADDOP_COMPARE(c, GtE);
|
||||
ADDOP_JUMP(c, POP_JUMP_IF_FALSE, fail_pop_1);
|
||||
NEXT_BLOCK(c);
|
||||
}
|
||||
if (only_wildcard) {
|
||||
// Patterns like: [] / [_] / [_, _] / [*_] / [_, *_] / [_, _, *_] / etc.
|
||||
ADDOP(c, POP_TOP);
|
||||
ADDOP_LOAD_CONST(c, Py_True);
|
||||
}
|
||||
else if (star_wildcard) {
|
||||
RETURN_IF_FALSE(pattern_helper_sequence_subscr(c, values, star, pc));
|
||||
}
|
||||
else {
|
||||
RETURN_IF_FALSE(pattern_helper_sequence_unpack(c, values, star, pc));
|
||||
}
|
||||
ADDOP_JUMP(c, JUMP_FORWARD, end);
|
||||
compiler_use_next_block(c, fail_pop_1);
|
||||
ADDOP(c, POP_TOP)
|
||||
ADDOP_LOAD_CONST(c, Py_False);
|
||||
compiler_use_next_block(c, end);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
compiler_pattern_value(struct compiler *c, expr_ty p, pattern_context *pc)
|
||||
{
|
||||
assert(p->kind == Attribute_kind);
|
||||
assert(p->v.Attribute.ctx == Load);
|
||||
VISIT(c, expr, p);
|
||||
ADDOP_COMPARE(c, Eq);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
compiler_pattern_wildcard(struct compiler *c, expr_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);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
compiler_pattern(struct compiler *c, expr_ty p, pattern_context *pc)
|
||||
{
|
||||
SET_LOC(c, p);
|
||||
switch (p->kind) {
|
||||
case Attribute_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:
|
||||
return compiler_pattern_sequence(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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
compiler_match(struct compiler *c, stmt_ty s)
|
||||
{
|
||||
VISIT(c, expr, s->v.Match.subject);
|
||||
basicblock *next, *end;
|
||||
RETURN_IF_FALSE(end = compiler_new_block(c));
|
||||
Py_ssize_t cases = asdl_seq_LEN(s->v.Match.cases);
|
||||
assert(cases);
|
||||
pattern_context pc;
|
||||
// We use pc.stores to track:
|
||||
// - Repeated name assignments in the same pattern.
|
||||
// - Different name assignments in alternatives.
|
||||
// It's a set of names, but we don't create it until it's needed:
|
||||
pc.stores = NULL;
|
||||
match_case_ty m = asdl_seq_GET(s->v.Match.cases, cases - 1);
|
||||
int has_default = WILDCARD_CHECK(m->pattern) && 1 < cases;
|
||||
for (Py_ssize_t i = 0; i < cases - has_default; i++) {
|
||||
m = asdl_seq_GET(s->v.Match.cases, i);
|
||||
SET_LOC(c, m->pattern);
|
||||
RETURN_IF_FALSE(next = compiler_new_block(c));
|
||||
// If pc.allow_irrefutable is 0, any name captures against our subject
|
||||
// will raise. Irrefutable cases must be either guarded, last, or both:
|
||||
pc.allow_irrefutable = m->guard != NULL || i == cases - 1;
|
||||
// Only copy the subject if we're *not* on the last case:
|
||||
if (i != cases - has_default - 1) {
|
||||
ADDOP(c, DUP_TOP);
|
||||
}
|
||||
int result = compiler_pattern(c, m->pattern, &pc);
|
||||
Py_CLEAR(pc.stores);
|
||||
RETURN_IF_FALSE(result);
|
||||
ADDOP_JUMP(c, POP_JUMP_IF_FALSE, next);
|
||||
NEXT_BLOCK(c);
|
||||
if (m->guard) {
|
||||
RETURN_IF_FALSE(compiler_jump_if(c, m->guard, next, 0));
|
||||
}
|
||||
// Success! Pop the subject off, we're done with it:
|
||||
if (i != cases - has_default - 1) {
|
||||
ADDOP(c, POP_TOP);
|
||||
}
|
||||
VISIT_SEQ(c, stmt, m->body);
|
||||
ADDOP_JUMP(c, JUMP_FORWARD, end);
|
||||
compiler_use_next_block(c, next);
|
||||
}
|
||||
if (has_default) {
|
||||
if (cases == 1) {
|
||||
// No matches. Done with the subject:
|
||||
ADDOP(c, POP_TOP);
|
||||
}
|
||||
// A trailing "case _" is common, and lets us save a bit of redundant
|
||||
// pushing and popping in the loop above:
|
||||
m = asdl_seq_GET(s->v.Match.cases, cases - 1);
|
||||
SET_LOC(c, m->pattern);
|
||||
if (m->guard) {
|
||||
RETURN_IF_FALSE(compiler_jump_if(c, m->guard, end, 0));
|
||||
}
|
||||
VISIT_SEQ(c, stmt, m->body);
|
||||
}
|
||||
compiler_use_next_block(c, end);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
#undef WILDCARD_CHECK
|
||||
|
||||
|
||||
/* End of the compiler section, beginning of the assembler section */
|
||||
|
||||
/* do depth-first search of basic block graph, starting with block.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue