Skip to content

Commit ca08236

Browse files
committed
Destruct
1 parent d736349 commit ca08236

15 files changed

Lines changed: 2210 additions & 1032 deletions

Grammar/python.gram

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1135,6 +1135,20 @@ star_atom[expr_ty]:
11351135
| '(' a=target_with_star_atom ')' { _PyPegen_set_expr_context(p, a, Store) }
11361136
| '(' a=[star_targets_tuple_seq] ')' { _PyAST_Tuple(a, Store, EXTRA) }
11371137
| '[' a=[star_targets_list_seq] ']' { _PyAST_List(a, Store, EXTRA) }
1138+
| '{' a=dict_unpack_fields '}' { a }
1139+
1140+
dict_unpack_fields[expr_ty]:
1141+
| a[asdl_seq*]=','.dict_unpack_field+ ',' '**' b=NAME [','] {
1142+
_PyPegen_make_dict_unpack_rest(p, a, b, EXTRA) }
1143+
| '**' b=NAME [','] {
1144+
_PyPegen_make_dict_unpack_rest(p, NULL, b, EXTRA) }
1145+
| a[asdl_seq*]=','.dict_unpack_field+ [','] {
1146+
_PyPegen_make_dict_unpack(p, a, EXTRA) }
1147+
1148+
dict_unpack_field[KeyValuePair*]:
1149+
| a=STRING ':' b=star_atom { _PyPegen_dict_unpack_kv(p, _PyPegen_constant_from_string(p, a), b) }
1150+
| a=NAME ':' b=star_atom { _PyPegen_dict_unpack_kv_name(p, a, b) }
1151+
| a=NAME { _PyPegen_dict_unpack_shorthand(p, a) }
11381152

11391153
single_target[expr_ty]:
11401154
| single_subscript_attribute_target

Include/internal/pycore_ast.h

Lines changed: 13 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Include/internal/pycore_ast_state.h

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Include/internal/pycore_intrinsics.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,10 @@
2929
#define INTRINSIC_TYPEVAR_WITH_CONSTRAINTS 3
3030
#define INTRINSIC_SET_FUNCTION_TYPE_PARAMS 4
3131
#define INTRINSIC_SET_TYPEPARAM_DEFAULT 5
32+
#define INTRINSIC_DESTRUCTURE 6
33+
#define INTRINSIC_DESTRUCTURE_REST 7
3234

33-
#define MAX_INTRINSIC_2 5
35+
#define MAX_INTRINSIC_2 7
3436

3537
typedef PyObject *(*intrinsic_func1)(PyThreadState* tstate, PyObject *value);
3638
typedef PyObject *(*intrinsic_func2)(PyThreadState* tstate, PyObject *value1, PyObject *value2);

Parser/Python.asdl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ module Python
9090
| Name(identifier id, expr_context ctx)
9191
| List(expr* elts, expr_context ctx)
9292
| Tuple(expr* elts, expr_context ctx)
93+
| DictUnpack(expr* keys, expr* targets, expr? rest, expr_context ctx)
9394

9495
-- can appear only in Subscript
9596
| Slice(expr? lower, expr? upper, expr? step)

Parser/action_helpers.c

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,14 @@ _PyPegen_set_expr_context(Parser *p, expr_ty expr, expr_context_ty ctx)
330330
case Starred_kind:
331331
new = _set_starred_context(p, expr, ctx);
332332
break;
333+
case DictUnpack_kind:
334+
new = _PyAST_DictUnpack(
335+
expr->v.DictUnpack.keys,
336+
_set_seq_context(p, expr->v.DictUnpack.targets, ctx),
337+
expr->v.DictUnpack.rest ? _PyPegen_set_expr_context(p, expr->v.DictUnpack.rest, ctx) : NULL,
338+
ctx,
339+
EXTRA_EXPR(expr, expr));
340+
break;
333341
default:
334342
new = expr;
335343
}
@@ -381,6 +389,117 @@ _PyPegen_get_values(Parser *p, asdl_seq *seq)
381389
return new_seq;
382390
}
383391

392+
/* Dict destructuring helpers */
393+
394+
/* Shorthand: {name} -> key=Constant("name"), target=Name("name", Store) */
395+
KeyValuePair *
396+
_PyPegen_dict_unpack_shorthand(Parser *p, expr_ty name)
397+
{
398+
assert(name->kind == Name_kind);
399+
KeyValuePair *a = _PyArena_Malloc(p->arena, sizeof(KeyValuePair));
400+
if (!a) {
401+
return NULL;
402+
}
403+
a->key = _PyAST_Constant(name->v.Name.id, NULL, EXTRA_EXPR(name, name));
404+
if (!a->key) {
405+
return NULL;
406+
}
407+
a->value = _PyAST_Name(name->v.Name.id, Store, EXTRA_EXPR(name, name));
408+
if (!a->value) {
409+
return NULL;
410+
}
411+
return a;
412+
}
413+
414+
/* Explicit string key: {'key': target} */
415+
KeyValuePair *
416+
_PyPegen_dict_unpack_kv(Parser *p, expr_ty string_expr, expr_ty target)
417+
{
418+
KeyValuePair *a = _PyArena_Malloc(p->arena, sizeof(KeyValuePair));
419+
if (!a) {
420+
return NULL;
421+
}
422+
/* string_expr is a Constant from the STRING token */
423+
a->key = string_expr;
424+
a->value = target;
425+
return a;
426+
}
427+
428+
/* Name as key: {name: target} -> key=Constant("name"), target=target */
429+
KeyValuePair *
430+
_PyPegen_dict_unpack_kv_name(Parser *p, expr_ty name, expr_ty target)
431+
{
432+
assert(name->kind == Name_kind);
433+
KeyValuePair *a = _PyArena_Malloc(p->arena, sizeof(KeyValuePair));
434+
if (!a) {
435+
return NULL;
436+
}
437+
a->key = _PyAST_Constant(name->v.Name.id, NULL, EXTRA_EXPR(name, name));
438+
if (!a->key) {
439+
return NULL;
440+
}
441+
a->value = target;
442+
return a;
443+
}
444+
445+
/* Build a DictUnpack AST node from a sequence of KeyValuePair* */
446+
expr_ty
447+
_PyPegen_make_dict_unpack(Parser *p, asdl_seq *seq,
448+
int lineno, int col_offset,
449+
int end_lineno, int end_col_offset,
450+
PyArena *arena)
451+
{
452+
Py_ssize_t len = asdl_seq_LEN(seq);
453+
asdl_expr_seq *keys = _Py_asdl_expr_seq_new(len, arena);
454+
if (!keys) {
455+
return NULL;
456+
}
457+
asdl_expr_seq *targets = _Py_asdl_expr_seq_new(len, arena);
458+
if (!targets) {
459+
return NULL;
460+
}
461+
for (Py_ssize_t i = 0; i < len; i++) {
462+
KeyValuePair *pair = asdl_seq_GET_UNTYPED(seq, i);
463+
asdl_seq_SET(keys, i, pair->key);
464+
asdl_seq_SET(targets, i, pair->value);
465+
}
466+
return _PyAST_DictUnpack(keys, targets, NULL, Store,
467+
lineno, col_offset, end_lineno, end_col_offset,
468+
arena);
469+
}
470+
471+
/* Build a DictUnpack AST node with **rest from a sequence of KeyValuePair* */
472+
expr_ty
473+
_PyPegen_make_dict_unpack_rest(Parser *p, asdl_seq *seq, expr_ty rest_name,
474+
int lineno, int col_offset,
475+
int end_lineno, int end_col_offset,
476+
PyArena *arena)
477+
{
478+
Py_ssize_t len = seq ? asdl_seq_LEN(seq) : 0;
479+
asdl_expr_seq *keys = _Py_asdl_expr_seq_new(len, arena);
480+
if (!keys) {
481+
return NULL;
482+
}
483+
asdl_expr_seq *targets = _Py_asdl_expr_seq_new(len, arena);
484+
if (!targets) {
485+
return NULL;
486+
}
487+
for (Py_ssize_t i = 0; i < len; i++) {
488+
KeyValuePair *pair = asdl_seq_GET_UNTYPED(seq, i);
489+
asdl_seq_SET(keys, i, pair->key);
490+
asdl_seq_SET(targets, i, pair->value);
491+
}
492+
/* rest_name is a Name expr in Load context; convert to Store */
493+
expr_ty rest = _PyAST_Name(rest_name->v.Name.id, Store,
494+
EXTRA_EXPR(rest_name, rest_name));
495+
if (!rest) {
496+
return NULL;
497+
}
498+
return _PyAST_DictUnpack(keys, targets, rest, Store,
499+
lineno, col_offset, end_lineno, end_col_offset,
500+
arena);
501+
}
502+
384503
/* Constructs a KeyPatternPair that is used when parsing mapping & class patterns */
385504
KeyPatternPair *
386505
_PyPegen_key_pattern_pair(Parser *p, expr_ty key, pattern_ty pattern)
@@ -1083,6 +1202,8 @@ _PyPegen_get_expr_name(expr_ty e)
10831202
return "list";
10841203
case Tuple_kind:
10851204
return "tuple";
1205+
case DictUnpack_kind:
1206+
return "dict destructuring";
10861207
case Lambda_kind:
10871208
return "lambda";
10881209
case Call_kind:
@@ -1221,6 +1342,17 @@ _PyPegen_get_invalid_target(expr_ty e, TARGETS_TYPE targets_type)
12211342
case Tuple_kind:
12221343
VISIT_CONTAINER(e, Tuple);
12231344
return NULL;
1345+
case DictUnpack_kind: {
1346+
Py_ssize_t len = asdl_seq_LEN(e->v.DictUnpack.targets);
1347+
for (Py_ssize_t i = 0; i < len; i++) {
1348+
expr_ty other = asdl_seq_GET(e->v.DictUnpack.targets, i);
1349+
expr_ty child = _PyPegen_get_invalid_target(other, targets_type);
1350+
if (child != NULL) {
1351+
return child;
1352+
}
1353+
}
1354+
return NULL;
1355+
}
12241356
case Starred_kind:
12251357
if (targets_type == DEL_TARGETS) {
12261358
return e;

0 commit comments

Comments
 (0)