aboutsummaryrefslogtreecommitdiff
path: root/py/compile.c
diff options
context:
space:
mode:
authorDavid Lechner <david@lechnology.com>2020-03-24 23:54:45 -0500
committerDamien George <damien@micropython.org>2022-03-31 16:59:30 +1100
commit783b1a868fb0f3c1fd6cf7231311c24801c33505 (patch)
treecb182083ad7661e8026ce5b280e972511389beaa /py/compile.c
parent1e99d29f362f8cccc60a36fcbd8590404c719f40 (diff)
py/runtime: Allow multiple *args in a function call.
This is a partial implementation of PEP 448 to allow unpacking multiple star args in a function or method call. This is implemented by changing the emitted bytecodes so that both positional args and star args are stored as positional args. A bitmap is added to indicate if an argument at a given position is a positional argument or a star arg. In the generated code, this new bitmap takes the place of the old star arg. It is stored as a small int, so this means only the first N arguments can be star args where N is the number of bits in a small int. The runtime is modified to interpret this new bytecode format while still trying to perform as few memory reallocations as possible. Signed-off-by: David Lechner <david@pybricks.com>
Diffstat (limited to 'py/compile.c')
-rw-r--r--py/compile.c37
1 files changed, 23 insertions, 14 deletions
diff --git a/py/compile.c b/py/compile.c
index 1636bd157..fc1106270 100644
--- a/py/compile.c
+++ b/py/compile.c
@@ -37,6 +37,7 @@
#include "py/asmbase.h"
#include "py/nativeglue.h"
#include "py/persistentcode.h"
+#include "py/smallint.h"
#if MICROPY_ENABLE_COMPILER
@@ -2397,17 +2398,30 @@ STATIC void compile_trailer_paren_helper(compiler_t *comp, mp_parse_node_t pn_ar
int n_positional = n_positional_extra;
uint n_keyword = 0;
uint star_flags = 0;
- mp_parse_node_struct_t *star_args_node = NULL;
+ mp_uint_t star_args = 0;
for (size_t i = 0; i < n_args; i++) {
if (MP_PARSE_NODE_IS_STRUCT(args[i])) {
mp_parse_node_struct_t *pns_arg = (mp_parse_node_struct_t *)args[i];
if (MP_PARSE_NODE_STRUCT_KIND(pns_arg) == PN_arglist_star) {
- if (star_flags & MP_EMIT_STAR_FLAG_SINGLE) {
- compile_syntax_error(comp, (mp_parse_node_t)pns_arg, MP_ERROR_TEXT("can't have multiple *x"));
+ if (star_flags & MP_EMIT_STAR_FLAG_DOUBLE) {
+ compile_syntax_error(comp, (mp_parse_node_t)pns_arg, MP_ERROR_TEXT("* arg after **"));
+ return;
+ }
+ #if MICROPY_DYNAMIC_COMPILER
+ if (i > mp_dynamic_compiler.small_int_bits)
+ #else
+ if (i > MP_SMALL_INT_BITS)
+ #endif
+ {
+ // If there are not enough bits in a small int to fit the flag, then we consider
+ // it a syntax error. It should be unlikely to have this many args in practice.
+ compile_syntax_error(comp, (mp_parse_node_t)pns_arg, MP_ERROR_TEXT("too many args"));
return;
}
star_flags |= MP_EMIT_STAR_FLAG_SINGLE;
- star_args_node = pns_arg;
+ star_args |= 1 << i;
+ compile_node(comp, pns_arg->nodes[0]);
+ n_positional++;
} else if (MP_PARSE_NODE_STRUCT_KIND(pns_arg) == PN_arglist_dbl_star) {
star_flags |= MP_EMIT_STAR_FLAG_DOUBLE;
// double-star args are stored as kw arg with key of None
@@ -2438,12 +2452,12 @@ STATIC void compile_trailer_paren_helper(compiler_t *comp, mp_parse_node_t pn_ar
}
} else {
normal_argument:
- if (star_flags) {
- compile_syntax_error(comp, args[i], MP_ERROR_TEXT("non-keyword arg after */**"));
+ if (star_flags & MP_EMIT_STAR_FLAG_DOUBLE) {
+ compile_syntax_error(comp, args[i], MP_ERROR_TEXT("positional arg after **"));
return;
}
if (n_keyword > 0) {
- compile_syntax_error(comp, args[i], MP_ERROR_TEXT("non-keyword arg after keyword arg"));
+ compile_syntax_error(comp, args[i], MP_ERROR_TEXT("positional arg after keyword arg"));
return;
}
compile_node(comp, args[i]);
@@ -2451,14 +2465,9 @@ STATIC void compile_trailer_paren_helper(compiler_t *comp, mp_parse_node_t pn_ar
}
}
- // compile the star/double-star arguments if we had them
- // if we had one but not the other then we load "null" as a place holder
if (star_flags != 0) {
- if (star_args_node == NULL) {
- EMIT(load_null);
- } else {
- compile_node(comp, star_args_node->nodes[0]);
- }
+ // one extra object that contains the star_args map
+ EMIT_ARG(load_const_small_int, star_args);
}
// emit the function/method call