diff options
-rw-r--r-- | py/builtin.c | 12 | ||||
-rw-r--r-- | py/builtin.h | 1 | ||||
-rw-r--r-- | py/builtintables.c | 1 | ||||
-rw-r--r-- | py/map.c | 188 | ||||
-rw-r--r-- | py/obj.c | 5 | ||||
-rw-r--r-- | py/obj.h | 9 | ||||
-rw-r--r-- | py/objdict.c | 9 | ||||
-rw-r--r-- | py/objfun.c | 13 | ||||
-rw-r--r-- | py/objset.c | 19 | ||||
-rw-r--r-- | py/objtype.c | 11 | ||||
-rw-r--r-- | py/runtime.c | 53 | ||||
-rw-r--r-- | py/runtime.h | 2 | ||||
-rw-r--r-- | py/vm.c | 5 | ||||
-rwxr-xr-x | stmhal/autoflash | 23 | ||||
-rw-r--r-- | stmhal/readline.c | 74 | ||||
-rw-r--r-- | tests/basics/dict-del.py | 21 | ||||
-rw-r--r-- | tests/basics/getattr1.py | 2 | ||||
-rw-r--r-- | tests/basics/set_remove.py | 24 | ||||
-rw-r--r-- | tests/basics/types2.py | 12 |
19 files changed, 379 insertions, 105 deletions
diff --git a/py/builtin.c b/py/builtin.c index 145bc65b14..2bf12e4928 100644 --- a/py/builtin.c +++ b/py/builtin.c @@ -400,9 +400,13 @@ STATIC mp_obj_t mp_builtin_id(mp_obj_t o_in) { MP_DEFINE_CONST_FUN_OBJ_1(mp_builtin_id_obj, mp_builtin_id); -STATIC mp_obj_t mp_builtin_getattr(mp_obj_t o_in, mp_obj_t attr) { - assert(MP_OBJ_IS_QSTR(attr)); - return mp_load_attr(o_in, MP_OBJ_QSTR_VALUE(attr)); +STATIC mp_obj_t mp_builtin_getattr(uint n_args, const mp_obj_t *args) { + assert(MP_OBJ_IS_QSTR(args[1])); + mp_obj_t defval = MP_OBJ_NULL; + if (n_args > 2) { + defval = args[2]; + } + return mp_load_attr_default(args[0], MP_OBJ_QSTR_VALUE(args[1]), defval); } -MP_DEFINE_CONST_FUN_OBJ_2(mp_builtin_getattr_obj, mp_builtin_getattr); +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_getattr_obj, 2, 3, mp_builtin_getattr); diff --git a/py/builtin.h b/py/builtin.h index 3120e5baf6..60b7c85b81 100644 --- a/py/builtin.h +++ b/py/builtin.h @@ -33,6 +33,7 @@ MP_DECLARE_CONST_FUN_OBJ(mp_builtin_sum_obj); MP_DECLARE_CONST_FUN_OBJ(mp_namedtuple_obj); +extern const mp_obj_module_t mp_module___main__; extern const mp_obj_module_t mp_module_array; extern const mp_obj_module_t mp_module_collections; extern const mp_obj_module_t mp_module_io; diff --git a/py/builtintables.c b/py/builtintables.c index 03fb92e569..1edeefa441 100644 --- a/py/builtintables.c +++ b/py/builtintables.c @@ -118,6 +118,7 @@ STATIC const mp_builtin_elem_t builtin_object_table[] = { }; STATIC const mp_builtin_elem_t builtin_module_table[] = { + { MP_QSTR___main__, (mp_obj_t)&mp_module___main__ }, { MP_QSTR_micropython, (mp_obj_t)&mp_module_micropython }, { MP_QSTR_array, (mp_obj_t)&mp_module_array }, @@ -83,7 +83,7 @@ STATIC void mp_map_rehash(mp_map_t *map) { map->all_keys_are_qstrs = 1; map->table = m_new0(mp_map_elem_t, map->alloc); for (int i = 0; i < old_alloc; i++) { - if (old_table[i].key != NULL) { + if (old_table[i].key != MP_OBJ_NULL && old_table[i].key != MP_OBJ_SENTINEL) { mp_map_lookup(map, old_table[i].key, MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)->value = old_table[i].value; } } @@ -106,8 +106,6 @@ mp_map_elem_t* mp_map_lookup(mp_map_t *map, mp_obj_t index, mp_map_lookup_kind_t // map is a hash table (not a fixed array), so do a hash lookup - machine_uint_t hash; - hash = mp_obj_hash(index); if (map->alloc == 0) { if (lookup_kind & MP_MAP_LOOKUP_ADD_IF_NOT_FOUND) { mp_map_rehash(map); @@ -116,49 +114,78 @@ mp_map_elem_t* mp_map_lookup(mp_map_t *map, mp_obj_t index, mp_map_lookup_kind_t } } + machine_uint_t hash = mp_obj_hash(index); uint pos = hash % map->alloc; + uint start_pos = pos; + mp_map_elem_t *avail_slot = NULL; for (;;) { - mp_map_elem_t *elem = &map->table[pos]; - if (elem->key == NULL) { - // not in table + mp_map_elem_t *slot = &map->table[pos]; + if (slot->key == MP_OBJ_NULL) { + // found NULL slot, so index is not in table if (lookup_kind & MP_MAP_LOOKUP_ADD_IF_NOT_FOUND) { - if (map->used + 1 >= map->alloc) { - // not enough room in table, rehash it - mp_map_rehash(map); - // restart the search for the new element - pos = hash % map->alloc; - } else { - map->used += 1; - elem->key = index; - if (!MP_OBJ_IS_QSTR(index)) { - map->all_keys_are_qstrs = 0; - } - return elem; + map->used += 1; + if (avail_slot == NULL) { + avail_slot = slot; + } + slot->key = index; + slot->value = MP_OBJ_NULL; + if (!MP_OBJ_IS_QSTR(index)) { + map->all_keys_are_qstrs = 0; } + return slot; } else { - return NULL; + return MP_OBJ_NULL; } - } else if (elem->key == index || (!map->all_keys_are_qstrs && mp_obj_equal(elem->key, index))) { - // found it - /* it seems CPython does not replace the index; try x={True:'true'};x[1]='one';x - if (add_if_not_found) { - elem->key = index; + } else if (slot->key == MP_OBJ_SENTINEL) { + // found deleted slot, remember for later + if (avail_slot == NULL) { + avail_slot = slot; } - */ + } else if (slot->key == index || (!map->all_keys_are_qstrs && mp_obj_equal(slot->key, index))) { + // found index + // Note: CPython does not replace the index; try x={True:'true'};x[1]='one';x if (lookup_kind & MP_MAP_LOOKUP_REMOVE_IF_FOUND) { - map->used--; // this leaks this memory (but see dict_get_helper) mp_map_elem_t *retval = m_new(mp_map_elem_t, 1); - retval->key = elem->key; - retval->value = elem->value; - elem->key = NULL; - elem->value = NULL; + retval->key = slot->key; + retval->value = slot->value; + // delete element in this slot + map->used--; + if (map->table[(pos + 1) % map->alloc].key == MP_OBJ_NULL) { + // optimisation if next slot is empty + slot->key = MP_OBJ_NULL; + } else { + slot->key = MP_OBJ_SENTINEL; + } return retval; } - return elem; - } else { - // not yet found, keep searching in this table - pos = (pos + 1) % map->alloc; + return slot; + } + + // not yet found, keep searching in this table + pos = (pos + 1) % map->alloc; + + if (pos == start_pos) { + // search got back to starting position, so index is not in table + if (lookup_kind & MP_MAP_LOOKUP_ADD_IF_NOT_FOUND) { + if (avail_slot != NULL) { + // there was an available slot, so use that + map->used++; + avail_slot->key = index; + avail_slot->value = MP_OBJ_NULL; + if (!MP_OBJ_IS_QSTR(index)) { + map->all_keys_are_qstrs = 0; + } + return avail_slot; + } else { + // not enough room in table, rehash it + mp_map_rehash(map); + // restart the search for the new element + start_pos = pos = hash % map->alloc; + } + } else { + return MP_OBJ_NULL; + } } } } @@ -179,16 +206,14 @@ STATIC void mp_set_rehash(mp_set_t *set) { set->used = 0; set->table = m_new0(mp_obj_t, set->alloc); for (int i = 0; i < old_alloc; i++) { - if (old_table[i] != NULL) { - mp_set_lookup(set, old_table[i], true); + if (old_table[i] != MP_OBJ_NULL && old_table[i] != MP_OBJ_SENTINEL) { + mp_set_lookup(set, old_table[i], MP_MAP_LOOKUP_ADD_IF_NOT_FOUND); } } m_del(mp_obj_t, old_table, old_alloc); } mp_obj_t mp_set_lookup(mp_set_t *set, mp_obj_t index, mp_map_lookup_kind_t lookup_kind) { - int hash; - int pos; if (set->alloc == 0) { if (lookup_kind & MP_MAP_LOOKUP_ADD_IF_NOT_FOUND) { mp_set_rehash(set); @@ -196,45 +221,84 @@ mp_obj_t mp_set_lookup(mp_set_t *set, mp_obj_t index, mp_map_lookup_kind_t looku return NULL; } } - if (lookup_kind & MP_MAP_LOOKUP_FIRST) { - hash = 0; - pos = 0; - } else { - hash = mp_obj_hash(index);; - pos = hash % set->alloc; - } + machine_uint_t hash = mp_obj_hash(index); + uint pos = hash % set->alloc; + uint start_pos = pos; + mp_obj_t *avail_slot = NULL; for (;;) { mp_obj_t elem = set->table[pos]; if (elem == MP_OBJ_NULL) { - // not in table + // found NULL slot, so index is not in table if (lookup_kind & MP_MAP_LOOKUP_ADD_IF_NOT_FOUND) { - if (set->used + 1 >= set->alloc) { + if (avail_slot == NULL) { + avail_slot = &set->table[pos]; + } + set->used++; + *avail_slot = index; + return index; + } else { + return MP_OBJ_NULL; + } + } else if (elem == MP_OBJ_SENTINEL) { + // found deleted slot, remember for later + if (avail_slot == NULL) { + avail_slot = &set->table[pos]; + } + } else if (mp_obj_equal(elem, index)) { + // found index + if (lookup_kind & MP_MAP_LOOKUP_REMOVE_IF_FOUND) { + // delete element + set->used--; + if (set->table[(pos + 1) % set->alloc] == MP_OBJ_NULL) { + // optimisation if next slot is empty + set->table[pos] = MP_OBJ_NULL; + } else { + set->table[pos] = MP_OBJ_SENTINEL; + } + } + return elem; + } + + // not yet found, keep searching in this table + pos = (pos + 1) % set->alloc; + + if (pos == start_pos) { + // search got back to starting position, so index is not in table + if (lookup_kind & MP_MAP_LOOKUP_ADD_IF_NOT_FOUND) { + if (avail_slot != NULL) { + // there was an available slot, so use that + set->used++; + *avail_slot = index; + return index; + } else { // not enough room in table, rehash it mp_set_rehash(set); // restart the search for the new element - pos = hash % set->alloc; - } else { - set->used += 1; - set->table[pos] = index; - return index; + start_pos = pos = hash % set->alloc; } - } else if (lookup_kind & MP_MAP_LOOKUP_FIRST) { - pos++; } else { return MP_OBJ_NULL; } - } else if ((lookup_kind & MP_MAP_LOOKUP_FIRST) || mp_obj_equal(elem, index)) { - // found it - if (lookup_kind & MP_MAP_LOOKUP_REMOVE_IF_FOUND) { - set->used--; - set->table[pos] = NULL; + } + } +} + +mp_obj_t mp_set_remove_first(mp_set_t *set) { + for (uint pos = 0; pos < set->alloc; pos++) { + if (set->table[pos] != MP_OBJ_NULL && set->table[pos] != MP_OBJ_SENTINEL) { + mp_obj_t elem = set->table[pos]; + // delete element + set->used--; + if (set->table[(pos + 1) % set->alloc] == MP_OBJ_NULL) { + // optimisation if next slot is empty + set->table[pos] = MP_OBJ_NULL; + } else { + set->table[pos] = MP_OBJ_SENTINEL; } return elem; - } else { - // not yet found, keep searching in this table - pos = (pos + 1) % set->alloc; } } + return MP_OBJ_NULL; } void mp_set_clear(mp_set_t *set) { @@ -118,6 +118,8 @@ machine_int_t mp_obj_hash(mp_obj_t o_in) { return (machine_int_t)o_in; } else if (MP_OBJ_IS_TYPE(o_in, &mp_type_tuple)) { return mp_obj_tuple_hash(o_in); + } else if (MP_OBJ_IS_TYPE(o_in, &mp_type_type)) { + return (machine_int_t)o_in; // TODO hash class and instances // TODO delegate to __hash__ method if it exists @@ -179,6 +181,9 @@ bool mp_obj_equal(mp_obj_t o1, mp_obj_t o2) { } machine_int_t mp_obj_get_int(mp_obj_t arg) { + // This function essentially performs implicit type conversion to int + // Note that Python does NOT provide implicit type conversion from + // float to int in the core expression language, try some_list[1.0]. if (arg == mp_const_false) { return 0; } else if (arg == mp_const_true) { @@ -23,6 +23,11 @@ typedef struct _mp_obj_base_t mp_obj_base_t; #define MP_OBJ_NULL ((mp_obj_t)NULL) +// The SENTINEL object is used for various internal purposes where one needs +// an object which is unique from all other objects, including MP_OBJ_NULL. + +#define MP_OBJ_SENTINEL ((mp_obj_t)8) + // These macros check for small int, qstr or object, and access small int and qstr values // - xxxx...xxx1: a small int, bits 1 and above are the value // - xxxx...xx10: a qstr, bits 2 and above are the value @@ -103,11 +108,11 @@ typedef struct _mp_map_t { mp_map_elem_t *table; } mp_map_t; +// These can be or'd together typedef enum _mp_map_lookup_kind_t { MP_MAP_LOOKUP, // 0 MP_MAP_LOOKUP_ADD_IF_NOT_FOUND, // 1 MP_MAP_LOOKUP_REMOVE_IF_FOUND, // 2 - MP_MAP_LOOKUP_FIRST = 4, } mp_map_lookup_kind_t; void mp_map_init(mp_map_t *map, int n); @@ -129,6 +134,7 @@ typedef struct _mp_set_t { void mp_set_init(mp_set_t *set, int n); mp_obj_t mp_set_lookup(mp_set_t *set, mp_obj_t index, mp_map_lookup_kind_t lookup_kind); +mp_obj_t mp_set_remove_first(mp_set_t *set); void mp_set_clear(mp_set_t *set); // Type definitions for methods @@ -440,6 +446,7 @@ typedef struct _mp_obj_dict_t { } mp_obj_dict_t; uint mp_obj_dict_len(mp_obj_t self_in); mp_obj_t mp_obj_dict_store(mp_obj_t self_in, mp_obj_t key, mp_obj_t value); +mp_obj_t mp_obj_dict_delete(mp_obj_t self_in, mp_obj_t key); mp_map_t *mp_obj_dict_get_map(mp_obj_t self_in); // set diff --git a/py/objdict.c b/py/objdict.c index ad8c137489..f6070a0b40 100644 --- a/py/objdict.c +++ b/py/objdict.c @@ -103,7 +103,7 @@ STATIC mp_map_elem_t *dict_it_iternext_elem(mp_obj_t self_in) { mp_map_elem_t *table = self->dict->map.table; for (int i = self->cur; i < max; i++) { - if (table[i].key != NULL) { + if (table[i].key != MP_OBJ_NULL && table[i].key != MP_OBJ_SENTINEL) { self->cur = i + 1; return &(table[i]); } @@ -481,6 +481,13 @@ mp_obj_t mp_obj_dict_store(mp_obj_t self_in, mp_obj_t key, mp_obj_t value) { return self_in; } +mp_obj_t mp_obj_dict_delete(mp_obj_t self_in, mp_obj_t key) { + assert(MP_OBJ_IS_TYPE(self_in, &mp_type_dict)); + mp_obj_dict_t *self = self_in; + dict_get_helper(&self->map, key, NULL, MP_MAP_LOOKUP_REMOVE_IF_FOUND); + return self_in; +} + mp_map_t *mp_obj_dict_get_map(mp_obj_t self_in) { assert(MP_OBJ_IS_TYPE(self_in, &mp_type_dict)); mp_obj_dict_t *self = self_in; diff --git a/py/objfun.c b/py/objfun.c index 3b48b570fd..db18aa8af6 100644 --- a/py/objfun.c +++ b/py/objfun.c @@ -53,6 +53,16 @@ void mp_check_nargs(int n_args, machine_uint_t n_args_min, machine_uint_t n_args } } +STATIC mp_obj_t fun_binary_op(int op, mp_obj_t lhs_in, mp_obj_t rhs_in) { + switch (op) { + case MP_BINARY_OP_EQUAL: + // These objects can be equal only if it's the same underlying structure, + // we don't even need to check for 2nd arg type. + return MP_BOOL(lhs_in == rhs_in); + } + return NULL; +} + STATIC mp_obj_t fun_native_call(mp_obj_t self_in, uint n_args, uint n_kw, const mp_obj_t *args) { assert(MP_OBJ_IS_TYPE(self_in, &mp_type_fun_native)); mp_obj_fun_native_t *self = self_in; @@ -102,6 +112,7 @@ const mp_obj_type_t mp_type_fun_native = { { &mp_type_type }, .name = MP_QSTR_function, .call = fun_native_call, + .binary_op = fun_binary_op, }; // fun must have the correct signature for n_args fixed arguments @@ -338,6 +349,7 @@ const mp_obj_type_t mp_type_fun_bc = { { &mp_type_type }, .name = MP_QSTR_function, .call = fun_bc_call, + .binary_op = fun_binary_op, }; mp_obj_t mp_obj_new_fun_bc(uint scope_flags, qstr *args, uint n_args, mp_obj_t def_args_in, const byte *code) { @@ -464,6 +476,7 @@ STATIC const mp_obj_type_t mp_type_fun_asm = { { &mp_type_type }, .name = MP_QSTR_function, .call = fun_asm_call, + .binary_op = fun_binary_op, }; mp_obj_t mp_obj_new_fun_asm(uint n_args, void *fun) { diff --git a/py/objset.c b/py/objset.c index 439c6e96e6..222f76e405 100644 --- a/py/objset.c +++ b/py/objset.c @@ -32,7 +32,7 @@ STATIC void set_print(void (*print)(void *env, const char *fmt, ...), void *env, bool first = true; print(env, "{"); for (int i = 0; i < self->set.alloc; i++) { - if (self->set.table[i] != MP_OBJ_NULL) { + if (self->set.table[i] != MP_OBJ_NULL && self->set.table[i] != MP_OBJ_SENTINEL) { if (!first) { print(env, ", "); } @@ -83,7 +83,7 @@ STATIC mp_obj_t set_it_iternext(mp_obj_t self_in) { mp_obj_t *table = self->set->set.table; for (machine_uint_t i = self->cur; i < max; i++) { - if (table[i] != NULL) { + if (table[i] != MP_OBJ_NULL && table[i] != MP_OBJ_SENTINEL) { self->cur = i + 1; return table[i]; } @@ -307,12 +307,10 @@ STATIC mp_obj_t set_equal(mp_obj_t self_in, mp_obj_t other_in) { STATIC mp_obj_t set_pop(mp_obj_t self_in) { assert(MP_OBJ_IS_TYPE(self_in, &mp_type_set)); mp_obj_set_t *self = self_in; - - if (self->set.used == 0) { + mp_obj_t obj = mp_set_remove_first(&self->set); + if (obj == MP_OBJ_NULL) { nlr_jump(mp_obj_new_exception_msg(&mp_type_KeyError, "pop from an empty set")); } - mp_obj_t obj = mp_set_lookup(&self->set, NULL, - MP_MAP_LOOKUP_REMOVE_IF_FOUND | MP_MAP_LOOKUP_FIRST); return obj; } STATIC MP_DEFINE_CONST_FUN_OBJ_1(set_pop_obj, set_pop); @@ -375,6 +373,14 @@ STATIC mp_obj_t set_union(mp_obj_t self_in, mp_obj_t other_in) { } STATIC MP_DEFINE_CONST_FUN_OBJ_2(set_union_obj, set_union); +STATIC mp_obj_t set_unary_op(int op, mp_obj_t self_in) { + mp_obj_set_t *self = self_in; + switch (op) { + case MP_UNARY_OP_BOOL: return MP_BOOL(self->set.used != 0); + case MP_UNARY_OP_LEN: return MP_OBJ_NEW_SMALL_INT((machine_int_t)self->set.used); + default: return MP_OBJ_NULL; // op not supported for None + } +} STATIC mp_obj_t set_binary_op(int op, mp_obj_t lhs, mp_obj_t rhs) { mp_obj_t args[] = {lhs, rhs}; @@ -450,6 +456,7 @@ const mp_obj_type_t mp_type_set = { .name = MP_QSTR_set, .print = set_print, .make_new = set_make_new, + .unary_op = set_unary_op, .binary_op = set_binary_op, .getiter = set_getiter, .locals_dict = (mp_obj_t)&set_locals_dict, diff --git a/py/objtype.c b/py/objtype.c index f4ce6a4f8c..6426ec2d04 100644 --- a/py/objtype.c +++ b/py/objtype.c @@ -367,6 +367,16 @@ STATIC bool type_store_attr(mp_obj_t self_in, qstr attr, mp_obj_t value) { return false; } +STATIC mp_obj_t type_binary_op(int op, mp_obj_t lhs_in, mp_obj_t rhs_in) { + switch (op) { + case MP_BINARY_OP_EQUAL: + // Types can be equal only if it's the same type structure, + // we don't even need to check for 2nd arg type. + return MP_BOOL(lhs_in == rhs_in); + } + return NULL; +} + const mp_obj_type_t mp_type_type = { { &mp_type_type }, .name = MP_QSTR_type, @@ -375,6 +385,7 @@ const mp_obj_type_t mp_type_type = { .call = type_call, .load_attr = type_load_attr, .store_attr = type_store_attr, + .binary_op = type_binary_op, }; mp_obj_t mp_obj_new_type(qstr name, mp_obj_t bases_tuple, mp_obj_t locals_dict) { diff --git a/py/runtime.c b/py/runtime.c index 238c3a56e2..5c9df06a28 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -33,6 +33,14 @@ STATIC mp_map_t *map_locals; STATIC mp_map_t *map_globals; STATIC mp_map_t map_builtins; +STATIC mp_map_t map_main; + +const mp_obj_module_t mp_module___main__ = { + .base = { &mp_type_module }, + .name = MP_QSTR___main__, + .globals = (mp_map_t*)&map_main, +}; + // a good optimising compiler will inline this if necessary STATIC void mp_map_add_qstr(mp_map_t *map, qstr qstr, mp_obj_t value) { mp_map_lookup(map, MP_OBJ_NEW_QSTR(qstr), MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)->value = value; @@ -41,17 +49,18 @@ STATIC void mp_map_add_qstr(mp_map_t *map, qstr qstr, mp_obj_t value) { void mp_init(void) { mp_emit_glue_init(); - // locals = globals for outer module (see Objects/frameobject.c/PyFrame_New()) - map_locals = map_globals = mp_map_new(1); - - // init built-in hash table - mp_map_init(&map_builtins, 3); - // init global module stuff mp_module_init(); + mp_map_init(&map_main, 1); // add some builtins that can't be done in ROM - mp_map_add_qstr(map_globals, MP_QSTR___name__, MP_OBJ_NEW_QSTR(MP_QSTR___main__)); + mp_map_add_qstr(&map_main, MP_QSTR___name__, MP_OBJ_NEW_QSTR(MP_QSTR___main__)); + + // locals = globals for outer module (see Objects/frameobject.c/PyFrame_New()) + map_locals = map_globals = &map_main; + + // init built-in hash table + mp_map_init(&map_builtins, 3); #if MICROPY_CPYTHON_COMPAT // Precreate sys module, so "import sys" didn't throw exceptions. @@ -141,6 +150,7 @@ void mp_store_name(qstr qstr, mp_obj_t obj) { void mp_delete_name(qstr qstr) { DEBUG_OP_printf("delete name %s\n", qstr_str(qstr)); + // TODO raise NameError if qstr not found mp_map_lookup(map_locals, MP_OBJ_NEW_QSTR(qstr), MP_MAP_LOOKUP_REMOVE_IF_FOUND); } @@ -680,12 +690,14 @@ too_long: nlr_jump(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "too many values to unpack (expected %d)", num)); } -mp_obj_t mp_load_attr(mp_obj_t base, qstr attr) { +mp_obj_t mp_load_attr_default(mp_obj_t base, qstr attr, mp_obj_t defval) { DEBUG_OP_printf("load attr %p.%s\n", base, qstr_str(attr)); - // use load_method mp_obj_t dest[2]; - mp_load_method(base, attr, dest); - if (dest[1] == MP_OBJ_NULL) { + // use load_method, raising or not raising exception + ((defval == MP_OBJ_NULL) ? mp_load_method : mp_load_method_maybe)(base, attr, dest); + if (dest[0] == MP_OBJ_NULL) { + return defval; + } else if (dest[1] == MP_OBJ_NULL) { // load_method returned just a normal attribute return dest[0]; } else { @@ -694,6 +706,10 @@ mp_obj_t mp_load_attr(mp_obj_t base, qstr attr) { } } +mp_obj_t mp_load_attr(mp_obj_t base, qstr attr) { + return mp_load_attr_default(base, attr, MP_OBJ_NULL); +} + // no attribute found, returns: dest[0] == MP_OBJ_NULL, dest[1] == MP_OBJ_NULL // normal attribute found, returns: dest[0] == <attribute>, dest[1] == MP_OBJ_NULL // method attribute found, returns: dest[0] == <method>, dest[1] == <self> @@ -798,6 +814,21 @@ void mp_store_subscr(mp_obj_t base, mp_obj_t index, mp_obj_t value) { } } +void mp_delete_subscr(mp_obj_t base, mp_obj_t index) { + DEBUG_OP_printf("delete subscr %p[%p]\n", base, index); + /* list delete not implemented + if (MP_OBJ_IS_TYPE(base, &mp_type_list)) { + // list delete + mp_obj_list_delete(base, index); + } else */ + if (MP_OBJ_IS_TYPE(base, &mp_type_dict)) { + // dict delete + mp_obj_dict_delete(base, index); + } else { + nlr_jump(mp_obj_new_exception_msg_varg(&mp_type_TypeError, "'%s' object does not support item deletion", mp_obj_get_type_str(base))); + } +} + mp_obj_t mp_getiter(mp_obj_t o_in) { mp_obj_type_t *type = mp_obj_get_type(o_in); if (type->getiter != NULL) { diff --git a/py/runtime.h b/py/runtime.h index b233d23b4a..6b3ab73d8f 100644 --- a/py/runtime.h +++ b/py/runtime.h @@ -45,10 +45,12 @@ mp_obj_t mp_call_method_n_kw_var(bool have_self, uint n_args_n_kw, const mp_obj_ void mp_unpack_sequence(mp_obj_t seq, uint num, mp_obj_t *items); mp_obj_t mp_store_map(mp_obj_t map, mp_obj_t key, mp_obj_t value); mp_obj_t mp_load_attr(mp_obj_t base, qstr attr); +mp_obj_t mp_load_attr_default(mp_obj_t base, qstr attr, mp_obj_t defval); void mp_load_method(mp_obj_t base, qstr attr, mp_obj_t *dest); void mp_load_method_maybe(mp_obj_t base, qstr attr, mp_obj_t *dest); void mp_store_attr(mp_obj_t base, qstr attr, mp_obj_t val); void mp_store_subscr(mp_obj_t base, mp_obj_t index, mp_obj_t val); +void mp_delete_subscr(mp_obj_t base, mp_obj_t index); mp_obj_t mp_getiter(mp_obj_t o); mp_obj_t mp_iternext_allow_raise(mp_obj_t o); // may return MP_OBJ_NULL instead of raising StopIteration() @@ -324,6 +324,11 @@ dispatch_loop: mp_delete_name(qst); break; + case MP_BC_DELETE_SUBSCR: + mp_delete_subscr(sp[-1], sp[0]); + sp -= 2; + break; + case MP_BC_DUP_TOP: obj1 = TOP(); PUSH(obj1); diff --git a/stmhal/autoflash b/stmhal/autoflash index 0f4bfd0394..dc28a97e29 100755 --- a/stmhal/autoflash +++ b/stmhal/autoflash @@ -1,7 +1,13 @@ #!/bin/sh # -# this script waits for a DFU device to be attached and then flashes it -# it then waits until the DFU mode is exited, and then loops +# This script loops doing the following: +# - wait for DFU device +# - flash DFU device +# - wait for DFU to exit +# - wait for serial port to appear +# - run a terminal + +SERIAL=/dev/ttyACM0 while true; do echo "waiting for DFU device..." @@ -11,8 +17,10 @@ while true; do fi sleep 1s done + echo "found DFU device, flashing" dfu-util -a 0 -D build/flash.dfu + echo "waiting for DFU to exit..." while true; do if lsusb | grep -q DFU; then @@ -21,4 +29,15 @@ while true; do fi break done + + echo "waiting for $SERIAL..." + while true; do + if ls /dev/tty* | grep -q $SERIAL; then + break + fi + sleep 1s + continue + done + sleep 1s + picocom $SERIAL done diff --git a/stmhal/readline.c b/stmhal/readline.c index 09294789cd..13c2dc93ce 100644 --- a/stmhal/readline.c +++ b/stmhal/readline.c @@ -1,3 +1,4 @@ +#include <stdio.h> #include <string.h> #include <stm32f4xx_hal.h> @@ -11,10 +12,19 @@ #include "readline.h" #include "usb.h" +#if 0 // print debugging info +#define DEBUG_PRINT (1) +#define DEBUG_printf printf +#else // don't print debugging info +#define DEBUG_printf(...) (void)0 +#endif + #define READLINE_HIST_SIZE (8) static const char *readline_hist[READLINE_HIST_SIZE] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}; +enum { ESEQ_NONE, ESEQ_ESC, ESEQ_ESC_BRACKET, ESEQ_ESC_BRACKET_DIGIT, ESEQ_ESC_O }; + void readline_init(void) { memset(readline_hist, 0, READLINE_HIST_SIZE * sizeof(const char*)); } @@ -30,7 +40,7 @@ STATIC char *str_dup(const char *str) { int readline(vstr_t *line, const char *prompt) { stdout_tx_str(prompt); int orig_line_len = line->len; - int escape_seq = 0; + int escape_seq = ESEQ_NONE; char escape_seq_buf[1] = {0}; int hist_cur = -1; int cursor_pos = orig_line_len; @@ -40,16 +50,16 @@ int readline(vstr_t *line, const char *prompt) { int redraw_step_back = 0; bool redraw_from_cursor = false; int redraw_step_forward = 0; - if (escape_seq == 0) { + if (escape_seq == ESEQ_NONE) { if (VCP_CHAR_CTRL_A <= c && c <= VCP_CHAR_CTRL_D && vstr_len(line) == orig_line_len) { // control character with empty line return c; } else if (c == VCP_CHAR_CTRL_A) { // CTRL-A with non-empty line is go-to-start-of-line - redraw_step_back = cursor_pos - orig_line_len; + goto home_key; } else if (c == VCP_CHAR_CTRL_E) { // CTRL-E is go-to-end-of-line - redraw_step_forward = line->len - cursor_pos; + goto end_key; } else if (c == '\r') { // newline stdout_tx_str("\r\n"); @@ -64,7 +74,7 @@ int readline(vstr_t *line, const char *prompt) { return 0; } else if (c == 27) { // escape sequence - escape_seq = 1; + escape_seq = ESEQ_ESC; } else if (c == 8 || c == 127) { // backspace/delete if (cursor_pos > orig_line_len) { @@ -80,18 +90,24 @@ int readline(vstr_t *line, const char *prompt) { redraw_from_cursor = true; redraw_step_forward = 1; } - } else if (escape_seq == 1) { - if (c == '[') { - escape_seq = 2; - } else { - escape_seq = 0; + } else if (escape_seq == ESEQ_ESC) { + switch (c) { + case '[': + escape_seq = ESEQ_ESC_BRACKET; + break; + case 'O': + escape_seq = ESEQ_ESC_O; + break; + default: + DEBUG_printf("(ESC %d)", c); + escape_seq = ESEQ_NONE; } - } else if (escape_seq == 2) { + } else if (escape_seq == ESEQ_ESC_BRACKET) { if ('0' <= c && c <= '9') { - escape_seq = 3; + escape_seq = ESEQ_ESC_BRACKET_DIGIT; escape_seq_buf[0] = c; } else { - escape_seq = 0; + escape_seq = ESEQ_NONE; if (c == 'A') { // up arrow if (hist_cur + 1 < READLINE_HIST_SIZE && readline_hist[hist_cur + 1] != NULL) { @@ -130,21 +146,43 @@ int readline(vstr_t *line, const char *prompt) { if (cursor_pos > orig_line_len) { redraw_step_back = 1; } + } else if (c == 'H') { + // home + goto home_key; + } else if (c == 'F') { + // end + goto end_key; + } else { + DEBUG_printf("(ESC [ %d)", c); } } - } else if (escape_seq == 3) { - escape_seq = 0; + } else if (escape_seq == ESEQ_ESC_BRACKET_DIGIT) { if (c == '~') { if (escape_seq_buf[0] == '1' || escape_seq_buf[0] == '7') { - // home key +home_key: redraw_step_back = cursor_pos - orig_line_len; } else if (escape_seq_buf[0] == '4' || escape_seq_buf[0] == '8') { - // end key +end_key: redraw_step_forward = line->len - cursor_pos; + } else { + DEBUG_printf("(ESC [ %c %d)", escape_seq_buf[0], c); } + } else { + DEBUG_printf("(ESC [ %c %d)", escape_seq_buf[0], c); + } + escape_seq = ESEQ_NONE; + } else if (escape_seq == ESEQ_ESC_O) { + switch (c) { + case 'H': + goto home_key; + case 'F': + goto end_key; + default: + DEBUG_printf("(ESC O %d)", c); + escape_seq = ESEQ_NONE; } } else { - escape_seq = 0; + escape_seq = ESEQ_NONE; } // redraw command prompt, efficiently diff --git a/tests/basics/dict-del.py b/tests/basics/dict-del.py new file mode 100644 index 0000000000..d908fea424 --- /dev/null +++ b/tests/basics/dict-del.py @@ -0,0 +1,21 @@ +for n in range(20): + print('testing dict with {} items'.format(n)) + for i in range(n): + # create dict + d = dict() + for j in range(n): + d[str(j)] = j + print(len(d)) + + # delete an item + del d[str(i)] + print(len(d)) + + # check items + for j in range(n): + if str(j) in d: + if j == i: + print(j, 'in d, but it should not be') + else: + if j != i: + print(j, 'not in d, but it should be') diff --git a/tests/basics/getattr1.py b/tests/basics/getattr1.py index 7b7e073922..9a96154ca5 100644 --- a/tests/basics/getattr1.py +++ b/tests/basics/getattr1.py @@ -13,3 +13,5 @@ a = A() print(getattr(a, "var")) print(getattr(a, "var2")) print(getattr(a, "meth")(5)) +print(getattr(a, "_none_such", 123)) +print(getattr(list, "foo", 456)) diff --git a/tests/basics/set_remove.py b/tests/basics/set_remove.py index 208ab137f3..5627516c43 100644 --- a/tests/basics/set_remove.py +++ b/tests/basics/set_remove.py @@ -1,3 +1,4 @@ +# basic test s = {1} print(s.remove(1)) print(list(s)) @@ -7,3 +8,26 @@ except KeyError: pass else: print("failed to raise KeyError") + +# test sets of varying size +for n in range(20): + print('testing set with {} items'.format(n)) + for i in range(n): + # create set + s = set() + for j in range(n): + s.add(str(j)) + print(len(s)) + + # delete an item + s.remove(str(i)) + print(len(s)) + + # check items + for j in range(n): + if str(j) in s: + if j == i: + print(j, 'in s, but it should not be') + else: + if j != i: + print(j, 'not in s, but it should be') diff --git a/tests/basics/types2.py b/tests/basics/types2.py new file mode 100644 index 0000000000..83a69c918f --- /dev/null +++ b/tests/basics/types2.py @@ -0,0 +1,12 @@ +# Types are hashable +print(hash(type) != 0) +print(hash(int) != 0) +print(hash(list) != 0) +class Foo: pass +print(hash(Foo) != 0) + +print(int == int) +print(int != list) + +d = {} +d[int] = float |