summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--py/builtin.c12
-rw-r--r--py/builtin.h1
-rw-r--r--py/builtintables.c1
-rw-r--r--py/map.c188
-rw-r--r--py/obj.c5
-rw-r--r--py/obj.h9
-rw-r--r--py/objdict.c9
-rw-r--r--py/objfun.c13
-rw-r--r--py/objset.c19
-rw-r--r--py/objtype.c11
-rw-r--r--py/runtime.c53
-rw-r--r--py/runtime.h2
-rw-r--r--py/vm.c5
-rwxr-xr-xstmhal/autoflash23
-rw-r--r--stmhal/readline.c74
-rw-r--r--tests/basics/dict-del.py21
-rw-r--r--tests/basics/getattr1.py2
-rw-r--r--tests/basics/set_remove.py24
-rw-r--r--tests/basics/types2.py12
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 },
diff --git a/py/map.c b/py/map.c
index 301ea51ae4..933b5f8e29 100644
--- a/py/map.c
+++ b/py/map.c
@@ -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) {
diff --git a/py/obj.c b/py/obj.c
index e177782a8b..d4eabbe840 100644
--- a/py/obj.c
+++ b/py/obj.c
@@ -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) {
diff --git a/py/obj.h b/py/obj.h
index b44b9c0a51..eb447f916c 100644
--- a/py/obj.h
+++ b/py/obj.h
@@ -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()
diff --git a/py/vm.c b/py/vm.c
index 6186bbcefb..f0afd6272b 100644
--- a/py/vm.c
+++ b/py/vm.c
@@ -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