runtime.c 33.2 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);
150
    mp_qstr_map_lookup(&map_builtins, qstr_from_str_static("type"), true)->value = (mp_obj_t)&mp_builtin_type_obj;
Damien's avatar
Damien committed
151

152

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

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

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

Damien's avatar
Damien committed
169
170
171
172
173
174
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
175
176
}

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

Damien George's avatar
Damien George committed
186
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) {
187
188
189
    alloc_unique_codes();

    assert(unique_code_id < next_unique_code_id);
190
    unique_codes[unique_code_id].kind = MP_CODE_BYTE;
191
    unique_codes[unique_code_id].n_args = n_args;
192
193
194
    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;
195
196
197
    unique_codes[unique_code_id].u_byte.code = code;
    unique_codes[unique_code_id].u_byte.len = len;

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

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

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

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

224
    assert(1 <= unique_code_id && unique_code_id < next_unique_code_id);
225
    unique_codes[unique_code_id].kind = MP_CODE_NATIVE;
Damien's avatar
Damien committed
226
    unique_codes[unique_code_id].n_args = n_args;
227
228
229
    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
230
231
    unique_codes[unique_code_id].u_native.fun = fun;

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

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

245
246
247
248
#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
249
    }
250
251
#endif
#endif
Damien's avatar
Damien committed
252
253
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    // 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
546
547
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

737
738
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
739
740
}

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

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

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

772
773
774
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
775
776
}

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

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

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

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

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

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

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

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

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

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

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