runtime.c 34 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
    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
477
        switch (op) {
478
            case RT_BINARY_OP_OR:
Damien's avatar
Damien committed
479
            case RT_BINARY_OP_INPLACE_OR: lhs_val |= rhs_val; break;
480
            case RT_BINARY_OP_XOR:
Damien's avatar
Damien committed
481
            case RT_BINARY_OP_INPLACE_XOR: lhs_val ^= rhs_val; break;
482
            case RT_BINARY_OP_AND:
Damien's avatar
Damien committed
483
            case RT_BINARY_OP_INPLACE_AND: lhs_val &= rhs_val; break;
484
            case RT_BINARY_OP_LSHIFT:
Damien's avatar
Damien committed
485
            case RT_BINARY_OP_INPLACE_LSHIFT: lhs_val <<= rhs_val; break;
486
            case RT_BINARY_OP_RSHIFT:
Damien's avatar
Damien committed
487
            case RT_BINARY_OP_INPLACE_RSHIFT: lhs_val >>= rhs_val; break;
Damien's avatar
Damien committed
488
            case RT_BINARY_OP_ADD:
Damien's avatar
Damien committed
489
            case RT_BINARY_OP_INPLACE_ADD: lhs_val += rhs_val; break;
490
            case RT_BINARY_OP_SUBTRACT:
Damien's avatar
Damien committed
491
            case RT_BINARY_OP_INPLACE_SUBTRACT: lhs_val -= rhs_val; break;
492
            case RT_BINARY_OP_MULTIPLY:
Damien's avatar
Damien committed
493
            case RT_BINARY_OP_INPLACE_MULTIPLY: lhs_val *= rhs_val; break;
494
            case RT_BINARY_OP_FLOOR_DIVIDE:
Damien's avatar
Damien committed
495
            case RT_BINARY_OP_INPLACE_FLOOR_DIVIDE: lhs_val /= rhs_val; break;
496
#if MICROPY_ENABLE_FLOAT
497
            case RT_BINARY_OP_TRUE_DIVIDE:
498
            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
499
#endif
500
501
502
503
504
505

            // 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
506
507
            case RT_BINARY_OP_POWER:
            case RT_BINARY_OP_INPLACE_POWER:
508
509
510
511
512
513
514
515
            {
                int ans = 1;
                while (rhs_val > 0) {
                    if (rhs_val & 1) {
                        ans *= lhs_val;
                    }
                    lhs_val *= lhs_val;
                    rhs_val /= 2;
516
                }
517
518
519
520
                lhs_val = ans;
                break;
            }

521
            default: assert(0);
Damien's avatar
Damien committed
522
        }
Damien's avatar
Damien committed
523
        if (fit_small_int(lhs_val)) {
524
525
526
527
528
529
530
531
            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
532
            }
Damien's avatar
Damien committed
533
534
        }
    }
535
536
537

    // 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
538
539
}

540
mp_obj_t rt_compare_op(int op, mp_obj_t lhs, mp_obj_t rhs) {
Damien's avatar
Damien committed
541
    DEBUG_OP_printf("compare %d %p %p\n", op, lhs, rhs);
542
543
544

    // deal with == and !=
    if (op == RT_COMPARE_OP_EQUAL || op == RT_COMPARE_OP_NOT_EQUAL) {
545
        if (mp_obj_equal(lhs, rhs)) {
546
            if (op == RT_COMPARE_OP_EQUAL) {
547
                return mp_const_true;
548
            } else {
549
                return mp_const_false;
550
551
552
            }
        } else {
            if (op == RT_COMPARE_OP_EQUAL) {
553
                return mp_const_false;
554
            } else {
555
                return mp_const_true;
556
557
558
559
            }
        }
    }

560
561
562
563
564
565
566
567
568
569
570
571
    // 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;
            }
        }
    }

572
    // deal with small ints
573
574
575
    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);
576
577
578
579
580
581
582
583
584
        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) {
585
            return mp_const_true;
586
        } else {
587
            return mp_const_false;
588
589
590
591
592
        }
    }

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

    // not implemented
Damien's avatar
Damien committed
613
    assert(0);
614
    return mp_const_none;
Damien's avatar
Damien committed
615
616
}

617
mp_obj_t rt_make_function_from_id(int unique_code_id) {
618
619
    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
620
        // illegal code id
621
        return mp_const_none;
Damien's avatar
Damien committed
622
    }
623
624
625
626

    // 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
627
    switch (c->kind) {
628
        case MP_CODE_BYTE:
Damien George's avatar
Damien George committed
629
            fun = mp_obj_new_fun_bc(c->n_args, c->n_locals + c->n_stack, c->u_byte.code);
630
            break;
631
        case MP_CODE_NATIVE:
Damien's avatar
Damien committed
632
            switch (c->n_args) {
633
634
635
636
                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
637
638
            }
            break;
639
640
        case MP_CODE_INLINE_ASM:
            fun = mp_obj_new_fun_asm(c->n_args, c->u_inline_asm.fun);
Damien's avatar
Damien committed
641
642
643
            break;
        default:
            assert(0);
644
            fun = mp_const_none;
Damien's avatar
Damien committed
645
    }
646
647
648

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

652
    return fun;
Damien's avatar
Damien committed
653
654
}

655
mp_obj_t rt_make_closure_from_id(int unique_code_id, mp_obj_t closure_tuple) {
Damien George's avatar
Damien George committed
656
    DEBUG_OP_printf("make_closure_from_id %d\n", unique_code_id);
657
658
    // make function object
    mp_obj_t ffun = rt_make_function_from_id(unique_code_id);
Damien's avatar
Damien committed
659
    // wrap function in closure object
660
    return mp_obj_new_closure(ffun, closure_tuple);
Damien's avatar
Damien committed
661
662
}

663
mp_obj_t rt_call_function_0(mp_obj_t fun) {
664
665
666
    return rt_call_function_n(fun, 0, NULL);
}

667
mp_obj_t rt_call_function_1(mp_obj_t fun, mp_obj_t arg) {
668
669
670
    return rt_call_function_n(fun, 1, &arg);
}

671
672
mp_obj_t rt_call_function_2(mp_obj_t fun, mp_obj_t arg1, mp_obj_t arg2) {
    mp_obj_t args[2];
673
674
675
676
677
678
    args[1] = arg1;
    args[0] = arg2;
    return rt_call_function_n(fun, 2, args);
}

// args are in reverse order in the array
679
680
681
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
682

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

685
686
687
688
689
690
    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);
691
        } else {
692
            nlr_jump(mp_obj_new_exception_msg_1_arg(rt_q_TypeError, "'%s' object is not callable", fun->type->name));
693
        }
Damien's avatar
Damien committed
694
695
696
    }
}

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

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

712
// 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
713
mp_obj_t rt_call_method_n_kw(uint n_args, uint n_kw, const mp_obj_t *args) {
714
715
716
717
718
    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);
}

719
// items are in reverse order
720
721
mp_obj_t rt_build_tuple(int n_args, mp_obj_t *items) {
    return mp_obj_new_tuple_reverse(n_args, items);
722
723
}

Damien's avatar
Damien committed
724
// items are in reverse order
725
726
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
727
728
}

729
730
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
731
732
}

733
mp_obj_t rt_store_set(mp_obj_t set, mp_obj_t item) {
734
    mp_obj_set_store(set, item);
Damien's avatar
Damien committed
735
736
737
    return set;
}

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

760
761
mp_obj_t rt_build_map(int n_args) {
    return mp_obj_new_dict(n_args);
Damien's avatar
Damien committed
762
763
}

764
765
766
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
767
768
}

769
mp_obj_t rt_load_attr(mp_obj_t base, qstr attr) {
770
    DEBUG_OP_printf("load attr %s\n", qstr_str(attr));
771
772
    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
773
        if (elem == NULL) {
774
775
            // TODO what about generic method lookup?
            goto no_attr;
Damien's avatar
Damien committed
776
777
        }
        return elem->value;
778
779
    } else if (MP_OBJ_IS_TYPE(base, &instance_type)) {
        return mp_obj_instance_load_attr(base, attr);
780
    } else if (MP_OBJ_IS_TYPE(base, &module_type)) {
781
        DEBUG_OP_printf("lookup module map %p\n", mp_obj_module_get_globals(base));
782
783
784
785
786
787
        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;
788
789
790
791
792
793
794
    } 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);
795
796
            }
        }
Damien's avatar
Damien committed
797
    }
798
799

no_attr:
800
    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
801
802
}

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

825
    // no method; fallback to load_attr
826
827
828
829
    dest[1] = rt_load_attr(base, attr);
    dest[0] = NULL;
}

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

847
void rt_store_subscr(mp_obj_t base, mp_obj_t index, mp_obj_t value) {
Damien's avatar
Damien committed
848
    DEBUG_OP_printf("store subscr %p[%p] <- %p\n", base, index, value);
849
    if (MP_OBJ_IS_TYPE(base, &list_type)) {
850
        // list store
851
852
853
854
        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
855
    } else {
856
        assert(0);
Damien's avatar
Damien committed
857
858
859
    }
}

860
861
862
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"));
863
    } else {
864
865
866
        mp_obj_base_t *o = o_in;
        if (o->type->getiter != NULL) {
            return o->type->getiter(o_in);
867
        } else {
868
            nlr_jump(mp_obj_new_exception_msg_1_arg(rt_q_TypeError, "'%s' object is not iterable", o->type->name));
869
        }
870
871
    }
}
872

873
874
875
876
877
878
879
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);
880
        } else {
881
            nlr_jump(mp_obj_new_exception_msg_1_arg(rt_q_TypeError, "? '%s' object is not iterable", o->type->name));
882
        }
883
884
885
    }
}

886
mp_obj_t rt_import_name(qstr name, mp_obj_t fromlist, mp_obj_t level) {
887
    // build args array
888
889
890
891
    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
892
893
894
895
    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
896
    return mp_builtin___import__(5, args);
897
898
}

899
900
mp_obj_t rt_import_from(mp_obj_t module, qstr name) {
    mp_obj_t x = rt_load_attr(module, name);
901
902
903
904
905
906
907
908
    /* TODO convert AttributeError to ImportError
    if (fail) {
        (ImportError, "cannot import name %s", qstr_str(name), NULL)
    }
    */
    return x;
}

909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
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;
}

927
// these must correspond to the respective enum
928
void *const rt_fun_table[RT_F_NUMBER_OF] = {
929
    rt_load_const_dec,
Damien's avatar
Damien committed
930
931
932
    rt_load_const_str,
    rt_load_name,
    rt_load_global,
933
    rt_load_build_class,
Damien's avatar
Damien committed
934
935
936
    rt_load_attr,
    rt_load_method,
    rt_store_name,
937
    rt_store_attr,
Damien's avatar
Damien committed
938
939
940
    rt_store_subscr,
    rt_is_true,
    rt_unary_op,
941
    rt_build_tuple,
Damien's avatar
Damien committed
942
    rt_build_list,
943
    rt_list_append,
Damien's avatar
Damien committed
944
945
946
    rt_build_map,
    rt_store_map,
    rt_build_set,
947
    rt_store_set,
Damien's avatar
Damien committed
948
    rt_make_function_from_id,
949
    rt_call_function_n,
950
    rt_call_method_n,
Damien's avatar
Damien committed
951
952
    rt_binary_op,
    rt_compare_op,
953
954
    rt_getiter,
    rt_iternext,
Damien's avatar
Damien committed
955
956
957
958
959
960
961
};

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