blob: 8902e1020c5cb292c5892391f93ecd7c972ad66c [file] [log] [blame]
Damiend99b0522013-12-21 18:17:45 +00001#include <stdlib.h>
2#include <stdint.h>
3#include <string.h>
4#include <assert.h>
5
6#include "nlr.h"
7#include "misc.h"
8#include "mpconfig.h"
Damien Georgeeb7bfcb2014-01-04 15:57:35 +00009#include "mpqstr.h"
Damiend99b0522013-12-21 18:17:45 +000010#include "obj.h"
11#include "runtime0.h"
12#include "runtime.h"
13#include "map.h"
14
15typedef struct _mp_obj_dict_t {
16 mp_obj_base_t base;
17 mp_map_t map;
18} mp_obj_dict_t;
19
John R. Lentona41fe312014-01-06 17:17:02 +000020static mp_obj_t mp_obj_new_dict_iterator(mp_obj_dict_t *dict, int cur);
21static mp_map_elem_t *dict_it_iternext_elem(mp_obj_t self_in);
22
Damien George71c51812014-01-04 20:21:15 +000023static void dict_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in) {
Damiend99b0522013-12-21 18:17:45 +000024 mp_obj_dict_t *self = self_in;
25 bool first = true;
26 print(env, "{");
John R. Lentona41fe312014-01-06 17:17:02 +000027 mp_obj_t *dict_iter = mp_obj_new_dict_iterator(self, 0);
28 mp_map_elem_t *next = NULL;
29 while ((next = dict_it_iternext_elem(dict_iter)) != NULL) {
30 if (!first) {
31 print(env, ", ");
Damiend99b0522013-12-21 18:17:45 +000032 }
John R. Lentona41fe312014-01-06 17:17:02 +000033 first = false;
34 mp_obj_print_helper(print, env, next->key);
35 print(env, ": ");
36 mp_obj_print_helper(print, env, next->value);
Damiend99b0522013-12-21 18:17:45 +000037 }
38 print(env, "}");
39}
40
Damien George71c51812014-01-04 20:21:15 +000041// args are reverse in the array
42static mp_obj_t dict_make_new(mp_obj_t type_in, int n_args, const mp_obj_t *args) {
43 // TODO create from an iterable!
44 return rt_build_map(0);
45}
46
47static mp_obj_t dict_binary_op(int op, mp_obj_t lhs_in, mp_obj_t rhs_in) {
Damiend99b0522013-12-21 18:17:45 +000048 mp_obj_dict_t *o = lhs_in;
49 switch (op) {
50 case RT_BINARY_OP_SUBSCR:
51 {
52 // dict load
Damien George38a2da62014-01-08 17:33:12 +000053 mp_map_elem_t *elem = mp_map_lookup(&o->map, rhs_in, MP_MAP_LOOKUP);
Damiend99b0522013-12-21 18:17:45 +000054 if (elem == NULL) {
Damien Georgeeb7bfcb2014-01-04 15:57:35 +000055 nlr_jump(mp_obj_new_exception_msg(MP_QSTR_KeyError, "<value>"));
Damiend99b0522013-12-21 18:17:45 +000056 } else {
57 return elem->value;
58 }
59 }
60 default:
61 // op not supported
62 return NULL;
63 }
64}
65
John R. Lentona41fe312014-01-06 17:17:02 +000066
67/******************************************************************************/
68/* dict iterator */
69
70typedef struct _mp_obj_dict_it_t {
71 mp_obj_base_t base;
72 mp_obj_dict_t *dict;
73 machine_uint_t cur;
74} mp_obj_dict_it_t;
75
76static mp_map_elem_t *dict_it_iternext_elem(mp_obj_t self_in) {
77 mp_obj_dict_it_t *self = self_in;
78 machine_uint_t max = self->dict->map.alloc;
79 mp_map_elem_t *table = self->dict->map.table;
80
81 for (int i = self->cur; i < max; i++) {
82 if (table[i].key != NULL) {
83 self->cur = i + 1;
84 return &(table[i]);
85 }
86 }
87
88 return NULL;
89}
90
91mp_obj_t dict_it_iternext(mp_obj_t self_in) {
92 mp_map_elem_t *next = dict_it_iternext_elem(self_in);
93
94 if (next != NULL) {
95 return next->key;
96 } else {
97 return mp_const_stop_iteration;
98 }
99}
100
101static const mp_obj_type_t dict_it_type = {
102 { &mp_const_type },
103 "dict_iterator",
104 .iternext = dict_it_iternext,
John R. Lentona41fe312014-01-06 17:17:02 +0000105};
106
107static mp_obj_t mp_obj_new_dict_iterator(mp_obj_dict_t *dict, int cur) {
108 mp_obj_dict_it_t *o = m_new_obj(mp_obj_dict_it_t);
109 o->base.type = &dict_it_type;
110 o->dict = dict;
111 o->cur = cur;
112 return o;
113}
114
115static mp_obj_t dict_getiter(mp_obj_t o_in) {
116 return mp_obj_new_dict_iterator(o_in, 0);
117}
118
119/******************************************************************************/
120/* dict methods */
121
John R. Lenton4ce6cea2014-01-06 17:38:47 +0000122static mp_obj_t dict_clear(mp_obj_t self_in) {
123 assert(MP_OBJ_IS_TYPE(self_in, &dict_type));
124 mp_obj_dict_t *self = self_in;
125
126 mp_map_clear(&self->map);
127
128 return mp_const_none;
129}
John R. Lenton7d21d512014-01-06 17:54:04 +0000130static MP_DEFINE_CONST_FUN_OBJ_1(dict_clear_obj, dict_clear);
131
John R. Lentond90b19e2014-01-06 18:11:20 +0000132static mp_obj_t dict_copy(mp_obj_t self_in) {
133 assert(MP_OBJ_IS_TYPE(self_in, &dict_type));
134 mp_obj_dict_t *self = self_in;
135 mp_obj_dict_t *other = mp_obj_new_dict(self->map.alloc);
136 other->map.used = self->map.used;
137 memcpy(other->map.table, self->map.table, self->map.alloc * sizeof(mp_map_elem_t));
138 return other;
139}
140static MP_DEFINE_CONST_FUN_OBJ_1(dict_copy_obj, dict_copy);
John R. Lenton4ce6cea2014-01-06 17:38:47 +0000141
Damien George38a2da62014-01-08 17:33:12 +0000142static 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) {
143 mp_map_elem_t *elem = mp_map_lookup(self, key, lookup_kind);
John R. Lentonbe8fe5b2014-01-06 20:25:51 +0000144 mp_obj_t value;
145 if (elem == NULL || elem->value == NULL) {
John R. Lenton0fcbaa42014-01-06 19:48:34 +0000146 if (deflt == NULL) {
Damien George38a2da62014-01-08 17:33:12 +0000147 if (lookup_kind == MP_MAP_LOOKUP_REMOVE_IF_FOUND) {
John R. Lenton0fcbaa42014-01-06 19:48:34 +0000148 nlr_jump(mp_obj_new_exception_msg(MP_QSTR_KeyError, "<value>"));
149 } else {
John R. Lentonbe8fe5b2014-01-06 20:25:51 +0000150 value = mp_const_none;
John R. Lenton0fcbaa42014-01-06 19:48:34 +0000151 }
152 } else {
John R. Lentonbe8fe5b2014-01-06 20:25:51 +0000153 value = deflt;
John R. Lenton0fcbaa42014-01-06 19:48:34 +0000154 }
155 } else {
John R. Lentonbe8fe5b2014-01-06 20:25:51 +0000156 value = elem->value;
Damien George38a2da62014-01-08 17:33:12 +0000157 if (lookup_kind == MP_MAP_LOOKUP_REMOVE_IF_FOUND) {
158 // catch the leak (from mp_map_lookup)
John R. Lenton88f30432014-01-06 22:58:17 +0000159 m_free(elem, sizeof(mp_map_elem_t));
John R. Lenton0fcbaa42014-01-06 19:48:34 +0000160 }
John R. Lenton0fcbaa42014-01-06 19:48:34 +0000161 }
Damien George38a2da62014-01-08 17:33:12 +0000162 if (lookup_kind == MP_MAP_LOOKUP_ADD_IF_NOT_FOUND) {
John R. Lentonbe8fe5b2014-01-06 20:25:51 +0000163 elem->value = value;
164 }
165 return value;
John R. Lenton0fcbaa42014-01-06 19:48:34 +0000166}
167
John R. Lentoncd088732014-01-06 18:53:16 +0000168static mp_obj_t dict_get(int n_args, const mp_obj_t *args) {
169 assert(2 <= n_args && n_args <= 3);
170 assert(MP_OBJ_IS_TYPE(args[0], &dict_type));
171
John R. Lenton0fcbaa42014-01-06 19:48:34 +0000172 return dict_get_helper(&((mp_obj_dict_t *)args[0])->map,
173 args[1],
174 n_args == 3 ? args[2] : NULL,
Damien George38a2da62014-01-08 17:33:12 +0000175 MP_MAP_LOOKUP);
John R. Lentoncd088732014-01-06 18:53:16 +0000176}
177static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(dict_get_obj, 2, 3, dict_get);
178
John R. Lenton0fcbaa42014-01-06 19:48:34 +0000179static mp_obj_t dict_pop(int n_args, const mp_obj_t *args) {
180 assert(2 <= n_args && n_args <= 3);
181 assert(MP_OBJ_IS_TYPE(args[0], &dict_type));
182
183 return dict_get_helper(&((mp_obj_dict_t *)args[0])->map,
184 args[1],
185 n_args == 3 ? args[2] : NULL,
Damien George38a2da62014-01-08 17:33:12 +0000186 MP_MAP_LOOKUP_REMOVE_IF_FOUND);
John R. Lenton0fcbaa42014-01-06 19:48:34 +0000187}
188static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(dict_pop_obj, 2, 3, dict_pop);
189
190
John R. Lentonbe8fe5b2014-01-06 20:25:51 +0000191static mp_obj_t dict_setdefault(int n_args, const mp_obj_t *args) {
192 assert(2 <= n_args && n_args <= 3);
193 assert(MP_OBJ_IS_TYPE(args[0], &dict_type));
194
195 return dict_get_helper(&((mp_obj_dict_t *)args[0])->map,
196 args[1],
197 n_args == 3 ? args[2] : NULL,
Damien George38a2da62014-01-08 17:33:12 +0000198 MP_MAP_LOOKUP_ADD_IF_NOT_FOUND);
John R. Lentonbe8fe5b2014-01-06 20:25:51 +0000199}
200static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(dict_setdefault_obj, 2, 3, dict_setdefault);
201
John R. Lentonf77dce82014-01-06 20:08:52 +0000202
203static mp_obj_t dict_popitem(mp_obj_t self_in) {
204 assert(MP_OBJ_IS_TYPE(self_in, &dict_type));
205 mp_obj_dict_t *self = self_in;
206 if (self->map.used == 0) {
207 nlr_jump(mp_obj_new_exception_msg(MP_QSTR_KeyError, "popitem(): dictionary is empty"));
208 }
209 mp_obj_dict_it_t *iter = mp_obj_new_dict_iterator(self, 0);
210
211 mp_map_elem_t *next = dict_it_iternext_elem(iter);
212 self->map.used--;
213 mp_obj_t items[] = {next->key, next->value};
214 next->key = NULL;
John R. Lentonbe8fe5b2014-01-06 20:25:51 +0000215 next->value = NULL;
John R. Lentonf77dce82014-01-06 20:08:52 +0000216 mp_obj_t tuple = mp_obj_new_tuple(2, items);
217
218 return tuple;
219}
220static MP_DEFINE_CONST_FUN_OBJ_1(dict_popitem_obj, dict_popitem);
221
John R. Lenton88f30432014-01-06 22:58:17 +0000222static mp_obj_t dict_update(mp_obj_t self_in, mp_obj_t iterable) {
223 assert(MP_OBJ_IS_TYPE(self_in, &dict_type));
224 mp_obj_dict_t *self = self_in;
225 /* TODO: check for the "keys" method */
226 mp_obj_t iter = rt_getiter(iterable);
227 mp_obj_t next = NULL;
228 while ((next = rt_iternext(iter)) != mp_const_stop_iteration) {
229 mp_obj_t inneriter = rt_getiter(next);
230 mp_obj_t key = rt_iternext(inneriter);
231 mp_obj_t value = rt_iternext(inneriter);
232 mp_obj_t stop = rt_iternext(inneriter);
233 if (key == mp_const_stop_iteration
234 || value == mp_const_stop_iteration
235 || stop != mp_const_stop_iteration) {
236 nlr_jump(mp_obj_new_exception_msg(
237 MP_QSTR_ValueError,
238 "dictionary update sequence has the wrong length"));
239 } else {
Damien George38a2da62014-01-08 17:33:12 +0000240 mp_map_lookup(&self->map, key, MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)->value = value;
John R. Lenton88f30432014-01-06 22:58:17 +0000241 }
242 }
243
244 return mp_const_none;
245}
246static MP_DEFINE_CONST_FUN_OBJ_2(dict_update_obj, dict_update);
247
John R. Lentonf77dce82014-01-06 20:08:52 +0000248
John R. Lentona41fe312014-01-06 17:17:02 +0000249/******************************************************************************/
John R. Lenton9ec3a872014-01-10 01:00:20 +0000250/* dict views */
251
252static const mp_obj_type_t dict_view_type;
253static const mp_obj_type_t dict_view_it_type;
254
255typedef enum _mp_dict_view_kind_t {
256 MP_DICT_VIEW_ITEMS,
257 MP_DICT_VIEW_KEYS,
258 MP_DICT_VIEW_VALUES,
259} mp_dict_view_kind_t;
260
261static char *mp_dict_view_names[] = {"dict_items", "dict_keys", "dict_values"};
262
263typedef struct _mp_obj_dict_view_it_t {
264 mp_obj_base_t base;
265 mp_dict_view_kind_t kind;
266 mp_obj_dict_it_t *iter;
267 machine_uint_t cur;
268} mp_obj_dict_view_it_t;
269
270typedef struct _mp_obj_dict_view_t {
271 mp_obj_base_t base;
272 mp_obj_dict_t *dict;
273 mp_dict_view_kind_t kind;
274} mp_obj_dict_view_t;
275
276static mp_obj_t dict_view_it_iternext(mp_obj_t self_in) {
277 assert(MP_OBJ_IS_TYPE(self_in, &dict_view_it_type));
278 mp_obj_dict_view_it_t *self = self_in;
279 mp_map_elem_t *next = dict_it_iternext_elem(self->iter);
280
281 if (next != NULL) {
282 switch (self->kind) {
283 case MP_DICT_VIEW_ITEMS:
284 {
285 mp_obj_t items[] = {next->key, next->value};
286 return mp_obj_new_tuple(2, items);
287 }
288 case MP_DICT_VIEW_KEYS:
289 {
290 return next->key;
291 }
292 case MP_DICT_VIEW_VALUES:
293 {
294 return next->value;
295 }
296 default:
297 {
298 assert(0); /* can't happen */
299 }
300 }
301 } else {
302 return mp_const_stop_iteration;
303 }
304}
305
306static const mp_obj_type_t dict_view_it_type = {
307 { &mp_const_type },
308 "dict_view_iterator",
309 .iternext = dict_view_it_iternext,
310 .methods = NULL, /* set operations still to come */
311};
312
313static mp_obj_t dict_view_getiter(mp_obj_t view_in) {
314 assert(MP_OBJ_IS_TYPE(view_in, &dict_view_type));
315 mp_obj_dict_view_t *view = view_in;
316 mp_obj_dict_view_it_t *o = m_new_obj(mp_obj_dict_view_it_t);
317 o->base.type = &dict_view_it_type;
318 o->kind = view->kind;
319 o->iter = mp_obj_new_dict_iterator(view->dict, 0);
320 return o;
321}
322
323
324static void dict_view_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in) {
325 assert(MP_OBJ_IS_TYPE(self_in, &dict_view_type));
326 mp_obj_dict_view_t *self = self_in;
327 bool first = true;
328 print(env, mp_dict_view_names[self->kind]);
329 print(env, "([");
330 mp_obj_t *self_iter = dict_view_getiter(self);
331 mp_obj_t *next = NULL;
332 while ((next = dict_view_it_iternext(self_iter)) != mp_const_stop_iteration) {
333 if (!first) {
334 print(env, ", ");
335 }
336 first = false;
337 mp_obj_print_helper(print, env, next);
338 }
339 print(env, "])");
340}
341
342static const mp_obj_type_t dict_view_type = {
343 { &mp_const_type },
344 "dict_view",
345 .print = dict_view_print,
346 .getiter = dict_view_getiter,
347};
348
349mp_obj_t mp_obj_new_dict_view(mp_obj_dict_t *dict, mp_dict_view_kind_t kind) {
350 mp_obj_dict_view_t *o = m_new_obj(mp_obj_dict_view_t);
351 o->base.type = &dict_view_type;
352 o->dict = dict;
353 o->kind = kind;
354 return o;
355}
356
357
358static mp_obj_t dict_view(mp_obj_t self_in, mp_dict_view_kind_t kind) {
359 assert(MP_OBJ_IS_TYPE(self_in, &dict_type));
360 mp_obj_dict_t *self = self_in;
361 return mp_obj_new_dict_view(self, kind);
362}
363
364static mp_obj_t dict_items(mp_obj_t self_in) {
365 return dict_view(self_in, MP_DICT_VIEW_ITEMS);
366}
367static MP_DEFINE_CONST_FUN_OBJ_1(dict_items_obj, dict_items);
368
369static mp_obj_t dict_keys(mp_obj_t self_in) {
370 return dict_view(self_in, MP_DICT_VIEW_KEYS);
371}
372static MP_DEFINE_CONST_FUN_OBJ_1(dict_keys_obj, dict_keys);
373
374static mp_obj_t dict_values(mp_obj_t self_in) {
375 return dict_view(self_in, MP_DICT_VIEW_VALUES);
376}
377static MP_DEFINE_CONST_FUN_OBJ_1(dict_values_obj, dict_values);
378
John R. Lenton4bee76e2014-01-10 11:25:03 +0000379
380/******************************************************************************/
381/* dict metaclass */
382
383static mp_obj_t dict_fromkeys(int n_args, const mp_obj_t *args) {
384 assert(2 <= n_args && n_args <= 3);
385 mp_obj_t iter = rt_getiter(args[1]);
386 mp_obj_t len = mp_obj_len_maybe(iter);
387 mp_obj_t value = mp_const_none;
388 mp_obj_t next = NULL;
389 mp_obj_dict_t *self = NULL;
390
391 if (n_args > 2) {
392 value = args[2];
393 }
394
395 if (len == NULL) {
396 /* object's type doesn't have a __len__ slot */
397 self = mp_obj_new_dict(0);
398 } else {
399 self = mp_obj_new_dict(MP_OBJ_SMALL_INT_VALUE(len));
400 }
401
402 while ((next = rt_iternext(iter)) != mp_const_stop_iteration) {
403 mp_map_lookup(&self->map, next, MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)->value = value;
404 }
405
406 return self;
407}
408static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(dict_fromkeys_obj, 2, 3, dict_fromkeys);
409
410static const mp_method_t dict_class_methods[] = {
411 { "fromkeys", &dict_fromkeys_obj },
412 { NULL, NULL }, // end-of-list sentinel
413};
414
415/* this should be unnecessary when inheritance works */
416static void dict_class_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in) {
417 print(env, "<class 'dict'>");
418}
419
420/* this should be unnecessary when inheritance works */
421static mp_obj_t dict_class_call_n(mp_obj_t self_in, int n_args, const mp_obj_t *args) {
422 return rt_build_map(0);
423}
424
425static const mp_obj_type_t dict_class = {
426 { &mp_const_type },
427 "dict_class",
428 .print = dict_class_print,
429 .methods = dict_class_methods,
430 .call_n = dict_class_call_n,
431};
432
433
John R. Lenton9ec3a872014-01-10 01:00:20 +0000434/******************************************************************************/
John R. Lentona41fe312014-01-06 17:17:02 +0000435/* dict constructors & etc */
436
John R. Lentonbaa66542014-01-07 23:18:25 +0000437static const mp_method_t dict_type_methods[] = {
438 { "clear", &dict_clear_obj },
439 { "copy", &dict_copy_obj },
440 { "get", &dict_get_obj },
John R. Lenton9ec3a872014-01-10 01:00:20 +0000441 { "items", &dict_items_obj },
442 { "keys", &dict_keys_obj },
John R. Lentonbaa66542014-01-07 23:18:25 +0000443 { "pop", &dict_pop_obj },
444 { "popitem", &dict_popitem_obj },
445 { "setdefault", &dict_setdefault_obj },
446 { "update", &dict_update_obj },
John R. Lenton9ec3a872014-01-10 01:00:20 +0000447 { "values", &dict_values_obj },
John R. Lentonbaa66542014-01-07 23:18:25 +0000448 { NULL, NULL }, // end-of-list sentinel
449};
450
Damiend99b0522013-12-21 18:17:45 +0000451const mp_obj_type_t dict_type = {
John R. Lenton4bee76e2014-01-10 11:25:03 +0000452 { &dict_class },
Damiend99b0522013-12-21 18:17:45 +0000453 "dict",
Paul Sokolovsky860ffb02014-01-05 22:34:09 +0200454 .print = dict_print,
455 .make_new = dict_make_new,
456 .binary_op = dict_binary_op,
John R. Lentona41fe312014-01-06 17:17:02 +0000457 .getiter = dict_getiter,
John R. Lentonbaa66542014-01-07 23:18:25 +0000458 .methods = dict_type_methods,
Damiend99b0522013-12-21 18:17:45 +0000459};
460
461mp_obj_t mp_obj_new_dict(int n_args) {
462 mp_obj_dict_t *o = m_new_obj(mp_obj_dict_t);
463 o->base.type = &dict_type;
Damien George38a2da62014-01-08 17:33:12 +0000464 mp_map_init(&o->map, n_args);
Damiend99b0522013-12-21 18:17:45 +0000465 return o;
466}
Damiendae7eb72013-12-29 22:32:51 +0000467
468uint mp_obj_dict_len(mp_obj_t self_in) {
John R. Lentoncd088732014-01-06 18:53:16 +0000469 return ((mp_obj_dict_t *)self_in)->map.used;
Damiendae7eb72013-12-29 22:32:51 +0000470}
471
472mp_obj_t mp_obj_dict_store(mp_obj_t self_in, mp_obj_t key, mp_obj_t value) {
473 assert(MP_OBJ_IS_TYPE(self_in, &dict_type));
474 mp_obj_dict_t *self = self_in;
Damien George38a2da62014-01-08 17:33:12 +0000475 mp_map_lookup(&self->map, key, MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)->value = value;
Damiendae7eb72013-12-29 22:32:51 +0000476 return self_in;
477}
Damien George062478e2014-01-09 20:57:50 +0000478
479mp_map_t *mp_obj_dict_get_map(mp_obj_t self_in) {
480 assert(MP_OBJ_IS_TYPE(self_in, &dict_type));
481 mp_obj_dict_t *self = self_in;
482 return &self->map;
483}