runtime.c 34.4 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
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
312
313
314
315
#define PARSE_DEC_IN_INTG (1)
#define PARSE_DEC_IN_FRAC (2)
#define PARSE_DEC_IN_EXP  (3)

316
mp_obj_t rt_load_const_dec(qstr qstr) {
Damien's avatar
Damien committed
317
318
319
320
#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;
321
    mp_float_t dec_val = 0;
Damien's avatar
Damien committed
322
323
324
325
326
327
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
    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) {
358
        nlr_jump(mp_obj_new_exception_msg(rt_q_SyntaxError, "invalid syntax for number"));
Damien's avatar
Damien committed
359
360
361
362
363
364
365
366
367
368
369
370
    }
    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) {
371
        return mp_obj_new_complex(0, dec_val);
Damien's avatar
Damien committed
372
    } else {
373
        return mp_obj_new_float(dec_val);
Damien's avatar
Damien committed
374
375
    }
#else
376
    nlr_jump(mp_obj_new_exception_msg(rt_q_SyntaxError, "decimal numbers not supported"));
Damien's avatar
Damien committed
377
378
379
#endif
}

380
mp_obj_t rt_load_const_str(qstr qstr) {
Damien's avatar
Damien committed
381
    DEBUG_OP_printf("load '%s'\n", qstr_str(qstr));
382
    return mp_obj_new_str(qstr);
Damien's avatar
Damien committed
383
384
}

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

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

414
mp_obj_t rt_load_build_class(void) {
Damien's avatar
Damien committed
415
    DEBUG_OP_printf("load_build_class\n");
416
    mp_map_elem_t *elem = mp_qstr_map_lookup(&map_builtins, rt_q___build_class__, false);
Damien's avatar
Damien committed
417
    if (elem == NULL) {
418
        nlr_jump(mp_obj_new_exception_msg(rt_q_NameError, "name '__build_class__' is not defined"));
Damien's avatar
Damien committed
419
420
421
422
    }
    return elem->value;
}

423
424
mp_obj_t rt_get_cell(mp_obj_t cell) {
    return mp_obj_cell_get(cell);
425
426
}

427
428
void rt_set_cell(mp_obj_t cell, mp_obj_t val) {
    mp_obj_cell_set(cell, val);
429
430
}

431
void rt_store_name(qstr qstr, mp_obj_t obj) {
432
    DEBUG_OP_printf("store name %s <- %p\n", qstr_str(qstr), obj);
433
    mp_qstr_map_lookup(map_locals, qstr, true)->value = obj;
434
435
}

436
void rt_store_global(qstr qstr, mp_obj_t obj) {
437
    DEBUG_OP_printf("store global %s <- %p\n", qstr_str(qstr), obj);
438
    mp_qstr_map_lookup(map_globals, qstr, true)->value = obj;
Damien's avatar
Damien committed
439
440
}

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

472
mp_obj_t rt_binary_op(int op, mp_obj_t lhs, mp_obj_t rhs) {
Damien's avatar
Damien committed
473
    DEBUG_OP_printf("binary %d %p %p\n", op, lhs, rhs);
474
475
476
477
478
479
480
481
482
483

    // TODO correctly distinguish inplace operators for mutable objects
    // lookup logic that CPython uses for +=:
    //   check for implemented +=
    //   then check for implemented +
    //   then check for implemented seq.inplace_concat
    //   then check for implemented seq.concat
    //   then fail
    // note that list does not implement + or +=, so that inplace_concat is reached first for +=

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

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

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

    // 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
548
549
}

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

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

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

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

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

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

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

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

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

662
    return fun;
Damien's avatar
Damien committed
663
664
}

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

673
mp_obj_t rt_call_function_0(mp_obj_t fun) {
674
675
676
    return rt_call_function_n(fun, 0, NULL);
}

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

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

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

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

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

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

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

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

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

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

739
740
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
741
742
}

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

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

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

774
775
776
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
777
778
}

779
mp_obj_t rt_load_attr(mp_obj_t base, qstr attr) {
780
    DEBUG_OP_printf("load attr %s\n", qstr_str(attr));
781
782
    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
783
        if (elem == NULL) {
784
785
            // TODO what about generic method lookup?
            goto no_attr;
Damien's avatar
Damien committed
786
787
        }
        return elem->value;
788
789
    } else if (MP_OBJ_IS_TYPE(base, &instance_type)) {
        return mp_obj_instance_load_attr(base, attr);
790
    } else if (MP_OBJ_IS_TYPE(base, &module_type)) {
791
        DEBUG_OP_printf("lookup module map %p\n", mp_obj_module_get_globals(base));
792
793
794
795
796
797
        mp_map_elem_t *elem = mp_qstr_map_lookup(mp_obj_module_get_globals(base), attr, false);
        if (elem == NULL) {
            // TODO what about generic method lookup?
            goto no_attr;
        }
        return elem->value;
798
799
800
801
802
803
804
    } 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);
805
806
            }
        }
Damien's avatar
Damien committed
807
    }
808
809

no_attr:
810
    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
811
812
}

813
void rt_load_method(mp_obj_t base, qstr attr, mp_obj_t *dest) {
Damien's avatar
Damien committed
814
    DEBUG_OP_printf("load method %s\n", qstr_str(attr));
815
    if (MP_OBJ_IS_TYPE(base, &gen_instance_type) && attr == rt_q___next__) {
816
        dest[1] = (mp_obj_t)&mp_builtin_next_obj;
817
818
        dest[0] = base;
        return;
819
820
    } else if (MP_OBJ_IS_TYPE(base, &instance_type)) {
        mp_obj_instance_load_method(base, attr, dest);
821
        return;
822
823
824
825
    } 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
826
827
        for (; meth->name != NULL; meth++) {
            if (strcmp(meth->name, qstr_str(attr)) == 0) {
828
                dest[1] = (mp_obj_t)meth->fun;
Damien's avatar
Damien committed
829
830
831
832
                dest[0] = base;
                return;
            }
        }
833
834
    }

835
    // no method; fallback to load_attr
836
837
838
839
    dest[1] = rt_load_attr(base, attr);
    dest[0] = NULL;
}

840
void rt_store_attr(mp_obj_t base, qstr attr, mp_obj_t value) {
Damien's avatar
Damien committed
841
    DEBUG_OP_printf("store attr %p.%s <- %p\n", base, qstr_str(attr), value);
842
    if (MP_OBJ_IS_TYPE(base, &class_type)) {
Damien's avatar
Damien committed
843
        // TODO CPython allows STORE_ATTR to a class, but is this the correct implementation?
844
845
846
847
        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);
848
849
850
851
    } else if (MP_OBJ_IS_TYPE(base, &module_type)) {
        // TODO CPython allows STORE_ATTR to a module, but is this the correct implementation?
        mp_map_t *globals = mp_obj_module_get_globals(base);
        mp_qstr_map_lookup(globals, attr, true)->value = value;
852
    } else {
853
        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)));
854
855
856
    }
}

857
void rt_store_subscr(mp_obj_t base, mp_obj_t index, mp_obj_t value) {
Damien's avatar
Damien committed
858
    DEBUG_OP_printf("store subscr %p[%p] <- %p\n", base, index, value);
859
    if (MP_OBJ_IS_TYPE(base, &list_type)) {
860
        // list store
861
862
863
864
        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
865
    } else {
866
        assert(0);
Damien's avatar
Damien committed
867
868
869
    }
}

870
871
872
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"));
873
    } else {
874
875
876
        mp_obj_base_t *o = o_in;
        if (o->type->getiter != NULL) {
            return o->type->getiter(o_in);
877
        } else {
878
            nlr_jump(mp_obj_new_exception_msg_1_arg(rt_q_TypeError, "'%s' object is not iterable", o->type->name));
879
        }
880
881
    }
}
882

883
884
885
886
887
888
889
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);
890
        } else {
891
            nlr_jump(mp_obj_new_exception_msg_1_arg(rt_q_TypeError, "? '%s' object is not iterable", o->type->name));
892
        }
893
894
895
    }
}

896
mp_obj_t rt_import_name(qstr name, mp_obj_t fromlist, mp_obj_t level) {
897
    // build args array
898
899
900
901
    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
902
903
904
905
    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
906
    return mp_builtin___import__(5, args);
907
908
}

909
910
mp_obj_t rt_import_from(mp_obj_t module, qstr name) {
    mp_obj_t x = rt_load_attr(module, name);
911
912
913
914
915
916
917
918
    /* TODO convert AttributeError to ImportError
    if (fail) {
        (ImportError, "cannot import name %s", qstr_str(name), NULL)
    }
    */
    return x;
}

919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
mp_map_t *rt_locals_get(void) {
    return map_locals;
}

void rt_locals_set(mp_map_t *m) {
    DEBUG_OP_printf("rt_locals_set(%p)\n", m);
    map_locals = m;
}

mp_map_t *rt_globals_get(void) {
    return map_globals;
}

void rt_globals_set(mp_map_t *m) {
    DEBUG_OP_printf("rt_globals_set(%p)\n", m);
    map_globals = m;
}

937
// these must correspond to the respective enum
938
void *const rt_fun_table[RT_F_NUMBER_OF] = {
939
    rt_load_const_dec,
Damien's avatar
Damien committed
940
941
942
    rt_load_const_str,
    rt_load_name,
    rt_load_global,
943
    rt_load_build_class,
Damien's avatar
Damien committed
944
945
946
    rt_load_attr,
    rt_load_method,
    rt_store_name,
947
    rt_store_attr,
Damien's avatar
Damien committed
948
949
950
    rt_store_subscr,
    rt_is_true,
    rt_unary_op,
951
    rt_build_tuple,
Damien's avatar
Damien committed
952
    rt_build_list,
953
    rt_list_append,
Damien's avatar
Damien committed
954
955
956
    rt_build_map,
    rt_store_map,
    rt_build_set,
957
    rt_store_set,
Damien's avatar
Damien committed
958
    rt_make_function_from_id,
959
    rt_call_function_n,
960
    rt_call_method_n,
Damien's avatar
Damien committed
961
962
    rt_binary_op,
    rt_compare_op,
963
964
    rt_getiter,
    rt_iternext,
Damien's avatar
Damien committed
965
966
967
968
969
970
971
};

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