runtime.c 33 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
    //printf("native code: %d bytes\n", len);
230

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
    if (elem == NULL) {
424
        nlr_jump(mp_obj_new_exception_msg(rt_q_NameError, "name '__build_class__' is not defined"));
Damien's avatar
Damien committed
425
426
427
428
    }
    return elem->value;
}

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

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

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

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

447
mp_obj_t rt_unary_op(int op, mp_obj_t arg) {
Damien's avatar
Damien committed
448
    DEBUG_OP_printf("unary %d %p\n", op, arg);
449
450
    if (MP_OBJ_IS_SMALL_INT(arg)) {
        mp_small_int_t val = MP_OBJ_SMALL_INT_VALUE(arg);
Damien's avatar
Damien committed
451
        switch (op) {
452
            case RT_UNARY_OP_NOT: if (val != 0) { return mp_const_true;} else { return mp_const_false; }
Damien's avatar
Damien committed
453
454
455
456
457
458
            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)) {
459
460
461
462
463
464
465
466
467
468
469
470
471
            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
472
        }
473
474
        // 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
475
    }
Damien's avatar
Damien committed
476
477
}

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

            // 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
512
513
            case RT_BINARY_OP_POWER:
            case RT_BINARY_OP_INPLACE_POWER:
514
515
516
517
518
519
520
521
            {
                int ans = 1;
                while (rhs_val > 0) {
                    if (rhs_val & 1) {
                        ans *= lhs_val;
                    }
                    lhs_val *= lhs_val;
                    rhs_val /= 2;
522
                }
523
524
525
526
                lhs_val = ans;
                break;
            }

527
            default: assert(0);
Damien's avatar
Damien committed
528
        }
Damien's avatar
Damien committed
529
        if (fit_small_int(lhs_val)) {
530
531
532
533
534
535
536
537
            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
538
            }
Damien's avatar
Damien committed
539
540
        }
    }
541
542
543

    // 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
544
545
}

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

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

566
567
568
569
570
571
572
573
574
575
576
577
    // 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;
            }
        }
    }

578
    // deal with small ints
579
580
581
    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);
582
583
584
585
586
587
588
589
590
        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) {
591
            return mp_const_true;
592
        } else {
593
            return mp_const_false;
594
595
596
597
598
        }
    }

#if MICROPY_ENABLE_FLOAT
    // deal with floats
599
600
601
    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
602
603
        int cmp;
        switch (op) {
604
605
606
607
            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
608
609
610
            default: assert(0); cmp = 0;
        }
        if (cmp) {
611
            return mp_const_true;
Damien's avatar
Damien committed
612
        } else {
613
            return mp_const_false;
Damien's avatar
Damien committed
614
615
        }
    }
616
#endif
617
618

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

623
mp_obj_t rt_make_function_from_id(int unique_code_id) {
624
625
    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
626
        // illegal code id
627
        return mp_const_none;
Damien's avatar
Damien committed
628
    }
629
630
631
632

    // 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
633
    switch (c->kind) {
634
635
        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);
636
            break;
637
        case MP_CODE_NATIVE:
Damien's avatar
Damien committed
638
            switch (c->n_args) {
639
640
641
642
                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
643
644
            }
            break;
645
646
        case MP_CODE_INLINE_ASM:
            fun = mp_obj_new_fun_asm(c->n_args, c->u_inline_asm.fun);
Damien's avatar
Damien committed
647
648
649
            break;
        default:
            assert(0);
650
            fun = mp_const_none;
Damien's avatar
Damien committed
651
    }
652
653
654

    // check for generator functions and if so wrap in generator object
    if (c->is_generator) {
655
        fun = mp_obj_new_gen_wrap(c->n_locals, c->n_cells, c->n_stack, fun);
656
657
    }

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

661
662
663
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
664
    // wrap function in closure object
665
    return mp_obj_new_closure(ffun, closure_tuple);
Damien's avatar
Damien committed
666
667
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

837
void rt_store_subscr(mp_obj_t base, mp_obj_t index, mp_obj_t value) {
Damien's avatar
Damien committed
838
    DEBUG_OP_printf("store subscr %p[%p] <- %p\n", base, index, value);
839
    if (MP_OBJ_IS_TYPE(base, &list_type)) {
840
        // list store
841
842
843
844
        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
845
    } else {
846
        assert(0);
Damien's avatar
Damien committed
847
848
849
    }
}

850
851
852
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"));
853
    } else {
854
855
856
        mp_obj_base_t *o = o_in;
        if (o->type->getiter != NULL) {
            return o->type->getiter(o_in);
857
        } else {
858
            nlr_jump(mp_obj_new_exception_msg_1_arg(rt_q_TypeError, "'%s' object is not iterable", o->type->name));
859
        }
860
861
    }
}
862

863
864
865
866
867
868
869
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);
870
        } else {
871
            nlr_jump(mp_obj_new_exception_msg_1_arg(rt_q_TypeError, "? '%s' object is not iterable", o->type->name));
872
        }
873
874
875
    }
}

876
mp_obj_t rt_import_name(qstr name, mp_obj_t fromlist, mp_obj_t level) {
877
    // build args array
878
879
880
881
    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
882
883
884
885
    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
886
    return mp_builtin___import__(5, args);
887
888
}

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

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

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