Skip to content

Commit 2c83894

Browse files
committed
py: Implement default and star args for lambdas.
1 parent cbd9ae5 commit 2c83894

4 files changed

Lines changed: 49 additions & 29 deletions

File tree

py/compile.c

Lines changed: 35 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -572,8 +572,9 @@ STATIC void close_over_variables_etc(compiler_t *comp, scope_t *this_scope, int
572572
}
573573
}
574574

575-
STATIC void compile_funcdef_param(compiler_t *comp, mp_parse_node_t pn) {
576-
if (MP_PARSE_NODE_IS_STRUCT_KIND(pn, PN_typedargslist_star)) {
575+
STATIC void compile_funcdef_lambdef_param(compiler_t *comp, mp_parse_node_t pn) {
576+
if (MP_PARSE_NODE_IS_STRUCT_KIND(pn, PN_typedargslist_star)
577+
|| MP_PARSE_NODE_IS_STRUCT_KIND(pn, PN_varargslist_star)) {
577578
comp->have_star = true;
578579
/* don't need to distinguish bare from named star
579580
mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)pn;
@@ -584,7 +585,8 @@ STATIC void compile_funcdef_param(compiler_t *comp, mp_parse_node_t pn) {
584585
}
585586
*/
586587

587-
} else if (MP_PARSE_NODE_IS_STRUCT_KIND(pn, PN_typedargslist_dbl_star)) {
588+
} else if (MP_PARSE_NODE_IS_STRUCT_KIND(pn, PN_typedargslist_dbl_star)
589+
|| MP_PARSE_NODE_IS_STRUCT_KIND(pn, PN_varargslist_dbl_star)) {
588590
// named double star
589591
// TODO do we need to do anything with this?
590592

@@ -599,15 +601,21 @@ STATIC void compile_funcdef_param(compiler_t *comp, mp_parse_node_t pn) {
599601
pn_colon = MP_PARSE_NODE_NULL;
600602
pn_equal = MP_PARSE_NODE_NULL;
601603

602-
} else {
604+
} else if (MP_PARSE_NODE_IS_STRUCT_KIND(pn, PN_typedargslist_name)) {
603605
// this parameter has a colon and/or equal specifier
604606

605-
assert(MP_PARSE_NODE_IS_STRUCT_KIND(pn, PN_typedargslist_name)); // should be
606-
607607
mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)pn;
608608
pn_id = pns->nodes[0];
609609
pn_colon = pns->nodes[1];
610610
pn_equal = pns->nodes[2];
611+
612+
} else {
613+
assert(MP_PARSE_NODE_IS_STRUCT_KIND(pn, PN_varargslist_name)); // should be
614+
// this parameter has an equal specifier
615+
616+
mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)pn;
617+
pn_id = pns->nodes[0];
618+
pn_equal = pns->nodes[1];
611619
}
612620

613621
if (MP_PARSE_NODE_IS_NULL(pn_equal)) {
@@ -653,24 +661,15 @@ STATIC void compile_funcdef_param(compiler_t *comp, mp_parse_node_t pn) {
653661
}
654662
}
655663

656-
// leaves function object on stack
657-
// returns function name
658-
STATIC qstr compile_funcdef_helper(compiler_t *comp, mp_parse_node_struct_t *pns, uint emit_options) {
659-
if (comp->pass == MP_PASS_SCOPE) {
660-
// create a new scope for this function
661-
scope_t *s = scope_new_and_link(comp, SCOPE_FUNCTION, (mp_parse_node_t)pns, emit_options);
662-
// store the function scope so the compiling function can use it at each pass
663-
pns->nodes[4] = (mp_parse_node_t)s;
664-
}
665-
664+
STATIC void compile_funcdef_lambdef(compiler_t *comp, scope_t *scope, mp_parse_node_t pn_params, pn_kind_t pn_list_kind) {
666665
// compile default parameters
667666
comp->have_star = false;
668667
comp->num_dict_params = 0;
669668
comp->num_default_params = 0;
670-
apply_to_single_or_list(comp, pns->nodes[1], PN_typedargslist, compile_funcdef_param);
669+
apply_to_single_or_list(comp, pn_params, pn_list_kind, compile_funcdef_lambdef_param);
671670

672671
if (comp->compile_error != MP_OBJ_NULL) {
673-
return MP_QSTR_NULL;
672+
return;
674673
}
675674

676675
// in Micro Python we put the default positional parameters into a tuple using the bytecode
@@ -680,11 +679,25 @@ STATIC qstr compile_funcdef_helper(compiler_t *comp, mp_parse_node_struct_t *pns
680679
EMIT(load_null); // sentinel indicating empty default keyword args
681680
}
682681

682+
// make the function
683+
close_over_variables_etc(comp, scope, comp->num_default_params, comp->num_dict_params);
684+
}
685+
686+
// leaves function object on stack
687+
// returns function name
688+
STATIC qstr compile_funcdef_helper(compiler_t *comp, mp_parse_node_struct_t *pns, uint emit_options) {
689+
if (comp->pass == MP_PASS_SCOPE) {
690+
// create a new scope for this function
691+
scope_t *s = scope_new_and_link(comp, SCOPE_FUNCTION, (mp_parse_node_t)pns, emit_options);
692+
// store the function scope so the compiling function can use it at each pass
693+
pns->nodes[4] = (mp_parse_node_t)s;
694+
}
695+
683696
// get the scope for this function
684697
scope_t *fscope = (scope_t*)pns->nodes[4];
685698

686-
// make the function
687-
close_over_variables_etc(comp, fscope, comp->num_default_params, comp->num_dict_params);
699+
// compile the function definition
700+
compile_funcdef_lambdef(comp, fscope, pns->nodes[1], PN_typedargslist);
688701

689702
// return its name (the 'f' in "def f(...):")
690703
return fscope->simple_name;
@@ -1762,10 +1775,6 @@ STATIC void compile_test_if_expr(compiler_t *comp, mp_parse_node_struct_t *pns)
17621775
}
17631776

17641777
STATIC void compile_lambdef(compiler_t *comp, mp_parse_node_struct_t *pns) {
1765-
// TODO default params etc for lambda; possibly just use funcdef code
1766-
//mp_parse_node_t pn_params = pns->nodes[0];
1767-
//mp_parse_node_t pn_body = pns->nodes[1];
1768-
17691778
if (comp->pass == MP_PASS_SCOPE) {
17701779
// create a new scope for this lambda
17711780
scope_t *s = scope_new_and_link(comp, SCOPE_LAMBDA, (mp_parse_node_t)pns, comp->scope_cur->emit_options);
@@ -1776,8 +1785,8 @@ STATIC void compile_lambdef(compiler_t *comp, mp_parse_node_struct_t *pns) {
17761785
// get the scope for this lambda
17771786
scope_t *this_scope = (scope_t*)pns->nodes[2];
17781787

1779-
// make the lambda
1780-
close_over_variables_etc(comp, this_scope, 0, 0);
1788+
// compile the lambda definition
1789+
compile_funcdef_lambdef(comp, this_scope, pns->nodes[0], PN_varargslist);
17811790
}
17821791

17831792
STATIC void compile_or_and_test(compiler_t *comp, mp_parse_node_struct_t *pns, bool cond) {

py/grammar.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,10 +72,10 @@ DEF_RULE(tfpdef, nc, and(2), tok(NAME), opt_rule(typedargslist_colon))
7272
// TODO varargslist lets through more than is allowed
7373
DEF_RULE(varargslist, nc, list_with_end, rule(varargslist_item), tok(DEL_COMMA))
7474
DEF_RULE(varargslist_item, nc, or(3), rule(varargslist_name), rule(varargslist_star), rule(varargslist_dbl_star))
75-
DEF_RULE(varargslist_name, nc, and(2), tok(NAME), opt_rule(varargslist_equal))
75+
DEF_RULE(varargslist_name, nc, ident | and(2), tok(NAME), opt_rule(varargslist_equal))
7676
DEF_RULE(varargslist_star, nc, and(2), tok(OP_STAR), opt_rule(vfpdef))
7777
DEF_RULE(varargslist_dbl_star, nc, and(2), tok(OP_DBL_STAR), tok(NAME))
78-
DEF_RULE(varargslist_equal, nc, and(2), tok(DEL_EQUAL), rule(test))
78+
DEF_RULE(varargslist_equal, nc, ident | and(2), tok(DEL_EQUAL), rule(test))
7979
DEF_RULE(vfpdef, nc, ident | and(1), tok(NAME))
8080

8181
// stmt: compound_stmt | simple_stmt

py/parse.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -853,7 +853,6 @@ mp_parse_tree_t mp_parse(mp_lexer_t *lex, mp_parse_input_kind_t input_kind) {
853853
// rule should not be emitted if it has only 1 argument
854854
// NOTE: can't set this flag for atom_paren because we need it
855855
// to distinguish, for example, [a,b] from [(a,b)]
856-
// TODO possibly set for: varargslist_name, varargslist_equal
857856
if (rule->act & RULE_ACT_ALLOW_IDENT) {
858857
emit_rule = false;
859858
}

tests/basics/lambda_defargs.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# test default args with lambda
2+
3+
f = lambda x=1: x
4+
print(f(), f(2), f(x=3))
5+
6+
y = 'y'
7+
f = lambda x=y: x
8+
print(f())
9+
10+
f = lambda x, y=[]: (x, y)
11+
f(0)[1].append(1)
12+
print(f(1), f(x=2), f(3, 4), f(4, y=5))

0 commit comments

Comments
 (0)