runtime.c 33.1 KB
Newer Older
1
// in principle, rt_xxx functions are called only by vm/native/viper and make assumptions about args
2
// mp_xxx functions are safer and can be called by anyone
3
// note that rt_assign_xxx are called only from emit*, and maybe we can rename them to reflect this
4

Damien's avatar
Damien committed
5
6
7
8
9
10
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>

11
#include "nlr.h"
Damien's avatar
Damien committed
12
#include "misc.h"
13
14
15
#include "mpconfig.h"
#include "obj.h"
#include "runtime0.h"
Damien's avatar
Damien committed
16
#include "runtime.h"
17
18
19
#include "map.h"
#include "builtin.h"

20
#if 0 // print debugging info
21
#define DEBUG_PRINT (1)
22
#define WRITE_CODE (1)
23
24
25
#define DEBUG_printf(args...) printf(args)
#define DEBUG_OP_printf(args...) printf(args)
#else // don't print debugging info
26
#define DEBUG_printf(args...) (void)0
Damien's avatar
Damien committed
27
#define DEBUG_OP_printf(args...) (void)0
28
#endif
Damien's avatar
Damien committed
29

30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// TODO make these predefined so they don't take up RAM
qstr rt_q_append;
qstr rt_q_pop;
qstr rt_q_sort;
qstr rt_q_join;
qstr rt_q_format;
qstr rt_q___build_class__;
qstr rt_q___next__;
qstr rt_q_AttributeError;
qstr rt_q_IndexError;
qstr rt_q_KeyError;
qstr rt_q_NameError;
qstr rt_q_TypeError;
qstr rt_q_SyntaxError;
qstr rt_q_ValueError;
45

46
// locals and globals need to be pointers because they can be the same in outer module scope
47
48
49
static mp_map_t *map_locals;
static mp_map_t *map_globals;
static mp_map_t map_builtins;
50

Damien's avatar
Damien committed
51
typedef enum {
52
53
54
55
56
57
58
59
    MP_CODE_NONE,
    MP_CODE_BYTE,
    MP_CODE_NATIVE,
    MP_CODE_INLINE_ASM,
} mp_code_kind_t;

typedef struct _mp_code_t {
    mp_code_kind_t kind;
Damien's avatar
Damien committed
60
    int n_args;
61
62
63
    int n_locals;
    int n_stack;
    bool is_generator;
Damien's avatar
Damien committed
64
65
66
67
68
    union {
        struct {
            byte *code;
            uint len;
        } u_byte;
69
        struct {
70
            mp_fun_t fun;
71
72
        } u_native;
        struct {
73
            void *fun;
74
        } u_inline_asm;
Damien's avatar
Damien committed
75
    };
76
} mp_code_t;
Damien's avatar
Damien committed
77
78

static int next_unique_code_id;
79
static mp_code_t *unique_codes;
Damien's avatar
Damien committed
80

81
82
#ifdef WRITE_CODE
FILE *fp_write_code = NULL;
83
#endif
Damien's avatar
Damien committed
84

85
void rt_init(void) {
86
87
88
89
90
91
92
93
94
95
96
97
98
99
    rt_q_append = qstr_from_str_static("append");
    rt_q_pop = qstr_from_str_static("pop");
    rt_q_sort = qstr_from_str_static("sort");
    rt_q_join = qstr_from_str_static("join");
    rt_q_format = qstr_from_str_static("format");
    rt_q___build_class__ = qstr_from_str_static("__build_class__");
    rt_q___next__ = qstr_from_str_static("__next__");
    rt_q_AttributeError = qstr_from_str_static("AttributeError");
    rt_q_IndexError = qstr_from_str_static("IndexError");
    rt_q_KeyError = qstr_from_str_static("KeyError");
    rt_q_NameError = qstr_from_str_static("NameError");
    rt_q_TypeError = qstr_from_str_static("TypeError");
    rt_q_SyntaxError = qstr_from_str_static("SyntaxError");
    rt_q_ValueError = qstr_from_str_static("ValueError");
Damien's avatar
Damien committed
100

101
    // locals = globals for outer module (see Objects/frameobject.c/PyFrame_New())
102
103
104
    map_locals = map_globals = mp_map_new(MP_MAP_QSTR, 1);
    mp_qstr_map_lookup(map_globals, qstr_from_str_static("__name__"), true)->value = mp_obj_new_str(qstr_from_str_static("__main__"));

105
    // init built-in hash table
106
    mp_map_init(&map_builtins, MP_MAP_QSTR, 3);
107
108
109
110
111
112
113
114
115
116
117

    // built-in exceptions (TODO, make these proper classes)
    mp_qstr_map_lookup(&map_builtins, rt_q_AttributeError, true)->value = mp_obj_new_exception(rt_q_AttributeError);
    mp_qstr_map_lookup(&map_builtins, rt_q_IndexError, true)->value = mp_obj_new_exception(rt_q_IndexError);
    mp_qstr_map_lookup(&map_builtins, rt_q_KeyError, true)->value = mp_obj_new_exception(rt_q_KeyError);
    mp_qstr_map_lookup(&map_builtins, rt_q_NameError, true)->value = mp_obj_new_exception(rt_q_NameError);
    mp_qstr_map_lookup(&map_builtins, rt_q_TypeError, true)->value = mp_obj_new_exception(rt_q_TypeError);
    mp_qstr_map_lookup(&map_builtins, rt_q_SyntaxError, true)->value = mp_obj_new_exception(rt_q_SyntaxError);
    mp_qstr_map_lookup(&map_builtins, rt_q_ValueError, true)->value = mp_obj_new_exception(rt_q_ValueError);

    // built-in core functions
118
119
    mp_qstr_map_lookup(&map_builtins, rt_q___build_class__, true)->value = rt_make_function_2(mp_builtin___build_class__);
    mp_qstr_map_lookup(&map_builtins, qstr_from_str_static("__repl_print__"), true)->value = rt_make_function_1(mp_builtin___repl_print__);
120
121

    // built-in user functions
122
123
124
125
126
    mp_qstr_map_lookup(&map_builtins, qstr_from_str_static("abs"), true)->value = rt_make_function_1(mp_builtin_abs);
    mp_qstr_map_lookup(&map_builtins, qstr_from_str_static("all"), true)->value = rt_make_function_1(mp_builtin_all);
    mp_qstr_map_lookup(&map_builtins, qstr_from_str_static("any"), true)->value = rt_make_function_1(mp_builtin_any);
    mp_qstr_map_lookup(&map_builtins, qstr_from_str_static("bool"), true)->value = rt_make_function_var(0, mp_builtin_bool);
    mp_qstr_map_lookup(&map_builtins, qstr_from_str_static("callable"), true)->value = rt_make_function_1(mp_builtin_callable);
127
    mp_qstr_map_lookup(&map_builtins, qstr_from_str_static("chr"), true)->value = rt_make_function_1(mp_builtin_chr);
128
#if MICROPY_ENABLE_FLOAT
129
    mp_qstr_map_lookup(&map_builtins, qstr_from_str_static("complex"), true)->value = (mp_obj_t)&mp_builtin_complex_obj;
130
131
132
#endif
    mp_qstr_map_lookup(&map_builtins, qstr_from_str_static("dict"), true)->value = rt_make_function_0(mp_builtin_dict);
    mp_qstr_map_lookup(&map_builtins, qstr_from_str_static("divmod"), true)->value = rt_make_function_2(mp_builtin_divmod);
133
134
135
#if MICROPY_ENABLE_FLOAT
    mp_qstr_map_lookup(&map_builtins, qstr_from_str_static("float"), true)->value = (mp_obj_t)&mp_builtin_float_obj;
#endif
136
    mp_qstr_map_lookup(&map_builtins, qstr_from_str_static("hash"), true)->value = (mp_obj_t)&mp_builtin_hash_obj;
137
    mp_qstr_map_lookup(&map_builtins, qstr_from_str_static("int"), true)->value = (mp_obj_t)&mp_builtin_int_obj;
138
139
140
141
142
143
144
145
146
147
148
149
    mp_qstr_map_lookup(&map_builtins, qstr_from_str_static("iter"), true)->value = (mp_obj_t)&mp_builtin_iter_obj;
    mp_qstr_map_lookup(&map_builtins, qstr_from_str_static("len"), true)->value = rt_make_function_1(mp_builtin_len);
    mp_qstr_map_lookup(&map_builtins, qstr_from_str_static("list"), true)->value = rt_make_function_var(0, mp_builtin_list);
    mp_qstr_map_lookup(&map_builtins, qstr_from_str_static("max"), true)->value = rt_make_function_var(1, mp_builtin_max);
    mp_qstr_map_lookup(&map_builtins, qstr_from_str_static("min"), true)->value = rt_make_function_var(1, mp_builtin_min);
    mp_qstr_map_lookup(&map_builtins, qstr_from_str_static("next"), true)->value = (mp_obj_t)&mp_builtin_next_obj;
    mp_qstr_map_lookup(&map_builtins, qstr_from_str_static("ord"), true)->value = rt_make_function_1(mp_builtin_ord);
    mp_qstr_map_lookup(&map_builtins, qstr_from_str_static("pow"), true)->value = rt_make_function_var(2, mp_builtin_pow);
    mp_qstr_map_lookup(&map_builtins, qstr_from_str_static("print"), true)->value = rt_make_function_var(0, mp_builtin_print);
    mp_qstr_map_lookup(&map_builtins, qstr_from_str_static("range"), true)->value = rt_make_function_var(1, mp_builtin_range);
    mp_qstr_map_lookup(&map_builtins, qstr_from_str_static("set"), true)->value = (mp_obj_t)&mp_builtin_set_obj;
    mp_qstr_map_lookup(&map_builtins, qstr_from_str_static("sum"), true)->value = rt_make_function_var(1, mp_builtin_sum);
Damien's avatar
Damien committed
150

151

Damien's avatar
Damien committed
152
    next_unique_code_id = 2; // 1 is reserved for the __main__ module scope
Damien's avatar
Damien committed
153
154
    unique_codes = NULL;

155
156
#ifdef WRITE_CODE
    fp_write_code = fopen("out-code", "wb");
157
#endif
Damien's avatar
Damien committed
158
159
}

160
void rt_deinit(void) {
161
162
163
#ifdef WRITE_CODE
    if (fp_write_code != NULL) {
        fclose(fp_write_code);
Damien's avatar
Damien committed
164
    }
165
#endif
Damien's avatar
Damien committed
166
167
}

Damien's avatar
Damien committed
168
169
170
171
172
173
int rt_get_unique_code_id(bool is_main_module) {
    if (is_main_module) {
        return 1;
    } else {
        return next_unique_code_id++;
    }
Damien's avatar
Damien committed
174
175
}

176
static void alloc_unique_codes(void) {
Damien's avatar
Damien committed
177
    if (unique_codes == NULL) {
178
        unique_codes = m_new(mp_code_t, next_unique_code_id + 10); // XXX hack until we fix the REPL allocation problem
179
        for (int i = 0; i < next_unique_code_id; i++) {
180
            unique_codes[i].kind = MP_CODE_NONE;
181
        }
Damien's avatar
Damien committed
182
    }
183
184
}

Damien George's avatar
Damien George committed
185
void rt_assign_byte_code(int unique_code_id, byte *code, uint len, int n_args, int n_locals, int n_stack, bool is_generator) {
186
187
188
    alloc_unique_codes();

    assert(unique_code_id < next_unique_code_id);
189
    unique_codes[unique_code_id].kind = MP_CODE_BYTE;
190
    unique_codes[unique_code_id].n_args = n_args;
191
192
193
    unique_codes[unique_code_id].n_locals = n_locals;
    unique_codes[unique_code_id].n_stack = n_stack;
    unique_codes[unique_code_id].is_generator = is_generator;
194
195
196
    unique_codes[unique_code_id].u_byte.code = code;
    unique_codes[unique_code_id].u_byte.len = len;

Damien's avatar
Damien committed
197
    //printf("byte code: %d bytes\n", len);
198
199

#ifdef DEBUG_PRINT
200
    DEBUG_printf("assign byte code: id=%d code=%p len=%u n_args=%d\n", unique_code_id, code, len, n_args);
201
202
203
204
205
206
207
    for (int i = 0; i < 128 && i < len; i++) {
        if (i > 0 && i % 16 == 0) {
            DEBUG_printf("\n");
        }
        DEBUG_printf(" %02x", code[i]);
    }
    DEBUG_printf("\n");
208
209
    extern void mp_show_byte_code(const byte *code, int len);
    mp_show_byte_code(code, len);
210
211
212
213
214
215
216
217

#ifdef WRITE_CODE
    if (fp_write_code != NULL) {
        fwrite(code, len, 1, fp_write_code);
        fflush(fp_write_code);
    }
#endif
#endif
218
219
}

220
void rt_assign_native_code(int unique_code_id, void *fun, uint len, int n_args) {
221
222
    alloc_unique_codes();

223
    assert(1 <= unique_code_id && unique_code_id < next_unique_code_id);
224
    unique_codes[unique_code_id].kind = MP_CODE_NATIVE;
Damien's avatar
Damien committed
225
    unique_codes[unique_code_id].n_args = n_args;
226
227
228
    unique_codes[unique_code_id].n_locals = 0;
    unique_codes[unique_code_id].n_stack = 0;
    unique_codes[unique_code_id].is_generator = false;
Damien's avatar
Damien committed
229
230
    unique_codes[unique_code_id].u_native.fun = fun;

231
    //printf("native code: %d bytes\n", len);
232

233
#ifdef DEBUG_PRINT
Damien's avatar
Damien committed
234
235
236
237
238
239
240
241
242
243
    DEBUG_printf("assign native code: id=%d fun=%p len=%u n_args=%d\n", unique_code_id, fun, len, n_args);
    byte *fun_data = (byte*)(((machine_uint_t)fun) & (~1)); // need to clear lower bit in case it's thumb code
    for (int i = 0; i < 128 && i < len; i++) {
        if (i > 0 && i % 16 == 0) {
            DEBUG_printf("\n");
        }
        DEBUG_printf(" %02x", fun_data[i]);
    }
    DEBUG_printf("\n");

244
245
246
247
#ifdef WRITE_CODE
    if (fp_write_code != NULL) {
        fwrite(fun_data, len, 1, fp_write_code);
        fflush(fp_write_code);
Damien's avatar
Damien committed
248
    }
249
250
#endif
#endif
Damien's avatar
Damien committed
251
252
}

253
void rt_assign_inline_asm_code(int unique_code_id, void *fun, uint len, int n_args) {
254
255
256
    alloc_unique_codes();

    assert(1 <= unique_code_id && unique_code_id < next_unique_code_id);
257
    unique_codes[unique_code_id].kind = MP_CODE_INLINE_ASM;
Damien's avatar
Damien committed
258
    unique_codes[unique_code_id].n_args = n_args;
259
260
261
    unique_codes[unique_code_id].n_locals = 0;
    unique_codes[unique_code_id].n_stack = 0;
    unique_codes[unique_code_id].is_generator = false;
262
    unique_codes[unique_code_id].u_inline_asm.fun = fun;
Damien's avatar
Damien committed
263

264
#ifdef DEBUG_PRINT
265
266
267
268
269
270
271
272
273
274
    DEBUG_printf("assign inline asm code: id=%d fun=%p len=%u n_args=%d\n", unique_code_id, fun, len, n_args);
    byte *fun_data = (byte*)(((machine_uint_t)fun) & (~1)); // need to clear lower bit in case it's thumb code
    for (int i = 0; i < 128 && i < len; i++) {
        if (i > 0 && i % 16 == 0) {
            DEBUG_printf("\n");
        }
        DEBUG_printf(" %02x", fun_data[i]);
    }
    DEBUG_printf("\n");

275
276
277
#ifdef WRITE_CODE
    if (fp_write_code != NULL) {
        fwrite(fun_data, len, 1, fp_write_code);
278
    }
279
280
#endif
#endif
Damien's avatar
Damien committed
281
282
}

283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
mp_map_t *rt_get_map_locals(void) {
    return map_locals;
}

void rt_set_map_locals(mp_map_t *m) {
    map_locals = m;
}

static bool fit_small_int(mp_small_int_t o) {
    return true;
}

int rt_is_true(mp_obj_t arg) {
    DEBUG_OP_printf("is true %p\n", arg);
    if (MP_OBJ_IS_SMALL_INT(arg)) {
        if (MP_OBJ_SMALL_INT_VALUE(arg) == 0) {
            return 0;
        } else {
            return 1;
        }
    } else if (arg == mp_const_none) {
        return 0;
    } else if (arg == mp_const_false) {
        return 0;
    } else if (arg == mp_const_true) {
        return 1;
    } else {
        assert(0);
        return 0;
    }
}

mp_obj_t rt_list_append(mp_obj_t self_in, mp_obj_t arg) {
    return mp_obj_list_append(self_in, arg);
}

Damien's avatar
Damien committed
319
320
321
322
#define PARSE_DEC_IN_INTG (1)
#define PARSE_DEC_IN_FRAC (2)
#define PARSE_DEC_IN_EXP  (3)

323
mp_obj_t rt_load_const_dec(qstr qstr) {
Damien's avatar
Damien committed
324
325
326
327
#if MICROPY_ENABLE_FLOAT
    DEBUG_OP_printf("load '%s'\n", qstr_str(qstr));
    const char *s = qstr_str(qstr);
    int in = PARSE_DEC_IN_INTG;
328
    mp_float_t dec_val = 0;
Damien's avatar
Damien committed
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
    bool exp_neg = false;
    int exp_val = 0;
    int exp_extra = 0;
    bool imag = false;
    for (; *s; s++) {
        int dig = *s;
        if ('0' <= dig && dig <= '9') {
            dig -= '0';
            if (in == PARSE_DEC_IN_EXP) {
                exp_val = 10 * exp_val + dig;
            } else {
                dec_val = 10 * dec_val + dig;
                if (in == PARSE_DEC_IN_FRAC) {
                    exp_extra -= 1;
                }
            }
        } else if (in == PARSE_DEC_IN_INTG && dig == '.') {
            in = PARSE_DEC_IN_FRAC;
        } else if (in != PARSE_DEC_IN_EXP && (dig == 'E' || dig == 'e')) {
            in = PARSE_DEC_IN_EXP;
            if (s[1] == '+') {
                s++;
            } else if (s[1] == '-') {
                s++;
                exp_neg = true;
            }
        } else if (dig == 'J' || dig == 'j') {
            s++;
            imag = true;
            break;
        } else {
            // unknown character
            break;
        }
    }
    if (*s != 0) {
365
        nlr_jump(mp_obj_new_exception_msg(rt_q_SyntaxError, "invalid syntax for number"));
Damien's avatar
Damien committed
366
367
368
369
370
371
372
373
374
375
376
377
    }
    if (exp_neg) {
        exp_val = -exp_val;
    }
    exp_val += exp_extra;
    for (; exp_val > 0; exp_val--) {
        dec_val *= 10;
    }
    for (; exp_val < 0; exp_val++) {
        dec_val *= 0.1;
    }
    if (imag) {
378
        return mp_obj_new_complex(0, dec_val);
Damien's avatar
Damien committed
379
    } else {
380
        return mp_obj_new_float(dec_val);
Damien's avatar
Damien committed
381
382
    }
#else
383
    nlr_jump(mp_obj_new_exception_msg(rt_q_SyntaxError, "decimal numbers not supported"));
Damien's avatar
Damien committed
384
385
386
#endif
}

387
mp_obj_t rt_load_const_str(qstr qstr) {
Damien's avatar
Damien committed
388
    DEBUG_OP_printf("load '%s'\n", qstr_str(qstr));
389
    return mp_obj_new_str(qstr);
Damien's avatar
Damien committed
390
391
}

392
mp_obj_t rt_load_name(qstr qstr) {
Damien's avatar
Damien committed
393
    // logic: search locals, globals, builtins
394
    DEBUG_OP_printf("load name %s\n", qstr_str(qstr));
395
    mp_map_elem_t *elem = mp_qstr_map_lookup(map_locals, qstr, false);
Damien's avatar
Damien committed
396
    if (elem == NULL) {
397
        elem = mp_qstr_map_lookup(map_globals, qstr, false);
Damien's avatar
Damien committed
398
        if (elem == NULL) {
399
            elem = mp_qstr_map_lookup(&map_builtins, qstr, false);
400
            if (elem == NULL) {
401
                nlr_jump(mp_obj_new_exception_msg_1_arg(rt_q_NameError, "name '%s' is not defined", qstr_str(qstr)));
402
            }
Damien's avatar
Damien committed
403
404
405
406
407
        }
    }
    return elem->value;
}

408
mp_obj_t rt_load_global(qstr qstr) {
409
410
    // logic: search globals, builtins
    DEBUG_OP_printf("load global %s\n", qstr_str(qstr));
411
    mp_map_elem_t *elem = mp_qstr_map_lookup(map_globals, qstr, false);
412
    if (elem == NULL) {
413
        elem = mp_qstr_map_lookup(&map_builtins, qstr, false);
414
        if (elem == NULL) {
415
            nlr_jump(mp_obj_new_exception_msg_1_arg(rt_q_NameError, "name '%s' is not defined", qstr_str(qstr)));
416
417
418
        }
    }
    return elem->value;
Damien's avatar
Damien committed
419
420
}

421
mp_obj_t rt_load_build_class(void) {
Damien's avatar
Damien committed
422
    DEBUG_OP_printf("load_build_class\n");
423
    mp_map_elem_t *elem = mp_qstr_map_lookup(&map_builtins, rt_q___build_class__, false);
Damien's avatar
Damien committed
424
    if (elem == NULL) {
425
        nlr_jump(mp_obj_new_exception_msg(rt_q_NameError, "name '__build_class__' is not defined"));
Damien's avatar
Damien committed
426
427
428
429
    }
    return elem->value;
}

430
431
mp_obj_t rt_get_cell(mp_obj_t cell) {
    return mp_obj_cell_get(cell);
432
433
}

434
435
void rt_set_cell(mp_obj_t cell, mp_obj_t val) {
    mp_obj_cell_set(cell, val);
436
437
}

438
void rt_store_name(qstr qstr, mp_obj_t obj) {
439
    DEBUG_OP_printf("store name %s <- %p\n", qstr_str(qstr), obj);
440
    mp_qstr_map_lookup(map_locals, qstr, true)->value = obj;
441
442
}

443
void rt_store_global(qstr qstr, mp_obj_t obj) {
444
    DEBUG_OP_printf("store global %s <- %p\n", qstr_str(qstr), obj);
445
    mp_qstr_map_lookup(map_globals, qstr, true)->value = obj;
Damien's avatar
Damien committed
446
447
}

448
mp_obj_t rt_unary_op(int op, mp_obj_t arg) {
Damien's avatar
Damien committed
449
    DEBUG_OP_printf("unary %d %p\n", op, arg);
450
451
    if (MP_OBJ_IS_SMALL_INT(arg)) {
        mp_small_int_t val = MP_OBJ_SMALL_INT_VALUE(arg);
Damien's avatar
Damien committed
452
        switch (op) {
453
            case RT_UNARY_OP_NOT: if (val != 0) { return mp_const_true;} else { return mp_const_false; }
Damien's avatar
Damien committed
454
455
456
457
458
459
            case RT_UNARY_OP_POSITIVE: break;
            case RT_UNARY_OP_NEGATIVE: val = -val; break;
            case RT_UNARY_OP_INVERT: val = ~val; break;
            default: assert(0); val = 0;
        }
        if (fit_small_int(val)) {
460
461
462
463
464
465
466
467
468
469
470
471
472
            return MP_OBJ_NEW_SMALL_INT(val);
        } else {
            // TODO make a bignum
            assert(0);
            return mp_const_none;
        }
    } else { // will be an object (small ints are caught in previous if)
        mp_obj_base_t *o = arg;
        if (o->type->unary_op != NULL) {
            mp_obj_t result = o->type->unary_op(op, arg);
            if (result != NULL) {
                return result;
            }
Damien's avatar
Damien committed
473
        }
474
475
        // TODO specify in error message what the operator is
        nlr_jump(mp_obj_new_exception_msg_1_arg(rt_q_TypeError, "bad operand type for unary operator: '%s'", o->type->name));
Damien's avatar
Damien committed
476
    }
Damien's avatar
Damien committed
477
478
}

479
mp_obj_t rt_binary_op(int op, mp_obj_t lhs, mp_obj_t rhs) {
Damien's avatar
Damien committed
480
    DEBUG_OP_printf("binary %d %p %p\n", op, lhs, rhs);
481
482
483
    if (MP_OBJ_IS_SMALL_INT(lhs) && MP_OBJ_IS_SMALL_INT(rhs)) {
        mp_small_int_t lhs_val = MP_OBJ_SMALL_INT_VALUE(lhs);
        mp_small_int_t rhs_val = MP_OBJ_SMALL_INT_VALUE(rhs);
Damien's avatar
Damien committed
484
        switch (op) {
485
            case RT_BINARY_OP_OR:
Damien's avatar
Damien committed
486
            case RT_BINARY_OP_INPLACE_OR: lhs_val |= rhs_val; break;
487
            case RT_BINARY_OP_XOR:
Damien's avatar
Damien committed
488
            case RT_BINARY_OP_INPLACE_XOR: lhs_val ^= rhs_val; break;
489
            case RT_BINARY_OP_AND:
Damien's avatar
Damien committed
490
            case RT_BINARY_OP_INPLACE_AND: lhs_val &= rhs_val; break;
491
            case RT_BINARY_OP_LSHIFT:
Damien's avatar
Damien committed
492
            case RT_BINARY_OP_INPLACE_LSHIFT: lhs_val <<= rhs_val; break;
493
            case RT_BINARY_OP_RSHIFT:
Damien's avatar
Damien committed
494
            case RT_BINARY_OP_INPLACE_RSHIFT: lhs_val >>= rhs_val; break;
Damien's avatar
Damien committed
495
            case RT_BINARY_OP_ADD:
Damien's avatar
Damien committed
496
            case RT_BINARY_OP_INPLACE_ADD: lhs_val += rhs_val; break;
497
            case RT_BINARY_OP_SUBTRACT:
Damien's avatar
Damien committed
498
            case RT_BINARY_OP_INPLACE_SUBTRACT: lhs_val -= rhs_val; break;
499
            case RT_BINARY_OP_MULTIPLY:
Damien's avatar
Damien committed
500
            case RT_BINARY_OP_INPLACE_MULTIPLY: lhs_val *= rhs_val; break;
501
            case RT_BINARY_OP_FLOOR_DIVIDE:
Damien's avatar
Damien committed
502
            case RT_BINARY_OP_INPLACE_FLOOR_DIVIDE: lhs_val /= rhs_val; break;
503
#if MICROPY_ENABLE_FLOAT
504
            case RT_BINARY_OP_TRUE_DIVIDE:
505
            case RT_BINARY_OP_INPLACE_TRUE_DIVIDE: return mp_obj_new_float((mp_float_t)lhs_val / (mp_float_t)rhs_val);
Damien's avatar
Damien committed
506
#endif
507
508
509
510
511
512

            // TODO implement modulo as specified by Python
            case RT_BINARY_OP_MODULO:
            case RT_BINARY_OP_INPLACE_MODULO: lhs_val %= rhs_val; break;

            // TODO check for negative power, and overflow
513
514
            case RT_BINARY_OP_POWER:
            case RT_BINARY_OP_INPLACE_POWER:
515
516
517
518
519
520
521
522
            {
                int ans = 1;
                while (rhs_val > 0) {
                    if (rhs_val & 1) {
                        ans *= lhs_val;
                    }
                    lhs_val *= lhs_val;
                    rhs_val /= 2;
523
                }
524
525
526
527
                lhs_val = ans;
                break;
            }

528
            default: assert(0);
Damien's avatar
Damien committed
529
        }
Damien's avatar
Damien committed
530
        if (fit_small_int(lhs_val)) {
531
532
533
534
535
536
537
538
            return MP_OBJ_NEW_SMALL_INT(lhs_val);
        }
    } else if (MP_OBJ_IS_OBJ(lhs)) {
        mp_obj_base_t *o = lhs;
        if (o->type->binary_op != NULL) {
            mp_obj_t result = o->type->binary_op(op, lhs, rhs);
            if (result != NULL) {
                return result;
Damien's avatar
Damien committed
539
            }
Damien's avatar
Damien committed
540
541
        }
    }
542
543
544

    // TODO specify in error message what the operator is
    nlr_jump(mp_obj_new_exception_msg_1_arg(rt_q_TypeError, "unsupported operand type for binary operator: '%s'", mp_obj_get_type_str(lhs)));
Damien's avatar
Damien committed
545
546
}

547
mp_obj_t rt_compare_op(int op, mp_obj_t lhs, mp_obj_t rhs) {
Damien's avatar
Damien committed
548
    DEBUG_OP_printf("compare %d %p %p\n", op, lhs, rhs);
549
550
551

    // deal with == and !=
    if (op == RT_COMPARE_OP_EQUAL || op == RT_COMPARE_OP_NOT_EQUAL) {
552
        if (mp_obj_equal(lhs, rhs)) {
553
            if (op == RT_COMPARE_OP_EQUAL) {
554
                return mp_const_true;
555
            } else {
556
                return mp_const_false;
557
558
559
            }
        } else {
            if (op == RT_COMPARE_OP_EQUAL) {
560
                return mp_const_false;
561
            } else {
562
                return mp_const_true;
563
564
565
566
            }
        }
    }

567
568
569
570
571
572
573
574
575
576
577
578
    // deal with exception_match
    if (op == RT_COMPARE_OP_EXCEPTION_MATCH) {
        // TODO properly! at the moment it just compares the exception identifier for equality
        if (MP_OBJ_IS_TYPE(lhs, &exception_type) && MP_OBJ_IS_TYPE(rhs, &exception_type)) {
            if (mp_obj_exception_get_type(lhs) == mp_obj_exception_get_type(rhs)) {
                return mp_const_true;
            } else {
                return mp_const_false;
            }
        }
    }

579
    // deal with small ints
580
581
582
    if (MP_OBJ_IS_SMALL_INT(lhs) && MP_OBJ_IS_SMALL_INT(rhs)) {
        mp_small_int_t lhs_val = MP_OBJ_SMALL_INT_VALUE(lhs);
        mp_small_int_t rhs_val = MP_OBJ_SMALL_INT_VALUE(rhs);
583
584
585
586
587
588
589
590
591
        int cmp;
        switch (op) {
            case RT_COMPARE_OP_LESS: cmp = lhs_val < rhs_val; break;
            case RT_COMPARE_OP_MORE: cmp = lhs_val > rhs_val; break;
            case RT_COMPARE_OP_LESS_EQUAL: cmp = lhs_val <= rhs_val; break;
            case RT_COMPARE_OP_MORE_EQUAL: cmp = lhs_val >= rhs_val; break;
            default: assert(0); cmp = 0;
        }
        if (cmp) {
592
            return mp_const_true;
593
        } else {
594
            return mp_const_false;
595
596
597
598
599
        }
    }

#if MICROPY_ENABLE_FLOAT
    // deal with floats
600
601
602
    if (MP_OBJ_IS_TYPE(lhs, &float_type) || MP_OBJ_IS_TYPE(rhs, &float_type)) {
        mp_float_t lhs_val = mp_obj_get_float(lhs);
        mp_float_t rhs_val = mp_obj_get_float(rhs);
Damien's avatar
Damien committed
603
604
        int cmp;
        switch (op) {
605
606
607
608
            case RT_COMPARE_OP_LESS: cmp = lhs_val < rhs_val; break;
            case RT_COMPARE_OP_MORE: cmp = lhs_val > rhs_val; break;
            case RT_COMPARE_OP_LESS_EQUAL: cmp = lhs_val <= rhs_val; break;
            case RT_COMPARE_OP_MORE_EQUAL: cmp = lhs_val >= rhs_val; break;
Damien's avatar
Damien committed
609
610
611
            default: assert(0); cmp = 0;
        }
        if (cmp) {
612
            return mp_const_true;
Damien's avatar
Damien committed
613
        } else {
614
            return mp_const_false;
Damien's avatar
Damien committed
615
616
        }
    }
617
#endif
618
619

    // not implemented
Damien's avatar
Damien committed
620
    assert(0);
621
    return mp_const_none;
Damien's avatar
Damien committed
622
623
}

624
mp_obj_t rt_make_function_from_id(int unique_code_id) {
625
626
    DEBUG_OP_printf("make_function_from_id %d\n", unique_code_id);
    if (unique_code_id < 1 || unique_code_id >= next_unique_code_id) {
Damien's avatar
Damien committed
627
        // illegal code id
628
        return mp_const_none;
Damien's avatar
Damien committed
629
    }
630
631
632
633

    // make the function, depending on the code kind
    mp_code_t *c = &unique_codes[unique_code_id];
    mp_obj_t fun;
Damien's avatar
Damien committed
634
    switch (c->kind) {
635
        case MP_CODE_BYTE:
Damien George's avatar
Damien George committed
636
            fun = mp_obj_new_fun_bc(c->n_args, c->n_locals + c->n_stack, c->u_byte.code);
637
            break;
638
        case MP_CODE_NATIVE:
Damien's avatar
Damien committed
639
            switch (c->n_args) {
640
641
642
643
                case 0: fun = rt_make_function_0(c->u_native.fun); break;
                case 1: fun = rt_make_function_1((mp_fun_1_t)c->u_native.fun); break;
                case 2: fun = rt_make_function_2((mp_fun_2_t)c->u_native.fun); break;
                default: assert(0); fun = mp_const_none;
Damien's avatar
Damien committed
644
645
            }
            break;
646
647
        case MP_CODE_INLINE_ASM:
            fun = mp_obj_new_fun_asm(c->n_args, c->u_inline_asm.fun);
Damien's avatar
Damien committed
648
649
650
            break;
        default:
            assert(0);
651
            fun = mp_const_none;
Damien's avatar
Damien committed
652
    }
653
654
655

    // check for generator functions and if so wrap in generator object
    if (c->is_generator) {
Damien George's avatar
Damien George committed
656
        fun = mp_obj_new_gen_wrap(c->n_locals, c->n_stack, fun);
657
658
    }

659
    return fun;
Damien's avatar
Damien committed
660
661
}

662
mp_obj_t rt_make_closure_from_id(int unique_code_id, mp_obj_t closure_tuple) {
Damien George's avatar
Damien George committed
663
    DEBUG_OP_printf("make_closure_from_id %d\n", unique_code_id);
664
665
    // make function object
    mp_obj_t ffun = rt_make_function_from_id(unique_code_id);
Damien's avatar
Damien committed
666
    // wrap function in closure object
667
    return mp_obj_new_closure(ffun, closure_tuple);
Damien's avatar
Damien committed
668
669
}

670
mp_obj_t rt_call_function_0(mp_obj_t fun) {
671
672
673
    return rt_call_function_n(fun, 0, NULL);
}

674
mp_obj_t rt_call_function_1(mp_obj_t fun, mp_obj_t arg) {
675
676
677
    return rt_call_function_n(fun, 1, &arg);
}

678
679
mp_obj_t rt_call_function_2(mp_obj_t fun, mp_obj_t arg1, mp_obj_t arg2) {
    mp_obj_t args[2];
680
681
682
683
684
685
    args[1] = arg1;
    args[0] = arg2;
    return rt_call_function_n(fun, 2, args);
}

// args are in reverse order in the array
686
687
688
mp_obj_t rt_call_function_n(mp_obj_t fun_in, int n_args, const mp_obj_t *args) {
    // TODO improve this: fun object can specify its type and we parse here the arguments,
    // passing to the function arrays of fixed and keyword arguments
689

690
    DEBUG_OP_printf("calling function %p(n_args=%d, args=%p)\n", fun_in, n_args, args);
691

692
693
694
695
696
697
    if (MP_OBJ_IS_SMALL_INT(fun_in)) {
        nlr_jump(mp_obj_new_exception_msg(rt_q_TypeError, "'int' object is not callable"));
    } else {
        mp_obj_base_t *fun = fun_in;
        if (fun->type->call_n != NULL) {
            return fun->type->call_n(fun_in, n_args, args);
698
        } else {
699
            nlr_jump(mp_obj_new_exception_msg_1_arg(rt_q_TypeError, "'%s' object is not callable", fun->type->name));
700
        }
Damien's avatar
Damien committed
701
702
703
    }
}

704
705
// args are in reverse order in the array; keyword arguments come first, value then key
// eg: (value1, key1, value0, key0, arg1, arg0)
706
mp_obj_t rt_call_function_n_kw(mp_obj_t fun, uint n_args, uint n_kw, const mp_obj_t *args) {
707
708
    // TODO
    assert(0);
709
    return mp_const_none;
710
711
}

712
713
// args contains: arg(n_args-1)  arg(n_args-2)  ...  arg(0)  self/NULL  fun
// if n_args==0 then there are only self/NULL and fun
714
mp_obj_t rt_call_method_n(uint n_args, const mp_obj_t *args) {
715
    DEBUG_OP_printf("call method %p(self=%p, n_args=%u)\n", args[n_args + 1], args[n_args], n_args);
716
717
718
    return rt_call_function_n(args[n_args + 1], n_args + ((args[n_args] == NULL) ? 0 : 1), args);
}

719
// args contains: kw_val(n_kw-1)  kw_key(n_kw-1) ... kw_val(0)  kw_key(0)  arg(n_args-1)  arg(n_args-2)  ...  arg(0)  self/NULL  fun
720
mp_obj_t rt_call_method_n_kw(uint n_args, uint n_kw, const mp_obj_t *args) {
721
722
723
724
725
    uint n = n_args + 2 * n_kw;
    DEBUG_OP_printf("call method %p(self=%p, n_args=%u, n_kw=%u)\n", args[n + 1], args[n], n_args, n_kw);
    return rt_call_function_n_kw(args[n + 1], n_args + ((args[n] == NULL) ? 0 : 1), n_kw, args);
}

726
// items are in reverse order
727
728
mp_obj_t rt_build_tuple(int n_args, mp_obj_t *items) {
    return mp_obj_new_tuple_reverse(n_args, items);
729
730
}

Damien's avatar
Damien committed
731
// items are in reverse order
732
733
mp_obj_t rt_build_list(int n_args, mp_obj_t *items) {
    return mp_obj_new_list_reverse(n_args, items);
Damien's avatar
Damien committed
734
735
}

736
737
mp_obj_t rt_build_set(int n_args, mp_obj_t *items) {
    return mp_obj_new_set(n_args, items);
Damien's avatar
Damien committed
738
739
}

740
mp_obj_t rt_store_set(mp_obj_t set, mp_obj_t item) {
741
    mp_obj_set_store(set, item);
Damien's avatar
Damien committed
742
743
744
    return set;
}

745
// unpacked items are stored in order into the array pointed to by items
746
747
748
749
750
751
752
753
void rt_unpack_sequence(mp_obj_t seq_in, uint num, mp_obj_t *items) {
    if (MP_OBJ_IS_TYPE(seq_in, &tuple_type) || MP_OBJ_IS_TYPE(seq_in, &list_type)) {
        uint seq_len;
        mp_obj_t *seq_items;
        if (MP_OBJ_IS_TYPE(seq_in, &tuple_type)) {
            mp_obj_tuple_get(seq_in, &seq_len, &seq_items);
        } else {
            mp_obj_list_get(seq_in, &seq_len, &seq_items);
754
        }
755
756
757
758
759
760
        if (seq_len < num) {
            nlr_jump(mp_obj_new_exception_msg_1_arg(rt_q_ValueError, "need more than %d values to unpack", (void*)(machine_uint_t)seq_len));
        } else if (seq_len > num) {
            nlr_jump(mp_obj_new_exception_msg_1_arg(rt_q_ValueError, "too many values to unpack (expected %d)", (void*)(machine_uint_t)num));
        }
        memcpy(items, seq_items, num * sizeof(mp_obj_t));
761
762
    } else {
        // TODO call rt_getiter and extract via rt_iternext
763
        nlr_jump(mp_obj_new_exception_msg_1_arg(rt_q_TypeError, "'%s' object is not iterable", mp_obj_get_type_str(seq_in)));
764
765
766
    }
}

767
768
mp_obj_t rt_build_map(int n_args) {
    return mp_obj_new_dict(n_args);
Damien's avatar
Damien committed
769
770
}

771
772
773
mp_obj_t rt_store_map(mp_obj_t map, mp_obj_t key, mp_obj_t value) {
    // map should always be a dict
    return mp_obj_dict_store(map, key, value);
Damien's avatar
Damien committed
774
775
}

776
mp_obj_t rt_load_attr(mp_obj_t base, qstr attr) {
777
    DEBUG_OP_printf("load attr %s\n", qstr_str(attr));
778
779
    if (MP_OBJ_IS_TYPE(base, &class_type)) {
        mp_map_elem_t *elem = mp_qstr_map_lookup(mp_obj_class_get_locals(base), attr, false);
Damien's avatar
Damien committed
780
        if (elem == NULL) {
781
            nlr_jump(mp_obj_new_exception_msg_2_args(rt_q_AttributeError, "'%s' object has no attribute '%s'", mp_obj_get_type_str(base), qstr_str(attr)));
Damien's avatar
Damien committed
782
783
        }
        return elem->value;
784
785
786
787
788
789
790
791
792
    } else if (MP_OBJ_IS_TYPE(base, &instance_type)) {
        return mp_obj_instance_load_attr(base, attr);
    } else if (MP_OBJ_IS_OBJ(base)) {
        // generic method lookup
        mp_obj_base_t *o = base;
        const mp_method_t *meth = &o->type->methods[0];
        for (; meth->name != NULL; meth++) {
            if (strcmp(meth->name, qstr_str(attr)) == 0) {
                return mp_obj_new_bound_meth(base, (mp_obj_t)meth->fun);
793
794
            }
        }
Damien's avatar
Damien committed
795
    }
796
    nlr_jump(mp_obj_new_exception_msg_2_args(rt_q_AttributeError, "'%s' object has no attribute '%s'", mp_obj_get_type_str(base), qstr_str(attr)));
Damien's avatar
Damien committed
797
798
}

799
void rt_load_method(mp_obj_t base, qstr attr, mp_obj_t *dest) {
Damien's avatar
Damien committed
800
    DEBUG_OP_printf("load method %s\n", qstr_str(attr));
801
    if (MP_OBJ_IS_TYPE(base, &gen_instance_type) && attr == rt_q___next__) {
802
        dest[1] = (mp_obj_t)&mp_builtin_next_obj;
803
804
        dest[0] = base;
        return;
805
806
    } else if (MP_OBJ_IS_TYPE(base, &instance_type)) {
        mp_obj_instance_load_method(base, attr, dest);
807
        return;
808
809
810
811
    } else if (MP_OBJ_IS_OBJ(base)) {
        // generic method lookup
        mp_obj_base_t *o = base;
        const mp_method_t *meth = &o->type->methods[0];
Damien's avatar
Damien committed
812
813
        for (; meth->name != NULL; meth++) {
            if (strcmp(meth->name, qstr_str(attr)) == 0) {
814
                dest[1] = (mp_obj_t)meth->fun;
Damien's avatar
Damien committed
815
816
817
818
                dest[0] = base;
                return;
            }
        }
819
820
    }

821
    // no method; fallback to load_attr
822
823
824
825
    dest[1] = rt_load_attr(base, attr);
    dest[0] = NULL;
}

826
void rt_store_attr(mp_obj_t base, qstr attr, mp_obj_t value) {
Damien's avatar
Damien committed
827
    DEBUG_OP_printf("store attr %p.%s <- %p\n", base, qstr_str(attr), value);
828
    if (MP_OBJ_IS_TYPE(base, &class_type)) {
Damien's avatar
Damien committed
829
        // TODO CPython allows STORE_ATTR to a class, but is this the correct implementation?
830
831
832
833
        mp_map_t *locals = mp_obj_class_get_locals(base);
        mp_qstr_map_lookup(locals, attr, true)->value = value;
    } else if (MP_OBJ_IS_TYPE(base, &instance_type)) {
        mp_obj_instance_store_attr(base, attr, value);
834
    } else {
835
        nlr_jump(mp_obj_new_exception_msg_2_args(rt_q_AttributeError, "'%s' object has no attribute '%s'", mp_obj_get_type_str(base), qstr_str(attr)));
836
837
838
    }
}

839
void rt_store_subscr(mp_obj_t base, mp_obj_t index, mp_obj_t value) {
Damien's avatar
Damien committed
840
    DEBUG_OP_printf("store subscr %p[%p] <- %p\n", base, index, value);
841
    if (MP_OBJ_IS_TYPE(base, &list_type)) {
842
        // list store
843
844
845
846
        mp_obj_list_store(base, index, value);
    } else if (MP_OBJ_IS_TYPE(base, &dict_type)) {
        // dict store
        mp_obj_dict_store(base, index, value);
Damien's avatar
Damien committed
847
    } else {
848
        assert(0);
Damien's avatar
Damien committed
849
850
851
    }
}

852
853
854
mp_obj_t rt_getiter(mp_obj_t o_in) {
    if (MP_OBJ_IS_SMALL_INT(o_in)) {
        nlr_jump(mp_obj_new_exception_msg(rt_q_TypeError, "'int' object is not iterable"));
855
    } else {
856
857
858
        mp_obj_base_t *o = o_in;
        if (o->type->getiter != NULL) {
            return o->type->getiter(o_in);
859
        } else {
860
            nlr_jump(mp_obj_new_exception_msg_1_arg(rt_q_TypeError, "'%s' object is not iterable", o->type->name));
861
        }
862
863
    }
}
864

865
866
867
868
869
870
871
mp_obj_t rt_iternext(mp_obj_t o_in) {
    if (MP_OBJ_IS_SMALL_INT(o_in)) {
        nlr_jump(mp_obj_new_exception_msg(rt_q_TypeError, "? 'int' object is not iterable"));
    } else {
        mp_obj_base_t *o = o_in;
        if (o->type->iternext != NULL) {
            return o->type->iternext(o_in);
872
        } else {
873
            nlr_jump(mp_obj_new_exception_msg_1_arg(rt_q_TypeError, "? '%s' object is not iterable", o->type->name));
874
        }
875
876
877
    }
}

878
mp_obj_t rt_import_name(qstr name, mp_obj_t fromlist, mp_obj_t level) {
879
    // build args array
880
881
882
883
    mp_obj_t args[5];
    args[0] = mp_obj_new_str(name);
    args[1] = mp_const_none; // TODO should be globals
    args[2] = mp_const_none; // TODO should be locals
884
885
886
887
    args[3] = fromlist;
    args[4] = level; // must be 0; we don't yet support other values

    // TODO lookup __import__ and call that instead of going straight to builtin implementation
888
    return mp_builtin___import__(5, args);
889
890
}

891
892
mp_obj_t rt_import_from(mp_obj_t module, qstr name) {
    mp_obj_t x = rt_load_attr(module, name);
893
894
895
896
897
898
899
900
    /* TODO convert AttributeError to ImportError
    if (fail) {
        (ImportError, "cannot import name %s", qstr_str(name), NULL)
    }
    */
    return x;
}

901
// these must correspond to the respective enum
902
void *const rt_fun_table[RT_F_NUMBER_OF] = {
903
    rt_load_const_dec,
Damien's avatar
Damien committed
904
905
906
    rt_load_const_str,
    rt_load_name,
    rt_load_global,
907
    rt_load_build_class,
Damien's avatar
Damien committed
908
909
910
    rt_load_attr,
    rt_load_method,
    rt_store_name,
911
    rt_store_attr,
Damien's avatar
Damien committed
912
913
914
    rt_store_subscr,
    rt_is_true,
    rt_unary_op,
915
    rt_build_tuple,
Damien's avatar
Damien committed
916
    rt_build_list,
917
    rt_list_append,
Damien's avatar
Damien committed
918
919
920
    rt_build_map,
    rt_store_map,
    rt_build_set,
921
    rt_store_set,
Damien's avatar
Damien committed
922
    rt_make_function_from_id,
923
    rt_call_function_n,
924
    rt_call_method_n,
Damien's avatar
Damien committed
925
926
    rt_binary_op,
    rt_compare_op,
927
928
    rt_getiter,
    rt_iternext,
Damien's avatar
Damien committed
929
930
931
932
933
934
935
};

/*
void rt_f_vector(rt_fun_kind_t fun_kind) {
    (rt_f_table[fun_kind])();
}
*/