diff --git a/CHANGELOG.md b/CHANGELOG.md index ca83faf51..e3ea9ded4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ All notable changes to this project will be documented in this file. ### Added - Turn HTTP API (command ``SetOption128 1``) default on for backward compatibility - Support for IEM3155 Wattmeter (#12940) +- Berry support for vararg ### Fixed - WDT reset on shutters with stepper motors during deceleration (#12849) diff --git a/lib/libesp32/Berry/src/be_parser.c b/lib/libesp32/Berry/src/be_parser.c index f326d34d0..1f38f48e2 100644 --- a/lib/libesp32/Berry/src/be_parser.c +++ b/lib/libesp32/Berry/src/be_parser.c @@ -528,23 +528,45 @@ static void singlevar(bparser *parser, bexpdesc *var) } } +/* parse a vararg argument in the form `def f(a, *b) end` */ +/* Munch the '*', read the token, create variable and declare the function as vararg */ +static void func_vararg(bparser *parser) { + bexpdesc v; + bstring *str; + match_token(parser, OptMul); /* skip '*' */ + str = next_token(parser).u.s; + match_token(parser, TokenId); /* match and skip ID */ + new_var(parser, str, &v); /* new variable */ + parser->finfo->proto->varg = 1; /* set varg flag */ +} + /* Parse function or method definition variable list */ /* Create an implicit local variable for each argument starting at R0 */ /* Update function proto argc to the expected number or arguments */ /* Raise an exception if multiple arguments have the same name */ +/* New: vararg support */ static void func_varlist(bparser *parser) { bexpdesc v; bstring *str; - /* '(' [ID {',' ID}] ')' */ + /* '(' [ ID {',' ID}] ')' or */ + /* '(' '*' ID ')' or */ + /* '(' [ ID {',' ID}] ',' '*' ID ')' */ match_token(parser, OptLBK); /* skip '(' */ - if (match_id(parser, str) != NULL) { + if (next_type(parser) == OptMul) { + func_vararg(parser); + } else if (match_id(parser, str) != NULL) { new_var(parser, str, &v); /* new variable */ while (match_skip(parser, OptComma)) { /* ',' */ - str = next_token(parser).u.s; - match_token(parser, TokenId); /* match and skip ID */ - /* new local variable */ - new_var(parser, str, &v); + if (next_type(parser) == OptMul) { + func_vararg(parser); + break; + } else { + str = next_token(parser).u.s; + match_token(parser, TokenId); /* match and skip ID */ + /* new local variable */ + new_var(parser, str, &v); + } } } match_token(parser, OptRBK); /* skip ')' */ diff --git a/lib/libesp32/Berry/src/be_vm.c b/lib/libesp32/Berry/src/be_vm.c index 359aaa2d5..370a7997c 100644 --- a/lib/libesp32/Berry/src/be_vm.c +++ b/lib/libesp32/Berry/src/be_vm.c @@ -12,6 +12,7 @@ #include "be_class.h" #include "be_func.h" #include "be_vector.h" +#include "be_list.h" #include "be_map.h" #include "be_module.h" #include "be_mem.h" @@ -1086,6 +1087,20 @@ newframe: /* a new call frame */ for (; v < end; ++v) { /* set all not provided arguments to nil */ var_setnil(v); } + if (proto->varg) { /* there are vararg at the last argument, build the list */ + /* code below uses mostly low-level calls for performance */ + be_stack_require(vm, argc + 2); /* make sure we don't overflow the stack */ + bvalue *top_save = vm->top; /* save original stack, we need fresh slots to create the 'list' instance */ + vm->top = v; /* move top of stack right after last argument */ + be_newobject(vm, "list"); /* this creates 2 objects on stack: list instance, BE_LIST object */ + blist *list = var_toobj(vm->top-1); /* get low-level BE_LIST structure */ + v = reg + proto->argc - 1; /* last argument */ + for (; v < reg + argc; v++) { + be_list_push(vm, list, v); /* push all varargs into list */ + } + *(reg + proto->argc - 1) = *(vm->top-2); /* change the vararg argument to now contain the list instance */ + vm->top = top_save; /* restore top of stack pointer */ + } goto newframe; /* continue execution of the closure */ } case BE_NTVCLOS: { diff --git a/lib/libesp32/Berry/tests/vararg.be b/lib/libesp32/Berry/tests/vararg.be new file mode 100644 index 000000000..7dd3541d3 --- /dev/null +++ b/lib/libesp32/Berry/tests/vararg.be @@ -0,0 +1,14 @@ +#- vararg -# +def f(a,*b) return b end + +assert(f() == []) +assert(f(1) == []) +assert(f(1,2) == [2]) +assert(f(1,2,3) == [2, 3]) + +def g(*a) return a end + +assert(g() == []) +assert(g("foo") == ["foo"]) +assert(g("foo", nil) == ["foo", nil]) +assert(g("foo", nil, 2) == ["foo", nil, 2]) diff --git a/tasmota/xdrv_52_9_berry.ino b/tasmota/xdrv_52_9_berry.ino index 6095a7750..36d006618 100644 --- a/tasmota/xdrv_52_9_berry.ino +++ b/tasmota/xdrv_52_9_berry.ino @@ -347,7 +347,7 @@ void BrLoad(const char * script_name) { bool loaded = be_tobool(berry.vm, -2); // did it succeed? be_pop(berry.vm, 2); if (loaded) { - AddLog(LOG_LEVEL_INFO, D_LOG_BERRY "sucessfully loaded '%s'", script_name); + AddLog(LOG_LEVEL_INFO, D_LOG_BERRY "successfully loaded '%s'", script_name); } else { AddLog(LOG_LEVEL_INFO, D_LOG_BERRY "no '%s'", script_name); }