diff --git a/py/mpqstrraw.h b/py/mpqstrraw.h index 06f83ddf0..356c91123 100644 --- a/py/mpqstrraw.h +++ b/py/mpqstrraw.h @@ -42,7 +42,9 @@ Q(chr) Q(complex) Q(dict) Q(divmod) +Q(enumerate) Q(eval) +Q(filter) Q(float) Q(hash) Q(int) @@ -51,6 +53,7 @@ Q(issubclass) Q(iter) Q(len) Q(list) +Q(map) Q(max) Q(min) Q(next) diff --git a/py/obj.h b/py/obj.h index 6dcc6827e..c2182863a 100644 --- a/py/obj.h +++ b/py/obj.h @@ -294,6 +294,15 @@ void mp_obj_list_get(mp_obj_t self_in, uint *len, mp_obj_t **items); void mp_obj_list_store(mp_obj_t self_in, mp_obj_t index, mp_obj_t value); mp_obj_t mp_obj_list_sort(mp_obj_t args, struct _mp_map_t *kwargs); +// map (the python builtin, not the dict implementation detail) +extern const mp_obj_type_t map_type; + +// enumerate +extern const mp_obj_type_t enumerate_type; + +// filter +extern const mp_obj_type_t filter_type; + // dict extern const mp_obj_type_t dict_type; uint mp_obj_dict_len(mp_obj_t self_in); diff --git a/py/objenumerate.c b/py/objenumerate.c new file mode 100644 index 000000000..5bfd8a337 --- /dev/null +++ b/py/objenumerate.c @@ -0,0 +1,53 @@ +#include +#include + +#include "misc.h" +#include "mpconfig.h" +#include "obj.h" +#include "runtime.h" + +typedef struct _mp_obj_enumerate_t { + mp_obj_base_t base; + mp_obj_t iter; + machine_int_t cur; +} mp_obj_enumerate_t; + +static mp_obj_t enumerate_getiter(mp_obj_t self_in) { + return self_in; +} + +static mp_obj_t enumerate_iternext(mp_obj_t self_in); + +/* TODO: enumerate is one of the ones that can take args or kwargs. + Sticking to args for now */ +static mp_obj_t enumerate_make_new(mp_obj_t type_in, int n_args, const mp_obj_t *args) { + /* NOTE: args are backwards */ + assert(n_args > 0); + args += n_args - 1; + mp_obj_enumerate_t *o = m_new_obj(mp_obj_enumerate_t); + o->base.type = &enumerate_type; + o->iter = rt_getiter(args[0]); + o->cur = n_args > 1 ? mp_obj_get_int(args[-1]) : 0; + + return o; +} + +const mp_obj_type_t enumerate_type = { + { &mp_const_type }, + "enumerate", + .make_new = enumerate_make_new, + .iternext = enumerate_iternext, + .getiter = enumerate_getiter, +}; + +static mp_obj_t enumerate_iternext(mp_obj_t self_in) { + assert(MP_OBJ_IS_TYPE(self_in, &enumerate_type)); + mp_obj_enumerate_t *self = self_in; + mp_obj_t next = rt_iternext(self->iter); + if (next == mp_const_stop_iteration) { + return mp_const_stop_iteration; + } else { + mp_obj_t items[] = {MP_OBJ_NEW_SMALL_INT(self->cur++), next}; + return mp_obj_new_tuple(2, items); + } +} diff --git a/py/objfilter.c b/py/objfilter.c new file mode 100644 index 000000000..18225ac10 --- /dev/null +++ b/py/objfilter.c @@ -0,0 +1,58 @@ +#include +#include + +#include "nlr.h" +#include "misc.h" +#include "mpconfig.h" +#include "mpqstr.h" +#include "obj.h" +#include "runtime.h" + +typedef struct _mp_obj_filter_t { + mp_obj_base_t base; + mp_obj_t fun; + mp_obj_t iter; +} mp_obj_filter_t; + +static mp_obj_t filter_make_new(mp_obj_t type_in, int n_args, const mp_obj_t *args) { + /* NOTE: args are backwards */ + if (n_args != 2) { + nlr_jump(mp_obj_new_exception_msg(MP_QSTR_TypeError, "filter expected 2 arguments")); + } + assert(n_args == 2); + mp_obj_filter_t *o = m_new_obj(mp_obj_filter_t); + o->base.type = &filter_type; + o->fun = args[1]; + o->iter = rt_getiter(args[0]); + return o; +} + +static mp_obj_t filter_getiter(mp_obj_t self_in) { + return self_in; +} + +static mp_obj_t filter_iternext(mp_obj_t self_in) { + assert(MP_OBJ_IS_TYPE(self_in, &filter_type)); + mp_obj_filter_t *self = self_in; + mp_obj_t next; + while ((next = rt_iternext(self->iter)) != mp_const_stop_iteration) { + mp_obj_t val; + if (self->fun != mp_const_none) { + val = rt_call_function_n(self->fun, 1, &next); + } else { + val = next; + } + if (rt_is_true(val)) { + return next; + } + } + return mp_const_stop_iteration; +} + +const mp_obj_type_t filter_type = { + { &mp_const_type }, + "filter", + .make_new = filter_make_new, + .getiter = filter_getiter, + .iternext = filter_iternext, +}; diff --git a/py/objmap.c b/py/objmap.c new file mode 100644 index 000000000..365735283 --- /dev/null +++ b/py/objmap.c @@ -0,0 +1,60 @@ +#include +#include + +#include "nlr.h" +#include "misc.h" +#include "mpconfig.h" +#include "mpqstr.h" +#include "obj.h" +#include "runtime.h" + +typedef struct _mp_obj_map_t { + mp_obj_base_t base; + machine_uint_t n_iters; + mp_obj_t fun; + mp_obj_t iters[]; +} mp_obj_map_t; + +static mp_obj_t map_make_new(mp_obj_t type_in, int n_args, const mp_obj_t *args) { + /* NOTE: args are backwards */ + if (n_args < 2) { + nlr_jump(mp_obj_new_exception_msg(MP_QSTR_TypeError, "map must have at least 2 arguments")); + } + assert(n_args >= 2); + mp_obj_map_t *o = m_new_obj_var(mp_obj_map_t, mp_obj_t, n_args - 1); + o->base.type = &map_type; + o->n_iters = n_args - 1; + o->fun = args[n_args - 1]; + for (int i = 0; i < n_args - 1; i++) { + o->iters[i] = rt_getiter(args[n_args-i-2]); + } + return o; +} + +static mp_obj_t map_getiter(mp_obj_t self_in) { + return self_in; +} + +static mp_obj_t map_iternext(mp_obj_t self_in) { + assert(MP_OBJ_IS_TYPE(self_in, &map_type)); + mp_obj_map_t *self = self_in; + mp_obj_t *nextses = m_new(mp_obj_t, self->n_iters); + + for (int i = 0; i < self->n_iters; i++) { + mp_obj_t next = rt_iternext(self->iters[i]); + if (next == mp_const_stop_iteration) { + m_del(mp_obj_t, nextses, self->n_iters); + return mp_const_stop_iteration; + } + nextses[i] = next; + } + return rt_call_function_n(self->fun, self->n_iters, nextses); +} + +const mp_obj_type_t map_type = { + { &mp_const_type }, + "map", + .make_new = map_make_new, + .getiter = map_getiter, + .iternext = map_iternext, +}; diff --git a/py/py.mk b/py/py.mk index 16e064019..e51b00f45 100644 --- a/py/py.mk +++ b/py/py.mk @@ -77,12 +77,15 @@ PY_O_BASENAME = \ objclosure.o \ objcomplex.o \ objdict.o \ + objenumerate.o \ objexcept.o \ + objfilter.o \ objfloat.o \ objfun.o \ objgenerator.o \ objint.o \ objlist.o \ + objmap.o \ objmodule.o \ objnone.o \ objrange.o \ diff --git a/py/runtime.c b/py/runtime.c index 594f79d84..18a3269f4 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -105,11 +105,14 @@ void rt_init(void) { mp_map_add_qstr(&map_builtins, MP_QSTR_complex, (mp_obj_t)&complex_type); #endif mp_map_add_qstr(&map_builtins, MP_QSTR_dict, (mp_obj_t)&dict_type); + mp_map_add_qstr(&map_builtins, MP_QSTR_enumerate, (mp_obj_t)&enumerate_type); + mp_map_add_qstr(&map_builtins, MP_QSTR_filter, (mp_obj_t)&filter_type); #if MICROPY_ENABLE_FLOAT mp_map_add_qstr(&map_builtins, MP_QSTR_float, (mp_obj_t)&float_type); #endif mp_map_add_qstr(&map_builtins, MP_QSTR_int, (mp_obj_t)&int_type); mp_map_add_qstr(&map_builtins, MP_QSTR_list, (mp_obj_t)&list_type); + mp_map_add_qstr(&map_builtins, MP_QSTR_map, (mp_obj_t)&map_type); mp_map_add_qstr(&map_builtins, MP_QSTR_set, (mp_obj_t)&set_type); mp_map_add_qstr(&map_builtins, MP_QSTR_tuple, (mp_obj_t)&tuple_type); mp_map_add_qstr(&map_builtins, MP_QSTR_type, (mp_obj_t)&mp_const_type); diff --git a/tests/basics/tests/enumerate.py b/tests/basics/tests/enumerate.py new file mode 100644 index 000000000..f2bdf4f32 --- /dev/null +++ b/tests/basics/tests/enumerate.py @@ -0,0 +1,6 @@ +print(list(enumerate([]))) +print(list(enumerate([1, 2, 3]))) +print(list(enumerate([1, 2, 3], 5))) +print(list(enumerate([1, 2, 3], -5))) +print(list(enumerate(range(10000)))) + diff --git a/tests/basics/tests/filter.py b/tests/basics/tests/filter.py new file mode 100644 index 000000000..5883e3d00 --- /dev/null +++ b/tests/basics/tests/filter.py @@ -0,0 +1,2 @@ +print(list(filter(lambda x: x & 1, range(-3, 4)))) +print(list(filter(None, range(-3, 4)))) diff --git a/tests/basics/tests/map.py b/tests/basics/tests/map.py new file mode 100644 index 000000000..62dca44ed --- /dev/null +++ b/tests/basics/tests/map.py @@ -0,0 +1,4 @@ +print(list(map(lambda x: x & 1, range(-3, 4)))) +print(list(map(abs, range(-3, 4)))) +print(list(map(set, [[i] for i in range(-3, 4)]))) +print(list(map(pow, range(4), range(4))))