diff --git a/CHANGELOG.md b/CHANGELOG.md index fec10d396..b10c34328 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ All notable changes to this project will be documented in this file. ### Changed - ESP32 shutter driver support up to 16 shutters (#18295) - Configuration backup and restore now backup and restore ``.xdrvsetXXX`` files too (#18295) +- Berry extend `range(lower, upper, incr)` to arbitrary increment ### Fixed diff --git a/lib/libesp32/berry/src/be_rangelib.c b/lib/libesp32/berry/src/be_rangelib.c index 3682ae3ce..0b964baec 100644 --- a/lib/libesp32/berry/src/be_rangelib.c +++ b/lib/libesp32/berry/src/be_rangelib.c @@ -11,31 +11,67 @@ static int m_init(bvm *vm) { + int argc = be_top(vm); be_pushvalue(vm, 2); be_setmember(vm, 1, "__lower__"); be_pop(vm, 1); be_pushvalue(vm, 3); be_setmember(vm, 1, "__upper__"); + int incr = 1; /* default increment is '1' */ + if (argc >= 4) { + incr = be_toint(vm, 4); + if (incr == 0) { be_raise(vm, "value_error", "increment cannot be zero"); } + } + be_pushint(vm, incr); + be_setmember(vm, 1, "__incr__"); be_return_nil(vm); } static int m_tostring(bvm *vm) { - be_pushstring(vm, "("); - be_getmember(vm, 1, "__lower__"); - be_tostring(vm, -1); - be_strconcat(vm, -2); - be_pop(vm, 1); - be_pushstring(vm, ".."); - be_strconcat(vm, -2); - be_pop(vm, 1); - be_getmember(vm, 1, "__upper__"); - be_tostring(vm, -1); - be_strconcat(vm, -2); - be_pop(vm, 1); - be_pushstring(vm, ")"); - be_strconcat(vm, -2); + be_getmember(vm, 1, "__incr__"); + int incr = be_toint(vm, -1); be_pop(vm, 1); + if (incr == 1) { + be_pushstring(vm, "("); + be_getmember(vm, 1, "__lower__"); + be_tostring(vm, -1); + be_strconcat(vm, -2); + be_pop(vm, 1); + be_pushstring(vm, ".."); + be_strconcat(vm, -2); + be_pop(vm, 1); + be_getmember(vm, 1, "__upper__"); + be_tostring(vm, -1); + be_strconcat(vm, -2); + be_pop(vm, 1); + be_pushstring(vm, ")"); + be_strconcat(vm, -2); + be_pop(vm, 1); + } else { + be_pushstring(vm, "range("); + be_getmember(vm, 1, "__lower__"); + be_tostring(vm, -1); + be_strconcat(vm, -2); + be_pop(vm, 1); + be_pushstring(vm, ", "); + be_strconcat(vm, -2); + be_pop(vm, 1); + be_getmember(vm, 1, "__upper__"); + be_tostring(vm, -1); + be_strconcat(vm, -2); + be_pop(vm, 1); + be_pushstring(vm, ", "); + be_strconcat(vm, -2); + be_pop(vm, 1); + be_getmember(vm, 1, "__incr__"); + be_tostring(vm, -1); + be_strconcat(vm, -2); + be_pop(vm, 1); + be_pushstring(vm, ")"); + be_strconcat(vm, -2); + be_pop(vm, 1); + } be_return(vm); } @@ -51,13 +87,27 @@ static int m_lower(bvm *vm) be_return(vm); } +static int m_incr(bvm *vm) +{ + be_getmember(vm, 1, "__incr__"); + be_return(vm); +} + static int m_setrange(bvm *vm) { + int argc = be_top(vm); be_pushvalue(vm, 2); be_setmember(vm, 1, "__lower__"); be_pop(vm, 1); be_pushvalue(vm, 3); be_setmember(vm, 1, "__upper__"); + int incr = 1; /* default increment is '1' */ + if (argc >= 4) { + incr = be_toint(vm, 4); + if (incr == 0) { be_raise(vm, "value_error", "increment cannot be zero"); } + } + be_pushint(vm, incr); + be_setmember(vm, 1, "__incr__"); be_return_nil(vm); } @@ -68,25 +118,30 @@ static int iter_closure(bvm *vm) bntvclos *func = var_toobj(vm->cf->func); bvalue *uv0 = be_ntvclos_upval(func, 0)->value; bvalue *uv1 = be_ntvclos_upval(func, 1)->value; + bvalue *uv2 = be_ntvclos_upval(func, 2)->value; bint lower = var_toint(uv0); /* upvalue[0] => lower */ bint upper = var_toint(uv1); /* upvalue[1] => upper */ - if (lower > upper) { + bint incr = var_toint(uv2); /* upvalue[2] => incr */ + if ((incr > 0 && lower > upper) || (incr < 0 && lower < upper)) { be_stop_iteration(vm); } - var_toint(uv0) = lower + 1; /* set upvale[0] */ + var_toint(uv0) = lower + incr; /* set upvale[0] */ be_pushint(vm, lower); /* push the return value */ be_return(vm); } static int m_iter(bvm *vm) { - be_pushntvclosure(vm, iter_closure, 2); + be_pushntvclosure(vm, iter_closure, 3); be_getmember(vm, 1, "__lower__"); be_setupval(vm, -2, 0); be_pop(vm, 1); be_getmember(vm, 1, "__upper__"); be_setupval(vm, -2, 1); be_pop(vm, 1); + be_getmember(vm, 1, "__incr__"); + be_setupval(vm, -2, 2); + be_pop(vm, 1); be_return(vm); } @@ -96,6 +151,7 @@ void be_load_rangelib(bvm *vm) static const bnfuncinfo members[] = { { "__lower__", NULL }, { "__upper__", NULL }, + { "__incr__", NULL }, { "init", m_init }, { "tostring", m_tostring }, { "lower", m_lower }, @@ -111,10 +167,12 @@ void be_load_rangelib(bvm *vm) class be_class_range (scope: global, name: range) { __lower__, var __upper__, var + __incr__, var init, func(m_init) tostring, func(m_tostring) lower, func(m_lower) upper, func(m_upper) + incr, func(m_incr) setrange, func(m_setrange) iter, func(m_iter) } diff --git a/lib/libesp32/berry/tests/range.be b/lib/libesp32/berry/tests/range.be new file mode 100644 index 000000000..08b5add8d --- /dev/null +++ b/lib/libesp32/berry/tests/range.be @@ -0,0 +1,39 @@ +# test for ranges + +# expand a range object as list +def expand(iter) + var ret = [] + for i: iter + ret.push(i) + end + return ret +end + +assert(expand(0..5) == [0, 1, 2, 3, 4, 5]) +assert(expand(0..0) == [0]) +assert(expand(5..0) == []) +var r = 1..5 +assert(r.lower() == 1) +assert(r.upper() == 5) +assert(r.incr() == 1) + +assert(expand(range(0,5)) == [0, 1, 2, 3, 4, 5]) +assert(expand(range(0,5,2)) == [0, 2, 4]) +assert(expand(range(0,5,12)) == [0]) +assert(expand(range(0,5,-1)) == []) + +assert(expand(range(5,0,-1)) == [5, 4, 3, 2, 1, 0]) +assert(expand(range(5,0,-2)) == [5, 3, 1]) +assert(expand(range(5,5,-2)) == [5]) +assert(expand(range(0,5,-2)) == []) + +def assert_value_error(c) + try + compile(c)() + assert(false, 'unexpected execution flow') + except 'value_error' as e, m + end +end + +# range with increment zero shoud raise an error +assert_value_error("range(1,2,0)")