runtime.c 32.9 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
    int n_locals;
Damien's avatar
Damien committed
62
    int n_cells;
63
64
    int n_stack;
    bool is_generator;
Damien's avatar
Damien committed
65
66
67
68
69
    union {
        struct {
            byte *code;
            uint len;
        } u_byte;
70
        struct {
71
            mp_fun_t fun;
72
73
        } u_native;
        struct {
74
            void *fun;
75
        } u_inline_asm;
Damien's avatar
Damien committed
76
    };
77
} mp_code_t;
Damien's avatar
Damien committed
78
79

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

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

86
void rt_init(void) {
87
88
89
90
91
92
93
94
95
96
97
98
99
100
    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
101

102
    // locals = globals for outer module (see Objects/frameobject.c/PyFrame_New())
103
104
105
    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__"));

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

    // 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
119
120
    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__);
121
122

    // built-in user functions
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
    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);
#if MICROPY_ENABLE_FLOAT
    mp_qstr_map_lookup(&map_builtins, qstr_from_str_static("complex"), true)->value = rt_make_function_var(0, mp_builtin_complex);
#endif
    mp_qstr_map_lookup(&map_builtins, qstr_from_str_static("chr"), true)->value = rt_make_function_1(mp_builtin_chr);
    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);
    mp_qstr_map_lookup(&map_builtins, qstr_from_str_static("hash"), true)->value = (mp_obj_t)&mp_builtin_hash_obj;
    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
147

Damien's avatar
Damien committed
148
    next_unique_code_id = 2; // 1 is reserved for the __main__ module scope
Damien's avatar
Damien committed
149
150
    unique_codes = NULL;

151
152
#ifdef WRITE_CODE
    fp_write_code = fopen("out-code", "wb");
153
#endif
Damien's avatar
Damien committed
154
155
}

156
void rt_deinit(void) {
157
158
159
#ifdef WRITE_CODE
    if (fp_write_code != NULL) {
        fclose(fp_write_code);
Damien's avatar
Damien committed
160
    }
161
#endif
Damien's avatar
Damien committed
162
163
}

Damien's avatar
Damien committed
164
165
166
167
168
169
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
170
171
}

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

Damien's avatar
Damien committed
181
void rt_assign_byte_code(int unique_code_id, byte *code, uint len, int n_args, int n_locals, int n_cells, int n_stack, bool is_generator) {
182
183
184
    alloc_unique_codes();

    assert(unique_code_id < next_unique_code_id);
185
    unique_codes[unique_code_id].kind = MP_CODE_BYTE;
186
    unique_codes[unique_code_id].n_args = n_args;
187
    unique_codes[unique_code_id].n_locals = n_locals;
Damien's avatar
Damien committed
188
    unique_codes[unique_code_id].n_cells = n_cells;
189
190
    unique_codes[unique_code_id].n_stack = n_stack;
    unique_codes[unique_code_id].is_generator = is_generator;
191
192
193
    unique_codes[unique_code_id].u_byte.code = code;
    unique_codes[unique_code_id].u_byte.len = len;

Damien's avatar
Damien committed
194
    //printf("byte code: %d bytes\n", len);
195
196

#ifdef DEBUG_PRINT
197
    DEBUG_printf("assign byte code: id=%d code=%p len=%u n_args=%d\n", unique_code_id, code, len, n_args);
198
199
200
201
202
203
204
    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");
205
206
    extern void mp_show_byte_code(const byte *code, int len);
    mp_show_byte_code(code, len);
207
208
209
210
211
212
213
214

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

217
void rt_assign_native_code(int unique_code_id, void *fun, uint len, int n_args) {
218
219
    alloc_unique_codes();

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

229
230
    printf("native code: %d bytes\n", len);

231
#ifdef DEBUG_PRINT
Damien's avatar
Damien committed
232
233
234
235
236
237
238
239
240
241
    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");

242
243
244
245
#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
246
    }
247
248
#endif
#endif
Damien's avatar
Damien committed
249
250
}

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

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

263
#ifdef DEBUG_PRINT
264
265
266
267
268
269
270
271
272
273
    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");

274
275
276
#ifdef WRITE_CODE
    if (fp_write_code != NULL) {
        fwrite(fun_data, len, 1, fp_write_code);
277
    }
278
279
#endif
#endif
Damien's avatar
Damien committed
280
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
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
318
319
320
321
#define PARSE_DEC_IN_INTG (1)
#define PARSE_DEC_IN_FRAC (2)
#define PARSE_DEC_IN_EXP  (3)

322
mp_obj_t rt_load_const_dec(qstr qstr) {
Damien's avatar
Damien committed
323
324
325
326
#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;
327
    mp_float_t dec_val = 0;
Damien's avatar
Damien committed
328
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
    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) {
364
        nlr_jump(mp_obj_new_exception_msg(rt_q_SyntaxError, "invalid syntax for number"));
Damien's avatar
Damien committed
365
366
367
368
369
370
371
372
373
374
375
376
    }
    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) {
377
        return mp_obj_new_complex(0, dec_val);
Damien's avatar
Damien committed
378
    } else {
379
        return mp_obj_new_float(dec_val);
Damien's avatar
Damien committed
380
381
    }
#else
382
    nlr_jump(mp_obj_new_exception_msg(rt_q_SyntaxError, "decimal numbers not supported"));
Damien's avatar
Damien committed
383
384
385
#endif
}

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

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

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

420
mp_obj_t rt_load_build_class(void) {
Damien's avatar
Damien committed
421
    DEBUG_OP_printf("load_build_class\n");
422
    mp_map_elem_t *elem = mp_qstr_map_lookup(&map_builtins, rt_q___build_class__, false);
Damien's avatar
Damien committed
423
424
425
426
427
428
429
    if (elem == NULL) {
        printf("name doesn't exist: __build_class__\n");
        assert(0);
    }
    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;
            }

Damien's avatar
Damien committed
528
            default: printf("%d\n", op); 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
636
        case MP_CODE_BYTE:
            fun = mp_obj_new_fun_bc(c->n_args, c->n_locals + c->n_cells + 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) {
656
        fun = mp_obj_new_gen_wrap(c->n_locals, c->n_cells, c->n_stack, fun);
657
658
    }

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

662
663
664
mp_obj_t rt_make_closure_from_id(int unique_code_id, mp_obj_t closure_tuple) {
    // make function object
    mp_obj_t ffun = rt_make_function_from_id(unique_code_id);
Damien's avatar
Damien committed
665
    // wrap function in closure object
666
    return mp_obj_new_closure(ffun, closure_tuple);
Damien's avatar
Damien committed
667
668
}

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

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

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

// args are in reverse order in the array
685
686
687
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
688

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

691
692
693
694
695
696
    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);
697
        } else {
698
            nlr_jump(mp_obj_new_exception_msg_1_arg(rt_q_TypeError, "'%s' object is not callable", fun->type->name));
699
        }
Damien's avatar
Damien committed
700
701
702
    }
}

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

711
712
// 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
713
mp_obj_t rt_call_method_n(uint n_args, const mp_obj_t *args) {
714
    DEBUG_OP_printf("call method %p(self=%p, n_args=%u)\n", args[n_args + 1], args[n_args], n_args);
715
716
717
    return rt_call_function_n(args[n_args + 1], n_args + ((args[n_args] == NULL) ? 0 : 1), args);
}

718
// 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
719
mp_obj_t rt_call_method_n_kw(uint n_args, uint n_kw, const mp_obj_t *args) {
720
721
722
723
724
    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);
}

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

Damien's avatar
Damien committed
730
// items are in reverse order
731
732
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
733
734
}

735
736
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
737
738
}

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

744
// unpacked items are stored in order into the array pointed to by items
745
746
747
748
749
750
751
752
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);
753
        }
754
755
756
757
758
759
        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));
760
761
    } else {
        // TODO call rt_getiter and extract via rt_iternext
762
        nlr_jump(mp_obj_new_exception_msg_1_arg(rt_q_TypeError, "'%s' object is not iterable", mp_obj_get_type_str(seq_in)));
763
764
765
    }
}

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

770
771
772
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
773
774
}

775
mp_obj_t rt_load_attr(mp_obj_t base, qstr attr) {
776
    DEBUG_OP_printf("load attr %s\n", qstr_str(attr));
777
778
    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
779
        if (elem == NULL) {
780
            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
781
782
        }
        return elem->value;
783
784
785
786
787
788
789
790
791
    } 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);
792
793
            }
        }
Damien's avatar
Damien committed
794
    }
795
    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
796
797
}

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

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

825
void rt_store_attr(mp_obj_t base, qstr attr, mp_obj_t value) {
Damien's avatar
Damien committed
826
    DEBUG_OP_printf("store attr %p.%s <- %p\n", base, qstr_str(attr), value);
827
    if (MP_OBJ_IS_TYPE(base, &class_type)) {
Damien's avatar
Damien committed
828
        // TODO CPython allows STORE_ATTR to a class, but is this the correct implementation?
829
830
831
832
        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);
833
    } else {
834
        printf("?AttributeError: '%s' object has no attribute '%s'\n", mp_obj_get_type_str(base), qstr_str(attr));
835
836
837
838
        assert(0);
    }
}

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])();
}
*/