blob: 09a5b321227807a09287b4f9a4a32b3ba5c800d3 [file] [log] [blame]
Damien George04b91472014-05-03 23:27:38 +01001/*
2 * This file is part of the Micro Python project, http://micropython.org/
3 *
4 * The MIT License (MIT)
5 *
6 * Copyright (c) 2013, 2014 Damien P. George
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a copy
9 * of this software and associated documentation files (the "Software"), to deal
10 * in the Software without restriction, including without limitation the rights
11 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 * copies of the Software, and to permit persons to whom the Software is
13 * furnished to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 * THE SOFTWARE.
25 */
26
Damiend99b0522013-12-21 18:17:45 +000027#include <string.h>
28#include <assert.h>
29
Damien George51dfcb42015-01-01 20:27:54 +000030#include "py/nlr.h"
31#include "py/obj.h"
32#include "py/runtime0.h"
33#include "py/runtime.h"
34#include "py/builtin.h"
Paul Sokolovsky0ef01d02015-03-18 01:25:04 +020035#include "py/objtype.h"
36
37#define MP_OBJ_IS_DICT_TYPE(o) (MP_OBJ_IS_OBJ(o) && ((mp_obj_base_t*)o)->type->make_new == dict_make_new)
Damiend99b0522013-12-21 18:17:45 +000038
Damien George93965e72014-08-30 13:23:35 +010039STATIC mp_obj_t dict_update(mp_uint_t n_args, const mp_obj_t *args, mp_map_t *kwargs);
John R. Lentona41fe312014-01-06 17:17:02 +000040
Damien George8a9b9992014-09-17 15:53:03 +010041// This is a helper function to iterate through a dictionary. The state of
42// the iteration is held in *cur and should be initialised with zero for the
43// first call. Will return NULL when no more elements are available.
44STATIC mp_map_elem_t *dict_iter_next(mp_obj_dict_t *dict, mp_uint_t *cur) {
45 mp_uint_t max = dict->map.alloc;
46 mp_map_t *map = &dict->map;
47
48 for (mp_uint_t i = *cur; i < max; i++) {
49 if (MP_MAP_SLOT_IS_FILLED(map, i)) {
50 *cur = i + 1;
51 return &(map->table[i]);
52 }
53 }
54
55 return NULL;
56}
57
Paul Sokolovskyd5df6cd2014-02-12 18:15:40 +020058STATIC void dict_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in, mp_print_kind_t kind) {
Damiend99b0522013-12-21 18:17:45 +000059 mp_obj_dict_t *self = self_in;
60 bool first = true;
Damien George612045f2014-09-17 22:56:34 +010061 if (!(MICROPY_PY_UJSON && kind == PRINT_JSON)) {
62 kind = PRINT_REPR;
63 }
Paul Sokolovsky0ef01d02015-03-18 01:25:04 +020064 if (MICROPY_PY_COLLECTIONS_ORDEREDDICT && self->base.type != &mp_type_dict) {
65 print(env, "%s(", qstr_str(self->base.type->name));
66 }
Damiend99b0522013-12-21 18:17:45 +000067 print(env, "{");
Damien George8a9b9992014-09-17 15:53:03 +010068 mp_uint_t cur = 0;
John R. Lentona41fe312014-01-06 17:17:02 +000069 mp_map_elem_t *next = NULL;
Damien George8a9b9992014-09-17 15:53:03 +010070 while ((next = dict_iter_next(self, &cur)) != NULL) {
John R. Lentona41fe312014-01-06 17:17:02 +000071 if (!first) {
72 print(env, ", ");
Damiend99b0522013-12-21 18:17:45 +000073 }
John R. Lentona41fe312014-01-06 17:17:02 +000074 first = false;
Damien George612045f2014-09-17 22:56:34 +010075 mp_obj_print_helper(print, env, next->key, kind);
John R. Lentona41fe312014-01-06 17:17:02 +000076 print(env, ": ");
Damien George612045f2014-09-17 22:56:34 +010077 mp_obj_print_helper(print, env, next->value, kind);
Damiend99b0522013-12-21 18:17:45 +000078 }
79 print(env, "}");
Paul Sokolovsky0ef01d02015-03-18 01:25:04 +020080 if (MICROPY_PY_COLLECTIONS_ORDEREDDICT && self->base.type != &mp_type_dict) {
81 print(env, ")");
82 }
Damiend99b0522013-12-21 18:17:45 +000083}
84
Damien Georgeecc88e92014-08-30 00:35:11 +010085STATIC mp_obj_t dict_make_new(mp_obj_t type_in, mp_uint_t n_args, mp_uint_t n_kw, const mp_obj_t *args) {
Paul Sokolovsky0ef01d02015-03-18 01:25:04 +020086 mp_obj_dict_t *dict = mp_obj_new_dict(0);
87 dict->base.type = type_in;
88 #if MICROPY_PY_COLLECTIONS_ORDEREDDICT
89 if (type_in == &mp_type_ordereddict) {
90 dict->map.is_ordered = 1;
91 }
92 #endif
Damien Georgebcb6ca42014-06-03 12:53:44 +010093 if (n_args > 0 || n_kw > 0) {
94 mp_obj_t args2[2] = {dict, args[0]}; // args[0] is always valid, even if it's not a positional arg
95 mp_map_t kwargs;
96 mp_map_init_fixed_table(&kwargs, n_kw, args + n_args);
97 dict_update(n_args + 1, args2, &kwargs); // dict_update will check that n_args + 1 == 1 or 2
Damien Georged7aadcf2014-04-04 15:08:00 +010098 }
Damien Georged7aadcf2014-04-04 15:08:00 +010099 return dict;
Damien George71c51812014-01-04 20:21:15 +0000100}
101
Damien Georgeecc88e92014-08-30 00:35:11 +0100102STATIC mp_obj_t dict_unary_op(mp_uint_t op, mp_obj_t self_in) {
Damien George4e8dc8c2014-01-27 23:15:32 +0000103 mp_obj_dict_t *self = self_in;
104 switch (op) {
Damien Georged17926d2014-03-30 13:35:08 +0100105 case MP_UNARY_OP_BOOL: return MP_BOOL(self->map.used != 0);
Damien Georgebb4c6f32014-07-31 10:49:14 +0100106 case MP_UNARY_OP_LEN: return MP_OBJ_NEW_SMALL_INT(self->map.used);
Damien George6ac5dce2014-05-21 19:42:43 +0100107 default: return MP_OBJ_NULL; // op not supported
Damien George4e8dc8c2014-01-27 23:15:32 +0000108 }
109}
110
Damien Georgeecc88e92014-08-30 00:35:11 +0100111STATIC mp_obj_t dict_binary_op(mp_uint_t op, mp_obj_t lhs_in, mp_obj_t rhs_in) {
Damiend99b0522013-12-21 18:17:45 +0000112 mp_obj_dict_t *o = lhs_in;
113 switch (op) {
Damien George729f7b42014-04-17 22:10:53 +0100114 case MP_BINARY_OP_IN: {
John R. Lentonc1bef212014-01-11 12:39:33 +0000115 mp_map_elem_t *elem = mp_map_lookup(&o->map, rhs_in, MP_MAP_LOOKUP);
Damien George9aa2a522014-02-01 23:04:09 +0000116 return MP_BOOL(elem != NULL);
John R. Lentonc1bef212014-01-11 12:39:33 +0000117 }
Paul Sokolovsky7cf057a2014-04-06 21:20:52 +0300118 case MP_BINARY_OP_EQUAL: {
Paul Sokolovsky0ef01d02015-03-18 01:25:04 +0200119 #if MICROPY_PY_COLLECTIONS_ORDEREDDICT
120 if (MP_UNLIKELY(MP_OBJ_IS_TYPE(lhs_in, &mp_type_ordereddict) && MP_OBJ_IS_TYPE(rhs_in, &mp_type_ordereddict))) {
121 //TODO: implement
122 return MP_OBJ_NULL;
123 } else
124 #endif
Paul Sokolovsky7cf057a2014-04-06 21:20:52 +0300125 if (MP_OBJ_IS_TYPE(rhs_in, &mp_type_dict)) {
126 mp_obj_dict_t *rhs = rhs_in;
127 if (o->map.used != rhs->map.used) {
128 return mp_const_false;
129 }
130
Damien George8a9b9992014-09-17 15:53:03 +0100131 mp_uint_t cur = 0;
132 mp_map_elem_t *next = NULL;
133 while ((next = dict_iter_next(o, &cur)) != NULL) {
134 mp_map_elem_t *elem = mp_map_lookup(&rhs->map, next->key, MP_MAP_LOOKUP);
135 if (elem == NULL || !mp_obj_equal(next->value, elem->value)) {
136 return mp_const_false;
Paul Sokolovsky7cf057a2014-04-06 21:20:52 +0300137 }
138 }
139 return mp_const_true;
Damien Georgee22d76e2014-04-11 10:52:06 +0000140 } else {
141 // dict is not equal to instance of any other type
142 return mp_const_false;
Paul Sokolovsky7cf057a2014-04-06 21:20:52 +0300143 }
144 }
Damiend99b0522013-12-21 18:17:45 +0000145 default:
146 // op not supported
Damien George6ac5dce2014-05-21 19:42:43 +0100147 return MP_OBJ_NULL;
Damiend99b0522013-12-21 18:17:45 +0000148 }
149}
150
Paul Sokolovsky75ce9252014-06-05 20:02:15 +0300151// TODO: Make sure this is inlined in dict_subscr() below.
152mp_obj_t mp_obj_dict_get(mp_obj_t self_in, mp_obj_t index) {
153 mp_obj_dict_t *self = self_in;
154 mp_map_elem_t *elem = mp_map_lookup(&self->map, index, MP_MAP_LOOKUP);
155 if (elem == NULL) {
156 nlr_raise(mp_obj_new_exception_msg(&mp_type_KeyError, "<value>"));
157 } else {
158 return elem->value;
159 }
160}
161
Damien George729f7b42014-04-17 22:10:53 +0100162STATIC mp_obj_t dict_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) {
Damien Georgef4c9b332014-04-08 21:32:29 +0100163 if (value == MP_OBJ_NULL) {
Damien George729f7b42014-04-17 22:10:53 +0100164 // delete
Damien Georgef4c9b332014-04-08 21:32:29 +0100165 mp_obj_dict_delete(self_in, index);
Damien George729f7b42014-04-17 22:10:53 +0100166 return mp_const_none;
167 } else if (value == MP_OBJ_SENTINEL) {
168 // load
169 mp_obj_dict_t *self = self_in;
170 mp_map_elem_t *elem = mp_map_lookup(&self->map, index, MP_MAP_LOOKUP);
171 if (elem == NULL) {
172 nlr_raise(mp_obj_new_exception_msg(&mp_type_KeyError, "<value>"));
173 } else {
174 return elem->value;
175 }
Damien Georgef4c9b332014-04-08 21:32:29 +0100176 } else {
Damien George729f7b42014-04-17 22:10:53 +0100177 // store
Damien Georgef4c9b332014-04-08 21:32:29 +0100178 mp_obj_dict_store(self_in, index, value);
Damien George729f7b42014-04-17 22:10:53 +0100179 return mp_const_none;
Damien Georgef4c9b332014-04-08 21:32:29 +0100180 }
Damien Georgef4c9b332014-04-08 21:32:29 +0100181}
John R. Lentona41fe312014-01-06 17:17:02 +0000182
183/******************************************************************************/
184/* dict iterator */
185
186typedef struct _mp_obj_dict_it_t {
187 mp_obj_base_t base;
188 mp_obj_dict_t *dict;
Damien George40f3c022014-07-03 13:25:24 +0100189 mp_uint_t cur;
John R. Lentona41fe312014-01-06 17:17:02 +0000190} mp_obj_dict_it_t;
191
Damien George8a9b9992014-09-17 15:53:03 +0100192STATIC mp_obj_t dict_it_iternext(mp_obj_t self_in) {
John R. Lentona41fe312014-01-06 17:17:02 +0000193 mp_obj_dict_it_t *self = self_in;
Damien George8a9b9992014-09-17 15:53:03 +0100194 mp_map_elem_t *next = dict_iter_next(self->dict, &self->cur);
John R. Lentona41fe312014-01-06 17:17:02 +0000195
Damien George8a9b9992014-09-17 15:53:03 +0100196 if (next == NULL) {
Damien Georgeea8d06c2014-04-17 23:19:36 +0100197 return MP_OBJ_STOP_ITERATION;
Damien George8a9b9992014-09-17 15:53:03 +0100198 } else {
199 return next->key;
John R. Lentona41fe312014-01-06 17:17:02 +0000200 }
201}
202
Damien George3e1a5c12014-03-29 13:43:38 +0000203STATIC const mp_obj_type_t mp_type_dict_it = {
Damien Georgec5966122014-02-15 16:10:44 +0000204 { &mp_type_type },
Damien Georgea71c83a2014-02-15 11:34:50 +0000205 .name = MP_QSTR_iterator,
Paul Sokolovskyf7eaf602014-03-30 22:00:12 +0300206 .getiter = mp_identity,
John R. Lentona41fe312014-01-06 17:17:02 +0000207 .iternext = dict_it_iternext,
John R. Lentona41fe312014-01-06 17:17:02 +0000208};
209
Damien George8a9b9992014-09-17 15:53:03 +0100210STATIC mp_obj_t dict_getiter(mp_obj_t o_in) {
John R. Lentona41fe312014-01-06 17:17:02 +0000211 mp_obj_dict_it_t *o = m_new_obj(mp_obj_dict_it_t);
Damien George3e1a5c12014-03-29 13:43:38 +0000212 o->base.type = &mp_type_dict_it;
Damien George8a9b9992014-09-17 15:53:03 +0100213 o->dict = o_in;
214 o->cur = 0;
John R. Lentona41fe312014-01-06 17:17:02 +0000215 return o;
216}
217
John R. Lentona41fe312014-01-06 17:17:02 +0000218/******************************************************************************/
219/* dict methods */
220
Paul Sokolovskyd5df6cd2014-02-12 18:15:40 +0200221STATIC mp_obj_t dict_clear(mp_obj_t self_in) {
Paul Sokolovsky0ef01d02015-03-18 01:25:04 +0200222 assert(MP_OBJ_IS_DICT_TYPE(self_in));
John R. Lenton4ce6cea2014-01-06 17:38:47 +0000223 mp_obj_dict_t *self = self_in;
224
225 mp_map_clear(&self->map);
226
227 return mp_const_none;
228}
Paul Sokolovskyd5df6cd2014-02-12 18:15:40 +0200229STATIC MP_DEFINE_CONST_FUN_OBJ_1(dict_clear_obj, dict_clear);
John R. Lenton7d21d512014-01-06 17:54:04 +0000230
Paul Sokolovskyd5df6cd2014-02-12 18:15:40 +0200231STATIC mp_obj_t dict_copy(mp_obj_t self_in) {
Paul Sokolovsky0ef01d02015-03-18 01:25:04 +0200232 assert(MP_OBJ_IS_DICT_TYPE(self_in));
John R. Lentond90b19e2014-01-06 18:11:20 +0000233 mp_obj_dict_t *self = self_in;
234 mp_obj_dict_t *other = mp_obj_new_dict(self->map.alloc);
Paul Sokolovsky0ef01d02015-03-18 01:25:04 +0200235 other->base.type = self->base.type;
John R. Lentond90b19e2014-01-06 18:11:20 +0000236 other->map.used = self->map.used;
Paul Sokolovsky5fedd0c2014-04-06 21:00:58 +0300237 other->map.all_keys_are_qstrs = self->map.all_keys_are_qstrs;
Paul Sokolovsky0ef01d02015-03-18 01:25:04 +0200238 other->map.is_fixed = 0;
239 other->map.is_ordered = self->map.is_ordered;
John R. Lentond90b19e2014-01-06 18:11:20 +0000240 memcpy(other->map.table, self->map.table, self->map.alloc * sizeof(mp_map_elem_t));
241 return other;
242}
Paul Sokolovskyd5df6cd2014-02-12 18:15:40 +0200243STATIC MP_DEFINE_CONST_FUN_OBJ_1(dict_copy_obj, dict_copy);
John R. Lenton4ce6cea2014-01-06 17:38:47 +0000244
Damien Georgeeae16442014-01-11 19:22:29 +0000245// this is a classmethod
Damien George93965e72014-08-30 13:23:35 +0100246STATIC mp_obj_t dict_fromkeys(mp_uint_t n_args, const mp_obj_t *args) {
Damien Georgeeae16442014-01-11 19:22:29 +0000247 assert(2 <= n_args && n_args <= 3);
Damien Georged17926d2014-03-30 13:35:08 +0100248 mp_obj_t iter = mp_getiter(args[1]);
Damien Georgeeae16442014-01-11 19:22:29 +0000249 mp_obj_t len = mp_obj_len_maybe(iter);
250 mp_obj_t value = mp_const_none;
251 mp_obj_t next = NULL;
252 mp_obj_dict_t *self = NULL;
253
254 if (n_args > 2) {
255 value = args[2];
256 }
257
258 if (len == MP_OBJ_NULL) {
259 /* object's type doesn't have a __len__ slot */
260 self = mp_obj_new_dict(0);
261 } else {
262 self = mp_obj_new_dict(MP_OBJ_SMALL_INT_VALUE(len));
263 }
264
Damien Georgeea8d06c2014-04-17 23:19:36 +0100265 while ((next = mp_iternext(iter)) != MP_OBJ_STOP_ITERATION) {
Damien Georgeeae16442014-01-11 19:22:29 +0000266 mp_map_lookup(&self->map, next, MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)->value = value;
267 }
268
269 return self;
270}
Paul Sokolovskyd5df6cd2014-02-12 18:15:40 +0200271STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(dict_fromkeys_fun_obj, 2, 3, dict_fromkeys);
272STATIC MP_DEFINE_CONST_CLASSMETHOD_OBJ(dict_fromkeys_obj, (const mp_obj_t)&dict_fromkeys_fun_obj);
Damien Georgeeae16442014-01-11 19:22:29 +0000273
Paul Sokolovskyd5df6cd2014-02-12 18:15:40 +0200274STATIC mp_obj_t dict_get_helper(mp_map_t *self, mp_obj_t key, mp_obj_t deflt, mp_map_lookup_kind_t lookup_kind) {
Damien George38a2da62014-01-08 17:33:12 +0000275 mp_map_elem_t *elem = mp_map_lookup(self, key, lookup_kind);
John R. Lentonbe8fe5b2014-01-06 20:25:51 +0000276 mp_obj_t value;
Damien George8a9b9992014-09-17 15:53:03 +0100277 if (elem == NULL || elem->value == MP_OBJ_NULL) {
278 if (deflt == MP_OBJ_NULL) {
Damien George38a2da62014-01-08 17:33:12 +0000279 if (lookup_kind == MP_MAP_LOOKUP_REMOVE_IF_FOUND) {
Damien Georgeea13f402014-04-05 18:32:08 +0100280 nlr_raise(mp_obj_new_exception_msg(&mp_type_KeyError, "<value>"));
John R. Lenton0fcbaa42014-01-06 19:48:34 +0000281 } else {
John R. Lentonbe8fe5b2014-01-06 20:25:51 +0000282 value = mp_const_none;
John R. Lenton0fcbaa42014-01-06 19:48:34 +0000283 }
284 } else {
John R. Lentonbe8fe5b2014-01-06 20:25:51 +0000285 value = deflt;
John R. Lenton0fcbaa42014-01-06 19:48:34 +0000286 }
Damien George8a9b9992014-09-17 15:53:03 +0100287 if (lookup_kind == MP_MAP_LOOKUP_ADD_IF_NOT_FOUND) {
288 elem->value = value;
289 }
John R. Lenton0fcbaa42014-01-06 19:48:34 +0000290 } else {
John R. Lentonbe8fe5b2014-01-06 20:25:51 +0000291 value = elem->value;
Damien George8a9b9992014-09-17 15:53:03 +0100292 if (lookup_kind == MP_MAP_LOOKUP_REMOVE_IF_FOUND) {
293 elem->value = MP_OBJ_NULL; // so that GC can collect the deleted value
294 }
John R. Lentonbe8fe5b2014-01-06 20:25:51 +0000295 }
296 return value;
John R. Lenton0fcbaa42014-01-06 19:48:34 +0000297}
298
Damien George93965e72014-08-30 13:23:35 +0100299STATIC mp_obj_t dict_get(mp_uint_t n_args, const mp_obj_t *args) {
John R. Lentoncd088732014-01-06 18:53:16 +0000300 assert(2 <= n_args && n_args <= 3);
Paul Sokolovsky0ef01d02015-03-18 01:25:04 +0200301 assert(MP_OBJ_IS_DICT_TYPE(args[0]));
John R. Lentoncd088732014-01-06 18:53:16 +0000302
John R. Lenton0fcbaa42014-01-06 19:48:34 +0000303 return dict_get_helper(&((mp_obj_dict_t *)args[0])->map,
304 args[1],
Damien George8a9b9992014-09-17 15:53:03 +0100305 n_args == 3 ? args[2] : MP_OBJ_NULL,
Damien George38a2da62014-01-08 17:33:12 +0000306 MP_MAP_LOOKUP);
John R. Lentoncd088732014-01-06 18:53:16 +0000307}
Paul Sokolovskyd5df6cd2014-02-12 18:15:40 +0200308STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(dict_get_obj, 2, 3, dict_get);
John R. Lentoncd088732014-01-06 18:53:16 +0000309
Damien George93965e72014-08-30 13:23:35 +0100310STATIC mp_obj_t dict_pop(mp_uint_t n_args, const mp_obj_t *args) {
John R. Lenton0fcbaa42014-01-06 19:48:34 +0000311 assert(2 <= n_args && n_args <= 3);
Paul Sokolovsky0ef01d02015-03-18 01:25:04 +0200312 assert(MP_OBJ_IS_DICT_TYPE(args[0]));
John R. Lenton0fcbaa42014-01-06 19:48:34 +0000313
314 return dict_get_helper(&((mp_obj_dict_t *)args[0])->map,
315 args[1],
Damien George8a9b9992014-09-17 15:53:03 +0100316 n_args == 3 ? args[2] : MP_OBJ_NULL,
Damien George38a2da62014-01-08 17:33:12 +0000317 MP_MAP_LOOKUP_REMOVE_IF_FOUND);
John R. Lenton0fcbaa42014-01-06 19:48:34 +0000318}
Paul Sokolovskyd5df6cd2014-02-12 18:15:40 +0200319STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(dict_pop_obj, 2, 3, dict_pop);
John R. Lenton0fcbaa42014-01-06 19:48:34 +0000320
321
Damien George93965e72014-08-30 13:23:35 +0100322STATIC mp_obj_t dict_setdefault(mp_uint_t n_args, const mp_obj_t *args) {
John R. Lentonbe8fe5b2014-01-06 20:25:51 +0000323 assert(2 <= n_args && n_args <= 3);
Paul Sokolovsky0ef01d02015-03-18 01:25:04 +0200324 assert(MP_OBJ_IS_DICT_TYPE(args[0]));
John R. Lentonbe8fe5b2014-01-06 20:25:51 +0000325
326 return dict_get_helper(&((mp_obj_dict_t *)args[0])->map,
327 args[1],
Damien George8a9b9992014-09-17 15:53:03 +0100328 n_args == 3 ? args[2] : MP_OBJ_NULL,
Damien George38a2da62014-01-08 17:33:12 +0000329 MP_MAP_LOOKUP_ADD_IF_NOT_FOUND);
John R. Lentonbe8fe5b2014-01-06 20:25:51 +0000330}
Paul Sokolovskyd5df6cd2014-02-12 18:15:40 +0200331STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(dict_setdefault_obj, 2, 3, dict_setdefault);
John R. Lentonbe8fe5b2014-01-06 20:25:51 +0000332
John R. Lentonf77dce82014-01-06 20:08:52 +0000333
Paul Sokolovskyd5df6cd2014-02-12 18:15:40 +0200334STATIC mp_obj_t dict_popitem(mp_obj_t self_in) {
Paul Sokolovsky0ef01d02015-03-18 01:25:04 +0200335 assert(MP_OBJ_IS_DICT_TYPE(self_in));
John R. Lentonf77dce82014-01-06 20:08:52 +0000336 mp_obj_dict_t *self = self_in;
Damien George8a9b9992014-09-17 15:53:03 +0100337 mp_uint_t cur = 0;
338 mp_map_elem_t *next = dict_iter_next(self, &cur);
339 if (next == NULL) {
Damien Georgeea13f402014-04-05 18:32:08 +0100340 nlr_raise(mp_obj_new_exception_msg(&mp_type_KeyError, "popitem(): dictionary is empty"));
John R. Lentonf77dce82014-01-06 20:08:52 +0000341 }
John R. Lentonf77dce82014-01-06 20:08:52 +0000342 self->map.used--;
343 mp_obj_t items[] = {next->key, next->value};
Damien George8a9b9992014-09-17 15:53:03 +0100344 next->key = MP_OBJ_SENTINEL; // must mark key as sentinel to indicate that it was deleted
345 next->value = MP_OBJ_NULL;
John R. Lentonf77dce82014-01-06 20:08:52 +0000346 mp_obj_t tuple = mp_obj_new_tuple(2, items);
347
348 return tuple;
349}
Paul Sokolovskyd5df6cd2014-02-12 18:15:40 +0200350STATIC MP_DEFINE_CONST_FUN_OBJ_1(dict_popitem_obj, dict_popitem);
John R. Lentonf77dce82014-01-06 20:08:52 +0000351
Damien George93965e72014-08-30 13:23:35 +0100352STATIC mp_obj_t dict_update(mp_uint_t n_args, const mp_obj_t *args, mp_map_t *kwargs) {
Paul Sokolovsky0ef01d02015-03-18 01:25:04 +0200353 assert(MP_OBJ_IS_DICT_TYPE(args[0]));
Damien Georgebcb6ca42014-06-03 12:53:44 +0100354 mp_obj_dict_t *self = args[0];
355
356 mp_arg_check_num(n_args, kwargs->used, 1, 2, true);
357
358 if (n_args == 2) {
359 // given a positional argument
360
Paul Sokolovsky0ef01d02015-03-18 01:25:04 +0200361 if (MP_OBJ_IS_DICT_TYPE(args[1])) {
Damien Georgebcb6ca42014-06-03 12:53:44 +0100362 // update from other dictionary (make sure other is not self)
363 if (args[1] != self) {
Damien George8a9b9992014-09-17 15:53:03 +0100364 mp_uint_t cur = 0;
Damien Georgebcb6ca42014-06-03 12:53:44 +0100365 mp_map_elem_t *elem = NULL;
Damien George8a9b9992014-09-17 15:53:03 +0100366 while ((elem = dict_iter_next((mp_obj_dict_t*)args[1], &cur)) != NULL) {
Damien Georgebcb6ca42014-06-03 12:53:44 +0100367 mp_map_lookup(&self->map, elem->key, MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)->value = elem->value;
368 }
369 }
John R. Lenton88f30432014-01-06 22:58:17 +0000370 } else {
Damien Georgebcb6ca42014-06-03 12:53:44 +0100371 // update from a generic iterable of pairs
372 mp_obj_t iter = mp_getiter(args[1]);
373 mp_obj_t next = NULL;
374 while ((next = mp_iternext(iter)) != MP_OBJ_STOP_ITERATION) {
375 mp_obj_t inneriter = mp_getiter(next);
376 mp_obj_t key = mp_iternext(inneriter);
377 mp_obj_t value = mp_iternext(inneriter);
378 mp_obj_t stop = mp_iternext(inneriter);
379 if (key == MP_OBJ_STOP_ITERATION
380 || value == MP_OBJ_STOP_ITERATION
381 || stop != MP_OBJ_STOP_ITERATION) {
382 nlr_raise(mp_obj_new_exception_msg(
383 &mp_type_ValueError,
384 "dictionary update sequence has the wrong length"));
385 } else {
386 mp_map_lookup(&self->map, key, MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)->value = value;
387 }
388 }
389 }
390 }
391
392 // update the dict with any keyword args
Damien George40f3c022014-07-03 13:25:24 +0100393 for (mp_uint_t i = 0; i < kwargs->alloc; i++) {
Damien Georgebcb6ca42014-06-03 12:53:44 +0100394 if (MP_MAP_SLOT_IS_FILLED(kwargs, i)) {
395 mp_map_lookup(&self->map, kwargs->table[i].key, MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)->value = kwargs->table[i].value;
John R. Lenton88f30432014-01-06 22:58:17 +0000396 }
397 }
398
399 return mp_const_none;
400}
Damien Georgebcb6ca42014-06-03 12:53:44 +0100401STATIC MP_DEFINE_CONST_FUN_OBJ_KW(dict_update_obj, 1, dict_update);
John R. Lenton88f30432014-01-06 22:58:17 +0000402
John R. Lentonf77dce82014-01-06 20:08:52 +0000403
John R. Lentona41fe312014-01-06 17:17:02 +0000404/******************************************************************************/
John R. Lenton9ec3a872014-01-10 01:00:20 +0000405/* dict views */
406
Paul Sokolovskyd5df6cd2014-02-12 18:15:40 +0200407STATIC const mp_obj_type_t dict_view_type;
408STATIC const mp_obj_type_t dict_view_it_type;
John R. Lenton9ec3a872014-01-10 01:00:20 +0000409
410typedef enum _mp_dict_view_kind_t {
411 MP_DICT_VIEW_ITEMS,
412 MP_DICT_VIEW_KEYS,
413 MP_DICT_VIEW_VALUES,
414} mp_dict_view_kind_t;
415
Paul Sokolovskyd5df6cd2014-02-12 18:15:40 +0200416STATIC char *mp_dict_view_names[] = {"dict_items", "dict_keys", "dict_values"};
John R. Lenton9ec3a872014-01-10 01:00:20 +0000417
418typedef struct _mp_obj_dict_view_it_t {
419 mp_obj_base_t base;
420 mp_dict_view_kind_t kind;
Damien George8a9b9992014-09-17 15:53:03 +0100421 mp_obj_dict_t *dict;
Damien George40f3c022014-07-03 13:25:24 +0100422 mp_uint_t cur;
John R. Lenton9ec3a872014-01-10 01:00:20 +0000423} mp_obj_dict_view_it_t;
424
425typedef struct _mp_obj_dict_view_t {
426 mp_obj_base_t base;
427 mp_obj_dict_t *dict;
428 mp_dict_view_kind_t kind;
429} mp_obj_dict_view_t;
430
Paul Sokolovskyd5df6cd2014-02-12 18:15:40 +0200431STATIC mp_obj_t dict_view_it_iternext(mp_obj_t self_in) {
John R. Lenton9ec3a872014-01-10 01:00:20 +0000432 assert(MP_OBJ_IS_TYPE(self_in, &dict_view_it_type));
433 mp_obj_dict_view_it_t *self = self_in;
Damien George8a9b9992014-09-17 15:53:03 +0100434 mp_map_elem_t *next = dict_iter_next(self->dict, &self->cur);
John R. Lenton9ec3a872014-01-10 01:00:20 +0000435
Damien George8a9b9992014-09-17 15:53:03 +0100436 if (next == NULL) {
437 return MP_OBJ_STOP_ITERATION;
438 } else {
John R. Lenton9ec3a872014-01-10 01:00:20 +0000439 switch (self->kind) {
Damien Georgeeae16442014-01-11 19:22:29 +0000440 case MP_DICT_VIEW_ITEMS:
Damien Georged2d64f02015-01-14 21:32:42 +0000441 default: {
Damien Georgeeae16442014-01-11 19:22:29 +0000442 mp_obj_t items[] = {next->key, next->value};
443 return mp_obj_new_tuple(2, items);
444 }
445 case MP_DICT_VIEW_KEYS:
446 return next->key;
447 case MP_DICT_VIEW_VALUES:
448 return next->value;
John R. Lenton9ec3a872014-01-10 01:00:20 +0000449 }
John R. Lenton9ec3a872014-01-10 01:00:20 +0000450 }
451}
452
Paul Sokolovskyd5df6cd2014-02-12 18:15:40 +0200453STATIC const mp_obj_type_t dict_view_it_type = {
Damien Georgec5966122014-02-15 16:10:44 +0000454 { &mp_type_type },
Damien Georgea71c83a2014-02-15 11:34:50 +0000455 .name = MP_QSTR_iterator,
Paul Sokolovskyf7eaf602014-03-30 22:00:12 +0300456 .getiter = mp_identity,
John R. Lenton9ec3a872014-01-10 01:00:20 +0000457 .iternext = dict_view_it_iternext,
John R. Lenton9ec3a872014-01-10 01:00:20 +0000458};
459
Paul Sokolovskyd5df6cd2014-02-12 18:15:40 +0200460STATIC mp_obj_t dict_view_getiter(mp_obj_t view_in) {
John R. Lenton9ec3a872014-01-10 01:00:20 +0000461 assert(MP_OBJ_IS_TYPE(view_in, &dict_view_type));
462 mp_obj_dict_view_t *view = view_in;
463 mp_obj_dict_view_it_t *o = m_new_obj(mp_obj_dict_view_it_t);
464 o->base.type = &dict_view_it_type;
465 o->kind = view->kind;
Damien George8a9b9992014-09-17 15:53:03 +0100466 o->dict = view->dict;
467 o->cur = 0;
John R. Lenton9ec3a872014-01-10 01:00:20 +0000468 return o;
469}
470
Paul Sokolovskyd5df6cd2014-02-12 18:15:40 +0200471STATIC void dict_view_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in, mp_print_kind_t kind) {
Damien Georgeff8dd3f2015-01-20 12:47:20 +0000472 (void)kind;
John R. Lenton9ec3a872014-01-10 01:00:20 +0000473 assert(MP_OBJ_IS_TYPE(self_in, &dict_view_type));
474 mp_obj_dict_view_t *self = self_in;
475 bool first = true;
476 print(env, mp_dict_view_names[self->kind]);
477 print(env, "([");
478 mp_obj_t *self_iter = dict_view_getiter(self);
479 mp_obj_t *next = NULL;
Damien Georgeea8d06c2014-04-17 23:19:36 +0100480 while ((next = dict_view_it_iternext(self_iter)) != MP_OBJ_STOP_ITERATION) {
John R. Lenton9ec3a872014-01-10 01:00:20 +0000481 if (!first) {
482 print(env, ", ");
483 }
484 first = false;
Paul Sokolovsky76d982e2014-01-13 19:19:16 +0200485 mp_obj_print_helper(print, env, next, PRINT_REPR);
John R. Lenton9ec3a872014-01-10 01:00:20 +0000486 }
487 print(env, "])");
488}
489
Damien Georgeecc88e92014-08-30 00:35:11 +0100490STATIC mp_obj_t dict_view_binary_op(mp_uint_t op, mp_obj_t lhs_in, mp_obj_t rhs_in) {
Damien George6ac5dce2014-05-21 19:42:43 +0100491 // only supported for the 'keys' kind until sets and dicts are refactored
John R. Lentonc1bef212014-01-11 12:39:33 +0000492 mp_obj_dict_view_t *o = lhs_in;
Damien Georged0a5bf32014-05-10 13:55:11 +0100493 if (o->kind != MP_DICT_VIEW_KEYS) {
Damien George6ac5dce2014-05-21 19:42:43 +0100494 return MP_OBJ_NULL; // op not supported
Damien Georged0a5bf32014-05-10 13:55:11 +0100495 }
496 if (op != MP_BINARY_OP_IN) {
Damien George6ac5dce2014-05-21 19:42:43 +0100497 return MP_OBJ_NULL; // op not supported
Damien Georged0a5bf32014-05-10 13:55:11 +0100498 }
John R. Lentonc1bef212014-01-11 12:39:33 +0000499 return dict_binary_op(op, o->dict, rhs_in);
500}
501
Paul Sokolovskyd5df6cd2014-02-12 18:15:40 +0200502STATIC const mp_obj_type_t dict_view_type = {
Damien Georgec5966122014-02-15 16:10:44 +0000503 { &mp_type_type },
Damien Georgea71c83a2014-02-15 11:34:50 +0000504 .name = MP_QSTR_dict_view,
John R. Lenton9ec3a872014-01-10 01:00:20 +0000505 .print = dict_view_print,
John R. Lentonc1bef212014-01-11 12:39:33 +0000506 .binary_op = dict_view_binary_op,
John R. Lenton9ec3a872014-01-10 01:00:20 +0000507 .getiter = dict_view_getiter,
508};
509
Damien George969a6b32014-12-10 22:07:04 +0000510STATIC mp_obj_t mp_obj_new_dict_view(mp_obj_dict_t *dict, mp_dict_view_kind_t kind) {
John R. Lenton9ec3a872014-01-10 01:00:20 +0000511 mp_obj_dict_view_t *o = m_new_obj(mp_obj_dict_view_t);
512 o->base.type = &dict_view_type;
513 o->dict = dict;
514 o->kind = kind;
515 return o;
516}
517
Paul Sokolovskyd5df6cd2014-02-12 18:15:40 +0200518STATIC mp_obj_t dict_view(mp_obj_t self_in, mp_dict_view_kind_t kind) {
Paul Sokolovsky0ef01d02015-03-18 01:25:04 +0200519 assert(MP_OBJ_IS_DICT_TYPE(self_in));
John R. Lenton9ec3a872014-01-10 01:00:20 +0000520 mp_obj_dict_t *self = self_in;
521 return mp_obj_new_dict_view(self, kind);
522}
523
Paul Sokolovskyd5df6cd2014-02-12 18:15:40 +0200524STATIC mp_obj_t dict_items(mp_obj_t self_in) {
John R. Lenton9ec3a872014-01-10 01:00:20 +0000525 return dict_view(self_in, MP_DICT_VIEW_ITEMS);
526}
Paul Sokolovskyd5df6cd2014-02-12 18:15:40 +0200527STATIC MP_DEFINE_CONST_FUN_OBJ_1(dict_items_obj, dict_items);
John R. Lenton9ec3a872014-01-10 01:00:20 +0000528
Paul Sokolovskyd5df6cd2014-02-12 18:15:40 +0200529STATIC mp_obj_t dict_keys(mp_obj_t self_in) {
John R. Lenton9ec3a872014-01-10 01:00:20 +0000530 return dict_view(self_in, MP_DICT_VIEW_KEYS);
531}
Paul Sokolovskyd5df6cd2014-02-12 18:15:40 +0200532STATIC MP_DEFINE_CONST_FUN_OBJ_1(dict_keys_obj, dict_keys);
John R. Lenton9ec3a872014-01-10 01:00:20 +0000533
Paul Sokolovskyd5df6cd2014-02-12 18:15:40 +0200534STATIC mp_obj_t dict_values(mp_obj_t self_in) {
John R. Lenton9ec3a872014-01-10 01:00:20 +0000535 return dict_view(self_in, MP_DICT_VIEW_VALUES);
536}
Paul Sokolovskyd5df6cd2014-02-12 18:15:40 +0200537STATIC MP_DEFINE_CONST_FUN_OBJ_1(dict_values_obj, dict_values);
John R. Lenton9ec3a872014-01-10 01:00:20 +0000538
John R. Lenton4bee76e2014-01-10 11:25:03 +0000539/******************************************************************************/
Damien Georgeeae16442014-01-11 19:22:29 +0000540/* dict constructors & public C API */
John R. Lentona41fe312014-01-06 17:17:02 +0000541
Damien George9b196cd2014-03-26 21:47:19 +0000542STATIC const mp_map_elem_t dict_locals_dict_table[] = {
543 { MP_OBJ_NEW_QSTR(MP_QSTR_clear), (mp_obj_t)&dict_clear_obj },
544 { MP_OBJ_NEW_QSTR(MP_QSTR_copy), (mp_obj_t)&dict_copy_obj },
545 { MP_OBJ_NEW_QSTR(MP_QSTR_fromkeys), (mp_obj_t)&dict_fromkeys_obj },
546 { MP_OBJ_NEW_QSTR(MP_QSTR_get), (mp_obj_t)&dict_get_obj },
547 { MP_OBJ_NEW_QSTR(MP_QSTR_items), (mp_obj_t)&dict_items_obj },
548 { MP_OBJ_NEW_QSTR(MP_QSTR_keys), (mp_obj_t)&dict_keys_obj },
549 { MP_OBJ_NEW_QSTR(MP_QSTR_pop), (mp_obj_t)&dict_pop_obj },
550 { MP_OBJ_NEW_QSTR(MP_QSTR_popitem), (mp_obj_t)&dict_popitem_obj },
551 { MP_OBJ_NEW_QSTR(MP_QSTR_setdefault), (mp_obj_t)&dict_setdefault_obj },
552 { MP_OBJ_NEW_QSTR(MP_QSTR_update), (mp_obj_t)&dict_update_obj },
553 { MP_OBJ_NEW_QSTR(MP_QSTR_values), (mp_obj_t)&dict_values_obj },
Paul Sokolovsky68e7c512014-04-13 11:53:15 +0300554 { MP_OBJ_NEW_QSTR(MP_QSTR___getitem__), (mp_obj_t)&mp_op_getitem_obj },
Paul Sokolovskycd94b382014-04-13 23:41:49 +0300555 { MP_OBJ_NEW_QSTR(MP_QSTR___setitem__), (mp_obj_t)&mp_op_setitem_obj },
Paul Sokolovsky14de1142014-04-13 23:55:59 +0300556 { MP_OBJ_NEW_QSTR(MP_QSTR___delitem__), (mp_obj_t)&mp_op_delitem_obj },
John R. Lentonbaa66542014-01-07 23:18:25 +0000557};
558
Damien George9b196cd2014-03-26 21:47:19 +0000559STATIC MP_DEFINE_CONST_DICT(dict_locals_dict, dict_locals_dict_table);
560
Damien George3e1a5c12014-03-29 13:43:38 +0000561const mp_obj_type_t mp_type_dict = {
Damien Georgec5966122014-02-15 16:10:44 +0000562 { &mp_type_type },
Damien Georgea71c83a2014-02-15 11:34:50 +0000563 .name = MP_QSTR_dict,
Paul Sokolovsky860ffb02014-01-05 22:34:09 +0200564 .print = dict_print,
565 .make_new = dict_make_new,
Damien George4e8dc8c2014-01-27 23:15:32 +0000566 .unary_op = dict_unary_op,
Paul Sokolovsky860ffb02014-01-05 22:34:09 +0200567 .binary_op = dict_binary_op,
Damien George729f7b42014-04-17 22:10:53 +0100568 .subscr = dict_subscr,
John R. Lentona41fe312014-01-06 17:17:02 +0000569 .getiter = dict_getiter,
Damien George9b196cd2014-03-26 21:47:19 +0000570 .locals_dict = (mp_obj_t)&dict_locals_dict,
Damiend99b0522013-12-21 18:17:45 +0000571};
572
Paul Sokolovsky0ef01d02015-03-18 01:25:04 +0200573#if MICROPY_PY_COLLECTIONS_ORDEREDDICT
574STATIC const mp_obj_tuple_t ordereddict_base_tuple = {{&mp_type_tuple}, 1, {(mp_obj_t)&mp_type_dict}};
575
576const mp_obj_type_t mp_type_ordereddict = {
577 { &mp_type_type },
578 .name = MP_QSTR_OrderedDict,
579 .print = dict_print,
580 .make_new = dict_make_new,
581 .unary_op = dict_unary_op,
582 .binary_op = dict_binary_op,
583 .subscr = dict_subscr,
584 .getiter = dict_getiter,
585 .bases_tuple = (mp_obj_t)&ordereddict_base_tuple,
586 .locals_dict = (mp_obj_t)&dict_locals_dict,
587};
588#endif
589
Damien George93965e72014-08-30 13:23:35 +0100590void mp_obj_dict_init(mp_obj_dict_t *dict, mp_uint_t n_args) {
Damien George8b0535e2014-04-05 21:53:54 +0100591 dict->base.type = &mp_type_dict;
592 mp_map_init(&dict->map, n_args);
593}
594
Damien George93965e72014-08-30 13:23:35 +0100595mp_obj_t mp_obj_new_dict(mp_uint_t n_args) {
Damiend99b0522013-12-21 18:17:45 +0000596 mp_obj_dict_t *o = m_new_obj(mp_obj_dict_t);
Damien George8b0535e2014-04-05 21:53:54 +0100597 mp_obj_dict_init(o, n_args);
Damiend99b0522013-12-21 18:17:45 +0000598 return o;
599}
Damiendae7eb72013-12-29 22:32:51 +0000600
Damien George93965e72014-08-30 13:23:35 +0100601mp_uint_t mp_obj_dict_len(mp_obj_t self_in) {
John R. Lentoncd088732014-01-06 18:53:16 +0000602 return ((mp_obj_dict_t *)self_in)->map.used;
Damiendae7eb72013-12-29 22:32:51 +0000603}
604
605mp_obj_t mp_obj_dict_store(mp_obj_t self_in, mp_obj_t key, mp_obj_t value) {
Paul Sokolovsky0ef01d02015-03-18 01:25:04 +0200606 assert(MP_OBJ_IS_DICT_TYPE(self_in));
Damiendae7eb72013-12-29 22:32:51 +0000607 mp_obj_dict_t *self = self_in;
Damien George38a2da62014-01-08 17:33:12 +0000608 mp_map_lookup(&self->map, key, MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)->value = value;
Damiendae7eb72013-12-29 22:32:51 +0000609 return self_in;
610}
Damien George062478e2014-01-09 20:57:50 +0000611
Damien George66edc5d2014-04-05 13:25:13 +0100612mp_obj_t mp_obj_dict_delete(mp_obj_t self_in, mp_obj_t key) {
Paul Sokolovsky0ef01d02015-03-18 01:25:04 +0200613 assert(MP_OBJ_IS_DICT_TYPE(self_in));
Damien George66edc5d2014-04-05 13:25:13 +0100614 mp_obj_dict_t *self = self_in;
Damien George8a9b9992014-09-17 15:53:03 +0100615 dict_get_helper(&self->map, key, MP_OBJ_NULL, MP_MAP_LOOKUP_REMOVE_IF_FOUND);
Damien George66edc5d2014-04-05 13:25:13 +0100616 return self_in;
617}
618
Damien George062478e2014-01-09 20:57:50 +0000619mp_map_t *mp_obj_dict_get_map(mp_obj_t self_in) {
Paul Sokolovsky0ef01d02015-03-18 01:25:04 +0200620 assert(MP_OBJ_IS_DICT_TYPE(self_in));
Damien George062478e2014-01-09 20:57:50 +0000621 mp_obj_dict_t *self = self_in;
622 return &self->map;
623}