diff --git a/py/asmthumb.c b/py/asmthumb.c
index 602caeb2d65e5e00902626296d9bd1972b9f3ec1..a6fe4be809b2bdbc97677a0f62bd9acab5d66220 100644
--- a/py/asmthumb.c
+++ b/py/asmthumb.c
@@ -4,7 +4,7 @@
 #include <string.h>
 
 #include "misc.h"
-#include "mpyconfig.h"
+#include "mpconfig.h"
 #include "asmthumb.h"
 
 #define UNSIGNED_FIT8(x) (((x) & 0xffffff00) == 0)
diff --git a/py/bc.h b/py/bc.h
index 6dc1dbbd4ee31d34cd963d316ec5da7cb4f1a38d..35847f458944ede0b6e1371cc4a7e9558ba0e0e2 100644
--- a/py/bc.h
+++ b/py/bc.h
@@ -1,101 +1,2 @@
-#define PYBC_LOAD_CONST_FALSE   (0x10)
-#define PYBC_LOAD_CONST_NONE    (0x11)
-#define PYBC_LOAD_CONST_TRUE    (0x12)
-#define PYBC_LOAD_CONST_SMALL_INT   (0x13) // 24-bit, in excess
-#define PYBC_LOAD_CONST_INT     (0x14) // qstr
-#define PYBC_LOAD_CONST_DEC     (0x15) // qstr
-#define PYBC_LOAD_CONST_ID      (0x16) // qstr
-#define PYBC_LOAD_CONST_BYTES   (0x17) // qstr
-#define PYBC_LOAD_CONST_STRING  (0x18) // qstr
-
-#define PYBC_LOAD_FAST_0        (0x20)
-#define PYBC_LOAD_FAST_1        (0x21)
-#define PYBC_LOAD_FAST_2        (0x22)
-#define PYBC_LOAD_FAST_N        (0x23) // uint
-#define PYBC_LOAD_DEREF         (0x24) // uint
-#define PYBC_LOAD_CLOSURE       (0x25) // uint
-#define PYBC_LOAD_NAME          (0x26) // qstr
-#define PYBC_LOAD_GLOBAL        (0x27) // qstr
-#define PYBC_LOAD_ATTR          (0x28) // qstr
-#define PYBC_LOAD_METHOD        (0x29) // qstr
-#define PYBC_LOAD_BUILD_CLASS   (0x2a)
-
-#define PYBC_STORE_FAST_0       (0x30)
-#define PYBC_STORE_FAST_1       (0x31)
-#define PYBC_STORE_FAST_2       (0x32)
-#define PYBC_STORE_FAST_N       (0x33) // uint
-#define PYBC_STORE_DEREF        (0x34) // uint
-#define PYBC_STORE_NAME         (0x35) // qstr
-#define PYBC_STORE_GLOBAL       (0x36) // qstr
-#define PYBC_STORE_ATTR         (0x37) // qstr
-#define PYBC_STORE_SUBSCR       (0x38)
-
-#define PYBC_DELETE_FAST_N      (0x39) // uint
-#define PYBC_DELETE_DEREF       (0x3a) // uint
-#define PYBC_DELETE_NAME        (0x3b) // qstr
-#define PYBC_DELETE_GLOBAL      (0x3c) // qstr
-#define PYBC_DELETE_ATTR        (0x3d) // qstr
-#define PYBC_DELETE_SUBSCR      (0x3e)
-
-#define PYBC_DUP_TOP            (0x40)
-#define PYBC_DUP_TOP_TWO        (0x41)
-#define PYBC_POP_TOP            (0x42)
-#define PYBC_ROT_TWO            (0x43)
-#define PYBC_ROT_THREE          (0x44)
-
-#define PYBC_JUMP               (0x45) // rel byte code offset, 16-bit signed, in excess
-#define PYBC_POP_JUMP_IF_TRUE   (0x46) // rel byte code offset, 16-bit signed, in excess
-#define PYBC_POP_JUMP_IF_FALSE  (0x47) // rel byte code offset, 16-bit signed, in excess
-#define PYBC_JUMP_IF_TRUE_OR_POP    (0x48) // rel byte code offset, 16-bit signed, in excess
-#define PYBC_JUMP_IF_FALSE_OR_POP   (0x49) // rel byte code offset, 16-bit signed, in excess
-#define PYBC_SETUP_LOOP         (0x4a) // rel byte code offset, 16-bit unsigned
-#define PYBC_BREAK_LOOP         (0x4b) // rel byte code offset, 16-bit unsigned
-#define PYBC_CONTINUE_LOOP      (0x4c) // rel byte code offset, 16-bit unsigned
-#define PYBC_SETUP_WITH         (0x4d) // rel byte code offset, 16-bit unsigned
-#define PYBC_WITH_CLEANUP       (0x4e)
-#define PYBC_SETUP_EXCEPT       (0x4f) // rel byte code offset, 16-bit unsigned
-#define PYBC_SETUP_FINALLY      (0x50) // rel byte code offset, 16-bit unsigned
-#define PYBC_END_FINALLY        (0x51)
-#define PYBC_GET_ITER           (0x52)
-#define PYBC_FOR_ITER           (0x53) // rel byte code offset, 16-bit unsigned
-#define PYBC_POP_BLOCK          (0x54)
-#define PYBC_POP_EXCEPT         (0x55)
-
-#define PYBC_UNARY_OP           (0x60) // byte
-#define PYBC_BINARY_OP          (0x61) // byte
-#define PYBC_COMPARE_OP         (0x62) // byte
-
-#define PYBC_BUILD_TUPLE        (0x70) // uint
-#define PYBC_BUILD_LIST         (0x71) // uint
-#define PYBC_LIST_APPEND        (0x72) // uint
-#define PYBC_BUILD_MAP          (0x73) // uint
-#define PYBC_STORE_MAP          (0x74)
-#define PYBC_MAP_ADD            (0x75) // uint
-#define PYBC_BUILD_SET          (0x76) // uint
-#define PYBC_SET_ADD            (0x77) // uint
-#define PYBC_BUILD_SLICE        (0x78) // uint
-#define PYBC_UNPACK_SEQUENCE    (0x79) // uint
-#define PYBC_UNPACK_EX          (0x7a) // uint
-
-#define PYBC_RETURN_VALUE       (0x80)
-#define PYBC_RAISE_VARARGS      (0x81) // uint
-#define PYBC_YIELD_VALUE        (0x82)
-#define PYBC_YIELD_FROM         (0x83)
-
-#define PYBC_MAKE_FUNCTION      (0x90) // uint
-#define PYBC_MAKE_CLOSURE       (0x91) // uint
-#define PYBC_CALL_FUNCTION      (0x92) // uint
-#define PYBC_CALL_FUNCTION_VAR  (0x93) // uint
-#define PYBC_CALL_FUNCTION_KW   (0x94) // uint
-#define PYBC_CALL_FUNCTION_VAR_KW   (0x95) // uint
-#define PYBC_CALL_METHOD        (0x96) // uint
-#define PYBC_CALL_METHOD_VAR    (0x97) // uint
-#define PYBC_CALL_METHOD_KW     (0x98) // uint
-#define PYBC_CALL_METHOD_VAR_KW (0x99) // uint
-
-#define PYBC_IMPORT_NAME        (0xe0) // qstr
-#define PYBC_IMPORT_FROM        (0xe1) // qstr
-#define PYBC_IMPORT_STAR        (0xe2)
-
-py_obj_t py_execute_byte_code(const byte *code, const py_obj_t *args, uint n_args, uint n_state);
-bool py_execute_byte_code_2(const byte **ip_in_out, py_obj_t *fastn, py_obj_t **sp_in_out);
+mp_obj_t mp_execute_byte_code(const byte *code, const mp_obj_t *args, uint n_args, uint n_state);
+bool mp_execute_byte_code_2(const byte **ip_in_out, mp_obj_t *fastn, mp_obj_t **sp_in_out);
diff --git a/py/bc0.h b/py/bc0.h
new file mode 100644
index 0000000000000000000000000000000000000000..e6dc84277a71aa34be6f541090da0a3fffca8508
--- /dev/null
+++ b/py/bc0.h
@@ -0,0 +1,98 @@
+#define MP_BC_LOAD_CONST_FALSE   (0x10)
+#define MP_BC_LOAD_CONST_NONE    (0x11)
+#define MP_BC_LOAD_CONST_TRUE    (0x12)
+#define MP_BC_LOAD_CONST_SMALL_INT   (0x13) // 24-bit, in excess
+#define MP_BC_LOAD_CONST_INT     (0x14) // qstr
+#define MP_BC_LOAD_CONST_DEC     (0x15) // qstr
+#define MP_BC_LOAD_CONST_ID      (0x16) // qstr
+#define MP_BC_LOAD_CONST_BYTES   (0x17) // qstr
+#define MP_BC_LOAD_CONST_STRING  (0x18) // qstr
+
+#define MP_BC_LOAD_FAST_0        (0x20)
+#define MP_BC_LOAD_FAST_1        (0x21)
+#define MP_BC_LOAD_FAST_2        (0x22)
+#define MP_BC_LOAD_FAST_N        (0x23) // uint
+#define MP_BC_LOAD_DEREF         (0x24) // uint
+#define MP_BC_LOAD_CLOSURE       (0x25) // uint
+#define MP_BC_LOAD_NAME          (0x26) // qstr
+#define MP_BC_LOAD_GLOBAL        (0x27) // qstr
+#define MP_BC_LOAD_ATTR          (0x28) // qstr
+#define MP_BC_LOAD_METHOD        (0x29) // qstr
+#define MP_BC_LOAD_BUILD_CLASS   (0x2a)
+
+#define MP_BC_STORE_FAST_0       (0x30)
+#define MP_BC_STORE_FAST_1       (0x31)
+#define MP_BC_STORE_FAST_2       (0x32)
+#define MP_BC_STORE_FAST_N       (0x33) // uint
+#define MP_BC_STORE_DEREF        (0x34) // uint
+#define MP_BC_STORE_NAME         (0x35) // qstr
+#define MP_BC_STORE_GLOBAL       (0x36) // qstr
+#define MP_BC_STORE_ATTR         (0x37) // qstr
+#define MP_BC_STORE_SUBSCR       (0x38)
+
+#define MP_BC_DELETE_FAST_N      (0x39) // uint
+#define MP_BC_DELETE_DEREF       (0x3a) // uint
+#define MP_BC_DELETE_NAME        (0x3b) // qstr
+#define MP_BC_DELETE_GLOBAL      (0x3c) // qstr
+#define MP_BC_DELETE_ATTR        (0x3d) // qstr
+#define MP_BC_DELETE_SUBSCR      (0x3e)
+
+#define MP_BC_DUP_TOP            (0x40)
+#define MP_BC_DUP_TOP_TWO        (0x41)
+#define MP_BC_POP_TOP            (0x42)
+#define MP_BC_ROT_TWO            (0x43)
+#define MP_BC_ROT_THREE          (0x44)
+
+#define MP_BC_JUMP               (0x45) // rel byte code offset, 16-bit signed, in excess
+#define MP_BC_POP_JUMP_IF_TRUE   (0x46) // rel byte code offset, 16-bit signed, in excess
+#define MP_BC_POP_JUMP_IF_FALSE  (0x47) // rel byte code offset, 16-bit signed, in excess
+#define MP_BC_JUMP_IF_TRUE_OR_POP    (0x48) // rel byte code offset, 16-bit signed, in excess
+#define MP_BC_JUMP_IF_FALSE_OR_POP   (0x49) // rel byte code offset, 16-bit signed, in excess
+#define MP_BC_SETUP_LOOP         (0x4a) // rel byte code offset, 16-bit unsigned
+#define MP_BC_BREAK_LOOP         (0x4b) // rel byte code offset, 16-bit unsigned
+#define MP_BC_CONTINUE_LOOP      (0x4c) // rel byte code offset, 16-bit unsigned
+#define MP_BC_SETUP_WITH         (0x4d) // rel byte code offset, 16-bit unsigned
+#define MP_BC_WITH_CLEANUP       (0x4e)
+#define MP_BC_SETUP_EXCEPT       (0x4f) // rel byte code offset, 16-bit unsigned
+#define MP_BC_SETUP_FINALLY      (0x50) // rel byte code offset, 16-bit unsigned
+#define MP_BC_END_FINALLY        (0x51)
+#define MP_BC_GET_ITER           (0x52)
+#define MP_BC_FOR_ITER           (0x53) // rel byte code offset, 16-bit unsigned
+#define MP_BC_POP_BLOCK          (0x54)
+#define MP_BC_POP_EXCEPT         (0x55)
+
+#define MP_BC_UNARY_OP           (0x60) // byte
+#define MP_BC_BINARY_OP          (0x61) // byte
+#define MP_BC_COMPARE_OP         (0x62) // byte
+
+#define MP_BC_BUILD_TUPLE        (0x70) // uint
+#define MP_BC_BUILD_LIST         (0x71) // uint
+#define MP_BC_LIST_APPEND        (0x72) // uint
+#define MP_BC_BUILD_MAP          (0x73) // uint
+#define MP_BC_STORE_MAP          (0x74)
+#define MP_BC_MAP_ADD            (0x75) // uint
+#define MP_BC_BUILD_SET          (0x76) // uint
+#define MP_BC_SET_ADD            (0x77) // uint
+#define MP_BC_BUILD_SLICE        (0x78) // uint
+#define MP_BC_UNPACK_SEQUENCE    (0x79) // uint
+#define MP_BC_UNPACK_EX          (0x7a) // uint
+
+#define MP_BC_RETURN_VALUE       (0x80)
+#define MP_BC_RAISE_VARARGS      (0x81) // uint
+#define MP_BC_YIELD_VALUE        (0x82)
+#define MP_BC_YIELD_FROM         (0x83)
+
+#define MP_BC_MAKE_FUNCTION      (0x90) // uint
+#define MP_BC_MAKE_CLOSURE       (0x91) // uint
+#define MP_BC_CALL_FUNCTION      (0x92) // uint
+#define MP_BC_CALL_FUNCTION_VAR  (0x93) // uint
+#define MP_BC_CALL_FUNCTION_KW   (0x94) // uint
+#define MP_BC_CALL_FUNCTION_VAR_KW   (0x95) // uint
+#define MP_BC_CALL_METHOD        (0x96) // uint
+#define MP_BC_CALL_METHOD_VAR    (0x97) // uint
+#define MP_BC_CALL_METHOD_KW     (0x98) // uint
+#define MP_BC_CALL_METHOD_VAR_KW (0x99) // uint
+
+#define MP_BC_IMPORT_NAME        (0xe0) // qstr
+#define MP_BC_IMPORT_FROM        (0xe1) // qstr
+#define MP_BC_IMPORT_STAR        (0xe2)
diff --git a/py/builtin.c b/py/builtin.c
index e45cdc98bc861c827bd3b806d554aa6e7236b01b..11e19fdea94c0bfe8b697bd2e0d4a35b5175d986 100644
--- a/py/builtin.c
+++ b/py/builtin.c
@@ -7,253 +7,258 @@
 
 #include "nlr.h"
 #include "misc.h"
-#include "mpyconfig.h"
+#include "mpconfig.h"
+#include "obj.h"
+#include "runtime0.h"
 #include "runtime.h"
-#include "bc.h"
-
+//#include "bc.h"
 #include "map.h"
-#include "obj.h"
-#include "objprivate.h"
 #include "builtin.h"
 
-py_obj_t py_builtin___build_class__(py_obj_t o_class_fun, py_obj_t o_class_name) {
+mp_obj_t mp_builtin___build_class__(mp_obj_t o_class_fun, mp_obj_t o_class_name) {
     // we differ from CPython: we set the new __locals__ object here
-    py_map_t *old_locals = rt_get_map_locals();
-    py_map_t *class_locals = py_map_new(MAP_QSTR, 0);
+    mp_map_t *old_locals = rt_get_map_locals();
+    mp_map_t *class_locals = mp_map_new(MP_MAP_QSTR, 0);
     rt_set_map_locals(class_locals);
 
     // call the class code
-    rt_call_function_1(o_class_fun, (py_obj_t)0xdeadbeef);
+    rt_call_function_1(o_class_fun, (mp_obj_t)0xdeadbeef);
 
     // restore old __locals__ object
     rt_set_map_locals(old_locals);
 
     // create and return the new class
-    py_obj_base_t *o = m_new(py_obj_base_t, 1);
-    o->kind = O_CLASS;
-    o->u_class.locals = class_locals;
-    return o;
+    return mp_obj_new_class(class_locals);
 }
 
-py_obj_t py_builtin___import__(int n, py_obj_t *args) {
+mp_obj_t mp_builtin___import__(int n, mp_obj_t *args) {
     printf("import:\n");
     for (int i = 0; i < n; i++) {
     printf("  ");
-    py_obj_print(args[i]);
+    mp_obj_print(args[i]);
     printf("\n");
     }
-    return py_const_none;
+    return mp_const_none;
 }
 
-py_obj_t py_builtin___repl_print__(py_obj_t o) {
-    if (o != py_const_none) {
-        py_obj_print(o);
+mp_obj_t mp_builtin___repl_print__(mp_obj_t o) {
+    if (o != mp_const_none) {
+        mp_obj_print(o);
         printf("\n");
     }
-    return py_const_none;
+    return mp_const_none;
 }
 
-py_obj_t py_builtin_abs(py_obj_t o_in) {
-    if (IS_SMALL_INT(o_in)) {
-        py_small_int_t val = FROM_SMALL_INT(o_in);
+mp_obj_t mp_builtin_abs(mp_obj_t o_in) {
+    if (MP_OBJ_IS_SMALL_INT(o_in)) {
+        mp_small_int_t val = MP_OBJ_SMALL_INT_VALUE(o_in);
         if (val < 0) {
             val = -val;
         }
-        return TO_SMALL_INT(val);
+        return MP_OBJ_NEW_SMALL_INT(val);
 #if MICROPY_ENABLE_FLOAT
-    } else if (IS_O(o_in, O_FLOAT)) {
-        py_obj_base_t *o = o_in;
+    } else if (MP_OBJ_IS_TYPE(o_in, &float_type)) {
+        mp_float_t value = mp_obj_float_get(o_in);
         // TODO check for NaN etc
-        if (o->u_float < 0) {
-            return py_obj_new_float(-o->u_float);
+        if (value < 0) {
+            return mp_obj_new_float(-value);
         } else {
             return o_in;
         }
-    } else if (IS_O(o_in, O_COMPLEX)) {
-        py_obj_base_t *o = o_in;
-        return py_obj_new_float(machine_sqrt(o->u_complex.real*o->u_complex.real + o->u_complex.imag*o->u_complex.imag));
+    } else if (MP_OBJ_IS_TYPE(o_in, &complex_type)) {
+        mp_float_t real, imag;
+        mp_obj_complex_get(o_in, &real, &imag);
+        return mp_obj_new_float(machine_sqrt(real*real + imag*imag));
 #endif
     } else {
         assert(0);
-        return py_const_none;
+        return mp_const_none;
     }
 }
 
-py_obj_t py_builtin_all(py_obj_t o_in) {
-    py_obj_t iterable = rt_getiter(o_in);
-    py_obj_t item;
-    while ((item = rt_iternext(iterable)) != py_const_stop_iteration) {
+mp_obj_t mp_builtin_all(mp_obj_t o_in) {
+    mp_obj_t iterable = rt_getiter(o_in);
+    mp_obj_t item;
+    while ((item = rt_iternext(iterable)) != mp_const_stop_iteration) {
         if (!rt_is_true(item)) {
-            return py_const_false;
+            return mp_const_false;
         }
     }
-    return py_const_true;
+    return mp_const_true;
 }
 
-py_obj_t py_builtin_any(py_obj_t o_in) {
-    py_obj_t iterable = rt_getiter(o_in);
-    py_obj_t item;
-    while ((item = rt_iternext(iterable)) != py_const_stop_iteration) {
+mp_obj_t mp_builtin_any(mp_obj_t o_in) {
+    mp_obj_t iterable = rt_getiter(o_in);
+    mp_obj_t item;
+    while ((item = rt_iternext(iterable)) != mp_const_stop_iteration) {
         if (rt_is_true(item)) {
-            return py_const_true;
+            return mp_const_true;
         }
     }
-    return py_const_false;
+    return mp_const_false;
 }
 
-py_obj_t py_builtin_bool(int n_args, const py_obj_t *args) {
+mp_obj_t mp_builtin_bool(int n_args, const mp_obj_t *args) {
     switch (n_args) {
-        case 0: return py_const_false;
-        case 1: if (rt_is_true(args[0])) { return py_const_true; } else { return py_const_false; }
-        default: nlr_jump(py_obj_new_exception_2(rt_q_TypeError, "bool() takes at most 1 argument (%d given)", (void*)(machine_int_t)n_args, NULL));
+        case 0: return mp_const_false;
+        case 1: if (rt_is_true(args[0])) { return mp_const_true; } else { return mp_const_false; }
+        default: nlr_jump(mp_obj_new_exception_msg_1_arg(rt_q_TypeError, "bool() takes at most 1 argument (%d given)", (void*)(machine_int_t)n_args));
     }
 }
 
-py_obj_t py_builtin_callable(py_obj_t o_in) {
-    if (py_obj_is_callable(o_in)) {
-        return py_const_true;
+mp_obj_t mp_builtin_callable(mp_obj_t o_in) {
+    if (mp_obj_is_callable(o_in)) {
+        return mp_const_true;
     } else {
-        return py_const_false;
+        return mp_const_false;
     }
 }
 
 #if MICROPY_ENABLE_FLOAT
-py_obj_t py_builtin_complex(int n_args, const py_obj_t *args) {
+mp_obj_t mp_builtin_complex(int n_args, const mp_obj_t *args) {
     switch (n_args) {
         case 0:
-            return py_obj_new_complex(0, 0);
+            return mp_obj_new_complex(0, 0);
 
         case 1:
             // TODO allow string as first arg
-            if (IS_O(args[0], O_COMPLEX)) {
+            if (MP_OBJ_IS_TYPE(args[0], &complex_type)) {
                 return args[0];
             } else {
-                return py_obj_new_complex(py_obj_get_float(args[0]), 0);
+                return mp_obj_new_complex(mp_obj_get_float(args[0]), 0);
             }
 
         case 2:
         {
-            py_float_t real, imag;
-            if (IS_O(args[0], O_COMPLEX)) {
-                py_obj_get_complex(args[0], &real, &imag);
+            mp_float_t real, imag;
+            if (MP_OBJ_IS_TYPE(args[0], &complex_type)) {
+                mp_obj_get_complex(args[0], &real, &imag);
             } else {
-                real = py_obj_get_float(args[0]);
+                real = mp_obj_get_float(args[0]);
                 imag = 0;
             }
-            if (IS_O(args[1], O_COMPLEX)) {
-                py_float_t real2, imag2;
-                py_obj_get_complex(args[1], &real2, &imag2);
+            if (MP_OBJ_IS_TYPE(args[1], &complex_type)) {
+                mp_float_t real2, imag2;
+                mp_obj_get_complex(args[1], &real2, &imag2);
                 real -= imag2;
                 imag += real2;
             } else {
-                imag += py_obj_get_float(args[1]);
+                imag += mp_obj_get_float(args[1]);
             }
-            return py_obj_new_complex(real, imag);
+            return mp_obj_new_complex(real, imag);
         }
 
-        default: nlr_jump(py_obj_new_exception_2(rt_q_TypeError, "comlpex() takes at most 2 arguments (%d given)", (void*)(machine_int_t)n_args, NULL));
+        default: nlr_jump(mp_obj_new_exception_msg_1_arg(rt_q_TypeError, "comlpex() takes at most 2 arguments (%d given)", (void*)(machine_int_t)n_args));
     }
 }
 #endif
 
-py_obj_t py_builtin_chr(py_obj_t o_in) {
-    int ord = py_obj_get_int(o_in);
+mp_obj_t mp_builtin_chr(mp_obj_t o_in) {
+    int ord = mp_obj_get_int(o_in);
     if (0 <= ord && ord <= 0x10ffff) {
         char *str = m_new(char, 2);
         str[0] = ord;
         str[1] = '\0';
-        return py_obj_new_str(qstr_from_str_take(str));
+        return mp_obj_new_str(qstr_from_str_take(str));
     } else {
-        nlr_jump(py_obj_new_exception_2(rt_q_ValueError, "chr() arg not in range(0x110000)", NULL, NULL));
+        nlr_jump(mp_obj_new_exception_msg(rt_q_ValueError, "chr() arg not in range(0x110000)"));
     }
 }
 
-py_obj_t py_builtin_dict(void) {
+mp_obj_t mp_builtin_dict(void) {
     // TODO create from an iterable!
     return rt_build_map(0);
 }
 
-py_obj_t py_builtin_divmod(py_obj_t o1_in, py_obj_t o2_in) {
-    if (IS_SMALL_INT(o1_in) && IS_SMALL_INT(o2_in)) {
-        py_small_int_t i1 = FROM_SMALL_INT(o1_in);
-        py_small_int_t i2 = FROM_SMALL_INT(o2_in);
-        py_obj_t revs_args[2];
-        revs_args[1] = TO_SMALL_INT(i1 / i2);
-        revs_args[0] = TO_SMALL_INT(i1 % i2);
+mp_obj_t mp_builtin_divmod(mp_obj_t o1_in, mp_obj_t o2_in) {
+    if (MP_OBJ_IS_SMALL_INT(o1_in) && MP_OBJ_IS_SMALL_INT(o2_in)) {
+        mp_small_int_t i1 = MP_OBJ_SMALL_INT_VALUE(o1_in);
+        mp_small_int_t i2 = MP_OBJ_SMALL_INT_VALUE(o2_in);
+        mp_obj_t revs_args[2];
+        revs_args[1] = MP_OBJ_NEW_SMALL_INT(i1 / i2);
+        revs_args[0] = MP_OBJ_NEW_SMALL_INT(i1 % i2);
         return rt_build_tuple(2, revs_args);
     } else {
-        nlr_jump(py_obj_new_exception_2(rt_q_TypeError, "unsupported operand type(s) for divmod(): '%s' and '%s'", py_obj_get_type_str(o1_in), py_obj_get_type_str(o2_in)));
+        nlr_jump(mp_obj_new_exception_msg_2_args(rt_q_TypeError, "unsupported operand type(s) for divmod(): '%s' and '%s'", mp_obj_get_type_str(o1_in), mp_obj_get_type_str(o2_in)));
     }
 }
 
-py_obj_t py_builtin_hash(py_obj_t o_in) {
+static mp_obj_t mp_builtin_hash(mp_obj_t o_in) {
     // TODO hash will generally overflow small integer; can we safely truncate it?
-    return py_obj_new_int(py_obj_hash(o_in));
+    return mp_obj_new_int(mp_obj_hash(o_in));
 }
 
-py_obj_t py_builtin_iter(py_obj_t o_in) {
-    return rt_getiter(o_in);
-}
+MP_DEFINE_CONST_FUN_OBJ_1(mp_builtin_hash_obj, mp_builtin_hash);
 
-py_obj_t py_builtin_next(py_obj_t o_in) {
-    return rt_gen_instance_next(o_in);
+static mp_obj_t mp_builtin_iter(mp_obj_t o_in) {
+    return rt_getiter(o_in);
 }
 
-py_obj_t py_builtin_len(py_obj_t o_in) {
-    py_small_int_t len = 0;
-    if (IS_O(o_in, O_STR)) {
-        py_obj_base_t *o = o_in;
-        len = strlen(qstr_str(o->u_str));
-    } else if (IS_O(o_in, O_TUPLE) || IS_O(o_in, O_LIST)) {
-        py_obj_base_t *o = o_in;
-        len = o->u_tuple_list.len;
-    } else if (IS_O(o_in, O_MAP)) {
-        py_obj_base_t *o = o_in;
+MP_DEFINE_CONST_FUN_OBJ_1(mp_builtin_iter_obj, mp_builtin_iter);
+
+mp_obj_t mp_builtin_len(mp_obj_t o_in) {
+    mp_small_int_t len = 0;
+    if (MP_OBJ_IS_TYPE(o_in, &str_type)) {
+        len = strlen(qstr_str(mp_obj_str_get(o_in)));
+    } else if (MP_OBJ_IS_TYPE(o_in, &tuple_type)) {
+        uint seq_len;
+        mp_obj_t *seq_items;
+        mp_obj_tuple_get(o_in, &seq_len, &seq_items);
+        len = seq_len;
+    } else if (MP_OBJ_IS_TYPE(o_in, &list_type)) {
+        uint seq_len;
+        mp_obj_t *seq_items;
+        mp_obj_list_get(o_in, &seq_len, &seq_items);
+        len = seq_len;
+        /* TODO
+    } else if (MP_OBJ_IS_TYPE(o_in, &dict_type)) {
+        mp_obj_base_t *o = o_in;
         len = o->u_map.used;
+        */
     } else {
-        nlr_jump(py_obj_new_exception_2(rt_q_TypeError, "object of type '%s' has no len()", py_obj_get_type_str(o_in), NULL));
+        nlr_jump(mp_obj_new_exception_msg_1_arg(rt_q_TypeError, "object of type '%s' has no len()", mp_obj_get_type_str(o_in)));
     }
-    return TO_SMALL_INT(len);
+    return MP_OBJ_NEW_SMALL_INT(len);
 }
 
-py_obj_t py_builtin_list(int n_args, const py_obj_t *args) {
+mp_obj_t mp_builtin_list(int n_args, const mp_obj_t *args) {
     switch (n_args) {
         case 0: return rt_build_list(0, NULL);
         case 1:
         {
             // make list from iterable
-            py_obj_t iterable = rt_getiter(args[0]);
-            py_obj_t list = rt_build_list(0, NULL);
-            py_obj_t item;
-            while ((item = rt_iternext(iterable)) != py_const_stop_iteration) {
+            mp_obj_t iterable = rt_getiter(args[0]);
+            mp_obj_t list = rt_build_list(0, NULL);
+            mp_obj_t item;
+            while ((item = rt_iternext(iterable)) != mp_const_stop_iteration) {
                 rt_list_append(list, item);
             }
             return list;
         }
-        default: nlr_jump(py_obj_new_exception_2(rt_q_TypeError, "list() takes at most 1 argument (%d given)", (void*)(machine_int_t)n_args, NULL));
+        default: nlr_jump(mp_obj_new_exception_msg_1_arg(rt_q_TypeError, "list() takes at most 1 argument (%d given)", (void*)(machine_int_t)n_args));
     }
 }
 
-py_obj_t py_builtin_max(int n_args, const py_obj_t *args) {
+mp_obj_t mp_builtin_max(int n_args, const mp_obj_t *args) {
     if (n_args == 1) {
         // given an iterable
-        py_obj_t iterable = rt_getiter(args[0]);
-        py_obj_t max_obj = NULL;
-        py_obj_t item;
-        while ((item = rt_iternext(iterable)) != py_const_stop_iteration) {
-            if (max_obj == NULL || py_obj_less(max_obj, item)) {
+        mp_obj_t iterable = rt_getiter(args[0]);
+        mp_obj_t max_obj = NULL;
+        mp_obj_t item;
+        while ((item = rt_iternext(iterable)) != mp_const_stop_iteration) {
+            if (max_obj == NULL || mp_obj_less(max_obj, item)) {
                 max_obj = item;
             }
         }
         if (max_obj == NULL) {
-            nlr_jump(py_obj_new_exception_2(rt_q_ValueError, "max() arg is an empty sequence", NULL, NULL));
+            nlr_jump(mp_obj_new_exception_msg(rt_q_ValueError, "max() arg is an empty sequence"));
         }
         return max_obj;
     } else {
         // given many args
-        py_obj_t max_obj = args[0];
+        mp_obj_t max_obj = args[0];
         for (int i = 1; i < n_args; i++) {
-            if (py_obj_less(max_obj, args[i])) {
+            if (mp_obj_less(max_obj, args[i])) {
                 max_obj = args[i];
             }
         }
@@ -261,26 +266,26 @@ py_obj_t py_builtin_max(int n_args, const py_obj_t *args) {
     }
 }
 
-py_obj_t py_builtin_min(int n_args, const py_obj_t *args) {
+mp_obj_t mp_builtin_min(int n_args, const mp_obj_t *args) {
     if (n_args == 1) {
         // given an iterable
-        py_obj_t iterable = rt_getiter(args[0]);
-        py_obj_t min_obj = NULL;
-        py_obj_t item;
-        while ((item = rt_iternext(iterable)) != py_const_stop_iteration) {
-            if (min_obj == NULL || py_obj_less(item, min_obj)) {
+        mp_obj_t iterable = rt_getiter(args[0]);
+        mp_obj_t min_obj = NULL;
+        mp_obj_t item;
+        while ((item = rt_iternext(iterable)) != mp_const_stop_iteration) {
+            if (min_obj == NULL || mp_obj_less(item, min_obj)) {
                 min_obj = item;
             }
         }
         if (min_obj == NULL) {
-            nlr_jump(py_obj_new_exception_2(rt_q_ValueError, "min() arg is an empty sequence", NULL, NULL));
+            nlr_jump(mp_obj_new_exception_msg(rt_q_ValueError, "min() arg is an empty sequence"));
         }
         return min_obj;
     } else {
         // given many args
-        py_obj_t min_obj = args[0];
+        mp_obj_t min_obj = args[0];
         for (int i = 1; i < n_args; i++) {
-            if (py_obj_less(args[i], min_obj)) {
+            if (mp_obj_less(args[i], min_obj)) {
                 min_obj = args[i];
             }
         }
@@ -288,59 +293,85 @@ py_obj_t py_builtin_min(int n_args, const py_obj_t *args) {
     }
 }
 
-py_obj_t py_builtin_ord(py_obj_t o_in) {
-    const char *str = qstr_str(py_obj_get_qstr(o_in));
+static mp_obj_t mp_builtin_next(mp_obj_t o_in) {
+    return mp_obj_gen_instance_next(o_in);
+}
+
+MP_DEFINE_CONST_FUN_OBJ_1(mp_builtin_next_obj, mp_builtin_next);
+
+mp_obj_t mp_builtin_ord(mp_obj_t o_in) {
+    const char *str = qstr_str(mp_obj_get_qstr(o_in));
     if (strlen(str) == 1) {
-        return py_obj_new_int(str[0]);
+        return mp_obj_new_int(str[0]);
     } else {
-        nlr_jump(py_obj_new_exception_2(rt_q_TypeError, "ord() expected a character, but string of length %d found", (void*)(machine_int_t)strlen(str), NULL));
+        nlr_jump(mp_obj_new_exception_msg_1_arg(rt_q_TypeError, "ord() expected a character, but string of length %d found", (void*)(machine_int_t)strlen(str)));
     }
 }
 
-py_obj_t py_builtin_pow(int n_args, const py_obj_t *args) {
+mp_obj_t mp_builtin_pow(int n_args, const mp_obj_t *args) {
     switch (n_args) {
         case 2: return rt_binary_op(RT_BINARY_OP_POWER, args[0], args[1]);
         case 3: return rt_binary_op(RT_BINARY_OP_MODULO, rt_binary_op(RT_BINARY_OP_POWER, args[0], args[1]), args[2]); // TODO optimise...
-        default: nlr_jump(py_obj_new_exception_2(rt_q_TypeError, "pow expected at most 3 arguments, got %d", (void*)(machine_int_t)n_args, NULL));
+        default: nlr_jump(mp_obj_new_exception_msg_1_arg(rt_q_TypeError, "pow expected at most 3 arguments, got %d", (void*)(machine_int_t)n_args));
     }
 }
 
-py_obj_t py_builtin_print(int n_args, const py_obj_t *args) {
+mp_obj_t mp_builtin_print(int n_args, const mp_obj_t *args) {
     for (int i = 0; i < n_args; i++) {
         if (i > 0) {
             printf(" ");
         }
-        if (IS_O(args[i], O_STR)) {
+        if (MP_OBJ_IS_TYPE(args[i], &str_type)) {
             // special case, print string raw
-            printf("%s", qstr_str(((py_obj_base_t*)args[i])->u_str));
+            printf("%s", qstr_str(mp_obj_str_get(args[i])));
         } else {
             // print the object Python style
-            py_obj_print(args[i]);
+            mp_obj_print(args[i]);
         }
     }
     printf("\n");
-    return py_const_none;
+    return mp_const_none;
 }
 
-py_obj_t py_builtin_range(int n_args, const py_obj_t *args) {
+mp_obj_t mp_builtin_range(int n_args, const mp_obj_t *args) {
     switch (n_args) {
-        case 1: return py_obj_new_range(0, py_obj_get_int(args[0]), 1);
-        case 2: return py_obj_new_range(py_obj_get_int(args[0]), py_obj_get_int(args[1]), 1);
-        case 3: return py_obj_new_range(py_obj_get_int(args[0]), py_obj_get_int(args[1]), py_obj_get_int(args[2]));
-        default: nlr_jump(py_obj_new_exception_2(rt_q_TypeError, "range expected at most 3 arguments, got %d", (void*)(machine_int_t)n_args, NULL));
+        case 1: return mp_obj_new_range(0, mp_obj_get_int(args[0]), 1);
+        case 2: return mp_obj_new_range(mp_obj_get_int(args[0]), mp_obj_get_int(args[1]), 1);
+        case 3: return mp_obj_new_range(mp_obj_get_int(args[0]), mp_obj_get_int(args[1]), mp_obj_get_int(args[2]));
+        default: nlr_jump(mp_obj_new_exception_msg_1_arg(rt_q_TypeError, "range expected at most 3 arguments, got %d", (void*)(machine_int_t)n_args));
+    }
+}
+
+static mp_obj_t mp_builtin_set(int n_args, const mp_obj_t *args) {
+    assert(0 <= n_args && n_args <= 1);
+
+    if (n_args == 0) {
+        // return a new, empty set
+        return mp_obj_new_set(0, NULL);
+    } else {
+        // 1 argument, an iterable from which we make a new set
+        mp_obj_t set = mp_obj_new_set(0, NULL);
+        mp_obj_t iterable = rt_getiter(args[0]);
+        mp_obj_t item;
+        while ((item = rt_iternext(iterable)) != mp_const_stop_iteration) {
+            mp_obj_set_store(set, item);
+        }
+        return set;
     }
 }
 
-py_obj_t py_builtin_sum(int n_args, const py_obj_t *args) {
-    py_obj_t value;
+MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_set_obj, 0, 1, mp_builtin_set);
+
+mp_obj_t mp_builtin_sum(int n_args, const mp_obj_t *args) {
+    mp_obj_t value;
     switch (n_args) {
-        case 1: value = py_obj_new_int(0); break;
+        case 1: value = mp_obj_new_int(0); break;
         case 2: value = args[1]; break;
-        default: nlr_jump(py_obj_new_exception_2(rt_q_TypeError, "sum expected at most 2 arguments, got %d", (void*)(machine_int_t)n_args, NULL));
+        default: nlr_jump(mp_obj_new_exception_msg_1_arg(rt_q_TypeError, "sum expected at most 2 arguments, got %d", (void*)(machine_int_t)n_args));
     }
-    py_obj_t iterable = rt_getiter(args[0]);
-    py_obj_t item;
-    while ((item = rt_iternext(iterable)) != py_const_stop_iteration) {
+    mp_obj_t iterable = rt_getiter(args[0]);
+    mp_obj_t item;
+    while ((item = rt_iternext(iterable)) != mp_const_stop_iteration) {
         value = rt_binary_op(RT_BINARY_OP_ADD, value, item);
     }
     return value;
diff --git a/py/builtin.h b/py/builtin.h
index 0a0470a842de74ff79aebb3edc161dfd9637d9a2..c164a76611054500eb3a0749dbf92b3e1bbd17e8 100644
--- a/py/builtin.h
+++ b/py/builtin.h
@@ -1,24 +1,27 @@
-py_obj_t py_builtin___build_class__(py_obj_t o_class_fun, py_obj_t o_class_name);
-py_obj_t py_builtin___import__(int n, py_obj_t *args);
-py_obj_t py_builtin___repl_print__(py_obj_t o);
-py_obj_t py_builtin_abs(py_obj_t o_in);
-py_obj_t py_builtin_all(py_obj_t o_in);
-py_obj_t py_builtin_any(py_obj_t o_in);
-py_obj_t py_builtin_bool(int n_args, const py_obj_t *args);
-py_obj_t py_builtin_callable(py_obj_t o_in);
-py_obj_t py_builtin_complex(int n_args, const py_obj_t *args);
-py_obj_t py_builtin_chr(py_obj_t o_in);
-py_obj_t py_builtin_dict(void);
-py_obj_t py_builtin_divmod(py_obj_t o1_in, py_obj_t o2_in);
-py_obj_t py_builtin_hash(py_obj_t o_in);
-py_obj_t py_builtin_iter(py_obj_t o_in);
-py_obj_t py_builtin_len(py_obj_t o_in);
-py_obj_t py_builtin_list(int n_args, const py_obj_t *args);
-py_obj_t py_builtin_max(int n_args, const py_obj_t *args);
-py_obj_t py_builtin_min(int n_args, const py_obj_t *args);
-py_obj_t py_builtin_next(py_obj_t o_in);
-py_obj_t py_builtin_ord(py_obj_t o_in);
-py_obj_t py_builtin_pow(int n_args, const py_obj_t *args);
-py_obj_t py_builtin_print(int n_args, const py_obj_t *args);
-py_obj_t py_builtin_range(int n_args, const py_obj_t *args);
-py_obj_t py_builtin_sum(int n_args, const py_obj_t *args);
+// TODO convert all these to objects using MP_DECLARE and MP_DEFINE
+
+mp_obj_t mp_builtin___build_class__(mp_obj_t o_class_fun, mp_obj_t o_class_name);
+mp_obj_t mp_builtin___import__(int n, mp_obj_t *args);
+mp_obj_t mp_builtin___repl_print__(mp_obj_t o);
+mp_obj_t mp_builtin_abs(mp_obj_t o_in);
+mp_obj_t mp_builtin_all(mp_obj_t o_in);
+mp_obj_t mp_builtin_any(mp_obj_t o_in);
+mp_obj_t mp_builtin_bool(int n_args, const mp_obj_t *args);
+mp_obj_t mp_builtin_callable(mp_obj_t o_in);
+mp_obj_t mp_builtin_complex(int n_args, const mp_obj_t *args);
+mp_obj_t mp_builtin_chr(mp_obj_t o_in);
+mp_obj_t mp_builtin_dict(void);
+mp_obj_t mp_builtin_divmod(mp_obj_t o1_in, mp_obj_t o2_in);
+MP_DECLARE_CONST_FUN_OBJ(mp_builtin_hash_obj);
+MP_DECLARE_CONST_FUN_OBJ(mp_builtin_iter_obj);
+mp_obj_t mp_builtin_len(mp_obj_t o_in);
+mp_obj_t mp_builtin_list(int n_args, const mp_obj_t *args);
+mp_obj_t mp_builtin_max(int n_args, const mp_obj_t *args);
+mp_obj_t mp_builtin_min(int n_args, const mp_obj_t *args);
+MP_DECLARE_CONST_FUN_OBJ(mp_builtin_next_obj);
+mp_obj_t mp_builtin_ord(mp_obj_t o_in);
+mp_obj_t mp_builtin_pow(int n_args, const mp_obj_t *args);
+mp_obj_t mp_builtin_print(int n_args, const mp_obj_t *args);
+mp_obj_t mp_builtin_range(int n_args, const mp_obj_t *args);
+MP_DECLARE_CONST_FUN_OBJ(mp_builtin_set_obj);
+mp_obj_t mp_builtin_sum(int n_args, const mp_obj_t *args);
diff --git a/py/compile.c b/py/compile.c
index 42bac7d28acaea7d7472e46dd3eb97b59b11dd4a..38f14155db484ffc55399123a5e78140d39dce06 100644
--- a/py/compile.c
+++ b/py/compile.c
@@ -6,12 +6,12 @@
 #include <assert.h>
 
 #include "misc.h"
-#include "mpyconfig.h"
+#include "mpconfig.h"
 #include "lexer.h"
 #include "parse.h"
 #include "scope.h"
 #include "compile.h"
-#include "runtime.h"
+#include "runtime0.h"
 #include "emit.h"
 
 // TODO need to mangle __attr names
@@ -78,28 +78,28 @@ typedef struct _compiler_t {
     const emit_inline_asm_method_table_t *emit_inline_asm_method_table;   // current emit method table for inline asm
 } compiler_t;
 
-py_parse_node_t fold_constants(py_parse_node_t pn) {
-    if (PY_PARSE_NODE_IS_STRUCT(pn)) {
-        py_parse_node_struct_t *pns = (py_parse_node_struct_t*)pn;
-        int n = PY_PARSE_NODE_STRUCT_NUM_NODES(pns);
+mp_parse_node_t fold_constants(mp_parse_node_t pn) {
+    if (MP_PARSE_NODE_IS_STRUCT(pn)) {
+        mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)pn;
+        int n = MP_PARSE_NODE_STRUCT_NUM_NODES(pns);
 
         // fold arguments first
         for (int i = 0; i < n; i++) {
             pns->nodes[i] = fold_constants(pns->nodes[i]);
         }
 
-        switch (PY_PARSE_NODE_STRUCT_KIND(pns)) {
+        switch (MP_PARSE_NODE_STRUCT_KIND(pns)) {
             case PN_shift_expr:
-                if (n == 3 && PY_PARSE_NODE_IS_SMALL_INT(pns->nodes[0]) && PY_PARSE_NODE_IS_SMALL_INT(pns->nodes[2])) {
-                    int arg0 = PY_PARSE_NODE_LEAF_ARG(pns->nodes[0]);
-                    int arg1 = PY_PARSE_NODE_LEAF_ARG(pns->nodes[2]);
-                    if (PY_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[1], PY_TOKEN_OP_DBL_LESS)) {
+                if (n == 3 && MP_PARSE_NODE_IS_SMALL_INT(pns->nodes[0]) && MP_PARSE_NODE_IS_SMALL_INT(pns->nodes[2])) {
+                    int arg0 = MP_PARSE_NODE_LEAF_ARG(pns->nodes[0]);
+                    int arg1 = MP_PARSE_NODE_LEAF_ARG(pns->nodes[2]);
+                    if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[1], MP_TOKEN_OP_DBL_LESS)) {
 #if MICROPY_EMIT_CPYTHON
                         // can overflow; enabled only to compare with CPython
-                        pn = py_parse_node_new_leaf(PY_PARSE_NODE_SMALL_INT, arg0 << arg1);
+                        pn = mp_parse_node_new_leaf(MP_PARSE_NODE_SMALL_INT, arg0 << arg1);
 #endif
-                    } else if (PY_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[1], PY_TOKEN_OP_DBL_MORE)) {
-                        pn = py_parse_node_new_leaf(PY_PARSE_NODE_SMALL_INT, arg0 >> arg1);
+                    } else if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[1], MP_TOKEN_OP_DBL_MORE)) {
+                        pn = mp_parse_node_new_leaf(MP_PARSE_NODE_SMALL_INT, arg0 >> arg1);
                     } else {
                         // shouldn't happen
                         assert(0);
@@ -109,42 +109,42 @@ py_parse_node_t fold_constants(py_parse_node_t pn) {
 
             case PN_arith_expr:
                 // overflow checking here relies on SMALL_INT being strictly smaller than machine_int_t
-                if (n == 3 && PY_PARSE_NODE_IS_SMALL_INT(pns->nodes[0]) && PY_PARSE_NODE_IS_SMALL_INT(pns->nodes[2])) {
-                    machine_int_t arg0 = PY_PARSE_NODE_LEAF_ARG(pns->nodes[0]);
-                    machine_int_t arg1 = PY_PARSE_NODE_LEAF_ARG(pns->nodes[2]);
+                if (n == 3 && MP_PARSE_NODE_IS_SMALL_INT(pns->nodes[0]) && MP_PARSE_NODE_IS_SMALL_INT(pns->nodes[2])) {
+                    machine_int_t arg0 = MP_PARSE_NODE_LEAF_ARG(pns->nodes[0]);
+                    machine_int_t arg1 = MP_PARSE_NODE_LEAF_ARG(pns->nodes[2]);
                     machine_int_t res;
-                    if (PY_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[1], PY_TOKEN_OP_PLUS)) {
+                    if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[1], MP_TOKEN_OP_PLUS)) {
                         res = arg0 + arg1;
-                    } else if (PY_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[1], PY_TOKEN_OP_MINUS)) {
+                    } else if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[1], MP_TOKEN_OP_MINUS)) {
                         res = arg0 - arg1;
                     } else {
                         // shouldn't happen
                         assert(0);
                         res = 0;
                     }
-                    if (PY_FIT_SMALL_INT(res)) {
-                        pn = py_parse_node_new_leaf(PY_PARSE_NODE_SMALL_INT, res);
+                    if (MP_FIT_SMALL_INT(res)) {
+                        pn = mp_parse_node_new_leaf(MP_PARSE_NODE_SMALL_INT, res);
                     }
                 }
                 break;
 
             case PN_term:
-                if (n == 3 && PY_PARSE_NODE_IS_SMALL_INT(pns->nodes[0]) && PY_PARSE_NODE_IS_SMALL_INT(pns->nodes[2])) {
-                    int arg0 = PY_PARSE_NODE_LEAF_ARG(pns->nodes[0]);
-                    int arg1 = PY_PARSE_NODE_LEAF_ARG(pns->nodes[2]);
-                    if (PY_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[1], PY_TOKEN_OP_STAR)) {
+                if (n == 3 && MP_PARSE_NODE_IS_SMALL_INT(pns->nodes[0]) && MP_PARSE_NODE_IS_SMALL_INT(pns->nodes[2])) {
+                    int arg0 = MP_PARSE_NODE_LEAF_ARG(pns->nodes[0]);
+                    int arg1 = MP_PARSE_NODE_LEAF_ARG(pns->nodes[2]);
+                    if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[1], MP_TOKEN_OP_STAR)) {
 #if MICROPY_EMIT_CPYTHON
                         // can overflow; enabled only to compare with CPython
-                        pn = py_parse_node_new_leaf(PY_PARSE_NODE_SMALL_INT, arg0 * arg1);
+                        pn = mp_parse_node_new_leaf(MP_PARSE_NODE_SMALL_INT, arg0 * arg1);
 #endif
-                    } else if (PY_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[1], PY_TOKEN_OP_SLASH)) {
+                    } else if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[1], MP_TOKEN_OP_SLASH)) {
                         ; // pass
-                    } else if (PY_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[1], PY_TOKEN_OP_PERCENT)) {
+                    } else if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[1], MP_TOKEN_OP_PERCENT)) {
                         // XXX implement this properly as Python's % operator acts differently to C's
-                        pn = py_parse_node_new_leaf(PY_PARSE_NODE_SMALL_INT, arg0 % arg1);
-                    } else if (PY_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[1], PY_TOKEN_OP_DBL_SLASH)) {
+                        pn = mp_parse_node_new_leaf(MP_PARSE_NODE_SMALL_INT, arg0 % arg1);
+                    } else if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[1], MP_TOKEN_OP_DBL_SLASH)) {
                         // XXX implement this properly as Python's // operator acts differently to C's
-                        pn = py_parse_node_new_leaf(PY_PARSE_NODE_SMALL_INT, arg0 / arg1);
+                        pn = mp_parse_node_new_leaf(MP_PARSE_NODE_SMALL_INT, arg0 / arg1);
                     } else {
                         // shouldn't happen
                         assert(0);
@@ -153,14 +153,14 @@ py_parse_node_t fold_constants(py_parse_node_t pn) {
                 break;
 
             case PN_factor_2:
-                if (PY_PARSE_NODE_IS_SMALL_INT(pns->nodes[1])) {
-                    machine_int_t arg = PY_PARSE_NODE_LEAF_ARG(pns->nodes[1]);
-                    if (PY_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[0], PY_TOKEN_OP_PLUS)) {
-                        pn = py_parse_node_new_leaf(PY_PARSE_NODE_SMALL_INT, arg);
-                    } else if (PY_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[0], PY_TOKEN_OP_MINUS)) {
-                        pn = py_parse_node_new_leaf(PY_PARSE_NODE_SMALL_INT, -arg);
-                    } else if (PY_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[0], PY_TOKEN_OP_TILDE)) {
-                        pn = py_parse_node_new_leaf(PY_PARSE_NODE_SMALL_INT, ~arg);
+                if (MP_PARSE_NODE_IS_SMALL_INT(pns->nodes[1])) {
+                    machine_int_t arg = MP_PARSE_NODE_LEAF_ARG(pns->nodes[1]);
+                    if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[0], MP_TOKEN_OP_PLUS)) {
+                        pn = mp_parse_node_new_leaf(MP_PARSE_NODE_SMALL_INT, arg);
+                    } else if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[0], MP_TOKEN_OP_MINUS)) {
+                        pn = mp_parse_node_new_leaf(MP_PARSE_NODE_SMALL_INT, -arg);
+                    } else if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[0], MP_TOKEN_OP_TILDE)) {
+                        pn = mp_parse_node_new_leaf(MP_PARSE_NODE_SMALL_INT, ~arg);
                     } else {
                         // shouldn't happen
                         assert(0);
@@ -171,17 +171,17 @@ py_parse_node_t fold_constants(py_parse_node_t pn) {
 #if MICROPY_EMIT_CPYTHON
             case PN_power:
                 // can overflow; enabled only to compare with CPython
-                if (PY_PARSE_NODE_IS_SMALL_INT(pns->nodes[0]) && PY_PARSE_NODE_IS_NULL(pns->nodes[1]) && !PY_PARSE_NODE_IS_NULL(pns->nodes[2])) {
-                    py_parse_node_struct_t* pns2 = (py_parse_node_struct_t*)pns->nodes[2];
-                    if (PY_PARSE_NODE_IS_SMALL_INT(pns2->nodes[0])) {
-                        int power = PY_PARSE_NODE_LEAF_ARG(pns2->nodes[0]);
+                if (MP_PARSE_NODE_IS_SMALL_INT(pns->nodes[0]) && MP_PARSE_NODE_IS_NULL(pns->nodes[1]) && !MP_PARSE_NODE_IS_NULL(pns->nodes[2])) {
+                    mp_parse_node_struct_t* pns2 = (mp_parse_node_struct_t*)pns->nodes[2];
+                    if (MP_PARSE_NODE_IS_SMALL_INT(pns2->nodes[0])) {
+                        int power = MP_PARSE_NODE_LEAF_ARG(pns2->nodes[0]);
                         if (power >= 0) {
                             int ans = 1;
-                            int base = PY_PARSE_NODE_LEAF_ARG(pns->nodes[0]);
+                            int base = MP_PARSE_NODE_LEAF_ARG(pns->nodes[0]);
                             for (; power > 0; power--) {
                                 ans *= base;
                             }
-                            pn = py_parse_node_new_leaf(PY_PARSE_NODE_SMALL_INT, ans);
+                            pn = mp_parse_node_new_leaf(MP_PARSE_NODE_SMALL_INT, ans);
                         }
                     }
                 }
@@ -193,13 +193,13 @@ py_parse_node_t fold_constants(py_parse_node_t pn) {
     return pn;
 }
 
-void compile_node(compiler_t *comp, py_parse_node_t pn);
+void compile_node(compiler_t *comp, mp_parse_node_t pn);
 
 static int comp_next_label(compiler_t *comp) {
     return comp->next_label++;
 }
 
-static scope_t *scope_new_and_link(compiler_t *comp, scope_kind_t kind, py_parse_node_t pn, uint emit_options) {
+static scope_t *scope_new_and_link(compiler_t *comp, scope_kind_t kind, mp_parse_node_t pn, uint emit_options) {
     scope_t *scope = scope_new(kind, pn, rt_get_unique_code_id(kind == SCOPE_MODULE), emit_options);
     scope->parent = comp->scope_cur;
     scope->next = NULL;
@@ -215,68 +215,68 @@ static scope_t *scope_new_and_link(compiler_t *comp, scope_kind_t kind, py_parse
     return scope;
 }
 
-static int list_len(py_parse_node_t pn, int pn_kind) {
-    if (PY_PARSE_NODE_IS_NULL(pn)) {
+static int list_len(mp_parse_node_t pn, int pn_kind) {
+    if (MP_PARSE_NODE_IS_NULL(pn)) {
         return 0;
-    } else if (PY_PARSE_NODE_IS_LEAF(pn)) {
+    } else if (MP_PARSE_NODE_IS_LEAF(pn)) {
         return 1;
     } else {
-        py_parse_node_struct_t *pns = (py_parse_node_struct_t*)pn;
-        if (PY_PARSE_NODE_STRUCT_KIND(pns) != pn_kind) {
+        mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)pn;
+        if (MP_PARSE_NODE_STRUCT_KIND(pns) != pn_kind) {
             return 1;
         } else {
-            return PY_PARSE_NODE_STRUCT_NUM_NODES(pns);
+            return MP_PARSE_NODE_STRUCT_NUM_NODES(pns);
         }
     }
 }
 
-static void apply_to_single_or_list(compiler_t *comp, py_parse_node_t pn, int pn_list_kind, void (*f)(compiler_t*, py_parse_node_t)) {
-    if (PY_PARSE_NODE_IS_STRUCT(pn) && PY_PARSE_NODE_STRUCT_KIND((py_parse_node_struct_t*)pn) == pn_list_kind) {
-        py_parse_node_struct_t *pns = (py_parse_node_struct_t*)pn;
-        int num_nodes = PY_PARSE_NODE_STRUCT_NUM_NODES(pns);
+static void apply_to_single_or_list(compiler_t *comp, mp_parse_node_t pn, int pn_list_kind, void (*f)(compiler_t*, mp_parse_node_t)) {
+    if (MP_PARSE_NODE_IS_STRUCT(pn) && MP_PARSE_NODE_STRUCT_KIND((mp_parse_node_struct_t*)pn) == pn_list_kind) {
+        mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)pn;
+        int num_nodes = MP_PARSE_NODE_STRUCT_NUM_NODES(pns);
         for (int i = 0; i < num_nodes; i++) {
             f(comp, pns->nodes[i]);
         }
-    } else if (!PY_PARSE_NODE_IS_NULL(pn)) {
+    } else if (!MP_PARSE_NODE_IS_NULL(pn)) {
         f(comp, pn);
     }
 }
 
-static int list_get(py_parse_node_t *pn, int pn_kind, py_parse_node_t **nodes) {
-    if (PY_PARSE_NODE_IS_NULL(*pn)) {
+static int list_get(mp_parse_node_t *pn, int pn_kind, mp_parse_node_t **nodes) {
+    if (MP_PARSE_NODE_IS_NULL(*pn)) {
         *nodes = NULL;
         return 0;
-    } else if (PY_PARSE_NODE_IS_LEAF(*pn)) {
+    } else if (MP_PARSE_NODE_IS_LEAF(*pn)) {
         *nodes = pn;
         return 1;
     } else {
-        py_parse_node_struct_t *pns = (py_parse_node_struct_t*)(*pn);
-        if (PY_PARSE_NODE_STRUCT_KIND(pns) != pn_kind) {
+        mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)(*pn);
+        if (MP_PARSE_NODE_STRUCT_KIND(pns) != pn_kind) {
             *nodes = pn;
             return 1;
         } else {
             *nodes = pns->nodes;
-            return PY_PARSE_NODE_STRUCT_NUM_NODES(pns);
+            return MP_PARSE_NODE_STRUCT_NUM_NODES(pns);
         }
     }
 }
 
-void compile_do_nothing(compiler_t *comp, py_parse_node_struct_t *pns) {
+void compile_do_nothing(compiler_t *comp, mp_parse_node_struct_t *pns) {
 }
 
-void compile_generic_all_nodes(compiler_t *comp, py_parse_node_struct_t *pns) {
-    int num_nodes = PY_PARSE_NODE_STRUCT_NUM_NODES(pns);
+void compile_generic_all_nodes(compiler_t *comp, mp_parse_node_struct_t *pns) {
+    int num_nodes = MP_PARSE_NODE_STRUCT_NUM_NODES(pns);
     for (int i = 0; i < num_nodes; i++) {
         compile_node(comp, pns->nodes[i]);
     }
 }
 
 #if MICROPY_EMIT_CPYTHON
-static bool cpython_c_tuple_is_const(py_parse_node_t pn) {
-    if (!PY_PARSE_NODE_IS_LEAF(pn)) {
+static bool cpython_c_tuple_is_const(mp_parse_node_t pn) {
+    if (!MP_PARSE_NODE_IS_LEAF(pn)) {
         return false;
     }
-    if (PY_PARSE_NODE_IS_ID(pn)) {
+    if (MP_PARSE_NODE_IS_ID(pn)) {
         return false;
     }
     return true;
@@ -322,21 +322,21 @@ static void cpython_c_print_quoted_str(vstr_t *vstr, qstr qstr, bool bytes) {
     }
 }
 
-static void cpython_c_tuple_emit_const(compiler_t *comp, py_parse_node_t pn, vstr_t *vstr) {
-    assert(PY_PARSE_NODE_IS_LEAF(pn));
-    int arg = PY_PARSE_NODE_LEAF_ARG(pn);
-    switch (PY_PARSE_NODE_LEAF_KIND(pn)) {
-        case PY_PARSE_NODE_ID: assert(0);
-        case PY_PARSE_NODE_SMALL_INT: vstr_printf(vstr, "%d", arg); break;
-        case PY_PARSE_NODE_INTEGER: vstr_printf(vstr, "%s", qstr_str(arg)); break;
-        case PY_PARSE_NODE_DECIMAL: vstr_printf(vstr, "%s", qstr_str(arg)); break;
-        case PY_PARSE_NODE_STRING: cpython_c_print_quoted_str(vstr, arg, false); break;
-        case PY_PARSE_NODE_BYTES: cpython_c_print_quoted_str(vstr, arg, true); break;
-        case PY_PARSE_NODE_TOKEN:
+static void cpython_c_tuple_emit_const(compiler_t *comp, mp_parse_node_t pn, vstr_t *vstr) {
+    assert(MP_PARSE_NODE_IS_LEAF(pn));
+    int arg = MP_PARSE_NODE_LEAF_ARG(pn);
+    switch (MP_PARSE_NODE_LEAF_KIND(pn)) {
+        case MP_PARSE_NODE_ID: assert(0);
+        case MP_PARSE_NODE_SMALL_INT: vstr_printf(vstr, "%d", arg); break;
+        case MP_PARSE_NODE_INTEGER: vstr_printf(vstr, "%s", qstr_str(arg)); break;
+        case MP_PARSE_NODE_DECIMAL: vstr_printf(vstr, "%s", qstr_str(arg)); break;
+        case MP_PARSE_NODE_STRING: cpython_c_print_quoted_str(vstr, arg, false); break;
+        case MP_PARSE_NODE_BYTES: cpython_c_print_quoted_str(vstr, arg, true); break;
+        case MP_PARSE_NODE_TOKEN:
             switch (arg) {
-                case PY_TOKEN_KW_FALSE: vstr_printf(vstr, "False"); break;
-                case PY_TOKEN_KW_NONE: vstr_printf(vstr, "None"); break;
-                case PY_TOKEN_KW_TRUE: vstr_printf(vstr, "True"); break;
+                case MP_TOKEN_KW_FALSE: vstr_printf(vstr, "False"); break;
+                case MP_TOKEN_KW_NONE: vstr_printf(vstr, "None"); break;
+                case MP_TOKEN_KW_TRUE: vstr_printf(vstr, "True"); break;
                 default: assert(0);
             }
             break;
@@ -344,14 +344,14 @@ static void cpython_c_tuple_emit_const(compiler_t *comp, py_parse_node_t pn, vst
     }
 }
 
-static void cpython_c_tuple(compiler_t *comp, py_parse_node_t pn, py_parse_node_struct_t *pns_list) {
+static void cpython_c_tuple(compiler_t *comp, mp_parse_node_t pn, mp_parse_node_struct_t *pns_list) {
     int n = 0;
     if (pns_list != NULL) {
-        n = PY_PARSE_NODE_STRUCT_NUM_NODES(pns_list);
+        n = MP_PARSE_NODE_STRUCT_NUM_NODES(pns_list);
     }
     int total = n;
     bool is_const = true;
-    if (!PY_PARSE_NODE_IS_NULL(pn)) {
+    if (!MP_PARSE_NODE_IS_NULL(pn)) {
         total += 1;
         if (!cpython_c_tuple_is_const(pn)) {
             is_const = false;
@@ -367,7 +367,7 @@ static void cpython_c_tuple(compiler_t *comp, py_parse_node_t pn, py_parse_node_
         bool need_comma = false;
         vstr_t *vstr = vstr_new();
         vstr_printf(vstr, "(");
-        if (!PY_PARSE_NODE_IS_NULL(pn)) {
+        if (!MP_PARSE_NODE_IS_NULL(pn)) {
             cpython_c_tuple_emit_const(comp, pn, vstr);
             need_comma = true;
         }
@@ -386,7 +386,7 @@ static void cpython_c_tuple(compiler_t *comp, py_parse_node_t pn, py_parse_node_
         EMIT(load_const_verbatim_str, vstr_str(vstr));
         vstr_free(vstr);
     } else {
-        if (!PY_PARSE_NODE_IS_NULL(pn)) {
+        if (!MP_PARSE_NODE_IS_NULL(pn)) {
             compile_node(comp, pn);
         }
         for (int i = 0; i < n; i++) {
@@ -398,17 +398,17 @@ static void cpython_c_tuple(compiler_t *comp, py_parse_node_t pn, py_parse_node_
 #endif
 
 // funnelling all tuple creations through this function is purely so we can optionally agree with CPython
-void c_tuple(compiler_t *comp, py_parse_node_t pn, py_parse_node_struct_t *pns_list) {
+void c_tuple(compiler_t *comp, mp_parse_node_t pn, mp_parse_node_struct_t *pns_list) {
 #if MICROPY_EMIT_CPYTHON
     cpython_c_tuple(comp, pn, pns_list);
 #else
     int total = 0;
-    if (!PY_PARSE_NODE_IS_NULL(pn)) {
+    if (!MP_PARSE_NODE_IS_NULL(pn)) {
         compile_node(comp, pn);
         total += 1;
     }
     if (pns_list != NULL) {
-        int n = PY_PARSE_NODE_STRUCT_NUM_NODES(pns_list);
+        int n = MP_PARSE_NODE_STRUCT_NUM_NODES(pns_list);
         for (int i = 0; i < n; i++) {
             compile_node(comp, pns_list->nodes[i]);
         }
@@ -418,23 +418,23 @@ void c_tuple(compiler_t *comp, py_parse_node_t pn, py_parse_node_struct_t *pns_l
 #endif
 }
 
-void compile_generic_tuple(compiler_t *comp, py_parse_node_struct_t *pns) {
+void compile_generic_tuple(compiler_t *comp, mp_parse_node_struct_t *pns) {
     // a simple tuple expression
-    c_tuple(comp, PY_PARSE_NODE_NULL, pns);
+    c_tuple(comp, MP_PARSE_NODE_NULL, pns);
 }
 
-static bool node_is_const_false(py_parse_node_t pn) {
-    return PY_PARSE_NODE_IS_TOKEN_KIND(pn, PY_TOKEN_KW_FALSE);
-    // untested: || (PY_PARSE_NODE_IS_SMALL_INT(pn) && PY_PARSE_NODE_LEAF_ARG(pn) == 1);
+static bool node_is_const_false(mp_parse_node_t pn) {
+    return MP_PARSE_NODE_IS_TOKEN_KIND(pn, MP_TOKEN_KW_FALSE);
+    // untested: || (MP_PARSE_NODE_IS_SMALL_INT(pn) && MP_PARSE_NODE_LEAF_ARG(pn) == 1);
 }
 
-static bool node_is_const_true(py_parse_node_t pn) {
-    return PY_PARSE_NODE_IS_TOKEN_KIND(pn, PY_TOKEN_KW_TRUE) || (PY_PARSE_NODE_IS_SMALL_INT(pn) && PY_PARSE_NODE_LEAF_ARG(pn) == 1);
+static bool node_is_const_true(mp_parse_node_t pn) {
+    return MP_PARSE_NODE_IS_TOKEN_KIND(pn, MP_TOKEN_KW_TRUE) || (MP_PARSE_NODE_IS_SMALL_INT(pn) && MP_PARSE_NODE_LEAF_ARG(pn) == 1);
 }
 
 #if MICROPY_EMIT_CPYTHON
 // the is_nested variable is purely to match with CPython, which doesn't fully optimise not's
-static void cpython_c_if_cond(compiler_t *comp, py_parse_node_t pn, bool jump_if, int label, bool is_nested) {
+static void cpython_c_if_cond(compiler_t *comp, mp_parse_node_t pn, bool jump_if, int label, bool is_nested) {
     if (node_is_const_false(pn)) {
         if (jump_if == false) {
             EMIT(jump, label);
@@ -445,10 +445,10 @@ static void cpython_c_if_cond(compiler_t *comp, py_parse_node_t pn, bool jump_if
             EMIT(jump, label);
         }
         return;
-    } else if (PY_PARSE_NODE_IS_STRUCT(pn)) {
-        py_parse_node_struct_t *pns = (py_parse_node_struct_t*)pn;
-        int n = PY_PARSE_NODE_STRUCT_NUM_NODES(pns);
-        if (PY_PARSE_NODE_STRUCT_KIND(pns) == PN_or_test) {
+    } else if (MP_PARSE_NODE_IS_STRUCT(pn)) {
+        mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)pn;
+        int n = MP_PARSE_NODE_STRUCT_NUM_NODES(pns);
+        if (MP_PARSE_NODE_STRUCT_KIND(pns) == PN_or_test) {
             if (jump_if == false) {
                 int label2 = comp_next_label(comp);
                 for (int i = 0; i < n - 1; i++) {
@@ -462,7 +462,7 @@ static void cpython_c_if_cond(compiler_t *comp, py_parse_node_t pn, bool jump_if
                 }
             }
             return;
-        } else if (PY_PARSE_NODE_STRUCT_KIND(pns) == PN_and_test) {
+        } else if (MP_PARSE_NODE_STRUCT_KIND(pns) == PN_and_test) {
             if (jump_if == false) {
                 for (int i = 0; i < n; i++) {
                     cpython_c_if_cond(comp, pns->nodes[i], false, label, true);
@@ -476,7 +476,7 @@ static void cpython_c_if_cond(compiler_t *comp, py_parse_node_t pn, bool jump_if
                 EMIT(label_assign, label2);
             }
             return;
-        } else if (!is_nested && PY_PARSE_NODE_STRUCT_KIND(pns) == PN_not_test_2) {
+        } else if (!is_nested && MP_PARSE_NODE_STRUCT_KIND(pns) == PN_not_test_2) {
             cpython_c_if_cond(comp, pns->nodes[0], !jump_if, label, true);
             return;
         }
@@ -492,7 +492,7 @@ static void cpython_c_if_cond(compiler_t *comp, py_parse_node_t pn, bool jump_if
 }
 #endif
 
-static void c_if_cond(compiler_t *comp, py_parse_node_t pn, bool jump_if, int label) {
+static void c_if_cond(compiler_t *comp, mp_parse_node_t pn, bool jump_if, int label) {
 #if MICROPY_EMIT_CPYTHON
     cpython_c_if_cond(comp, pn, jump_if, label, false);
 #else
@@ -506,10 +506,10 @@ static void c_if_cond(compiler_t *comp, py_parse_node_t pn, bool jump_if, int la
             EMIT(jump, label);
         }
         return;
-    } else if (PY_PARSE_NODE_IS_STRUCT(pn)) {
-        py_parse_node_struct_t *pns = (py_parse_node_struct_t*)pn;
-        int n = PY_PARSE_NODE_STRUCT_NUM_NODES(pns);
-        if (PY_PARSE_NODE_STRUCT_KIND(pns) == PN_or_test) {
+    } else if (MP_PARSE_NODE_IS_STRUCT(pn)) {
+        mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)pn;
+        int n = MP_PARSE_NODE_STRUCT_NUM_NODES(pns);
+        if (MP_PARSE_NODE_STRUCT_KIND(pns) == PN_or_test) {
             if (jump_if == false) {
                 int label2 = comp_next_label(comp);
                 for (int i = 0; i < n - 1; i++) {
@@ -523,7 +523,7 @@ static void c_if_cond(compiler_t *comp, py_parse_node_t pn, bool jump_if, int la
                 }
             }
             return;
-        } else if (PY_PARSE_NODE_STRUCT_KIND(pns) == PN_and_test) {
+        } else if (MP_PARSE_NODE_STRUCT_KIND(pns) == PN_and_test) {
             if (jump_if == false) {
                 for (int i = 0; i < n; i++) {
                     c_if_cond(comp, pns->nodes[i], false, label);
@@ -537,7 +537,7 @@ static void c_if_cond(compiler_t *comp, py_parse_node_t pn, bool jump_if, int la
                 EMIT(label_assign, label2);
             }
             return;
-        } else if (PY_PARSE_NODE_STRUCT_KIND(pns) == PN_not_test_2) {
+        } else if (MP_PARSE_NODE_STRUCT_KIND(pns) == PN_not_test_2) {
             c_if_cond(comp, pns->nodes[0], !jump_if, label);
             return;
         }
@@ -554,29 +554,29 @@ static void c_if_cond(compiler_t *comp, py_parse_node_t pn, bool jump_if, int la
 }
 
 typedef enum { ASSIGN_STORE, ASSIGN_AUG_LOAD, ASSIGN_AUG_STORE } assign_kind_t;
-void c_assign(compiler_t *comp, py_parse_node_t pn, assign_kind_t kind);
+void c_assign(compiler_t *comp, mp_parse_node_t pn, assign_kind_t kind);
 
-void c_assign_power(compiler_t *comp, py_parse_node_struct_t *pns, assign_kind_t assign_kind) {
+void c_assign_power(compiler_t *comp, mp_parse_node_struct_t *pns, assign_kind_t assign_kind) {
     if (assign_kind != ASSIGN_AUG_STORE) {
         compile_node(comp, pns->nodes[0]);
     }
 
-    if (PY_PARSE_NODE_IS_STRUCT(pns->nodes[1])) {
-        py_parse_node_struct_t *pns1 = (py_parse_node_struct_t*)pns->nodes[1];
-        if (PY_PARSE_NODE_STRUCT_KIND(pns1) == PN_power_trailers) {
-            int n = PY_PARSE_NODE_STRUCT_NUM_NODES(pns1);
+    if (MP_PARSE_NODE_IS_STRUCT(pns->nodes[1])) {
+        mp_parse_node_struct_t *pns1 = (mp_parse_node_struct_t*)pns->nodes[1];
+        if (MP_PARSE_NODE_STRUCT_KIND(pns1) == PN_power_trailers) {
+            int n = MP_PARSE_NODE_STRUCT_NUM_NODES(pns1);
             if (assign_kind != ASSIGN_AUG_STORE) {
                 for (int i = 0; i < n - 1; i++) {
                     compile_node(comp, pns1->nodes[i]);
                 }
             }
-            assert(PY_PARSE_NODE_IS_STRUCT(pns1->nodes[n - 1]));
-            pns1 = (py_parse_node_struct_t*)pns1->nodes[n - 1];
+            assert(MP_PARSE_NODE_IS_STRUCT(pns1->nodes[n - 1]));
+            pns1 = (mp_parse_node_struct_t*)pns1->nodes[n - 1];
         }
-        if (PY_PARSE_NODE_STRUCT_KIND(pns1) == PN_trailer_paren) {
+        if (MP_PARSE_NODE_STRUCT_KIND(pns1) == PN_trailer_paren) {
             printf("SyntaxError: can't assign to function call\n");
             return;
-        } else if (PY_PARSE_NODE_STRUCT_KIND(pns1) == PN_trailer_bracket) {
+        } else if (MP_PARSE_NODE_STRUCT_KIND(pns1) == PN_trailer_bracket) {
             if (assign_kind == ASSIGN_AUG_STORE) {
                 EMIT(rot_three);
                 EMIT(store_subscr);
@@ -589,16 +589,16 @@ void c_assign_power(compiler_t *comp, py_parse_node_struct_t *pns, assign_kind_t
                     EMIT(store_subscr);
                 }
             }
-        } else if (PY_PARSE_NODE_STRUCT_KIND(pns1) == PN_trailer_period) {
-            assert(PY_PARSE_NODE_IS_ID(pns1->nodes[0]));
+        } else if (MP_PARSE_NODE_STRUCT_KIND(pns1) == PN_trailer_period) {
+            assert(MP_PARSE_NODE_IS_ID(pns1->nodes[0]));
             if (assign_kind == ASSIGN_AUG_LOAD) {
                 EMIT(dup_top);
-                EMIT(load_attr, PY_PARSE_NODE_LEAF_ARG(pns1->nodes[0]));
+                EMIT(load_attr, MP_PARSE_NODE_LEAF_ARG(pns1->nodes[0]));
             } else {
                 if (assign_kind == ASSIGN_AUG_STORE) {
                     EMIT(rot_two);
                 }
-                EMIT(store_attr, PY_PARSE_NODE_LEAF_ARG(pns1->nodes[0]));
+                EMIT(store_attr, MP_PARSE_NODE_LEAF_ARG(pns1->nodes[0]));
             }
         } else {
             // shouldn't happen
@@ -609,17 +609,17 @@ void c_assign_power(compiler_t *comp, py_parse_node_struct_t *pns, assign_kind_t
         assert(0);
     }
 
-    if (!PY_PARSE_NODE_IS_NULL(pns->nodes[2])) {
+    if (!MP_PARSE_NODE_IS_NULL(pns->nodes[2])) {
         // SyntaxError, cannot assign
         assert(0);
     }
 }
 
-void c_assign_tuple(compiler_t *comp, int n, py_parse_node_t *nodes) {
+void c_assign_tuple(compiler_t *comp, int n, mp_parse_node_t *nodes) {
     assert(n >= 0);
     int have_star_index = -1;
     for (int i = 0; i < n; i++) {
-        if (PY_PARSE_NODE_IS_STRUCT_KIND(nodes[i], PN_star_expr)) {
+        if (MP_PARSE_NODE_IS_STRUCT_KIND(nodes[i], PN_star_expr)) {
             if (have_star_index < 0) {
                 EMIT(unpack_ex, i, n - i - 1);
                 have_star_index = i;
@@ -634,7 +634,7 @@ void c_assign_tuple(compiler_t *comp, int n, py_parse_node_t *nodes) {
     }
     for (int i = 0; i < n; i++) {
         if (i == have_star_index) {
-            c_assign(comp, ((py_parse_node_struct_t*)nodes[i])->nodes[0], ASSIGN_STORE);
+            c_assign(comp, ((mp_parse_node_struct_t*)nodes[i])->nodes[0], ASSIGN_STORE);
         } else {
             c_assign(comp, nodes[i], ASSIGN_STORE);
         }
@@ -642,13 +642,13 @@ void c_assign_tuple(compiler_t *comp, int n, py_parse_node_t *nodes) {
 }
 
 // assigns top of stack to pn
-void c_assign(compiler_t *comp, py_parse_node_t pn, assign_kind_t assign_kind) {
+void c_assign(compiler_t *comp, mp_parse_node_t pn, assign_kind_t assign_kind) {
     tail_recursion:
-    if (PY_PARSE_NODE_IS_NULL(pn)) {
+    if (MP_PARSE_NODE_IS_NULL(pn)) {
         assert(0);
-    } else if (PY_PARSE_NODE_IS_LEAF(pn)) {
-        if (PY_PARSE_NODE_IS_ID(pn)) {
-            int arg = PY_PARSE_NODE_LEAF_ARG(pn);
+    } else if (MP_PARSE_NODE_IS_LEAF(pn)) {
+        if (MP_PARSE_NODE_IS_ID(pn)) {
+            int arg = MP_PARSE_NODE_LEAF_ARG(pn);
             switch (assign_kind) {
                 case ASSIGN_STORE:
                 case ASSIGN_AUG_STORE:
@@ -663,8 +663,8 @@ void c_assign(compiler_t *comp, py_parse_node_t pn, assign_kind_t assign_kind) {
             return;
         }
     } else {
-        py_parse_node_struct_t *pns = (py_parse_node_struct_t*)pn;
-        switch (PY_PARSE_NODE_STRUCT_KIND(pns)) {
+        mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)pn;
+        switch (MP_PARSE_NODE_STRUCT_KIND(pns)) {
             case PN_power:
                 // lhs is an index or attribute
                 c_assign_power(comp, pns, assign_kind);
@@ -676,17 +676,17 @@ void c_assign(compiler_t *comp, py_parse_node_t pn, assign_kind_t assign_kind) {
                 if (assign_kind != ASSIGN_STORE) {
                     goto bad_aug;
                 }
-                c_assign_tuple(comp, PY_PARSE_NODE_STRUCT_NUM_NODES(pns), pns->nodes);
+                c_assign_tuple(comp, MP_PARSE_NODE_STRUCT_NUM_NODES(pns), pns->nodes);
                 break;
 
             case PN_atom_paren:
                 // lhs is something in parenthesis
-                if (PY_PARSE_NODE_IS_NULL(pns->nodes[0])) {
+                if (MP_PARSE_NODE_IS_NULL(pns->nodes[0])) {
                     // empty tuple
                     printf("SyntaxError: can't assign to ()\n");
                     return;
-                } else if (PY_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_testlist_comp)) {
-                    pns = (py_parse_node_struct_t*)pns->nodes[0];
+                } else if (MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_testlist_comp)) {
+                    pns = (mp_parse_node_struct_t*)pns->nodes[0];
                     goto testlist_comp;
                 } else {
                     // parenthesis around 1 item, is just that item
@@ -700,11 +700,11 @@ void c_assign(compiler_t *comp, py_parse_node_t pn, assign_kind_t assign_kind) {
                 if (assign_kind != ASSIGN_STORE) {
                     goto bad_aug;
                 }
-                if (PY_PARSE_NODE_IS_NULL(pns->nodes[0])) {
+                if (MP_PARSE_NODE_IS_NULL(pns->nodes[0])) {
                     // empty list, assignment allowed
                     c_assign_tuple(comp, 0, NULL);
-                } else if (PY_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_testlist_comp)) {
-                    pns = (py_parse_node_struct_t*)pns->nodes[0];
+                } else if (MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_testlist_comp)) {
+                    pns = (mp_parse_node_struct_t*)pns->nodes[0];
                     goto testlist_comp;
                 } else {
                     // brackets around 1 item
@@ -713,29 +713,29 @@ void c_assign(compiler_t *comp, py_parse_node_t pn, assign_kind_t assign_kind) {
                 break;
 
             default:
-                printf("unknown assign, %u\n", (uint)PY_PARSE_NODE_STRUCT_KIND(pns));
+                printf("unknown assign, %u\n", (uint)MP_PARSE_NODE_STRUCT_KIND(pns));
                 assert(0);
         }
         return;
 
         testlist_comp:
         // lhs is a sequence
-        if (PY_PARSE_NODE_IS_STRUCT(pns->nodes[1])) {
-            py_parse_node_struct_t *pns2 = (py_parse_node_struct_t*)pns->nodes[1];
-            if (PY_PARSE_NODE_STRUCT_KIND(pns2) == PN_testlist_comp_3b) {
+        if (MP_PARSE_NODE_IS_STRUCT(pns->nodes[1])) {
+            mp_parse_node_struct_t *pns2 = (mp_parse_node_struct_t*)pns->nodes[1];
+            if (MP_PARSE_NODE_STRUCT_KIND(pns2) == PN_testlist_comp_3b) {
                 // sequence of one item, with trailing comma
-                assert(PY_PARSE_NODE_IS_NULL(pns2->nodes[0]));
+                assert(MP_PARSE_NODE_IS_NULL(pns2->nodes[0]));
                 c_assign_tuple(comp, 1, &pns->nodes[0]);
-            } else if (PY_PARSE_NODE_STRUCT_KIND(pns2) == PN_testlist_comp_3c) {
+            } else if (MP_PARSE_NODE_STRUCT_KIND(pns2) == PN_testlist_comp_3c) {
                 // sequence of many items
                 // TODO call c_assign_tuple instead
-                int n = PY_PARSE_NODE_STRUCT_NUM_NODES(pns2);
+                int n = MP_PARSE_NODE_STRUCT_NUM_NODES(pns2);
                 EMIT(unpack_sequence, 1 + n);
                 c_assign(comp, pns->nodes[0], ASSIGN_STORE);
                 for (int i = 0; i < n; i++) {
                     c_assign(comp, pns2->nodes[i], ASSIGN_STORE);
                 }
-            } else if (PY_PARSE_NODE_STRUCT_KIND(pns) == PN_comp_for) {
+            } else if (MP_PARSE_NODE_STRUCT_KIND(pns) == PN_comp_for) {
                 // TODO not implemented
                 assert(0);
             } else {
@@ -786,16 +786,16 @@ void close_over_variables_etc(compiler_t *comp, scope_t *this_scope, int n_dict_
     }
 }
 
-void compile_funcdef_param(compiler_t *comp, py_parse_node_t pn) {
-    if (PY_PARSE_NODE_IS_STRUCT_KIND(pn, PN_typedargslist_name)) {
-        py_parse_node_struct_t *pns = (py_parse_node_struct_t*)pn;
-        if (!PY_PARSE_NODE_IS_NULL(pns->nodes[2])) {
+void compile_funcdef_param(compiler_t *comp, mp_parse_node_t pn) {
+    if (MP_PARSE_NODE_IS_STRUCT_KIND(pn, PN_typedargslist_name)) {
+        mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)pn;
+        if (!MP_PARSE_NODE_IS_NULL(pns->nodes[2])) {
             // this parameter has a default value
             // in CPython, None (and True, False?) as default parameters are loaded with LOAD_NAME; don't understandy why
             if (comp->have_bare_star) {
                 comp->param_pass_num_dict_params += 1;
                 if (comp->param_pass == 1) {
-                    EMIT(load_const_id, PY_PARSE_NODE_LEAF_ARG(pns->nodes[0]));
+                    EMIT(load_const_id, MP_PARSE_NODE_LEAF_ARG(pns->nodes[0]));
                     compile_node(comp, pns->nodes[2]);
                 }
             } else {
@@ -805,9 +805,9 @@ void compile_funcdef_param(compiler_t *comp, py_parse_node_t pn) {
                 }
             }
         }
-    } else if (PY_PARSE_NODE_IS_STRUCT_KIND(pn, PN_typedargslist_star)) {
-        py_parse_node_struct_t *pns = (py_parse_node_struct_t*)pn;
-        if (PY_PARSE_NODE_IS_NULL(pns->nodes[0])) {
+    } else if (MP_PARSE_NODE_IS_STRUCT_KIND(pn, PN_typedargslist_star)) {
+        mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)pn;
+        if (MP_PARSE_NODE_IS_NULL(pns->nodes[0])) {
             // bare star
             comp->have_bare_star = true;
         }
@@ -816,12 +816,12 @@ void compile_funcdef_param(compiler_t *comp, py_parse_node_t pn) {
 
 // leaves function object on stack
 // returns function name
-qstr compile_funcdef_helper(compiler_t *comp, py_parse_node_struct_t *pns, uint emit_options) {
+qstr compile_funcdef_helper(compiler_t *comp, mp_parse_node_struct_t *pns, uint emit_options) {
     if (comp->pass == PASS_1) {
         // create a new scope for this function
-        scope_t *s = scope_new_and_link(comp, SCOPE_FUNCTION, (py_parse_node_t)pns, emit_options);
+        scope_t *s = scope_new_and_link(comp, SCOPE_FUNCTION, (mp_parse_node_t)pns, emit_options);
         // store the function scope so the compiling function can use it at each pass
-        pns->nodes[4] = (py_parse_node_t)s;
+        pns->nodes[4] = (mp_parse_node_t)s;
     }
 
     // save variables (probably don't need to do this, since we can't have nested definitions..?)
@@ -860,12 +860,12 @@ qstr compile_funcdef_helper(compiler_t *comp, py_parse_node_struct_t *pns, uint
 
 // leaves class object on stack
 // returns class name
-qstr compile_classdef_helper(compiler_t *comp, py_parse_node_struct_t *pns, uint emit_options) {
+qstr compile_classdef_helper(compiler_t *comp, mp_parse_node_struct_t *pns, uint emit_options) {
     if (comp->pass == PASS_1) {
         // create a new scope for this class
-        scope_t *s = scope_new_and_link(comp, SCOPE_CLASS, (py_parse_node_t)pns, emit_options);
+        scope_t *s = scope_new_and_link(comp, SCOPE_CLASS, (mp_parse_node_t)pns, emit_options);
         // store the class scope so the compiling function can use it at each pass
-        pns->nodes[3] = (py_parse_node_t)s;
+        pns->nodes[3] = (mp_parse_node_t)s;
     }
 
     EMIT(load_build_class);
@@ -880,7 +880,7 @@ qstr compile_classdef_helper(compiler_t *comp, py_parse_node_struct_t *pns, uint
     EMIT(load_const_id, cscope->simple_name);
 
     // nodes[1] has parent classes, if any
-    if (PY_PARSE_NODE_IS_NULL(pns->nodes[1])) {
+    if (MP_PARSE_NODE_IS_NULL(pns->nodes[1])) {
         // no parent classes
         EMIT(call_function, 2, 0, false, false);
     } else {
@@ -895,8 +895,8 @@ qstr compile_classdef_helper(compiler_t *comp, py_parse_node_struct_t *pns, uint
 }
 
 // returns true if it was a built-in decorator (even if the built-in had an error)
-static bool compile_built_in_decorator(compiler_t *comp, int name_len, py_parse_node_t *name_nodes, uint *emit_options) {
-    if (PY_PARSE_NODE_LEAF_ARG(name_nodes[0]) != comp->qstr_micropython) {
+static bool compile_built_in_decorator(compiler_t *comp, int name_len, mp_parse_node_t *name_nodes, uint *emit_options) {
+    if (MP_PARSE_NODE_LEAF_ARG(name_nodes[0]) != comp->qstr_micropython) {
         return false;
     }
 
@@ -905,7 +905,7 @@ static bool compile_built_in_decorator(compiler_t *comp, int name_len, py_parse_
         return true;
     }
 
-    qstr attr = PY_PARSE_NODE_LEAF_ARG(name_nodes[1]);
+    qstr attr = MP_PARSE_NODE_LEAF_ARG(name_nodes[1]);
     if (attr == comp->qstr_byte_code) {
         *emit_options = EMIT_OPT_BYTE_CODE;
 #if MICROPY_EMIT_NATIVE
@@ -925,9 +925,9 @@ static bool compile_built_in_decorator(compiler_t *comp, int name_len, py_parse_
     return true;
 }
 
-void compile_decorated(compiler_t *comp, py_parse_node_struct_t *pns) {
+void compile_decorated(compiler_t *comp, mp_parse_node_struct_t *pns) {
     // get the list of decorators
-    py_parse_node_t *nodes;
+    mp_parse_node_t *nodes;
     int n = list_get(&pns->nodes[0], PN_decorators, &nodes);
 
     // inherit emit options for this function/class definition
@@ -936,11 +936,11 @@ void compile_decorated(compiler_t *comp, py_parse_node_struct_t *pns) {
     // compile each decorator
     int num_built_in_decorators = 0;
     for (int i = 0; i < n; i++) {
-        assert(PY_PARSE_NODE_IS_STRUCT_KIND(nodes[i], PN_decorator)); // should be
-        py_parse_node_struct_t *pns_decorator = (py_parse_node_struct_t*)nodes[i];
+        assert(MP_PARSE_NODE_IS_STRUCT_KIND(nodes[i], PN_decorator)); // should be
+        mp_parse_node_struct_t *pns_decorator = (mp_parse_node_struct_t*)nodes[i];
 
         // nodes[0] contains the decorator function, which is a dotted name
-        py_parse_node_t *name_nodes;
+        mp_parse_node_t *name_nodes;
         int name_len = list_get(&pns_decorator->nodes[0], PN_dotted_name, &name_nodes);
 
         // check for built-in decorators
@@ -954,12 +954,12 @@ void compile_decorated(compiler_t *comp, py_parse_node_struct_t *pns) {
             // compile the decorator function
             compile_node(comp, name_nodes[0]);
             for (int i = 1; i < name_len; i++) {
-                assert(PY_PARSE_NODE_IS_ID(name_nodes[i])); // should be
-                EMIT(load_attr, PY_PARSE_NODE_LEAF_ARG(name_nodes[i]));
+                assert(MP_PARSE_NODE_IS_ID(name_nodes[i])); // should be
+                EMIT(load_attr, MP_PARSE_NODE_LEAF_ARG(name_nodes[i]));
             }
 
             // nodes[1] contains arguments to the decorator function, if any
-            if (!PY_PARSE_NODE_IS_NULL(pns_decorator->nodes[1])) {
+            if (!MP_PARSE_NODE_IS_NULL(pns_decorator->nodes[1])) {
                 // call the decorator function with the arguments in nodes[1]
                 compile_node(comp, pns_decorator->nodes[1]);
             }
@@ -967,11 +967,11 @@ void compile_decorated(compiler_t *comp, py_parse_node_struct_t *pns) {
     }
 
     // compile the body (funcdef or classdef) and get its name
-    py_parse_node_struct_t *pns_body = (py_parse_node_struct_t*)pns->nodes[1];
+    mp_parse_node_struct_t *pns_body = (mp_parse_node_struct_t*)pns->nodes[1];
     qstr body_name = 0;
-    if (PY_PARSE_NODE_STRUCT_KIND(pns_body) == PN_funcdef) {
+    if (MP_PARSE_NODE_STRUCT_KIND(pns_body) == PN_funcdef) {
         body_name = compile_funcdef_helper(comp, pns_body, emit_options);
-    } else if (PY_PARSE_NODE_STRUCT_KIND(pns_body) == PN_classdef) {
+    } else if (MP_PARSE_NODE_STRUCT_KIND(pns_body) == PN_classdef) {
         body_name = compile_classdef_helper(comp, pns_body, emit_options);
     } else {
         // shouldn't happen
@@ -987,39 +987,39 @@ void compile_decorated(compiler_t *comp, py_parse_node_struct_t *pns) {
     EMIT(store_id, body_name);
 }
 
-void compile_funcdef(compiler_t *comp, py_parse_node_struct_t *pns) {
+void compile_funcdef(compiler_t *comp, mp_parse_node_struct_t *pns) {
     qstr fname = compile_funcdef_helper(comp, pns, comp->scope_cur->emit_options);
     // store function object into function name
     EMIT(store_id, fname);
 }
 
-void c_del_stmt(compiler_t *comp, py_parse_node_t pn) {
-    if (PY_PARSE_NODE_IS_ID(pn)) {
-        EMIT(delete_id, PY_PARSE_NODE_LEAF_ARG(pn));
-    } else if (PY_PARSE_NODE_IS_STRUCT_KIND(pn, PN_power)) {
-        py_parse_node_struct_t *pns = (py_parse_node_struct_t*)pn;
+void c_del_stmt(compiler_t *comp, mp_parse_node_t pn) {
+    if (MP_PARSE_NODE_IS_ID(pn)) {
+        EMIT(delete_id, MP_PARSE_NODE_LEAF_ARG(pn));
+    } else if (MP_PARSE_NODE_IS_STRUCT_KIND(pn, PN_power)) {
+        mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)pn;
 
         compile_node(comp, pns->nodes[0]); // base of the power node
 
-        if (PY_PARSE_NODE_IS_STRUCT(pns->nodes[1])) {
-            py_parse_node_struct_t *pns1 = (py_parse_node_struct_t*)pns->nodes[1];
-            if (PY_PARSE_NODE_STRUCT_KIND(pns1) == PN_power_trailers) {
-                int n = PY_PARSE_NODE_STRUCT_NUM_NODES(pns1);
+        if (MP_PARSE_NODE_IS_STRUCT(pns->nodes[1])) {
+            mp_parse_node_struct_t *pns1 = (mp_parse_node_struct_t*)pns->nodes[1];
+            if (MP_PARSE_NODE_STRUCT_KIND(pns1) == PN_power_trailers) {
+                int n = MP_PARSE_NODE_STRUCT_NUM_NODES(pns1);
                 for (int i = 0; i < n - 1; i++) {
                     compile_node(comp, pns1->nodes[i]);
                 }
-                assert(PY_PARSE_NODE_IS_STRUCT(pns1->nodes[n - 1]));
-                pns1 = (py_parse_node_struct_t*)pns1->nodes[n - 1];
+                assert(MP_PARSE_NODE_IS_STRUCT(pns1->nodes[n - 1]));
+                pns1 = (mp_parse_node_struct_t*)pns1->nodes[n - 1];
             }
-            if (PY_PARSE_NODE_STRUCT_KIND(pns1) == PN_trailer_paren) {
+            if (MP_PARSE_NODE_STRUCT_KIND(pns1) == PN_trailer_paren) {
                 // SyntaxError: can't delete a function call
                 assert(0);
-            } else if (PY_PARSE_NODE_STRUCT_KIND(pns1) == PN_trailer_bracket) {
+            } else if (MP_PARSE_NODE_STRUCT_KIND(pns1) == PN_trailer_bracket) {
                 compile_node(comp, pns1->nodes[0]);
                 EMIT(delete_subscr);
-            } else if (PY_PARSE_NODE_STRUCT_KIND(pns1) == PN_trailer_period) {
-                assert(PY_PARSE_NODE_IS_ID(pns1->nodes[0]));
-                EMIT(delete_attr, PY_PARSE_NODE_LEAF_ARG(pns1->nodes[0]));
+            } else if (MP_PARSE_NODE_STRUCT_KIND(pns1) == PN_trailer_period) {
+                assert(MP_PARSE_NODE_IS_ID(pns1->nodes[0]));
+                EMIT(delete_attr, MP_PARSE_NODE_LEAF_ARG(pns1->nodes[0]));
             } else {
                 // shouldn't happen
                 assert(0);
@@ -1029,30 +1029,30 @@ void c_del_stmt(compiler_t *comp, py_parse_node_t pn) {
             assert(0);
         }
 
-        if (!PY_PARSE_NODE_IS_NULL(pns->nodes[2])) {
+        if (!MP_PARSE_NODE_IS_NULL(pns->nodes[2])) {
             // SyntaxError, cannot delete
             assert(0);
         }
-    } else if (PY_PARSE_NODE_IS_STRUCT_KIND(pn, PN_atom_paren)) {
-        pn = ((py_parse_node_struct_t*)pn)->nodes[0];
-        if (PY_PARSE_NODE_IS_STRUCT_KIND(pn, PN_testlist_comp)) {
-            py_parse_node_struct_t *pns = (py_parse_node_struct_t*)pn;
+    } else if (MP_PARSE_NODE_IS_STRUCT_KIND(pn, PN_atom_paren)) {
+        pn = ((mp_parse_node_struct_t*)pn)->nodes[0];
+        if (MP_PARSE_NODE_IS_STRUCT_KIND(pn, PN_testlist_comp)) {
+            mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)pn;
             // TODO perhaps factorise testlist_comp code with other uses of PN_testlist_comp
 
-            if (PY_PARSE_NODE_IS_STRUCT(pns->nodes[1])) {
-                py_parse_node_struct_t *pns1 = (py_parse_node_struct_t*)pns->nodes[1];
-                if (PY_PARSE_NODE_STRUCT_KIND(pns1) == PN_testlist_comp_3b) {
+            if (MP_PARSE_NODE_IS_STRUCT(pns->nodes[1])) {
+                mp_parse_node_struct_t *pns1 = (mp_parse_node_struct_t*)pns->nodes[1];
+                if (MP_PARSE_NODE_STRUCT_KIND(pns1) == PN_testlist_comp_3b) {
                     // sequence of one item, with trailing comma
-                    assert(PY_PARSE_NODE_IS_NULL(pns1->nodes[0]));
+                    assert(MP_PARSE_NODE_IS_NULL(pns1->nodes[0]));
                     c_del_stmt(comp, pns->nodes[0]);
-                } else if (PY_PARSE_NODE_STRUCT_KIND(pns1) == PN_testlist_comp_3c) {
+                } else if (MP_PARSE_NODE_STRUCT_KIND(pns1) == PN_testlist_comp_3c) {
                     // sequence of many items
-                    int n = PY_PARSE_NODE_STRUCT_NUM_NODES(pns1);
+                    int n = MP_PARSE_NODE_STRUCT_NUM_NODES(pns1);
                     c_del_stmt(comp, pns->nodes[0]);
                     for (int i = 0; i < n; i++) {
                         c_del_stmt(comp, pns1->nodes[i]);
                     }
-                } else if (PY_PARSE_NODE_STRUCT_KIND(pns) == PN_comp_for) {
+                } else if (MP_PARSE_NODE_STRUCT_KIND(pns) == PN_comp_for) {
                     // TODO not implemented; can't del comprehension?
                     assert(0);
                 } else {
@@ -1075,18 +1075,18 @@ void c_del_stmt(compiler_t *comp, py_parse_node_t pn) {
     }
 }
 
-void compile_del_stmt(compiler_t *comp, py_parse_node_struct_t *pns) {
+void compile_del_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) {
     apply_to_single_or_list(comp, pns->nodes[0], PN_exprlist, c_del_stmt);
 }
 
-void compile_break_stmt(compiler_t *comp, py_parse_node_struct_t *pns) {
+void compile_break_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) {
     if (comp->break_label == 0) {
         printf("ERROR: cannot break from here\n");
     }
     EMIT(break_loop, comp->break_label);
 }
 
-void compile_continue_stmt(compiler_t *comp, py_parse_node_struct_t *pns) {
+void compile_continue_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) {
     if (comp->continue_label == 0) {
         printf("ERROR: cannot continue from here\n");
     }
@@ -1097,19 +1097,19 @@ void compile_continue_stmt(compiler_t *comp, py_parse_node_struct_t *pns) {
     }
 }
 
-void compile_return_stmt(compiler_t *comp, py_parse_node_struct_t *pns) {
+void compile_return_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) {
     if (comp->scope_cur->kind != SCOPE_FUNCTION) {
         printf("SyntaxError: 'return' outside function\n");
         comp->had_error = true;
         return;
     }
-    if (PY_PARSE_NODE_IS_NULL(pns->nodes[0])) {
+    if (MP_PARSE_NODE_IS_NULL(pns->nodes[0])) {
         // no argument to 'return', so return None
-        EMIT(load_const_tok, PY_TOKEN_KW_NONE);
-    } else if (PY_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_test_if_expr)) {
+        EMIT(load_const_tok, MP_TOKEN_KW_NONE);
+    } else if (MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_test_if_expr)) {
         // special case when returning an if-expression; to match CPython optimisation
-        py_parse_node_struct_t *pns_test_if_expr = (py_parse_node_struct_t*)pns->nodes[0];
-        py_parse_node_struct_t *pns_test_if_else = (py_parse_node_struct_t*)pns_test_if_expr->nodes[1];
+        mp_parse_node_struct_t *pns_test_if_expr = (mp_parse_node_struct_t*)pns->nodes[0];
+        mp_parse_node_struct_t *pns_test_if_else = (mp_parse_node_struct_t*)pns_test_if_expr->nodes[1];
 
         int l_fail = comp_next_label(comp);
         c_if_cond(comp, pns_test_if_else->nodes[0], false, l_fail); // condition
@@ -1123,18 +1123,18 @@ void compile_return_stmt(compiler_t *comp, py_parse_node_struct_t *pns) {
     EMIT(return_value);
 }
 
-void compile_yield_stmt(compiler_t *comp, py_parse_node_struct_t *pns) {
+void compile_yield_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) {
     compile_node(comp, pns->nodes[0]);
     EMIT(pop_top);
 }
 
-void compile_raise_stmt(compiler_t *comp, py_parse_node_struct_t *pns) {
-    if (PY_PARSE_NODE_IS_NULL(pns->nodes[0])) {
+void compile_raise_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) {
+    if (MP_PARSE_NODE_IS_NULL(pns->nodes[0])) {
         // raise
         EMIT(raise_varargs, 0);
-    } else if (PY_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_raise_stmt_arg)) {
+    } else if (MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_raise_stmt_arg)) {
         // raise x from y
-        pns = (py_parse_node_struct_t*)pns->nodes[0];
+        pns = (mp_parse_node_struct_t*)pns->nodes[0];
         compile_node(comp, pns->nodes[0]);
         compile_node(comp, pns->nodes[1]);
         EMIT(raise_varargs, 2);
@@ -1148,33 +1148,33 @@ void compile_raise_stmt(compiler_t *comp, py_parse_node_struct_t *pns) {
 // q1 holds the base, q2 the full name
 // eg   a -> q1=q2=a
 //      a.b.c -> q1=a, q2=a.b.c
-void do_import_name(compiler_t *comp, py_parse_node_t pn, qstr *q1, qstr *q2) {
+void do_import_name(compiler_t *comp, mp_parse_node_t pn, qstr *q1, qstr *q2) {
     bool is_as = false;
-    if (PY_PARSE_NODE_IS_STRUCT_KIND(pn, PN_dotted_as_name)) {
-        py_parse_node_struct_t *pns = (py_parse_node_struct_t*)pn;
+    if (MP_PARSE_NODE_IS_STRUCT_KIND(pn, PN_dotted_as_name)) {
+        mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)pn;
         // a name of the form x as y; unwrap it
-        *q1 = PY_PARSE_NODE_LEAF_ARG(pns->nodes[1]);
+        *q1 = MP_PARSE_NODE_LEAF_ARG(pns->nodes[1]);
         pn = pns->nodes[0];
         is_as = true;
     }
-    if (PY_PARSE_NODE_IS_ID(pn)) {
+    if (MP_PARSE_NODE_IS_ID(pn)) {
         // just a simple name
-        *q2 = PY_PARSE_NODE_LEAF_ARG(pn);
+        *q2 = MP_PARSE_NODE_LEAF_ARG(pn);
         if (!is_as) {
             *q1 = *q2;
         }
         EMIT(import_name, *q2);
-    } else if (PY_PARSE_NODE_IS_STRUCT(pn)) {
-        py_parse_node_struct_t *pns = (py_parse_node_struct_t*)pn;
-        if (PY_PARSE_NODE_STRUCT_KIND(pns) == PN_dotted_name) {
+    } else if (MP_PARSE_NODE_IS_STRUCT(pn)) {
+        mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)pn;
+        if (MP_PARSE_NODE_STRUCT_KIND(pns) == PN_dotted_name) {
             // a name of the form a.b.c
             if (!is_as) {
-                *q1 = PY_PARSE_NODE_LEAF_ARG(pns->nodes[0]);
+                *q1 = MP_PARSE_NODE_LEAF_ARG(pns->nodes[0]);
             }
-            int n = PY_PARSE_NODE_STRUCT_NUM_NODES(pns);
+            int n = MP_PARSE_NODE_STRUCT_NUM_NODES(pns);
             int len = n - 1;
             for (int i = 0; i < n; i++) {
-                len += strlen(qstr_str(PY_PARSE_NODE_LEAF_ARG(pns->nodes[i])));
+                len += strlen(qstr_str(MP_PARSE_NODE_LEAF_ARG(pns->nodes[i])));
             }
             char *str = m_new(char, len + 1);
             str[0] = 0;
@@ -1182,13 +1182,13 @@ void do_import_name(compiler_t *comp, py_parse_node_t pn, qstr *q1, qstr *q2) {
                 if (i > 0) {
                     strcat(str, ".");
                 }
-                strcat(str, qstr_str(PY_PARSE_NODE_LEAF_ARG(pns->nodes[i])));
+                strcat(str, qstr_str(MP_PARSE_NODE_LEAF_ARG(pns->nodes[i])));
             }
             *q2 = qstr_from_str_take(str);
             EMIT(import_name, *q2);
             if (is_as) {
                 for (int i = 1; i < n; i++) {
-                    EMIT(load_attr, PY_PARSE_NODE_LEAF_ARG(pns->nodes[i]));
+                    EMIT(load_attr, MP_PARSE_NODE_LEAF_ARG(pns->nodes[i]));
                 }
             }
         } else {
@@ -1201,20 +1201,20 @@ void do_import_name(compiler_t *comp, py_parse_node_t pn, qstr *q1, qstr *q2) {
     }
 }
 
-void compile_dotted_as_name(compiler_t *comp, py_parse_node_t pn) {
+void compile_dotted_as_name(compiler_t *comp, mp_parse_node_t pn) {
     EMIT(load_const_small_int, 0); // ??
-    EMIT(load_const_tok, PY_TOKEN_KW_NONE);
+    EMIT(load_const_tok, MP_TOKEN_KW_NONE);
     qstr q1, q2;
     do_import_name(comp, pn, &q1, &q2);
     EMIT(store_id, q1);
 }
 
-void compile_import_name(compiler_t *comp, py_parse_node_struct_t *pns) {
+void compile_import_name(compiler_t *comp, mp_parse_node_struct_t *pns) {
     apply_to_single_or_list(comp, pns->nodes[0], PN_dotted_as_names, compile_dotted_as_name);
 }
 
-void compile_import_from(compiler_t *comp, py_parse_node_struct_t *pns) {
-    if (PY_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[1], PY_TOKEN_OP_STAR)) {
+void compile_import_from(compiler_t *comp, mp_parse_node_struct_t *pns) {
+    if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[1], MP_TOKEN_OP_STAR)) {
         EMIT(load_const_small_int, 0); // level 0 for __import__
 
         // build the "fromlist" tuple
@@ -1234,16 +1234,16 @@ void compile_import_from(compiler_t *comp, py_parse_node_struct_t *pns) {
         EMIT(load_const_small_int, 0); // level 0 for __import__
 
         // build the "fromlist" tuple
-        py_parse_node_t *pn_nodes;
+        mp_parse_node_t *pn_nodes;
         int n = list_get(&pns->nodes[1], PN_import_as_names, &pn_nodes);
 #if MICROPY_EMIT_CPYTHON
         {
             vstr_t *vstr = vstr_new();
             vstr_printf(vstr, "(");
             for (int i = 0; i < n; i++) {
-                assert(PY_PARSE_NODE_IS_STRUCT_KIND(pn_nodes[i], PN_import_as_name));
-                py_parse_node_struct_t *pns3 = (py_parse_node_struct_t*)pn_nodes[i];
-                qstr id2 = PY_PARSE_NODE_LEAF_ARG(pns3->nodes[0]); // should be id
+                assert(MP_PARSE_NODE_IS_STRUCT_KIND(pn_nodes[i], PN_import_as_name));
+                mp_parse_node_struct_t *pns3 = (mp_parse_node_struct_t*)pn_nodes[i];
+                qstr id2 = MP_PARSE_NODE_LEAF_ARG(pns3->nodes[0]); // should be id
                 if (i > 0) {
                     vstr_printf(vstr, ", ");
                 }
@@ -1260,9 +1260,9 @@ void compile_import_from(compiler_t *comp, py_parse_node_struct_t *pns) {
         }
 #else
         for (int i = 0; i < n; i++) {
-            assert(PY_PARSE_NODE_IS_STRUCT_KIND(pn_nodes[i], PN_import_as_name));
-            py_parse_node_struct_t *pns3 = (py_parse_node_struct_t*)pn_nodes[i];
-            qstr id2 = PY_PARSE_NODE_LEAF_ARG(pns3->nodes[0]); // should be id
+            assert(MP_PARSE_NODE_IS_STRUCT_KIND(pn_nodes[i], PN_import_as_name));
+            mp_parse_node_struct_t *pns3 = (mp_parse_node_struct_t*)pn_nodes[i];
+            qstr id2 = MP_PARSE_NODE_LEAF_ARG(pns3->nodes[0]); // should be id
             EMIT(load_const_str, id2, false);
         }
         EMIT(build_tuple, n);
@@ -1272,53 +1272,53 @@ void compile_import_from(compiler_t *comp, py_parse_node_struct_t *pns) {
         qstr dummy_q, id1;
         do_import_name(comp, pns->nodes[0], &dummy_q, &id1);
         for (int i = 0; i < n; i++) {
-            assert(PY_PARSE_NODE_IS_STRUCT_KIND(pn_nodes[i], PN_import_as_name));
-            py_parse_node_struct_t *pns3 = (py_parse_node_struct_t*)pn_nodes[i];
-            qstr id2 = PY_PARSE_NODE_LEAF_ARG(pns3->nodes[0]); // should be id
+            assert(MP_PARSE_NODE_IS_STRUCT_KIND(pn_nodes[i], PN_import_as_name));
+            mp_parse_node_struct_t *pns3 = (mp_parse_node_struct_t*)pn_nodes[i];
+            qstr id2 = MP_PARSE_NODE_LEAF_ARG(pns3->nodes[0]); // should be id
             EMIT(import_from, id2);
-            if (PY_PARSE_NODE_IS_NULL(pns3->nodes[1])) {
+            if (MP_PARSE_NODE_IS_NULL(pns3->nodes[1])) {
                 EMIT(store_id, id2);
             } else {
-                EMIT(store_id, PY_PARSE_NODE_LEAF_ARG(pns3->nodes[1]));
+                EMIT(store_id, MP_PARSE_NODE_LEAF_ARG(pns3->nodes[1]));
             }
         }
         EMIT(pop_top);
     }
 }
 
-void compile_global_stmt(compiler_t *comp, py_parse_node_struct_t *pns) {
+void compile_global_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) {
     if (comp->pass == PASS_1) {
-        if (PY_PARSE_NODE_IS_LEAF(pns->nodes[0])) {
-            scope_declare_global(comp->scope_cur, PY_PARSE_NODE_LEAF_ARG(pns->nodes[0]));
+        if (MP_PARSE_NODE_IS_LEAF(pns->nodes[0])) {
+            scope_declare_global(comp->scope_cur, MP_PARSE_NODE_LEAF_ARG(pns->nodes[0]));
         } else {
-            pns = (py_parse_node_struct_t*)pns->nodes[0];
-            int num_nodes = PY_PARSE_NODE_STRUCT_NUM_NODES(pns);
+            pns = (mp_parse_node_struct_t*)pns->nodes[0];
+            int num_nodes = MP_PARSE_NODE_STRUCT_NUM_NODES(pns);
             for (int i = 0; i < num_nodes; i++) {
-                scope_declare_global(comp->scope_cur, PY_PARSE_NODE_LEAF_ARG(pns->nodes[i]));
+                scope_declare_global(comp->scope_cur, MP_PARSE_NODE_LEAF_ARG(pns->nodes[i]));
             }
         }
     }
 }
 
-void compile_nonlocal_stmt(compiler_t *comp, py_parse_node_struct_t *pns) {
+void compile_nonlocal_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) {
     if (comp->pass == PASS_1) {
-        if (PY_PARSE_NODE_IS_LEAF(pns->nodes[0])) {
-            scope_declare_nonlocal(comp->scope_cur, PY_PARSE_NODE_LEAF_ARG(pns->nodes[0]));
+        if (MP_PARSE_NODE_IS_LEAF(pns->nodes[0])) {
+            scope_declare_nonlocal(comp->scope_cur, MP_PARSE_NODE_LEAF_ARG(pns->nodes[0]));
         } else {
-            pns = (py_parse_node_struct_t*)pns->nodes[0];
-            int num_nodes = PY_PARSE_NODE_STRUCT_NUM_NODES(pns);
+            pns = (mp_parse_node_struct_t*)pns->nodes[0];
+            int num_nodes = MP_PARSE_NODE_STRUCT_NUM_NODES(pns);
             for (int i = 0; i < num_nodes; i++) {
-                scope_declare_nonlocal(comp->scope_cur, PY_PARSE_NODE_LEAF_ARG(pns->nodes[i]));
+                scope_declare_nonlocal(comp->scope_cur, MP_PARSE_NODE_LEAF_ARG(pns->nodes[i]));
             }
         }
     }
 }
 
-void compile_assert_stmt(compiler_t *comp, py_parse_node_struct_t *pns) {
+void compile_assert_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) {
     int l_end = comp_next_label(comp);
     c_if_cond(comp, pns->nodes[0], true, l_end);
     EMIT(load_id, comp->qstr_assertion_error);
-    if (!PY_PARSE_NODE_IS_NULL(pns->nodes[1])) {
+    if (!MP_PARSE_NODE_IS_NULL(pns->nodes[1])) {
         // assertion message
         compile_node(comp, pns->nodes[1]);
         EMIT(call_function, 1, 0, false, false);
@@ -1327,7 +1327,7 @@ void compile_assert_stmt(compiler_t *comp, py_parse_node_struct_t *pns) {
     EMIT(label_assign, l_end);
 }
 
-void compile_if_stmt(compiler_t *comp, py_parse_node_struct_t *pns) {
+void compile_if_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) {
     // TODO proper and/or short circuiting
 
     int l_end = comp_next_label(comp);
@@ -1336,7 +1336,7 @@ void compile_if_stmt(compiler_t *comp, py_parse_node_struct_t *pns) {
     c_if_cond(comp, pns->nodes[0], false, l_fail); // if condition
 
     compile_node(comp, pns->nodes[1]); // if block
-    //if (!(PY_PARSE_NODE_IS_NULL(pns->nodes[2]) && PY_PARSE_NODE_IS_NULL(pns->nodes[3]))) { // optimisation; doesn't align with CPython
+    //if (!(MP_PARSE_NODE_IS_NULL(pns->nodes[2]) && MP_PARSE_NODE_IS_NULL(pns->nodes[3]))) { // optimisation; doesn't align with CPython
         // jump over elif/else blocks if they exist
         if (!EMIT(last_emit_was_return_value)) { // simple optimisation to align with CPython
             EMIT(jump, l_end);
@@ -1344,17 +1344,17 @@ void compile_if_stmt(compiler_t *comp, py_parse_node_struct_t *pns) {
     //}
     EMIT(label_assign, l_fail);
 
-    if (!PY_PARSE_NODE_IS_NULL(pns->nodes[2])) {
+    if (!MP_PARSE_NODE_IS_NULL(pns->nodes[2])) {
         // compile elif blocks
 
-        py_parse_node_struct_t *pns_elif = (py_parse_node_struct_t*)pns->nodes[2];
+        mp_parse_node_struct_t *pns_elif = (mp_parse_node_struct_t*)pns->nodes[2];
 
-        if (PY_PARSE_NODE_STRUCT_KIND(pns_elif) == PN_if_stmt_elif_list) {
+        if (MP_PARSE_NODE_STRUCT_KIND(pns_elif) == PN_if_stmt_elif_list) {
             // multiple elif blocks
 
-            int n = PY_PARSE_NODE_STRUCT_NUM_NODES(pns_elif);
+            int n = MP_PARSE_NODE_STRUCT_NUM_NODES(pns_elif);
             for (int i = 0; i < n; i++) {
-                py_parse_node_struct_t *pns_elif2 = (py_parse_node_struct_t*)pns_elif->nodes[i];
+                mp_parse_node_struct_t *pns_elif2 = (mp_parse_node_struct_t*)pns_elif->nodes[i];
                 l_fail = comp_next_label(comp);
                 c_if_cond(comp, pns_elif2->nodes[0], false, l_fail); // elif condition
 
@@ -1385,7 +1385,7 @@ void compile_if_stmt(compiler_t *comp, py_parse_node_struct_t *pns) {
     EMIT(label_assign, l_end);
 }
 
-void compile_while_stmt(compiler_t *comp, py_parse_node_struct_t *pns) {
+void compile_while_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) {
     int old_break_label = comp->break_label;
     int old_continue_label = comp->continue_label;
 
@@ -1431,7 +1431,7 @@ void compile_while_stmt(compiler_t *comp, py_parse_node_struct_t *pns) {
 
 // TODO preload end and step onto stack if they are not constants
 // TODO check if step is negative and do opposite test
-void compile_for_stmt_optimised_range(compiler_t *comp, py_parse_node_t pn_var, py_parse_node_t pn_start, py_parse_node_t pn_end, py_parse_node_t pn_step, py_parse_node_t pn_body, py_parse_node_t pn_else) {
+void compile_for_stmt_optimised_range(compiler_t *comp, mp_parse_node_t pn_var, mp_parse_node_t pn_start, mp_parse_node_t pn_end, mp_parse_node_t pn_step, mp_parse_node_t pn_body, mp_parse_node_t pn_else) {
     int old_break_label = comp->break_label;
     int old_continue_label = comp->continue_label;
 
@@ -1476,29 +1476,29 @@ void compile_for_stmt_optimised_range(compiler_t *comp, py_parse_node_t pn_var,
     EMIT(label_assign, break_label);
 }
 
-void compile_for_stmt(compiler_t *comp, py_parse_node_struct_t *pns) {
+void compile_for_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) {
 #if !MICROPY_EMIT_CPYTHON
     // this bit optimises: for <x> in range(...), turning it into an explicitly incremented variable
     // this is actually slower, but uses no heap memory
     // for viper it will be much, much faster
-    if (/*comp->scope_cur->emit_options == EMIT_OPT_VIPER &&*/ PY_PARSE_NODE_IS_ID(pns->nodes[0]) && PY_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[1], PN_power)) {
-        py_parse_node_struct_t *pns_it = (py_parse_node_struct_t*)pns->nodes[1];
-        if (PY_PARSE_NODE_IS_ID(pns_it->nodes[0]) && PY_PARSE_NODE_LEAF_ARG(pns_it->nodes[0]) == comp->qstr_range && PY_PARSE_NODE_IS_STRUCT_KIND(pns_it->nodes[1], PN_trailer_paren) && PY_PARSE_NODE_IS_NULL(pns_it->nodes[2])) {
-            py_parse_node_t pn_range_args = ((py_parse_node_struct_t*)pns_it->nodes[1])->nodes[0];
-            py_parse_node_t *args;
+    if (/*comp->scope_cur->emit_options == EMIT_OPT_VIPER &&*/ MP_PARSE_NODE_IS_ID(pns->nodes[0]) && MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[1], PN_power)) {
+        mp_parse_node_struct_t *pns_it = (mp_parse_node_struct_t*)pns->nodes[1];
+        if (MP_PARSE_NODE_IS_ID(pns_it->nodes[0]) && MP_PARSE_NODE_LEAF_ARG(pns_it->nodes[0]) == comp->qstr_range && MP_PARSE_NODE_IS_STRUCT_KIND(pns_it->nodes[1], PN_trailer_paren) && MP_PARSE_NODE_IS_NULL(pns_it->nodes[2])) {
+            mp_parse_node_t pn_range_args = ((mp_parse_node_struct_t*)pns_it->nodes[1])->nodes[0];
+            mp_parse_node_t *args;
             int n_args = list_get(&pn_range_args, PN_arglist, &args);
             if (1 <= n_args && n_args <= 3) {
-                py_parse_node_t pn_range_start;
-                py_parse_node_t pn_range_end;
-                py_parse_node_t pn_range_step;
+                mp_parse_node_t pn_range_start;
+                mp_parse_node_t pn_range_end;
+                mp_parse_node_t pn_range_step;
                 if (n_args == 1) {
-                    pn_range_start = py_parse_node_new_leaf(PY_PARSE_NODE_SMALL_INT, 0);
+                    pn_range_start = mp_parse_node_new_leaf(MP_PARSE_NODE_SMALL_INT, 0);
                     pn_range_end = args[0];
-                    pn_range_step = py_parse_node_new_leaf(PY_PARSE_NODE_SMALL_INT, 1);
+                    pn_range_step = mp_parse_node_new_leaf(MP_PARSE_NODE_SMALL_INT, 1);
                 } else if (n_args == 2) {
                     pn_range_start = args[0];
                     pn_range_end = args[1];
-                    pn_range_step = py_parse_node_new_leaf(PY_PARSE_NODE_SMALL_INT, 1);
+                    pn_range_step = mp_parse_node_new_leaf(MP_PARSE_NODE_SMALL_INT, 1);
                 } else {
                     pn_range_start = args[0];
                     pn_range_end = args[1];
@@ -1554,7 +1554,7 @@ void compile_for_stmt(compiler_t *comp, py_parse_node_struct_t *pns) {
     EMIT(label_assign, end_label);
 }
 
-void compile_try_except(compiler_t *comp, py_parse_node_t pn_body, int n_except, py_parse_node_t *pn_excepts, py_parse_node_t pn_else) {
+void compile_try_except(compiler_t *comp, mp_parse_node_t pn_body, int n_except, mp_parse_node_t *pn_excepts, mp_parse_node_t pn_else) {
     // this function is a bit of a hack at the moment
     // don't understand how the stack works with exceptions, so we force it to return to the correct value
 
@@ -1571,13 +1571,13 @@ void compile_try_except(compiler_t *comp, py_parse_node_t pn_body, int n_except,
     int l2 = comp_next_label(comp);
 
     for (int i = 0; i < n_except; i++) {
-        assert(PY_PARSE_NODE_IS_STRUCT_KIND(pn_excepts[i], PN_try_stmt_except)); // should be
-        py_parse_node_struct_t *pns_except = (py_parse_node_struct_t*)pn_excepts[i];
+        assert(MP_PARSE_NODE_IS_STRUCT_KIND(pn_excepts[i], PN_try_stmt_except)); // should be
+        mp_parse_node_struct_t *pns_except = (mp_parse_node_struct_t*)pn_excepts[i];
 
         qstr qstr_exception_local = 0;
         int end_finally_label = comp_next_label(comp);
 
-        if (PY_PARSE_NODE_IS_NULL(pns_except->nodes[0])) {
+        if (MP_PARSE_NODE_IS_NULL(pns_except->nodes[0])) {
             // this is a catch all exception handler
             if (i + 1 != n_except) {
                 printf("SyntaxError: default 'except:' must be last\n");
@@ -1585,13 +1585,13 @@ void compile_try_except(compiler_t *comp, py_parse_node_t pn_body, int n_except,
             }
         } else {
             // this exception handler requires a match to a certain type of exception
-            py_parse_node_t pns_exception_expr = pns_except->nodes[0];
-            if (PY_PARSE_NODE_IS_STRUCT(pns_exception_expr)) {
-                py_parse_node_struct_t *pns3 = (py_parse_node_struct_t*)pns_exception_expr;
-                if (PY_PARSE_NODE_STRUCT_KIND(pns3) == PN_try_stmt_as_name) {
+            mp_parse_node_t pns_exception_expr = pns_except->nodes[0];
+            if (MP_PARSE_NODE_IS_STRUCT(pns_exception_expr)) {
+                mp_parse_node_struct_t *pns3 = (mp_parse_node_struct_t*)pns_exception_expr;
+                if (MP_PARSE_NODE_STRUCT_KIND(pns3) == PN_try_stmt_as_name) {
                     // handler binds the exception to a local
                     pns_exception_expr = pns3->nodes[0];
-                    qstr_exception_local = PY_PARSE_NODE_LEAF_ARG(pns3->nodes[1]);
+                    qstr_exception_local = MP_PARSE_NODE_LEAF_ARG(pns3->nodes[1]);
                 }
             }
             EMIT(dup_top);
@@ -1621,9 +1621,9 @@ void compile_try_except(compiler_t *comp, py_parse_node_t pn_body, int n_except,
         }
         EMIT(pop_except);
         if (qstr_exception_local != 0) {
-            EMIT(load_const_tok, PY_TOKEN_KW_NONE);
+            EMIT(load_const_tok, MP_TOKEN_KW_NONE);
             EMIT(label_assign, l3);
-            EMIT(load_const_tok, PY_TOKEN_KW_NONE);
+            EMIT(load_const_tok, MP_TOKEN_KW_NONE);
             EMIT(store_id, qstr_exception_local);
             EMIT(delete_id, qstr_exception_local);
             EMIT(end_finally);
@@ -1640,47 +1640,47 @@ void compile_try_except(compiler_t *comp, py_parse_node_t pn_body, int n_except,
     EMIT(set_stack_size, stack_size);
 }
 
-void compile_try_finally(compiler_t *comp, py_parse_node_t pn_body, int n_except, py_parse_node_t *pn_except, py_parse_node_t pn_else, py_parse_node_t pn_finally) {
+void compile_try_finally(compiler_t *comp, mp_parse_node_t pn_body, int n_except, mp_parse_node_t *pn_except, mp_parse_node_t pn_else, mp_parse_node_t pn_finally) {
     // don't understand how the stack works with exceptions, so we force it to return to the correct value
     int stack_size = EMIT(get_stack_size);
     int l_finally_block = comp_next_label(comp);
     EMIT(setup_finally, l_finally_block);
     if (n_except == 0) {
-        assert(PY_PARSE_NODE_IS_NULL(pn_else));
+        assert(MP_PARSE_NODE_IS_NULL(pn_else));
         compile_node(comp, pn_body);
     } else {
         compile_try_except(comp, pn_body, n_except, pn_except, pn_else);
     }
     EMIT(pop_block);
-    EMIT(load_const_tok, PY_TOKEN_KW_NONE);
+    EMIT(load_const_tok, MP_TOKEN_KW_NONE);
     EMIT(label_assign, l_finally_block);
     compile_node(comp, pn_finally);
     EMIT(end_finally);
     EMIT(set_stack_size, stack_size);
 }
 
-void compile_try_stmt(compiler_t *comp, py_parse_node_struct_t *pns) {
-    if (PY_PARSE_NODE_IS_STRUCT(pns->nodes[1])) {
-        py_parse_node_struct_t *pns2 = (py_parse_node_struct_t*)pns->nodes[1];
-        if (PY_PARSE_NODE_STRUCT_KIND(pns2) == PN_try_stmt_finally) {
+void compile_try_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) {
+    if (MP_PARSE_NODE_IS_STRUCT(pns->nodes[1])) {
+        mp_parse_node_struct_t *pns2 = (mp_parse_node_struct_t*)pns->nodes[1];
+        if (MP_PARSE_NODE_STRUCT_KIND(pns2) == PN_try_stmt_finally) {
             // just try-finally
-            compile_try_finally(comp, pns->nodes[0], 0, NULL, PY_PARSE_NODE_NULL, pns2->nodes[0]);
-        } else if (PY_PARSE_NODE_STRUCT_KIND(pns2) == PN_try_stmt_except_and_more) {
+            compile_try_finally(comp, pns->nodes[0], 0, NULL, MP_PARSE_NODE_NULL, pns2->nodes[0]);
+        } else if (MP_PARSE_NODE_STRUCT_KIND(pns2) == PN_try_stmt_except_and_more) {
             // try-except and possibly else and/or finally
-            py_parse_node_t *pn_excepts;
+            mp_parse_node_t *pn_excepts;
             int n_except = list_get(&pns2->nodes[0], PN_try_stmt_except_list, &pn_excepts);
-            if (PY_PARSE_NODE_IS_NULL(pns2->nodes[2])) {
+            if (MP_PARSE_NODE_IS_NULL(pns2->nodes[2])) {
                 // no finally
                 compile_try_except(comp, pns->nodes[0], n_except, pn_excepts, pns2->nodes[1]);
             } else {
                 // have finally
-                compile_try_finally(comp, pns->nodes[0], n_except, pn_excepts, pns2->nodes[1], ((py_parse_node_struct_t*)pns2->nodes[2])->nodes[0]);
+                compile_try_finally(comp, pns->nodes[0], n_except, pn_excepts, pns2->nodes[1], ((mp_parse_node_struct_t*)pns2->nodes[2])->nodes[0]);
             }
         } else {
             // just try-except
-            py_parse_node_t *pn_excepts;
+            mp_parse_node_t *pn_excepts;
             int n_except = list_get(&pns->nodes[1], PN_try_stmt_except_list, &pn_excepts);
-            compile_try_except(comp, pns->nodes[0], n_except, pn_excepts, PY_PARSE_NODE_NULL);
+            compile_try_except(comp, pns->nodes[0], n_except, pn_excepts, MP_PARSE_NODE_NULL);
         }
     } else {
         // shouldn't happen
@@ -1688,15 +1688,15 @@ void compile_try_stmt(compiler_t *comp, py_parse_node_struct_t *pns) {
     }
 }
 
-void compile_with_stmt_helper(compiler_t *comp, int n, py_parse_node_t *nodes, py_parse_node_t body) {
+void compile_with_stmt_helper(compiler_t *comp, int n, mp_parse_node_t *nodes, mp_parse_node_t body) {
     if (n == 0) {
         // no more pre-bits, compile the body of the with
         compile_node(comp, body);
     } else {
         int l_end = comp_next_label(comp);
-        if (PY_PARSE_NODE_IS_STRUCT_KIND(nodes[0], PN_with_item)) {
+        if (MP_PARSE_NODE_IS_STRUCT_KIND(nodes[0], PN_with_item)) {
             // this pre-bit is of the form "a as b"
-            py_parse_node_struct_t *pns = (py_parse_node_struct_t*)nodes[0];
+            mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)nodes[0];
             compile_node(comp, pns->nodes[0]);
             EMIT(setup_with, l_end);
             c_assign(comp, pns->nodes[1], ASSIGN_STORE);
@@ -1710,16 +1710,16 @@ void compile_with_stmt_helper(compiler_t *comp, int n, py_parse_node_t *nodes, p
         compile_with_stmt_helper(comp, n - 1, nodes + 1, body);
         // finish this with block
         EMIT(pop_block);
-        EMIT(load_const_tok, PY_TOKEN_KW_NONE);
+        EMIT(load_const_tok, MP_TOKEN_KW_NONE);
         EMIT(label_assign, l_end);
         EMIT(with_cleanup);
         EMIT(end_finally);
     }
 }
 
-void compile_with_stmt(compiler_t *comp, py_parse_node_struct_t *pns) {
+void compile_with_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) {
     // get the nodes for the pre-bit of the with (the a as b, c as d, ... bit)
-    py_parse_node_t *nodes;
+    mp_parse_node_t *nodes;
     int n = list_get(&pns->nodes[0], PN_with_stmt_list, &nodes);
     assert(n > 0);
 
@@ -1727,8 +1727,8 @@ void compile_with_stmt(compiler_t *comp, py_parse_node_struct_t *pns) {
     compile_with_stmt_helper(comp, n, nodes, pns->nodes[1]);
 }
 
-void compile_expr_stmt(compiler_t *comp, py_parse_node_struct_t *pns) {
-    if (PY_PARSE_NODE_IS_NULL(pns->nodes[1])) {
+void compile_expr_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) {
+    if (MP_PARSE_NODE_IS_NULL(pns->nodes[1])) {
         if (comp->is_repl && comp->scope_cur->kind == SCOPE_MODULE) {
             // for REPL, evaluate then print the expression
             EMIT(load_id, qstr_from_str_static("__repl_print__"));
@@ -1738,7 +1738,7 @@ void compile_expr_stmt(compiler_t *comp, py_parse_node_struct_t *pns) {
 
         } else {
             // for non-REPL, evaluate then discard the expression
-            if (PY_PARSE_NODE_IS_LEAF(pns->nodes[0]) && !PY_PARSE_NODE_IS_ID(pns->nodes[0])) {
+            if (MP_PARSE_NODE_IS_LEAF(pns->nodes[0]) && !MP_PARSE_NODE_IS_ID(pns->nodes[0])) {
                 // do nothing with a lonely constant
             } else {
                 compile_node(comp, pns->nodes[0]); // just an expression
@@ -1746,32 +1746,32 @@ void compile_expr_stmt(compiler_t *comp, py_parse_node_struct_t *pns) {
             }
         }
     } else {
-        py_parse_node_struct_t *pns1 = (py_parse_node_struct_t*)pns->nodes[1];
-        int kind = PY_PARSE_NODE_STRUCT_KIND(pns1);
+        mp_parse_node_struct_t *pns1 = (mp_parse_node_struct_t*)pns->nodes[1];
+        int kind = MP_PARSE_NODE_STRUCT_KIND(pns1);
         if (kind == PN_expr_stmt_augassign) {
             c_assign(comp, pns->nodes[0], ASSIGN_AUG_LOAD); // lhs load for aug assign
             compile_node(comp, pns1->nodes[1]); // rhs
-            assert(PY_PARSE_NODE_IS_TOKEN(pns1->nodes[0]));
+            assert(MP_PARSE_NODE_IS_TOKEN(pns1->nodes[0]));
             // note that we don't really need to implement separate inplace ops, just normal binary ops will suffice
-            switch (PY_PARSE_NODE_LEAF_ARG(pns1->nodes[0])) {
-                case PY_TOKEN_DEL_PIPE_EQUAL: EMIT(binary_op, RT_BINARY_OP_INPLACE_OR); break;
-                case PY_TOKEN_DEL_CARET_EQUAL: EMIT(binary_op, RT_BINARY_OP_INPLACE_XOR); break;
-                case PY_TOKEN_DEL_AMPERSAND_EQUAL: EMIT(binary_op, RT_BINARY_OP_INPLACE_AND); break;
-                case PY_TOKEN_DEL_DBL_LESS_EQUAL: EMIT(binary_op, RT_BINARY_OP_INPLACE_LSHIFT); break;
-                case PY_TOKEN_DEL_DBL_MORE_EQUAL: EMIT(binary_op, RT_BINARY_OP_INPLACE_RSHIFT); break;
-                case PY_TOKEN_DEL_PLUS_EQUAL: EMIT(binary_op, RT_BINARY_OP_INPLACE_ADD); break;
-                case PY_TOKEN_DEL_MINUS_EQUAL: EMIT(binary_op, RT_BINARY_OP_INPLACE_SUBTRACT); break;
-                case PY_TOKEN_DEL_STAR_EQUAL: EMIT(binary_op, RT_BINARY_OP_INPLACE_MULTIPLY); break;
-                case PY_TOKEN_DEL_DBL_SLASH_EQUAL: EMIT(binary_op, RT_BINARY_OP_INPLACE_FLOOR_DIVIDE); break;
-                case PY_TOKEN_DEL_SLASH_EQUAL: EMIT(binary_op, RT_BINARY_OP_INPLACE_TRUE_DIVIDE); break;
-                case PY_TOKEN_DEL_PERCENT_EQUAL: EMIT(binary_op, RT_BINARY_OP_INPLACE_MODULO); break;
-                case PY_TOKEN_DEL_DBL_STAR_EQUAL: EMIT(binary_op, RT_BINARY_OP_INPLACE_POWER); break;
+            switch (MP_PARSE_NODE_LEAF_ARG(pns1->nodes[0])) {
+                case MP_TOKEN_DEL_PIPE_EQUAL: EMIT(binary_op, RT_BINARY_OP_INPLACE_OR); break;
+                case MP_TOKEN_DEL_CARET_EQUAL: EMIT(binary_op, RT_BINARY_OP_INPLACE_XOR); break;
+                case MP_TOKEN_DEL_AMPERSAND_EQUAL: EMIT(binary_op, RT_BINARY_OP_INPLACE_AND); break;
+                case MP_TOKEN_DEL_DBL_LESS_EQUAL: EMIT(binary_op, RT_BINARY_OP_INPLACE_LSHIFT); break;
+                case MP_TOKEN_DEL_DBL_MORE_EQUAL: EMIT(binary_op, RT_BINARY_OP_INPLACE_RSHIFT); break;
+                case MP_TOKEN_DEL_PLUS_EQUAL: EMIT(binary_op, RT_BINARY_OP_INPLACE_ADD); break;
+                case MP_TOKEN_DEL_MINUS_EQUAL: EMIT(binary_op, RT_BINARY_OP_INPLACE_SUBTRACT); break;
+                case MP_TOKEN_DEL_STAR_EQUAL: EMIT(binary_op, RT_BINARY_OP_INPLACE_MULTIPLY); break;
+                case MP_TOKEN_DEL_DBL_SLASH_EQUAL: EMIT(binary_op, RT_BINARY_OP_INPLACE_FLOOR_DIVIDE); break;
+                case MP_TOKEN_DEL_SLASH_EQUAL: EMIT(binary_op, RT_BINARY_OP_INPLACE_TRUE_DIVIDE); break;
+                case MP_TOKEN_DEL_PERCENT_EQUAL: EMIT(binary_op, RT_BINARY_OP_INPLACE_MODULO); break;
+                case MP_TOKEN_DEL_DBL_STAR_EQUAL: EMIT(binary_op, RT_BINARY_OP_INPLACE_POWER); break;
                 default: assert(0); // shouldn't happen
             }
             c_assign(comp, pns->nodes[0], ASSIGN_AUG_STORE); // lhs store for aug assign
         } else if (kind == PN_expr_stmt_assign_list) {
-            int rhs = PY_PARSE_NODE_STRUCT_NUM_NODES(pns1) - 1;
-            compile_node(comp, ((py_parse_node_struct_t*)pns1->nodes[rhs])->nodes[0]); // rhs
+            int rhs = MP_PARSE_NODE_STRUCT_NUM_NODES(pns1) - 1;
+            compile_node(comp, ((mp_parse_node_struct_t*)pns1->nodes[rhs])->nodes[0]); // rhs
             // following CPython, we store left-most first
             if (rhs > 0) {
                 EMIT(dup_top);
@@ -1781,28 +1781,28 @@ void compile_expr_stmt(compiler_t *comp, py_parse_node_struct_t *pns) {
                 if (i + 1 < rhs) {
                     EMIT(dup_top);
                 }
-                c_assign(comp, ((py_parse_node_struct_t*)pns1->nodes[i])->nodes[0], ASSIGN_STORE); // middle store
+                c_assign(comp, ((mp_parse_node_struct_t*)pns1->nodes[i])->nodes[0], ASSIGN_STORE); // middle store
             }
         } else if (kind == PN_expr_stmt_assign) {
-            if (PY_PARSE_NODE_IS_STRUCT_KIND(pns1->nodes[0], PN_testlist_star_expr)
-                && PY_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_testlist_star_expr)
-                && PY_PARSE_NODE_STRUCT_NUM_NODES((py_parse_node_struct_t*)pns1->nodes[0]) == 2
-                && PY_PARSE_NODE_STRUCT_NUM_NODES((py_parse_node_struct_t*)pns->nodes[0]) == 2) {
+            if (MP_PARSE_NODE_IS_STRUCT_KIND(pns1->nodes[0], PN_testlist_star_expr)
+                && MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_testlist_star_expr)
+                && MP_PARSE_NODE_STRUCT_NUM_NODES((mp_parse_node_struct_t*)pns1->nodes[0]) == 2
+                && MP_PARSE_NODE_STRUCT_NUM_NODES((mp_parse_node_struct_t*)pns->nodes[0]) == 2) {
                 // optimisation for a, b = c, d; to match CPython's optimisation
-                py_parse_node_struct_t* pns10 = (py_parse_node_struct_t*)pns1->nodes[0];
-                py_parse_node_struct_t* pns0 = (py_parse_node_struct_t*)pns->nodes[0];
+                mp_parse_node_struct_t* pns10 = (mp_parse_node_struct_t*)pns1->nodes[0];
+                mp_parse_node_struct_t* pns0 = (mp_parse_node_struct_t*)pns->nodes[0];
                 compile_node(comp, pns10->nodes[0]); // rhs
                 compile_node(comp, pns10->nodes[1]); // rhs
                 EMIT(rot_two);
                 c_assign(comp, pns0->nodes[0], ASSIGN_STORE); // lhs store
                 c_assign(comp, pns0->nodes[1], ASSIGN_STORE); // lhs store
-            } else if (PY_PARSE_NODE_IS_STRUCT_KIND(pns1->nodes[0], PN_testlist_star_expr)
-                && PY_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_testlist_star_expr)
-                && PY_PARSE_NODE_STRUCT_NUM_NODES((py_parse_node_struct_t*)pns1->nodes[0]) == 3
-                && PY_PARSE_NODE_STRUCT_NUM_NODES((py_parse_node_struct_t*)pns->nodes[0]) == 3) {
+            } else if (MP_PARSE_NODE_IS_STRUCT_KIND(pns1->nodes[0], PN_testlist_star_expr)
+                && MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_testlist_star_expr)
+                && MP_PARSE_NODE_STRUCT_NUM_NODES((mp_parse_node_struct_t*)pns1->nodes[0]) == 3
+                && MP_PARSE_NODE_STRUCT_NUM_NODES((mp_parse_node_struct_t*)pns->nodes[0]) == 3) {
                 // optimisation for a, b, c = d, e, f; to match CPython's optimisation
-                py_parse_node_struct_t* pns10 = (py_parse_node_struct_t*)pns1->nodes[0];
-                py_parse_node_struct_t* pns0 = (py_parse_node_struct_t*)pns->nodes[0];
+                mp_parse_node_struct_t* pns10 = (mp_parse_node_struct_t*)pns1->nodes[0];
+                mp_parse_node_struct_t* pns0 = (mp_parse_node_struct_t*)pns->nodes[0];
                 compile_node(comp, pns10->nodes[0]); // rhs
                 compile_node(comp, pns10->nodes[1]); // rhs
                 compile_node(comp, pns10->nodes[2]); // rhs
@@ -1822,8 +1822,8 @@ void compile_expr_stmt(compiler_t *comp, py_parse_node_struct_t *pns) {
     }
 }
 
-void c_binary_op(compiler_t *comp, py_parse_node_struct_t *pns, rt_binary_op_t binary_op) {
-    int num_nodes = PY_PARSE_NODE_STRUCT_NUM_NODES(pns);
+void c_binary_op(compiler_t *comp, mp_parse_node_struct_t *pns, rt_binary_op_t binary_op) {
+    int num_nodes = MP_PARSE_NODE_STRUCT_NUM_NODES(pns);
     compile_node(comp, pns->nodes[0]);
     for (int i = 1; i < num_nodes; i += 1) {
         compile_node(comp, pns->nodes[i]);
@@ -1831,9 +1831,9 @@ void c_binary_op(compiler_t *comp, py_parse_node_struct_t *pns, rt_binary_op_t b
     }
 }
 
-void compile_test_if_expr(compiler_t *comp, py_parse_node_struct_t *pns) {
-    assert(PY_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[1], PN_test_if_else));
-    py_parse_node_struct_t *pns_test_if_else = (py_parse_node_struct_t*)pns->nodes[1];
+void compile_test_if_expr(compiler_t *comp, mp_parse_node_struct_t *pns) {
+    assert(MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[1], PN_test_if_else));
+    mp_parse_node_struct_t *pns_test_if_else = (mp_parse_node_struct_t*)pns->nodes[1];
 
     int stack_size = EMIT(get_stack_size);
     int l_fail = comp_next_label(comp);
@@ -1847,16 +1847,16 @@ void compile_test_if_expr(compiler_t *comp, py_parse_node_struct_t *pns) {
     EMIT(label_assign, l_end);
 }
 
-void compile_lambdef(compiler_t *comp, py_parse_node_struct_t *pns) {
+void compile_lambdef(compiler_t *comp, mp_parse_node_struct_t *pns) {
     // TODO default params etc for lambda; possibly just use funcdef code
-    //py_parse_node_t pn_params = pns->nodes[0];
-    //py_parse_node_t pn_body = pns->nodes[1];
+    //mp_parse_node_t pn_params = pns->nodes[0];
+    //mp_parse_node_t pn_body = pns->nodes[1];
 
     if (comp->pass == PASS_1) {
         // create a new scope for this lambda
-        scope_t *s = scope_new_and_link(comp, SCOPE_LAMBDA, (py_parse_node_t)pns, comp->scope_cur->emit_options);
+        scope_t *s = scope_new_and_link(comp, SCOPE_LAMBDA, (mp_parse_node_t)pns, comp->scope_cur->emit_options);
         // store the lambda scope so the compiling function (this one) can use it at each pass
-        pns->nodes[2] = (py_parse_node_t)s;
+        pns->nodes[2] = (mp_parse_node_t)s;
     }
 
     // get the scope for this lambda
@@ -1866,9 +1866,9 @@ void compile_lambdef(compiler_t *comp, py_parse_node_struct_t *pns) {
     close_over_variables_etc(comp, this_scope, 0, 0);
 }
 
-void compile_or_test(compiler_t *comp, py_parse_node_struct_t *pns) {
+void compile_or_test(compiler_t *comp, mp_parse_node_struct_t *pns) {
     int l_end = comp_next_label(comp);
-    int n = PY_PARSE_NODE_STRUCT_NUM_NODES(pns);
+    int n = MP_PARSE_NODE_STRUCT_NUM_NODES(pns);
     for (int i = 0; i < n; i += 1) {
         compile_node(comp, pns->nodes[i]);
         if (i + 1 < n) {
@@ -1878,9 +1878,9 @@ void compile_or_test(compiler_t *comp, py_parse_node_struct_t *pns) {
     EMIT(label_assign, l_end);
 }
 
-void compile_and_test(compiler_t *comp, py_parse_node_struct_t *pns) {
+void compile_and_test(compiler_t *comp, mp_parse_node_struct_t *pns) {
     int l_end = comp_next_label(comp);
-    int n = PY_PARSE_NODE_STRUCT_NUM_NODES(pns);
+    int n = MP_PARSE_NODE_STRUCT_NUM_NODES(pns);
     for (int i = 0; i < n; i += 1) {
         compile_node(comp, pns->nodes[i]);
         if (i + 1 < n) {
@@ -1890,14 +1890,14 @@ void compile_and_test(compiler_t *comp, py_parse_node_struct_t *pns) {
     EMIT(label_assign, l_end);
 }
 
-void compile_not_test_2(compiler_t *comp, py_parse_node_struct_t *pns) {
+void compile_not_test_2(compiler_t *comp, mp_parse_node_struct_t *pns) {
     compile_node(comp, pns->nodes[0]);
     EMIT(unary_op, RT_UNARY_OP_NOT);
 }
 
-void compile_comparison(compiler_t *comp, py_parse_node_struct_t *pns) {
+void compile_comparison(compiler_t *comp, mp_parse_node_struct_t *pns) {
     int stack_size = EMIT(get_stack_size);
-    int num_nodes = PY_PARSE_NODE_STRUCT_NUM_NODES(pns);
+    int num_nodes = MP_PARSE_NODE_STRUCT_NUM_NODES(pns);
     compile_node(comp, pns->nodes[0]);
     bool multi = (num_nodes > 3);
     int l_fail = 0;
@@ -1910,27 +1910,27 @@ void compile_comparison(compiler_t *comp, py_parse_node_struct_t *pns) {
             EMIT(dup_top);
             EMIT(rot_three);
         }
-        if (PY_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[i], PY_TOKEN_OP_LESS)) {
+        if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[i], MP_TOKEN_OP_LESS)) {
             EMIT(compare_op, RT_COMPARE_OP_LESS);
-        } else if (PY_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[i], PY_TOKEN_OP_MORE)) {
+        } else if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[i], MP_TOKEN_OP_MORE)) {
             EMIT(compare_op, RT_COMPARE_OP_MORE);
-        } else if (PY_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[i], PY_TOKEN_OP_DBL_EQUAL)) {
+        } else if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[i], MP_TOKEN_OP_DBL_EQUAL)) {
             EMIT(compare_op, RT_COMPARE_OP_EQUAL);
-        } else if (PY_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[i], PY_TOKEN_OP_LESS_EQUAL)) {
+        } else if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[i], MP_TOKEN_OP_LESS_EQUAL)) {
             EMIT(compare_op, RT_COMPARE_OP_LESS_EQUAL);
-        } else if (PY_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[i], PY_TOKEN_OP_MORE_EQUAL)) {
+        } else if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[i], MP_TOKEN_OP_MORE_EQUAL)) {
             EMIT(compare_op, RT_COMPARE_OP_MORE_EQUAL);
-        } else if (PY_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[i], PY_TOKEN_OP_NOT_EQUAL)) {
+        } else if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[i], MP_TOKEN_OP_NOT_EQUAL)) {
             EMIT(compare_op, RT_COMPARE_OP_NOT_EQUAL);
-        } else if (PY_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[i], PY_TOKEN_KW_IN)) {
+        } else if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[i], MP_TOKEN_KW_IN)) {
             EMIT(compare_op, RT_COMPARE_OP_IN);
-        } else if (PY_PARSE_NODE_IS_STRUCT(pns->nodes[i])) {
-            py_parse_node_struct_t *pns2 = (py_parse_node_struct_t*)pns->nodes[i];
-            int kind = PY_PARSE_NODE_STRUCT_KIND(pns2);
+        } else if (MP_PARSE_NODE_IS_STRUCT(pns->nodes[i])) {
+            mp_parse_node_struct_t *pns2 = (mp_parse_node_struct_t*)pns->nodes[i];
+            int kind = MP_PARSE_NODE_STRUCT_KIND(pns2);
             if (kind == PN_comp_op_not_in) {
                 EMIT(compare_op, RT_COMPARE_OP_NOT_IN);
             } else if (kind == PN_comp_op_is) {
-                if (PY_PARSE_NODE_IS_NULL(pns2->nodes[0])) {
+                if (MP_PARSE_NODE_IS_NULL(pns2->nodes[0])) {
                     EMIT(compare_op, RT_COMPARE_OP_IS);
                 } else {
                     EMIT(compare_op, RT_COMPARE_OP_IS_NOT);
@@ -1958,33 +1958,33 @@ void compile_comparison(compiler_t *comp, py_parse_node_struct_t *pns) {
     }
 }
 
-void compile_star_expr(compiler_t *comp, py_parse_node_struct_t *pns) {
+void compile_star_expr(compiler_t *comp, mp_parse_node_struct_t *pns) {
     // TODO
     assert(0);
     compile_node(comp, pns->nodes[0]);
     //EMIT(unary_op, "UNARY_STAR");
 }
 
-void compile_expr(compiler_t *comp, py_parse_node_struct_t *pns) {
+void compile_expr(compiler_t *comp, mp_parse_node_struct_t *pns) {
     c_binary_op(comp, pns, RT_BINARY_OP_OR);
 }
 
-void compile_xor_expr(compiler_t *comp, py_parse_node_struct_t *pns) {
+void compile_xor_expr(compiler_t *comp, mp_parse_node_struct_t *pns) {
     c_binary_op(comp, pns, RT_BINARY_OP_XOR);
 }
 
-void compile_and_expr(compiler_t *comp, py_parse_node_struct_t *pns) {
+void compile_and_expr(compiler_t *comp, mp_parse_node_struct_t *pns) {
     c_binary_op(comp, pns, RT_BINARY_OP_AND);
 }
 
-void compile_shift_expr(compiler_t *comp, py_parse_node_struct_t *pns) {
-    int num_nodes = PY_PARSE_NODE_STRUCT_NUM_NODES(pns);
+void compile_shift_expr(compiler_t *comp, mp_parse_node_struct_t *pns) {
+    int num_nodes = MP_PARSE_NODE_STRUCT_NUM_NODES(pns);
     compile_node(comp, pns->nodes[0]);
     for (int i = 1; i + 1 < num_nodes; i += 2) {
         compile_node(comp, pns->nodes[i + 1]);
-        if (PY_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[i], PY_TOKEN_OP_DBL_LESS)) {
+        if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[i], MP_TOKEN_OP_DBL_LESS)) {
             EMIT(binary_op, RT_BINARY_OP_LSHIFT);
-        } else if (PY_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[i], PY_TOKEN_OP_DBL_MORE)) {
+        } else if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[i], MP_TOKEN_OP_DBL_MORE)) {
             EMIT(binary_op, RT_BINARY_OP_RSHIFT);
         } else {
             // shouldn't happen
@@ -1993,14 +1993,14 @@ void compile_shift_expr(compiler_t *comp, py_parse_node_struct_t *pns) {
     }
 }
 
-void compile_arith_expr(compiler_t *comp, py_parse_node_struct_t *pns) {
-    int num_nodes = PY_PARSE_NODE_STRUCT_NUM_NODES(pns);
+void compile_arith_expr(compiler_t *comp, mp_parse_node_struct_t *pns) {
+    int num_nodes = MP_PARSE_NODE_STRUCT_NUM_NODES(pns);
     compile_node(comp, pns->nodes[0]);
     for (int i = 1; i + 1 < num_nodes; i += 2) {
         compile_node(comp, pns->nodes[i + 1]);
-        if (PY_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[i], PY_TOKEN_OP_PLUS)) {
+        if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[i], MP_TOKEN_OP_PLUS)) {
             EMIT(binary_op, RT_BINARY_OP_ADD);
-        } else if (PY_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[i], PY_TOKEN_OP_MINUS)) {
+        } else if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[i], MP_TOKEN_OP_MINUS)) {
             EMIT(binary_op, RT_BINARY_OP_SUBTRACT);
         } else {
             // shouldn't happen
@@ -2009,18 +2009,18 @@ void compile_arith_expr(compiler_t *comp, py_parse_node_struct_t *pns) {
     }
 }
 
-void compile_term(compiler_t *comp, py_parse_node_struct_t *pns) {
-    int num_nodes = PY_PARSE_NODE_STRUCT_NUM_NODES(pns);
+void compile_term(compiler_t *comp, mp_parse_node_struct_t *pns) {
+    int num_nodes = MP_PARSE_NODE_STRUCT_NUM_NODES(pns);
     compile_node(comp, pns->nodes[0]);
     for (int i = 1; i + 1 < num_nodes; i += 2) {
         compile_node(comp, pns->nodes[i + 1]);
-        if (PY_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[i], PY_TOKEN_OP_STAR)) {
+        if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[i], MP_TOKEN_OP_STAR)) {
             EMIT(binary_op, RT_BINARY_OP_MULTIPLY);
-        } else if (PY_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[i], PY_TOKEN_OP_DBL_SLASH)) {
+        } else if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[i], MP_TOKEN_OP_DBL_SLASH)) {
             EMIT(binary_op, RT_BINARY_OP_FLOOR_DIVIDE);
-        } else if (PY_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[i], PY_TOKEN_OP_SLASH)) {
+        } else if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[i], MP_TOKEN_OP_SLASH)) {
             EMIT(binary_op, RT_BINARY_OP_TRUE_DIVIDE);
-        } else if (PY_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[i], PY_TOKEN_OP_PERCENT)) {
+        } else if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[i], MP_TOKEN_OP_PERCENT)) {
             EMIT(binary_op, RT_BINARY_OP_MODULO);
         } else {
             // shouldn't happen
@@ -2029,13 +2029,13 @@ void compile_term(compiler_t *comp, py_parse_node_struct_t *pns) {
     }
 }
 
-void compile_factor_2(compiler_t *comp, py_parse_node_struct_t *pns) {
+void compile_factor_2(compiler_t *comp, mp_parse_node_struct_t *pns) {
     compile_node(comp, pns->nodes[1]);
-    if (PY_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[0], PY_TOKEN_OP_PLUS)) {
+    if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[0], MP_TOKEN_OP_PLUS)) {
         EMIT(unary_op, RT_UNARY_OP_POSITIVE);
-    } else if (PY_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[0], PY_TOKEN_OP_MINUS)) {
+    } else if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[0], MP_TOKEN_OP_MINUS)) {
         EMIT(unary_op, RT_UNARY_OP_NEGATIVE);
-    } else if (PY_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[0], PY_TOKEN_OP_TILDE)) {
+    } else if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[0], MP_TOKEN_OP_TILDE)) {
         EMIT(unary_op, RT_UNARY_OP_INVERT);
     } else {
         // shouldn't happen
@@ -2043,7 +2043,7 @@ void compile_factor_2(compiler_t *comp, py_parse_node_struct_t *pns) {
     }
 }
 
-void compile_trailer_paren_helper(compiler_t *comp, py_parse_node_struct_t *pns, bool is_method_call) {
+void compile_trailer_paren_helper(compiler_t *comp, mp_parse_node_struct_t *pns, bool is_method_call) {
     // function to call is on top of stack
 
     int old_n_arg_keyword = comp->n_arg_keyword;
@@ -2075,14 +2075,14 @@ void compile_trailer_paren_helper(compiler_t *comp, py_parse_node_struct_t *pns,
     comp->have_dbl_star_arg = old_have_dbl_star_arg;
 }
 
-void compile_power_trailers(compiler_t *comp, py_parse_node_struct_t *pns) {
-    int num_nodes = PY_PARSE_NODE_STRUCT_NUM_NODES(pns);
+void compile_power_trailers(compiler_t *comp, mp_parse_node_struct_t *pns) {
+    int num_nodes = MP_PARSE_NODE_STRUCT_NUM_NODES(pns);
     for (int i = 0; i < num_nodes; i++) {
-        if (i + 1 < num_nodes && PY_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[i], PN_trailer_period) && PY_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[i + 1], PN_trailer_paren)) {
+        if (i + 1 < num_nodes && MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[i], PN_trailer_period) && MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[i + 1], PN_trailer_paren)) {
             // optimisation for method calls a.f(...), following PyPy
-            py_parse_node_struct_t *pns_period = (py_parse_node_struct_t*)pns->nodes[i];
-            py_parse_node_struct_t *pns_paren = (py_parse_node_struct_t*)pns->nodes[i + 1];
-            EMIT(load_method, PY_PARSE_NODE_LEAF_ARG(pns_period->nodes[0])); // get the method
+            mp_parse_node_struct_t *pns_period = (mp_parse_node_struct_t*)pns->nodes[i];
+            mp_parse_node_struct_t *pns_paren = (mp_parse_node_struct_t*)pns->nodes[i + 1];
+            EMIT(load_method, MP_PARSE_NODE_LEAF_ARG(pns_period->nodes[0])); // get the method
             compile_trailer_paren_helper(comp, pns_paren, true);
             i += 1;
         } else {
@@ -2091,29 +2091,29 @@ void compile_power_trailers(compiler_t *comp, py_parse_node_struct_t *pns) {
     }
 }
 
-void compile_power_dbl_star(compiler_t *comp, py_parse_node_struct_t *pns) {
+void compile_power_dbl_star(compiler_t *comp, mp_parse_node_struct_t *pns) {
     compile_node(comp, pns->nodes[0]);
     EMIT(binary_op, RT_BINARY_OP_POWER);
 }
 
-void compile_atom_string(compiler_t *comp, py_parse_node_struct_t *pns) {
+void compile_atom_string(compiler_t *comp, mp_parse_node_struct_t *pns) {
     // a list of strings
 
     // check type of list (string or bytes) and count total number of bytes
-    int n = PY_PARSE_NODE_STRUCT_NUM_NODES(pns);
+    int n = MP_PARSE_NODE_STRUCT_NUM_NODES(pns);
     int n_bytes = 0;
-    int string_kind = PY_PARSE_NODE_NULL;
+    int string_kind = MP_PARSE_NODE_NULL;
     for (int i = 0; i < n; i++) {
-        assert(PY_PARSE_NODE_IS_LEAF(pns->nodes[i]));
-        int pn_kind = PY_PARSE_NODE_LEAF_KIND(pns->nodes[i]);
-        assert(pn_kind == PY_PARSE_NODE_STRING || pn_kind == PY_PARSE_NODE_BYTES);
+        assert(MP_PARSE_NODE_IS_LEAF(pns->nodes[i]));
+        int pn_kind = MP_PARSE_NODE_LEAF_KIND(pns->nodes[i]);
+        assert(pn_kind == MP_PARSE_NODE_STRING || pn_kind == MP_PARSE_NODE_BYTES);
         if (i == 0) {
             string_kind = pn_kind;
         } else if (pn_kind != string_kind) {
             printf("SyntaxError: cannot mix bytes and nonbytes literals\n");
             return;
         }
-        const char *str = qstr_str(PY_PARSE_NODE_LEAF_ARG(pns->nodes[i]));
+        const char *str = qstr_str(MP_PARSE_NODE_LEAF_ARG(pns->nodes[i]));
         n_bytes += strlen(str);
     }
 
@@ -2123,24 +2123,24 @@ void compile_atom_string(compiler_t *comp, py_parse_node_struct_t *pns) {
 
     // concatenate string/bytes
     for (int i = 0; i < n; i++) {
-        const char *str = qstr_str(PY_PARSE_NODE_LEAF_ARG(pns->nodes[i]));
+        const char *str = qstr_str(MP_PARSE_NODE_LEAF_ARG(pns->nodes[i]));
         strcat(cat_str, str);
     }
 
-    EMIT(load_const_str, qstr_from_str_take(cat_str), string_kind == PY_PARSE_NODE_BYTES);
+    EMIT(load_const_str, qstr_from_str_take(cat_str), string_kind == MP_PARSE_NODE_BYTES);
 }
 
 // pns needs to have 2 nodes, first is lhs of comprehension, second is PN_comp_for node
-void compile_comprehension(compiler_t *comp, py_parse_node_struct_t *pns, scope_kind_t kind) {
-    assert(PY_PARSE_NODE_STRUCT_NUM_NODES(pns) == 2);
-    assert(PY_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[1], PN_comp_for));
-    py_parse_node_struct_t *pns_comp_for = (py_parse_node_struct_t*)pns->nodes[1];
+void compile_comprehension(compiler_t *comp, mp_parse_node_struct_t *pns, scope_kind_t kind) {
+    assert(MP_PARSE_NODE_STRUCT_NUM_NODES(pns) == 2);
+    assert(MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[1], PN_comp_for));
+    mp_parse_node_struct_t *pns_comp_for = (mp_parse_node_struct_t*)pns->nodes[1];
 
     if (comp->pass == PASS_1) {
         // create a new scope for this comprehension
-        scope_t *s = scope_new_and_link(comp, kind, (py_parse_node_t)pns, comp->scope_cur->emit_options);
+        scope_t *s = scope_new_and_link(comp, kind, (mp_parse_node_t)pns, comp->scope_cur->emit_options);
         // store the comprehension scope so the compiling function (this one) can use it at each pass
-        pns_comp_for->nodes[3] = (py_parse_node_t)s;
+        pns_comp_for->nodes[3] = (mp_parse_node_t)s;
     }
 
     // get the scope for this comprehension
@@ -2154,23 +2154,23 @@ void compile_comprehension(compiler_t *comp, py_parse_node_struct_t *pns, scope_
     EMIT(call_function, 1, 0, false, false);
 }
 
-void compile_atom_paren(compiler_t *comp, py_parse_node_struct_t *pns) {
-    if (PY_PARSE_NODE_IS_NULL(pns->nodes[0])) {
+void compile_atom_paren(compiler_t *comp, mp_parse_node_struct_t *pns) {
+    if (MP_PARSE_NODE_IS_NULL(pns->nodes[0])) {
         // an empty tuple
-        c_tuple(comp, PY_PARSE_NODE_NULL, NULL);
-    } else if (PY_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_testlist_comp)) {
-        pns = (py_parse_node_struct_t*)pns->nodes[0];
-        assert(!PY_PARSE_NODE_IS_NULL(pns->nodes[1]));
-        if (PY_PARSE_NODE_IS_STRUCT(pns->nodes[1])) {
-            py_parse_node_struct_t *pns2 = (py_parse_node_struct_t*)pns->nodes[1];
-            if (PY_PARSE_NODE_STRUCT_KIND(pns2) == PN_testlist_comp_3b) {
+        c_tuple(comp, MP_PARSE_NODE_NULL, NULL);
+    } else if (MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_testlist_comp)) {
+        pns = (mp_parse_node_struct_t*)pns->nodes[0];
+        assert(!MP_PARSE_NODE_IS_NULL(pns->nodes[1]));
+        if (MP_PARSE_NODE_IS_STRUCT(pns->nodes[1])) {
+            mp_parse_node_struct_t *pns2 = (mp_parse_node_struct_t*)pns->nodes[1];
+            if (MP_PARSE_NODE_STRUCT_KIND(pns2) == PN_testlist_comp_3b) {
                 // tuple of one item, with trailing comma
-                assert(PY_PARSE_NODE_IS_NULL(pns2->nodes[0]));
+                assert(MP_PARSE_NODE_IS_NULL(pns2->nodes[0]));
                 c_tuple(comp, pns->nodes[0], NULL);
-            } else if (PY_PARSE_NODE_STRUCT_KIND(pns2) == PN_testlist_comp_3c) {
+            } else if (MP_PARSE_NODE_STRUCT_KIND(pns2) == PN_testlist_comp_3c) {
                 // tuple of many items
                 c_tuple(comp, pns->nodes[0], pns2);
-            } else if (PY_PARSE_NODE_STRUCT_KIND(pns2) == PN_comp_for) {
+            } else if (MP_PARSE_NODE_STRUCT_KIND(pns2) == PN_comp_for) {
                 // generator expression
                 compile_comprehension(comp, pns, SCOPE_GEN_EXPR);
             } else {
@@ -2180,7 +2180,7 @@ void compile_atom_paren(compiler_t *comp, py_parse_node_struct_t *pns) {
         } else {
             // tuple with 2 items
             tuple_with_2_items:
-            c_tuple(comp, PY_PARSE_NODE_NULL, pns);
+            c_tuple(comp, MP_PARSE_NODE_NULL, pns);
         }
     } else {
         // parenthesis around a single item, is just that item
@@ -2188,25 +2188,25 @@ void compile_atom_paren(compiler_t *comp, py_parse_node_struct_t *pns) {
     }
 }
 
-void compile_atom_bracket(compiler_t *comp, py_parse_node_struct_t *pns) {
-    if (PY_PARSE_NODE_IS_NULL(pns->nodes[0])) {
+void compile_atom_bracket(compiler_t *comp, mp_parse_node_struct_t *pns) {
+    if (MP_PARSE_NODE_IS_NULL(pns->nodes[0])) {
         // empty list
         EMIT(build_list, 0);
-    } else if (PY_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_testlist_comp)) {
-        py_parse_node_struct_t *pns2 = (py_parse_node_struct_t*)pns->nodes[0];
-        if (PY_PARSE_NODE_IS_STRUCT(pns2->nodes[1])) {
-            py_parse_node_struct_t *pns3 = (py_parse_node_struct_t*)pns2->nodes[1];
-            if (PY_PARSE_NODE_STRUCT_KIND(pns3) == PN_testlist_comp_3b) {
+    } else if (MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_testlist_comp)) {
+        mp_parse_node_struct_t *pns2 = (mp_parse_node_struct_t*)pns->nodes[0];
+        if (MP_PARSE_NODE_IS_STRUCT(pns2->nodes[1])) {
+            mp_parse_node_struct_t *pns3 = (mp_parse_node_struct_t*)pns2->nodes[1];
+            if (MP_PARSE_NODE_STRUCT_KIND(pns3) == PN_testlist_comp_3b) {
                 // list of one item, with trailing comma
-                assert(PY_PARSE_NODE_IS_NULL(pns3->nodes[0]));
+                assert(MP_PARSE_NODE_IS_NULL(pns3->nodes[0]));
                 compile_node(comp, pns2->nodes[0]);
                 EMIT(build_list, 1);
-            } else if (PY_PARSE_NODE_STRUCT_KIND(pns3) == PN_testlist_comp_3c) {
+            } else if (MP_PARSE_NODE_STRUCT_KIND(pns3) == PN_testlist_comp_3c) {
                 // list of many items
                 compile_node(comp, pns2->nodes[0]);
                 compile_generic_all_nodes(comp, pns3);
-                EMIT(build_list, 1 + PY_PARSE_NODE_STRUCT_NUM_NODES(pns3));
-            } else if (PY_PARSE_NODE_STRUCT_KIND(pns3) == PN_comp_for) {
+                EMIT(build_list, 1 + MP_PARSE_NODE_STRUCT_NUM_NODES(pns3));
+            } else if (MP_PARSE_NODE_STRUCT_KIND(pns3) == PN_comp_for) {
                 // list comprehension
                 compile_comprehension(comp, pns2, SCOPE_LIST_COMP);
             } else {
@@ -2227,31 +2227,31 @@ void compile_atom_bracket(compiler_t *comp, py_parse_node_struct_t *pns) {
     }
 }
 
-void compile_atom_brace(compiler_t *comp, py_parse_node_struct_t *pns) {
-    py_parse_node_t pn = pns->nodes[0];
-    if (PY_PARSE_NODE_IS_NULL(pn)) {
+void compile_atom_brace(compiler_t *comp, mp_parse_node_struct_t *pns) {
+    mp_parse_node_t pn = pns->nodes[0];
+    if (MP_PARSE_NODE_IS_NULL(pn)) {
         // empty dict
         EMIT(build_map, 0);
-    } else if (PY_PARSE_NODE_IS_STRUCT(pn)) {
-        pns = (py_parse_node_struct_t*)pn;
-        if (PY_PARSE_NODE_STRUCT_KIND(pns) == PN_dictorsetmaker_item) {
+    } else if (MP_PARSE_NODE_IS_STRUCT(pn)) {
+        pns = (mp_parse_node_struct_t*)pn;
+        if (MP_PARSE_NODE_STRUCT_KIND(pns) == PN_dictorsetmaker_item) {
             // dict with one element
             EMIT(build_map, 1);
             compile_node(comp, pn);
             EMIT(store_map);
-        } else if (PY_PARSE_NODE_STRUCT_KIND(pns) == PN_dictorsetmaker) {
-            assert(PY_PARSE_NODE_IS_STRUCT(pns->nodes[1])); // should succeed
-            py_parse_node_struct_t *pns1 = (py_parse_node_struct_t*)pns->nodes[1];
-            if (PY_PARSE_NODE_STRUCT_KIND(pns1) == PN_dictorsetmaker_list) {
+        } else if (MP_PARSE_NODE_STRUCT_KIND(pns) == PN_dictorsetmaker) {
+            assert(MP_PARSE_NODE_IS_STRUCT(pns->nodes[1])); // should succeed
+            mp_parse_node_struct_t *pns1 = (mp_parse_node_struct_t*)pns->nodes[1];
+            if (MP_PARSE_NODE_STRUCT_KIND(pns1) == PN_dictorsetmaker_list) {
                 // dict/set with multiple elements
 
                 // get tail elements (2nd, 3rd, ...)
-                py_parse_node_t *nodes;
+                mp_parse_node_t *nodes;
                 int n = list_get(&pns1->nodes[0], PN_dictorsetmaker_list2, &nodes);
 
                 // first element sets whether it's a dict or set
                 bool is_dict;
-                if (PY_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_dictorsetmaker_item)) {
+                if (MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_dictorsetmaker_item)) {
                     // a dictionary
                     EMIT(build_map, 1 + n);
                     compile_node(comp, pns->nodes[0]);
@@ -2265,8 +2265,8 @@ void compile_atom_brace(compiler_t *comp, py_parse_node_struct_t *pns) {
 
                 // process rest of elements
                 for (int i = 0; i < n; i++) {
-                    py_parse_node_t pn = nodes[i];
-                    bool is_key_value = PY_PARSE_NODE_IS_STRUCT_KIND(pn, PN_dictorsetmaker_item);
+                    mp_parse_node_t pn = nodes[i];
+                    bool is_key_value = MP_PARSE_NODE_IS_STRUCT_KIND(pn, PN_dictorsetmaker_item);
                     compile_node(comp, pn);
                     if (is_dict) {
                         if (!is_key_value) {
@@ -2286,9 +2286,9 @@ void compile_atom_brace(compiler_t *comp, py_parse_node_struct_t *pns) {
                 if (!is_dict) {
                     EMIT(build_set, 1 + n);
                 }
-            } else if (PY_PARSE_NODE_STRUCT_KIND(pns1) == PN_comp_for) {
+            } else if (MP_PARSE_NODE_STRUCT_KIND(pns1) == PN_comp_for) {
                 // dict/set comprehension
-                if (PY_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_dictorsetmaker_item)) {
+                if (MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_dictorsetmaker_item)) {
                     // a dictionary comprehension
                     compile_comprehension(comp, pns, SCOPE_DICT_COMP);
                 } else {
@@ -2311,34 +2311,34 @@ void compile_atom_brace(compiler_t *comp, py_parse_node_struct_t *pns) {
     }
 }
 
-void compile_trailer_paren(compiler_t *comp, py_parse_node_struct_t *pns) {
+void compile_trailer_paren(compiler_t *comp, mp_parse_node_struct_t *pns) {
     compile_trailer_paren_helper(comp, pns, false);
 }
 
-void compile_trailer_bracket(compiler_t *comp, py_parse_node_struct_t *pns) {
+void compile_trailer_bracket(compiler_t *comp, mp_parse_node_struct_t *pns) {
     // object who's index we want is on top of stack
     compile_node(comp, pns->nodes[0]); // the index
     EMIT(binary_op, RT_BINARY_OP_SUBSCR);
 }
 
-void compile_trailer_period(compiler_t *comp, py_parse_node_struct_t *pns) {
+void compile_trailer_period(compiler_t *comp, mp_parse_node_struct_t *pns) {
     // object who's attribute we want is on top of stack
-    EMIT(load_attr, PY_PARSE_NODE_LEAF_ARG(pns->nodes[0])); // attribute to get
+    EMIT(load_attr, MP_PARSE_NODE_LEAF_ARG(pns->nodes[0])); // attribute to get
 }
 
-void compile_subscript_3_helper(compiler_t *comp, py_parse_node_struct_t *pns) {
-    assert(PY_PARSE_NODE_STRUCT_KIND(pns) == PN_subscript_3); // should always be
-    py_parse_node_t pn = pns->nodes[0];
-    if (PY_PARSE_NODE_IS_NULL(pn)) {
+void compile_subscript_3_helper(compiler_t *comp, mp_parse_node_struct_t *pns) {
+    assert(MP_PARSE_NODE_STRUCT_KIND(pns) == PN_subscript_3); // should always be
+    mp_parse_node_t pn = pns->nodes[0];
+    if (MP_PARSE_NODE_IS_NULL(pn)) {
         // [?:]
-        EMIT(load_const_tok, PY_TOKEN_KW_NONE);
+        EMIT(load_const_tok, MP_TOKEN_KW_NONE);
         EMIT(build_slice, 2);
-    } else if (PY_PARSE_NODE_IS_STRUCT(pn)) {
-        pns = (py_parse_node_struct_t*)pn;
-        if (PY_PARSE_NODE_STRUCT_KIND(pns) == PN_subscript_3c) {
-            EMIT(load_const_tok, PY_TOKEN_KW_NONE);
+    } else if (MP_PARSE_NODE_IS_STRUCT(pn)) {
+        pns = (mp_parse_node_struct_t*)pn;
+        if (MP_PARSE_NODE_STRUCT_KIND(pns) == PN_subscript_3c) {
+            EMIT(load_const_tok, MP_TOKEN_KW_NONE);
             pn = pns->nodes[0];
-            if (PY_PARSE_NODE_IS_NULL(pn)) {
+            if (MP_PARSE_NODE_IS_NULL(pn)) {
                 // [?::]
                 EMIT(build_slice, 2);
             } else {
@@ -2346,12 +2346,12 @@ void compile_subscript_3_helper(compiler_t *comp, py_parse_node_struct_t *pns) {
                 compile_node(comp, pn);
                 EMIT(build_slice, 3);
             }
-        } else if (PY_PARSE_NODE_STRUCT_KIND(pns) == PN_subscript_3d) {
+        } else if (MP_PARSE_NODE_STRUCT_KIND(pns) == PN_subscript_3d) {
             compile_node(comp, pns->nodes[0]);
-            assert(PY_PARSE_NODE_IS_STRUCT(pns->nodes[1])); // should always be
-            pns = (py_parse_node_struct_t*)pns->nodes[1];
-            assert(PY_PARSE_NODE_STRUCT_KIND(pns) == PN_sliceop); // should always be
-            if (PY_PARSE_NODE_IS_NULL(pns->nodes[0])) {
+            assert(MP_PARSE_NODE_IS_STRUCT(pns->nodes[1])); // should always be
+            pns = (mp_parse_node_struct_t*)pns->nodes[1];
+            assert(MP_PARSE_NODE_STRUCT_KIND(pns) == PN_sliceop); // should always be
+            if (MP_PARSE_NODE_IS_NULL(pns->nodes[0])) {
                 // [?:x:]
                 EMIT(build_slice, 2);
             } else {
@@ -2371,30 +2371,30 @@ void compile_subscript_3_helper(compiler_t *comp, py_parse_node_struct_t *pns) {
     }
 }
 
-void compile_subscript_2(compiler_t *comp, py_parse_node_struct_t *pns) {
+void compile_subscript_2(compiler_t *comp, mp_parse_node_struct_t *pns) {
     compile_node(comp, pns->nodes[0]); // start of slice
-    assert(PY_PARSE_NODE_IS_STRUCT(pns->nodes[1])); // should always be
-    compile_subscript_3_helper(comp, (py_parse_node_struct_t*)pns->nodes[1]);
+    assert(MP_PARSE_NODE_IS_STRUCT(pns->nodes[1])); // should always be
+    compile_subscript_3_helper(comp, (mp_parse_node_struct_t*)pns->nodes[1]);
 }
 
-void compile_subscript_3(compiler_t *comp, py_parse_node_struct_t *pns) {
-    EMIT(load_const_tok, PY_TOKEN_KW_NONE);
+void compile_subscript_3(compiler_t *comp, mp_parse_node_struct_t *pns) {
+    EMIT(load_const_tok, MP_TOKEN_KW_NONE);
     compile_subscript_3_helper(comp, pns);
 }
 
-void compile_dictorsetmaker_item(compiler_t *comp, py_parse_node_struct_t *pns) {
+void compile_dictorsetmaker_item(compiler_t *comp, mp_parse_node_struct_t *pns) {
     // if this is called then we are compiling a dict key:value pair
     compile_node(comp, pns->nodes[1]); // value
     compile_node(comp, pns->nodes[0]); // key
 }
 
-void compile_classdef(compiler_t *comp, py_parse_node_struct_t *pns) {
+void compile_classdef(compiler_t *comp, mp_parse_node_struct_t *pns) {
     qstr cname = compile_classdef_helper(comp, pns, comp->scope_cur->emit_options);
     // store class object into class name
     EMIT(store_id, cname);
 }
 
-void compile_arglist_star(compiler_t *comp, py_parse_node_struct_t *pns) {
+void compile_arglist_star(compiler_t *comp, mp_parse_node_struct_t *pns) {
     if (comp->have_star_arg) {
         printf("SyntaxError?: can't have multiple *x\n");
         return;
@@ -2403,7 +2403,7 @@ void compile_arglist_star(compiler_t *comp, py_parse_node_struct_t *pns) {
     compile_node(comp, pns->nodes[0]);
 }
 
-void compile_arglist_dbl_star(compiler_t *comp, py_parse_node_struct_t *pns) {
+void compile_arglist_dbl_star(compiler_t *comp, mp_parse_node_struct_t *pns) {
     if (comp->have_dbl_star_arg) {
         printf("SyntaxError?: can't have multiple **x\n");
         return;
@@ -2412,18 +2412,18 @@ void compile_arglist_dbl_star(compiler_t *comp, py_parse_node_struct_t *pns) {
     compile_node(comp, pns->nodes[0]);
 }
 
-void compile_argument(compiler_t *comp, py_parse_node_struct_t *pns) {
-    assert(PY_PARSE_NODE_IS_STRUCT(pns->nodes[1])); // should always be
-    py_parse_node_struct_t *pns2 = (py_parse_node_struct_t*)pns->nodes[1];
-    if (PY_PARSE_NODE_STRUCT_KIND(pns2) == PN_argument_3) {
-        if (!PY_PARSE_NODE_IS_ID(pns->nodes[0])) {
+void compile_argument(compiler_t *comp, mp_parse_node_struct_t *pns) {
+    assert(MP_PARSE_NODE_IS_STRUCT(pns->nodes[1])); // should always be
+    mp_parse_node_struct_t *pns2 = (mp_parse_node_struct_t*)pns->nodes[1];
+    if (MP_PARSE_NODE_STRUCT_KIND(pns2) == PN_argument_3) {
+        if (!MP_PARSE_NODE_IS_ID(pns->nodes[0])) {
             printf("SyntaxError?: lhs of keyword argument must be an id\n");
             return;
         }
-        EMIT(load_const_id, PY_PARSE_NODE_LEAF_ARG(pns->nodes[0]));
+        EMIT(load_const_id, MP_PARSE_NODE_LEAF_ARG(pns->nodes[0]));
         compile_node(comp, pns2->nodes[0]);
         comp->n_arg_keyword += 1;
-    } else if (PY_PARSE_NODE_STRUCT_KIND(pns2) == PN_comp_for) {
+    } else if (MP_PARSE_NODE_STRUCT_KIND(pns2) == PN_comp_for) {
         compile_comprehension(comp, pns, SCOPE_GEN_EXPR);
     } else {
         // shouldn't happen
@@ -2431,19 +2431,19 @@ void compile_argument(compiler_t *comp, py_parse_node_struct_t *pns) {
     }
 }
 
-void compile_yield_expr(compiler_t *comp, py_parse_node_struct_t *pns) {
+void compile_yield_expr(compiler_t *comp, mp_parse_node_struct_t *pns) {
     if (comp->scope_cur->kind != SCOPE_FUNCTION) {
         printf("SyntaxError: 'yield' outside function\n");
         return;
     }
-    if (PY_PARSE_NODE_IS_NULL(pns->nodes[0])) {
-        EMIT(load_const_tok, PY_TOKEN_KW_NONE);
+    if (MP_PARSE_NODE_IS_NULL(pns->nodes[0])) {
+        EMIT(load_const_tok, MP_TOKEN_KW_NONE);
         EMIT(yield_value);
-    } else if (PY_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_yield_arg_from)) {
-        pns = (py_parse_node_struct_t*)pns->nodes[0];
+    } else if (MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_yield_arg_from)) {
+        pns = (mp_parse_node_struct_t*)pns->nodes[0];
         compile_node(comp, pns->nodes[0]);
         EMIT(get_iter);
-        EMIT(load_const_tok, PY_TOKEN_KW_NONE);
+        EMIT(load_const_tok, MP_TOKEN_KW_NONE);
         EMIT(yield_from);
     } else {
         compile_node(comp, pns->nodes[0]);
@@ -2451,7 +2451,7 @@ void compile_yield_expr(compiler_t *comp, py_parse_node_struct_t *pns) {
     }
 }
 
-typedef void (*compile_function_t)(compiler_t*, py_parse_node_struct_t*);
+typedef void (*compile_function_t)(compiler_t*, mp_parse_node_struct_t*);
 static compile_function_t compile_function[] = {
     NULL,
 #define nc NULL
@@ -2463,20 +2463,20 @@ static compile_function_t compile_function[] = {
 #undef DEF_RULE
 };
 
-void compile_node(compiler_t *comp, py_parse_node_t pn) {
-    if (PY_PARSE_NODE_IS_NULL(pn)) {
+void compile_node(compiler_t *comp, mp_parse_node_t pn) {
+    if (MP_PARSE_NODE_IS_NULL(pn)) {
         // pass
-    } else if (PY_PARSE_NODE_IS_LEAF(pn)) {
-        int arg = PY_PARSE_NODE_LEAF_ARG(pn);
-        switch (PY_PARSE_NODE_LEAF_KIND(pn)) {
-            case PY_PARSE_NODE_ID: EMIT(load_id, arg); break;
-            case PY_PARSE_NODE_SMALL_INT: EMIT(load_const_small_int, arg); break;
-            case PY_PARSE_NODE_INTEGER: EMIT(load_const_int, arg); break;
-            case PY_PARSE_NODE_DECIMAL: EMIT(load_const_dec, arg); break;
-            case PY_PARSE_NODE_STRING: EMIT(load_const_str, arg, false); break;
-            case PY_PARSE_NODE_BYTES: EMIT(load_const_str, arg, true); break;
-            case PY_PARSE_NODE_TOKEN:
-                if (arg == PY_TOKEN_NEWLINE) {
+    } else if (MP_PARSE_NODE_IS_LEAF(pn)) {
+        int arg = MP_PARSE_NODE_LEAF_ARG(pn);
+        switch (MP_PARSE_NODE_LEAF_KIND(pn)) {
+            case MP_PARSE_NODE_ID: EMIT(load_id, arg); break;
+            case MP_PARSE_NODE_SMALL_INT: EMIT(load_const_small_int, arg); break;
+            case MP_PARSE_NODE_INTEGER: EMIT(load_const_int, arg); break;
+            case MP_PARSE_NODE_DECIMAL: EMIT(load_const_dec, arg); break;
+            case MP_PARSE_NODE_STRING: EMIT(load_const_str, arg, false); break;
+            case MP_PARSE_NODE_BYTES: EMIT(load_const_str, arg, true); break;
+            case MP_PARSE_NODE_TOKEN:
+                if (arg == MP_TOKEN_NEWLINE) {
                     // this can occur when file_input lets through a NEWLINE (eg if file starts with a newline)
                     // or when single_input lets through a NEWLINE (user enters a blank line)
                     // do nothing
@@ -2487,11 +2487,11 @@ void compile_node(compiler_t *comp, py_parse_node_t pn) {
             default: assert(0);
         }
     } else {
-        py_parse_node_struct_t *pns = (py_parse_node_struct_t*)pn;
-        compile_function_t f = compile_function[PY_PARSE_NODE_STRUCT_KIND(pns)];
+        mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)pn;
+        compile_function_t f = compile_function[MP_PARSE_NODE_STRUCT_KIND(pns)];
         if (f == NULL) {
-            printf("node %u cannot be compiled\n", (uint)PY_PARSE_NODE_STRUCT_KIND(pns));
-            py_parse_node_show(pn, 0);
+            printf("node %u cannot be compiled\n", (uint)MP_PARSE_NODE_STRUCT_KIND(pns));
+            mp_parse_node_show(pn, 0);
             assert(0);
         } else {
             f(comp, pns);
@@ -2499,32 +2499,32 @@ void compile_node(compiler_t *comp, py_parse_node_t pn) {
     }
 }
 
-void compile_scope_func_lambda_param(compiler_t *comp, py_parse_node_t pn, pn_kind_t pn_name, pn_kind_t pn_star, pn_kind_t pn_dbl_star, bool allow_annotations) {
+void compile_scope_func_lambda_param(compiler_t *comp, mp_parse_node_t pn, pn_kind_t pn_name, pn_kind_t pn_star, pn_kind_t pn_dbl_star, bool allow_annotations) {
     // TODO verify that *k and **k are last etc
     qstr param_name = 0;
-    py_parse_node_t pn_annotation = PY_PARSE_NODE_NULL;
-    if (PY_PARSE_NODE_IS_ID(pn)) {
-        param_name = PY_PARSE_NODE_LEAF_ARG(pn);
+    mp_parse_node_t pn_annotation = MP_PARSE_NODE_NULL;
+    if (MP_PARSE_NODE_IS_ID(pn)) {
+        param_name = MP_PARSE_NODE_LEAF_ARG(pn);
         if (comp->have_bare_star) {
             // comes after a bare star, so doesn't count as a parameter
         } else {
             comp->scope_cur->num_params += 1;
         }
     } else {
-        assert(PY_PARSE_NODE_IS_STRUCT(pn));
-        py_parse_node_struct_t *pns = (py_parse_node_struct_t*)pn;
-        if (PY_PARSE_NODE_STRUCT_KIND(pns) == pn_name) {
-            param_name = PY_PARSE_NODE_LEAF_ARG(pns->nodes[0]);
+        assert(MP_PARSE_NODE_IS_STRUCT(pn));
+        mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)pn;
+        if (MP_PARSE_NODE_STRUCT_KIND(pns) == pn_name) {
+            param_name = MP_PARSE_NODE_LEAF_ARG(pns->nodes[0]);
             //int node_index = 1; unused
             if (allow_annotations) {
-                if (!PY_PARSE_NODE_IS_NULL(pns->nodes[1])) {
+                if (!MP_PARSE_NODE_IS_NULL(pns->nodes[1])) {
                     // this parameter has an annotation
                     pn_annotation = pns->nodes[1];
                 }
                 //node_index = 2; unused
             }
             /* this is obsolete now that num dict/default params are calculated in compile_funcdef_param
-            if (!PY_PARSE_NODE_IS_NULL(pns->nodes[node_index])) {
+            if (!MP_PARSE_NODE_IS_NULL(pns->nodes[node_index])) {
                 // this parameter has a default value
                 if (comp->have_bare_star) {
                     comp->scope_cur->num_dict_params += 1;
@@ -2538,29 +2538,29 @@ void compile_scope_func_lambda_param(compiler_t *comp, py_parse_node_t pn, pn_ki
             } else {
                 comp->scope_cur->num_params += 1;
             }
-        } else if (PY_PARSE_NODE_STRUCT_KIND(pns) == pn_star) {
-            if (PY_PARSE_NODE_IS_NULL(pns->nodes[0])) {
+        } else if (MP_PARSE_NODE_STRUCT_KIND(pns) == pn_star) {
+            if (MP_PARSE_NODE_IS_NULL(pns->nodes[0])) {
                 // bare star
                 // TODO see http://www.python.org/dev/peps/pep-3102/
                 comp->have_bare_star = true;
                 //assert(comp->scope_cur->num_dict_params == 0);
-            } else if (PY_PARSE_NODE_IS_ID(pns->nodes[0])) {
+            } else if (MP_PARSE_NODE_IS_ID(pns->nodes[0])) {
                 // named star
                 comp->scope_cur->flags |= SCOPE_FLAG_VARARGS;
-                param_name = PY_PARSE_NODE_LEAF_ARG(pns->nodes[0]);
-            } else if (allow_annotations && PY_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_tfpdef)) {
+                param_name = MP_PARSE_NODE_LEAF_ARG(pns->nodes[0]);
+            } else if (allow_annotations && MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_tfpdef)) {
                 // named star with annotation
                 comp->scope_cur->flags |= SCOPE_FLAG_VARARGS;
-                pns = (py_parse_node_struct_t*)pns->nodes[0];
-                param_name = PY_PARSE_NODE_LEAF_ARG(pns->nodes[0]);
+                pns = (mp_parse_node_struct_t*)pns->nodes[0];
+                param_name = MP_PARSE_NODE_LEAF_ARG(pns->nodes[0]);
                 pn_annotation = pns->nodes[1];
             } else {
                 // shouldn't happen
                 assert(0);
             }
-        } else if (PY_PARSE_NODE_STRUCT_KIND(pns) == pn_dbl_star) {
-            param_name = PY_PARSE_NODE_LEAF_ARG(pns->nodes[0]);
-            if (allow_annotations && !PY_PARSE_NODE_IS_NULL(pns->nodes[1])) {
+        } else if (MP_PARSE_NODE_STRUCT_KIND(pns) == pn_dbl_star) {
+            param_name = MP_PARSE_NODE_LEAF_ARG(pns->nodes[0]);
+            if (allow_annotations && !MP_PARSE_NODE_IS_NULL(pns->nodes[1])) {
                 // this parameter has an annotation
                 pn_annotation = pns->nodes[1];
             }
@@ -2572,7 +2572,7 @@ void compile_scope_func_lambda_param(compiler_t *comp, py_parse_node_t pn, pn_ki
     }
 
     if (param_name != 0) {
-        if (!PY_PARSE_NODE_IS_NULL(pn_annotation)) {
+        if (!MP_PARSE_NODE_IS_NULL(pn_annotation)) {
             // TODO this parameter has an annotation
         }
         bool added;
@@ -2586,17 +2586,17 @@ void compile_scope_func_lambda_param(compiler_t *comp, py_parse_node_t pn, pn_ki
     }
 }
 
-void compile_scope_func_param(compiler_t *comp, py_parse_node_t pn) {
+void compile_scope_func_param(compiler_t *comp, mp_parse_node_t pn) {
     compile_scope_func_lambda_param(comp, pn, PN_typedargslist_name, PN_typedargslist_star, PN_typedargslist_dbl_star, true);
 }
 
-void compile_scope_lambda_param(compiler_t *comp, py_parse_node_t pn) {
+void compile_scope_lambda_param(compiler_t *comp, mp_parse_node_t pn) {
     compile_scope_func_lambda_param(comp, pn, PN_varargslist_name, PN_varargslist_star, PN_varargslist_dbl_star, false);
 }
 
-void compile_scope_comp_iter(compiler_t *comp, py_parse_node_t pn_iter, py_parse_node_t pn_inner_expr, int l_top, int for_depth) {
+void compile_scope_comp_iter(compiler_t *comp, mp_parse_node_t pn_iter, mp_parse_node_t pn_inner_expr, int l_top, int for_depth) {
     tail_recursion:
-    if (PY_PARSE_NODE_IS_NULL(pn_iter)) {
+    if (MP_PARSE_NODE_IS_NULL(pn_iter)) {
         // no more nested if/for; compile inner expression
         compile_node(comp, pn_inner_expr);
         if (comp->scope_cur->kind == SCOPE_LIST_COMP) {
@@ -2609,15 +2609,15 @@ void compile_scope_comp_iter(compiler_t *comp, py_parse_node_t pn_iter, py_parse
             EMIT(yield_value);
             EMIT(pop_top);
         }
-    } else if (PY_PARSE_NODE_IS_STRUCT_KIND(pn_iter, PN_comp_if)) {
+    } else if (MP_PARSE_NODE_IS_STRUCT_KIND(pn_iter, PN_comp_if)) {
         // if condition
-        py_parse_node_struct_t *pns_comp_if = (py_parse_node_struct_t*)pn_iter;
+        mp_parse_node_struct_t *pns_comp_if = (mp_parse_node_struct_t*)pn_iter;
         c_if_cond(comp, pns_comp_if->nodes[0], false, l_top);
         pn_iter = pns_comp_if->nodes[1];
         goto tail_recursion;
-    } else if (PY_PARSE_NODE_IS_STRUCT_KIND(pn_iter, PN_comp_for)) {
+    } else if (MP_PARSE_NODE_IS_STRUCT_KIND(pn_iter, PN_comp_for)) {
         // for loop
-        py_parse_node_struct_t *pns_comp_for2 = (py_parse_node_struct_t*)pn_iter;
+        mp_parse_node_struct_t *pns_comp_for2 = (mp_parse_node_struct_t*)pn_iter;
         compile_node(comp, pns_comp_for2->nodes[1]);
         int l_end2 = comp_next_label(comp);
         int l_top2 = comp_next_label(comp);
@@ -2635,37 +2635,37 @@ void compile_scope_comp_iter(compiler_t *comp, py_parse_node_t pn_iter, py_parse
     }
 }
 
-void check_for_doc_string(compiler_t *comp, py_parse_node_t pn) {
+void check_for_doc_string(compiler_t *comp, mp_parse_node_t pn) {
     // see http://www.python.org/dev/peps/pep-0257/
 
     // look for the first statement
-    if (PY_PARSE_NODE_IS_STRUCT_KIND(pn, PN_expr_stmt)) {
+    if (MP_PARSE_NODE_IS_STRUCT_KIND(pn, PN_expr_stmt)) {
         // a statement; fall through
-    } else if (PY_PARSE_NODE_IS_STRUCT_KIND(pn, PN_file_input_2)) {
+    } else if (MP_PARSE_NODE_IS_STRUCT_KIND(pn, PN_file_input_2)) {
         // file input; find the first non-newline node
-        py_parse_node_struct_t *pns = (py_parse_node_struct_t*)pn;
-        int num_nodes = PY_PARSE_NODE_STRUCT_NUM_NODES(pns);
+        mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)pn;
+        int num_nodes = MP_PARSE_NODE_STRUCT_NUM_NODES(pns);
         for (int i = 0; i < num_nodes; i++) {
             pn = pns->nodes[i];
-            if (!(PY_PARSE_NODE_IS_LEAF(pn) && PY_PARSE_NODE_LEAF_KIND(pn) == PY_PARSE_NODE_TOKEN && PY_PARSE_NODE_LEAF_ARG(pn) == PY_TOKEN_NEWLINE)) {
+            if (!(MP_PARSE_NODE_IS_LEAF(pn) && MP_PARSE_NODE_LEAF_KIND(pn) == MP_PARSE_NODE_TOKEN && MP_PARSE_NODE_LEAF_ARG(pn) == MP_TOKEN_NEWLINE)) {
                 // not a newline, so this is the first statement; finish search
                 break;
             }
         }
         // if we didn't find a non-newline then it's okay to fall through; pn will be a newline and so doc-string test below will fail gracefully
-    } else if (PY_PARSE_NODE_IS_STRUCT_KIND(pn, PN_suite_block_stmts)) {
+    } else if (MP_PARSE_NODE_IS_STRUCT_KIND(pn, PN_suite_block_stmts)) {
         // a list of statements; get the first one
-        pn = ((py_parse_node_struct_t*)pn)->nodes[0];
+        pn = ((mp_parse_node_struct_t*)pn)->nodes[0];
     } else {
         return;
     }
 
     // check the first statement for a doc string
-    if (PY_PARSE_NODE_IS_STRUCT_KIND(pn, PN_expr_stmt)) {
-        py_parse_node_struct_t* pns = (py_parse_node_struct_t*)pn;
-        if (PY_PARSE_NODE_IS_LEAF(pns->nodes[0])) {
-            int kind = PY_PARSE_NODE_LEAF_KIND(pns->nodes[0]);
-            if (kind == PY_PARSE_NODE_STRING) {
+    if (MP_PARSE_NODE_IS_STRUCT_KIND(pn, PN_expr_stmt)) {
+        mp_parse_node_struct_t* pns = (mp_parse_node_struct_t*)pn;
+        if (MP_PARSE_NODE_IS_LEAF(pns->nodes[0])) {
+            int kind = MP_PARSE_NODE_LEAF_KIND(pns->nodes[0]);
+            if (kind == MP_PARSE_NODE_STRING) {
                 compile_node(comp, pns->nodes[0]); // a doc string
                 // store doc string
                 EMIT(store_id, comp->qstr___doc__);
@@ -2696,12 +2696,12 @@ void compile_scope(compiler_t *comp, scope_t *scope, pass_kind_t pass) {
             check_for_doc_string(comp, scope->pn);
         }
         compile_node(comp, scope->pn);
-        EMIT(load_const_tok, PY_TOKEN_KW_NONE);
+        EMIT(load_const_tok, MP_TOKEN_KW_NONE);
         EMIT(return_value);
     } else if (scope->kind == SCOPE_FUNCTION) {
-        assert(PY_PARSE_NODE_IS_STRUCT(scope->pn));
-        py_parse_node_struct_t *pns = (py_parse_node_struct_t*)scope->pn;
-        assert(PY_PARSE_NODE_STRUCT_KIND(pns) == PN_funcdef);
+        assert(MP_PARSE_NODE_IS_STRUCT(scope->pn));
+        mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)scope->pn;
+        assert(MP_PARSE_NODE_STRUCT_KIND(pns) == PN_funcdef);
 
         // work out number of parameters, keywords and default parameters, and add them to the id_info array
         // must be done before compiling the body so that arguments are numbered first (for LOAD_FAST etc)
@@ -2710,18 +2710,18 @@ void compile_scope(compiler_t *comp, scope_t *scope, pass_kind_t pass) {
             apply_to_single_or_list(comp, pns->nodes[1], PN_typedargslist, compile_scope_func_param);
         }
 
-        assert(PY_PARSE_NODE_IS_NULL(pns->nodes[2])); // 2 is something...
+        assert(MP_PARSE_NODE_IS_NULL(pns->nodes[2])); // 2 is something...
 
         compile_node(comp, pns->nodes[3]); // 3 is function body
         // emit return if it wasn't the last opcode
         if (!EMIT(last_emit_was_return_value)) {
-            EMIT(load_const_tok, PY_TOKEN_KW_NONE);
+            EMIT(load_const_tok, MP_TOKEN_KW_NONE);
             EMIT(return_value);
         }
     } else if (scope->kind == SCOPE_LAMBDA) {
-        assert(PY_PARSE_NODE_IS_STRUCT(scope->pn));
-        py_parse_node_struct_t *pns = (py_parse_node_struct_t*)scope->pn;
-        assert(PY_PARSE_NODE_STRUCT_NUM_NODES(pns) == 3);
+        assert(MP_PARSE_NODE_IS_STRUCT(scope->pn));
+        mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)scope->pn;
+        assert(MP_PARSE_NODE_STRUCT_NUM_NODES(pns) == 3);
 
         // work out number of parameters, keywords and default parameters, and add them to the id_info array
         // must be done before compiling the body so that arguments are numbered first (for LOAD_FAST etc)
@@ -2735,11 +2735,11 @@ void compile_scope(compiler_t *comp, scope_t *scope, pass_kind_t pass) {
     } else if (scope->kind == SCOPE_LIST_COMP || scope->kind == SCOPE_DICT_COMP || scope->kind == SCOPE_SET_COMP || scope->kind == SCOPE_GEN_EXPR) {
         // a bit of a hack at the moment
 
-        assert(PY_PARSE_NODE_IS_STRUCT(scope->pn));
-        py_parse_node_struct_t *pns = (py_parse_node_struct_t*)scope->pn;
-        assert(PY_PARSE_NODE_STRUCT_NUM_NODES(pns) == 2);
-        assert(PY_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[1], PN_comp_for));
-        py_parse_node_struct_t *pns_comp_for = (py_parse_node_struct_t*)pns->nodes[1];
+        assert(MP_PARSE_NODE_IS_STRUCT(scope->pn));
+        mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)scope->pn;
+        assert(MP_PARSE_NODE_STRUCT_NUM_NODES(pns) == 2);
+        assert(MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[1], PN_comp_for));
+        mp_parse_node_struct_t *pns_comp_for = (mp_parse_node_struct_t*)pns->nodes[1];
 
         qstr qstr_arg = qstr_from_str_static(".0");
         if (comp->pass == PASS_1) {
@@ -2770,14 +2770,14 @@ void compile_scope(compiler_t *comp, scope_t *scope, pass_kind_t pass) {
         EMIT(for_iter_end);
 
         if (scope->kind == SCOPE_GEN_EXPR) {
-            EMIT(load_const_tok, PY_TOKEN_KW_NONE);
+            EMIT(load_const_tok, MP_TOKEN_KW_NONE);
         }
         EMIT(return_value);
     } else {
         assert(scope->kind == SCOPE_CLASS);
-        assert(PY_PARSE_NODE_IS_STRUCT(scope->pn));
-        py_parse_node_struct_t *pns = (py_parse_node_struct_t*)scope->pn;
-        assert(PY_PARSE_NODE_STRUCT_KIND(pns) == PN_classdef);
+        assert(MP_PARSE_NODE_IS_STRUCT(scope->pn));
+        mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)scope->pn;
+        assert(MP_PARSE_NODE_STRUCT_KIND(pns) == PN_classdef);
 
         if (comp->pass == PASS_1) {
             bool added;
@@ -2795,7 +2795,7 @@ void compile_scope(compiler_t *comp, scope_t *scope, pass_kind_t pass) {
         EMIT(store_locals);
         EMIT(load_id, comp->qstr___name__);
         EMIT(store_id, comp->qstr___module__);
-        EMIT(load_const_id, PY_PARSE_NODE_LEAF_ARG(pns->nodes[0])); // 0 is class name
+        EMIT(load_const_id, MP_PARSE_NODE_LEAF_ARG(pns->nodes[0])); // 0 is class name
         EMIT(store_id, comp->qstr___qualname__);
 
         check_for_doc_string(comp, pns->nodes[2]);
@@ -2804,7 +2804,7 @@ void compile_scope(compiler_t *comp, scope_t *scope, pass_kind_t pass) {
         id_info_t *id = scope_find(scope, comp->qstr___class__);
         assert(id != NULL);
         if (id->kind == ID_INFO_KIND_LOCAL) {
-            EMIT(load_const_tok, PY_TOKEN_KW_NONE);
+            EMIT(load_const_tok, MP_TOKEN_KW_NONE);
         } else {
             EMIT(load_closure, comp->qstr___class__, 0); // XXX check this is the correct local num
         }
@@ -2830,23 +2830,23 @@ void compile_scope_inline_asm(compiler_t *comp, scope_t *scope, pass_kind_t pass
     }
 
     // get the function definition parse node
-    assert(PY_PARSE_NODE_IS_STRUCT(scope->pn));
-    py_parse_node_struct_t *pns = (py_parse_node_struct_t*)scope->pn;
-    assert(PY_PARSE_NODE_STRUCT_KIND(pns) == PN_funcdef);
+    assert(MP_PARSE_NODE_IS_STRUCT(scope->pn));
+    mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)scope->pn;
+    assert(MP_PARSE_NODE_STRUCT_KIND(pns) == PN_funcdef);
 
-    //qstr f_id = PY_PARSE_NODE_LEAF_ARG(pns->nodes[0]); // function name
+    //qstr f_id = MP_PARSE_NODE_LEAF_ARG(pns->nodes[0]); // function name
 
     // parameters are in pns->nodes[1]
     if (comp->pass == PASS_2) {
-        py_parse_node_t *pn_params;
+        mp_parse_node_t *pn_params;
         int n_params = list_get(&pns->nodes[1], PN_typedargslist, &pn_params);
         scope->num_params = EMIT_INLINE_ASM(count_params, n_params, pn_params);
     }
 
-    assert(PY_PARSE_NODE_IS_NULL(pns->nodes[2])); // type
+    assert(MP_PARSE_NODE_IS_NULL(pns->nodes[2])); // type
 
-    py_parse_node_t pn_body = pns->nodes[3]; // body
-    py_parse_node_t *nodes;
+    mp_parse_node_t pn_body = pns->nodes[3]; // body
+    mp_parse_node_t *nodes;
     int num = list_get(&pn_body, PN_suite_block_stmts, &nodes);
 
     if (comp->pass == PASS_3) {
@@ -2855,30 +2855,30 @@ void compile_scope_inline_asm(compiler_t *comp, scope_t *scope, pass_kind_t pass
     }
 
     for (int i = 0; i < num; i++) {
-        assert(PY_PARSE_NODE_IS_STRUCT(nodes[i]));
-        py_parse_node_struct_t *pns2 = (py_parse_node_struct_t*)nodes[i];
-        assert(PY_PARSE_NODE_STRUCT_KIND(pns2) == PN_expr_stmt);
-        assert(PY_PARSE_NODE_IS_STRUCT(pns2->nodes[0]));
-        assert(PY_PARSE_NODE_IS_NULL(pns2->nodes[1]));
-        pns2 = (py_parse_node_struct_t*)pns2->nodes[0];
-        assert(PY_PARSE_NODE_STRUCT_KIND(pns2) == PN_power);
-        assert(PY_PARSE_NODE_IS_ID(pns2->nodes[0]));
-        assert(PY_PARSE_NODE_IS_STRUCT_KIND(pns2->nodes[1], PN_trailer_paren));
-        assert(PY_PARSE_NODE_IS_NULL(pns2->nodes[2]));
-        qstr op = PY_PARSE_NODE_LEAF_ARG(pns2->nodes[0]);
-        pns2 = (py_parse_node_struct_t*)pns2->nodes[1]; // PN_trailer_paren
-        py_parse_node_t *pn_arg;
+        assert(MP_PARSE_NODE_IS_STRUCT(nodes[i]));
+        mp_parse_node_struct_t *pns2 = (mp_parse_node_struct_t*)nodes[i];
+        assert(MP_PARSE_NODE_STRUCT_KIND(pns2) == PN_expr_stmt);
+        assert(MP_PARSE_NODE_IS_STRUCT(pns2->nodes[0]));
+        assert(MP_PARSE_NODE_IS_NULL(pns2->nodes[1]));
+        pns2 = (mp_parse_node_struct_t*)pns2->nodes[0];
+        assert(MP_PARSE_NODE_STRUCT_KIND(pns2) == PN_power);
+        assert(MP_PARSE_NODE_IS_ID(pns2->nodes[0]));
+        assert(MP_PARSE_NODE_IS_STRUCT_KIND(pns2->nodes[1], PN_trailer_paren));
+        assert(MP_PARSE_NODE_IS_NULL(pns2->nodes[2]));
+        qstr op = MP_PARSE_NODE_LEAF_ARG(pns2->nodes[0]);
+        pns2 = (mp_parse_node_struct_t*)pns2->nodes[1]; // PN_trailer_paren
+        mp_parse_node_t *pn_arg;
         int n_args = list_get(&pns2->nodes[0], PN_arglist, &pn_arg);
 
         // emit instructions
         if (strcmp(qstr_str(op), "label") == 0) {
-            if (!(n_args == 1 && PY_PARSE_NODE_IS_ID(pn_arg[0]))) {
+            if (!(n_args == 1 && MP_PARSE_NODE_IS_ID(pn_arg[0]))) {
                 printf("SyntaxError: inline assembler 'label' requires 1 argument\n");
                 return;
             }
             int lab = comp_next_label(comp);
             if (pass > PASS_1) {
-                EMIT_INLINE_ASM(label, lab, PY_PARSE_NODE_LEAF_ARG(pn_arg[0]));
+                EMIT_INLINE_ASM(label, lab, MP_PARSE_NODE_LEAF_ARG(pn_arg[0]));
             }
         } else {
             if (pass > PASS_1) {
@@ -2975,7 +2975,7 @@ void compile_scope_compute_things(compiler_t *comp, scope_t *scope) {
     }
 }
 
-bool py_compile(py_parse_node_t pn, bool is_repl) {
+bool mp_compile(mp_parse_node_t pn, bool is_repl) {
     compiler_t *comp = m_new(compiler_t, 1);
 
     comp->qstr___class__ = qstr_from_str_static("__class__");
diff --git a/py/compile.h b/py/compile.h
index c15b40aa67ee5253160a3a66a646c6948dc3a06c..e283442bb066d465ead0c6bc4a410c58f6b47e71 100644
--- a/py/compile.h
+++ b/py/compile.h
@@ -1 +1 @@
-bool py_compile(py_parse_node_t pn, bool is_repl);
+bool mp_compile(mp_parse_node_t pn, bool is_repl);
diff --git a/py/emit.h b/py/emit.h
index 62a149e10c3b419bd2f08c8b2c8d1f1ee2e18aaa..ea65731038676ddd2fc51cdf61c77f2a1e4f3741 100644
--- a/py/emit.h
+++ b/py/emit.h
@@ -32,7 +32,7 @@ typedef struct _emit_method_table_t {
     void (*import_name)(emit_t *emit, qstr qstr);
     void (*import_from)(emit_t *emit, qstr qstr);
     void (*import_star)(emit_t *emit);
-    void (*load_const_tok)(emit_t *emit, py_token_kind_t tok);
+    void (*load_const_tok)(emit_t *emit, mp_token_kind_t tok);
     void (*load_const_small_int)(emit_t *emit, int arg);
     void (*load_const_int)(emit_t *emit, qstr qstr);
     void (*load_const_dec)(emit_t *emit, qstr qstr);
@@ -129,9 +129,9 @@ typedef struct _emit_inline_asm_t emit_inline_asm_t;
 typedef struct _emit_inline_asm_method_table_t {
     void (*start_pass)(emit_inline_asm_t *emit, pass_kind_t pass, scope_t *scope);
     void (*end_pass)(emit_inline_asm_t *emit);
-    int (*count_params)(emit_inline_asm_t *emit, int n_params, py_parse_node_t *pn_params);
+    int (*count_params)(emit_inline_asm_t *emit, int n_params, mp_parse_node_t *pn_params);
     void (*label)(emit_inline_asm_t *emit, int label_num, qstr label_id);
-    void (*op)(emit_inline_asm_t *emit, qstr op, int n_args, py_parse_node_t *pn_args);
+    void (*op)(emit_inline_asm_t *emit, qstr op, int n_args, mp_parse_node_t *pn_args);
 } emit_inline_asm_method_table_t;
 
 extern const emit_inline_asm_method_table_t emit_inline_thumb_method_table;
diff --git a/py/emitbc.c b/py/emitbc.c
index e1632f37f206ec1a06732148fa56c2589cf9889c..a10a3b96ebdca29385d6d2f5f924165d8c713ae5 100644
--- a/py/emitbc.c
+++ b/py/emitbc.c
@@ -6,14 +6,14 @@
 #include <assert.h>
 
 #include "misc.h"
-#include "mpyconfig.h"
+#include "mpconfig.h"
 #include "lexer.h"
 #include "parse.h"
 #include "compile.h"
 #include "scope.h"
-#include "runtime.h"
+#include "runtime0.h"
 #include "emit.h"
-#include "bc.h"
+#include "bc0.h"
 
 struct _emit_t {
     pass_kind_t pass;
@@ -214,55 +214,55 @@ static void emit_bc_label_assign(emit_t *emit, int l) {
 
 static void emit_bc_import_name(emit_t *emit, qstr qstr) {
     emit_pre(emit, -1);
-    emit_write_byte_1_qstr(emit, PYBC_IMPORT_NAME, qstr);
+    emit_write_byte_1_qstr(emit, MP_BC_IMPORT_NAME, qstr);
 }
 
 static void emit_bc_import_from(emit_t *emit, qstr qstr) {
     emit_pre(emit, 1);
-    emit_write_byte_1_qstr(emit, PYBC_IMPORT_FROM, qstr);
+    emit_write_byte_1_qstr(emit, MP_BC_IMPORT_FROM, qstr);
 }
 
 static void emit_bc_import_star(emit_t *emit) {
     emit_pre(emit, -1);
-    emit_write_byte_1(emit, PYBC_IMPORT_STAR);
+    emit_write_byte_1(emit, MP_BC_IMPORT_STAR);
 }
 
-static void emit_bc_load_const_tok(emit_t *emit, py_token_kind_t tok) {
+static void emit_bc_load_const_tok(emit_t *emit, mp_token_kind_t tok) {
     emit_pre(emit, 1);
     switch (tok) {
-        case PY_TOKEN_KW_FALSE: emit_write_byte_1(emit, PYBC_LOAD_CONST_FALSE); break;
-        case PY_TOKEN_KW_NONE: emit_write_byte_1(emit, PYBC_LOAD_CONST_NONE); break;
-        case PY_TOKEN_KW_TRUE: emit_write_byte_1(emit, PYBC_LOAD_CONST_TRUE); break;
+        case MP_TOKEN_KW_FALSE: emit_write_byte_1(emit, MP_BC_LOAD_CONST_FALSE); break;
+        case MP_TOKEN_KW_NONE: emit_write_byte_1(emit, MP_BC_LOAD_CONST_NONE); break;
+        case MP_TOKEN_KW_TRUE: emit_write_byte_1(emit, MP_BC_LOAD_CONST_TRUE); break;
         default: assert(0);
     }
 }
 
 static void emit_bc_load_const_small_int(emit_t *emit, int arg) {
     emit_pre(emit, 1);
-    emit_write_byte_1_int(emit, PYBC_LOAD_CONST_SMALL_INT, arg);
+    emit_write_byte_1_int(emit, MP_BC_LOAD_CONST_SMALL_INT, arg);
 }
 
 static void emit_bc_load_const_int(emit_t *emit, qstr qstr) {
     emit_pre(emit, 1);
-    emit_write_byte_1_qstr(emit, PYBC_LOAD_CONST_INT, qstr);
+    emit_write_byte_1_qstr(emit, MP_BC_LOAD_CONST_INT, qstr);
 }
 
 static void emit_bc_load_const_dec(emit_t *emit, qstr qstr) {
     emit_pre(emit, 1);
-    emit_write_byte_1_qstr(emit, PYBC_LOAD_CONST_DEC, qstr);
+    emit_write_byte_1_qstr(emit, MP_BC_LOAD_CONST_DEC, qstr);
 }
 
 static void emit_bc_load_const_id(emit_t *emit, qstr qstr) {
     emit_pre(emit, 1);
-    emit_write_byte_1_qstr(emit, PYBC_LOAD_CONST_ID, qstr);
+    emit_write_byte_1_qstr(emit, MP_BC_LOAD_CONST_ID, qstr);
 }
 
 static void emit_bc_load_const_str(emit_t *emit, qstr qstr, bool bytes) {
     emit_pre(emit, 1);
     if (bytes) {
-        emit_write_byte_1_qstr(emit, PYBC_LOAD_CONST_BYTES, qstr);
+        emit_write_byte_1_qstr(emit, MP_BC_LOAD_CONST_BYTES, qstr);
     } else {
-        emit_write_byte_1_qstr(emit, PYBC_LOAD_CONST_STRING, qstr);
+        emit_write_byte_1_qstr(emit, MP_BC_LOAD_CONST_STRING, qstr);
     }
 }
 
@@ -275,219 +275,219 @@ static void emit_bc_load_fast(emit_t *emit, qstr qstr, int local_num) {
     assert(local_num >= 0);
     emit_pre(emit, 1);
     switch (local_num) {
-        case 0: emit_write_byte_1(emit, PYBC_LOAD_FAST_0); break;
-        case 1: emit_write_byte_1(emit, PYBC_LOAD_FAST_1); break;
-        case 2: emit_write_byte_1(emit, PYBC_LOAD_FAST_2); break;
-        default: emit_write_byte_1_uint(emit, PYBC_LOAD_FAST_N, local_num); break;
+        case 0: emit_write_byte_1(emit, MP_BC_LOAD_FAST_0); break;
+        case 1: emit_write_byte_1(emit, MP_BC_LOAD_FAST_1); break;
+        case 2: emit_write_byte_1(emit, MP_BC_LOAD_FAST_2); break;
+        default: emit_write_byte_1_uint(emit, MP_BC_LOAD_FAST_N, local_num); break;
     }
 }
 
 static void emit_bc_load_deref(emit_t *emit, qstr qstr, int local_num) {
     emit_pre(emit, 1);
-    emit_write_byte_1_uint(emit, PYBC_LOAD_DEREF, local_num);
+    emit_write_byte_1_uint(emit, MP_BC_LOAD_DEREF, local_num);
 }
 
 static void emit_bc_load_closure(emit_t *emit, qstr qstr, int local_num) {
     emit_pre(emit, 1);
-    emit_write_byte_1_uint(emit, PYBC_LOAD_CLOSURE, local_num);
+    emit_write_byte_1_uint(emit, MP_BC_LOAD_CLOSURE, local_num);
 }
 
 static void emit_bc_load_name(emit_t *emit, qstr qstr) {
     emit_pre(emit, 1);
-    emit_write_byte_1_qstr(emit, PYBC_LOAD_NAME, qstr);
+    emit_write_byte_1_qstr(emit, MP_BC_LOAD_NAME, qstr);
 }
 
 static void emit_bc_load_global(emit_t *emit, qstr qstr) {
     emit_pre(emit, 1);
-    emit_write_byte_1_qstr(emit, PYBC_LOAD_GLOBAL, qstr);
+    emit_write_byte_1_qstr(emit, MP_BC_LOAD_GLOBAL, qstr);
 }
 
 static void emit_bc_load_attr(emit_t *emit, qstr qstr) {
     emit_pre(emit, 0);
-    emit_write_byte_1_qstr(emit, PYBC_LOAD_ATTR, qstr);
+    emit_write_byte_1_qstr(emit, MP_BC_LOAD_ATTR, qstr);
 }
 
 static void emit_bc_load_method(emit_t *emit, qstr qstr) {
     emit_pre(emit, 0);
-    emit_write_byte_1_qstr(emit, PYBC_LOAD_METHOD, qstr);
+    emit_write_byte_1_qstr(emit, MP_BC_LOAD_METHOD, qstr);
 }
 
 static void emit_bc_load_build_class(emit_t *emit) {
     emit_pre(emit, 1);
-    emit_write_byte_1(emit, PYBC_LOAD_BUILD_CLASS);
+    emit_write_byte_1(emit, MP_BC_LOAD_BUILD_CLASS);
 }
 
 static void emit_bc_store_fast(emit_t *emit, qstr qstr, int local_num) {
     assert(local_num >= 0);
     emit_pre(emit, -1);
     switch (local_num) {
-        case 0: emit_write_byte_1(emit, PYBC_STORE_FAST_0); break;
-        case 1: emit_write_byte_1(emit, PYBC_STORE_FAST_1); break;
-        case 2: emit_write_byte_1(emit, PYBC_STORE_FAST_2); break;
-        default: emit_write_byte_1_uint(emit, PYBC_STORE_FAST_N, local_num); break;
+        case 0: emit_write_byte_1(emit, MP_BC_STORE_FAST_0); break;
+        case 1: emit_write_byte_1(emit, MP_BC_STORE_FAST_1); break;
+        case 2: emit_write_byte_1(emit, MP_BC_STORE_FAST_2); break;
+        default: emit_write_byte_1_uint(emit, MP_BC_STORE_FAST_N, local_num); break;
     }
 }
 
 static void emit_bc_store_deref(emit_t *emit, qstr qstr, int local_num) {
     emit_pre(emit, -1);
-    emit_write_byte_1_uint(emit, PYBC_STORE_DEREF, local_num);
+    emit_write_byte_1_uint(emit, MP_BC_STORE_DEREF, local_num);
 }
 
 static void emit_bc_store_name(emit_t *emit, qstr qstr) {
     emit_pre(emit, -1);
-    emit_write_byte_1_qstr(emit, PYBC_STORE_NAME, qstr);
+    emit_write_byte_1_qstr(emit, MP_BC_STORE_NAME, qstr);
 }
 
 static void emit_bc_store_global(emit_t *emit, qstr qstr) {
     emit_pre(emit, -1);
-    emit_write_byte_1_qstr(emit, PYBC_STORE_GLOBAL, qstr);
+    emit_write_byte_1_qstr(emit, MP_BC_STORE_GLOBAL, qstr);
 }
 
 static void emit_bc_store_attr(emit_t *emit, qstr qstr) {
     emit_pre(emit, -2);
-    emit_write_byte_1_qstr(emit, PYBC_STORE_ATTR, qstr);
+    emit_write_byte_1_qstr(emit, MP_BC_STORE_ATTR, qstr);
 }
 
 static void emit_bc_store_subscr(emit_t *emit) {
     emit_pre(emit, -3);
-    emit_write_byte_1(emit, PYBC_STORE_SUBSCR);
+    emit_write_byte_1(emit, MP_BC_STORE_SUBSCR);
 }
 
 static void emit_bc_store_locals(emit_t *emit) {
     // not needed
     emit_pre(emit, -1);
-    emit_write_byte_1(emit, PYBC_POP_TOP);
+    emit_write_byte_1(emit, MP_BC_POP_TOP);
 }
 
 static void emit_bc_delete_fast(emit_t *emit, qstr qstr, int local_num) {
     assert(local_num >= 0);
     emit_pre(emit, 0);
-    emit_write_byte_1_uint(emit, PYBC_DELETE_FAST_N, local_num);
+    emit_write_byte_1_uint(emit, MP_BC_DELETE_FAST_N, local_num);
 }
 
 static void emit_bc_delete_deref(emit_t *emit, qstr qstr, int local_num) {
     emit_pre(emit, 0);
-    emit_write_byte_1_qstr(emit, PYBC_DELETE_DEREF, local_num);
+    emit_write_byte_1_qstr(emit, MP_BC_DELETE_DEREF, local_num);
 }
 
 static void emit_bc_delete_name(emit_t *emit, qstr qstr) {
     emit_pre(emit, 0);
-    emit_write_byte_1_qstr(emit, PYBC_DELETE_NAME, qstr);
+    emit_write_byte_1_qstr(emit, MP_BC_DELETE_NAME, qstr);
 }
 
 static void emit_bc_delete_global(emit_t *emit, qstr qstr) {
     emit_pre(emit, 0);
-    emit_write_byte_1_qstr(emit, PYBC_DELETE_GLOBAL, qstr);
+    emit_write_byte_1_qstr(emit, MP_BC_DELETE_GLOBAL, qstr);
 }
 
 static void emit_bc_delete_attr(emit_t *emit, qstr qstr) {
     emit_pre(emit, -1);
-    emit_write_byte_1_qstr(emit, PYBC_DELETE_ATTR, qstr);
+    emit_write_byte_1_qstr(emit, MP_BC_DELETE_ATTR, qstr);
 }
 
 static void emit_bc_delete_subscr(emit_t *emit) {
     emit_pre(emit, -2);
-    emit_write_byte_1(emit, PYBC_DELETE_SUBSCR);
+    emit_write_byte_1(emit, MP_BC_DELETE_SUBSCR);
 }
 
 static void emit_bc_dup_top(emit_t *emit) {
     emit_pre(emit, 1);
-    emit_write_byte_1(emit, PYBC_DUP_TOP);
+    emit_write_byte_1(emit, MP_BC_DUP_TOP);
 }
 
 static void emit_bc_dup_top_two(emit_t *emit) {
     emit_pre(emit, 2);
-    emit_write_byte_1(emit, PYBC_DUP_TOP_TWO);
+    emit_write_byte_1(emit, MP_BC_DUP_TOP_TWO);
 }
 
 static void emit_bc_pop_top(emit_t *emit) {
     emit_pre(emit, -1);
-    emit_write_byte_1(emit, PYBC_POP_TOP);
+    emit_write_byte_1(emit, MP_BC_POP_TOP);
 }
 
 static void emit_bc_rot_two(emit_t *emit) {
     emit_pre(emit, 0);
-    emit_write_byte_1(emit, PYBC_ROT_TWO);
+    emit_write_byte_1(emit, MP_BC_ROT_TWO);
 }
 
 static void emit_bc_rot_three(emit_t *emit) {
     emit_pre(emit, 0);
-    emit_write_byte_1(emit, PYBC_ROT_THREE);
+    emit_write_byte_1(emit, MP_BC_ROT_THREE);
 }
 
 static void emit_bc_jump(emit_t *emit, int label) {
     emit_pre(emit, 0);
-    emit_write_byte_1_signed_label(emit, PYBC_JUMP, label);
+    emit_write_byte_1_signed_label(emit, MP_BC_JUMP, label);
 }
 
 static void emit_bc_pop_jump_if_true(emit_t *emit, int label) {
     emit_pre(emit, -1);
-    emit_write_byte_1_signed_label(emit, PYBC_POP_JUMP_IF_TRUE, label);
+    emit_write_byte_1_signed_label(emit, MP_BC_POP_JUMP_IF_TRUE, label);
 }
 
 static void emit_bc_pop_jump_if_false(emit_t *emit, int label) {
     emit_pre(emit, -1);
-    emit_write_byte_1_signed_label(emit, PYBC_POP_JUMP_IF_FALSE, label);
+    emit_write_byte_1_signed_label(emit, MP_BC_POP_JUMP_IF_FALSE, label);
 }
 
 static void emit_bc_jump_if_true_or_pop(emit_t *emit, int label) {
     emit_pre(emit, -1);
-    emit_write_byte_1_signed_label(emit, PYBC_JUMP_IF_TRUE_OR_POP, label);
+    emit_write_byte_1_signed_label(emit, MP_BC_JUMP_IF_TRUE_OR_POP, label);
 }
 
 static void emit_bc_jump_if_false_or_pop(emit_t *emit, int label) {
     emit_pre(emit, -1);
-    emit_write_byte_1_signed_label(emit, PYBC_JUMP_IF_FALSE_OR_POP, label);
+    emit_write_byte_1_signed_label(emit, MP_BC_JUMP_IF_FALSE_OR_POP, label);
 }
 
 static void emit_bc_setup_loop(emit_t *emit, int label) {
     emit_pre(emit, 0);
-    emit_write_byte_1_unsigned_label(emit, PYBC_SETUP_LOOP, label);
+    emit_write_byte_1_unsigned_label(emit, MP_BC_SETUP_LOOP, label);
 }
 
 static void emit_bc_break_loop(emit_t *emit, int label) {
     emit_pre(emit, 0);
-    emit_write_byte_1_unsigned_label(emit, PYBC_BREAK_LOOP, label);
+    emit_write_byte_1_unsigned_label(emit, MP_BC_BREAK_LOOP, label);
 }
 
 static void emit_bc_continue_loop(emit_t *emit, int label) {
     emit_pre(emit, 0);
-    emit_write_byte_1_unsigned_label(emit, PYBC_CONTINUE_LOOP, label);
+    emit_write_byte_1_unsigned_label(emit, MP_BC_CONTINUE_LOOP, label);
 }
 
 static void emit_bc_setup_with(emit_t *emit, int label) {
     emit_pre(emit, 7);
-    emit_write_byte_1_unsigned_label(emit, PYBC_SETUP_WITH, label);
+    emit_write_byte_1_unsigned_label(emit, MP_BC_SETUP_WITH, label);
 }
 
 static void emit_bc_with_cleanup(emit_t *emit) {
     emit_pre(emit, -7);
-    emit_write_byte_1(emit, PYBC_WITH_CLEANUP);
+    emit_write_byte_1(emit, MP_BC_WITH_CLEANUP);
 }
 
 static void emit_bc_setup_except(emit_t *emit, int label) {
     emit_pre(emit, 6);
-    emit_write_byte_1_unsigned_label(emit, PYBC_SETUP_EXCEPT, label);
+    emit_write_byte_1_unsigned_label(emit, MP_BC_SETUP_EXCEPT, label);
 }
 
 static void emit_bc_setup_finally(emit_t *emit, int label) {
     emit_pre(emit, 6);
-    emit_write_byte_1_unsigned_label(emit, PYBC_SETUP_FINALLY, label);
+    emit_write_byte_1_unsigned_label(emit, MP_BC_SETUP_FINALLY, label);
 }
 
 static void emit_bc_end_finally(emit_t *emit) {
     emit_pre(emit, -1);
-    emit_write_byte_1(emit, PYBC_END_FINALLY);
+    emit_write_byte_1(emit, MP_BC_END_FINALLY);
 }
 
 static void emit_bc_get_iter(emit_t *emit) {
     emit_pre(emit, 0);
-    emit_write_byte_1(emit, PYBC_GET_ITER);
+    emit_write_byte_1(emit, MP_BC_GET_ITER);
 }
 
 static void emit_bc_for_iter(emit_t *emit, int label) {
     emit_pre(emit, 1);
-    emit_write_byte_1_unsigned_label(emit, PYBC_FOR_ITER, label);
+    emit_write_byte_1_unsigned_label(emit, MP_BC_FOR_ITER, label);
 }
 
 static void emit_bc_for_iter_end(emit_t *emit) {
@@ -496,104 +496,104 @@ static void emit_bc_for_iter_end(emit_t *emit) {
 
 static void emit_bc_pop_block(emit_t *emit) {
     emit_pre(emit, 0);
-    emit_write_byte_1(emit, PYBC_POP_BLOCK);
+    emit_write_byte_1(emit, MP_BC_POP_BLOCK);
 }
 
 static void emit_bc_pop_except(emit_t *emit) {
     emit_pre(emit, 0);
-    emit_write_byte_1(emit, PYBC_POP_EXCEPT);
+    emit_write_byte_1(emit, MP_BC_POP_EXCEPT);
 }
 
 static void emit_bc_unary_op(emit_t *emit, rt_unary_op_t op) {
     emit_pre(emit, 0);
-    emit_write_byte_1_byte(emit, PYBC_UNARY_OP, op);
+    emit_write_byte_1_byte(emit, MP_BC_UNARY_OP, op);
 }
 
 static void emit_bc_binary_op(emit_t *emit, rt_binary_op_t op) {
     emit_pre(emit, -1);
-    emit_write_byte_1_byte(emit, PYBC_BINARY_OP, op);
+    emit_write_byte_1_byte(emit, MP_BC_BINARY_OP, op);
 }
 
 static void emit_bc_compare_op(emit_t *emit, rt_compare_op_t op) {
     emit_pre(emit, -1);
-    emit_write_byte_1_byte(emit, PYBC_COMPARE_OP, op);
+    emit_write_byte_1_byte(emit, MP_BC_COMPARE_OP, op);
 }
 
 static void emit_bc_build_tuple(emit_t *emit, int n_args) {
     assert(n_args >= 0);
     emit_pre(emit, 1 - n_args);
-    emit_write_byte_1_uint(emit, PYBC_BUILD_TUPLE, n_args);
+    emit_write_byte_1_uint(emit, MP_BC_BUILD_TUPLE, n_args);
 }
 
 static void emit_bc_build_list(emit_t *emit, int n_args) {
     assert(n_args >= 0);
     emit_pre(emit, 1 - n_args);
-    emit_write_byte_1_uint(emit, PYBC_BUILD_LIST, n_args);
+    emit_write_byte_1_uint(emit, MP_BC_BUILD_LIST, n_args);
 }
 
 static void emit_bc_list_append(emit_t *emit, int list_stack_index) {
     assert(list_stack_index >= 0);
     emit_pre(emit, -1);
-    emit_write_byte_1_uint(emit, PYBC_LIST_APPEND, list_stack_index);
+    emit_write_byte_1_uint(emit, MP_BC_LIST_APPEND, list_stack_index);
 }
 
 static void emit_bc_build_map(emit_t *emit, int n_args) {
     assert(n_args >= 0);
     emit_pre(emit, 1);
-    emit_write_byte_1_uint(emit, PYBC_BUILD_MAP, n_args);
+    emit_write_byte_1_uint(emit, MP_BC_BUILD_MAP, n_args);
 }
 
 static void emit_bc_store_map(emit_t *emit) {
     emit_pre(emit, -2);
-    emit_write_byte_1(emit, PYBC_STORE_MAP);
+    emit_write_byte_1(emit, MP_BC_STORE_MAP);
 }
 
 static void emit_bc_map_add(emit_t *emit, int map_stack_index) {
     assert(map_stack_index >= 0);
     emit_pre(emit, -2);
-    emit_write_byte_1_uint(emit, PYBC_MAP_ADD, map_stack_index);
+    emit_write_byte_1_uint(emit, MP_BC_MAP_ADD, map_stack_index);
 }
 
 static void emit_bc_build_set(emit_t *emit, int n_args) {
     assert(n_args >= 0);
     emit_pre(emit, 1 - n_args);
-    emit_write_byte_1_uint(emit, PYBC_BUILD_SET, n_args);
+    emit_write_byte_1_uint(emit, MP_BC_BUILD_SET, n_args);
 }
 
 static void emit_bc_set_add(emit_t *emit, int set_stack_index) {
     assert(set_stack_index >= 0);
     emit_pre(emit, -1);
-    emit_write_byte_1_uint(emit, PYBC_SET_ADD, set_stack_index);
+    emit_write_byte_1_uint(emit, MP_BC_SET_ADD, set_stack_index);
 }
 
 static void emit_bc_build_slice(emit_t *emit, int n_args) {
     assert(n_args >= 0);
     emit_pre(emit, 1 - n_args);
-    emit_write_byte_1_uint(emit, PYBC_BUILD_SLICE, n_args);
+    emit_write_byte_1_uint(emit, MP_BC_BUILD_SLICE, n_args);
 }
 
 static void emit_bc_unpack_sequence(emit_t *emit, int n_args) {
     assert(n_args >= 0);
     emit_pre(emit, -1 + n_args);
-    emit_write_byte_1_uint(emit, PYBC_UNPACK_SEQUENCE, n_args);
+    emit_write_byte_1_uint(emit, MP_BC_UNPACK_SEQUENCE, n_args);
 }
 
 static void emit_bc_unpack_ex(emit_t *emit, int n_left, int n_right) {
     assert(n_left >=0 && n_right >= 0);
     emit_pre(emit, -1 + n_left + n_right + 1);
-    emit_write_byte_1_uint(emit, PYBC_UNPACK_EX, n_left | (n_right << 8));
+    emit_write_byte_1_uint(emit, MP_BC_UNPACK_EX, n_left | (n_right << 8));
 }
 
 static void emit_bc_make_function(emit_t *emit, scope_t *scope, int n_dict_params, int n_default_params) {
     assert(n_default_params == 0 && n_dict_params == 0);
     emit_pre(emit, 1);
-    emit_write_byte_1_uint(emit, PYBC_MAKE_FUNCTION, scope->unique_code_id);
+    emit_write_byte_1_uint(emit, MP_BC_MAKE_FUNCTION, scope->unique_code_id);
 }
 
 static void emit_bc_make_closure(emit_t *emit, scope_t *scope, int n_dict_params, int n_default_params) {
     assert(n_default_params == 0 && n_dict_params == 0);
     emit_pre(emit, 0);
-    emit_write_byte_1_uint(emit, PYBC_MAKE_CLOSURE, scope->unique_code_id);
+    emit_write_byte_1_uint(emit, MP_BC_MAKE_CLOSURE, scope->unique_code_id);
 }
 
 static void emit_bc_call_function(emit_t *emit, int n_positional, int n_keyword, bool have_star_arg, bool have_dbl_star_arg) {
@@ -608,15 +608,15 @@ static void emit_bc_call_function(emit_t *emit, int n_positional, int n_keyword,
     int op;
     if (have_star_arg) {
         if (have_dbl_star_arg) {
-            op = PYBC_CALL_FUNCTION_VAR_KW;
+            op = MP_BC_CALL_FUNCTION_VAR_KW;
         } else {
-            op = PYBC_CALL_FUNCTION_VAR;
+            op = MP_BC_CALL_FUNCTION_VAR;
         }
     } else {
         if (have_dbl_star_arg) {
-            op = PYBC_CALL_FUNCTION_KW;
+            op = MP_BC_CALL_FUNCTION_KW;
         } else {
-            op = PYBC_CALL_FUNCTION;
+            op = MP_BC_CALL_FUNCTION;
         }
     }
     emit_write_byte_1_uint(emit, op, (n_keyword << 8) | n_positional); // TODO make it 2 separate uints
@@ -634,15 +634,15 @@ static void emit_bc_call_method(emit_t *emit, int n_positional, int n_keyword, b
     int op;
     if (have_star_arg) {
         if (have_dbl_star_arg) {
-            op = PYBC_CALL_METHOD_VAR_KW;
+            op = MP_BC_CALL_METHOD_VAR_KW;
         } else {
-            op = PYBC_CALL_METHOD_VAR;
+            op = MP_BC_CALL_METHOD_VAR;
         }
     } else {
         if (have_dbl_star_arg) {
-            op = PYBC_CALL_METHOD_KW;
+            op = MP_BC_CALL_METHOD_KW;
         } else {
-            op = PYBC_CALL_METHOD;
+            op = MP_BC_CALL_METHOD;
         }
     }
     emit_write_byte_1_uint(emit, op, (n_keyword << 8) | n_positional); // TODO make it 2 separate uints
@@ -651,13 +651,13 @@ static void emit_bc_call_method(emit_t *emit, int n_positional, int n_keyword, b
 static void emit_bc_return_value(emit_t *emit) {
     emit_pre(emit, -1);
     emit->last_emit_was_return_value = true;
-    emit_write_byte_1(emit, PYBC_RETURN_VALUE);
+    emit_write_byte_1(emit, MP_BC_RETURN_VALUE);
 }
 
 static void emit_bc_raise_varargs(emit_t *emit, int n_args) {
     assert(n_args >= 0);
     emit_pre(emit, -n_args);
-    emit_write_byte_1_uint(emit, PYBC_RAISE_VARARGS, n_args);
+    emit_write_byte_1_uint(emit, MP_BC_RAISE_VARARGS, n_args);
 }
 
 static void emit_bc_yield_value(emit_t *emit) {
@@ -665,7 +665,7 @@ static void emit_bc_yield_value(emit_t *emit) {
     if (emit->pass == PASS_2) {
         emit->scope->flags |= SCOPE_FLAG_GENERATOR;
     }
-    emit_write_byte_1(emit, PYBC_YIELD_VALUE);
+    emit_write_byte_1(emit, MP_BC_YIELD_VALUE);
 }
 
 static void emit_bc_yield_from(emit_t *emit) {
@@ -673,7 +673,7 @@ static void emit_bc_yield_from(emit_t *emit) {
     if (emit->pass == PASS_2) {
         emit->scope->flags |= SCOPE_FLAG_GENERATOR;
     }
-    emit_write_byte_1(emit, PYBC_YIELD_FROM);
+    emit_write_byte_1(emit, MP_BC_YIELD_FROM);
 }
 
 const emit_method_table_t emit_bc_method_table = {
diff --git a/py/emitcommon.c b/py/emitcommon.c
index 0dfe96d891b068864e9bf7972aeed9e06a6033cc..e30cad74963843a0d48cbc6df5dfc3868dc10a2d 100644
--- a/py/emitcommon.c
+++ b/py/emitcommon.c
@@ -5,11 +5,11 @@
 #include <assert.h>
 
 #include "misc.h"
-#include "mpyconfig.h"
+#include "mpconfig.h"
 #include "lexer.h"
 #include "parse.h"
 #include "scope.h"
-#include "runtime.h"
+#include "runtime0.h"
 #include "emit.h"
 
 #define EMIT(fun, arg...) (emit_method_table->fun(emit, ##arg))
diff --git a/py/emitcpy.c b/py/emitcpy.c
index cb8bef47946ecc51427d2af08fbe134381d68da1..52f804925c8a49604915234cd5bb439644df18c7 100644
--- a/py/emitcpy.c
+++ b/py/emitcpy.c
@@ -6,12 +6,12 @@
 #include <assert.h>
 
 #include "misc.h"
-#include "mpyconfig.h"
+#include "mpconfig.h"
 #include "lexer.h"
 #include "parse.h"
 #include "compile.h"
 #include "scope.h"
-#include "runtime.h"
+#include "runtime0.h"
 #include "emit.h"
 
 #if MICROPY_EMIT_CPYTHON
@@ -136,9 +136,9 @@ static void emit_cpy_load_const_tok(emit_t *emit, py_token_kind_t tok) {
     if (emit->pass == PASS_3) {
         printf("LOAD_CONST ");
         switch (tok) {
-            case PY_TOKEN_KW_FALSE: printf("False"); break;
-            case PY_TOKEN_KW_NONE: printf("None"); break;
-            case PY_TOKEN_KW_TRUE: printf("True"); break;
+            case MP_TOKEN_KW_FALSE: printf("False"); break;
+            case MP_TOKEN_KW_NONE: printf("None"); break;
+            case MP_TOKEN_KW_TRUE: printf("True"); break;
             default: printf("?=%d\n", tok); return; assert(0);
         }
         printf("\n");
diff --git a/py/emitinlinethumb.c b/py/emitinlinethumb.c
index 4a57235da7615217667d515a94ae317b24c881c7..073dfa06040c2a117e18a72384cf3043eca26b76 100644
--- a/py/emitinlinethumb.c
+++ b/py/emitinlinethumb.c
@@ -6,11 +6,11 @@
 #include <assert.h>
 
 #include "misc.h"
-#include "mpyconfig.h"
+#include "mpconfig.h"
 #include "lexer.h"
 #include "parse.h"
 #include "scope.h"
-#include "runtime.h"
+#include "runtime0.h"
 #include "emit.h"
 #include "asmthumb.h"
 
@@ -45,22 +45,22 @@ static void emit_inline_thumb_end_pass(emit_inline_asm_t *emit) {
     asm_thumb_end_pass(emit->as);
 
     if (emit->pass == PASS_3) {
-        py_fun_t f = asm_thumb_get_code(emit->as);
+        void *f = asm_thumb_get_code(emit->as);
         rt_assign_inline_asm_code(emit->scope->unique_code_id, f, asm_thumb_get_code_size(emit->as), emit->scope->num_params);
     }
 }
 
-static int emit_inline_thumb_count_params(emit_inline_asm_t *emit, int n_params, py_parse_node_t *pn_params) {
+static int emit_inline_thumb_count_params(emit_inline_asm_t *emit, int n_params, mp_parse_node_t *pn_params) {
     if (n_params > 4) {
         printf("SyntaxError: can only have up to 4 parameters to inline thumb assembly\n");
         return 0;
     }
     for (int i = 0; i < n_params; i++) {
-        if (!PY_PARSE_NODE_IS_ID(pn_params[i])) {
+        if (!MP_PARSE_NODE_IS_ID(pn_params[i])) {
             printf("SyntaxError: parameter to inline assembler must be an identifier\n");
             return 0;
         }
-        const char *p = qstr_str(PY_PARSE_NODE_LEAF_ARG(pn_params[i]));
+        const char *p = qstr_str(MP_PARSE_NODE_LEAF_ARG(pn_params[i]));
         if (!(strlen(p) == 2 && p[0] == 'r' && p[1] == '0' + i)) {
             printf("SyntaxError: parameter %d to inline assembler must be r%d\n", i + 1, i);
             return 0;
@@ -84,12 +84,12 @@ static bool check_n_arg(qstr op, int n_args, int wanted_n_args) {
     }
 }
 
-static uint get_arg_rlo(qstr op, py_parse_node_t *pn_args, int wanted_arg_num) {
-    if (!PY_PARSE_NODE_IS_ID(pn_args[wanted_arg_num])) {
+static uint get_arg_rlo(qstr op, mp_parse_node_t *pn_args, int wanted_arg_num) {
+    if (!MP_PARSE_NODE_IS_ID(pn_args[wanted_arg_num])) {
         printf("SyntaxError: '%s' expects a register in position %d\n", qstr_str(op), wanted_arg_num);
         return 0;
     }
-    qstr reg_qstr = PY_PARSE_NODE_LEAF_ARG(pn_args[wanted_arg_num]);
+    qstr reg_qstr = MP_PARSE_NODE_LEAF_ARG(pn_args[wanted_arg_num]);
     const char *reg_str = qstr_str(reg_qstr);
     if (!(strlen(reg_str) == 2 && reg_str[0] == 'r' && ('0' <= reg_str[1] && reg_str[1] <= '7'))) {
         printf("SyntaxError: '%s' expects a register in position %d\n", qstr_str(op), wanted_arg_num);
@@ -98,12 +98,12 @@ static uint get_arg_rlo(qstr op, py_parse_node_t *pn_args, int wanted_arg_num) {
     return reg_str[1] - '0';
 }
 
-static int get_arg_i(qstr op, py_parse_node_t *pn_args, int wanted_arg_num, int fit_mask) {
-    if (!PY_PARSE_NODE_IS_SMALL_INT(pn_args[wanted_arg_num])) {
+static int get_arg_i(qstr op, mp_parse_node_t *pn_args, int wanted_arg_num, int fit_mask) {
+    if (!MP_PARSE_NODE_IS_SMALL_INT(pn_args[wanted_arg_num])) {
         printf("SyntaxError: '%s' expects an integer in position %d\n", qstr_str(op), wanted_arg_num);
         return 0;
     }
-    int i = PY_PARSE_NODE_LEAF_ARG(pn_args[wanted_arg_num]);
+    int i = MP_PARSE_NODE_LEAF_ARG(pn_args[wanted_arg_num]);
     if ((i & (~fit_mask)) != 0) {
         printf("SyntaxError: '%s' integer 0x%x does not fit in mask 0x%x\n", qstr_str(op), i, fit_mask);
         return 0;
@@ -111,12 +111,12 @@ static int get_arg_i(qstr op, py_parse_node_t *pn_args, int wanted_arg_num, int
     return i;
 }
 
-static int get_arg_label(emit_inline_asm_t *emit, qstr op, py_parse_node_t *pn_args, int wanted_arg_num) {
-    if (!PY_PARSE_NODE_IS_ID(pn_args[wanted_arg_num])) {
+static int get_arg_label(emit_inline_asm_t *emit, qstr op, mp_parse_node_t *pn_args, int wanted_arg_num) {
+    if (!MP_PARSE_NODE_IS_ID(pn_args[wanted_arg_num])) {
         printf("SyntaxError: '%s' expects a label in position %d\n", qstr_str(op), wanted_arg_num);
         return 0;
     }
-    qstr label_qstr = PY_PARSE_NODE_LEAF_ARG(pn_args[wanted_arg_num]);
+    qstr label_qstr = MP_PARSE_NODE_LEAF_ARG(pn_args[wanted_arg_num]);
     for (int i = 0; i < emit->max_num_labels; i++) {
         if (emit->label_lookup[i] == label_qstr) {
             return i;
@@ -129,7 +129,7 @@ static int get_arg_label(emit_inline_asm_t *emit, qstr op, py_parse_node_t *pn_a
     return 0;
 }
 
-static void emit_inline_thumb_op(emit_inline_asm_t *emit, qstr op, int n_args, py_parse_node_t *pn_args) {
+static void emit_inline_thumb_op(emit_inline_asm_t *emit, qstr op, int n_args, mp_parse_node_t *pn_args) {
     // TODO perhaps make two tables:
     // one_args =
     // "b", LAB, asm_thumb_b_n,
diff --git a/py/emitnative.c b/py/emitnative.c
index 17d217c9731af49f29c65c8b4f9805a36cb45401..a29922d96cbdbb1a388daee9bf0b736a96a2123c 100644
--- a/py/emitnative.c
+++ b/py/emitnative.c
@@ -24,12 +24,14 @@
 #include <assert.h>
 
 #include "misc.h"
-#include "mpyconfig.h"
+#include "mpconfig.h"
 #include "lexer.h"
 #include "parse.h"
 #include "scope.h"
-#include "runtime.h"
+#include "runtime0.h"
 #include "emit.h"
+#include "obj.h"
+#include "runtime.h"
 
 // wrapper around everything in this file
 #if N_X64 || N_THUMB
@@ -258,10 +260,10 @@ static void emit_native_end_pass(emit_t *emit) {
 
     if (emit->pass == PASS_3) {
 #if N_X64
-        py_fun_t f = asm_x64_get_code(emit->as);
+        void *f = asm_x64_get_code(emit->as);
         rt_assign_native_code(emit->scope->unique_code_id, f, asm_x64_get_code_size(emit->as), emit->scope->num_params);
 #elif N_THUMB
-        py_fun_t f = asm_thumb_get_code(emit->as);
+        void *f = asm_thumb_get_code(emit->as);
         rt_assign_native_code(emit->scope->unique_code_id, f, asm_thumb_get_code_size(emit->as), emit->scope->num_params);
 #endif
     }
@@ -460,9 +462,9 @@ static void emit_get_stack_pointer_to_reg_for_pop(emit_t *emit, int reg_dest, in
                 case VTYPE_BOOL:
                     si->vtype = VTYPE_PYOBJ;
                     if (si->u_imm == 0) {
-                        ASM_MOV_IMM_TO_LOCAL_USING((machine_uint_t)py_const_false, emit->stack_start + emit->stack_size - 1 - i, reg_dest);
+                        ASM_MOV_IMM_TO_LOCAL_USING((machine_uint_t)mp_const_false, emit->stack_start + emit->stack_size - 1 - i, reg_dest);
                     } else {
-                        ASM_MOV_IMM_TO_LOCAL_USING((machine_uint_t)py_const_true, emit->stack_start + emit->stack_size - 1 - i, reg_dest);
+                        ASM_MOV_IMM_TO_LOCAL_USING((machine_uint_t)mp_const_true, emit->stack_start + emit->stack_size - 1 - i, reg_dest);
                     }
                     break;
                 case VTYPE_INT:
@@ -561,23 +563,23 @@ static void emit_native_import_star(emit_t *emit) {
     assert(0);
 }
 
-static void emit_native_load_const_tok(emit_t *emit, py_token_kind_t tok) {
+static void emit_native_load_const_tok(emit_t *emit, mp_token_kind_t tok) {
     emit_pre(emit);
     int vtype;
     machine_uint_t val;
     if (emit->do_viper_types) {
         switch (tok) {
-            case PY_TOKEN_KW_NONE: vtype = VTYPE_PTR_NONE; val = 0; break;
-            case PY_TOKEN_KW_FALSE: vtype = VTYPE_BOOL; val = 0; break;
-            case PY_TOKEN_KW_TRUE: vtype = VTYPE_BOOL; val = 1; break;
+            case MP_TOKEN_KW_NONE: vtype = VTYPE_PTR_NONE; val = 0; break;
+            case MP_TOKEN_KW_FALSE: vtype = VTYPE_BOOL; val = 0; break;
+            case MP_TOKEN_KW_TRUE: vtype = VTYPE_BOOL; val = 1; break;
             default: assert(0); vtype = 0; val = 0; // shouldn't happen
         }
     } else {
         vtype = VTYPE_PYOBJ;
         switch (tok) {
-            case PY_TOKEN_KW_NONE: val = (machine_uint_t)py_const_none; break;
-            case PY_TOKEN_KW_FALSE: val = (machine_uint_t)py_const_false; break;
-            case PY_TOKEN_KW_TRUE: val = (machine_uint_t)py_const_true; break;
+            case MP_TOKEN_KW_NONE: val = (machine_uint_t)mp_const_none; break;
+            case MP_TOKEN_KW_FALSE: val = (machine_uint_t)mp_const_false; break;
+            case MP_TOKEN_KW_TRUE: val = (machine_uint_t)mp_const_true; break;
             default: assert(0); vtype = 0; val = 0; // shouldn't happen
         }
     }
@@ -956,7 +958,7 @@ static void emit_native_for_iter(emit_t *emit, int label) {
     emit_access_stack(emit, 1, &vtype, REG_ARG_1);
     assert(vtype == VTYPE_PYOBJ);
     emit_call(emit, RT_F_ITERNEXT, rt_iternext);
-    ASM_MOV_IMM_TO_REG((machine_uint_t)py_const_stop_iteration, REG_TEMP1);
+    ASM_MOV_IMM_TO_REG((machine_uint_t)mp_const_stop_iteration, REG_TEMP1);
 #if N_X64
     asm_x64_cmp_r64_with_r64(emit->as, REG_RET, REG_TEMP1);
     asm_x64_jcc_label(emit->as, JCC_JE, label);
diff --git a/py/emitpass1.c b/py/emitpass1.c
index 5526c2d5d84c43f5d08d6833907c124a721ae804..551f30eb4c9cf46b4f1e6c15a65ed6ad01cf7e5f 100644
--- a/py/emitpass1.c
+++ b/py/emitpass1.c
@@ -6,12 +6,12 @@
 #include <assert.h>
 
 #include "misc.h"
-#include "mpyconfig.h"
+#include "mpconfig.h"
 #include "lexer.h"
 #include "parse.h"
 #include "compile.h"
 #include "scope.h"
-#include "runtime.h"
+#include "runtime0.h"
 #include "emit.h"
 
 struct _emit_t {
diff --git a/py/gc.c b/py/gc.c
index 690503d92c443e503ceb76d3ba0f82094d21195e..7d4cac6e74df3dee6958778ad9c9c4d198bb3d9a 100644
--- a/py/gc.c
+++ b/py/gc.c
@@ -3,7 +3,7 @@
 #include <stdint.h>
 #include <string.h>
 
-#include "mpyconfig.h"
+#include "mpconfig.h"
 #include "gc.h"
 
 // a machine word is big enough to hold a pointer
diff --git a/py/lexer.c b/py/lexer.c
index cd2e05ece01d4432e51bda6e264be4596185b664..cf9eae5531fe2829f096301cab21fe8609c3d97d 100644
--- a/py/lexer.c
+++ b/py/lexer.c
@@ -13,11 +13,11 @@
 // TODO seems that CPython allows NULL byte in the input stream
 // don't know if that's intentional or not, but we don't allow it
 
-struct _py_lexer_t {
+struct _mp_lexer_t {
     const char *name;           // name of source
     void *stream_data;          // data for stream
-    py_lexer_stream_next_char_t stream_next_char;   // stream callback to get next char
-    py_lexer_stream_close_t stream_close;           // stream callback to free
+    mp_lexer_stream_next_char_t stream_next_char;   // stream callback to get next char
+    mp_lexer_stream_close_t stream_close;           // stream callback to free
 
     unichar chr0, chr1, chr2;   // current cached characters from source
 
@@ -32,7 +32,7 @@ struct _py_lexer_t {
     uint16_t *indent_level;
 
     vstr_t vstr;
-    py_token_t tok_cur;
+    mp_token_t tok_cur;
 };
 
 bool str_strn_equal(const char *str, const char *strn, int len) {
@@ -47,7 +47,7 @@ bool str_strn_equal(const char *str, const char *strn, int len) {
     return i == len && *str == 0;
 }
 
-void py_token_show(const py_token_t *tok) {
+void mp_token_show(const mp_token_t *tok) {
     printf("(%s:%d:%d) kind:%d str:%p len:%d", tok->src_name, tok->src_line, tok->src_column, tok->kind, tok->str, tok->len);
     if (tok->str != NULL && tok->len > 0) {
         const char *i = tok->str;
@@ -66,83 +66,83 @@ void py_token_show(const py_token_t *tok) {
     printf("\n");
 }
 
-void py_token_show_error_prefix(const py_token_t *tok) {
+void mp_token_show_error_prefix(const mp_token_t *tok) {
     printf("(%s:%d:%d) ", tok->src_name, tok->src_line, tok->src_column);
 }
 
-bool py_token_show_error(const py_token_t *tok, const char *msg) {
+bool mp_token_show_error(const mp_token_t *tok, const char *msg) {
     printf("(%s:%d:%d) %s\n", tok->src_name, tok->src_line, tok->src_column, msg);
     return false;
 }
 
 #define CUR_CHAR(lex) ((lex)->chr0)
 
-static bool is_end(py_lexer_t *lex) {
-    return lex->chr0 == PY_LEXER_CHAR_EOF;
+static bool is_end(mp_lexer_t *lex) {
+    return lex->chr0 == MP_LEXER_CHAR_EOF;
 }
 
-static bool is_physical_newline(py_lexer_t *lex) {
+static bool is_physical_newline(mp_lexer_t *lex) {
     return lex->chr0 == '\n' || lex->chr0 == '\r';
 }
 
-static bool is_char(py_lexer_t *lex, char c) {
+static bool is_char(mp_lexer_t *lex, char c) {
     return lex->chr0 == c;
 }
 
-static bool is_char_or(py_lexer_t *lex, char c1, char c2) {
+static bool is_char_or(mp_lexer_t *lex, char c1, char c2) {
     return lex->chr0 == c1 || lex->chr0 == c2;
 }
 
-static bool is_char_or3(py_lexer_t *lex, char c1, char c2, char c3) {
+static bool is_char_or3(mp_lexer_t *lex, char c1, char c2, char c3) {
     return lex->chr0 == c1 || lex->chr0 == c2 || lex->chr0 == c3;
 }
 
 /*
-static bool is_char_following(py_lexer_t *lex, char c) {
+static bool is_char_following(mp_lexer_t *lex, char c) {
     return lex->chr1 == c;
 }
 */
 
-static bool is_char_following_or(py_lexer_t *lex, char c1, char c2) {
+static bool is_char_following_or(mp_lexer_t *lex, char c1, char c2) {
     return lex->chr1 == c1 || lex->chr1 == c2;
 }
 
-static bool is_char_following_following_or(py_lexer_t *lex, char c1, char c2) {
+static bool is_char_following_following_or(mp_lexer_t *lex, char c1, char c2) {
     return lex->chr2 == c1 || lex->chr2 == c2;
 }
 
-static bool is_char_and(py_lexer_t *lex, char c1, char c2) {
+static bool is_char_and(mp_lexer_t *lex, char c1, char c2) {
     return lex->chr0 == c1 && lex->chr1 == c2;
 }
 
-static bool is_whitespace(py_lexer_t *lex) {
+static bool is_whitespace(mp_lexer_t *lex) {
     return g_unichar_isspace(lex->chr0);
 }
 
-static bool is_letter(py_lexer_t *lex) {
+static bool is_letter(mp_lexer_t *lex) {
     return g_unichar_isalpha(lex->chr0);
 }
 
-static bool is_digit(py_lexer_t *lex) {
+static bool is_digit(mp_lexer_t *lex) {
     return g_unichar_isdigit(lex->chr0);
 }
 
-static bool is_following_digit(py_lexer_t *lex) {
+static bool is_following_digit(mp_lexer_t *lex) {
     return g_unichar_isdigit(lex->chr1);
 }
 
 // TODO UNICODE include unicode characters in definition of identifiers
-static bool is_head_of_identifier(py_lexer_t *lex) {
+static bool is_head_of_identifier(mp_lexer_t *lex) {
     return is_letter(lex) || lex->chr0 == '_';
 }
 
 // TODO UNICODE include unicode characters in definition of identifiers
-static bool is_tail_of_identifier(py_lexer_t *lex) {
+static bool is_tail_of_identifier(mp_lexer_t *lex) {
     return is_head_of_identifier(lex) || is_digit(lex);
 }
 
-static void next_char(py_lexer_t *lex) {
-    if (lex->chr0 == PY_LEXER_CHAR_EOF) {
+static void next_char(mp_lexer_t *lex) {
+    if (lex->chr0 == MP_LEXER_CHAR_EOF) {
         return;
     }
 
@@ -172,16 +172,16 @@ static void next_char(py_lexer_t *lex) {
         lex->chr0 = lex->chr1;
         lex->chr1 = lex->chr2;
         lex->chr2 = lex->stream_next_char(lex->stream_data);
-        if (lex->chr2 == PY_LEXER_CHAR_EOF) {
+        if (lex->chr2 == MP_LEXER_CHAR_EOF) {
             // EOF
-            if (lex->chr1 != PY_LEXER_CHAR_EOF && lex->chr1 != '\n' && lex->chr1 != '\r') {
+            if (lex->chr1 != MP_LEXER_CHAR_EOF && lex->chr1 != '\n' && lex->chr1 != '\r') {
                 lex->chr2 = '\n'; // insert newline at end of file
             }
         }
     }
 }
 
-void indent_push(py_lexer_t *lex, uint indent) {
+void indent_push(mp_lexer_t *lex, uint indent) {
     if (lex->num_indent_level >= lex->alloc_indent_level) {
         lex->alloc_indent_level *= 2;
         lex->indent_level = m_renew(uint16_t, lex->indent_level, lex->alloc_indent_level);
@@ -189,11 +189,11 @@ void indent_push(py_lexer_t *lex, uint indent) {
     lex->indent_level[lex->num_indent_level++] = indent;
 }
 
-uint indent_top(py_lexer_t *lex) {
+uint indent_top(mp_lexer_t *lex) {
     return lex->indent_level[lex->num_indent_level - 1];
 }
 
-void indent_pop(py_lexer_t *lex) {
+void indent_pop(mp_lexer_t *lex) {
     lex->num_indent_level -= 1;
 }
 
@@ -222,24 +222,24 @@ static const char *tok_enc =
 
 // TODO static assert that number of tokens is less than 256 so we can safely make this table with byte sized entries
 static const uint8_t tok_enc_kind[] = {
-    PY_TOKEN_DEL_PAREN_OPEN, PY_TOKEN_DEL_PAREN_CLOSE,
-    PY_TOKEN_DEL_BRACKET_OPEN, PY_TOKEN_DEL_BRACKET_CLOSE,
-    PY_TOKEN_DEL_BRACE_OPEN, PY_TOKEN_DEL_BRACE_CLOSE,
-    PY_TOKEN_DEL_COMMA, PY_TOKEN_DEL_COLON, PY_TOKEN_DEL_SEMICOLON, PY_TOKEN_DEL_AT, PY_TOKEN_OP_TILDE,
-
-    PY_TOKEN_OP_LESS, PY_TOKEN_OP_LESS_EQUAL, PY_TOKEN_OP_DBL_LESS, PY_TOKEN_DEL_DBL_LESS_EQUAL,
-    PY_TOKEN_OP_MORE, PY_TOKEN_OP_MORE_EQUAL, PY_TOKEN_OP_DBL_MORE, PY_TOKEN_DEL_DBL_MORE_EQUAL,
-    PY_TOKEN_OP_STAR, PY_TOKEN_DEL_STAR_EQUAL, PY_TOKEN_OP_DBL_STAR, PY_TOKEN_DEL_DBL_STAR_EQUAL,
-    PY_TOKEN_OP_PLUS, PY_TOKEN_DEL_PLUS_EQUAL,
-    PY_TOKEN_OP_MINUS, PY_TOKEN_DEL_MINUS_EQUAL, PY_TOKEN_DEL_MINUS_MORE,
-    PY_TOKEN_OP_AMPERSAND, PY_TOKEN_DEL_AMPERSAND_EQUAL,
-    PY_TOKEN_OP_PIPE, PY_TOKEN_DEL_PIPE_EQUAL,
-    PY_TOKEN_OP_SLASH, PY_TOKEN_DEL_SLASH_EQUAL, PY_TOKEN_OP_DBL_SLASH, PY_TOKEN_DEL_DBL_SLASH_EQUAL,
-    PY_TOKEN_OP_PERCENT, PY_TOKEN_DEL_PERCENT_EQUAL,
-    PY_TOKEN_OP_CARET, PY_TOKEN_DEL_CARET_EQUAL,
-    PY_TOKEN_DEL_EQUAL, PY_TOKEN_OP_DBL_EQUAL,
-    PY_TOKEN_OP_NOT_EQUAL,
-    PY_TOKEN_DEL_PERIOD, PY_TOKEN_ELLIPSES,
+    MP_TOKEN_DEL_PAREN_OPEN, MP_TOKEN_DEL_PAREN_CLOSE,
+    MP_TOKEN_DEL_BRACKET_OPEN, MP_TOKEN_DEL_BRACKET_CLOSE,
+    MP_TOKEN_DEL_BRACE_OPEN, MP_TOKEN_DEL_BRACE_CLOSE,
+    MP_TOKEN_DEL_COMMA, MP_TOKEN_DEL_COLON, MP_TOKEN_DEL_SEMICOLON, MP_TOKEN_DEL_AT, MP_TOKEN_OP_TILDE,
+
+    MP_TOKEN_OP_LESS, MP_TOKEN_OP_LESS_EQUAL, MP_TOKEN_OP_DBL_LESS, MP_TOKEN_DEL_DBL_LESS_EQUAL,
+    MP_TOKEN_OP_MORE, MP_TOKEN_OP_MORE_EQUAL, MP_TOKEN_OP_DBL_MORE, MP_TOKEN_DEL_DBL_MORE_EQUAL,
+    MP_TOKEN_OP_STAR, MP_TOKEN_DEL_STAR_EQUAL, MP_TOKEN_OP_DBL_STAR, MP_TOKEN_DEL_DBL_STAR_EQUAL,
+    MP_TOKEN_OP_PLUS, MP_TOKEN_DEL_PLUS_EQUAL,
+    MP_TOKEN_OP_MINUS, MP_TOKEN_DEL_MINUS_EQUAL, MP_TOKEN_DEL_MINUS_MORE,
+    MP_TOKEN_OP_AMPERSAND, MP_TOKEN_DEL_AMPERSAND_EQUAL,
+    MP_TOKEN_OP_PIPE, MP_TOKEN_DEL_PIPE_EQUAL,
+    MP_TOKEN_OP_SLASH, MP_TOKEN_DEL_SLASH_EQUAL, MP_TOKEN_OP_DBL_SLASH, MP_TOKEN_DEL_DBL_SLASH_EQUAL,
+    MP_TOKEN_OP_PERCENT, MP_TOKEN_DEL_PERCENT_EQUAL,
+    MP_TOKEN_OP_CARET, MP_TOKEN_DEL_CARET_EQUAL,
+    MP_TOKEN_DEL_EQUAL, MP_TOKEN_OP_DBL_EQUAL,
+    MP_TOKEN_OP_NOT_EQUAL,
+    MP_TOKEN_DEL_PERIOD, MP_TOKEN_ELLIPSES,
 };
 
 // must have the same order as enum in lexer.h
@@ -280,7 +280,7 @@ static const char *tok_kw[] = {
     NULL,
 };
 
-static void py_lexer_next_token_into(py_lexer_t *lex, py_token_t *tok, bool first_token) {
+static void mp_lexer_next_token_into(mp_lexer_t *lex, mp_token_t *tok, bool first_token) {
     // skip white space and comments
     bool had_physical_newline = false;
     while (!is_end(lex)) {
@@ -322,18 +322,18 @@ static void py_lexer_next_token_into(py_lexer_t *lex, py_token_t *tok, bool firs
         // if first token is not on first line, we get a physical newline and
         // this check is done as part of normal indent/dedent checking below
         // (done to get equivalence with CPython)
-        tok->kind = PY_TOKEN_INDENT;
+        tok->kind = MP_TOKEN_INDENT;
 
     } else if (lex->emit_dent < 0) {
-        tok->kind = PY_TOKEN_DEDENT;
+        tok->kind = MP_TOKEN_DEDENT;
         lex->emit_dent += 1;
 
     } else if (lex->emit_dent > 0) {
-        tok->kind = PY_TOKEN_INDENT;
+        tok->kind = MP_TOKEN_INDENT;
         lex->emit_dent -= 1;
 
     } else if (had_physical_newline && lex->nested_bracket_level == 0) {
-        tok->kind = PY_TOKEN_NEWLINE;
+        tok->kind = MP_TOKEN_NEWLINE;
 
         uint num_spaces = lex->column - 1;
         lex->emit_dent = 0;
@@ -347,20 +347,20 @@ static void py_lexer_next_token_into(py_lexer_t *lex, py_token_t *tok, bool firs
                 lex->emit_dent -= 1;
             }
             if (num_spaces != indent_top(lex)) {
-                tok->kind = PY_TOKEN_DEDENT_MISMATCH;
+                tok->kind = MP_TOKEN_DEDENT_MISMATCH;
             }
         }
 
     } else if (is_end(lex)) {
         if (indent_top(lex) > 0) {
-            tok->kind = PY_TOKEN_NEWLINE;
+            tok->kind = MP_TOKEN_NEWLINE;
             lex->emit_dent = 0;
             while (indent_top(lex) > 0) {
                 indent_pop(lex);
                 lex->emit_dent -= 1;
             }
         } else {
-            tok->kind = PY_TOKEN_END;
+            tok->kind = MP_TOKEN_END;
         }
 
     } else if (is_char_or(lex, '\'', '\"')
@@ -391,9 +391,9 @@ static void py_lexer_next_token_into(py_lexer_t *lex, py_token_t *tok, bool firs
 
         // set token kind
         if (is_bytes) {
-            tok->kind = PY_TOKEN_BYTES;
+            tok->kind = MP_TOKEN_BYTES;
         } else {
-            tok->kind = PY_TOKEN_STRING;
+            tok->kind = MP_TOKEN_STRING;
         }
 
         // get first quoting character
@@ -427,8 +427,8 @@ static void py_lexer_next_token_into(py_lexer_t *lex, py_token_t *tok, bool firs
                     next_char(lex);
                     unichar c = CUR_CHAR(lex);
                     switch (c) {
-                        case PY_LEXER_CHAR_EOF: break; // TODO a proper error message?
-                        case '\n': c = PY_LEXER_CHAR_EOF; break; // TODO check this works correctly (we are supposed to ignore it
+                        case MP_LEXER_CHAR_EOF: break; // TODO a proper error message?
+                        case '\n': c = MP_LEXER_CHAR_EOF; break; // TODO check this works correctly (we are supposed to ignore it
                         case '\\': break;
                         case '\'': break;
                         case '"': break;
@@ -446,7 +446,7 @@ static void py_lexer_next_token_into(py_lexer_t *lex, py_token_t *tok, bool firs
                         case 'U': // TODO \Uxxxxxxxx only in strings
                         default: break; // TODO error message
                     }
-                    if (c != PY_LEXER_CHAR_EOF) {
+                    if (c != MP_LEXER_CHAR_EOF) {
                         vstr_add_char(&lex->vstr, c);
                     }
                 } else {
@@ -458,14 +458,14 @@ static void py_lexer_next_token_into(py_lexer_t *lex, py_token_t *tok, bool firs
 
         // check we got the required end quotes
         if (n_closing < num_quotes) {
-            tok->kind = PY_TOKEN_LONELY_STRING_OPEN;
+            tok->kind = MP_TOKEN_LONELY_STRING_OPEN;
         }
 
         // cut off the end quotes from the token text
         vstr_cut_tail(&lex->vstr, n_closing);
 
     } else if (is_head_of_identifier(lex)) {
-        tok->kind = PY_TOKEN_NAME;
+        tok->kind = MP_TOKEN_NAME;
 
         // get first char
         vstr_add_char(&lex->vstr, CUR_CHAR(lex));
@@ -478,7 +478,7 @@ static void py_lexer_next_token_into(py_lexer_t *lex, py_token_t *tok, bool firs
         }
 
     } else if (is_digit(lex) || (is_char(lex, '.') && is_following_digit(lex))) {
-        tok->kind = PY_TOKEN_NUMBER;
+        tok->kind = MP_TOKEN_NUMBER;
 
         // get first char
         vstr_add_char(&lex->vstr, CUR_CHAR(lex));
@@ -520,7 +520,7 @@ static void py_lexer_next_token_into(py_lexer_t *lex, py_token_t *tok, bool firs
 
         if (*t == 0) {
             // didn't match any delimiter or operator characters
-            tok->kind = PY_TOKEN_INVALID;
+            tok->kind = MP_TOKEN_INVALID;
 
         } else {
             // matched a delimiter or operator character
@@ -545,7 +545,7 @@ static void py_lexer_next_token_into(py_lexer_t *lex, py_token_t *tok, bool firs
                         next_char(lex);
                         tok_enc_index = t_index;
                     } else {
-                        tok->kind = PY_TOKEN_INVALID;
+                        tok->kind = MP_TOKEN_INVALID;
                     }
                     break;
                 }
@@ -569,9 +569,9 @@ static void py_lexer_next_token_into(py_lexer_t *lex, py_token_t *tok, bool firs
             tok->kind = tok_enc_kind[tok_enc_index];
 
             // compute bracket level for implicit line joining
-            if (tok->kind == PY_TOKEN_DEL_PAREN_OPEN || tok->kind == PY_TOKEN_DEL_BRACKET_OPEN || tok->kind == PY_TOKEN_DEL_BRACE_OPEN) {
+            if (tok->kind == MP_TOKEN_DEL_PAREN_OPEN || tok->kind == MP_TOKEN_DEL_BRACKET_OPEN || tok->kind == MP_TOKEN_DEL_BRACE_OPEN) {
                 lex->nested_bracket_level += 1;
-            } else if (tok->kind == PY_TOKEN_DEL_PAREN_CLOSE || tok->kind == PY_TOKEN_DEL_BRACKET_CLOSE || tok->kind == PY_TOKEN_DEL_BRACE_CLOSE) {
+            } else if (tok->kind == MP_TOKEN_DEL_PAREN_CLOSE || tok->kind == MP_TOKEN_DEL_BRACKET_CLOSE || tok->kind == MP_TOKEN_DEL_BRACE_CLOSE) {
                 lex->nested_bracket_level -= 1;
             }
         }
@@ -582,18 +582,18 @@ static void py_lexer_next_token_into(py_lexer_t *lex, py_token_t *tok, bool firs
     tok->len = vstr_len(&lex->vstr);
 
     // check for keywords
-    if (tok->kind == PY_TOKEN_NAME) {
+    if (tok->kind == MP_TOKEN_NAME) {
         for (int i = 0; tok_kw[i] != NULL; i++) {
             if (str_strn_equal(tok_kw[i], tok->str, tok->len)) {
-                tok->kind = PY_TOKEN_KW_FALSE + i;
+                tok->kind = MP_TOKEN_KW_FALSE + i;
                 break;
             }
         }
     }
 }
 
-py_lexer_t *py_lexer_new(const char *src_name, void *stream_data, py_lexer_stream_next_char_t stream_next_char, py_lexer_stream_close_t stream_close) {
-    py_lexer_t *lex = m_new(py_lexer_t, 1);
+mp_lexer_t *mp_lexer_new(const char *src_name, void *stream_data, mp_lexer_stream_next_char_t stream_next_char, mp_lexer_stream_close_t stream_close) {
+    mp_lexer_t *lex = m_new(mp_lexer_t, 1);
 
     lex->name = src_name; // TODO do we need to strdup this?
     lex->stream_data = stream_data;
@@ -615,25 +615,25 @@ py_lexer_t *py_lexer_new(const char *src_name, void *stream_data, py_lexer_strea
     lex->chr2 = stream_next_char(stream_data);
 
     // if input stream is 0, 1 or 2 characters long and doesn't end in a newline, then insert a newline at the end
-    if (lex->chr0 == PY_LEXER_CHAR_EOF) {
+    if (lex->chr0 == MP_LEXER_CHAR_EOF) {
         lex->chr0 = '\n';
-    } else if (lex->chr1 == PY_LEXER_CHAR_EOF) {
+    } else if (lex->chr1 == MP_LEXER_CHAR_EOF) {
         if (lex->chr0 != '\n' && lex->chr0 != '\r') {
             lex->chr1 = '\n';
         }
-    } else if (lex->chr2 == PY_LEXER_CHAR_EOF) {
+    } else if (lex->chr2 == MP_LEXER_CHAR_EOF) {
         if (lex->chr1 != '\n' && lex->chr1 != '\r') {
             lex->chr2 = '\n';
         }
     }
 
     // preload first token
-    py_lexer_next_token_into(lex, &lex->tok_cur, true);
+    mp_lexer_next_token_into(lex, &lex->tok_cur, true);
 
     return lex;
 }
 
-void py_lexer_free(py_lexer_t *lex) {
+void mp_lexer_free(mp_lexer_t *lex) {
     if (lex) {
         if (lex->stream_close) {
             lex->stream_close(lex->stream_data);
@@ -643,45 +643,45 @@ void py_lexer_free(py_lexer_t *lex) {
     }
 }
 
-void py_lexer_to_next(py_lexer_t *lex) {
-    py_lexer_next_token_into(lex, &lex->tok_cur, false);
+void mp_lexer_to_next(mp_lexer_t *lex) {
+    mp_lexer_next_token_into(lex, &lex->tok_cur, false);
 }
 
-const py_token_t *py_lexer_cur(const py_lexer_t *lex) {
+const mp_token_t *mp_lexer_cur(const mp_lexer_t *lex) {
     return &lex->tok_cur;
 }
 
-bool py_lexer_is_kind(py_lexer_t *lex, py_token_kind_t kind) {
+bool mp_lexer_is_kind(mp_lexer_t *lex, mp_token_kind_t kind) {
     return lex->tok_cur.kind == kind;
 }
 
 /*
-bool py_lexer_is_str(py_lexer_t *lex, const char *str) {
-    return py_token_is_str(&lex->tok_cur, str);
+bool mp_lexer_is_str(mp_lexer_t *lex, const char *str) {
+    return mp_token_is_str(&lex->tok_cur, str);
 }
 
-bool py_lexer_opt_kind(py_lexer_t *lex, py_token_kind_t kind) {
-    if (py_lexer_is_kind(lex, kind)) {
-        py_lexer_to_next(lex);
+bool mp_lexer_opt_kind(mp_lexer_t *lex, mp_token_kind_t kind) {
+    if (mp_lexer_is_kind(lex, kind)) {
+        mp_lexer_to_next(lex);
         return true;
     }
     return false;
 }
 
-bool py_lexer_opt_str(py_lexer_t *lex, const char *str) {
-    if (py_lexer_is_str(lex, str)) {
-        py_lexer_to_next(lex);
+bool mp_lexer_opt_str(mp_lexer_t *lex, const char *str) {
+    if (mp_lexer_is_str(lex, str)) {
+        mp_lexer_to_next(lex);
         return true;
     }
     return false;
 }
 */
 
-bool py_lexer_show_error(py_lexer_t *lex, const char *msg) {
-    return py_token_show_error(&lex->tok_cur, msg);
+bool mp_lexer_show_error(mp_lexer_t *lex, const char *msg) {
+    return mp_token_show_error(&lex->tok_cur, msg);
 }
 
-bool py_lexer_show_error_pythonic(py_lexer_t *lex, const char *msg) {
+bool mp_lexer_show_error_pythonic(mp_lexer_t *lex, const char *msg) {
     printf("  File \"%s\", line %d column %d\n%s\n", lex->tok_cur.src_name, lex->tok_cur.src_line, lex->tok_cur.src_column, msg);
     return false;
 }
diff --git a/py/lexer.h b/py/lexer.h
index 3472370604638302a59d969d7ec72fd350901853..f58a38e92b7c0f88aab0947c5f85bf96cb642fa0 100644
--- a/py/lexer.h
+++ b/py/lexer.h
@@ -1,146 +1,140 @@
-/* lexer.h -- simple tokeniser for Python implementation
+/* lexer.h -- simple tokeniser for Micro Python
+ *
+ * Uses (byte) length instead of null termination.
+ * Tokens are the same - UTF-8 with (byte) length.
  */
 
-#ifndef INCLUDED_LEXER_H
-#define INCLUDED_LEXER_H
-
-/* uses (byte) length instead of null termination
- * tokens are the same - UTF-8 with (byte) length
- */
-
-typedef enum _py_token_kind_t {
-    PY_TOKEN_END,                   // 0
-
-    PY_TOKEN_INVALID,
-    PY_TOKEN_DEDENT_MISMATCH,
-    PY_TOKEN_LONELY_STRING_OPEN,
-
-    PY_TOKEN_NEWLINE,               // 4
-    PY_TOKEN_INDENT,                // 5
-    PY_TOKEN_DEDENT,                // 6
-
-    PY_TOKEN_NAME,                  // 7
-    PY_TOKEN_NUMBER,
-    PY_TOKEN_STRING,
-    PY_TOKEN_BYTES,
-
-    PY_TOKEN_ELLIPSES,
-
-    PY_TOKEN_KW_FALSE,              // 12
-    PY_TOKEN_KW_NONE,
-    PY_TOKEN_KW_TRUE,
-    PY_TOKEN_KW_AND,
-    PY_TOKEN_KW_AS,
-    PY_TOKEN_KW_ASSERT,
-    PY_TOKEN_KW_BREAK,
-    PY_TOKEN_KW_CLASS,
-    PY_TOKEN_KW_CONTINUE,
-    PY_TOKEN_KW_DEF,                // 21
-    PY_TOKEN_KW_DEL,
-    PY_TOKEN_KW_ELIF,
-    PY_TOKEN_KW_ELSE,
-    PY_TOKEN_KW_EXCEPT,
-    PY_TOKEN_KW_FINALLY,
-    PY_TOKEN_KW_FOR,
-    PY_TOKEN_KW_FROM,
-    PY_TOKEN_KW_GLOBAL,
-    PY_TOKEN_KW_IF,
-    PY_TOKEN_KW_IMPORT,             // 31
-    PY_TOKEN_KW_IN,
-    PY_TOKEN_KW_IS,
-    PY_TOKEN_KW_LAMBDA,
-    PY_TOKEN_KW_NONLOCAL,
-    PY_TOKEN_KW_NOT,
-    PY_TOKEN_KW_OR,
-    PY_TOKEN_KW_PASS,
-    PY_TOKEN_KW_RAISE,
-    PY_TOKEN_KW_RETURN,
-    PY_TOKEN_KW_TRY,                // 41
-    PY_TOKEN_KW_WHILE,
-    PY_TOKEN_KW_WITH,
-    PY_TOKEN_KW_YIELD,
-
-    PY_TOKEN_OP_PLUS,               // 45
-    PY_TOKEN_OP_MINUS,
-    PY_TOKEN_OP_STAR,
-    PY_TOKEN_OP_DBL_STAR,
-    PY_TOKEN_OP_SLASH,
-    PY_TOKEN_OP_DBL_SLASH,
-    PY_TOKEN_OP_PERCENT,
-    PY_TOKEN_OP_LESS,
-    PY_TOKEN_OP_DBL_LESS,
-    PY_TOKEN_OP_MORE,
-    PY_TOKEN_OP_DBL_MORE,           // 55
-    PY_TOKEN_OP_AMPERSAND,
-    PY_TOKEN_OP_PIPE,
-    PY_TOKEN_OP_CARET,
-    PY_TOKEN_OP_TILDE,
-    PY_TOKEN_OP_LESS_EQUAL,
-    PY_TOKEN_OP_MORE_EQUAL,
-    PY_TOKEN_OP_DBL_EQUAL,
-    PY_TOKEN_OP_NOT_EQUAL,
-
-    PY_TOKEN_DEL_PAREN_OPEN,        // 64
-    PY_TOKEN_DEL_PAREN_CLOSE,
-    PY_TOKEN_DEL_BRACKET_OPEN,
-    PY_TOKEN_DEL_BRACKET_CLOSE,
-    PY_TOKEN_DEL_BRACE_OPEN,
-    PY_TOKEN_DEL_BRACE_CLOSE,
-    PY_TOKEN_DEL_COMMA,
-    PY_TOKEN_DEL_COLON,
-    PY_TOKEN_DEL_PERIOD,
-    PY_TOKEN_DEL_SEMICOLON,
-    PY_TOKEN_DEL_AT,                // 74
-    PY_TOKEN_DEL_EQUAL,
-    PY_TOKEN_DEL_PLUS_EQUAL,
-    PY_TOKEN_DEL_MINUS_EQUAL,
-    PY_TOKEN_DEL_STAR_EQUAL,
-    PY_TOKEN_DEL_SLASH_EQUAL,
-    PY_TOKEN_DEL_DBL_SLASH_EQUAL,
-    PY_TOKEN_DEL_PERCENT_EQUAL,
-    PY_TOKEN_DEL_AMPERSAND_EQUAL,
-    PY_TOKEN_DEL_PIPE_EQUAL,
-    PY_TOKEN_DEL_CARET_EQUAL,       // 84
-    PY_TOKEN_DEL_DBL_MORE_EQUAL,
-    PY_TOKEN_DEL_DBL_LESS_EQUAL,
-    PY_TOKEN_DEL_DBL_STAR_EQUAL,
-    PY_TOKEN_DEL_MINUS_MORE,
-} py_token_kind_t;
-
-typedef struct _py_token_t {
+typedef enum _mp_token_kind_t {
+    MP_TOKEN_END,                   // 0
+
+    MP_TOKEN_INVALID,
+    MP_TOKEN_DEDENT_MISMATCH,
+    MP_TOKEN_LONELY_STRING_OPEN,
+
+    MP_TOKEN_NEWLINE,               // 4
+    MP_TOKEN_INDENT,                // 5
+    MP_TOKEN_DEDENT,                // 6
+
+    MP_TOKEN_NAME,                  // 7
+    MP_TOKEN_NUMBER,
+    MP_TOKEN_STRING,
+    MP_TOKEN_BYTES,
+
+    MP_TOKEN_ELLIPSES,
+
+    MP_TOKEN_KW_FALSE,              // 12
+    MP_TOKEN_KW_NONE,
+    MP_TOKEN_KW_TRUE,
+    MP_TOKEN_KW_AND,
+    MP_TOKEN_KW_AS,
+    MP_TOKEN_KW_ASSERT,
+    MP_TOKEN_KW_BREAK,
+    MP_TOKEN_KW_CLASS,
+    MP_TOKEN_KW_CONTINUE,
+    MP_TOKEN_KW_DEF,                // 21
+    MP_TOKEN_KW_DEL,
+    MP_TOKEN_KW_ELIF,
+    MP_TOKEN_KW_ELSE,
+    MP_TOKEN_KW_EXCEPT,
+    MP_TOKEN_KW_FINALLY,
+    MP_TOKEN_KW_FOR,
+    MP_TOKEN_KW_FROM,
+    MP_TOKEN_KW_GLOBAL,
+    MP_TOKEN_KW_IF,
+    MP_TOKEN_KW_IMPORT,             // 31
+    MP_TOKEN_KW_IN,
+    MP_TOKEN_KW_IS,
+    MP_TOKEN_KW_LAMBDA,
+    MP_TOKEN_KW_NONLOCAL,
+    MP_TOKEN_KW_NOT,
+    MP_TOKEN_KW_OR,
+    MP_TOKEN_KW_PASS,
+    MP_TOKEN_KW_RAISE,
+    MP_TOKEN_KW_RETURN,
+    MP_TOKEN_KW_TRY,                // 41
+    MP_TOKEN_KW_WHILE,
+    MP_TOKEN_KW_WITH,
+    MP_TOKEN_KW_YIELD,
+
+    MP_TOKEN_OP_PLUS,               // 45
+    MP_TOKEN_OP_MINUS,
+    MP_TOKEN_OP_STAR,
+    MP_TOKEN_OP_DBL_STAR,
+    MP_TOKEN_OP_SLASH,
+    MP_TOKEN_OP_DBL_SLASH,
+    MP_TOKEN_OP_PERCENT,
+    MP_TOKEN_OP_LESS,
+    MP_TOKEN_OP_DBL_LESS,
+    MP_TOKEN_OP_MORE,
+    MP_TOKEN_OP_DBL_MORE,           // 55
+    MP_TOKEN_OP_AMPERSAND,
+    MP_TOKEN_OP_PIPE,
+    MP_TOKEN_OP_CARET,
+    MP_TOKEN_OP_TILDE,
+    MP_TOKEN_OP_LESS_EQUAL,
+    MP_TOKEN_OP_MORE_EQUAL,
+    MP_TOKEN_OP_DBL_EQUAL,
+    MP_TOKEN_OP_NOT_EQUAL,
+
+    MP_TOKEN_DEL_PAREN_OPEN,        // 64
+    MP_TOKEN_DEL_PAREN_CLOSE,
+    MP_TOKEN_DEL_BRACKET_OPEN,
+    MP_TOKEN_DEL_BRACKET_CLOSE,
+    MP_TOKEN_DEL_BRACE_OPEN,
+    MP_TOKEN_DEL_BRACE_CLOSE,
+    MP_TOKEN_DEL_COMMA,
+    MP_TOKEN_DEL_COLON,
+    MP_TOKEN_DEL_PERIOD,
+    MP_TOKEN_DEL_SEMICOLON,
+    MP_TOKEN_DEL_AT,                // 74
+    MP_TOKEN_DEL_EQUAL,
+    MP_TOKEN_DEL_PLUS_EQUAL,
+    MP_TOKEN_DEL_MINUS_EQUAL,
+    MP_TOKEN_DEL_STAR_EQUAL,
+    MP_TOKEN_DEL_SLASH_EQUAL,
+    MP_TOKEN_DEL_DBL_SLASH_EQUAL,
+    MP_TOKEN_DEL_PERCENT_EQUAL,
+    MP_TOKEN_DEL_AMPERSAND_EQUAL,
+    MP_TOKEN_DEL_PIPE_EQUAL,
+    MP_TOKEN_DEL_CARET_EQUAL,       // 84
+    MP_TOKEN_DEL_DBL_MORE_EQUAL,
+    MP_TOKEN_DEL_DBL_LESS_EQUAL,
+    MP_TOKEN_DEL_DBL_STAR_EQUAL,
+    MP_TOKEN_DEL_MINUS_MORE,
+} mp_token_kind_t;
+
+typedef struct _mp_token_t {
     const char *src_name;       // name of source
     uint src_line;              // source line
     uint src_column;            // source column
 
-    py_token_kind_t kind;       // kind of token
+    mp_token_kind_t kind;       // kind of token
     const char *str;            // string of token (valid only while this token is current token)
     uint len;                   // (byte) length of string of token
-} py_token_t;
+} mp_token_t;
 
 // the next-char function must return the next character in the stream
-// it must return PY_LEXER_CHAR_EOF if end of stream
-// it can be called again after returning PY_LEXER_CHAR_EOF, and in that case must return PY_LEXER_CHAR_EOF
-#define PY_LEXER_CHAR_EOF (-1)
-typedef unichar (*py_lexer_stream_next_char_t)(void*);
-typedef void (*py_lexer_stream_close_t)(void*);
-
-typedef struct _py_lexer_t py_lexer_t;
-
-void py_token_show(const py_token_t *tok);
-void py_token_show_error_prefix(const py_token_t *tok);
-bool py_token_show_error(const py_token_t *tok, const char *msg);
-
-py_lexer_t *py_lexer_new(const char *src_name, void *stream_data, py_lexer_stream_next_char_t stream_next_char, py_lexer_stream_close_t stream_close);
-void py_lexer_free(py_lexer_t *lex);
-void py_lexer_to_next(py_lexer_t *lex);
-const py_token_t *py_lexer_cur(const py_lexer_t *lex);
-bool py_lexer_is_kind(py_lexer_t *lex, py_token_kind_t kind);
+// it must return MP_LEXER_CHAR_EOF if end of stream
+// it can be called again after returning MP_LEXER_CHAR_EOF, and in that case must return MP_LEXER_CHAR_EOF
+#define MP_LEXER_CHAR_EOF (-1)
+typedef unichar (*mp_lexer_stream_next_char_t)(void*);
+typedef void (*mp_lexer_stream_close_t)(void*);
+
+typedef struct _mp_lexer_t mp_lexer_t;
+
+void mp_token_show(const mp_token_t *tok);
+void mp_token_show_error_prefix(const mp_token_t *tok);
+bool mp_token_show_error(const mp_token_t *tok, const char *msg);
+
+mp_lexer_t *mp_lexer_new(const char *src_name, void *stream_data, mp_lexer_stream_next_char_t stream_next_char, mp_lexer_stream_close_t stream_close);
+void mp_lexer_free(mp_lexer_t *lex);
+void mp_lexer_to_next(mp_lexer_t *lex);
+const mp_token_t *mp_lexer_cur(const mp_lexer_t *lex);
+bool mp_lexer_is_kind(mp_lexer_t *lex, mp_token_kind_t kind);
 /* unused
-bool py_lexer_is_str(py_lexer_t *lex, const char *str);
-bool py_lexer_opt_kind(py_lexer_t *lex, py_token_kind_t kind);
-bool py_lexer_opt_str(py_lexer_t *lex, const char *str);
+bool mp_lexer_is_str(mp_lexer_t *lex, const char *str);
+bool mp_lexer_opt_kind(mp_lexer_t *lex, mp_token_kind_t kind);
+bool mp_lexer_opt_str(mp_lexer_t *lex, const char *str);
 */
-bool py_lexer_show_error(py_lexer_t *lex, const char *msg);
-bool py_lexer_show_error_pythonic(py_lexer_t *lex, const char *msg);
-
-#endif /* INCLUDED_LEXER_H */
+bool mp_lexer_show_error(mp_lexer_t *lex, const char *msg);
+bool mp_lexer_show_error_pythonic(mp_lexer_t *lex, const char *msg);
diff --git a/py/map.c b/py/map.c
index 12753af9f9f99300402073b514f9d1f60437c99d..3c2d1fbb5511174ec5eb9d103646fd775b06566b 100644
--- a/py/map.c
+++ b/py/map.c
@@ -3,12 +3,10 @@
 #include <assert.h>
 
 #include "misc.h"
-#include "mpyconfig.h"
-#include "runtime.h"
-
-#include "map.h"
+#include "mpconfig.h"
 #include "obj.h"
-#include "objprivate.h"
+#include "runtime0.h"
+#include "map.h"
 
 // approximatelly doubling primes; made with Mathematica command: Table[Prime[Floor[(1.7)^n]], {n, 3, 24}]
 static int doubling_primes[] = {7, 19, 43, 89, 179, 347, 647, 1229, 2297, 4243, 7829, 14347, 26017, 47149, 84947, 152443, 273253, 488399, 869927, 1547173, 2745121, 4861607};
@@ -24,43 +22,46 @@ int get_doubling_prime_greater_or_equal_to(int x) {
     return x | 1;
 }
 
-void py_map_init(py_map_t *map, py_map_kind_t kind, int n) {
+/******************************************************************************/
+/* map                                                                        */
+
+void mp_map_init(mp_map_t *map, mp_map_kind_t kind, int n) {
     map->kind = kind;
     map->used = 0;
     map->alloc = get_doubling_prime_greater_or_equal_to(n + 1);
-    map->table = m_new0(py_map_elem_t, map->alloc);
+    map->table = m_new0(mp_map_elem_t, map->alloc);
 }
 
-py_map_t *py_map_new(py_map_kind_t kind, int n) {
-    py_map_t *map = m_new(py_map_t, 1);
-    py_map_init(map, kind, n);
+mp_map_t *mp_map_new(mp_map_kind_t kind, int n) {
+    mp_map_t *map = m_new(mp_map_t, 1);
+    mp_map_init(map, kind, n);
     return map;
 }
 
-py_map_elem_t* py_map_lookup_helper(py_map_t *map, py_obj_t index, bool add_if_not_found) {
-    bool is_map_py_obj = (map->kind == MAP_PY_OBJ);
+mp_map_elem_t* mp_map_lookup_helper(mp_map_t *map, mp_obj_t index, bool add_if_not_found) {
+    bool is_map_mp_obj = (map->kind == MP_MAP_OBJ);
     machine_uint_t hash;
-    if (is_map_py_obj) {
-        hash = py_obj_hash(index);
+    if (is_map_mp_obj) {
+        hash = mp_obj_hash(index);
     } else {
         hash = (machine_uint_t)index;
     }
     uint pos = hash % map->alloc;
     for (;;) {
-        py_map_elem_t *elem = &map->table[pos];
+        mp_map_elem_t *elem = &map->table[pos];
         if (elem->key == NULL) {
             // not in table
             if (add_if_not_found) {
                 if (map->used + 1 >= map->alloc) {
                     // not enough room in table, rehash it
                     int old_alloc = map->alloc;
-                    py_map_elem_t *old_table = map->table;
+                    mp_map_elem_t *old_table = map->table;
                     map->alloc = get_doubling_prime_greater_or_equal_to(map->alloc + 1);
                     map->used = 0;
-                    map->table = m_new0(py_map_elem_t, map->alloc);
+                    map->table = m_new0(mp_map_elem_t, map->alloc);
                     for (int i = 0; i < old_alloc; i++) {
                         if (old_table[i].key != NULL) {
-                            py_map_lookup_helper(map, old_table[i].key, true)->value = old_table[i].value;
+                            mp_map_lookup_helper(map, old_table[i].key, true)->value = old_table[i].value;
                         }
                     }
                     m_free(old_table);
@@ -74,7 +75,7 @@ py_map_elem_t* py_map_lookup_helper(py_map_t *map, py_obj_t index, bool add_if_n
             } else {
                 return NULL;
             }
-        } else if (elem->key == index || (is_map_py_obj && py_obj_equal(elem->key, index))) {
+        } else if (elem->key == index || (is_map_mp_obj && mp_obj_equal(elem->key, index))) {
             // found it
             /* it seems CPython does not replace the index; try x={True:'true'};x[1]='one';x
             if (add_if_not_found) {
@@ -89,55 +90,57 @@ py_map_elem_t* py_map_lookup_helper(py_map_t *map, py_obj_t index, bool add_if_n
     }
 }
 
-py_map_elem_t* py_qstr_map_lookup(py_map_t *map, qstr index, bool add_if_not_found) {
-    py_obj_t o = (py_obj_t)(machine_uint_t)index;
-    return py_map_lookup_helper(map, o, add_if_not_found);
+mp_map_elem_t* mp_qstr_map_lookup(mp_map_t *map, qstr index, bool add_if_not_found) {
+    mp_obj_t o = (mp_obj_t)(machine_uint_t)index;
+    return mp_map_lookup_helper(map, o, add_if_not_found);
 }
 
-py_map_elem_t* py_map_lookup(py_obj_t o, py_obj_t index, bool add_if_not_found) {
-    assert(IS_O(o, O_MAP));
-    return py_map_lookup_helper(&((py_obj_base_t *)o)->u_map, index, add_if_not_found);
+/******************************************************************************/
+/* set                                                                        */
+
+void mp_set_init(mp_set_t *set, int n) {
+    set->alloc = get_doubling_prime_greater_or_equal_to(n + 1);
+    set->used = 0;
+    set->table = m_new0(mp_obj_t, set->alloc);
 }
 
-py_obj_t py_set_lookup(py_obj_t o_in, py_obj_t index, bool add_if_not_found) {
-    assert(IS_O(o_in, O_SET));
-    py_obj_base_t *o = o_in;
-    int hash = py_obj_hash(index);
-    int pos = hash % o->u_set.alloc;
+mp_obj_t mp_set_lookup(mp_set_t *set, mp_obj_t index, bool add_if_not_found) {
+    int hash = mp_obj_hash(index);
+    int pos = hash % set->alloc;
     for (;;) {
-        py_obj_t elem = o->u_set.table[pos];
-        if (elem == NULL) {
+        mp_obj_t elem = set->table[pos];
+        if (elem == MP_OBJ_NULL) {
             // not in table
             if (add_if_not_found) {
-                if (o->u_set.used + 1 >= o->u_set.alloc) {
+                if (set->used + 1 >= set->alloc) {
                     // not enough room in table, rehash it
-                    int old_alloc = o->u_set.alloc;
-                    py_obj_t *old_table = o->u_set.table;
-                    o->u_set.alloc = get_doubling_prime_greater_or_equal_to(o->u_set.alloc + 1);
-                    o->u_set.used = 0;
-                    o->u_set.table = m_new(py_obj_t, o->u_set.alloc);
+                    int old_alloc = set->alloc;
+                    mp_obj_t *old_table = set->table;
+                    set->alloc = get_doubling_prime_greater_or_equal_to(set->alloc + 1);
+                    set->used = 0;
+                    set->table = m_new(mp_obj_t, set->alloc);
                     for (int i = 0; i < old_alloc; i++) {
                         if (old_table[i] != NULL) {
-                            py_set_lookup(o, old_table[i], true);
+                            mp_set_lookup(set, old_table[i], true);
                         }
                     }
                     m_free(old_table);
                     // restart the search for the new element
-                    pos = hash % o->u_set.alloc;
+                    pos = hash % set->alloc;
                 } else {
-                    o->u_set.used += 1;
-                    o->u_set.table[pos] = index;
+                    set->used += 1;
+                    set->table[pos] = index;
                     return index;
                 }
             } else {
-                return NULL;
+                return MP_OBJ_NULL;
             }
-        } else if (py_obj_equal(elem, index)) {
+        } else if (mp_obj_equal(elem, index)) {
             // found it
             return elem;
         } else {
             // not yet found, keep searching in this table
-            pos = (pos + 1) % o->u_set.alloc;
+            pos = (pos + 1) % set->alloc;
         }
     }
 }
diff --git a/py/map.h b/py/map.h
index 6c9df9ece09ecb49be01f8964412f38523522786..8ee8429b5271ff4bf3584b8696f5682a580f110c 100644
--- a/py/map.h
+++ b/py/map.h
@@ -1,30 +1,37 @@
 typedef enum {
-    MAP_QSTR,
-    MAP_PY_OBJ,
-} py_map_kind_t;
+    MP_MAP_QSTR,
+    MP_MAP_OBJ,
+} mp_map_kind_t;
 
-typedef struct _py_map_elem_t {
-    py_obj_t key;
-    py_obj_t value;
-} py_map_elem_t;
+typedef struct _mp_map_elem_t {
+    mp_obj_t key;
+    mp_obj_t value;
+} mp_map_elem_t;
 
-typedef struct _py_map_t {
+typedef struct _mp_map_t {
     struct {
-        py_map_kind_t kind : 1;
+        mp_map_kind_t kind : 1;
         machine_uint_t used : (8 * BYTES_PER_WORD - 1);
     };
     machine_uint_t alloc;
-    py_map_elem_t *table;
-} py_map_t;
+    mp_map_elem_t *table;
+} mp_map_t;
+
+typedef struct _mp_set_t {
+    machine_uint_t alloc;
+    machine_uint_t used;
+    mp_obj_t *table;
+} mp_set_t;
 
 // these are defined in runtime.c
-py_map_t *rt_get_map_locals(void);
-void rt_set_map_locals(py_map_t *m);
+mp_map_t *rt_get_map_locals(void);
+void rt_set_map_locals(mp_map_t *m);
 
 int get_doubling_prime_greater_or_equal_to(int x);
-void py_map_init(py_map_t *map, py_map_kind_t kind, int n);
-py_map_t *py_map_new(py_map_kind_t kind, int n);
-py_map_elem_t* py_map_lookup_helper(py_map_t *map, py_obj_t index, bool add_if_not_found);
-py_map_elem_t* py_qstr_map_lookup(py_map_t *map, qstr index, bool add_if_not_found);
-py_map_elem_t* py_map_lookup(py_obj_t o, py_obj_t index, bool add_if_not_found);
-py_obj_t py_set_lookup(py_obj_t o_in, py_obj_t index, bool add_if_not_found);
+void mp_map_init(mp_map_t *map, mp_map_kind_t kind, int n);
+mp_map_t *mp_map_new(mp_map_kind_t kind, int n);
+mp_map_elem_t* mp_map_lookup_helper(mp_map_t *map, mp_obj_t index, bool add_if_not_found);
+mp_map_elem_t* mp_qstr_map_lookup(mp_map_t *map, qstr index, bool add_if_not_found);
+
+void mp_set_init(mp_set_t *set, int n);
+mp_obj_t mp_set_lookup(mp_set_t *set, mp_obj_t index, bool add_if_not_found);
diff --git a/py/misc.h b/py/misc.h
index 4a100af29d0797cffe5d456302c038fc2e433494..6c0e18ca0f8e23b87f41325e711fbdedce342591 100644
--- a/py/misc.h
+++ b/py/misc.h
@@ -19,6 +19,8 @@ typedef unsigned int uint;
 #define m_new(type, num) ((type*)(m_malloc(sizeof(type) * (num))))
 #define m_new0(type, num) ((type*)(m_malloc0(sizeof(type) * (num))))
 #define m_renew(type, ptr, num) ((type*)(m_realloc((ptr), sizeof(type) * (num))))
+#define m_new_obj(type) (m_new(type, 1))
+#define m_new_obj_var(obj_type, var_type, var_num) ((obj_type*)m_malloc(sizeof(obj_type) + sizeof(var_type) * (var_num)))
 
 void m_free(void *ptr);
 void *m_malloc(int num_bytes);
diff --git a/py/obj.c b/py/obj.c
index 0699d4cde7cc7935a8f7029f8c5a36ee01c0d89e..ff49492bf8e1e40fda87ff04cda91d95a314f966 100644
--- a/py/obj.c
+++ b/py/obj.c
@@ -6,166 +6,22 @@
 
 #include "nlr.h"
 #include "misc.h"
-#include "mpyconfig.h"
+#include "mpconfig.h"
+#include "obj.h"
+#include "runtime0.h"
 #include "runtime.h"
-
 #include "map.h"
-#include "obj.h"
-#include "objprivate.h"
-
-py_obj_t py_obj_new_int(machine_int_t value) {
-    return TO_SMALL_INT(value);
-}
-
-py_obj_t py_obj_new_const(const char *id) {
-    py_obj_base_t *o = m_new(py_obj_base_t, 1);
-    o->kind = O_CONST;
-    o->id = id;
-    return (py_obj_t)o;
-}
-
-py_obj_t py_obj_new_str(qstr qstr) {
-    py_obj_base_t *o = m_new(py_obj_base_t, 1);
-    o->kind = O_STR;
-    o->u_str = qstr;
-    return (py_obj_t)o;
-}
-
-#if MICROPY_ENABLE_FLOAT
-py_obj_t py_obj_new_float(py_float_t val) {
-    py_obj_base_t *o = m_new(py_obj_base_t, 1);
-    o->kind = O_FLOAT;
-    o->u_float = val;
-    return (py_obj_t)o;
-}
-
-py_obj_t py_obj_new_complex(py_float_t real, py_float_t imag) {
-    py_obj_base_t *o = m_new(py_obj_base_t, 1);
-    o->kind = O_COMPLEX;
-    o->u_complex.real = real;
-    o->u_complex.imag = imag;
-    return (py_obj_t)o;
-}
-#endif
-
-py_obj_t py_obj_new_exception_0(qstr id) {
-    py_obj_base_t *o = m_new(py_obj_base_t, 1);
-    o->kind = O_EXCEPTION_0;
-    o->u_exc0.id = id;
-    return (py_obj_t)o;
-}
-
-py_obj_t py_obj_new_exception_2(qstr id, const char *fmt, const char *s1, const char *s2) {
-    py_obj_base_t *o = m_new(py_obj_base_t, 1);
-    o->kind = O_EXCEPTION_N;
-    o->u_exc_n.id = id;
-    o->u_exc_n.n_args = 3;
-    o->u_exc_n.args = m_new(const void*, 3);
-    o->u_exc_n.args[0] = fmt;
-    o->u_exc_n.args[1] = s1;
-    o->u_exc_n.args[2] = s2;
-    return (py_obj_t)o;
-}
-
-// range is a class and instances are immutable sequence objects
-py_obj_t py_obj_new_range(int start, int stop, int step) {
-    py_obj_base_t *o = m_new(py_obj_base_t, 1);
-    o->kind = O_RANGE;
-    o->u_range.start = start;
-    o->u_range.stop = stop;
-    o->u_range.step = step;
-    return o;
-}
-
-py_obj_t py_obj_new_range_iterator(int cur, int stop, int step) {
-    py_obj_base_t *o = m_new(py_obj_base_t, 1);
-    o->kind = O_RANGE_IT;
-    o->u_range_it.cur = cur;
-    o->u_range_it.stop = stop;
-    o->u_range_it.step = step;
-    return o;
-}
-
-py_obj_t py_obj_new_tuple_iterator(py_obj_base_t *tuple, int cur) {
-    py_obj_base_t *o = m_new(py_obj_base_t, 1);
-    o->kind = O_TUPLE_IT;
-    o->u_tuple_list_it.obj = tuple;
-    o->u_tuple_list_it.cur = cur;
-    return o;
-}
 
-py_obj_t py_obj_new_list_iterator(py_obj_base_t *list, int cur) {
-    py_obj_base_t *o = m_new(py_obj_base_t, 1);
-    o->kind = O_LIST_IT;
-    o->u_tuple_list_it.obj = list;
-    o->u_tuple_list_it.cur = cur;
-    return o;
+mp_obj_t mp_obj_new_int(machine_int_t value) {
+    return MP_OBJ_NEW_SMALL_INT(value);
 }
 
-py_obj_t py_obj_new_user(const py_user_info_t *info, machine_uint_t data1, machine_uint_t data2) {
-    py_obj_base_t *o = m_new(py_obj_base_t, 1);
-    o->kind = O_USER;
-    // TODO should probably parse the info to turn strings to qstr's, and wrap functions in O_FUN_N objects
-    // that'll take up some memory.  maybe we can lazily do the O_FUN_N: leave it a ptr to a C function, and
-    // only when the method is looked-up do we change that to the O_FUN_N object.
-    o->u_user.info = info;
-    o->u_user.data1 = data1;
-    o->u_user.data2 = data2;
-    return o;
-}
-
-const char *py_obj_get_type_str(py_obj_t o_in) {
-    if (IS_SMALL_INT(o_in)) {
+const char *mp_obj_get_type_str(mp_obj_t o_in) {
+    if (MP_OBJ_IS_SMALL_INT(o_in)) {
         return "int";
     } else {
-        py_obj_base_t *o = o_in;
-        switch (o->kind) {
-            case O_CONST:
-                if (o == py_const_none) {
-                    return "NoneType";
-                } else {
-                    return "bool";
-                }
-            case O_STR:
-                return "str";
-#if MICROPY_ENABLE_FLOAT
-            case O_FLOAT:
-                return "float";
-#endif
-            case O_FUN_0:
-            case O_FUN_1:
-            case O_FUN_2:
-            case O_FUN_N:
-            case O_FUN_VAR:
-            case O_FUN_BC:
-                return "function";
-            case O_GEN_INSTANCE:
-                return "generator";
-            case O_TUPLE:
-                return "tuple";
-            case O_LIST:
-                return "list";
-            case O_TUPLE_IT:
-                return "tuple_iterator";
-            case O_LIST_IT:
-                return "list_iterator";
-            case O_SET:
-                return "set";
-            case O_MAP:
-                return "dict";
-            case O_OBJ:
-            {
-                py_map_elem_t *qn = py_qstr_map_lookup(o->u_obj.class->u_class.locals, qstr_from_str_static("__qualname__"), false);
-                assert(qn != NULL);
-                assert(IS_O(qn->value, O_STR));
-                return qstr_str(((py_obj_base_t*)qn->value)->u_str);
-            }
-            case O_USER:
-                return o->u_user.info->type_name;
-            default:
-                assert(0);
-                return "UnknownType";
-        }
+        mp_obj_base_t *o = o_in;
+        return o->type->name;
     }
 }
 
@@ -176,148 +32,43 @@ void printf_wrapper(void *env, const char *fmt, ...) {
     va_end(args);
 }
 
-void py_obj_print_helper(void (*print)(void *env, const char *fmt, ...), void *env, py_obj_t o_in) {
-    if (IS_SMALL_INT(o_in)) {
-        print(env, "%d", (int)FROM_SMALL_INT(o_in));
+void mp_obj_print_helper(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t o_in) {
+    if (MP_OBJ_IS_SMALL_INT(o_in)) {
+        print(env, "%d", (int)MP_OBJ_SMALL_INT_VALUE(o_in));
     } else {
-        py_obj_base_t *o = o_in;
-        switch (o->kind) {
-            case O_CONST:
-                print(env, "%s", o->id);
-                break;
-            case O_STR:
-                // TODO need to escape chars etc
-                print(env, "'%s'", qstr_str(o->u_str));
-                break;
-#if MICROPY_ENABLE_FLOAT
-            case O_FLOAT:
-                print(env, "%.8g", o->u_float);
-                break;
-            case O_COMPLEX:
-                if (o->u_complex.real == 0) {
-                    print(env, "%.8gj", o->u_complex.imag);
-                } else {
-                    print(env, "(%.8g+%.8gj)", o->u_complex.real, o->u_complex.imag);
-                }
-                break;
-#endif
-            case O_EXCEPTION_0:
-                print(env, "%s", qstr_str(o->u_exc0.id));
-                break;
-            case O_EXCEPTION_N:
-                print(env, "%s: ", qstr_str(o->u_exc_n.id));
-                print(env, o->u_exc_n.args[0], o->u_exc_n.args[1], o->u_exc_n.args[2]);
-                break;
-            case O_GEN_INSTANCE:
-                print(env, "<generator object 'fun-name' at %p>", o);
-                break;
-            case O_TUPLE:
-                print(env, "(");
-                for (int i = 0; i < o->u_tuple_list.len; i++) {
-                    if (i > 0) {
-                        print(env, ", ");
-                    }
-                    py_obj_print_helper(print, env, o->u_tuple_list.items[i]);
-                }
-                if (o->u_tuple_list.len == 1) {
-                    print(env, ",");
-                }
-                print(env, ")");
-                break;
-            case O_LIST:
-                print(env, "[");
-                for (int i = 0; i < o->u_tuple_list.len; i++) {
-                    if (i > 0) {
-                        print(env, ", ");
-                    }
-                    py_obj_print_helper(print, env, o->u_tuple_list.items[i]);
-                }
-                print(env, "]");
-                break;
-            case O_SET:
-            {
-                bool first = true;
-                print(env, "{");
-                for (int i = 0; i < o->u_set.alloc; i++) {
-                    if (o->u_set.table[i] != NULL) {
-                        if (!first) {
-                            print(env, ", ");
-                        }
-                        first = false;
-                        py_obj_print_helper(print, env, o->u_set.table[i]);
-                    }
-                }
-                print(env, "}");
-                break;
-            }
-            case O_MAP:
-            {
-                bool first = true;
-                print(env, "{");
-                for (int i = 0; i < o->u_map.alloc; i++) {
-                    if (o->u_map.table[i].key != NULL) {
-                        if (!first) {
-                            print(env, ", ");
-                        }
-                        first = false;
-                        py_obj_print_helper(print, env, o->u_map.table[i].key);
-                        print(env, ": ");
-                        py_obj_print_helper(print, env, o->u_map.table[i].value);
-                    }
-                }
-                print(env, "}");
-                break;
-            }
-            case O_USER:
-                if (o->u_user.info->print == NULL) {
-                    print(env, "<unknown user object>");
-                } else {
-                    o->u_user.info->print(o_in);
-                }
-                break;
-            default:
-                print(env, "<? %d>", o->kind);
-                assert(0);
+        mp_obj_base_t *o = o_in;
+        if (o->type->print != NULL) {
+            o->type->print(print, env, o_in);
+        } else {
+            print(env, "<%s>", o->type->name);
         }
     }
 }
 
-void py_obj_print(py_obj_t o_in) {
-    py_obj_print_helper(printf_wrapper, NULL, o_in);
+void mp_obj_print(mp_obj_t o_in) {
+    mp_obj_print_helper(printf_wrapper, NULL, o_in);
 }
 
-bool py_obj_is_callable(py_obj_t o_in) {
-    if (IS_SMALL_INT(o_in)) {
+bool mp_obj_is_callable(mp_obj_t o_in) {
+    if (MP_OBJ_IS_SMALL_INT(o_in)) {
         return false;
     } else {
-        py_obj_base_t *o = o_in;
-        switch (o->kind) {
-            case O_FUN_0:
-            case O_FUN_1:
-            case O_FUN_2:
-            case O_FUN_VAR:
-            case O_FUN_N:
-            case O_FUN_BC:
-            case O_FUN_ASM:
-            // what about O_CLASS, and an O_OBJ that has a __call__ method?
-                return true;
-            default:
-                return false;
-        }
+        mp_obj_base_t *o = o_in;
+        return o->type->call_n != NULL;
     }
 }
 
-machine_int_t py_obj_hash(py_obj_t o_in) {
-    if (o_in == py_const_false) {
+machine_int_t mp_obj_hash(mp_obj_t o_in) {
+    if (o_in == mp_const_false) {
         return 0; // needs to hash to same as the integer 0, since False==0
-    } else if (o_in == py_const_true) {
+    } else if (o_in == mp_const_true) {
         return 1; // needs to hash to same as the integer 1, since True==1
-    } else if (IS_SMALL_INT(o_in)) {
-        return FROM_SMALL_INT(o_in);
-    } else if (IS_O(o_in, O_CONST)) {
+    } else if (MP_OBJ_IS_SMALL_INT(o_in)) {
+        return MP_OBJ_SMALL_INT_VALUE(o_in);
+    } else if (MP_OBJ_IS_TYPE(o_in, &none_type)) {
         return (machine_int_t)o_in;
-    } else if (IS_O(o_in, O_STR)) {
-        return ((py_obj_base_t*)o_in)->u_str;
+    } else if (MP_OBJ_IS_TYPE(o_in, &str_type)) {
+        return mp_obj_str_get(o_in);
     } else {
         assert(0);
         return 0;
@@ -330,38 +81,38 @@ machine_int_t py_obj_hash(py_obj_t o_in) {
 // to a common type. Otherwise, the == and != operators always consider objects of
 // different types to be unequal."
 // note also that False==0 and True==1 are true expressions
-bool py_obj_equal(py_obj_t o1, py_obj_t o2) {
+bool mp_obj_equal(mp_obj_t o1, mp_obj_t o2) {
     if (o1 == o2) {
         return true;
-    } else if (IS_SMALL_INT(o1) || IS_SMALL_INT(o2)) {
-        if (IS_SMALL_INT(o1) && IS_SMALL_INT(o2)) {
+    } else if (MP_OBJ_IS_SMALL_INT(o1) || MP_OBJ_IS_SMALL_INT(o2)) {
+        if (MP_OBJ_IS_SMALL_INT(o1) && MP_OBJ_IS_SMALL_INT(o2)) {
             return false;
         } else {
-            if (IS_SMALL_INT(o2)) {
-                py_obj_t temp = o1; o1 = o2; o2 = temp;
+            if (MP_OBJ_IS_SMALL_INT(o2)) {
+                mp_obj_t temp = o1; o1 = o2; o2 = temp;
             }
             // o1 is the SMALL_INT, o2 is not
-            py_small_int_t val = FROM_SMALL_INT(o1);
-            if (o2 == py_const_false) {
+            mp_small_int_t val = MP_OBJ_SMALL_INT_VALUE(o1);
+            if (o2 == mp_const_false) {
                 return val == 0;
-            } else if (o2 == py_const_true) {
+            } else if (o2 == mp_const_true) {
                 return val == 1;
             } else {
                 return false;
             }
         }
-    } else if (IS_O(o1, O_STR) && IS_O(o2, O_STR)) {
-        return ((py_obj_base_t*)o1)->u_str == ((py_obj_base_t*)o2)->u_str;
+    } else if (MP_OBJ_IS_TYPE(o1, &str_type) && MP_OBJ_IS_TYPE(o2, &str_type)) {
+        return mp_obj_str_get(o1) == mp_obj_str_get(o2);
     } else {
         assert(0);
         return false;
     }
 }
 
-bool py_obj_less(py_obj_t o1, py_obj_t o2) {
-    if (IS_SMALL_INT(o1) && IS_SMALL_INT(o2)) {
-        py_small_int_t i1 = FROM_SMALL_INT(o1);
-        py_small_int_t i2 = FROM_SMALL_INT(o2);
+bool mp_obj_less(mp_obj_t o1, mp_obj_t o2) {
+    if (MP_OBJ_IS_SMALL_INT(o1) && MP_OBJ_IS_SMALL_INT(o2)) {
+        mp_small_int_t i1 = MP_OBJ_SMALL_INT_VALUE(o1);
+        mp_small_int_t i2 = MP_OBJ_SMALL_INT_VALUE(o2);
         return i1 < i2;
     } else {
         assert(0);
@@ -369,13 +120,13 @@ bool py_obj_less(py_obj_t o1, py_obj_t o2) {
     }
 }
 
-machine_int_t py_obj_get_int(py_obj_t arg) {
-    if (arg == py_const_false) {
+machine_int_t mp_obj_get_int(mp_obj_t arg) {
+    if (arg == mp_const_false) {
         return 0;
-    } else if (arg == py_const_true) {
+    } else if (arg == mp_const_true) {
         return 1;
-    } else if (IS_SMALL_INT(arg)) {
-        return FROM_SMALL_INT(arg);
+    } else if (MP_OBJ_IS_SMALL_INT(arg)) {
+        return MP_OBJ_SMALL_INT_VALUE(arg);
     } else {
         assert(0);
         return 0;
@@ -383,37 +134,36 @@ machine_int_t py_obj_get_int(py_obj_t arg) {
 }
 
 #if MICROPY_ENABLE_FLOAT
-machine_float_t py_obj_get_float(py_obj_t arg) {
-    if (arg == py_const_false) {
+machine_float_t mp_obj_get_float(mp_obj_t arg) {
+    if (arg == mp_const_false) {
         return 0;
-    } else if (arg == py_const_true) {
+    } else if (arg == mp_const_true) {
         return 1;
-    } else if (IS_SMALL_INT(arg)) {
-        return FROM_SMALL_INT(arg);
-    } else if (IS_O(arg, O_FLOAT)) {
-        return ((py_obj_base_t*)arg)->u_float;
+    } else if (MP_OBJ_IS_SMALL_INT(arg)) {
+        return MP_OBJ_SMALL_INT_VALUE(arg);
+    } else if (MP_OBJ_IS_TYPE(arg, &float_type)) {
+        return mp_obj_float_get(arg);
     } else {
         assert(0);
         return 0;
     }
 }
 
-void py_obj_get_complex(py_obj_t arg, py_float_t *real, py_float_t *imag) {
-    if (arg == py_const_false) {
+void mp_obj_get_complex(mp_obj_t arg, mp_float_t *real, mp_float_t *imag) {
+    if (arg == mp_const_false) {
         *real = 0;
         *imag = 0;
-    } else if (arg == py_const_true) {
+    } else if (arg == mp_const_true) {
         *real = 1;
         *imag = 0;
-    } else if (IS_SMALL_INT(arg)) {
-        *real = FROM_SMALL_INT(arg);
+    } else if (MP_OBJ_IS_SMALL_INT(arg)) {
+        *real = MP_OBJ_SMALL_INT_VALUE(arg);
         *imag = 0;
-    } else if (IS_O(arg, O_FLOAT)) {
-        *real = ((py_obj_base_t*)arg)->u_float;
+    } else if (MP_OBJ_IS_TYPE(arg, &float_type)) {
+        *real = mp_obj_float_get(arg);
         *imag = 0;
-    } else if (IS_O(arg, O_COMPLEX)) {
-        *real = ((py_obj_base_t*)arg)->u_complex.real;
-        *imag = ((py_obj_base_t*)arg)->u_complex.imag;
+    } else if (MP_OBJ_IS_TYPE(arg, &complex_type)) {
+        mp_obj_complex_get(arg, real, imag);
     } else {
         assert(0);
         *real = 0;
@@ -422,49 +172,45 @@ void py_obj_get_complex(py_obj_t arg, py_float_t *real, py_float_t *imag) {
 }
 #endif
 
-qstr py_obj_get_qstr(py_obj_t arg) {
-    if (IS_O(arg, O_STR)) {
-        return ((py_obj_base_t*)arg)->u_str;
+qstr mp_obj_get_qstr(mp_obj_t arg) {
+    if (MP_OBJ_IS_TYPE(arg, &str_type)) {
+        return mp_obj_str_get(arg);
     } else {
         assert(0);
         return 0;
     }
 }
 
-py_obj_t *py_obj_get_array_fixed_n(py_obj_t o_in, machine_int_t n) {
-    if (IS_O(o_in, O_TUPLE) || IS_O(o_in, O_LIST)) {
-        py_obj_base_t *o = o_in;
-        if (o->u_tuple_list.len != n) {
-            nlr_jump(py_obj_new_exception_2(rt_q_IndexError, "requested length %d but object has length %d", (void*)n, (void*)o->u_tuple_list.len));
+mp_obj_t *mp_obj_get_array_fixed_n(mp_obj_t o_in, machine_int_t n) {
+    if (MP_OBJ_IS_TYPE(o_in, &tuple_type) || MP_OBJ_IS_TYPE(o_in, &list_type)) {
+        uint seq_len;
+        mp_obj_t *seq_items;
+        if (MP_OBJ_IS_TYPE(o_in, &tuple_type)) {
+            mp_obj_tuple_get(o_in, &seq_len, &seq_items);
+        } else {
+            mp_obj_list_get(o_in, &seq_len, &seq_items);
+        }
+        if (seq_len != n) {
+            nlr_jump(mp_obj_new_exception_msg_2_args(rt_q_IndexError, "requested length %d but object has length %d", (void*)n, (void*)(machine_uint_t)seq_len));
         }
-        return o->u_tuple_list.items;
+        return seq_items;
     } else {
-        nlr_jump(py_obj_new_exception_2(rt_q_TypeError, "object '%s' is not a tuple or list", py_obj_get_type_str(o_in), NULL));
+        nlr_jump(mp_obj_new_exception_msg_1_arg(rt_q_TypeError, "object '%s' is not a tuple or list", mp_obj_get_type_str(o_in)));
     }
 }
 
-void py_user_get_data(py_obj_t o, machine_uint_t *data1, machine_uint_t *data2) {
-    assert(IS_O(o, O_USER));
-    if (data1 != NULL) {
-        *data1 = ((py_obj_base_t*)o)->u_user.data1;
-    }
-    if (data2 != NULL) {
-        *data2 = ((py_obj_base_t*)o)->u_user.data2;
+uint mp_get_index(const mp_obj_type_t *type, machine_uint_t len, mp_obj_t index) {
+    // TODO False and True are considered 0 and 1 for indexing purposes
+    if (MP_OBJ_IS_SMALL_INT(index)) {
+        int i = MP_OBJ_SMALL_INT_VALUE(index);
+        if (i < 0) {
+            i += len;
+        }
+        if (i < 0 || i >= len) {
+            nlr_jump(mp_obj_new_exception_msg_1_arg(rt_q_IndexError, "%s index out of range", type->name));
+        }
+        return i;
+    } else {
+        nlr_jump(mp_obj_new_exception_msg_2_args(rt_q_TypeError, "%s indices must be integers, not %s", type->name, mp_obj_get_type_str(index)));
     }
 }
-
-void py_user_set_data(py_obj_t o, machine_uint_t data1, machine_uint_t data2) {
-    assert(IS_O(o, O_USER));
-    ((py_obj_base_t*)o)->u_user.data1 = data1;
-    ((py_obj_base_t*)o)->u_user.data2 = data2;
-}
-
-// temporary way of making C modules
-// hack: use class to mimic a module
-
-py_obj_t py_module_new(void) {
-    py_obj_base_t *o = m_new(py_obj_base_t, 1);
-    o->kind = O_CLASS;
-    o->u_class.locals = py_map_new(MAP_QSTR, 0);
-    return o;
-}
diff --git a/py/obj.h b/py/obj.h
index 746c92bbadc0b0cd1f2f20eb5f238a0cd989e943..7d58371dbb5c2f7755127653b2788f4d1110939a 100644
--- a/py/obj.h
+++ b/py/obj.h
@@ -1,56 +1,238 @@
-typedef machine_int_t py_small_int_t; // do we need to expose this in the public API?
+// All Micro Python objects are at least this type
+// It must be of pointer size
+
+typedef machine_ptr_t mp_obj_t;
+typedef machine_const_ptr_t mp_const_obj_t;
+
+// Integers that fit in a pointer have this type
+// (do we need to expose this in the public API?)
+
+typedef machine_int_t mp_small_int_t;
+
+// The machine floating-point type used for float and complex numbers
 
 #if MICROPY_ENABLE_FLOAT
-typedef machine_float_t py_float_t;
+typedef machine_float_t mp_float_t;
 #endif
 
-// user defined objects
+// Anything that wants to be a Micro Python object must
+// have mp_obj_base_t as its first member (except NULL and small ints)
+
+typedef struct _mp_obj_base_t mp_obj_base_t;
+typedef struct _mp_obj_type_t mp_obj_type_t;
+
+struct _mp_obj_base_t {
+    const mp_obj_type_t *type;
+};
+
+// The NULL object is used to indicate the absence of an object
+// It *cannot* be used when an mp_obj_t is expected, except where explicitly allowed
+
+#define MP_OBJ_NULL ((mp_obj_t)NULL)
+
+// These macros check for small int or object, and access small int values
+
+#define MP_OBJ_IS_OBJ(o) ((((mp_small_int_t)(o)) & 1) == 0)
+#define MP_OBJ_IS_SMALL_INT(o) ((((mp_small_int_t)(o)) & 1) != 0)
+#define MP_OBJ_IS_TYPE(o, t) (((((mp_small_int_t)(o)) & 1) == 0) && (((mp_obj_base_t*)(o))->type == (t)))
+#define MP_OBJ_SMALL_INT_VALUE(o) (((mp_small_int_t)(o)) >> 1)
+#define MP_OBJ_NEW_SMALL_INT(o) ((mp_obj_t)(((o) << 1) | 1))
+
+// These macros are used to declare and define constant function objects
+// You can put "static" in front of the definitions to make them local
+
+#define MP_DECLARE_CONST_FUN_OBJ(obj_name) extern const mp_obj_fun_native_t obj_name
 
-typedef struct _py_user_method_t {
+#define MP_DEFINE_CONST_FUN_OBJ_0(obj_name, fun_name) const mp_obj_fun_native_t obj_name = {{&fun_native_type}, 0, 0, fun_name}
+#define MP_DEFINE_CONST_FUN_OBJ_1(obj_name, fun_name) const mp_obj_fun_native_t obj_name = {{&fun_native_type}, 1, 1, fun_name}
+#define MP_DEFINE_CONST_FUN_OBJ_2(obj_name, fun_name) const mp_obj_fun_native_t obj_name = {{&fun_native_type}, 2, 2, fun_name}
+#define MP_DEFINE_CONST_FUN_OBJ_VAR(obj_name, n_args_min, fun_name) const mp_obj_fun_native_t obj_name = {{&fun_native_type}, n_args_min, (~((machine_uint_t)0)), fun_name}
+#define MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(obj_name, n_args_min, n_args_max, fun_name) const mp_obj_fun_native_t obj_name = {{&fun_native_type}, n_args_min, n_args_max, fun_name}
+
+// Type definitions for methods
+
+typedef mp_obj_t (*mp_fun_0_t)(void);
+typedef mp_obj_t (*mp_fun_1_t)(mp_obj_t);
+typedef mp_obj_t (*mp_fun_2_t)(mp_obj_t, mp_obj_t);
+typedef mp_obj_t (*mp_fun_t)(void);
+typedef mp_obj_t (*mp_fun_var_t)(int n, const mp_obj_t *);
+
+typedef void (*mp_print_fun_t)(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t o);
+typedef mp_obj_t (*mp_call_n_fun_t)(mp_obj_t fun, int n_args, const mp_obj_t *args); // args are in reverse order in the array
+typedef mp_obj_t (*mp_unary_op_fun_t)(int op, mp_obj_t);
+typedef mp_obj_t (*mp_binary_op_fun_t)(int op, mp_obj_t, mp_obj_t);
+
+typedef struct _mp_method_t {
     const char *name;
-    machine_uint_t kind;
-    void *fun;
-} py_user_method_t;
+    mp_const_obj_t fun;
+} mp_method_t;
+
+struct _mp_obj_type_t {
+    mp_obj_base_t base;
+    const char *name;
+    mp_print_fun_t print;
+
+    mp_call_n_fun_t call_n;
+    mp_unary_op_fun_t unary_op;     // can return NULL if op not supported
+    mp_binary_op_fun_t binary_op;   // can return NULL if op not supported
+
+    mp_fun_1_t getiter;
+    mp_fun_1_t iternext;
+
+    const mp_method_t methods[];
+
+    /*
+    What we might need to add here:
+
+    dynamic_type    instance
 
-typedef struct _py_user_info_t {
-    const char *type_name;
-    void (*print)(py_obj_t);
-    const py_user_method_t methods[];
-} py_user_info_t;
+    compare_op
+    load_attr       instance class list
+    load_method     instance str gen list user
+    store_attr      instance class
+    store_subscr    list dict
 
-py_obj_t py_obj_new_int(machine_int_t value);
-py_obj_t py_obj_new_const(const char *id);
-py_obj_t py_obj_new_str(qstr qstr);
+    len             str tuple list map
+    abs             float complex
+    hash            bool int none str
+    equal           int str
+    less            int
+    get_array_n     tuple list
+
+    unpack seq      list tuple
+    __next__        gen-instance
+    */
+};
+
+// Constant objects, globally accessible
+
+extern const mp_obj_type_t mp_const_type;
+extern const mp_obj_t mp_const_none;
+extern const mp_obj_t mp_const_false;
+extern const mp_obj_t mp_const_true;
+extern const mp_obj_t mp_const_stop_iteration; // special object indicating end of iteration (not StopIteration exception!)
+
+// Need to declare this here so we are not dependent on map.h
+
+typedef struct _mp_map_t mp_map_t;
+
+// General API for objects
+
+mp_obj_t mp_obj_new_none(void);
+mp_obj_t mp_obj_new_bool(bool value);
+mp_obj_t mp_obj_new_int(machine_int_t value);
+mp_obj_t mp_obj_new_str(qstr qstr);
 #if MICROPY_ENABLE_FLOAT
-py_obj_t py_obj_new_float(py_float_t val);
-py_obj_t py_obj_new_complex(py_float_t real, py_float_t imag);
+mp_obj_t mp_obj_new_float(mp_float_t val);
+mp_obj_t mp_obj_new_complex(mp_float_t real, mp_float_t imag);
 #endif
-py_obj_t py_obj_new_exception_0(qstr id);
-py_obj_t py_obj_new_exception_2(qstr id, const char *fmt, const char *s1, const char *s2);
-py_obj_t py_obj_new_range(int start, int stop, int step);
-py_obj_t py_obj_new_range_iterator(int cur, int stop, int step);
-py_obj_t py_obj_new_user(const py_user_info_t *info, machine_uint_t data1, machine_uint_t data2);
+mp_obj_t mp_obj_new_exception(qstr id);
+mp_obj_t mp_obj_new_exception_msg(qstr id, const char *msg);
+mp_obj_t mp_obj_new_exception_msg_1_arg(qstr id, const char *fmt, const char *a1);
+mp_obj_t mp_obj_new_exception_msg_2_args(qstr id, const char *fmt, const char *a1, const char *a2);
+mp_obj_t mp_obj_new_range(int start, int stop, int step);
+mp_obj_t mp_obj_new_range_iterator(int cur, int stop, int step);
+mp_obj_t mp_obj_new_fun_bc(int n_args, uint n_state, const byte *code);
+mp_obj_t mp_obj_new_fun_asm(uint n_args, void *fun);
+mp_obj_t mp_obj_new_gen_wrap(uint n_locals, uint n_cells, uint n_stack, mp_obj_t fun);
+mp_obj_t mp_obj_new_gen_instance(mp_obj_t state, const byte *ip, mp_obj_t *sp);
+mp_obj_t mp_obj_new_closure(mp_obj_t fun, mp_obj_t closure_tuple);
+mp_obj_t mp_obj_new_tuple(uint n, mp_obj_t *items);
+mp_obj_t mp_obj_new_tuple_reverse(uint n, mp_obj_t *items);
+mp_obj_t mp_obj_new_list(uint n, mp_obj_t *items);
+mp_obj_t mp_obj_new_list_reverse(uint n, mp_obj_t *items);
+mp_obj_t mp_obj_new_dict(int n_args);
+mp_obj_t mp_obj_new_set(int n_args, mp_obj_t *items);
+mp_obj_t mp_obj_new_bound_meth(mp_obj_t self, mp_obj_t meth);
+mp_obj_t mp_obj_new_class(mp_map_t *class_locals);
+mp_obj_t mp_obj_new_instance(mp_obj_t clas);
 
-const char *py_obj_get_type_str(py_obj_t o_in);
+const char *mp_obj_get_type_str(mp_obj_t o_in);
 
-void py_obj_print_helper(void (*print)(void *env, const char *fmt, ...), void *env, py_obj_t o_in);
-void py_obj_print(py_obj_t o);
+void mp_obj_print_helper(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t o_in);
+void mp_obj_print(mp_obj_t o);
 
-bool py_obj_is_callable(py_obj_t o_in);
-machine_int_t py_obj_hash(py_obj_t o_in);
-bool py_obj_equal(py_obj_t o1, py_obj_t o2);
-bool py_obj_less(py_obj_t o1, py_obj_t o2);
+bool mp_obj_is_callable(mp_obj_t o_in);
+machine_int_t mp_obj_hash(mp_obj_t o_in);
+bool mp_obj_equal(mp_obj_t o1, mp_obj_t o2);
+bool mp_obj_less(mp_obj_t o1, mp_obj_t o2);
 
-machine_int_t py_obj_get_int(py_obj_t arg);
+machine_int_t mp_obj_get_int(mp_obj_t arg);
 #if MICROPY_ENABLE_FLOAT
-machine_float_t py_obj_get_float(py_obj_t arg);
-void py_obj_get_complex(py_obj_t arg, py_float_t *real, py_float_t *imag);
+mp_float_t mp_obj_get_float(mp_obj_t self_in);
+void mp_obj_get_complex(mp_obj_t self_in, mp_float_t *real, mp_float_t *imag);
 #endif
-qstr py_obj_get_qstr(py_obj_t arg);
-py_obj_t *py_obj_get_array_fixed_n(py_obj_t o, machine_int_t n);
+qstr mp_obj_get_qstr(mp_obj_t arg);
+mp_obj_t *mp_obj_get_array_fixed_n(mp_obj_t o, machine_int_t n);
+uint mp_get_index(const mp_obj_type_t *type, machine_uint_t len, mp_obj_t index);
+
+// none
+extern const mp_obj_type_t none_type;
+
+// bool
+extern const mp_obj_type_t bool_type;
+
+// cell
+mp_obj_t mp_obj_cell_get(mp_obj_t self_in);
+void mp_obj_cell_set(mp_obj_t self_in, mp_obj_t obj);
+
+// str
+extern const mp_obj_type_t str_type;
+qstr mp_obj_str_get(mp_obj_t self_in);
+
+#if MICROPY_ENABLE_FLOAT
+// float
+extern const mp_obj_type_t float_type;
+mp_float_t mp_obj_float_get(mp_obj_t self_in);
+
+// complex
+extern const mp_obj_type_t complex_type;
+void mp_obj_complex_get(mp_obj_t self_in, mp_float_t *real, mp_float_t *imag);
+#endif
+
+// tuple
+extern const mp_obj_type_t tuple_type;
+void mp_obj_tuple_get(mp_obj_t self_in, uint *len, mp_obj_t **items);
+
+// list
+extern const mp_obj_type_t list_type;
+mp_obj_t mp_obj_list_append(mp_obj_t self_in, mp_obj_t arg);
+void mp_obj_list_get(mp_obj_t self_in, uint *len, mp_obj_t **items);
+void mp_obj_list_store(mp_obj_t self_in, mp_obj_t index, mp_obj_t value);
+
+// dict
+extern const mp_obj_type_t dict_type;
+mp_obj_t mp_obj_dict_store(mp_obj_t self_in, mp_obj_t key, mp_obj_t value);
+
+// set
+void mp_obj_set_store(mp_obj_t self_in, mp_obj_t item);
+
+// functions
+typedef struct _mp_obj_fun_native_t { // need this so we can define static objects
+    mp_obj_base_t base;
+    machine_uint_t n_args_min; // inclusive
+    machine_uint_t n_args_max; // inclusive
+    void *fun;
+} mp_obj_fun_native_t;
+extern const mp_obj_type_t fun_native_type;
+extern const mp_obj_type_t fun_bc_type;
+void mp_obj_fun_bc_get(mp_obj_t self_in, int *n_args, uint *n_state, const byte **code);
+
+// generator
+extern const mp_obj_type_t gen_instance_type;
+mp_obj_t mp_obj_gen_instance_next(mp_obj_t self_in);
+MP_DECLARE_CONST_FUN_OBJ(mp_obj_gen_instance_next_obj);
+
+// class
+extern const mp_obj_type_t class_type;
+extern const mp_obj_t gen_instance_next_obj;
+mp_map_t *mp_obj_class_get_locals(mp_obj_t self_in);
 
-void py_user_get_data(py_obj_t o, machine_uint_t *data1, machine_uint_t *data2);
-void py_user_set_data(py_obj_t o, machine_uint_t data1, machine_uint_t data2);
+// instance
+extern const mp_obj_type_t instance_type;
+mp_obj_t mp_obj_instance_load_attr(mp_obj_t self_in, qstr attr);
+void mp_obj_instance_load_method(mp_obj_t self_in, qstr attr, mp_obj_t *dest);
+void mp_obj_instance_store_attr(mp_obj_t self_in, qstr attr, mp_obj_t value);
 
 // temporary way of making C modules
-py_obj_t py_module_new(void);
+mp_obj_t mp_module_new(void);
diff --git a/py/objbool.c b/py/objbool.c
new file mode 100644
index 0000000000000000000000000000000000000000..9b53ffae94f012f0e349c81b06e7ad7562ae2c07
--- /dev/null
+++ b/py/objbool.c
@@ -0,0 +1,39 @@
+#include <stdlib.h>
+#include <stdint.h>
+
+#include "nlr.h"
+#include "misc.h"
+#include "mpconfig.h"
+#include "obj.h"
+
+typedef struct _mp_obj_bool_t {
+    mp_obj_base_t base;
+    bool value;
+} mp_obj_bool_t;
+
+void bool_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in) {
+    mp_obj_bool_t *self = self_in;
+    if (self->value) {
+        print(env, "True");
+    } else {
+        print(env, "False");
+    }
+}
+
+const mp_obj_type_t bool_type = {
+    { &mp_const_type },
+    "bool",
+    bool_print, // print
+    NULL, // call_n
+    NULL, // unary_op
+    NULL, // binary_op
+    NULL, // getiter
+    NULL, // iternext
+    {{NULL, NULL},}, // method list
+};
+
+static const mp_obj_bool_t false_obj = {{&bool_type}, false};
+static const mp_obj_bool_t true_obj = {{&bool_type}, true};
+
+const mp_obj_t mp_const_false = (mp_obj_t)&false_obj;
+const mp_obj_t mp_const_true = (mp_obj_t)&true_obj;
diff --git a/py/objboundmeth.c b/py/objboundmeth.c
new file mode 100644
index 0000000000000000000000000000000000000000..8bd238c65ed63f86ccb4f7267df90903dfa7c1e2
--- /dev/null
+++ b/py/objboundmeth.c
@@ -0,0 +1,54 @@
+#include <stdlib.h>
+#include <stdint.h>
+#include <assert.h>
+
+#include "nlr.h"
+#include "misc.h"
+#include "mpconfig.h"
+#include "obj.h"
+#include "runtime.h"
+
+typedef struct _mp_obj_bound_meth_t {
+    mp_obj_base_t base;
+    mp_obj_t meth;
+    mp_obj_t self;
+} mp_obj_bound_meth_t;
+
+// args are in reverse order in the array
+mp_obj_t bound_meth_call_n(mp_obj_t self_in, int n_args, const mp_obj_t *args) {
+    mp_obj_bound_meth_t *self = self_in;
+
+    if (n_args == 0) {
+        return rt_call_function_n(self->meth, 1, &self->self);
+    } else if (n_args == 1) {
+        mp_obj_t args2[2];
+        args2[1] = self->self;
+        args2[0] = args[0];
+        return rt_call_function_n(self->meth, 2, args2);
+    } else {
+        // TODO not implemented
+        assert(0);
+        return mp_const_none;
+        //return rt_call_function_2(self->meth, n_args + 1, self->self + args);
+    }
+}
+
+const mp_obj_type_t bound_meth_type = {
+    { &mp_const_type },
+    "bound_method",
+    NULL, // print
+    bound_meth_call_n, // call_n
+    NULL, // unary_op
+    NULL, // binary_op
+    NULL, // getiter
+    NULL, // iternext
+    {{NULL, NULL},}, // method list
+};
+
+mp_obj_t mp_obj_new_bound_meth(mp_obj_t self, mp_obj_t meth) {
+    mp_obj_bound_meth_t *o = m_new_obj(mp_obj_bound_meth_t);
+    o->base.type = &bound_meth_type;
+    o->meth = meth;
+    o->self = self;
+    return o;
+}
diff --git a/py/objcell.c b/py/objcell.c
new file mode 100644
index 0000000000000000000000000000000000000000..cba1980579acae2fb4aa0cc584ec9a9ff5f8def6
--- /dev/null
+++ b/py/objcell.c
@@ -0,0 +1,43 @@
+#include <stdlib.h>
+#include <stdint.h>
+#include <assert.h>
+
+#include "nlr.h"
+#include "misc.h"
+#include "mpconfig.h"
+#include "obj.h"
+#include "runtime.h"
+
+typedef struct _mp_obj_cell_t {
+    mp_obj_base_t base;
+    mp_obj_t obj;
+} mp_obj_cell_t;
+
+mp_obj_t mp_obj_cell_get(mp_obj_t self_in) {
+    mp_obj_cell_t *self = self_in;
+    return self->obj;
+}
+
+void mp_obj_cell_set(mp_obj_t self_in, mp_obj_t obj) {
+    mp_obj_cell_t *self = self_in;
+    self->obj = obj;
+}
+
+const mp_obj_type_t cell_type = {
+    { &mp_const_type },
+    "cell",
+    NULL, // print
+    NULL, // call_n
+    NULL, // unary_op
+    NULL, // binary_op
+    NULL, // getiter
+    NULL, // iternext
+    {{NULL, NULL},}, // method list
+};
+
+mp_obj_t mp_obj_new_cell(mp_obj_t obj) {
+    mp_obj_cell_t *o = m_new_obj(mp_obj_cell_t);
+    o->base.type = &cell_type;
+    o->obj = obj;
+    return o;
+}
diff --git a/py/objclass.c b/py/objclass.c
new file mode 100644
index 0000000000000000000000000000000000000000..52e43724f7cc34eda66ddeb58843309fd5bc0269
--- /dev/null
+++ b/py/objclass.c
@@ -0,0 +1,85 @@
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <assert.h>
+
+#include "nlr.h"
+#include "misc.h"
+#include "mpconfig.h"
+#include "obj.h"
+#include "runtime.h"
+#include "map.h"
+
+typedef struct _mp_obj_class_t {
+    mp_obj_base_t base;
+    mp_map_t *locals;
+} mp_obj_class_t;
+
+// args are in reverse order in the array
+mp_obj_t class_call_n(mp_obj_t self_in, int n_args, const mp_obj_t *args) {
+    // instantiate an instance of a class
+
+    mp_obj_class_t *self = self_in;
+
+    // make instance
+    mp_obj_t o = mp_obj_new_instance(self_in);
+
+    // look for __init__ function
+    mp_map_elem_t *init_fn = mp_qstr_map_lookup(self->locals, qstr_from_str_static("__init__"), false);
+
+    if (init_fn != NULL) {
+        // call __init__ function
+        mp_obj_t init_ret;
+        if (n_args == 0) {
+            init_ret = rt_call_function_n(init_fn->value, 1, (mp_obj_t*)&o);
+        } else {
+            mp_obj_t *args2 = m_new(mp_obj_t, n_args + 1);
+            memcpy(args2, args, n_args * sizeof(mp_obj_t));
+            args2[n_args] = o;
+            init_ret = rt_call_function_n(init_fn->value, n_args + 1, args2);
+            m_free(args2);
+        }
+        if (init_ret != mp_const_none) {
+            nlr_jump(mp_obj_new_exception_msg_1_arg(rt_q_TypeError, "__init__() should return None, not '%s'", mp_obj_get_type_str(init_ret)));
+        }
+
+    } else {
+        // TODO
+        if (n_args != 0) {
+            nlr_jump(mp_obj_new_exception_msg_1_arg(rt_q_TypeError, "function takes 0 positional arguments but %d were given", (void*)(machine_int_t)n_args));
+        }
+    }
+
+    return o;
+}
+
+mp_map_t *mp_obj_class_get_locals(mp_obj_t self_in) {
+    assert(MP_OBJ_IS_TYPE(self_in, &class_type));
+    mp_obj_class_t *self = self_in;
+    return self->locals;
+}
+
+const mp_obj_type_t class_type = {
+    { &mp_const_type },
+    "class",
+    NULL, // print
+    class_call_n, // call_n
+    NULL, // unary_op
+    NULL, // binary_op
+    NULL, // getiter
+    NULL, // iternext
+    {{NULL, NULL},}, // method list
+};
+
+mp_obj_t mp_obj_new_class(mp_map_t *class_locals) {
+    mp_obj_class_t *o = m_new_obj(mp_obj_class_t);
+    o->base.type = &class_type;
+    o->locals = class_locals;
+    return o;
+}
+
+// temporary way of making C modules
+// hack: use class to mimic a module
+mp_obj_t mp_module_new(void) {
+    return mp_obj_new_class(mp_map_new(MP_MAP_QSTR, 0));
+}
diff --git a/py/objclosure.c b/py/objclosure.c
new file mode 100644
index 0000000000000000000000000000000000000000..e699c5daaa4d0e874560a4cd2fada6044b9d5c3c
--- /dev/null
+++ b/py/objclosure.c
@@ -0,0 +1,35 @@
+#include <stdlib.h>
+#include <stdint.h>
+#include <assert.h>
+
+#include "nlr.h"
+#include "misc.h"
+#include "mpconfig.h"
+#include "obj.h"
+#include "runtime.h"
+
+typedef struct _mp_obj_closure_t {
+    mp_obj_base_t base;
+    mp_obj_t fun;
+    mp_obj_t vars;
+} mp_obj_closure_t;
+
+const mp_obj_type_t closure_type = {
+    { &mp_const_type },
+    "closure",
+    NULL, // print
+    NULL, // call_n
+    NULL, // unary_op
+    NULL, // binary_op
+    NULL, // getiter
+    NULL, // iternext
+    {{NULL, NULL},}, // method list
+};
+
+mp_obj_t mp_obj_new_closure(mp_obj_t fun, mp_obj_t closure_tuple) {
+    mp_obj_closure_t *o = m_new_obj(mp_obj_closure_t);
+    o->base.type = &closure_type;
+    o->fun = fun;
+    o->vars = closure_tuple;
+    return o;
+}
diff --git a/py/objcomplex.c b/py/objcomplex.c
new file mode 100644
index 0000000000000000000000000000000000000000..ab9c146774493df2bad97a91db94ce370cfa0331
--- /dev/null
+++ b/py/objcomplex.c
@@ -0,0 +1,105 @@
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <assert.h>
+
+#include "nlr.h"
+#include "misc.h"
+#include "mpconfig.h"
+#include "obj.h"
+#include "runtime0.h"
+#include "map.h"
+
+#if MICROPY_ENABLE_FLOAT
+
+typedef struct _mp_obj_complex_t {
+    mp_obj_base_t base;
+    mp_float_t real;
+    mp_float_t imag;
+} mp_obj_complex_t;
+
+mp_obj_t mp_obj_new_complex(mp_float_t real, mp_float_t imag);
+
+void complex_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t o_in) {
+    mp_obj_complex_t *o = o_in;
+    if (o->real == 0) {
+        print(env, "%.8gj", o->imag);
+    } else {
+        print(env, "(%.8g+%.8gj)", o->real, o->imag);
+    }
+}
+
+mp_obj_t complex_unary_op(int op, mp_obj_t o_in) {
+    mp_obj_complex_t *o = o_in;
+    switch (op) {
+        case RT_UNARY_OP_NOT: if (o->real != 0 || o->imag != 0) { return mp_const_true;} else { return mp_const_false; }
+        case RT_UNARY_OP_POSITIVE: return o_in;
+        case RT_UNARY_OP_NEGATIVE: return mp_obj_new_complex(-o->real, -o->imag);
+        default: return MP_OBJ_NULL; // op not supported
+    }
+}
+
+mp_obj_t complex_binary_op(int op, mp_obj_t lhs_in, mp_obj_t rhs_in) {
+    mp_float_t lhs_real, lhs_imag, rhs_real, rhs_imag;
+    mp_obj_complex_get(lhs_in, &lhs_real, &lhs_imag);
+    mp_obj_complex_get(rhs_in, &rhs_real, &rhs_imag);
+    switch (op) {
+        case RT_BINARY_OP_ADD:
+        case RT_BINARY_OP_INPLACE_ADD:
+            lhs_real += rhs_real;
+            lhs_imag += rhs_imag;
+            break;
+        case RT_BINARY_OP_SUBTRACT:
+        case RT_BINARY_OP_INPLACE_SUBTRACT:
+            lhs_real -= rhs_real;
+            lhs_imag -= rhs_imag;
+            break;
+        case RT_BINARY_OP_MULTIPLY:
+        case RT_BINARY_OP_INPLACE_MULTIPLY:
+        {
+            mp_float_t real = lhs_real * rhs_real - lhs_imag * rhs_imag;
+            lhs_imag = lhs_real * rhs_imag + lhs_imag * rhs_real;
+            lhs_real = real;
+            break;
+        }
+        /* TODO floor(?) the value
+        case RT_BINARY_OP_FLOOR_DIVIDE:
+        case RT_BINARY_OP_INPLACE_FLOOR_DIVIDE: val = lhs_val / rhs_val; break;
+        */
+        /* TODO
+        case RT_BINARY_OP_TRUE_DIVIDE:
+        case RT_BINARY_OP_INPLACE_TRUE_DIVIDE: val = lhs_val / rhs_val; break;
+        */
+        return NULL; // op not supported
+    }
+    return mp_obj_new_complex(lhs_real, lhs_imag);
+}
+
+const mp_obj_type_t complex_type = {
+    { &mp_const_type },
+    "complex",
+    complex_print, // print
+    NULL, // call_n
+    complex_unary_op, // unary_op
+    complex_binary_op, // binary_op
+    NULL, // getiter
+    NULL, // iternext
+    { { NULL, NULL }, }, // method list
+};
+
+mp_obj_t mp_obj_new_complex(mp_float_t real, mp_float_t imag) {
+    mp_obj_complex_t *o = m_new_obj(mp_obj_complex_t);
+    o->base.type = &complex_type;
+    o->real = real;
+    o->imag = imag;
+    return o;
+}
+
+void mp_obj_complex_get(mp_obj_t self_in, mp_float_t *real, mp_float_t *imag) {
+    assert(MP_OBJ_IS_TYPE(self_in, &complex_type));
+    mp_obj_complex_t *self = self_in;
+    *real = self->real;
+    *imag = self->imag;
+}
+
+#endif
diff --git a/py/objdict.c b/py/objdict.c
new file mode 100644
index 0000000000000000000000000000000000000000..02753e3817087f82dcdbd0192e41f18bd5e5a5a1
--- /dev/null
+++ b/py/objdict.c
@@ -0,0 +1,80 @@
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <assert.h>
+
+#include "nlr.h"
+#include "misc.h"
+#include "mpconfig.h"
+#include "obj.h"
+#include "runtime0.h"
+#include "runtime.h"
+#include "map.h"
+
+typedef struct _mp_obj_dict_t {
+    mp_obj_base_t base;
+    mp_map_t map;
+} mp_obj_dict_t;
+
+void dict_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in) {
+    mp_obj_dict_t *self = self_in;
+    bool first = true;
+    print(env, "{");
+    for (int i = 0; i < self->map.alloc; i++) {
+        if (self->map.table[i].key != NULL) {
+            if (!first) {
+                print(env, ", ");
+            }
+            first = false;
+            mp_obj_print_helper(print, env, self->map.table[i].key);
+            print(env, ": ");
+            mp_obj_print_helper(print, env, self->map.table[i].value);
+        }
+    }
+    print(env, "}");
+}
+
+mp_obj_t dict_binary_op(int op, mp_obj_t lhs_in, mp_obj_t rhs_in) {
+    mp_obj_dict_t *o = lhs_in;
+    switch (op) {
+        case RT_BINARY_OP_SUBSCR:
+        {
+            // dict load
+            mp_map_elem_t *elem = mp_map_lookup_helper(&o->map, rhs_in, false);
+            if (elem == NULL) {
+                nlr_jump(mp_obj_new_exception_msg(rt_q_KeyError, "<value>"));
+            } else {
+                return elem->value;
+            }
+        }
+        default:
+            // op not supported
+            return NULL;
+    }
+}
+
+mp_obj_t mp_obj_dict_store(mp_obj_t self_in, mp_obj_t key, mp_obj_t value) {
+    assert(MP_OBJ_IS_TYPE(self_in, &dict_type));
+    mp_obj_dict_t *self = self_in;
+    mp_map_lookup_helper(&self->map, key, true)->value = value;
+    return self_in;
+}
+
+const mp_obj_type_t dict_type = {
+    { &mp_const_type },
+    "dict",
+    dict_print, // print
+    NULL, // call_n
+    NULL, // unary_op
+    dict_binary_op, // binary_op
+    NULL, // getiter
+    NULL, // iternext
+    {{NULL, NULL},}, // method list
+};
+
+mp_obj_t mp_obj_new_dict(int n_args) {
+    mp_obj_dict_t *o = m_new_obj(mp_obj_dict_t);
+    o->base.type = &dict_type;
+    mp_map_init(&o->map, MP_MAP_OBJ, n_args);
+    return o;
+}
diff --git a/py/objexcept.c b/py/objexcept.c
new file mode 100644
index 0000000000000000000000000000000000000000..4abc6243760bf53cdeb08f6e0e24a22882ee3964
--- /dev/null
+++ b/py/objexcept.c
@@ -0,0 +1,86 @@
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <assert.h>
+
+#include "nlr.h"
+#include "misc.h"
+#include "mpconfig.h"
+#include "obj.h"
+
+typedef struct mp_obj_exception_t {
+    mp_obj_base_t base;
+    qstr id;
+    int n_args;
+    const void *args[];
+} mp_obj_exception_t;
+
+void exception_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t o_in) {
+    mp_obj_exception_t *o = o_in;
+    switch (o->n_args) {
+        case 0:
+            print(env, "%s", qstr_str(o->id));
+            break;
+        case 1:
+            print(env, "%s: %s", qstr_str(o->id), (const char*)o->args[0]);
+            break;
+        case 2:
+            print(env, "%s: ", qstr_str(o->id));
+            print(env, (const char*)o->args[0], o->args[1]);
+            break;
+        default: // here we just assume at least 3 args, but only use first 3
+            print(env, "%s: ", qstr_str(o->id));
+            print(env, (const char*)o->args[0], o->args[1], o->args[2]);
+            break;
+    }
+}
+
+const mp_obj_type_t exception_type = {
+    { &mp_const_type },
+    "exception",
+    exception_print, // print
+    NULL, // call_n
+    NULL, // unary_op
+    NULL, // binary_op
+    NULL, // getiter
+    NULL, // iternext
+    {{NULL, NULL},}, // method list
+};
+
+mp_obj_t mp_obj_new_exception(qstr id) {
+    mp_obj_exception_t *o = m_new_obj(mp_obj_exception_t);
+    o->base.type = &exception_type;
+    o->id = id;
+    o->n_args = 0;
+    return o;
+}
+
+mp_obj_t mp_obj_new_exception_msg(qstr id, const char *msg) {
+    mp_obj_exception_t *o = m_new_obj_var(mp_obj_exception_t, void*, 1);
+    o->base.type = &exception_type;
+    o->id = id;
+    o->n_args = 1;
+    o->args[0] = msg;
+    return o;
+}
+
+mp_obj_t mp_obj_new_exception_msg_1_arg(qstr id, const char *fmt, const char *a1) {
+    mp_obj_exception_t *o = m_new_obj_var(mp_obj_exception_t, void*, 2);
+    o->base.type = &exception_type;
+    o->id = id;
+    o->n_args = 2;
+    o->args[0] = fmt;
+    o->args[1] = a1;
+    return o;
+}
+
+mp_obj_t mp_obj_new_exception_msg_2_args(qstr id, const char *fmt, const char *a1, const char *a2) {
+    mp_obj_exception_t *o = m_new_obj_var(mp_obj_exception_t, void*, 3);
+    o->base.type = &exception_type;
+    o->id = id;
+    o->n_args = 3;
+    o->args[0] = fmt;
+    o->args[1] = a1;
+    o->args[2] = a2;
+    return o;
+}
diff --git a/py/objfloat.c b/py/objfloat.c
new file mode 100644
index 0000000000000000000000000000000000000000..f151fe25a0aceed5d50a05b1900be6db20ee6923
--- /dev/null
+++ b/py/objfloat.c
@@ -0,0 +1,85 @@
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <assert.h>
+
+#include "nlr.h"
+#include "misc.h"
+#include "mpconfig.h"
+#include "obj.h"
+#include "runtime0.h"
+
+#if MICROPY_ENABLE_FLOAT
+
+typedef struct _mp_obj_float_t {
+    mp_obj_base_t base;
+    mp_float_t value;
+} mp_obj_float_t;
+
+mp_obj_t mp_obj_new_float(mp_float_t value);
+
+void float_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t o_in) {
+    mp_obj_float_t *o = o_in;
+    print(env, "%.8g", o->value);
+}
+
+mp_obj_t float_unary_op(int op, mp_obj_t o_in) {
+    mp_obj_float_t *o = o_in;
+    switch (op) {
+        case RT_UNARY_OP_NOT: if (o->value != 0) { return mp_const_true;} else { return mp_const_false; }
+        case RT_UNARY_OP_POSITIVE: return o_in;
+        case RT_UNARY_OP_NEGATIVE: return mp_obj_new_float(-o->value);
+        default: return NULL; // op not supported
+    }
+}
+
+mp_obj_t float_binary_op(int op, mp_obj_t lhs_in, mp_obj_t rhs_in) {
+    if (MP_OBJ_IS_TYPE(rhs_in, &complex_type)) {
+        return complex_type.binary_op(op, lhs_in, rhs_in);
+    }
+    mp_float_t lhs_val = mp_obj_get_float(lhs_in);
+    mp_float_t rhs_val = mp_obj_get_float(rhs_in);
+    switch (op) {
+        case RT_BINARY_OP_ADD:
+        case RT_BINARY_OP_INPLACE_ADD: lhs_val += rhs_val; break;
+        case RT_BINARY_OP_SUBTRACT:
+        case RT_BINARY_OP_INPLACE_SUBTRACT: lhs_val -= rhs_val; break;
+        case RT_BINARY_OP_MULTIPLY:
+        case RT_BINARY_OP_INPLACE_MULTIPLY: lhs_val *= rhs_val; break;
+        /* TODO floor(?) the value
+        case RT_BINARY_OP_FLOOR_DIVIDE:
+        case RT_BINARY_OP_INPLACE_FLOOR_DIVIDE: val = lhs_val / rhs_val; break;
+        */
+        case RT_BINARY_OP_TRUE_DIVIDE:
+        case RT_BINARY_OP_INPLACE_TRUE_DIVIDE: lhs_val /= rhs_val; break;
+        return NULL; // op not supported
+    }
+    return mp_obj_new_float(lhs_val);
+}
+
+const mp_obj_type_t float_type = {
+    { &mp_const_type },
+    "float",
+    float_print,
+    NULL, // call_n
+    float_unary_op,
+    float_binary_op,
+    NULL, // getiter
+    NULL, // iternext
+    { { NULL, NULL }, }, // method list
+};
+
+mp_obj_t mp_obj_new_float(mp_float_t value) {
+    mp_obj_float_t *o = m_new(mp_obj_float_t, 1);
+    o->base.type = &float_type;
+    o->value = value;
+    return (mp_obj_t)o;
+}
+
+mp_float_t mp_obj_float_get(mp_obj_t self_in) {
+    assert(MP_OBJ_IS_TYPE(self_in, &float_type));
+    mp_obj_float_t *self = self_in;
+    return self->value;
+}
+
+#endif
diff --git a/py/objfun.c b/py/objfun.c
new file mode 100644
index 0000000000000000000000000000000000000000..bf11b5e6ab71e696aa9eb9969c63d452fe62c10b
--- /dev/null
+++ b/py/objfun.c
@@ -0,0 +1,280 @@
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <assert.h>
+
+#include "nlr.h"
+#include "misc.h"
+#include "mpconfig.h"
+#include "obj.h"
+#include "runtime.h"
+#include "bc.h"
+
+/******************************************************************************/
+/* native functions                                                           */
+
+// mp_obj_fun_native_t defined in obj.h
+
+// args are in reverse order in the array
+mp_obj_t fun_native_call_n(mp_obj_t self_in, int n_args, const mp_obj_t *args) {
+    mp_obj_fun_native_t *self = self_in;
+    if (self->n_args_min == self->n_args_max) {
+        // function requires a fixed number of arguments
+
+        // check number of arguments
+        if (n_args != self->n_args_min) {
+            nlr_jump(mp_obj_new_exception_msg_2_args(rt_q_TypeError, "function takes %d positional arguments but %d were given", (const char*)(machine_int_t)self->n_args_min, (const char*)(machine_int_t)n_args));
+        }
+
+        // dispatch function call
+        switch (self->n_args_min) {
+            case 0:
+                return ((mp_fun_0_t)self->fun)();
+
+            case 1:
+                return ((mp_fun_1_t)self->fun)(args[0]);
+
+            case 2:
+                return ((mp_fun_2_t)self->fun)(args[1], args[0]);
+
+            default:
+                assert(0);
+                return mp_const_none;
+        }
+
+    } else {
+        // function takes a variable number of arguments
+
+        if (n_args < self->n_args_min) {
+            nlr_jump(mp_obj_new_exception_msg_1_arg(rt_q_TypeError, "<fun name>() missing %d required positional arguments: <list of names of params>", (const char*)(machine_int_t)(self->n_args_min - n_args)));
+        } else if (n_args > self->n_args_max) {
+            nlr_jump(mp_obj_new_exception_msg_2_args(rt_q_TypeError, "<fun name> expected at most %d arguments, got %d", (void*)(machine_int_t)self->n_args_max, (void*)(machine_int_t)n_args));
+        }
+
+        // TODO really the args need to be passed in as a Python tuple, as the form f(*[1,2]) can be used to pass var args
+        mp_obj_t *args_ordered = m_new(mp_obj_t, n_args);
+        for (int i = 0; i < n_args; i++) {
+            args_ordered[i] = args[n_args - i - 1];
+        }
+
+        mp_obj_t res = ((mp_fun_var_t)self->fun)(n_args, args_ordered);
+        m_free(args_ordered);
+
+        return res;
+    }
+}
+
+const mp_obj_type_t fun_native_type = {
+    { &mp_const_type },
+    "function",
+    NULL, // print
+    fun_native_call_n, // call_n
+    NULL, // unary_op
+    NULL, // binary_op
+    NULL, // getiter
+    NULL, // iternext
+    { // method list
+        {NULL, NULL}, // end-of-list sentinel
+    },
+};
+
+mp_obj_t rt_make_function_0(mp_fun_0_t fun) {
+    mp_obj_fun_native_t *o = m_new_obj(mp_obj_fun_native_t);
+    o->base.type = &fun_native_type;
+    o->n_args_min = 0;
+    o->n_args_max = 0;
+    o->fun = fun;
+    return o;
+}
+
+mp_obj_t rt_make_function_1(mp_fun_1_t fun) {
+    mp_obj_fun_native_t *o = m_new_obj(mp_obj_fun_native_t);
+    o->base.type = &fun_native_type;
+    o->n_args_min = 1;
+    o->n_args_max = 1;
+    o->fun = fun;
+    return o;
+}
+
+mp_obj_t rt_make_function_2(mp_fun_2_t fun) {
+    mp_obj_fun_native_t *o = m_new_obj(mp_obj_fun_native_t);
+    o->base.type = &fun_native_type;
+    o->n_args_min = 2;
+    o->n_args_max = 2;
+    o->fun = fun;
+    return o;
+}
+
+mp_obj_t rt_make_function_var(int n_args_min, mp_fun_var_t fun) {
+    mp_obj_fun_native_t *o = m_new_obj(mp_obj_fun_native_t);
+    o->base.type = &fun_native_type;
+    o->n_args_min = n_args_min;
+    o->n_args_max = ~((machine_uint_t)0);
+    o->fun = fun;
+    return o;
+}
+
+// min and max are inclusive
+mp_obj_t rt_make_function_var_between(int n_args_min, int n_args_max, mp_fun_var_t fun) {
+    mp_obj_fun_native_t *o = m_new_obj(mp_obj_fun_native_t);
+    o->base.type = &fun_native_type;
+    o->n_args_min = n_args_min;
+    o->n_args_max = n_args_max;
+    o->fun = fun;
+    return o;
+}
+
+/******************************************************************************/
+/* byte code functions                                                        */
+
+typedef struct _mp_obj_fun_bc_t {
+    mp_obj_base_t base;
+    int n_args;
+    uint n_state;
+    const byte *code;
+} mp_obj_fun_bc_t;
+
+// args are in reverse order in the array
+mp_obj_t fun_bc_call_n(mp_obj_t self_in, int n_args, const mp_obj_t *args) {
+    mp_obj_fun_bc_t *self = self_in;
+
+    if (n_args != self->n_args) {
+        nlr_jump(mp_obj_new_exception_msg_2_args(rt_q_TypeError, "function takes %d positional arguments but %d were given", (const char*)(machine_int_t)self->n_args, (const char*)(machine_int_t)n_args));
+    }
+
+    return mp_execute_byte_code(self->code, args, n_args, self->n_state);
+}
+
+void mp_obj_fun_bc_get(mp_obj_t self_in, int *n_args, uint *n_state, const byte **code) {
+    assert(MP_OBJ_IS_TYPE(self_in, &fun_bc_type));
+    mp_obj_fun_bc_t *self = self_in;
+    *n_args = self->n_args;
+    *n_state = self->n_state;
+    *code = self->code;
+}
+
+const mp_obj_type_t fun_bc_type = {
+    { &mp_const_type },
+    "function",
+    NULL, // print
+    fun_bc_call_n, // call_n
+    NULL, // unary_op
+    NULL, // binary_op
+    NULL, // getiter
+    NULL, // iternext
+    { // method list
+        {NULL, NULL}, // end-of-list sentinel
+    },
+};
+
+mp_obj_t mp_obj_new_fun_bc(int n_args, uint n_state, const byte *code) {
+    mp_obj_fun_bc_t *o = m_new_obj(mp_obj_fun_bc_t);
+    o->base.type = &fun_bc_type;
+    o->n_args = n_args;
+    o->n_state = n_state;
+    o->code = code;
+    return o;
+}
+
+/******************************************************************************/
+/* inline assembler functions                                                 */
+
+typedef struct _mp_obj_fun_asm_t {
+    mp_obj_base_t base;
+    int n_args;
+    void *fun;
+} mp_obj_fun_asm_t;
+
+typedef machine_uint_t (*inline_asm_fun_0_t)();
+typedef machine_uint_t (*inline_asm_fun_1_t)(machine_uint_t);
+typedef machine_uint_t (*inline_asm_fun_2_t)(machine_uint_t, machine_uint_t);
+typedef machine_uint_t (*inline_asm_fun_3_t)(machine_uint_t, machine_uint_t, machine_uint_t);
+
+// convert a Micro Python object to a sensible value for inline asm
+machine_uint_t convert_obj_for_inline_asm(mp_obj_t obj) {
+    // TODO for byte_array, pass pointer to the array
+    if (MP_OBJ_IS_SMALL_INT(obj)) {
+        return MP_OBJ_SMALL_INT_VALUE(obj);
+    } else if (obj == mp_const_none) {
+        return 0;
+    } else if (obj == mp_const_false) {
+        return 0;
+    } else if (obj == mp_const_true) {
+        return 1;
+    } else if (MP_OBJ_IS_TYPE(obj, &str_type)) {
+        // pointer to the string (it's probably constant though!)
+        return (machine_uint_t)qstr_str(mp_obj_str_get(obj));
+#if MICROPY_ENABLE_FLOAT
+    } else if (MP_OBJ_IS_TYPE(obj, &float_type)) {
+        // convert float to int (could also pass in float registers)
+        return (machine_int_t)mp_obj_float_get(obj);
+#endif
+    } else if (MP_OBJ_IS_TYPE(obj, &tuple_type)) {
+        // pointer to start of tuple (could pass length, but then could use len(x) for that)
+        uint len;
+        mp_obj_t *items;
+        mp_obj_tuple_get(obj, &len, &items);
+        return (machine_uint_t)items;
+    } else if (MP_OBJ_IS_TYPE(obj, &list_type)) {
+        // pointer to start of list (could pass length, but then could use len(x) for that)
+        uint len;
+        mp_obj_t *items;
+        mp_obj_list_get(obj, &len, &items);
+        return (machine_uint_t)items;
+    } else {
+        // just pass along a pointer to the object
+        return (machine_uint_t)obj;
+    }
+}
+
+// convert a return value from inline asm to a sensible Micro Python object
+mp_obj_t convert_val_from_inline_asm(machine_uint_t val) {
+    return MP_OBJ_NEW_SMALL_INT(val);
+}
+
+// args are in reverse order in the array
+mp_obj_t fun_asm_call_n(mp_obj_t self_in, int n_args, const mp_obj_t *args) {
+    mp_obj_fun_asm_t *self = self_in;
+
+    if (n_args != self->n_args) {
+        nlr_jump(mp_obj_new_exception_msg_2_args(rt_q_TypeError, "function takes %d positional arguments but %d were given", (const char*)(machine_int_t)self->n_args, (const char*)(machine_int_t)n_args));
+    }
+
+    machine_uint_t ret;
+    if (n_args == 0) {
+        ret = ((inline_asm_fun_0_t)self->fun)();
+    } else if (n_args == 1) {
+        ret = ((inline_asm_fun_1_t)self->fun)(convert_obj_for_inline_asm(args[0]));
+    } else if (n_args == 2) {
+        ret = ((inline_asm_fun_2_t)self->fun)(convert_obj_for_inline_asm(args[1]), convert_obj_for_inline_asm(args[0]));
+    } else if (n_args == 3) {
+        ret = ((inline_asm_fun_3_t)self->fun)(convert_obj_for_inline_asm(args[2]), convert_obj_for_inline_asm(args[1]), convert_obj_for_inline_asm(args[0]));
+    } else {
+        assert(0);
+        ret = 0;
+    }
+
+    return convert_val_from_inline_asm(ret);
+}
+
+static const mp_obj_type_t fun_asm_type = {
+    { &mp_const_type },
+    "function",
+    NULL, // print
+    fun_asm_call_n, // call_n
+    NULL, // unary_op
+    NULL, // binary_op
+    NULL, // getiter
+    NULL, // iternext
+    { // method list
+        {NULL, NULL}, // end-of-list sentinel
+    },
+};
+
+mp_obj_t mp_obj_new_fun_asm(uint n_args, void *fun) {
+    mp_obj_fun_asm_t *o = m_new_obj(mp_obj_fun_asm_t);
+    o->base.type = &fun_asm_type;
+    o->n_args = n_args;
+    o->fun = fun;
+    return o;
+}
diff --git a/py/objgenerator.c b/py/objgenerator.c
new file mode 100644
index 0000000000000000000000000000000000000000..fdb642c1c49c872c87d5e5ea2442b3d6f043c39a
--- /dev/null
+++ b/py/objgenerator.c
@@ -0,0 +1,130 @@
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <assert.h>
+
+#include "nlr.h"
+#include "misc.h"
+#include "mpconfig.h"
+#include "obj.h"
+#include "runtime.h"
+#include "bc.h"
+
+/******************************************************************************/
+/* generator wrapper                                                          */
+
+typedef struct _mp_obj_gen_wrap_t {
+    mp_obj_base_t base;
+    int n_state;
+    mp_obj_t *fun;
+} mp_obj_gen_wrap_t;
+
+// args are in reverse order in the array
+mp_obj_t gen_wrap_call_n(mp_obj_t self_in, int n_args, const mp_obj_t *args) {
+    mp_obj_gen_wrap_t *self = self_in;
+    mp_obj_t self_fun = self->fun;
+    assert(MP_OBJ_IS_TYPE(self_fun, &fun_bc_type));
+    int bc_n_args;
+    uint bc_n_state;
+    const byte *bc_code;
+    mp_obj_fun_bc_get(self_fun, &bc_n_args, &bc_n_state, &bc_code);
+    if (n_args != bc_n_args) {
+        nlr_jump(mp_obj_new_exception_msg_2_args(rt_q_TypeError, "function takes %d positional arguments but %d were given", (const char*)(machine_int_t)bc_n_args, (const char*)(machine_int_t)n_args));
+    }
+    mp_obj_t *state = m_new(mp_obj_t, 1 + self->n_state);
+    // put function object at first slot in state (to keep u_gen_instance small)
+    state[0] = self_fun;
+    // init args
+    for (int i = 0; i < n_args; i++) {
+        state[1 + i] = args[n_args - 1 - i];
+    }
+    return mp_obj_new_gen_instance(state, bc_code, state + self->n_state);
+}
+
+const mp_obj_type_t gen_wrap_type = {
+    { &mp_const_type },
+    "generator",
+    NULL, // print
+    gen_wrap_call_n, // call_n
+    NULL, // unary_op
+    NULL, // binary_op
+    NULL, // getiter
+    NULL, // iternext
+    {{NULL, NULL},}, // method list
+};
+
+mp_obj_t mp_obj_new_gen_wrap(uint n_locals, uint n_cells, uint n_stack, mp_obj_t fun) {
+    mp_obj_gen_wrap_t *o = m_new_obj(mp_obj_gen_wrap_t);
+    o->base.type = &gen_wrap_type;
+    // we have at least 3 locals so the bc can write back fast[0,1,2] safely; should improve how this is done
+    o->n_state = ((n_locals + n_cells) < 3 ? 3 : (n_locals + n_cells)) + n_stack;
+    o->fun = fun;
+    return o;
+}
+
+/******************************************************************************/
+/* generator instance                                                         */
+
+typedef struct _mp_obj_gen_instance_t {
+    mp_obj_base_t base;
+    mp_obj_t *state;
+    const byte *ip;
+    mp_obj_t *sp;
+} mp_obj_gen_instance_t;
+
+void gen_instance_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in) {
+    print(env, "<generator object 'fun-name' at %p>", self_in);
+}
+
+mp_obj_t gen_instance_getiter(mp_obj_t self_in) {
+    return self_in;
+}
+
+mp_obj_t gen_instance_iternext(mp_obj_t self_in) {
+    mp_obj_gen_instance_t *self = self_in;
+    //mp_obj_base_t *fun = self->u_gen_instance.state[0];
+    //assert(fun->kind == O_FUN_BC);
+    bool yield = mp_execute_byte_code_2(&self->ip, &self->state[1], &self->sp);
+    if (yield) {
+        return *self->sp;
+    } else {
+        if (*self->sp == mp_const_none) {
+            return mp_const_stop_iteration;
+        } else {
+            // TODO return StopIteration with value *self->sp
+            return mp_const_stop_iteration;
+        }
+    }
+}
+
+const mp_obj_type_t gen_instance_type = {
+    { &mp_const_type },
+    "generator",
+    gen_instance_print, // print
+    NULL, // call_n
+    NULL, // unary_op
+    NULL, // binary_op
+    gen_instance_getiter, // getiter
+    gen_instance_iternext, // iternext
+    {{NULL, NULL},}, // method list
+};
+
+mp_obj_t mp_obj_new_gen_instance(mp_obj_t state, const byte *ip, mp_obj_t *sp) {
+    mp_obj_gen_instance_t *o = m_new_obj(mp_obj_gen_instance_t);
+    o->base.type = &gen_instance_type;
+    o->state = state;
+    o->ip = ip;
+    o->sp = sp;
+    return o;
+}
+
+mp_obj_t mp_obj_gen_instance_next(mp_obj_t self_in) {
+    mp_obj_t ret = rt_iternext(self_in);
+    if (ret == mp_const_stop_iteration) {
+        nlr_jump(mp_obj_new_exception(qstr_from_str_static("StopIteration")));
+    } else {
+        return ret;
+    }
+}
+
+MP_DEFINE_CONST_FUN_OBJ_1(mp_obj_gen_instance_next_obj, mp_obj_gen_instance_next);
diff --git a/py/objinstance.c b/py/objinstance.c
new file mode 100644
index 0000000000000000000000000000000000000000..e5d23af2d50b25cd75be5f1b3fd2a78c5a214240
--- /dev/null
+++ b/py/objinstance.c
@@ -0,0 +1,109 @@
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <assert.h>
+
+#include "nlr.h"
+#include "misc.h"
+#include "mpconfig.h"
+#include "obj.h"
+#include "runtime.h"
+#include "map.h"
+
+typedef struct _mp_obj_instance_t {
+    mp_obj_base_t base;
+    mp_obj_base_t *class; // points to a "class" object
+    mp_map_t *members;
+} mp_obj_instance_t;
+
+/*
+type needs to be specified dynamically
+            case O_OBJ:
+            {
+                py_map_elem_t *qn = py_qstr_map_lookup(o->u_obj.class->u_class.locals, qstr_from_str_static("__qualname__"), false); assert(qn != NULL);
+                assert(IS_O(qn->value, O_STR));
+                return qstr_str(((py_obj_base_t*)qn->value)->u_str);
+            }
+            */
+
+mp_obj_t mp_obj_instance_load_attr(mp_obj_t self_in, qstr attr) {
+    // logic: look in obj members then class locals (TODO check this against CPython)
+    mp_obj_instance_t *self = self_in;
+    mp_map_elem_t *elem = mp_qstr_map_lookup(self->members, attr, false);
+    if (elem != NULL) {
+        // object member, always treated as a value
+        return elem->value;
+    }
+    elem = mp_qstr_map_lookup(mp_obj_class_get_locals(self->class), attr, false);
+    if (elem != NULL) {
+        if (mp_obj_is_callable(elem->value)) {
+            // class member is callable so build a bound method
+            return mp_obj_new_bound_meth(self_in, elem->value);
+        } else {
+            // class member is a value, so just return that value
+            return elem->value;
+        }
+    }
+    nlr_jump(mp_obj_new_exception_msg_2_args(rt_q_AttributeError, "'%s' object has no attribute '%s'", mp_obj_get_type_str(self_in), qstr_str(attr)));
+}
+
+void mp_obj_instance_load_method(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
+    // logic: look in obj members then class locals (TODO check this against CPython)
+    mp_obj_instance_t *self = self_in;
+    mp_map_elem_t *elem = mp_qstr_map_lookup(self->members, attr, false);
+    if (elem != NULL) {
+        // object member, always treated as a value
+        dest[1] = elem->value;
+        dest[0] = NULL;
+        return;
+    }
+    elem = mp_qstr_map_lookup(mp_obj_class_get_locals(self->class), attr, false);
+    if (elem != NULL) {
+        if (mp_obj_is_callable(elem->value)) {
+            // class member is callable so build a bound method
+            dest[1] = elem->value;
+            dest[0] = self_in;
+            return;
+        } else {
+            // class member is a value, so just return that value
+            dest[1] = elem->value;
+            dest[0] = NULL;
+            return;
+        }
+    }
+
+    // no such method, so fall back to load attr
+    dest[1] = rt_load_attr(self_in, attr);
+    dest[0] = NULL;
+}
+
+void mp_obj_instance_store_attr(mp_obj_t self_in, qstr attr, mp_obj_t value) {
+    // logic: look in class locals (no add) then obj members (add) (TODO check this against CPython)
+    mp_obj_instance_t *self = self_in;
+    mp_map_elem_t *elem = mp_qstr_map_lookup(mp_obj_class_get_locals(self->class), attr, false);
+    if (elem != NULL) {
+        elem->value = value;
+    } else {
+        mp_qstr_map_lookup(self->members, attr, true)->value = value;
+    }
+}
+
+const mp_obj_type_t instance_type = {
+    { &mp_const_type },
+    "instance",
+    NULL, // print
+    NULL, // call_n
+    NULL, // unary_op
+    NULL, // binary_op
+    NULL, // getiter
+    NULL, // iternext
+    {{NULL, NULL},}, // method list
+};
+
+mp_obj_t mp_obj_new_instance(mp_obj_t class) {
+    mp_obj_instance_t *o = m_new_obj(mp_obj_instance_t);
+    o->base.type = &instance_type;
+    o->class = class;
+    o->members = mp_map_new(MP_MAP_QSTR, 0);
+    return o;
+}
diff --git a/py/objlist.c b/py/objlist.c
new file mode 100644
index 0000000000000000000000000000000000000000..4d70c37eb3ebab66003dc1c9dc94269635e79d98
--- /dev/null
+++ b/py/objlist.c
@@ -0,0 +1,204 @@
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <assert.h>
+
+#include "nlr.h"
+#include "misc.h"
+#include "mpconfig.h"
+#include "obj.h"
+#include "runtime0.h"
+#include "runtime.h"
+
+typedef struct _mp_obj_list_t {
+    mp_obj_base_t base;
+    machine_uint_t alloc;
+    machine_uint_t len;
+    mp_obj_t *items;
+} mp_obj_list_t;
+
+static mp_obj_t mp_obj_new_list_iterator(mp_obj_list_t *list, int cur);
+
+/******************************************************************************/
+/* list                                                                       */
+
+static void list_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t o_in) {
+    mp_obj_list_t *o = o_in;
+    print(env, "[");
+    for (int i = 0; i < o->len; i++) {
+        if (i > 0) {
+            print(env, ", ");
+        }
+        mp_obj_print_helper(print, env, o->items[i]);
+    }
+    print(env, "]");
+}
+
+static mp_obj_t list_binary_op(int op, mp_obj_t lhs, mp_obj_t rhs) {
+    mp_obj_list_t *o = lhs;
+    switch (op) {
+        case RT_BINARY_OP_SUBSCR:
+        {
+            // list load
+            uint index = mp_get_index(o->base.type, o->len, rhs);
+            return o->items[index];
+        }
+        default:
+            // op not supported
+            return NULL;
+    }
+}
+
+static mp_obj_t list_getiter(mp_obj_t o_in) {
+    return mp_obj_new_list_iterator(o_in, 0);
+}
+
+mp_obj_t mp_obj_list_append(mp_obj_t self_in, mp_obj_t arg) {
+    assert(MP_OBJ_IS_TYPE(self_in, &list_type));
+    mp_obj_list_t *self = self_in;
+    if (self->len >= self->alloc) {
+        self->alloc *= 2;
+        self->items = m_renew(mp_obj_t, self->items, self->alloc);
+    }
+    self->items[self->len++] = arg;
+    return mp_const_none; // return None, as per CPython
+}
+
+static mp_obj_t list_pop(mp_obj_t self_in, mp_obj_t arg) {
+    assert(MP_OBJ_IS_TYPE(self_in, &list_type));
+    mp_obj_list_t *self = self_in;
+    uint index = mp_get_index(self->base.type, self->len, arg);
+    mp_obj_t ret = self->items[index];
+    self->len -= 1;
+    memcpy(self->items + index, self->items + index + 1, (self->len - index) * sizeof(mp_obj_t));
+    return ret;
+}
+
+// TODO make this conform to CPython's definition of sort
+static void mp_quicksort(mp_obj_t *head, mp_obj_t *tail, mp_obj_t key_fn) {
+    while (head < tail) {
+        mp_obj_t *h = head - 1;
+        mp_obj_t *t = tail;
+        mp_obj_t v = rt_call_function_1(key_fn, tail[0]); // get pivot using key_fn
+        for (;;) {
+            do ++h; while (rt_compare_op(RT_COMPARE_OP_LESS, rt_call_function_1(key_fn, h[0]), v) == mp_const_true);
+            do --t; while (h < t && rt_compare_op(RT_COMPARE_OP_LESS, v, rt_call_function_1(key_fn, t[0])) == mp_const_true);
+            if (h >= t) break;
+            mp_obj_t x = h[0];
+            h[0] = t[0];
+            t[0] = x;
+        }
+        mp_obj_t x = h[0];
+        h[0] = tail[0];
+        tail[0] = x;
+        mp_quicksort(head, t, key_fn);
+        head = h + 1;
+    }
+}
+
+static mp_obj_t list_sort(mp_obj_t self_in, mp_obj_t key_fn) {
+    assert(MP_OBJ_IS_TYPE(self_in, &list_type));
+    mp_obj_list_t *self = self_in;
+    if (self->len > 1) {
+        mp_quicksort(self->items, self->items + self->len - 1, key_fn);
+    }
+    return mp_const_none; // return None, as per CPython
+}
+
+static MP_DEFINE_CONST_FUN_OBJ_2(list_append_obj, mp_obj_list_append);
+static MP_DEFINE_CONST_FUN_OBJ_2(list_pop_obj, list_pop);
+static MP_DEFINE_CONST_FUN_OBJ_2(list_sort_obj, list_sort);
+
+const mp_obj_type_t list_type = {
+    { &mp_const_type },
+    "list",
+    list_print, // print
+    NULL, // call_n
+    NULL, // unary_op
+    list_binary_op, // binary_op
+    list_getiter, // getiter
+    NULL, // iternext
+    { // method list
+        { "append", &list_append_obj },
+        { "pop", &list_pop_obj },
+        { "sort", &list_sort_obj },
+        { NULL, NULL }, // end-of-list sentinel
+    },
+};
+
+static mp_obj_list_t *list_new(uint n) {
+    mp_obj_list_t *o = m_new_obj(mp_obj_list_t);
+    o->base.type = &list_type;
+    o->alloc = n < 4 ? 4 : n;
+    o->len = n;
+    o->items = m_new(mp_obj_t, o->alloc);
+    return o;
+}
+
+mp_obj_t mp_obj_new_list(uint n, mp_obj_t *items) {
+    mp_obj_list_t *o = list_new(n);
+    for (int i = 0; i < n; i++) {
+        o->items[i] = items[i];
+    }
+    return o;
+}
+
+mp_obj_t mp_obj_new_list_reverse(uint n, mp_obj_t *items) {
+    mp_obj_list_t *o = list_new(n);
+    for (int i = 0; i < n; i++) {
+        o->items[i] = items[n - i - 1];
+    }
+    return o;
+}
+
+void mp_obj_list_get(mp_obj_t self_in, uint *len, mp_obj_t **items) {
+    mp_obj_list_t *self = self_in;
+    *len = self->len;
+    *items = self->items;
+}
+
+void mp_obj_list_store(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) {
+    mp_obj_list_t *self = self_in;
+    uint i = mp_get_index(self->base.type, self->len, index);
+    self->items[i] = value;
+}
+
+/******************************************************************************/
+/* list iterator                                                              */
+
+typedef struct _mp_obj_list_it_t {
+    mp_obj_base_t base;
+    mp_obj_list_t *list;
+    machine_uint_t cur;
+} mp_obj_list_it_t;
+
+mp_obj_t list_it_iternext(mp_obj_t self_in) {
+    mp_obj_list_it_t *self = self_in;
+    if (self->cur < self->list->len) {
+        mp_obj_t o_out = self->list->items[self->cur];
+        self->cur += 1;
+        return o_out;
+    } else {
+        return mp_const_stop_iteration;
+    }
+}
+
+static const mp_obj_type_t list_it_type = {
+    { &mp_const_type },
+    "list_iterator",
+    NULL, // print
+    NULL, // call_n
+    NULL, // unary_op
+    NULL, // binary_op
+    NULL, // getiter
+    list_it_iternext, // iternext
+    { { NULL, NULL }, }, // method list
+};
+
+mp_obj_t mp_obj_new_list_iterator(mp_obj_list_t *list, int cur) {
+    mp_obj_list_it_t *o = m_new_obj(mp_obj_list_it_t);
+    o->base.type = &list_it_type;
+    o->list = list;
+    o->cur = cur;
+    return o;
+}
diff --git a/py/objnone.c b/py/objnone.c
new file mode 100644
index 0000000000000000000000000000000000000000..f7b665e9974ddaeaa4f80a6c70cee3f95dd9b8f3
--- /dev/null
+++ b/py/objnone.c
@@ -0,0 +1,35 @@
+#include <stdlib.h>
+#include <stdint.h>
+
+#include "nlr.h"
+#include "misc.h"
+#include "mpconfig.h"
+#include "obj.h"
+
+typedef struct _mp_obj_none_t {
+    mp_obj_base_t base;
+} mp_obj_none_t;
+
+void none_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in) {
+    print(env, "None");
+}
+
+const mp_obj_type_t none_type = {
+    { &mp_const_type },
+    "NoneType",
+    none_print, // print
+    NULL, // call_n
+    NULL, // unary_op
+    NULL, // binary_op
+    NULL, // getiter
+    NULL, // iternext
+    {{NULL, NULL},}, // method list
+};
+
+static const mp_obj_none_t none_obj = {{&none_type}};
+const mp_obj_t mp_const_none = (mp_obj_t)&none_obj;
+
+// the stop-iteration object just needs to be something unique
+// it's not the StopIteration exception
+static const mp_obj_none_t stop_it_obj = {{&none_type}};
+const mp_obj_t mp_const_stop_iteration = (mp_obj_t)&stop_it_obj;
diff --git a/py/objprivate.h b/py/objprivate.h
deleted file mode 100644
index 70ee6560da243dd5a3e0bd82e8e6992beaaada88..0000000000000000000000000000000000000000
--- a/py/objprivate.h
+++ /dev/null
@@ -1,140 +0,0 @@
-// internal py_obj_t representation
-// not for general use, instead access using py_obj_xxx functions
-
-#define IS_O(o, k) (((((py_small_int_t)(o)) & 1) == 0) && (((py_obj_base_t*)(o))->kind == (k)))
-#define IS_SMALL_INT(o) (((py_small_int_t)(o)) & 1)
-#define FROM_SMALL_INT(o) (((py_small_int_t)(o)) >> 1)
-#define TO_SMALL_INT(o) ((py_obj_t)(((o) << 1) | 1))
-
-typedef enum {
-    O_CONST,
-    O_STR,
-#if MICROPY_ENABLE_FLOAT
-    O_FLOAT,
-    O_COMPLEX,
-#endif
-    O_CELL,
-    O_EXCEPTION_0,
-    O_EXCEPTION_N,
-    O_RANGE,
-    O_RANGE_IT,
-    O_FUN_0,
-    O_FUN_1,
-    O_FUN_2,
-    O_FUN_N,
-    O_FUN_VAR,
-    O_FUN_BC,
-    O_FUN_ASM,
-    O_GEN_WRAP,
-    O_GEN_INSTANCE,
-    O_BOUND_METH,
-    O_CLOSURE,
-    O_TUPLE,
-    O_LIST,
-    O_TUPLE_IT,
-    O_LIST_IT,
-    O_SET,
-    O_MAP,
-    O_CLASS,
-    O_OBJ,
-    O_USER,
-} py_obj_kind_t;
-
-typedef struct _py_obj_base_t py_obj_base_t;
-
-struct _py_obj_base_t {
-    py_obj_kind_t kind;
-    union {
-        const char *id;
-        qstr u_str;
-#if MICROPY_ENABLE_FLOAT
-        py_float_t u_float; // for O_FLOAT
-        struct { // for O_COMPLEX
-            py_float_t real;
-            py_float_t imag;
-        } u_complex;
-#endif
-        py_obj_t u_cell; // for O_CELL
-        struct { // for O_EXCEPTION_0
-            qstr id;
-        } u_exc0;
-        struct { // for O_EXCEPTION_N
-            // TODO make generic object or something
-            qstr id;
-            int n_args;
-            const void **args;
-        } u_exc_n;
-        struct { // for O_RANGE
-            // TODO make generic object or something
-            machine_int_t start;
-            machine_int_t stop;
-            machine_int_t step;
-        } u_range;
-        struct { // for O_RANGE_IT
-            // TODO make generic object or something
-            machine_int_t cur;
-            machine_int_t stop;
-            machine_int_t step;
-        } u_range_it;
-        struct { // for O_FUN_[012N], O_FUN_VAR
-            int n_args;
-            void *fun;
-        } u_fun;
-        struct { // for O_FUN_BC
-            int n_args;
-            uint n_state;
-            byte *code;
-        } u_fun_bc;
-        struct { // for O_FUN_ASM
-            int n_args;
-            void *fun;
-        } u_fun_asm;
-        struct { // for O_GEN_WRAP
-            int n_state;
-            py_obj_base_t *fun;
-        } u_gen_wrap;
-        struct { // for O_GEN_INSTANCE
-            py_obj_t *state;
-            const byte *ip;
-            py_obj_t *sp;
-        } u_gen_instance;
-        struct { // for O_BOUND_METH
-            py_obj_t meth;
-            py_obj_t self;
-        } u_bound_meth;
-        struct { // for O_CLOSURE
-            py_obj_t fun;
-            py_obj_t vars;
-        } u_closure;
-        struct { // for O_TUPLE, O_LIST
-            machine_uint_t alloc;
-            machine_uint_t len;
-            py_obj_t *items;
-        } u_tuple_list;
-        struct { // for O_TUPLE_IT, O_LIST_IT
-            py_obj_base_t *obj;
-            machine_uint_t cur;
-        } u_tuple_list_it;
-        struct { // for O_SET
-            machine_uint_t alloc;
-            machine_uint_t used;
-            py_obj_t *table;
-        } u_set;
-        py_map_t u_map; // for O_MAP
-        struct { // for O_CLASS
-            py_map_t *locals;
-        } u_class;
-        struct { // for O_OBJ
-            py_obj_base_t *class; // points to a O_CLASS object
-            py_map_t *members;
-        } u_obj;
-        struct { // for O_USER
-            const py_user_info_t *info;
-            machine_uint_t data1;
-            machine_uint_t data2;
-        } u_user;
-    };
-};
-
-py_obj_t py_obj_new_tuple_iterator(py_obj_base_t *tuple, int cur);
-py_obj_t py_obj_new_list_iterator(py_obj_base_t *list, int cur);
diff --git a/py/objrange.c b/py/objrange.c
new file mode 100644
index 0000000000000000000000000000000000000000..b7fd17fa0ada1fb5924317f862fadcea891357c3
--- /dev/null
+++ b/py/objrange.c
@@ -0,0 +1,88 @@
+#include <stdlib.h>
+#include <stdint.h>
+
+#include "nlr.h"
+#include "misc.h"
+#include "mpconfig.h"
+#include "obj.h"
+
+/******************************************************************************/
+/* range                                                                      */
+
+typedef struct _mp_obj_range_t {
+    mp_obj_base_t base;
+    // TODO make these values generic objects or something
+    machine_int_t start;
+    machine_int_t stop;
+    machine_int_t step;
+} mp_obj_range_t;
+
+mp_obj_t range_getiter(mp_obj_t o_in) {
+    mp_obj_range_t *o = o_in;
+    return mp_obj_new_range_iterator(o->start, o->stop, o->step);
+}
+
+static const mp_obj_type_t range_type = {
+    { &mp_const_type} ,
+    "range",
+    NULL, // print
+    NULL, // call_n
+    NULL, // unary_op
+    NULL, // binary_op
+    range_getiter,
+    NULL, // iternext
+    {{NULL, NULL},}, // method list
+};
+
+// range is a class and instances are immutable sequence objects
+mp_obj_t mp_obj_new_range(int start, int stop, int step) {
+    mp_obj_range_t *o = m_new_obj(mp_obj_range_t);
+    o->base.type = &range_type;
+    o->start = start;
+    o->stop = stop;
+    o->step = step;
+    return o;
+}
+
+/******************************************************************************/
+/* range iterator                                                             */
+
+typedef struct _mp_obj_range_it_t {
+    mp_obj_base_t base;
+    // TODO make these values generic objects or something
+    machine_int_t cur;
+    machine_int_t stop;
+    machine_int_t step;
+} mp_obj_range_it_t;
+
+mp_obj_t range_it_iternext(mp_obj_t o_in) {
+    mp_obj_range_it_t *o = o_in;
+    if ((o->step > 0 && o->cur < o->stop) || (o->step < 0 && o->cur > o->stop)) {
+        mp_obj_t o_out = MP_OBJ_NEW_SMALL_INT(o->cur);
+        o->cur += o->step;
+        return o_out;
+    } else {
+        return mp_const_stop_iteration;
+    }
+}
+
+static const mp_obj_type_t range_it_type = {
+    { &mp_const_type },
+    "range_iterator",
+    NULL, // print
+    NULL, // call_n
+    NULL, // unary_op
+    NULL, // binary_op
+    NULL, // getiter
+    range_it_iternext,
+    {{NULL, NULL},}, // method list
+};
+
+mp_obj_t mp_obj_new_range_iterator(int cur, int stop, int step) {
+    mp_obj_range_it_t *o = m_new_obj(mp_obj_range_it_t);
+    o->base.type = &range_it_type;
+    o->cur = cur;
+    o->stop = stop;
+    o->step = step;
+    return o;
+}
diff --git a/py/objset.c b/py/objset.c
new file mode 100644
index 0000000000000000000000000000000000000000..f225ca7f66efa52f5c9a92defff3b71c59b5cc0d
--- /dev/null
+++ b/py/objset.c
@@ -0,0 +1,58 @@
+#include <stdlib.h>
+#include <stdint.h>
+#include <assert.h>
+
+#include "nlr.h"
+#include "misc.h"
+#include "mpconfig.h"
+#include "obj.h"
+#include "map.h"
+
+typedef struct _mp_obj_set_t {
+    mp_obj_base_t base;
+    mp_set_t set;
+} mp_obj_set_t;
+
+void set_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in) {
+    mp_obj_set_t *self = self_in;
+    bool first = true;
+    print(env, "{");
+    for (int i = 0; i < self->set.alloc; i++) {
+        if (self->set.table[i] != MP_OBJ_NULL) {
+            if (!first) {
+                print(env, ", ");
+            }
+            first = false;
+            mp_obj_print_helper(print, env, self->set.table[i]);
+        }
+    }
+    print(env, "}");
+}
+
+static const mp_obj_type_t set_type = {
+    { &mp_const_type },
+    "set",
+    set_print, // print
+    NULL, // call_n
+    NULL, // unary_op
+    NULL, // binary_op
+    NULL, // getiter
+    NULL, // iternext
+    { { NULL, NULL }, }, // method list
+};
+
+mp_obj_t mp_obj_new_set(int n_args, mp_obj_t *items) {
+    mp_obj_set_t *o = m_new_obj(mp_obj_set_t);
+    o->base.type = &set_type;
+    mp_set_init(&o->set, n_args);
+    for (int i = 0; i < n_args; i++) {
+        mp_set_lookup(&o->set, items[i], true);
+    }
+    return o;
+}
+
+void mp_obj_set_store(mp_obj_t self_in, mp_obj_t item) {
+    assert(MP_OBJ_IS_TYPE(self_in, &set_type));
+    mp_obj_set_t *self = self_in;
+    mp_set_lookup(&self->set, item, true);
+}
diff --git a/py/objstr.c b/py/objstr.c
new file mode 100644
index 0000000000000000000000000000000000000000..d82ea3d3ce88a8b2ba01583ae18bc498a0671dea
--- /dev/null
+++ b/py/objstr.c
@@ -0,0 +1,151 @@
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdarg.h>
+#include <string.h>
+#include <assert.h>
+
+#include "nlr.h"
+#include "misc.h"
+#include "mpconfig.h"
+#include "obj.h"
+#include "runtime0.h"
+#include "runtime.h"
+
+typedef struct _mp_obj_str_t {
+    mp_obj_base_t base;
+    qstr qstr;
+} mp_obj_str_t;
+
+void str_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in) {
+    mp_obj_str_t *self = self_in;
+    // TODO need to escape chars etc
+    print(env, "'%s'", qstr_str(self->qstr));
+}
+
+mp_obj_t str_binary_op(int op, mp_obj_t lhs_in, mp_obj_t rhs_in) {
+    mp_obj_str_t *lhs = lhs_in;
+    const char *lhs_str = qstr_str(lhs->qstr);
+    switch (op) {
+        case RT_BINARY_OP_SUBSCR:
+            // string access
+            // XXX a massive hack!
+            return mp_obj_new_int(lhs_str[mp_obj_get_int(rhs_in)]);
+
+        case RT_BINARY_OP_ADD:
+        case RT_BINARY_OP_INPLACE_ADD:
+            if (MP_OBJ_IS_TYPE(rhs_in, &str_type)) {
+                // add 2 strings
+                const char *rhs_str = qstr_str(((mp_obj_str_t*)rhs_in)->qstr);
+                char *val = m_new(char, strlen(lhs_str) + strlen(rhs_str) + 1);
+                stpcpy(stpcpy(val, lhs_str), rhs_str);
+                return mp_obj_new_str(qstr_from_str_take(val));
+            }
+            break;
+    }
+
+    return MP_OBJ_NULL; // op not supported
+}
+
+mp_obj_t str_join(mp_obj_t self_in, mp_obj_t arg) {
+    assert(MP_OBJ_IS_TYPE(self_in, &str_type));
+    mp_obj_str_t *self = self_in;
+    int required_len = strlen(qstr_str(self->qstr));
+
+    // process arg, count required chars
+    uint seq_len;
+    mp_obj_t *seq_items;
+    if (MP_OBJ_IS_TYPE(arg, &tuple_type)) {
+        mp_obj_tuple_get(arg, &seq_len, &seq_items);
+    } else if (MP_OBJ_IS_TYPE(arg, &list_type)) {
+        mp_obj_list_get(arg, &seq_len, &seq_items);
+    } else {
+        goto bad_arg;
+    }
+    for (int i = 0; i < seq_len; i++) {
+        if (!MP_OBJ_IS_TYPE(seq_items[i], &str_type)) {
+            goto bad_arg;
+        }
+        required_len += strlen(qstr_str(mp_obj_str_get(seq_items[i])));
+    }
+
+    // make joined string
+    char *joined_str = m_new(char, required_len + 1);
+    joined_str[0] = 0;
+    for (int i = 0; i < seq_len; i++) {
+        const char *s2 = qstr_str(mp_obj_str_get(seq_items[i]));
+        if (i > 0) {
+            strcat(joined_str, qstr_str(self->qstr));
+        }
+        strcat(joined_str, s2);
+    }
+    return mp_obj_new_str(qstr_from_str_take(joined_str));
+
+bad_arg:
+    nlr_jump(mp_obj_new_exception_msg(rt_q_TypeError, "?str.join expecting a list of str's"));
+}
+
+void vstr_printf_wrapper(void *env, const char *fmt, ...) {
+    va_list args;
+    va_start(args, fmt);
+    vstr_vprintf(env, fmt, args);
+    va_end(args);
+}
+
+mp_obj_t str_format(int n_args, const mp_obj_t *args) {
+    assert(MP_OBJ_IS_TYPE(args[0], &str_type));
+    mp_obj_str_t *self = args[0];
+
+    const char *str = qstr_str(self->qstr);
+    int arg_i = 1;
+    vstr_t *vstr = vstr_new();
+    for (; *str; str++) {
+        if (*str == '{') {
+            str++;
+            if (*str == '{') {
+                vstr_add_char(vstr, '{');
+            } else if (*str == '}') {
+                if (arg_i >= n_args) {
+                    nlr_jump(mp_obj_new_exception_msg(rt_q_IndexError, "tuple index out of range"));
+                }
+                mp_obj_print_helper(vstr_printf_wrapper, vstr, args[arg_i]);
+                arg_i++;
+            }
+        } else {
+            vstr_add_char(vstr, *str);
+        }
+    }
+
+    return mp_obj_new_str(qstr_from_str_take(vstr->buf));
+}
+
+static MP_DEFINE_CONST_FUN_OBJ_2(str_join_obj, str_join);
+static MP_DEFINE_CONST_FUN_OBJ_VAR(str_format_obj, 1, str_format);
+
+const mp_obj_type_t str_type = {
+    { &mp_const_type },
+    "str",
+    str_print, // print
+    NULL, // call_n
+    NULL, // unary_op
+    str_binary_op, // binary_op
+    NULL, // getiter
+    NULL, // iternext
+    { // method list
+        { "join", &str_join_obj },
+        { "format", &str_format_obj },
+        { NULL, NULL }, // end-of-list sentinel
+    },
+};
+
+mp_obj_t mp_obj_new_str(qstr qstr) {
+    mp_obj_str_t *o = m_new_obj(mp_obj_str_t);
+    o->base.type = &str_type;
+    o->qstr = qstr;
+    return o;
+}
+
+qstr mp_obj_str_get(mp_obj_t self_in) {
+    assert(MP_OBJ_IS_TYPE(self_in, &str_type));
+    mp_obj_str_t *self = self_in;
+    return self->qstr;
+}
diff --git a/py/objtuple.c b/py/objtuple.c
new file mode 100644
index 0000000000000000000000000000000000000000..b98d6ede7be40909927e416c1a3de35ed8fc6529
--- /dev/null
+++ b/py/objtuple.c
@@ -0,0 +1,133 @@
+#include <stdlib.h>
+#include <stdint.h>
+//#include <string.h>
+#include <assert.h>
+
+#include "nlr.h"
+#include "misc.h"
+#include "mpconfig.h"
+#include "obj.h"
+#include "runtime0.h"
+
+typedef struct _mp_obj_tuple_t {
+    mp_obj_base_t base;
+    machine_uint_t len;
+    mp_obj_t items[];
+} mp_obj_tuple_t;
+
+static mp_obj_t mp_obj_new_tuple_iterator(mp_obj_tuple_t *tuple, int cur);
+
+/******************************************************************************/
+/* tuple                                                                      */
+
+void tuple_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t o_in) {
+    mp_obj_tuple_t *o = o_in;
+    print(env, "(");
+    for (int i = 0; i < o->len; i++) {
+        if (i > 0) {
+            print(env, ", ");
+        }
+        mp_obj_print_helper(print, env, o->items[i]);
+    }
+    if (o->len == 1) {
+        print(env, ",");
+    }
+    print(env, ")");
+}
+
+mp_obj_t tuple_binary_op(int op, mp_obj_t lhs, mp_obj_t rhs) {
+    mp_obj_tuple_t *o = lhs;
+    switch (op) {
+        case RT_BINARY_OP_SUBSCR:
+        {
+            // tuple load
+            uint index = mp_get_index(o->base.type, o->len, rhs);
+            return o->items[index];
+        }
+        default:
+            // op not supported
+            return NULL;
+    }
+}
+
+mp_obj_t tuple_getiter(mp_obj_t o_in) {
+    return mp_obj_new_tuple_iterator(o_in, 0);
+}
+
+void mp_obj_tuple_get(mp_obj_t self_in, uint *len, mp_obj_t **items) {
+    mp_obj_tuple_t *self = self_in;
+    *len = self->len;
+    *items = &self->items[0];
+}
+
+const mp_obj_type_t tuple_type = {
+    { &mp_const_type },
+    "tuple",
+    tuple_print, // print
+    NULL, // call_n
+    NULL, // unary_op
+    tuple_binary_op, // binary_op
+    tuple_getiter, // getiter
+    NULL, // iternext
+    {{NULL, NULL},}, // method list
+};
+
+mp_obj_t mp_obj_new_tuple(uint n, mp_obj_t *items) {
+    mp_obj_tuple_t *o = m_new_obj_var(mp_obj_tuple_t, mp_obj_t, n);
+    o->base.type = &tuple_type;
+    o->len = n;
+    for (int i = 0; i < n; i++) {
+        o->items[i] = items[i];
+    }
+    return o;
+}
+
+mp_obj_t mp_obj_new_tuple_reverse(uint n, mp_obj_t *items) {
+    mp_obj_tuple_t *o = m_new_obj_var(mp_obj_tuple_t, mp_obj_t, n);
+    o->base.type = &tuple_type;
+    o->len = n;
+    for (int i = 0; i < n; i++) {
+        o->items[i] = items[n - i - 1];
+    }
+    return o;
+}
+
+/******************************************************************************/
+/* tuple iterator                                                             */
+
+typedef struct _mp_obj_tuple_it_t {
+    mp_obj_base_t base;
+    mp_obj_tuple_t *tuple;
+    machine_uint_t cur;
+} mp_obj_tuple_it_t;
+
+mp_obj_t tuple_it_iternext(mp_obj_t self_in) {
+    mp_obj_tuple_it_t *self = self_in;
+    if (self->cur < self->tuple->len) {
+        mp_obj_t o_out = self->tuple->items[self->cur];
+        self->cur += 1;
+        return o_out;
+    } else {
+        return mp_const_stop_iteration;
+    }
+}
+
+static const mp_obj_type_t tuple_it_type = {
+    { &mp_const_type },
+    "tuple_iterator",
+    NULL, // print
+    NULL, // call_n
+    NULL, // unary_op
+    NULL, // binary_op
+    NULL, // getiter
+    tuple_it_iternext,
+    {{NULL, NULL},}, // method list
+};
+
+static mp_obj_t mp_obj_new_tuple_iterator(mp_obj_tuple_t *tuple, int cur) {
+    mp_obj_tuple_it_t *o = m_new_obj(mp_obj_tuple_it_t);
+    o->base.type = &tuple_it_type;
+    o->tuple = tuple;
+    o->cur = cur;
+    return o;
+}
diff --git a/py/objtype.c b/py/objtype.c
new file mode 100644
index 0000000000000000000000000000000000000000..4358148c04dca28c788c5db251435e41f74a748d
--- /dev/null
+++ b/py/objtype.c
@@ -0,0 +1,23 @@
+#include <stdlib.h>
+#include <stdint.h>
+
+#include "nlr.h"
+#include "misc.h"
+#include "mpconfig.h"
+#include "obj.h"
+
+void type_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in) {
+    print(env, "type?");
+}
+
+const mp_obj_type_t mp_const_type = {
+    { &mp_const_type },
+    "type?",
+    type_print, // print
+    NULL, // call_n
+    NULL, // unary_op
+    NULL, // binary_op
+    NULL, // getiter
+    NULL, // iternext
+    {{NULL, NULL},}, // method list
+};
diff --git a/py/parse.c b/py/parse.c
index 632ac4e4f50e2bf0ed6c2914fe8b33e172eedeca..48c30480568a951dbef8d4392acae978deae4ac8 100644
--- a/py/parse.c
+++ b/py/parse.c
@@ -7,7 +7,7 @@
 #include <assert.h>
 
 #include "misc.h"
-#include "mpyconfig.h"
+#include "mpconfig.h"
 #include "lexer.h"
 #include "parse.h"
 
@@ -50,9 +50,9 @@ enum {
 #define one_or_more             (RULE_ACT_LIST | 2)
 #define list                    (RULE_ACT_LIST | 1)
 #define list_with_end           (RULE_ACT_LIST | 3)
-#define tok(t)                  (RULE_ARG_TOK | PY_TOKEN_##t)
+#define tok(t)                  (RULE_ARG_TOK | MP_TOKEN_##t)
 #define rule(r)                 (RULE_ARG_RULE | RULE_##r)
-#define opt_tok(t)              (RULE_ARG_OPT_TOK | PY_TOKEN_##t)
+#define opt_tok(t)              (RULE_ARG_OPT_TOK | MP_TOKEN_##t)
 #define opt_rule(r)             (RULE_ARG_OPT_RULE | RULE_##r)
 #ifdef USE_RULE_NAME
 #define DEF_RULE(rule, comp, kind, arg...) static const rule_t rule_##rule = { RULE_##rule, kind, #rule, { arg } };
@@ -89,7 +89,7 @@ typedef struct _parser_t {
     rule_stack_t *rule_stack;
 
     uint result_stack_top;
-    py_parse_node_t *result_stack;
+    mp_parse_node_t *result_stack;
 } parser_t;
 
 static void push_rule(parser_t *parser, const rule_t *rule, int arg_i) {
@@ -115,47 +115,47 @@ static void pop_rule(parser_t *parser, const rule_t **rule, uint *arg_i) {
     *arg_i = parser->rule_stack[parser->rule_stack_top].arg_i;
 }
 
-py_parse_node_t py_parse_node_new_leaf(machine_int_t kind, machine_int_t arg) {
-    return (py_parse_node_t)(kind | (arg << 4));
+mp_parse_node_t mp_parse_node_new_leaf(machine_int_t kind, machine_int_t arg) {
+    return (mp_parse_node_t)(kind | (arg << 4));
 }
 
 int num_parse_nodes_allocated = 0;
-py_parse_node_struct_t *parse_node_new_struct(int rule_id, int num_args) {
-    py_parse_node_struct_t *pn = m_malloc(sizeof(py_parse_node_struct_t) + num_args * sizeof(py_parse_node_t));
+mp_parse_node_struct_t *parse_node_new_struct(int rule_id, int num_args) {
+    mp_parse_node_struct_t *pn = m_malloc(sizeof(mp_parse_node_struct_t) + num_args * sizeof(mp_parse_node_t));
     pn->source = 0; // TODO
     pn->kind_num_nodes = (rule_id & 0xff) | (num_args << 8);
     num_parse_nodes_allocated += 1;
     return pn;
 }
 
-void py_parse_node_show(py_parse_node_t pn, int indent) {
+void mp_parse_node_show(mp_parse_node_t pn, int indent) {
     for (int i = 0; i < indent; i++) {
         printf(" ");
     }
-    if (PY_PARSE_NODE_IS_NULL(pn)) {
+    if (MP_PARSE_NODE_IS_NULL(pn)) {
         printf("NULL\n");
-    } else if (PY_PARSE_NODE_IS_LEAF(pn)) {
-        int arg = PY_PARSE_NODE_LEAF_ARG(pn);
-        switch (PY_PARSE_NODE_LEAF_KIND(pn)) {
-            case PY_PARSE_NODE_ID: printf("id(%s)\n", qstr_str(arg)); break;
-            case PY_PARSE_NODE_SMALL_INT: printf("int(%d)\n", arg); break;
-            case PY_PARSE_NODE_INTEGER: printf("int(%s)\n", qstr_str(arg)); break;
-            case PY_PARSE_NODE_DECIMAL: printf("dec(%s)\n", qstr_str(arg)); break;
-            case PY_PARSE_NODE_STRING: printf("str(%s)\n", qstr_str(arg)); break;
-            case PY_PARSE_NODE_BYTES: printf("bytes(%s)\n", qstr_str(arg)); break;
-            case PY_PARSE_NODE_TOKEN: printf("tok(%d)\n", arg); break;
+    } else if (MP_PARSE_NODE_IS_LEAF(pn)) {
+        int arg = MP_PARSE_NODE_LEAF_ARG(pn);
+        switch (MP_PARSE_NODE_LEAF_KIND(pn)) {
+            case MP_PARSE_NODE_ID: printf("id(%s)\n", qstr_str(arg)); break;
+            case MP_PARSE_NODE_SMALL_INT: printf("int(%d)\n", arg); break;
+            case MP_PARSE_NODE_INTEGER: printf("int(%s)\n", qstr_str(arg)); break;
+            case MP_PARSE_NODE_DECIMAL: printf("dec(%s)\n", qstr_str(arg)); break;
+            case MP_PARSE_NODE_STRING: printf("str(%s)\n", qstr_str(arg)); break;
+            case MP_PARSE_NODE_BYTES: printf("bytes(%s)\n", qstr_str(arg)); break;
+            case MP_PARSE_NODE_TOKEN: printf("tok(%d)\n", arg); break;
             default: assert(0);
         }
     } else {
-        py_parse_node_struct_t *pns2 = (py_parse_node_struct_t*)pn;
+        mp_parse_node_struct_t *pns2 = (mp_parse_node_struct_t*)pn;
         int n = pns2->kind_num_nodes >> 8;
 #ifdef USE_RULE_NAME
-        printf("%s(%d) (n=%d)\n", rules[PY_PARSE_NODE_STRUCT_KIND(pns2)]->rule_name, PY_PARSE_NODE_STRUCT_KIND(pns2), n);
+        printf("%s(%d) (n=%d)\n", rules[MP_PARSE_NODE_STRUCT_KIND(pns2)]->rule_name, MP_PARSE_NODE_STRUCT_KIND(pns2), n);
 #else
-        printf("rule(%u) (n=%d)\n", (uint)PY_PARSE_NODE_STRUCT_KIND(pns2), n);
+        printf("rule(%u) (n=%d)\n", (uint)MP_PARSE_NODE_STRUCT_KIND(pns2), n);
 #endif
         for (int i = 0; i < n; i++) {
-            py_parse_node_show(pns2->nodes[i], indent + 2);
+            mp_parse_node_show(pns2->nodes[i], indent + 2);
         }
     }
 }
@@ -164,31 +164,31 @@ void py_parse_node_show(py_parse_node_t pn, int indent) {
 static void result_stack_show(parser_t *parser) {
     printf("result stack, most recent first\n");
     for (int i = parser->result_stack_top - 1; i >= 0; i--) {
-        py_parse_node_show(parser->result_stack[i], 0);
+        mp_parse_node_show(parser->result_stack[i], 0);
     }
 }
 */
 
-static py_parse_node_t pop_result(parser_t *parser) {
+static mp_parse_node_t pop_result(parser_t *parser) {
     assert(parser->result_stack_top > 0);
     return parser->result_stack[--parser->result_stack_top];
 }
 
-static py_parse_node_t peek_result(parser_t *parser, int pos) {
+static mp_parse_node_t peek_result(parser_t *parser, int pos) {
     assert(parser->result_stack_top > pos);
     return parser->result_stack[parser->result_stack_top - 1 - pos];
 }
 
-static void push_result_node(parser_t *parser, py_parse_node_t pn) {
+static void push_result_node(parser_t *parser, mp_parse_node_t pn) {
     parser->result_stack[parser->result_stack_top++] = pn;
 }
 
-static void push_result_token(parser_t *parser, const py_lexer_t *lex) {
-    const py_token_t *tok = py_lexer_cur(lex);
-    py_parse_node_t pn;
-    if (tok->kind == PY_TOKEN_NAME) {
-        pn = py_parse_node_new_leaf(PY_PARSE_NODE_ID, qstr_from_strn_copy(tok->str, tok->len));
-    } else if (tok->kind == PY_TOKEN_NUMBER) {
+static void push_result_token(parser_t *parser, const mp_lexer_t *lex) {
+    const mp_token_t *tok = mp_lexer_cur(lex);
+    mp_parse_node_t pn;
+    if (tok->kind == MP_TOKEN_NAME) {
+        pn = mp_parse_node_new_leaf(MP_PARSE_NODE_ID, qstr_from_strn_copy(tok->str, tok->len));
+    } else if (tok->kind == MP_TOKEN_NUMBER) {
         bool dec = false;
         bool small_int = true;
         int int_val = 0;
@@ -227,43 +227,43 @@ static void push_result_token(parser_t *parser, const py_lexer_t *lex) {
             }
         }
         if (dec) {
-            pn = py_parse_node_new_leaf(PY_PARSE_NODE_DECIMAL, qstr_from_strn_copy(str, len));
-        } else if (small_int && PY_FIT_SMALL_INT(int_val)) {
-            pn = py_parse_node_new_leaf(PY_PARSE_NODE_SMALL_INT, int_val);
+            pn = mp_parse_node_new_leaf(MP_PARSE_NODE_DECIMAL, qstr_from_strn_copy(str, len));
+        } else if (small_int && MP_FIT_SMALL_INT(int_val)) {
+            pn = mp_parse_node_new_leaf(MP_PARSE_NODE_SMALL_INT, int_val);
         } else {
-            pn = py_parse_node_new_leaf(PY_PARSE_NODE_INTEGER, qstr_from_strn_copy(str, len));
+            pn = mp_parse_node_new_leaf(MP_PARSE_NODE_INTEGER, qstr_from_strn_copy(str, len));
         }
-    } else if (tok->kind == PY_TOKEN_STRING) {
-        pn = py_parse_node_new_leaf(PY_PARSE_NODE_STRING, qstr_from_strn_copy(tok->str, tok->len));
-    } else if (tok->kind == PY_TOKEN_BYTES) {
-        pn = py_parse_node_new_leaf(PY_PARSE_NODE_BYTES, qstr_from_strn_copy(tok->str, tok->len));
+    } else if (tok->kind == MP_TOKEN_STRING) {
+        pn = mp_parse_node_new_leaf(MP_PARSE_NODE_STRING, qstr_from_strn_copy(tok->str, tok->len));
+    } else if (tok->kind == MP_TOKEN_BYTES) {
+        pn = mp_parse_node_new_leaf(MP_PARSE_NODE_BYTES, qstr_from_strn_copy(tok->str, tok->len));
     } else {
-        pn = py_parse_node_new_leaf(PY_PARSE_NODE_TOKEN, tok->kind);
+        pn = mp_parse_node_new_leaf(MP_PARSE_NODE_TOKEN, tok->kind);
     }
     push_result_node(parser, pn);
 }
 
 static void push_result_rule(parser_t *parser, const rule_t *rule, int num_args) {
-    py_parse_node_struct_t *pn = parse_node_new_struct(rule->rule_id, num_args);
+    mp_parse_node_struct_t *pn = parse_node_new_struct(rule->rule_id, num_args);
     for (int i = num_args; i > 0; i--) {
         pn->nodes[i - 1] = pop_result(parser);
     }
-    push_result_node(parser, (py_parse_node_t)pn);
+    push_result_node(parser, (mp_parse_node_t)pn);
 }
 
-py_parse_node_t py_parse(py_lexer_t *lex, py_parse_input_kind_t input_kind) {
+mp_parse_node_t mp_parse(mp_lexer_t *lex, mp_parse_input_kind_t input_kind) {
     parser_t *parser = m_new(parser_t, 1);
     parser->rule_stack_alloc = 64;
     parser->rule_stack_top = 0;
     parser->rule_stack = m_new(rule_stack_t, parser->rule_stack_alloc);
 
-    parser->result_stack = m_new(py_parse_node_t, 1000);
+    parser->result_stack = m_new(mp_parse_node_t, 1000);
     parser->result_stack_top = 0;
 
     int top_level_rule;
     switch (input_kind) {
-        case PY_PARSE_SINGLE_INPUT: top_level_rule = RULE_single_input; break;
-        //case PY_PARSE_EVAL_INPUT: top_level_rule = RULE_eval_input; break;
+        case MP_PARSE_SINGLE_INPUT: top_level_rule = RULE_single_input; break;
+        //case MP_PARSE_EVAL_INPUT: top_level_rule = RULE_eval_input; break;
         default: top_level_rule = RULE_file_input;
     }
     push_rule(parser, rules[top_level_rule], 0);
@@ -271,7 +271,7 @@ py_parse_node_t py_parse(py_lexer_t *lex, py_parse_input_kind_t input_kind) {
     uint n, i;
     bool backtrack = false;
     const rule_t *rule;
-    py_token_kind_t tok_kind;
+    mp_token_kind_t tok_kind;
     bool emit_rule;
     bool had_trailing_sep;
 
@@ -303,9 +303,9 @@ py_parse_node_t py_parse(py_lexer_t *lex, py_parse_input_kind_t input_kind) {
                 for (; i < n - 1; ++i) {
                     switch (rule->arg[i] & RULE_ARG_KIND_MASK) {
                         case RULE_ARG_TOK:
-                            if (py_lexer_is_kind(lex, rule->arg[i] & RULE_ARG_ARG_MASK)) {
+                            if (mp_lexer_is_kind(lex, rule->arg[i] & RULE_ARG_ARG_MASK)) {
                                 push_result_token(parser, lex);
-                                py_lexer_to_next(lex);
+                                mp_lexer_to_next(lex);
                                 goto next_rule;
                             }
                             break;
@@ -318,9 +318,9 @@ py_parse_node_t py_parse(py_lexer_t *lex, py_parse_input_kind_t input_kind) {
                     }
                 }
                 if ((rule->arg[i] & RULE_ARG_KIND_MASK) == RULE_ARG_TOK) {
-                    if (py_lexer_is_kind(lex, rule->arg[i] & RULE_ARG_ARG_MASK)) {
+                    if (mp_lexer_is_kind(lex, rule->arg[i] & RULE_ARG_ARG_MASK)) {
                         push_result_token(parser, lex);
-                        py_lexer_to_next(lex);
+                        mp_lexer_to_next(lex);
                     } else {
                         backtrack = true;
                         goto next_rule;
@@ -337,7 +337,7 @@ py_parse_node_t py_parse(py_lexer_t *lex, py_parse_input_kind_t input_kind) {
                     assert(i > 0);
                     if ((rule->arg[i - 1] & RULE_ARG_KIND_MASK) == RULE_ARG_OPT_RULE) {
                         // an optional rule that failed, so continue with next arg
-                        push_result_node(parser, PY_PARSE_NODE_NULL);
+                        push_result_node(parser, MP_PARSE_NODE_NULL);
                         backtrack = false;
                     } else {
                         // a mandatory rule that failed, so propagate backtrack
@@ -356,12 +356,12 @@ py_parse_node_t py_parse(py_lexer_t *lex, py_parse_input_kind_t input_kind) {
                         case RULE_ARG_TOK:
                             // need to match a token
                             tok_kind = rule->arg[i] & RULE_ARG_ARG_MASK;
-                            if (py_lexer_is_kind(lex, tok_kind)) {
+                            if (mp_lexer_is_kind(lex, tok_kind)) {
                                 // matched token
-                                if (tok_kind == PY_TOKEN_NAME) {
+                                if (tok_kind == MP_TOKEN_NAME) {
                                     push_result_token(parser, lex);
                                 }
-                                py_lexer_to_next(lex);
+                                mp_lexer_to_next(lex);
                             } else {
                                 // failed to match token
                                 if (i > 0) {
@@ -399,10 +399,10 @@ py_parse_node_t py_parse(py_lexer_t *lex, py_parse_input_kind_t input_kind) {
                 for (int x = 0; x < n; ++x) {
                     if ((rule->arg[x] & RULE_ARG_KIND_MASK) == RULE_ARG_TOK) {
                         tok_kind = rule->arg[x] & RULE_ARG_ARG_MASK;
-                        if (tok_kind >= PY_TOKEN_NAME) {
+                        if (tok_kind >= MP_TOKEN_NAME) {
                             emit_rule = true;
                         }
-                        if (tok_kind == PY_TOKEN_NAME) {
+                        if (tok_kind == MP_TOKEN_NAME) {
                             // only tokens which were names are pushed to stack
                             i += 1;
                         }
@@ -427,13 +427,13 @@ py_parse_node_t py_parse(py_lexer_t *lex, py_parse_input_kind_t input_kind) {
                 // always emit these rules, and add an extra blank node at the end (to be used by the compiler to store data)
                 if (rule->rule_id == RULE_funcdef || rule->rule_id == RULE_classdef || rule->rule_id == RULE_comp_for || rule->rule_id == RULE_lambdef || rule->rule_id == RULE_lambdef_nocond) {
                     emit_rule = true;
-                    push_result_node(parser, PY_PARSE_NODE_NULL);
+                    push_result_node(parser, MP_PARSE_NODE_NULL);
                     i += 1;
                 }
 
                 int num_not_nil = 0;
                 for (int x = 0; x < i; ++x) {
-                    if (peek_result(parser, x) != PY_PARSE_NODE_NULL) {
+                    if (peek_result(parser, x) != MP_PARSE_NODE_NULL) {
                         num_not_nil += 1;
                     }
                 }
@@ -446,10 +446,10 @@ py_parse_node_t py_parse(py_lexer_t *lex, py_parse_input_kind_t input_kind) {
                     //assert(0);
                 } else if (num_not_nil == 1) {
                     // single result, leave it on stack
-                    py_parse_node_t pn = PY_PARSE_NODE_NULL;
+                    mp_parse_node_t pn = MP_PARSE_NODE_NULL;
                     for (int x = 0; x < i; ++x) {
-                        py_parse_node_t pn2 = pop_result(parser);
-                        if (pn2 != PY_PARSE_NODE_NULL) {
+                        mp_parse_node_t pn2 = pop_result(parser);
+                        if (pn2 != MP_PARSE_NODE_NULL) {
                             pn = pn2;
                         }
                     }
@@ -498,13 +498,13 @@ py_parse_node_t py_parse(py_lexer_t *lex, py_parse_input_kind_t input_kind) {
                         uint arg = rule->arg[i & 1 & n];
                         switch (arg & RULE_ARG_KIND_MASK) {
                             case RULE_ARG_TOK:
-                                if (py_lexer_is_kind(lex, arg & RULE_ARG_ARG_MASK)) {
+                                if (mp_lexer_is_kind(lex, arg & RULE_ARG_ARG_MASK)) {
                                     if (i & 1 & n) {
                                         // separators which are tokens are not pushed to result stack
                                     } else {
                                         push_result_token(parser, lex);
                                     }
-                                    py_lexer_to_next(lex);
+                                    mp_lexer_to_next(lex);
                                     // got element of list, so continue parsing list
                                     i += 1;
                                 } else {
@@ -552,7 +552,7 @@ py_parse_node_t py_parse(py_lexer_t *lex, py_parse_input_kind_t input_kind) {
     }
 
     // check we are at the end of the token stream
-    if (!py_lexer_is_kind(lex, PY_TOKEN_END)) {
+    if (!mp_lexer_is_kind(lex, MP_TOKEN_END)) {
         goto syntax_error;
     }
 
@@ -564,16 +564,16 @@ py_parse_node_t py_parse(py_lexer_t *lex, py_parse_input_kind_t input_kind) {
     return parser->result_stack[0];
 
 syntax_error:
-    if (py_lexer_is_kind(lex, PY_TOKEN_INDENT)) {
-        py_lexer_show_error_pythonic(lex, "IndentationError: unexpected indent");
-    } else if (py_lexer_is_kind(lex, PY_TOKEN_DEDENT_MISMATCH)) {
-        py_lexer_show_error_pythonic(lex, "IndentationError: unindent does not match any outer indentation level");
+    if (mp_lexer_is_kind(lex, MP_TOKEN_INDENT)) {
+        mp_lexer_show_error_pythonic(lex, "IndentationError: unexpected indent");
+    } else if (mp_lexer_is_kind(lex, MP_TOKEN_DEDENT_MISMATCH)) {
+        mp_lexer_show_error_pythonic(lex, "IndentationError: unindent does not match any outer indentation level");
     } else {
-        py_lexer_show_error_pythonic(lex, "syntax error:");
+        mp_lexer_show_error_pythonic(lex, "syntax error:");
 #ifdef USE_RULE_NAME
-        py_lexer_show_error(lex, rule->rule_name);
+        mp_lexer_show_error(lex, rule->rule_name);
 #endif
-        py_token_show(py_lexer_cur(lex));
+        mp_token_show(mp_lexer_cur(lex));
     }
-    return PY_PARSE_NODE_NULL;
+    return MP_PARSE_NODE_NULL;
 }
diff --git a/py/parse.h b/py/parse.h
index 722d9f6c20705b67bafa2687b091dd3f5d28daa6..7326243ea442ae49c21f2d3afed8daeb7013f020 100644
--- a/py/parse.h
+++ b/py/parse.h
@@ -1,6 +1,6 @@
-struct _py_lexer_t;
+struct _mp_lexer_t;
 
-// a py_parse_node_t is:
+// a mp_parse_node_t is:
 //  - 0000...0000: no node
 //  - xxxx...0001: an identifier; bits 4 and above are the qstr
 //  - xxxx...0011: a small integer; bits 4 and above are the signed value, 2's complement
@@ -8,58 +8,58 @@ struct _py_lexer_t;
 //  - xxxx...0111: a decimal; bits 4 and above are the qstr holding the value
 //  - xxxx...1001: a string; bits 4 and above are the qstr holding the value
 //  - xxxx...1011: a string with triple quotes; bits 4 and above are the qstr holding the value
-//  - xxxx...1101: a token; bits 4 and above are py_token_kind_t
-//  - xxxx...xxx0: pointer to py_parse_node_struct_t
+//  - xxxx...1101: a token; bits 4 and above are mp_token_kind_t
+//  - xxxx...xxx0: pointer to mp_parse_node_struct_t
 
 // makes sure the top 5 bits of x are all cleared (positive number) or all set (negavite number)
 // these macros can probably go somewhere else because they are used more than just in the parser
-#define PY_UINT_HIGH_5_BITS (~((~((machine_uint_t)0)) >> 5))
-#define PY_FIT_SMALL_INT(x) (((((machine_uint_t)(x)) & PY_UINT_HIGH_5_BITS) == 0) || ((((machine_uint_t)(x)) & PY_UINT_HIGH_5_BITS) == PY_UINT_HIGH_5_BITS))
+#define MP_UINT_HIGH_5_BITS (~((~((machine_uint_t)0)) >> 5))
+#define MP_FIT_SMALL_INT(x) (((((machine_uint_t)(x)) & MP_UINT_HIGH_5_BITS) == 0) || ((((machine_uint_t)(x)) & MP_UINT_HIGH_5_BITS) == MP_UINT_HIGH_5_BITS))
 
-#define PY_PARSE_NODE_NULL      (0)
-#define PY_PARSE_NODE_ID        (0x1)
-#define PY_PARSE_NODE_SMALL_INT (0x3)
-#define PY_PARSE_NODE_INTEGER   (0x5)
-#define PY_PARSE_NODE_DECIMAL   (0x7)
-#define PY_PARSE_NODE_STRING    (0x9)
-#define PY_PARSE_NODE_BYTES     (0xb)
-#define PY_PARSE_NODE_TOKEN     (0xd)
+#define MP_PARSE_NODE_NULL      (0)
+#define MP_PARSE_NODE_ID        (0x1)
+#define MP_PARSE_NODE_SMALL_INT (0x3)
+#define MP_PARSE_NODE_INTEGER   (0x5)
+#define MP_PARSE_NODE_DECIMAL   (0x7)
+#define MP_PARSE_NODE_STRING    (0x9)
+#define MP_PARSE_NODE_BYTES     (0xb)
+#define MP_PARSE_NODE_TOKEN     (0xd)
 
-typedef machine_uint_t py_parse_node_t; // must be pointer size
+typedef machine_uint_t mp_parse_node_t; // must be pointer size
 
-typedef struct _py_parse_node_struct_t {
+typedef struct _mp_parse_node_struct_t {
     uint32_t source;            // file identifier, and line number
     uint32_t kind_num_nodes;    // parse node kind, and number of nodes
-    py_parse_node_t nodes[];    // nodes
-} py_parse_node_struct_t;
+    mp_parse_node_t nodes[];    // nodes
+} mp_parse_node_struct_t;
 
-// macros for py_parse_node_t usage
+// macros for mp_parse_node_t usage
 // some of these evaluate their argument more than once
 
-#define PY_PARSE_NODE_IS_NULL(pn) ((pn) == PY_PARSE_NODE_NULL)
-#define PY_PARSE_NODE_IS_LEAF(pn) ((pn) & 1)
-#define PY_PARSE_NODE_IS_STRUCT(pn) ((pn) != PY_PARSE_NODE_NULL && ((pn) & 1) == 0)
-#define PY_PARSE_NODE_IS_STRUCT_KIND(pn, k) ((pn) != PY_PARSE_NODE_NULL && ((pn) & 1) == 0 && PY_PARSE_NODE_STRUCT_KIND((py_parse_node_struct_t*)(pn)) == (k))
+#define MP_PARSE_NODE_IS_NULL(pn) ((pn) == MP_PARSE_NODE_NULL)
+#define MP_PARSE_NODE_IS_LEAF(pn) ((pn) & 1)
+#define MP_PARSE_NODE_IS_STRUCT(pn) ((pn) != MP_PARSE_NODE_NULL && ((pn) & 1) == 0)
+#define MP_PARSE_NODE_IS_STRUCT_KIND(pn, k) ((pn) != MP_PARSE_NODE_NULL && ((pn) & 1) == 0 && MP_PARSE_NODE_STRUCT_KIND((mp_parse_node_struct_t*)(pn)) == (k))
 
-#define PY_PARSE_NODE_IS_ID(pn) (((pn) & 0xf) == PY_PARSE_NODE_ID)
-#define PY_PARSE_NODE_IS_SMALL_INT(pn) (((pn) & 0xf) == PY_PARSE_NODE_SMALL_INT)
-#define PY_PARSE_NODE_IS_TOKEN(pn) (((pn) & 0xf) == PY_PARSE_NODE_TOKEN)
-#define PY_PARSE_NODE_IS_TOKEN_KIND(pn, k) ((pn) == (PY_PARSE_NODE_TOKEN | (k << 4)))
+#define MP_PARSE_NODE_IS_ID(pn) (((pn) & 0xf) == MP_PARSE_NODE_ID)
+#define MP_PARSE_NODE_IS_SMALL_INT(pn) (((pn) & 0xf) == MP_PARSE_NODE_SMALL_INT)
+#define MP_PARSE_NODE_IS_TOKEN(pn) (((pn) & 0xf) == MP_PARSE_NODE_TOKEN)
+#define MP_PARSE_NODE_IS_TOKEN_KIND(pn, k) ((pn) == (MP_PARSE_NODE_TOKEN | (k << 4)))
 
-#define PY_PARSE_NODE_LEAF_KIND(pn) ((pn) & 0xf)
+#define MP_PARSE_NODE_LEAF_KIND(pn) ((pn) & 0xf)
 // TODO should probably have int and uint versions of this macro
-#define PY_PARSE_NODE_LEAF_ARG(pn) (((machine_int_t)(pn)) >> 4)
-#define PY_PARSE_NODE_STRUCT_KIND(pns) ((pns)->kind_num_nodes & 0xff)
-#define PY_PARSE_NODE_STRUCT_NUM_NODES(pns) ((pns)->kind_num_nodes >> 8)
+#define MP_PARSE_NODE_LEAF_ARG(pn) (((machine_int_t)(pn)) >> 4)
+#define MP_PARSE_NODE_STRUCT_KIND(pns) ((pns)->kind_num_nodes & 0xff)
+#define MP_PARSE_NODE_STRUCT_NUM_NODES(pns) ((pns)->kind_num_nodes >> 8)
 
-py_parse_node_t py_parse_node_new_leaf(machine_int_t kind, machine_int_t arg);
+mp_parse_node_t mp_parse_node_new_leaf(machine_int_t kind, machine_int_t arg);
 
-void py_parse_node_show(py_parse_node_t pn, int indent);
+void mp_parse_node_show(mp_parse_node_t pn, int indent);
 
 typedef enum {
-    PY_PARSE_SINGLE_INPUT,
-    PY_PARSE_FILE_INPUT,
-    PY_PARSE_EVAL_INPUT,
-} py_parse_input_kind_t;
+    MP_PARSE_SINGLE_INPUT,
+    MP_PARSE_FILE_INPUT,
+    MP_PARSE_EVAL_INPUT,
+} mp_parse_input_kind_t;
 
-py_parse_node_t py_parse(struct _py_lexer_t *lex, py_parse_input_kind_t input_kind);
+mp_parse_node_t mp_parse(struct _mp_lexer_t *lex, mp_parse_input_kind_t input_kind);
diff --git a/py/repl.c b/py/repl.c
index f295aff23db50a43cfbee66fd83273d2e38b6ed4..ecf6e2d20b910b3effcef55ddc22ec15eabdfca7 100644
--- a/py/repl.c
+++ b/py/repl.c
@@ -11,7 +11,7 @@ bool str_startswith_word(const char *str, const char *head) {
     return head[i] == '\0' && (str[i] == '\0' || !g_unichar_isalpha(str[i]));
 }
 
-bool py_repl_is_compound_stmt(const char *line) {
+bool mp_repl_is_compound_stmt(const char *line) {
     // compound if line starts with a certain keyword
     if (
            str_startswith_word(line, "if")
diff --git a/py/repl.h b/py/repl.h
index 014e8609b4edadff0bdfbe70dce42c5b79f5a8c8..02fe523ed431580d8ad3119847abdc504372610a 100644
--- a/py/repl.h
+++ b/py/repl.h
@@ -1 +1 @@
-bool py_repl_is_compound_stmt(const char *line);
+bool mp_repl_is_compound_stmt(const char *line);
diff --git a/py/runtime.c b/py/runtime.c
index 35f4f2af29f133fd9774d59ecf71ea0ceb353e6c..a3d11f3c6ce58bec277d03e46303088fe30a7270 100644
--- a/py/runtime.c
+++ b/py/runtime.c
@@ -1,23 +1,20 @@
 // in principle, rt_xxx functions are called only by vm/native/viper and make assumptions about args
-// py_xxx functions are safer and can be called by anyone
+// mp_xxx functions are safer and can be called by anyone
 // note that rt_assign_xxx are called only from emit*, and maybe we can rename them to reflect this
 
 #include <stdint.h>
 #include <stdlib.h>
 #include <stdio.h>
-#include <stdarg.h>
 #include <string.h>
 #include <assert.h>
 
 #include "nlr.h"
 #include "misc.h"
-#include "mpyconfig.h"
+#include "mpconfig.h"
+#include "obj.h"
+#include "runtime0.h"
 #include "runtime.h"
-#include "bc.h"
-
 #include "map.h"
-#include "obj.h"
-#include "objprivate.h"
 #include "builtin.h"
 
 #if 0 // print debugging info
@@ -46,203 +43,20 @@ qstr rt_q_TypeError;
 qstr rt_q_SyntaxError;
 qstr rt_q_ValueError;
 
-py_obj_t py_const_none;
-py_obj_t py_const_false;
-py_obj_t py_const_true;
-py_obj_t py_const_stop_iteration;
-
 // locals and globals need to be pointers because they can be the same in outer module scope
-static py_map_t *map_locals;
-static py_map_t *map_globals;
-static py_map_t map_builtins;
-
-py_map_t *rt_get_map_locals(void) {
-    return map_locals;
-}
-
-void rt_set_map_locals(py_map_t *m) {
-    map_locals = m;
-}
-
-static bool fit_small_int(py_small_int_t o) {
-    return true;
-}
-
-int rt_is_true(py_obj_t arg) {
-    DEBUG_OP_printf("is true %p\n", arg);
-    if (IS_SMALL_INT(arg)) {
-        if (FROM_SMALL_INT(arg) == 0) {
-            return 0;
-        } else {
-            return 1;
-        }
-    } else if (arg == py_const_none) {
-        return 0;
-    } else if (arg == py_const_false) {
-        return 0;
-    } else if (arg == py_const_true) {
-        return 1;
-    } else {
-        assert(0);
-        return 0;
-    }
-}
-
-void vstr_printf_wrapper(void *env, const char *fmt, ...) {
-    va_list args;
-    va_start(args, fmt);
-    vstr_vprintf(env, fmt, args);
-    va_end(args);
-}
-
-py_obj_t rt_str_join(py_obj_t self_in, py_obj_t arg) {
-    assert(IS_O(self_in, O_STR));
-    py_obj_base_t *self = self_in;
-    int required_len = strlen(qstr_str(self->u_str));
-
-    // process arg, count required chars
-    if (!IS_O(arg, O_TUPLE) && !IS_O(arg, O_LIST)) {
-        goto bad_arg;
-    }
-    py_obj_base_t *tuple_list = arg;
-    for (int i = 0; i < tuple_list->u_tuple_list.len; i++) {
-        if (!IS_O(tuple_list->u_tuple_list.items[i], O_STR)) {
-            goto bad_arg;
-        }
-        required_len += strlen(qstr_str(((py_obj_base_t*)tuple_list->u_tuple_list.items[i])->u_str));
-    }
-
-    // make joined string
-    char *joined_str = m_new(char, required_len + 1);
-    joined_str[0] = 0;
-    for (int i = 0; i < tuple_list->u_tuple_list.len; i++) {
-        const char *s2 = qstr_str(((py_obj_base_t*)tuple_list->u_tuple_list.items[i])->u_str);
-        if (i > 0) {
-            strcat(joined_str, qstr_str(self->u_str));
-        }
-        strcat(joined_str, s2);
-    }
-    return py_obj_new_str(qstr_from_str_take(joined_str));
-
-bad_arg:
-    nlr_jump(py_obj_new_exception_2(rt_q_TypeError, "?str.join expecting a list of str's", NULL, NULL));
-}
-
-py_obj_t rt_str_format(int n_args, const py_obj_t *args) {
-    assert(IS_O(args[0], O_STR));
-    py_obj_base_t *self = args[0];
-
-    const char *str = qstr_str(self->u_str);
-    int arg_i = 1;
-    vstr_t *vstr = vstr_new();
-    for (; *str; str++) {
-        if (*str == '{') {
-            str++;
-            if (*str == '{') {
-                vstr_add_char(vstr, '{');
-            } else if (*str == '}') {
-                if (arg_i >= n_args) {
-                    nlr_jump(py_obj_new_exception_2(rt_q_IndexError, "tuple index out of range", NULL, NULL));
-                }
-                py_obj_print_helper(vstr_printf_wrapper, vstr, args[arg_i]);
-                arg_i++;
-            }
-        } else {
-            vstr_add_char(vstr, *str);
-        }
-    }
-
-    return py_obj_new_str(qstr_from_str_take(vstr->buf));
-}
-
-uint get_index(py_obj_base_t *base, py_obj_t index) {
-    // assumes base is O_TUPLE or O_LIST
-    // TODO False and True are considered 0 and 1 for indexing purposes
-    int len = base->u_tuple_list.len;
-    if (IS_SMALL_INT(index)) {
-        int i = FROM_SMALL_INT(index);
-        if (i < 0) {
-            i += len;
-        }
-        if (i < 0 || i >= len) {
-            nlr_jump(py_obj_new_exception_2(rt_q_IndexError, "%s index out of range", py_obj_get_type_str(base), NULL));
-        }
-        return i;
-    } else {
-        nlr_jump(py_obj_new_exception_2(rt_q_TypeError, "%s indices must be integers, not %s", py_obj_get_type_str(base), py_obj_get_type_str(index)));
-    }
-}
-
-py_obj_t rt_list_append(py_obj_t self_in, py_obj_t arg) {
-    assert(IS_O(self_in, O_LIST));
-    py_obj_base_t *self = self_in;
-    if (self->u_tuple_list.len >= self->u_tuple_list.alloc) {
-        self->u_tuple_list.alloc *= 2;
-        self->u_tuple_list.items = m_renew(py_obj_t, self->u_tuple_list.items, self->u_tuple_list.alloc);
-    }
-    self->u_tuple_list.items[self->u_tuple_list.len++] = arg;
-    return py_const_none; // return None, as per CPython
-}
-
-py_obj_t rt_list_pop(py_obj_t self_in, py_obj_t arg) {
-    assert(IS_O(self_in, O_LIST));
-    py_obj_base_t *self = self_in;
-    uint index = get_index(self, arg);
-    py_obj_t ret = self->u_tuple_list.items[index];
-    self->u_tuple_list.len -= 1;
-    memcpy(self->u_tuple_list.items + index, self->u_tuple_list.items + index + 1, (self->u_tuple_list.len - index) * sizeof(py_obj_t));
-    return ret;
-}
-
-// TODO make this conform to CPython's definition of sort
-static void py_quicksort(py_obj_t *head, py_obj_t *tail, py_obj_t key_fn) {
-    while (head < tail) {
-        py_obj_t *h = head - 1;
-        py_obj_t *t = tail;
-        py_obj_t v = rt_call_function_1(key_fn, tail[0]); // get pivot using key_fn
-        for (;;) {
-            do ++h; while (rt_compare_op(RT_COMPARE_OP_LESS, rt_call_function_1(key_fn, h[0]), v) == py_const_true);
-            do --t; while (h < t && rt_compare_op(RT_COMPARE_OP_LESS, v, rt_call_function_1(key_fn, t[0])) == py_const_true);
-            if (h >= t) break;
-            py_obj_t x = h[0];
-            h[0] = t[0];
-            t[0] = x;
-        }
-        py_obj_t x = h[0];
-        h[0] = tail[0];
-        tail[0] = x;
-        py_quicksort(head, t, key_fn);
-        head = h + 1;
-    }
-}
-
-py_obj_t rt_list_sort(py_obj_t self_in, py_obj_t key_fn) {
-    assert(IS_O(self_in, O_LIST));
-    py_obj_base_t *self = self_in;
-    if (self->u_tuple_list.len > 1) {
-        py_quicksort(self->u_tuple_list.items, self->u_tuple_list.items + self->u_tuple_list.len - 1, key_fn);
-    }
-    return py_const_none; // return None, as per CPython
-}
-
-py_obj_t rt_gen_instance_next(py_obj_t self_in) {
-    py_obj_t ret = rt_iternext(self_in);
-    if (ret == py_const_stop_iteration) {
-        nlr_jump(py_obj_new_exception_0(qstr_from_str_static("StopIteration")));
-    } else {
-        return ret;
-    }
-}
+static mp_map_t *map_locals;
+static mp_map_t *map_globals;
+static mp_map_t map_builtins;
 
 typedef enum {
-    PY_CODE_NONE,
-    PY_CODE_BYTE,
-    PY_CODE_NATIVE,
-    PY_CODE_INLINE_ASM,
-} py_code_kind_t;
-
-typedef struct _py_code_t {
-    py_code_kind_t kind;
+    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;
     int n_args;
     int n_locals;
     int n_cells;
@@ -254,23 +68,16 @@ typedef struct _py_code_t {
             uint len;
         } u_byte;
         struct {
-            py_fun_t fun;
+            mp_fun_t fun;
         } u_native;
         struct {
             void *fun;
         } u_inline_asm;
     };
-} py_code_t;
+} mp_code_t;
 
 static int next_unique_code_id;
-static py_code_t *unique_codes;
-
-py_obj_t fun_str_join;
-py_obj_t fun_str_format;
-py_obj_t fun_list_append;
-py_obj_t fun_list_pop;
-py_obj_t fun_list_sort;
-py_obj_t fun_gen_instance_next;
+static mp_code_t *unique_codes;
 
 #ifdef WRITE_CODE
 FILE *fp_write_code = NULL;
@@ -292,50 +99,41 @@ void rt_init(void) {
     rt_q_SyntaxError = qstr_from_str_static("SyntaxError");
     rt_q_ValueError = qstr_from_str_static("ValueError");
 
-    py_const_none = py_obj_new_const("None");
-    py_const_false = py_obj_new_const("False");
-    py_const_true = py_obj_new_const("True");
-    py_const_stop_iteration = py_obj_new_const("StopIteration");
-
     // locals = globals for outer module (see Objects/frameobject.c/PyFrame_New())
-    map_locals = map_globals = py_map_new(MAP_QSTR, 1);
-    py_qstr_map_lookup(map_globals, qstr_from_str_static("__name__"), true)->value = py_obj_new_str(qstr_from_str_static("__main__"));
-
-    py_map_init(&map_builtins, MAP_QSTR, 3);
-    py_qstr_map_lookup(&map_builtins, rt_q___build_class__, true)->value = rt_make_function_2(py_builtin___build_class__);
-    py_qstr_map_lookup(&map_builtins, qstr_from_str_static("__repl_print__"), true)->value = rt_make_function_1(py_builtin___repl_print__);
-    py_qstr_map_lookup(&map_builtins, qstr_from_str_static("abs"), true)->value = rt_make_function_1(py_builtin_abs);
-    py_qstr_map_lookup(&map_builtins, qstr_from_str_static("all"), true)->value = rt_make_function_1(py_builtin_all);
-    py_qstr_map_lookup(&map_builtins, qstr_from_str_static("any"), true)->value = rt_make_function_1(py_builtin_any);
-    py_qstr_map_lookup(&map_builtins, qstr_from_str_static("bool"), true)->value = rt_make_function_var(0, py_builtin_bool);
-    py_qstr_map_lookup(&map_builtins, qstr_from_str_static("callable"), true)->value = rt_make_function_1(py_builtin_callable);
-    py_qstr_map_lookup(&map_builtins, qstr_from_str_static("complex"), true)->value = rt_make_function_var(0, py_builtin_complex);
-    py_qstr_map_lookup(&map_builtins, qstr_from_str_static("chr"), true)->value = rt_make_function_1(py_builtin_chr);
-    py_qstr_map_lookup(&map_builtins, qstr_from_str_static("dict"), true)->value = rt_make_function_0(py_builtin_dict);
-    py_qstr_map_lookup(&map_builtins, qstr_from_str_static("divmod"), true)->value = rt_make_function_2(py_builtin_divmod);
-    py_qstr_map_lookup(&map_builtins, qstr_from_str_static("hash"), true)->value = rt_make_function_1(py_builtin_hash);
-    py_qstr_map_lookup(&map_builtins, qstr_from_str_static("iter"), true)->value = rt_make_function_1(py_builtin_iter);
-    py_qstr_map_lookup(&map_builtins, qstr_from_str_static("len"), true)->value = rt_make_function_1(py_builtin_len);
-    py_qstr_map_lookup(&map_builtins, qstr_from_str_static("list"), true)->value = rt_make_function_var(0, py_builtin_list);
-    py_qstr_map_lookup(&map_builtins, qstr_from_str_static("max"), true)->value = rt_make_function_var(1, py_builtin_max);
-    py_qstr_map_lookup(&map_builtins, qstr_from_str_static("min"), true)->value = rt_make_function_var(1, py_builtin_min);
-    py_qstr_map_lookup(&map_builtins, qstr_from_str_static("next"), true)->value = rt_make_function_1(py_builtin_next);
-    py_qstr_map_lookup(&map_builtins, qstr_from_str_static("ord"), true)->value = rt_make_function_1(py_builtin_ord);
-    py_qstr_map_lookup(&map_builtins, qstr_from_str_static("pow"), true)->value = rt_make_function_var(2, py_builtin_pow);
-    py_qstr_map_lookup(&map_builtins, qstr_from_str_static("print"), true)->value = rt_make_function_var(0, py_builtin_print);
-    py_qstr_map_lookup(&map_builtins, qstr_from_str_static("range"), true)->value = rt_make_function_var(1, py_builtin_range);
-    py_qstr_map_lookup(&map_builtins, qstr_from_str_static("sum"), true)->value = rt_make_function_var(1, py_builtin_sum);
+    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__"));
+
+    mp_map_init(&map_builtins, MP_MAP_QSTR, 3);
+    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__);
+    mp_qstr_map_lookup(&map_builtins, qstr_from_str_static("abs"), true)->value = rt_make_function_1(mp_builtin_abs);
+    mp_qstr_map_lookup(&map_builtins, qstr_from_str_static("all"), true)->value = rt_make_function_1(mp_builtin_all);
+    mp_qstr_map_lookup(&map_builtins, qstr_from_str_static("any"), true)->value = rt_make_function_1(mp_builtin_any);
+    mp_qstr_map_lookup(&map_builtins, qstr_from_str_static("bool"), true)->value = rt_make_function_var(0, mp_builtin_bool);
+    mp_qstr_map_lookup(&map_builtins, qstr_from_str_static("callable"), true)->value = rt_make_function_1(mp_builtin_callable);
+#if MICROPY_ENABLE_FLOAT
+    mp_qstr_map_lookup(&map_builtins, qstr_from_str_static("complex"), true)->value = rt_make_function_var(0, mp_builtin_complex);
+#endif
+    mp_qstr_map_lookup(&map_builtins, qstr_from_str_static("chr"), true)->value = rt_make_function_1(mp_builtin_chr);
+    mp_qstr_map_lookup(&map_builtins, qstr_from_str_static("dict"), true)->value = rt_make_function_0(mp_builtin_dict);
+    mp_qstr_map_lookup(&map_builtins, qstr_from_str_static("divmod"), true)->value = rt_make_function_2(mp_builtin_divmod);
+    mp_qstr_map_lookup(&map_builtins, qstr_from_str_static("hash"), true)->value = (mp_obj_t)&mp_builtin_hash_obj;
+    mp_qstr_map_lookup(&map_builtins, qstr_from_str_static("iter"), true)->value = (mp_obj_t)&mp_builtin_iter_obj;
+    mp_qstr_map_lookup(&map_builtins, qstr_from_str_static("len"), true)->value = rt_make_function_1(mp_builtin_len);
+    mp_qstr_map_lookup(&map_builtins, qstr_from_str_static("list"), true)->value = rt_make_function_var(0, mp_builtin_list);
+    mp_qstr_map_lookup(&map_builtins, qstr_from_str_static("max"), true)->value = rt_make_function_var(1, mp_builtin_max);
+    mp_qstr_map_lookup(&map_builtins, qstr_from_str_static("min"), true)->value = rt_make_function_var(1, mp_builtin_min);
+    mp_qstr_map_lookup(&map_builtins, qstr_from_str_static("next"), true)->value = (mp_obj_t)&mp_builtin_next_obj;
+    mp_qstr_map_lookup(&map_builtins, qstr_from_str_static("ord"), true)->value = rt_make_function_1(mp_builtin_ord);
+    mp_qstr_map_lookup(&map_builtins, qstr_from_str_static("pow"), true)->value = rt_make_function_var(2, mp_builtin_pow);
+    mp_qstr_map_lookup(&map_builtins, qstr_from_str_static("print"), true)->value = rt_make_function_var(0, mp_builtin_print);
+    mp_qstr_map_lookup(&map_builtins, qstr_from_str_static("range"), true)->value = rt_make_function_var(1, mp_builtin_range);
+    mp_qstr_map_lookup(&map_builtins, qstr_from_str_static("set"), true)->value = (mp_obj_t)&mp_builtin_set_obj;
+    mp_qstr_map_lookup(&map_builtins, qstr_from_str_static("sum"), true)->value = rt_make_function_var(1, mp_builtin_sum);
 
     next_unique_code_id = 2; // 1 is reserved for the __main__ module scope
     unique_codes = NULL;
 
-    fun_str_join = rt_make_function_2(rt_str_join);
-    fun_str_format = rt_make_function_var(1, rt_str_format);
-    fun_list_append = rt_make_function_2(rt_list_append);
-    fun_list_pop = rt_make_function_2(rt_list_pop);
-    fun_list_sort = rt_make_function_2(rt_list_sort);
-    fun_gen_instance_next = rt_make_function_1(rt_gen_instance_next);
-
 #ifdef WRITE_CODE
     fp_write_code = fopen("out-code", "wb");
 #endif
@@ -359,9 +157,9 @@ int rt_get_unique_code_id(bool is_main_module) {
 
 static void alloc_unique_codes(void) {
     if (unique_codes == NULL) {
-        unique_codes = m_new(py_code_t, next_unique_code_id + 10); // XXX hack until we fix the REPL allocation problem
+        unique_codes = m_new(mp_code_t, next_unique_code_id + 10); // XXX hack until we fix the REPL allocation problem
         for (int i = 0; i < next_unique_code_id; i++) {
-            unique_codes[i].kind = PY_CODE_NONE;
+            unique_codes[i].kind = MP_CODE_NONE;
         }
     }
 }
@@ -370,7 +168,7 @@ void rt_assign_byte_code(int unique_code_id, byte *code, uint len, int n_args, i
     alloc_unique_codes();
 
     assert(unique_code_id < next_unique_code_id);
-    unique_codes[unique_code_id].kind = PY_CODE_BYTE;
+    unique_codes[unique_code_id].kind = MP_CODE_BYTE;
     unique_codes[unique_code_id].n_args = n_args;
     unique_codes[unique_code_id].n_locals = n_locals;
     unique_codes[unique_code_id].n_cells = n_cells;
@@ -390,8 +188,8 @@ void rt_assign_byte_code(int unique_code_id, byte *code, uint len, int n_args, i
         DEBUG_printf(" %02x", code[i]);
     }
     DEBUG_printf("\n");
-    extern void py_show_byte_code(const byte *code, int len);
-    py_show_byte_code(code, len);
+    extern void mp_show_byte_code(const byte *code, int len);
+    mp_show_byte_code(code, len);
 
 #ifdef WRITE_CODE
     if (fp_write_code != NULL) {
@@ -402,11 +200,11 @@ void rt_assign_byte_code(int unique_code_id, byte *code, uint len, int n_args, i
 #endif
 }
 
-void rt_assign_native_code(int unique_code_id, py_fun_t fun, uint len, int n_args) {
+void rt_assign_native_code(int unique_code_id, void *fun, uint len, int n_args) {
     alloc_unique_codes();
 
     assert(1 <= unique_code_id && unique_code_id < next_unique_code_id);
-    unique_codes[unique_code_id].kind = PY_CODE_NATIVE;
+    unique_codes[unique_code_id].kind = MP_CODE_NATIVE;
     unique_codes[unique_code_id].n_args = n_args;
     unique_codes[unique_code_id].n_locals = 0;
     unique_codes[unique_code_id].n_cells = 0;
@@ -436,11 +234,11 @@ void rt_assign_native_code(int unique_code_id, py_fun_t fun, uint len, int n_arg
 #endif
 }
 
-void rt_assign_inline_asm_code(int unique_code_id, py_fun_t fun, uint len, int n_args) {
+void rt_assign_inline_asm_code(int unique_code_id, void *fun, uint len, int n_args) {
     alloc_unique_codes();
 
     assert(1 <= unique_code_id && unique_code_id < next_unique_code_id);
-    unique_codes[unique_code_id].kind = PY_CODE_INLINE_ASM;
+    unique_codes[unique_code_id].kind = MP_CODE_INLINE_ASM;
     unique_codes[unique_code_id].n_args = n_args;
     unique_codes[unique_code_id].n_locals = 0;
     unique_codes[unique_code_id].n_cells = 0;
@@ -467,16 +265,52 @@ void rt_assign_inline_asm_code(int unique_code_id, py_fun_t fun, uint len, int n
 #endif
 }
 
+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);
+}
+
 #define PARSE_DEC_IN_INTG (1)
 #define PARSE_DEC_IN_FRAC (2)
 #define PARSE_DEC_IN_EXP  (3)
 
-py_obj_t rt_load_const_dec(qstr qstr) {
+mp_obj_t rt_load_const_dec(qstr qstr) {
 #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;
-    py_float_t dec_val = 0;
+    mp_float_t dec_val = 0;
     bool exp_neg = false;
     int exp_val = 0;
     int exp_extra = 0;
@@ -513,7 +347,7 @@ py_obj_t rt_load_const_dec(qstr qstr) {
         }
     }
     if (*s != 0) {
-        nlr_jump(py_obj_new_exception_2(rt_q_SyntaxError, "invalid syntax for number", NULL, NULL));
+        nlr_jump(mp_obj_new_exception_msg(rt_q_SyntaxError, "invalid syntax for number"));
     }
     if (exp_neg) {
         exp_val = -exp_val;
@@ -526,52 +360,52 @@ py_obj_t rt_load_const_dec(qstr qstr) {
         dec_val *= 0.1;
     }
     if (imag) {
-        return py_obj_new_complex(0, dec_val);
+        return mp_obj_new_complex(0, dec_val);
     } else {
-        return py_obj_new_float(dec_val);
+        return mp_obj_new_float(dec_val);
     }
 #else
-    nlr_jump(py_obj_new_exception_2(rt_q_SyntaxError, "decimal numbers not supported", NULL, NULL));
+    nlr_jump(mp_obj_new_exception_msg(rt_q_SyntaxError, "decimal numbers not supported"));
 #endif
 }
 
-py_obj_t rt_load_const_str(qstr qstr) {
+mp_obj_t rt_load_const_str(qstr qstr) {
     DEBUG_OP_printf("load '%s'\n", qstr_str(qstr));
-    return py_obj_new_str(qstr);
+    return mp_obj_new_str(qstr);
 }
 
-py_obj_t rt_load_name(qstr qstr) {
+mp_obj_t rt_load_name(qstr qstr) {
     // logic: search locals, globals, builtins
     DEBUG_OP_printf("load name %s\n", qstr_str(qstr));
-    py_map_elem_t *elem = py_qstr_map_lookup(map_locals, qstr, false);
+    mp_map_elem_t *elem = mp_qstr_map_lookup(map_locals, qstr, false);
     if (elem == NULL) {
-        elem = py_qstr_map_lookup(map_globals, qstr, false);
+        elem = mp_qstr_map_lookup(map_globals, qstr, false);
         if (elem == NULL) {
-            elem = py_qstr_map_lookup(&map_builtins, qstr, false);
+            elem = mp_qstr_map_lookup(&map_builtins, qstr, false);
             if (elem == NULL) {
-                nlr_jump(py_obj_new_exception_2(rt_q_NameError, "name '%s' is not defined", qstr_str(qstr), NULL));
+                nlr_jump(mp_obj_new_exception_msg_1_arg(rt_q_NameError, "name '%s' is not defined", qstr_str(qstr)));
             }
         }
     }
     return elem->value;
 }
 
-py_obj_t rt_load_global(qstr qstr) {
+mp_obj_t rt_load_global(qstr qstr) {
     // logic: search globals, builtins
     DEBUG_OP_printf("load global %s\n", qstr_str(qstr));
-    py_map_elem_t *elem = py_qstr_map_lookup(map_globals, qstr, false);
+    mp_map_elem_t *elem = mp_qstr_map_lookup(map_globals, qstr, false);
     if (elem == NULL) {
-        elem = py_qstr_map_lookup(&map_builtins, qstr, false);
+        elem = mp_qstr_map_lookup(&map_builtins, qstr, false);
         if (elem == NULL) {
-            nlr_jump(py_obj_new_exception_2(rt_q_NameError, "name '%s' is not defined", qstr_str(qstr), NULL));
+            nlr_jump(mp_obj_new_exception_msg_1_arg(rt_q_NameError, "name '%s' is not defined", qstr_str(qstr)));
         }
     }
     return elem->value;
 }
 
-py_obj_t rt_load_build_class(void) {
+mp_obj_t rt_load_build_class(void) {
     DEBUG_OP_printf("load_build_class\n");
-    py_map_elem_t *elem = py_qstr_map_lookup(&map_builtins, rt_q___build_class__, false);
+    mp_map_elem_t *elem = mp_qstr_map_lookup(&map_builtins, rt_q___build_class__, false);
     if (elem == NULL) {
         printf("name doesn't exist: __build_class__\n");
         assert(0);
@@ -579,97 +413,60 @@ py_obj_t rt_load_build_class(void) {
     return elem->value;
 }
 
-py_obj_t rt_new_cell(py_obj_t val) {
-    py_obj_base_t *o = m_new(py_obj_base_t, 1);
-    o->kind = O_CELL;
-    o->u_cell = val;
-    return (py_obj_t)o;
-}
-
-py_obj_t rt_get_cell(py_obj_t cell) {
-    if (IS_O(cell, O_CELL)) {
-        return ((py_obj_base_t*)cell)->u_cell;
-    } else {
-        assert(0);
-        return py_const_none;
-    }
+mp_obj_t rt_get_cell(mp_obj_t cell) {
+    return mp_obj_cell_get(cell);
 }
 
-void rt_set_cell(py_obj_t cell, py_obj_t val) {
-    if (IS_O(cell, O_CELL)) {
-        ((py_obj_base_t*)cell)->u_cell = val;
-    } else {
-        assert(0);
-    }
+void rt_set_cell(mp_obj_t cell, mp_obj_t val) {
+    mp_obj_cell_set(cell, val);
 }
 
-void rt_store_name(qstr qstr, py_obj_t obj) {
+void rt_store_name(qstr qstr, mp_obj_t obj) {
     DEBUG_OP_printf("store name %s <- %p\n", qstr_str(qstr), obj);
-    py_qstr_map_lookup(map_locals, qstr, true)->value = obj;
+    mp_qstr_map_lookup(map_locals, qstr, true)->value = obj;
 }
 
-void rt_store_global(qstr qstr, py_obj_t obj) {
+void rt_store_global(qstr qstr, mp_obj_t obj) {
     DEBUG_OP_printf("store global %s <- %p\n", qstr_str(qstr), obj);
-    py_qstr_map_lookup(map_globals, qstr, true)->value = obj;
+    mp_qstr_map_lookup(map_globals, qstr, true)->value = obj;
 }
 
-py_obj_t rt_unary_op(int op, py_obj_t arg) {
+mp_obj_t rt_unary_op(int op, mp_obj_t arg) {
     DEBUG_OP_printf("unary %d %p\n", op, arg);
-    if (IS_SMALL_INT(arg)) {
-        py_small_int_t val = FROM_SMALL_INT(arg);
+    if (MP_OBJ_IS_SMALL_INT(arg)) {
+        mp_small_int_t val = MP_OBJ_SMALL_INT_VALUE(arg);
         switch (op) {
-            case RT_UNARY_OP_NOT: if (val != 0) { return py_const_true;} else { return py_const_false; }
+            case RT_UNARY_OP_NOT: if (val != 0) { return mp_const_true;} else { return mp_const_false; }
             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)) {
-            return TO_SMALL_INT(val);
-        }
-#if MICROPY_ENABLE_FLOAT
-    } else if (IS_O(arg, O_FLOAT)) {
-        py_float_t val = py_obj_get_float(arg);
-        switch (op) {
-            case RT_UNARY_OP_NOT: if (val != 0) { return py_const_true;} else { return py_const_false; }
-            case RT_UNARY_OP_POSITIVE: break;
-            case RT_UNARY_OP_NEGATIVE: val = -val; break;
-            case RT_UNARY_OP_INVERT: nlr_jump(py_obj_new_exception_2(rt_q_TypeError, "bad operand type for unary ~: 'float'", NULL, NULL));
-            default: assert(0); val = 0;
+            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;
+            }
         }
-        return py_obj_new_float(val);
-#endif
+        // 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));
     }
-    assert(0);
-    return py_const_none;
 }
 
-py_obj_t rt_binary_op(int op, py_obj_t lhs, py_obj_t rhs) {
+mp_obj_t rt_binary_op(int op, mp_obj_t lhs, mp_obj_t rhs) {
     DEBUG_OP_printf("binary %d %p %p\n", op, lhs, rhs);
-    if (op == RT_BINARY_OP_SUBSCR) {
-        if (IS_O(lhs, O_STR)) {
-            // string access
-            // XXX a hack!
-            const char *str = qstr_str(((py_obj_base_t*)lhs)->u_str);
-            return py_obj_new_int(str[FROM_SMALL_INT(rhs)]);
-        } else if ((IS_O(lhs, O_TUPLE) || IS_O(lhs, O_LIST))) {
-            // tuple/list load
-            uint index = get_index(lhs, rhs);
-            return ((py_obj_base_t*)lhs)->u_tuple_list.items[index];
-        } else if (IS_O(lhs, O_MAP)) {
-            // map load
-            py_map_elem_t *elem = py_map_lookup(lhs, rhs, false);
-            if (elem == NULL) {
-                nlr_jump(py_obj_new_exception_2(rt_q_KeyError, "<value>", NULL, NULL));
-            } else {
-                return elem->value;
-            }
-        } else {
-            assert(0);
-        }
-    } else if (IS_SMALL_INT(lhs) && IS_SMALL_INT(rhs)) {
-        py_small_int_t lhs_val = FROM_SMALL_INT(lhs);
-        py_small_int_t rhs_val = FROM_SMALL_INT(rhs);
+    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);
         switch (op) {
             case RT_BINARY_OP_OR:
             case RT_BINARY_OP_INPLACE_OR: lhs_val |= rhs_val; break;
@@ -691,7 +488,7 @@ py_obj_t rt_binary_op(int op, py_obj_t lhs, py_obj_t rhs) {
             case RT_BINARY_OP_INPLACE_FLOOR_DIVIDE: lhs_val /= rhs_val; break;
 #if MICROPY_ENABLE_FLOAT
             case RT_BINARY_OP_TRUE_DIVIDE:
-            case RT_BINARY_OP_INPLACE_TRUE_DIVIDE: return py_obj_new_float((py_float_t)lhs_val / (py_float_t)rhs_val);
+            case RT_BINARY_OP_INPLACE_TRUE_DIVIDE: return mp_obj_new_float((mp_float_t)lhs_val / (mp_float_t)rhs_val);
 #endif
 
             // TODO implement modulo as specified by Python
@@ -717,102 +514,46 @@ py_obj_t rt_binary_op(int op, py_obj_t lhs, py_obj_t rhs) {
             default: printf("%d\n", op); assert(0);
         }
         if (fit_small_int(lhs_val)) {
-            return TO_SMALL_INT(lhs_val);
-        }
-#if MICROPY_ENABLE_FLOAT
-    } else if (IS_O(lhs, O_COMPLEX) || IS_O(rhs, O_COMPLEX)) {
-        py_float_t lhs_real, lhs_imag, rhs_real, rhs_imag;
-        py_obj_get_complex(lhs, &lhs_real, &lhs_imag);
-        py_obj_get_complex(rhs, &rhs_real, &rhs_imag);
-        switch (op) {
-            case RT_BINARY_OP_ADD:
-            case RT_BINARY_OP_INPLACE_ADD:
-                lhs_real += rhs_real;
-                lhs_imag += rhs_imag;
-                break;
-            case RT_BINARY_OP_SUBTRACT:
-            case RT_BINARY_OP_INPLACE_SUBTRACT:
-                lhs_real -= rhs_real;
-                lhs_imag -= rhs_imag;
-                break;
-            case RT_BINARY_OP_MULTIPLY:
-            case RT_BINARY_OP_INPLACE_MULTIPLY:
-            {
-                py_float_t real = lhs_real * rhs_real - lhs_imag * rhs_imag;
-                lhs_imag = lhs_real * rhs_imag + lhs_imag * rhs_real;
-                lhs_real = real;
-                break;
+            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;
             }
-            /* TODO floor(?) the value
-            case RT_BINARY_OP_FLOOR_DIVIDE:
-            case RT_BINARY_OP_INPLACE_FLOOR_DIVIDE: val = lhs_val / rhs_val; break;
-            */
-            /* TODO
-            case RT_BINARY_OP_TRUE_DIVIDE:
-            case RT_BINARY_OP_INPLACE_TRUE_DIVIDE: val = lhs_val / rhs_val; break;
-            */
-            default: printf("%d\n", op); assert(0);
-        }
-        return py_obj_new_complex(lhs_real, lhs_imag);
-    } else if (IS_O(lhs, O_FLOAT) || IS_O(rhs, O_FLOAT)) {
-        py_float_t lhs_val = py_obj_get_float(lhs);
-        py_float_t rhs_val = py_obj_get_float(rhs);
-        switch (op) {
-            case RT_BINARY_OP_ADD:
-            case RT_BINARY_OP_INPLACE_ADD: lhs_val += rhs_val; break;
-            case RT_BINARY_OP_SUBTRACT:
-            case RT_BINARY_OP_INPLACE_SUBTRACT: lhs_val -= rhs_val; break;
-            case RT_BINARY_OP_MULTIPLY:
-            case RT_BINARY_OP_INPLACE_MULTIPLY: lhs_val *= rhs_val; break;
-            /* TODO floor(?) the value
-            case RT_BINARY_OP_FLOOR_DIVIDE:
-            case RT_BINARY_OP_INPLACE_FLOOR_DIVIDE: val = lhs_val / rhs_val; break;
-            */
-            case RT_BINARY_OP_TRUE_DIVIDE:
-            case RT_BINARY_OP_INPLACE_TRUE_DIVIDE: lhs_val /= rhs_val; break;
-            default: printf("%d\n", op); assert(0);
-        }
-        return py_obj_new_float(lhs_val);
-#endif
-    } else if (IS_O(lhs, O_STR) && IS_O(rhs, O_STR)) {
-        const char *lhs_str = qstr_str(((py_obj_base_t*)lhs)->u_str);
-        const char *rhs_str = qstr_str(((py_obj_base_t*)rhs)->u_str);
-        char *val;
-        switch (op) {
-            case RT_BINARY_OP_ADD:
-            case RT_BINARY_OP_INPLACE_ADD: val = m_new(char, strlen(lhs_str) + strlen(rhs_str) + 1); strcpy(val, lhs_str); strcat(val, rhs_str); break;
-            default: printf("%d\n", op); assert(0); val = NULL;
         }
-        return py_obj_new_str(qstr_from_str_take(val));
     }
-    assert(0);
-    return py_const_none;
+
+    // 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)));
 }
 
-py_obj_t rt_compare_op(int op, py_obj_t lhs, py_obj_t rhs) {
+mp_obj_t rt_compare_op(int op, mp_obj_t lhs, mp_obj_t rhs) {
     DEBUG_OP_printf("compare %d %p %p\n", op, lhs, rhs);
 
     // deal with == and !=
     if (op == RT_COMPARE_OP_EQUAL || op == RT_COMPARE_OP_NOT_EQUAL) {
-        if (py_obj_equal(lhs, rhs)) {
+        if (mp_obj_equal(lhs, rhs)) {
             if (op == RT_COMPARE_OP_EQUAL) {
-                return py_const_true;
+                return mp_const_true;
             } else {
-                return py_const_false;
+                return mp_const_false;
             }
         } else {
             if (op == RT_COMPARE_OP_EQUAL) {
-                return py_const_false;
+                return mp_const_false;
             } else {
-                return py_const_true;
+                return mp_const_true;
             }
         }
     }
 
     // deal with small ints
-    if (IS_SMALL_INT(lhs) && IS_SMALL_INT(rhs)) {
-        py_small_int_t lhs_val = FROM_SMALL_INT(lhs);
-        py_small_int_t rhs_val = FROM_SMALL_INT(rhs);
+    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);
         int cmp;
         switch (op) {
             case RT_COMPARE_OP_LESS: cmp = lhs_val < rhs_val; break;
@@ -822,17 +563,17 @@ py_obj_t rt_compare_op(int op, py_obj_t lhs, py_obj_t rhs) {
             default: assert(0); cmp = 0;
         }
         if (cmp) {
-            return py_const_true;
+            return mp_const_true;
         } else {
-            return py_const_false;
+            return mp_const_false;
         }
     }
 
 #if MICROPY_ENABLE_FLOAT
     // deal with floats
-    if (IS_O(lhs, O_FLOAT) || IS_O(rhs, O_FLOAT)) {
-        py_float_t lhs_val = py_obj_get_float(lhs);
-        py_float_t rhs_val = py_obj_get_float(rhs);
+    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);
         int cmp;
         switch (op) {
             case RT_COMPARE_OP_LESS: cmp = lhs_val < rhs_val; break;
@@ -842,666 +583,287 @@ py_obj_t rt_compare_op(int op, py_obj_t lhs, py_obj_t rhs) {
             default: assert(0); cmp = 0;
         }
         if (cmp) {
-            return py_const_true;
+            return mp_const_true;
         } else {
-            return py_const_false;
+            return mp_const_false;
         }
     }
 #endif
 
     // not implemented
     assert(0);
-    return py_const_none;
+    return mp_const_none;
 }
 
-py_obj_t rt_make_function_from_id(int unique_code_id) {
+mp_obj_t rt_make_function_from_id(int unique_code_id) {
     DEBUG_OP_printf("make_function_from_id %d\n", unique_code_id);
     if (unique_code_id < 1 || unique_code_id >= next_unique_code_id) {
         // illegal code id
-        return py_const_none;
+        return mp_const_none;
     }
-    py_code_t *c = &unique_codes[unique_code_id];
-    py_obj_base_t *o = m_new(py_obj_base_t, 1);
+
+    // make the function, depending on the code kind
+    mp_code_t *c = &unique_codes[unique_code_id];
+    mp_obj_t fun;
     switch (c->kind) {
-        case PY_CODE_BYTE:
-            o->kind = O_FUN_BC;
-            o->u_fun_bc.n_args = c->n_args;
-            o->u_fun_bc.n_state = c->n_locals + c->n_cells + c->n_stack;
-            o->u_fun_bc.code = c->u_byte.code;
+        case MP_CODE_BYTE:
+            fun = mp_obj_new_fun_bc(c->n_args, c->n_locals + c->n_cells + c->n_stack, c->u_byte.code);
             break;
-        case PY_CODE_NATIVE:
+        case MP_CODE_NATIVE:
             switch (c->n_args) {
-                case 0: o->kind = O_FUN_0; break;
-                case 1: o->kind = O_FUN_1; break;
-                case 2: o->kind = O_FUN_2; break;
-                default: assert(0);
+                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;
             }
-            o->u_fun.fun = c->u_native.fun;
             break;
-        case PY_CODE_INLINE_ASM:
-            o->kind = O_FUN_ASM;
-            o->u_fun_asm.n_args = c->n_args;
-            o->u_fun_asm.fun = c->u_inline_asm.fun;
+        case MP_CODE_INLINE_ASM:
+            fun = mp_obj_new_fun_asm(c->n_args, c->u_inline_asm.fun);
             break;
         default:
             assert(0);
+            fun = mp_const_none;
     }
 
     // check for generator functions and if so wrap in generator object
     if (c->is_generator) {
-        py_obj_base_t *o2 = m_new(py_obj_base_t, 1);
-        o2->kind = O_GEN_WRAP;
-        // we have at least 3 locals so the bc can write back fast[0,1,2] safely; should improve how this is done
-        o2->u_gen_wrap.n_state = ((c->n_locals + c->n_cells) < 3 ? 3 : (c->n_locals + c->n_cells)) + c->n_stack;
-        o2->u_gen_wrap.fun = o;
-        o = o2;
+        fun = mp_obj_new_gen_wrap(c->n_locals, c->n_cells, c->n_stack, fun);
     }
 
-    return o;
-}
-
-py_obj_t rt_make_function_0(py_fun_0_t fun) {
-    py_obj_base_t *o = m_new(py_obj_base_t, 1);
-    o->kind = O_FUN_0;
-    o->u_fun.fun = fun;
-    return o;
+    return fun;
 }
 
-py_obj_t rt_make_function_1(py_fun_1_t fun) {
-    py_obj_base_t *o = m_new(py_obj_base_t, 1);
-    o->kind = O_FUN_1;
-    o->u_fun.fun = fun;
-    return o;
-}
-
-py_obj_t rt_make_function_2(py_fun_2_t fun) {
-    py_obj_base_t *o = m_new(py_obj_base_t, 1);
-    o->kind = O_FUN_2;
-    o->u_fun.fun = fun;
-    return o;
-}
-
-py_obj_t rt_make_function(int n_args, py_fun_t code) {
-    // assumes code is a pointer to a py_fun_t (i think this is safe...)
-    py_obj_base_t *o = m_new(py_obj_base_t, 1);
-    o->kind = O_FUN_N;
-    o->u_fun.n_args = n_args;
-    o->u_fun.fun = code;
-    return o;
-}
-
-py_obj_t rt_make_function_var(int n_fixed_args, py_fun_var_t f) {
-    py_obj_base_t *o = m_new(py_obj_base_t, 1);
-    o->kind = O_FUN_VAR;
-    o->u_fun.n_args = n_fixed_args;
-    o->u_fun.fun = f;
-    return o;
-}
-
-py_obj_t rt_make_closure_from_id(int unique_code_id, py_obj_t closure_tuple) {
-    py_obj_t f = rt_make_function_from_id(unique_code_id);
+mp_obj_t rt_make_closure_from_id(int unique_code_id, mp_obj_t closure_tuple) {
+    // make function object
+    mp_obj_t ffun = rt_make_function_from_id(unique_code_id);
     // wrap function in closure object
-    py_obj_base_t *f2 = m_new(py_obj_base_t, 1);
-    f2->kind = O_CLOSURE;
-    f2->u_closure.fun = f;
-    f2->u_closure.vars = closure_tuple;
-    return f2;
+    return mp_obj_new_closure(ffun, closure_tuple);
 }
 
-py_obj_t rt_call_function_0(py_obj_t fun) {
+mp_obj_t rt_call_function_0(mp_obj_t fun) {
     return rt_call_function_n(fun, 0, NULL);
 }
 
-py_obj_t rt_call_function_1(py_obj_t fun, py_obj_t arg) {
+mp_obj_t rt_call_function_1(mp_obj_t fun, mp_obj_t arg) {
     return rt_call_function_n(fun, 1, &arg);
 }
 
-py_obj_t rt_call_function_2(py_obj_t fun, py_obj_t arg1, py_obj_t arg2) {
-    py_obj_t args[2];
+mp_obj_t rt_call_function_2(mp_obj_t fun, mp_obj_t arg1, mp_obj_t arg2) {
+    mp_obj_t args[2];
     args[1] = arg1;
     args[0] = arg2;
     return rt_call_function_n(fun, 2, args);
 }
 
-typedef machine_uint_t (*inline_asm_fun_0_t)();
-typedef machine_uint_t (*inline_asm_fun_1_t)(machine_uint_t);
-typedef machine_uint_t (*inline_asm_fun_2_t)(machine_uint_t, machine_uint_t);
-typedef machine_uint_t (*inline_asm_fun_3_t)(machine_uint_t, machine_uint_t, machine_uint_t);
-
-// convert a Python object to a sensible value for inline asm
-machine_uint_t rt_convert_obj_for_inline_asm(py_obj_t obj) {
-    // TODO for byte_array, pass pointer to the array
-    if (IS_SMALL_INT(obj)) {
-        return FROM_SMALL_INT(obj);
-    } else if (obj == py_const_none) {
-        return 0;
-    } else if (obj == py_const_false) {
-        return 0;
-    } else if (obj == py_const_true) {
-        return 1;
-    } else {
-        py_obj_base_t *o = obj;
-        switch (o->kind) {
-            case O_STR:
-                // pointer to the string (it's probably constant though!)
-                return (machine_uint_t)qstr_str(o->u_str);
-
-#if MICROPY_ENABLE_FLOAT
-            case O_FLOAT:
-                // convert float to int (could also pass in float registers)
-                return (machine_int_t)o->u_float;
-#endif
-
-            case O_TUPLE:
-            case O_LIST:
-                // pointer to start of tuple/list (could pass length, but then could use len(x) for that)
-                return (machine_uint_t)o->u_tuple_list.items;
-
-            default:
-                // just pass along a pointer to the object
-                return (machine_uint_t)obj;
-        }
-    }
-}
-
-// convert a return value from inline asm to a sensible Python object
-py_obj_t rt_convert_val_from_inline_asm(machine_uint_t val) {
-    return TO_SMALL_INT(val);
-}
-
 // args are in reverse order in the array
-py_obj_t rt_call_function_n(py_obj_t fun, int n_args, const py_obj_t *args) {
-    int n_args_fun = 0;
-    if (IS_O(fun, O_FUN_0)) {
-        py_obj_base_t *o = fun;
-        if (n_args != 0) {
-            n_args_fun = 0;
-            goto bad_n_args;
-        }
-        DEBUG_OP_printf("calling native %p()\n", o->u_fun.fun);
-        return ((py_fun_0_t)o->u_fun.fun)();
-
-    } else if (IS_O(fun, O_FUN_1)) {
-        py_obj_base_t *o = fun;
-        if (n_args != 1) {
-            n_args_fun = 1;
-            goto bad_n_args;
-        }
-        DEBUG_OP_printf("calling native %p(%p)\n", o->u_fun.fun, args[0]);
-        return ((py_fun_1_t)o->u_fun.fun)(args[0]);
-
-    } else if (IS_O(fun, O_FUN_2)) {
-        py_obj_base_t *o = fun;
-        if (n_args != 2) {
-            n_args_fun = 2;
-            goto bad_n_args;
-        }
-        DEBUG_OP_printf("calling native %p(%p, %p)\n", o->u_fun.fun, args[1], args[0]);
-        return ((py_fun_2_t)o->u_fun.fun)(args[1], args[0]);
-
-    // TODO O_FUN_N
-
-    } else if (IS_O(fun, O_FUN_VAR)) {
-        py_obj_base_t *o = fun;
-        if (n_args < o->u_fun.n_args) {
-            nlr_jump(py_obj_new_exception_2(rt_q_TypeError, "<fun name>() missing %d required positional arguments: <list of names of params>", (const char*)(machine_int_t)(o->u_fun.n_args - n_args), NULL));
-        }
-        // TODO really the args need to be passed in as a Python tuple, as the form f(*[1,2]) can be used to pass var args
-        py_obj_t *args_ordered = m_new(py_obj_t, n_args);
-        for (int i = 0; i < n_args; i++) {
-            args_ordered[i] = args[n_args - i - 1];
-        }
-        py_obj_t res = ((py_fun_var_t)o->u_fun.fun)(n_args, args_ordered);
-        m_free(args_ordered);
-        return res;
-
-    } else if (IS_O(fun, O_FUN_BC)) {
-        py_obj_base_t *o = fun;
-        if (n_args != o->u_fun_bc.n_args) {
-            n_args_fun = o->u_fun_bc.n_args;
-            goto bad_n_args;
-        }
-        DEBUG_OP_printf("calling byte code %p(n_args=%d)\n", o->u_fun_bc.code, n_args);
-        return py_execute_byte_code(o->u_fun_bc.code, args, n_args, o->u_fun_bc.n_state);
-
-    } else if (IS_O(fun, O_FUN_ASM)) {
-        py_obj_base_t *o = fun;
-        if (n_args != o->u_fun_asm.n_args) {
-            n_args_fun = o->u_fun_asm.n_args;
-            goto bad_n_args;
-        }
-        DEBUG_OP_printf("calling inline asm %p(n_args=%d)\n", o->u_fun_asm.fun, n_args);
-        machine_uint_t ret;
-        if (n_args == 0) {
-            ret = ((inline_asm_fun_0_t)o->u_fun_asm.fun)();
-        } else if (n_args == 1) {
-            ret = ((inline_asm_fun_1_t)o->u_fun_asm.fun)(rt_convert_obj_for_inline_asm(args[0]));
-        } else if (n_args == 2) {
-            ret = ((inline_asm_fun_2_t)o->u_fun_asm.fun)(rt_convert_obj_for_inline_asm(args[1]), rt_convert_obj_for_inline_asm(args[0]));
-        } else if (n_args == 3) {
-            ret = ((inline_asm_fun_3_t)o->u_fun_asm.fun)(rt_convert_obj_for_inline_asm(args[2]), rt_convert_obj_for_inline_asm(args[1]), rt_convert_obj_for_inline_asm(args[0]));
-        } else {
-            assert(0);
-            ret = 0;
-        }
-        return rt_convert_val_from_inline_asm(ret);
-
-    } else if (IS_O(fun, O_GEN_WRAP)) {
-        py_obj_base_t *o = fun;
-        py_obj_base_t *o_fun = o->u_gen_wrap.fun;
-        assert(o_fun->kind == O_FUN_BC); // TODO
-        if (n_args != o_fun->u_fun_bc.n_args) {
-            n_args_fun = o_fun->u_fun_bc.n_args;
-            goto bad_n_args;
-        }
-        py_obj_t *state = m_new(py_obj_t, 1 + o->u_gen_wrap.n_state);
-        // put function object at first slot in state (to keep u_gen_instance small)
-        state[0] = o_fun;
-        // init args
-        for (int i = 0; i < n_args; i++) {
-            state[1 + i] = args[n_args - 1 - i];
-        }
-        py_obj_base_t *o2 = m_new(py_obj_base_t, 1);
-        o2->kind = O_GEN_INSTANCE;
-        o2->u_gen_instance.state = state;
-        o2->u_gen_instance.ip = o_fun->u_fun_bc.code;
-        o2->u_gen_instance.sp = state + o->u_gen_wrap.n_state;
-        return o2;
-
-    } else if (IS_O(fun, O_BOUND_METH)) {
-        py_obj_base_t *o = fun;
-        DEBUG_OP_printf("calling bound method %p(self=%p, n_args=%d)\n", o->u_bound_meth.meth, o->u_bound_meth.self, n_args);
-        if (n_args == 0) {
-            return rt_call_function_n(o->u_bound_meth.meth, 1, &o->u_bound_meth.self);
-        } else if (n_args == 1) {
-            py_obj_t args2[2];
-            args2[1] = o->u_bound_meth.self;
-            args2[0] = args[0];
-            return rt_call_function_n(o->u_bound_meth.meth, 2, args2);
-        } else {
-            // TODO not implemented
-            assert(0);
-            return py_const_none;
-            //return rt_call_function_2(o->u_bound_meth.meth, n_args + 1, o->u_bound_meth.self + args);
-        }
+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
 
-    } else if (IS_O(fun, O_CLASS)) {
-        // instantiate an instance of a class
-
-        DEBUG_OP_printf("instantiate object of class %p with %d args\n", fun, n_args);
-        py_obj_base_t *o = m_new(py_obj_base_t, 1);
-        o->kind = O_OBJ;
-        o->u_obj.class = fun;
-        o->u_obj.members = py_map_new(MAP_QSTR, 0);
-
-        // look for __init__ function
-        py_obj_base_t *o_class = fun;
-        py_map_elem_t *init_fn = py_qstr_map_lookup(o_class->u_class.locals, qstr_from_str_static("__init__"), false);
-
-        if (init_fn != NULL) {
-            // call __init__ function
-            py_obj_t init_ret;
-            if (n_args == 0) {
-                init_ret = rt_call_function_n(init_fn->value, 1, (py_obj_t*)&o);
-            } else {
-                py_obj_t *args2 = m_new(py_obj_t, n_args + 1);
-                memcpy(args2, args, n_args * sizeof(py_obj_t));
-                args2[n_args] = o;
-                init_ret = rt_call_function_n(init_fn->value, n_args + 1, args2);
-                m_free(args2);
-            }
-            if (init_ret != py_const_none) {
-                nlr_jump(py_obj_new_exception_2(rt_q_TypeError, "__init__() should return None, not '%s'", py_obj_get_type_str(init_ret), NULL));
-            }
+    DEBUG_OP_printf("calling function %p(n_args=%d, args=%p)\n", fun_in, n_args, args);
 
+    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);
         } else {
-            // TODO
-            if (n_args != 0) {
-                n_args_fun = 0;
-                goto bad_n_args;
-            }
+            nlr_jump(mp_obj_new_exception_msg_1_arg(rt_q_TypeError, "'%s' object is not callable", fun->type->name));
         }
-
-        return o;
-
-    } else {
-        printf("fun %p %d\n", fun, ((py_obj_base_t*)fun)->kind);
-        assert(0);
-        return py_const_none;
     }
-
-bad_n_args:
-    nlr_jump(py_obj_new_exception_2(rt_q_TypeError, "function takes %d positional arguments but %d were given", (const char*)(machine_int_t)n_args_fun, (const char*)(machine_int_t)n_args));
 }
 
 // args are in reverse order in the array; keyword arguments come first, value then key
 // eg: (value1, key1, value0, key0, arg1, arg0)
-py_obj_t rt_call_function_n_kw(py_obj_t fun, uint n_args, uint n_kw, const py_obj_t *args) {
+mp_obj_t rt_call_function_n_kw(mp_obj_t fun, uint n_args, uint n_kw, const mp_obj_t *args) {
     // TODO
     assert(0);
-    return py_const_none;
+    return mp_const_none;
 }
 
 // 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
-py_obj_t rt_call_method_n(uint n_args, const py_obj_t *args) {
+mp_obj_t rt_call_method_n(uint n_args, const mp_obj_t *args) {
     DEBUG_OP_printf("call method %p(self=%p, n_args=%u)\n", args[n_args + 1], args[n_args], n_args);
     return rt_call_function_n(args[n_args + 1], n_args + ((args[n_args] == NULL) ? 0 : 1), args);
 }
 
 // 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
-py_obj_t rt_call_method_n_kw(uint n_args, uint n_kw, const py_obj_t *args) {
+mp_obj_t rt_call_method_n_kw(uint n_args, uint n_kw, const mp_obj_t *args) {
     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);
 }
 
 // items are in reverse order
-py_obj_t rt_build_tuple(int n_args, py_obj_t *items) {
-    py_obj_base_t *o = m_new(py_obj_base_t, 1);
-    o->kind = O_TUPLE;
-    o->u_tuple_list.alloc = n_args < 4 ? 4 : n_args;
-    o->u_tuple_list.len = n_args;
-    o->u_tuple_list.items = m_new(py_obj_t, o->u_tuple_list.alloc);
-    for (int i = 0; i < n_args; i++) {
-        o->u_tuple_list.items[i] = items[n_args - i - 1];
-    }
-    return o;
+mp_obj_t rt_build_tuple(int n_args, mp_obj_t *items) {
+    return mp_obj_new_tuple_reverse(n_args, items);
 }
 
 // items are in reverse order
-py_obj_t rt_build_list(int n_args, py_obj_t *items) {
-    py_obj_base_t *o = m_new(py_obj_base_t, 1);
-    o->kind = O_LIST;
-    o->u_tuple_list.alloc = n_args < 4 ? 4 : n_args;
-    o->u_tuple_list.len = n_args;
-    o->u_tuple_list.items = m_new(py_obj_t, o->u_tuple_list.alloc);
-    for (int i = 0; i < n_args; i++) {
-        o->u_tuple_list.items[i] = items[n_args - i - 1];
-    }
-    return o;
+mp_obj_t rt_build_list(int n_args, mp_obj_t *items) {
+    return mp_obj_new_list_reverse(n_args, items);
 }
 
-py_obj_t rt_build_set(int n_args, py_obj_t *items) {
-    py_obj_base_t *o = m_new(py_obj_base_t, 1);
-    o->kind = O_SET;
-    o->u_set.alloc = get_doubling_prime_greater_or_equal_to(n_args + 1);
-    o->u_set.used = 0;
-    o->u_set.table = m_new(py_obj_t, o->u_set.alloc);
-    for (int i = 0; i < o->u_set.alloc; i++) {
-        o->u_set.table[i] = NULL;
-    }
-    for (int i = 0; i < n_args; i++) {
-        py_set_lookup(o, items[i], true);
-    }
-    return o;
+mp_obj_t rt_build_set(int n_args, mp_obj_t *items) {
+    return mp_obj_new_set(n_args, items);
 }
 
-py_obj_t rt_store_set(py_obj_t set, py_obj_t item) {
-    py_set_lookup(set, item, true);
+mp_obj_t rt_store_set(mp_obj_t set, mp_obj_t item) {
+    mp_set_lookup(set, item, true);
     return set;
 }
 
 // unpacked items are stored in order into the array pointed to by items
-void rt_unpack_sequence(py_obj_t seq_in, uint num, py_obj_t *items) {
-    if (IS_O(seq_in, O_TUPLE) || IS_O(seq_in, O_LIST)) {
-        py_obj_base_t *seq = seq_in;
-        if (seq->u_tuple_list.len < num) {
-            nlr_jump(py_obj_new_exception_2(rt_q_ValueError, "need more than %d values to unpack", (void*)seq->u_tuple_list.len, NULL));
-        } else if (seq->u_tuple_list.len > num) {
-            nlr_jump(py_obj_new_exception_2(rt_q_ValueError, "too many values to unpack (expected %d)", (void*)(machine_uint_t)num, NULL));
+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);
         }
-        memcpy(items, seq->u_tuple_list.items, num * sizeof(py_obj_t));
+        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));
     } else {
         // TODO call rt_getiter and extract via rt_iternext
-        nlr_jump(py_obj_new_exception_2(rt_q_TypeError, "'%s' object is not iterable", py_obj_get_type_str(seq_in), NULL));
+        nlr_jump(mp_obj_new_exception_msg_1_arg(rt_q_TypeError, "'%s' object is not iterable", mp_obj_get_type_str(seq_in)));
     }
 }
 
-py_obj_t rt_build_map(int n_args) {
-    py_obj_base_t *o = m_new(py_obj_base_t, 1);
-    o->kind = O_MAP;
-    py_map_init(&o->u_map, MAP_PY_OBJ, n_args);
-    return o;
-}
-
-py_obj_t rt_store_map(py_obj_t map, py_obj_t key, py_obj_t value) {
-    assert(IS_O(map, O_MAP)); // should always be
-    py_map_lookup(map, key, true)->value = value;
-    return map;
+mp_obj_t rt_build_map(int n_args) {
+    return mp_obj_new_dict(n_args);
 }
 
-py_obj_t build_bound_method(py_obj_t self, py_obj_t meth) {
-    py_obj_base_t *o = m_new(py_obj_base_t, 1);
-    o->kind = O_BOUND_METH;
-    o->u_bound_meth.meth = meth;
-    o->u_bound_meth.self = self;
-    return o;
+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);
 }
 
-py_obj_t rt_load_attr(py_obj_t base, qstr attr) {
+mp_obj_t rt_load_attr(mp_obj_t base, qstr attr) {
     DEBUG_OP_printf("load attr %s\n", qstr_str(attr));
-    if (IS_O(base, O_LIST) && attr == rt_q_append) {
-        return build_bound_method(base, fun_list_append);
-    } else if (IS_O(base, O_LIST) && attr == rt_q_pop) {
-        return build_bound_method(base, fun_list_pop);
-    } else if (IS_O(base, O_LIST) && attr == rt_q_sort) {
-        return build_bound_method(base, fun_list_sort);
-    } else if (IS_O(base, O_CLASS)) {
-        py_obj_base_t *o = base;
-        py_map_elem_t *elem = py_qstr_map_lookup(o->u_class.locals, attr, false);
+    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);
         if (elem == NULL) {
-            goto no_attr;
+            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)));
         }
         return elem->value;
-    } else if (IS_O(base, O_OBJ)) {
-        // logic: look in obj members then class locals (TODO check this against CPython)
-        py_obj_base_t *o = base;
-        py_map_elem_t *elem = py_qstr_map_lookup(o->u_obj.members, attr, false);
-        if (elem != NULL) {
-            // object member, always treated as a value
-            return elem->value;
-        }
-        elem = py_qstr_map_lookup(o->u_obj.class->u_class.locals, attr, false);
-        if (elem != NULL) {
-            if (py_obj_is_callable(elem->value)) {
-                // class member is callable so build a bound method
-                return build_bound_method(base, elem->value);
-            } else {
-                // class member is a value, so just return that value
-                return elem->value;
+    } 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);
             }
         }
-        goto no_attr;
     }
-
-no_attr:
-    nlr_jump(py_obj_new_exception_2(rt_q_AttributeError, "'%s' object has no attribute '%s'", py_obj_get_type_str(base), qstr_str(attr)));
+    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)));
 }
 
-void rt_load_method(py_obj_t base, qstr attr, py_obj_t *dest) {
+void rt_load_method(mp_obj_t base, qstr attr, mp_obj_t *dest) {
     DEBUG_OP_printf("load method %s\n", qstr_str(attr));
-    if (IS_O(base, O_STR)) {
-        if (attr == rt_q_join) {
-            dest[1] = fun_str_join;
-            dest[0] = base;
-            return;
-        } else if (attr == rt_q_format) {
-            dest[1] = fun_str_format;
-            dest[0] = base;
-            return;
-        }
-    } else if (IS_O(base, O_GEN_INSTANCE) && attr == rt_q___next__) {
-        dest[1] = fun_gen_instance_next;
-        dest[0] = base;
-        return;
-    } else if (IS_O(base, O_LIST) && attr == rt_q_append) {
-        dest[1] = fun_list_append;
-        dest[0] = base;
-        return;
-    } else if (IS_O(base, O_LIST) && attr == rt_q_pop) {
-        dest[1] = fun_list_pop;
+    if (MP_OBJ_IS_TYPE(base, &gen_instance_type) && attr == rt_q___next__) {
+        dest[1] = (mp_obj_t)&mp_obj_gen_instance_next_obj;
         dest[0] = base;
         return;
-    } else if (IS_O(base, O_LIST) && attr == rt_q_sort) {
-        dest[1] = fun_list_sort;
-        dest[0] = base;
+    } else if (MP_OBJ_IS_TYPE(base, &instance_type)) {
+        mp_obj_instance_load_method(base, attr, dest);
         return;
-    } else if (IS_O(base, O_OBJ)) {
-        // logic: look in obj members then class locals (TODO check this against CPython)
-        py_obj_base_t *o = base;
-        py_map_elem_t *elem = py_qstr_map_lookup(o->u_obj.members, attr, false);
-        if (elem != NULL) {
-            // object member, always treated as a value
-            dest[1] = elem->value;
-            dest[0] = NULL;
-            return;
-        }
-        elem = py_qstr_map_lookup(o->u_obj.class->u_class.locals, attr, false);
-        if (elem != NULL) {
-            if (py_obj_is_callable(elem->value)) {
-                // class member is callable so build a bound method
-                dest[1] = elem->value;
-                dest[0] = base;
-                return;
-            } else {
-                // class member is a value, so just return that value
-                dest[1] = elem->value;
-                dest[0] = NULL;
-                return;
-            }
-        }
-        goto no_attr;
-    } else if (IS_O(base, O_USER)) {
-        py_obj_base_t *o = base;
-        const py_user_method_t *meth = &o->u_user.info->methods[0];
+    } 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) {
-                if (meth->kind == 0) {
-                    dest[1] = rt_make_function_1(meth->fun);
-                } else if (meth->kind == 1) {
-                    dest[1] = rt_make_function_2(meth->fun);
-                } else {
-                    assert(0);
-                }
+                dest[1] = (mp_obj_t)meth->fun;
                 dest[0] = base;
                 return;
             }
         }
     }
 
-no_attr:
+    // no method; fallback to load_attr
     dest[1] = rt_load_attr(base, attr);
     dest[0] = NULL;
 }
 
-void rt_store_attr(py_obj_t base, qstr attr, py_obj_t value) {
+void rt_store_attr(mp_obj_t base, qstr attr, mp_obj_t value) {
     DEBUG_OP_printf("store attr %p.%s <- %p\n", base, qstr_str(attr), value);
-    if (IS_O(base, O_CLASS)) {
+    if (MP_OBJ_IS_TYPE(base, &class_type)) {
         // TODO CPython allows STORE_ATTR to a class, but is this the correct implementation?
-        py_obj_base_t *o = base;
-        py_qstr_map_lookup(o->u_class.locals, attr, true)->value = value;
-    } else if (IS_O(base, O_OBJ)) {
-        // logic: look in class locals (no add) then obj members (add) (TODO check this against CPython)
-        py_obj_base_t *o = base;
-        py_map_elem_t *elem = py_qstr_map_lookup(o->u_obj.class->u_class.locals, attr, false);
-        if (elem != NULL) {
-            elem->value = value;
-        } else {
-            py_qstr_map_lookup(o->u_obj.members, attr, true)->value = value;
-        }
+        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);
     } else {
-        printf("?AttributeError: '%s' object has no attribute '%s'\n", py_obj_get_type_str(base), qstr_str(attr));
+        printf("?AttributeError: '%s' object has no attribute '%s'\n", mp_obj_get_type_str(base), qstr_str(attr));
         assert(0);
     }
 }
 
-void rt_store_subscr(py_obj_t base, py_obj_t index, py_obj_t value) {
+void rt_store_subscr(mp_obj_t base, mp_obj_t index, mp_obj_t value) {
     DEBUG_OP_printf("store subscr %p[%p] <- %p\n", base, index, value);
-    if (IS_O(base, O_LIST)) {
+    if (MP_OBJ_IS_TYPE(base, &list_type)) {
         // list store
-        uint i = get_index(base, index);
-        ((py_obj_base_t*)base)->u_tuple_list.items[i] = value;
-    } else if (IS_O(base, O_MAP)) {
-        // map store
-        py_map_lookup(base, index, true)->value = value;
+        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);
     } else {
         assert(0);
     }
 }
 
-py_obj_t rt_getiter(py_obj_t o_in) {
-    if (IS_O(o_in, O_GEN_INSTANCE)) {
-        return o_in;
-    } else if (IS_O(o_in, O_RANGE)) {
-        py_obj_base_t *o = o_in;
-        return py_obj_new_range_iterator(o->u_range.start, o->u_range.stop, o->u_range.step);
-    } else if (IS_O(o_in, O_TUPLE)) {
-        return py_obj_new_tuple_iterator(o_in, 0);
-    } else if (IS_O(o_in, O_LIST)) {
-        return py_obj_new_list_iterator(o_in, 0);
+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"));
     } else {
-        nlr_jump(py_obj_new_exception_2(rt_q_TypeError, "'%s' object is not iterable", py_obj_get_type_str(o_in), NULL));
-    }
-}
-
-py_obj_t rt_iternext(py_obj_t o_in) {
-    if (IS_O(o_in, O_GEN_INSTANCE)) {
-        py_obj_base_t *self = o_in;
-        //py_obj_base_t *fun = self->u_gen_instance.state[0];
-        //assert(fun->kind == O_FUN_BC);
-        bool yield = py_execute_byte_code_2(&self->u_gen_instance.ip, &self->u_gen_instance.state[1], &self->u_gen_instance.sp);
-        if (yield) {
-            return *self->u_gen_instance.sp;
+        mp_obj_base_t *o = o_in;
+        if (o->type->getiter != NULL) {
+            return o->type->getiter(o_in);
         } else {
-            if (*self->u_gen_instance.sp == py_const_none) {
-                return py_const_stop_iteration;
-            } else {
-                // TODO return StopIteration with value *self->u_gen_instance.sp
-                return py_const_stop_iteration;
-            }
-        }
-
-    } else if (IS_O(o_in, O_RANGE_IT)) {
-        py_obj_base_t *o = o_in;
-        if ((o->u_range_it.step > 0 && o->u_range_it.cur < o->u_range_it.stop) || (o->u_range_it.step < 0 && o->u_range_it.cur > o->u_range_it.stop)) {
-            py_obj_t o_out = TO_SMALL_INT(o->u_range_it.cur);
-            o->u_range_it.cur += o->u_range_it.step;
-            return o_out;
-        } else {
-            return py_const_stop_iteration;
+            nlr_jump(mp_obj_new_exception_msg_1_arg(rt_q_TypeError, "'%s' object is not iterable", o->type->name));
         }
+    }
+}
 
-    } else if (IS_O(o_in, O_TUPLE_IT) || IS_O(o_in, O_LIST_IT)) {
-        py_obj_base_t *o = o_in;
-        if (o->u_tuple_list_it.cur < o->u_tuple_list_it.obj->u_tuple_list.len) {
-            py_obj_t o_out = o->u_tuple_list_it.obj->u_tuple_list.items[o->u_tuple_list_it.cur];
-            o->u_tuple_list_it.cur += 1;
-            return o_out;
+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);
         } else {
-            return py_const_stop_iteration;
+            nlr_jump(mp_obj_new_exception_msg_1_arg(rt_q_TypeError, "? '%s' object is not iterable", o->type->name));
         }
-
-    } else {
-        nlr_jump(py_obj_new_exception_2(rt_q_TypeError, "? '%s' object is not iterable", py_obj_get_type_str(o_in), NULL));
     }
 }
 
-py_obj_t rt_import_name(qstr name, py_obj_t fromlist, py_obj_t level) {
+mp_obj_t rt_import_name(qstr name, mp_obj_t fromlist, mp_obj_t level) {
     // build args array
-    py_obj_t args[5];
-    args[0] = py_obj_new_str(name);
-    args[1] = py_const_none; // TODO should be globals
-    args[2] = py_const_none; // TODO should be locals
+    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
     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
-    return py_builtin___import__(5, args);
+    return mp_builtin___import__(5, args);
 }
 
-py_obj_t rt_import_from(py_obj_t module, qstr name) {
-    py_obj_t x = rt_load_attr(module, name);
+mp_obj_t rt_import_from(mp_obj_t module, qstr name) {
+    mp_obj_t x = rt_load_attr(module, name);
     /* TODO convert AttributeError to ImportError
     if (fail) {
         (ImportError, "cannot import name %s", qstr_str(name), NULL)
diff --git a/py/runtime.h b/py/runtime.h
index d5d160535f29ca4723c04012bed9324b33c0e096..37b036852fd49902192853731eaffa8cd3141039 100644
--- a/py/runtime.h
+++ b/py/runtime.h
@@ -1,91 +1,3 @@
-typedef enum {
-    RT_UNARY_OP_NOT,
-    RT_UNARY_OP_POSITIVE,
-    RT_UNARY_OP_NEGATIVE,
-    RT_UNARY_OP_INVERT,
-} rt_unary_op_t;
-
-typedef enum {
-    RT_BINARY_OP_SUBSCR,
-    RT_BINARY_OP_OR,
-    RT_BINARY_OP_XOR,
-    RT_BINARY_OP_AND,
-    RT_BINARY_OP_LSHIFT,
-    RT_BINARY_OP_RSHIFT,
-    RT_BINARY_OP_ADD,
-    RT_BINARY_OP_SUBTRACT,
-    RT_BINARY_OP_MULTIPLY,
-    RT_BINARY_OP_FLOOR_DIVIDE,
-    RT_BINARY_OP_TRUE_DIVIDE,
-    RT_BINARY_OP_MODULO,
-    RT_BINARY_OP_POWER,
-    RT_BINARY_OP_INPLACE_OR,
-    RT_BINARY_OP_INPLACE_XOR,
-    RT_BINARY_OP_INPLACE_AND,
-    RT_BINARY_OP_INPLACE_LSHIFT,
-    RT_BINARY_OP_INPLACE_RSHIFT,
-    RT_BINARY_OP_INPLACE_ADD,
-    RT_BINARY_OP_INPLACE_SUBTRACT,
-    RT_BINARY_OP_INPLACE_MULTIPLY,
-    RT_BINARY_OP_INPLACE_FLOOR_DIVIDE,
-    RT_BINARY_OP_INPLACE_TRUE_DIVIDE,
-    RT_BINARY_OP_INPLACE_MODULO,
-    RT_BINARY_OP_INPLACE_POWER,
-} rt_binary_op_t;
-
-typedef enum {
-    RT_COMPARE_OP_LESS,
-    RT_COMPARE_OP_MORE,
-    RT_COMPARE_OP_EQUAL,
-    RT_COMPARE_OP_LESS_EQUAL,
-    RT_COMPARE_OP_MORE_EQUAL,
-    RT_COMPARE_OP_NOT_EQUAL,
-    RT_COMPARE_OP_IN,
-    RT_COMPARE_OP_NOT_IN,
-    RT_COMPARE_OP_IS,
-    RT_COMPARE_OP_IS_NOT,
-    RT_COMPARE_OP_EXCEPTION_MATCH,
-} rt_compare_op_t;
-
-typedef enum {
-    RT_F_LOAD_CONST_DEC = 0,
-    RT_F_LOAD_CONST_STR,
-    RT_F_LOAD_NAME,
-    RT_F_LOAD_GLOBAL,
-    RT_F_LOAD_BUILD_CLASS,
-    RT_F_LOAD_ATTR,
-    RT_F_LOAD_METHOD,
-    RT_F_STORE_NAME,
-    RT_F_STORE_ATTR,
-    RT_F_STORE_SUBSCR,
-    RT_F_IS_TRUE,
-    RT_F_UNARY_OP,
-    RT_F_BUILD_TUPLE,
-    RT_F_BUILD_LIST,
-    RT_F_LIST_APPEND,
-    RT_F_BUILD_MAP,
-    RT_F_STORE_MAP,
-    RT_F_BUILD_SET,
-    RT_F_STORE_SET,
-    RT_F_MAKE_FUNCTION_FROM_ID,
-    RT_F_CALL_FUNCTION_N,
-    RT_F_CALL_METHOD_N,
-    RT_F_BINARY_OP,
-    RT_F_COMPARE_OP,
-    RT_F_GETITER,
-    RT_F_ITERNEXT,
-    RT_F_NUMBER_OF,
-} rt_fun_kind_t;
-
-extern void *const rt_fun_table[RT_F_NUMBER_OF];
-
-typedef machine_ptr_t py_obj_t; // must be of pointer size
-typedef py_obj_t (*py_fun_0_t)(void);
-typedef py_obj_t (*py_fun_1_t)(py_obj_t);
-typedef py_obj_t (*py_fun_2_t)(py_obj_t, py_obj_t);
-typedef py_obj_t (*py_fun_t)(void);
-typedef py_obj_t (*py_fun_var_t)(int n, const py_obj_t *);
-
 extern qstr rt_q_append;
 extern qstr rt_q_pop;
 extern qstr rt_q_sort;
@@ -101,61 +13,47 @@ extern qstr rt_q_TypeError;
 extern qstr rt_q_SyntaxError;
 extern qstr rt_q_ValueError;
 
-extern py_obj_t py_const_none;
-extern py_obj_t py_const_false;
-extern py_obj_t py_const_true;
-extern py_obj_t py_const_stop_iteration; // special object indicating end of iteration (not StopIteration exception!)
-
-void rt_init(void);
-void rt_deinit(void);
-int rt_get_unique_code_id(bool is_main_module);
-void rt_assign_byte_code(int unique_code_id, byte *code, uint len, int n_args, int n_locals, int n_cells, int n_stack, bool is_generator);
-void rt_assign_native_code(int unique_code_id, py_fun_t f, uint len, int n_args);
-void rt_assign_inline_asm_code(int unique_code_id, py_fun_t f, uint len, int n_args);
-
-int rt_is_true(py_obj_t arg);
-
-py_obj_t rt_load_const_dec(qstr qstr);
-py_obj_t rt_load_const_str(qstr qstr);
-py_obj_t rt_load_name(qstr qstr);
-py_obj_t rt_load_global(qstr qstr);
-py_obj_t rt_load_build_class(void);
-py_obj_t rt_get_cell(py_obj_t cell);
-void rt_set_cell(py_obj_t cell, py_obj_t val);
-void rt_store_name(qstr qstr, py_obj_t obj);
-void rt_store_global(qstr qstr, py_obj_t obj);
-py_obj_t rt_unary_op(int op, py_obj_t arg);
-py_obj_t rt_binary_op(int op, py_obj_t lhs, py_obj_t rhs);
-py_obj_t rt_compare_op(int op, py_obj_t lhs, py_obj_t rhs);
-py_obj_t rt_make_function_from_id(int unique_code_id);
-py_obj_t rt_make_function_0(py_fun_0_t f);
-py_obj_t rt_make_function_1(py_fun_1_t f);
-py_obj_t rt_make_function_2(py_fun_2_t f);
-py_obj_t rt_make_function(int n_args, py_fun_t code);
-py_obj_t rt_make_function_var(int n_fixed_args, py_fun_var_t f);
-py_obj_t rt_make_closure_from_id(int unique_code_id, py_obj_t closure_tuple);
-py_obj_t rt_call_function_0(py_obj_t fun);
-py_obj_t rt_call_function_1(py_obj_t fun, py_obj_t arg);
-py_obj_t rt_call_function_2(py_obj_t fun, py_obj_t arg1, py_obj_t arg2);
-py_obj_t rt_call_function_n(py_obj_t fun, int n_args, const py_obj_t *args);
-py_obj_t rt_call_function_n_kw(py_obj_t fun, uint n_args, uint n_kw, const py_obj_t *args);
-py_obj_t rt_call_method_n(uint n_args, const py_obj_t *args);
-py_obj_t rt_call_method_n_kw(uint n_args, uint n_kw, const py_obj_t *args);
-py_obj_t rt_build_tuple(int n_args, py_obj_t *items);
-py_obj_t rt_build_list(int n_args, py_obj_t *items);
-py_obj_t rt_list_append(py_obj_t list, py_obj_t arg);
-py_obj_t rt_build_set(int n_args, py_obj_t *items);
-py_obj_t rt_store_set(py_obj_t set, py_obj_t item);
-void rt_unpack_sequence(py_obj_t seq, uint num, py_obj_t *items);
-py_obj_t rt_build_map(int n_args);
-py_obj_t rt_store_map(py_obj_t map, py_obj_t key, py_obj_t value);
-py_obj_t rt_load_attr(py_obj_t base, qstr attr);
-void rt_load_method(py_obj_t base, qstr attr, py_obj_t *dest);
-void rt_store_attr(py_obj_t base, qstr attr, py_obj_t val);
-void rt_store_subscr(py_obj_t base, py_obj_t index, py_obj_t val);
-py_obj_t rt_getiter(py_obj_t o);
-py_obj_t rt_iternext(py_obj_t o);
-py_obj_t rt_import_name(qstr name, py_obj_t fromlist, py_obj_t level);
-py_obj_t rt_import_from(py_obj_t module, qstr name);
-
-py_obj_t rt_gen_instance_next(py_obj_t self_in);
+int rt_is_true(mp_obj_t arg);
+
+mp_obj_t rt_load_const_dec(qstr qstr);
+mp_obj_t rt_load_const_str(qstr qstr);
+mp_obj_t rt_load_name(qstr qstr);
+mp_obj_t rt_load_global(qstr qstr);
+mp_obj_t rt_load_build_class(void);
+mp_obj_t rt_get_cell(mp_obj_t cell);
+void rt_set_cell(mp_obj_t cell, mp_obj_t val);
+void rt_store_name(qstr qstr, mp_obj_t obj);
+void rt_store_global(qstr qstr, mp_obj_t obj);
+mp_obj_t rt_unary_op(int op, mp_obj_t arg);
+mp_obj_t rt_binary_op(int op, mp_obj_t lhs, mp_obj_t rhs);
+mp_obj_t rt_compare_op(int op, mp_obj_t lhs, mp_obj_t rhs);
+mp_obj_t rt_make_function_from_id(int unique_code_id);
+mp_obj_t rt_make_function_0(mp_fun_0_t f);
+mp_obj_t rt_make_function_1(mp_fun_1_t f);
+mp_obj_t rt_make_function_2(mp_fun_2_t f);
+mp_obj_t rt_make_function_var(int n_args_min, mp_fun_var_t f);
+mp_obj_t rt_make_function_var_between(int n_args_min, int n_args_max, mp_fun_var_t fun); // min and max are inclusive
+mp_obj_t rt_make_closure_from_id(int unique_code_id, mp_obj_t closure_tuple);
+mp_obj_t rt_call_function_0(mp_obj_t fun);
+mp_obj_t rt_call_function_1(mp_obj_t fun, mp_obj_t arg);
+mp_obj_t rt_call_function_2(mp_obj_t fun, mp_obj_t arg1, mp_obj_t arg2);
+mp_obj_t rt_call_function_n(mp_obj_t fun, int n_args, const mp_obj_t *args);
+mp_obj_t rt_call_function_n_kw(mp_obj_t fun, uint n_args, uint n_kw, const mp_obj_t *args);
+mp_obj_t rt_call_method_n(uint n_args, const mp_obj_t *args);
+mp_obj_t rt_call_method_n_kw(uint n_args, uint n_kw, const mp_obj_t *args);
+mp_obj_t rt_build_tuple(int n_args, mp_obj_t *items);
+mp_obj_t rt_build_list(int n_args, mp_obj_t *items);
+mp_obj_t rt_list_append(mp_obj_t list, mp_obj_t arg);
+mp_obj_t rt_build_set(int n_args, mp_obj_t *items);
+mp_obj_t rt_store_set(mp_obj_t set, mp_obj_t item);
+void rt_unpack_sequence(mp_obj_t seq, uint num, mp_obj_t *items);
+mp_obj_t rt_build_map(int n_args);
+mp_obj_t rt_store_map(mp_obj_t map, mp_obj_t key, mp_obj_t value);
+mp_obj_t rt_load_attr(mp_obj_t base, qstr attr);
+void rt_load_method(mp_obj_t base, qstr attr, mp_obj_t *dest);
+void rt_store_attr(mp_obj_t base, qstr attr, mp_obj_t val);
+void rt_store_subscr(mp_obj_t base, mp_obj_t index, mp_obj_t val);
+mp_obj_t rt_getiter(mp_obj_t o);
+mp_obj_t rt_iternext(mp_obj_t o);
+mp_obj_t rt_import_name(qstr name, mp_obj_t fromlist, mp_obj_t level);
+mp_obj_t rt_import_from(mp_obj_t module, qstr name);
diff --git a/py/runtime0.h b/py/runtime0.h
new file mode 100644
index 0000000000000000000000000000000000000000..f68b7b961b481850f248e369e3844a821910a3e0
--- /dev/null
+++ b/py/runtime0.h
@@ -0,0 +1,87 @@
+typedef enum {
+    RT_UNARY_OP_NOT,
+    RT_UNARY_OP_POSITIVE,
+    RT_UNARY_OP_NEGATIVE,
+    RT_UNARY_OP_INVERT,
+} rt_unary_op_t;
+
+typedef enum {
+    RT_BINARY_OP_SUBSCR,
+    RT_BINARY_OP_OR,
+    RT_BINARY_OP_XOR,
+    RT_BINARY_OP_AND,
+    RT_BINARY_OP_LSHIFT,
+    RT_BINARY_OP_RSHIFT,
+    RT_BINARY_OP_ADD,
+    RT_BINARY_OP_SUBTRACT,
+    RT_BINARY_OP_MULTIPLY,
+    RT_BINARY_OP_FLOOR_DIVIDE,
+    RT_BINARY_OP_TRUE_DIVIDE,
+    RT_BINARY_OP_MODULO,
+    RT_BINARY_OP_POWER,
+    RT_BINARY_OP_INPLACE_OR,
+    RT_BINARY_OP_INPLACE_XOR,
+    RT_BINARY_OP_INPLACE_AND,
+    RT_BINARY_OP_INPLACE_LSHIFT,
+    RT_BINARY_OP_INPLACE_RSHIFT,
+    RT_BINARY_OP_INPLACE_ADD,
+    RT_BINARY_OP_INPLACE_SUBTRACT,
+    RT_BINARY_OP_INPLACE_MULTIPLY,
+    RT_BINARY_OP_INPLACE_FLOOR_DIVIDE,
+    RT_BINARY_OP_INPLACE_TRUE_DIVIDE,
+    RT_BINARY_OP_INPLACE_MODULO,
+    RT_BINARY_OP_INPLACE_POWER,
+} rt_binary_op_t;
+
+typedef enum {
+    RT_COMPARE_OP_LESS,
+    RT_COMPARE_OP_MORE,
+    RT_COMPARE_OP_EQUAL,
+    RT_COMPARE_OP_LESS_EQUAL,
+    RT_COMPARE_OP_MORE_EQUAL,
+    RT_COMPARE_OP_NOT_EQUAL,
+    RT_COMPARE_OP_IN,
+    RT_COMPARE_OP_NOT_IN,
+    RT_COMPARE_OP_IS,
+    RT_COMPARE_OP_IS_NOT,
+    RT_COMPARE_OP_EXCEPTION_MATCH,
+} rt_compare_op_t;
+
+typedef enum {
+    RT_F_LOAD_CONST_DEC = 0,
+    RT_F_LOAD_CONST_STR,
+    RT_F_LOAD_NAME,
+    RT_F_LOAD_GLOBAL,
+    RT_F_LOAD_BUILD_CLASS,
+    RT_F_LOAD_ATTR,
+    RT_F_LOAD_METHOD,
+    RT_F_STORE_NAME,
+    RT_F_STORE_ATTR,
+    RT_F_STORE_SUBSCR,
+    RT_F_IS_TRUE,
+    RT_F_UNARY_OP,
+    RT_F_BUILD_TUPLE,
+    RT_F_BUILD_LIST,
+    RT_F_LIST_APPEND,
+    RT_F_BUILD_MAP,
+    RT_F_STORE_MAP,
+    RT_F_BUILD_SET,
+    RT_F_STORE_SET,
+    RT_F_MAKE_FUNCTION_FROM_ID,
+    RT_F_CALL_FUNCTION_N,
+    RT_F_CALL_METHOD_N,
+    RT_F_BINARY_OP,
+    RT_F_COMPARE_OP,
+    RT_F_GETITER,
+    RT_F_ITERNEXT,
+    RT_F_NUMBER_OF,
+} rt_fun_kind_t;
+
+extern void *const rt_fun_table[RT_F_NUMBER_OF];
+
+void rt_init(void);
+void rt_deinit(void);
+int rt_get_unique_code_id(bool is_main_module);
+void rt_assign_byte_code(int unique_code_id, byte *code, uint len, int n_args, int n_locals, int n_cells, int n_stack, bool is_generator);
+void rt_assign_native_code(int unique_code_id, void *f, uint len, int n_args);
+void rt_assign_inline_asm_code(int unique_code_id, void *f, uint len, int n_args);
diff --git a/py/scope.c b/py/scope.c
index d72d130f28c05734d5ede4b81de62911b1c20caf..7d55d3923f002c5851af3213efb83646be99295c 100644
--- a/py/scope.c
+++ b/py/scope.c
@@ -4,11 +4,11 @@
 #include <assert.h>
 
 #include "misc.h"
-#include "mpyconfig.h"
+#include "mpconfig.h"
 #include "parse.h"
 #include "scope.h"
 
-scope_t *scope_new(scope_kind_t kind, py_parse_node_t pn, uint unique_code_id, uint emit_options) {
+scope_t *scope_new(scope_kind_t kind, mp_parse_node_t pn, uint unique_code_id, uint emit_options) {
     scope_t *scope = m_new(scope_t, 1);
     scope->kind = kind;
     scope->parent = NULL;
@@ -20,8 +20,8 @@ scope_t *scope_new(scope_kind_t kind, py_parse_node_t pn, uint unique_code_id, u
             break;
         case SCOPE_FUNCTION:
         case SCOPE_CLASS:
-            assert(PY_PARSE_NODE_IS_STRUCT(pn));
-            scope->simple_name = PY_PARSE_NODE_LEAF_ARG(((py_parse_node_struct_t*)pn)->nodes[0]);
+            assert(MP_PARSE_NODE_IS_STRUCT(pn));
+            scope->simple_name = MP_PARSE_NODE_LEAF_ARG(((mp_parse_node_struct_t*)pn)->nodes[0]);
             break;
         case SCOPE_LAMBDA:
             scope->simple_name = qstr_from_str_static("<lambda>");
diff --git a/py/scope.h b/py/scope.h
index f560e3d21abe3ede8331071b048f6ebc28a3c9fe..1231b3cc5e37a28af91d486567d1030dd5bae6f1 100644
--- a/py/scope.h
+++ b/py/scope.h
@@ -37,7 +37,7 @@ typedef struct _scope_t {
     scope_kind_t kind;
     struct _scope_t *parent;
     struct _scope_t *next;
-    py_parse_node_t pn;
+    mp_parse_node_t pn;
     qstr simple_name;
     int id_info_alloc;
     int id_info_len;
@@ -55,7 +55,7 @@ typedef struct _scope_t {
     uint emit_options;
 } scope_t;
 
-scope_t *scope_new(scope_kind_t kind, py_parse_node_t pn, uint unique_code_id, uint emit_options);
+scope_t *scope_new(scope_kind_t kind, mp_parse_node_t pn, uint unique_code_id, uint emit_options);
 id_info_t *scope_find_or_add_id(scope_t *scope, qstr qstr, bool *added);
 id_info_t *scope_find(scope_t *scope, qstr qstr);
 id_info_t *scope_find_global(scope_t *scope, qstr qstr);
diff --git a/py/showbc.c b/py/showbc.c
index bd8a50515515420876c2d3cdd77a1bdb0de43fa9..fd1d512031ddfb2e1056207f663417be5fe7c70b 100644
--- a/py/showbc.c
+++ b/py/showbc.c
@@ -5,16 +5,15 @@
 #include <assert.h>
 
 #include "misc.h"
-#include "mpyconfig.h"
-#include "runtime.h"
-#include "bc.h"
+#include "mpconfig.h"
+#include "bc0.h"
 
 #define DECODE_UINT do { unum = *ip++; if (unum > 127) { unum = ((unum & 0x3f) << 8) | (*ip++); } } while (0)
 #define DECODE_ULABEL do { unum = (ip[0] | (ip[1] << 8)); ip += 2; } while (0)
 #define DECODE_SLABEL do { unum = (ip[0] | (ip[1] << 8)) - 0x8000; ip += 2; } while (0)
 #define DECODE_QSTR do { qstr = *ip++; if (qstr > 127) { qstr = ((qstr & 0x3f) << 8) | (*ip++); } } while (0)
 
-void py_show_byte_code(const byte *ip, int len) {
+void mp_show_byte_code(const byte *ip, int len) {
     const byte *ip_start = ip;
     machine_uint_t unum;
     qstr qstr;
@@ -22,164 +21,164 @@ void py_show_byte_code(const byte *ip, int len) {
         printf("%02u ", (uint)(ip - ip_start));
         int op = *ip++;
         switch (op) {
-            case PYBC_LOAD_CONST_FALSE:
+            case MP_BC_LOAD_CONST_FALSE:
                 printf("LOAD_CONST_FALSE");
                 break;
 
-            case PYBC_LOAD_CONST_NONE:
+            case MP_BC_LOAD_CONST_NONE:
                 printf("LOAD_CONST_NONE");
                 break;
 
-            case PYBC_LOAD_CONST_TRUE:
+            case MP_BC_LOAD_CONST_TRUE:
                 printf("LOAD_CONST_TRUE");
                 break;
 
-            case PYBC_LOAD_CONST_SMALL_INT:
+            case MP_BC_LOAD_CONST_SMALL_INT:
                 unum = (ip[0] | (ip[1] << 8) | (ip[2] << 16)) - 0x800000;
                 ip += 3;
                 printf("LOAD_CONST_SMALL_INT %d", (int)unum);
                 break;
 
                 /*
-            case PYBC_LOAD_CONST_DEC:
+            case MP_BC_LOAD_CONST_DEC:
                 DECODE_QSTR;
                 PUSH(rt_load_const_dec(qstr));
                 break;
                 */
 
-            case PYBC_LOAD_CONST_ID:
+            case MP_BC_LOAD_CONST_ID:
                 DECODE_QSTR;
                 printf("LOAD_CONST_ID %s", qstr_str(qstr));
                 break;
 
-            case PYBC_LOAD_CONST_STRING:
+            case MP_BC_LOAD_CONST_STRING:
                 DECODE_QSTR;
                 printf("LOAD_CONST_STRING %s", qstr_str(qstr));
                 break;
 
-            case PYBC_LOAD_FAST_0:
+            case MP_BC_LOAD_FAST_0:
                 printf("LOAD_FAST_0");
                 break;
 
-            case PYBC_LOAD_FAST_1:
+            case MP_BC_LOAD_FAST_1:
                 printf("LOAD_FAST_1");
                 break;
 
-            case PYBC_LOAD_FAST_2:
+            case MP_BC_LOAD_FAST_2:
                 printf("LOAD_FAST_2");
                 break;
 
-            case PYBC_LOAD_FAST_N:
+            case MP_BC_LOAD_FAST_N:
                 DECODE_UINT;
                 printf("LOAD_FAST_N %lu", unum);
                 break;
 
-            case PYBC_LOAD_NAME:
+            case MP_BC_LOAD_NAME:
                 DECODE_QSTR;
                 printf("LOAD_NAME %s", qstr_str(qstr));
                 break;
 
-            case PYBC_LOAD_GLOBAL:
+            case MP_BC_LOAD_GLOBAL:
                 DECODE_QSTR;
                 printf("LOAD_GLOBAL %s", qstr_str(qstr));
                 break;
 
-            case PYBC_LOAD_ATTR:
+            case MP_BC_LOAD_ATTR:
                 DECODE_QSTR;
                 printf("LOAD_ATTR %s", qstr_str(qstr));
                 break;
 
-            case PYBC_LOAD_METHOD:
+            case MP_BC_LOAD_METHOD:
                 DECODE_QSTR;
                 printf("LOAD_METHOD %s", qstr_str(qstr));
                 break;
 
-            case PYBC_LOAD_BUILD_CLASS:
+            case MP_BC_LOAD_BUILD_CLASS:
                 printf("LOAD_BUILD_CLASS");
                 break;
 
-            case PYBC_STORE_FAST_0:
+            case MP_BC_STORE_FAST_0:
                 printf("STORE_FAST_0");
                 break;
 
-            case PYBC_STORE_FAST_1:
+            case MP_BC_STORE_FAST_1:
                 printf("STORE_FAST_1");
                 break;
 
-            case PYBC_STORE_FAST_2:
+            case MP_BC_STORE_FAST_2:
                 printf("STORE_FAST_2");
                 break;
 
-            case PYBC_STORE_FAST_N:
+            case MP_BC_STORE_FAST_N:
                 DECODE_UINT;
                 printf("STORE_FAST_N %lu", unum);
                 break;
 
-            case PYBC_STORE_NAME:
+            case MP_BC_STORE_NAME:
                 DECODE_QSTR;
                 printf("STORE_NAME %s", qstr_str(qstr));
                 break;
 
                 /*
-            case PYBC_STORE_GLOBAL:
+            case MP_BC_STORE_GLOBAL:
                 DECODE_QSTR;
                 rt_store_global(qstr, POP());
                 break;
                 */
 
-            case PYBC_STORE_ATTR:
+            case MP_BC_STORE_ATTR:
                 DECODE_QSTR;
                 printf("STORE_ATTR %s", qstr_str(qstr));
                 break;
 
-            case PYBC_STORE_SUBSCR:
+            case MP_BC_STORE_SUBSCR:
                 printf("STORE_SUBSCR");
                 break;
 
                 /*
-            case PYBC_DUP_TOP:
+            case MP_BC_DUP_TOP:
                 obj1 = *sp;
                 PUSH(obj1);
                 break;
                 */
 
-            case PYBC_DUP_TOP_TWO:
+            case MP_BC_DUP_TOP_TWO:
                 printf("DUP_TOP_TWO");
                 break;
 
-            case PYBC_POP_TOP:
+            case MP_BC_POP_TOP:
                 printf("POP_TOP");
                 break;
 
                 /*
-            case PYBC_ROT_TWO:
+            case MP_BC_ROT_TWO:
                 obj1 = sp[0];
                 sp[0] = sp[1];
                 sp[1] = obj1;
                 break;
                 */
 
-            case PYBC_ROT_THREE:
+            case MP_BC_ROT_THREE:
                 printf("ROT_THREE");
                 break;
 
-            case PYBC_JUMP:
+            case MP_BC_JUMP:
                 DECODE_SLABEL;
                 printf("JUMP %lu", ip + unum - ip_start);
                 break;
 
-            case PYBC_POP_JUMP_IF_TRUE:
+            case MP_BC_POP_JUMP_IF_TRUE:
                 DECODE_SLABEL;
                 printf("POP_JUMP_IF_TRUE %lu", ip + unum - ip_start);
                 break;
 
-            case PYBC_POP_JUMP_IF_FALSE:
+            case MP_BC_POP_JUMP_IF_FALSE:
                 DECODE_SLABEL;
                 printf("POP_JUMP_IF_FALSE %lu", ip + unum - ip_start);
                 break;
 
                 /*
-            case PYBC_JUMP_IF_TRUE_OR_POP:
+            case MP_BC_JUMP_IF_TRUE_OR_POP:
                 DECODE_SLABEL;
                 if (rt_is_true(*sp)) {
                     ip += unum;
@@ -188,7 +187,7 @@ void py_show_byte_code(const byte *ip, int len) {
                 }
                 break;
 
-            case PYBC_JUMP_IF_FALSE_OR_POP:
+            case MP_BC_JUMP_IF_FALSE_OR_POP:
                 DECODE_SLABEL;
                 if (rt_is_true(*sp)) {
                     sp++;
@@ -197,13 +196,13 @@ void py_show_byte_code(const byte *ip, int len) {
                 }
                 break;
 
-            case PYBC_SETUP_EXCEPT:
+            case MP_BC_SETUP_EXCEPT:
                 DECODE_ULABEL; // except labels are always forward
                 *++exc_sp = (machine_uint_t)ip + unum;
                 *++exc_sp = (machine_uint_t)sp;
                 break;
 
-            case PYBC_END_FINALLY:
+            case MP_BC_END_FINALLY:
                 // not implemented
                 // if TOS is an exception, reraises the exception (3 values on TOS)
                 // if TOS is an integer, does something else
@@ -213,90 +212,90 @@ void py_show_byte_code(const byte *ip, int len) {
                 break;
                 */
 
-            case PYBC_GET_ITER:
+            case MP_BC_GET_ITER:
                 printf("GET_ITER");
                 break;
 
-            case PYBC_FOR_ITER:
+            case MP_BC_FOR_ITER:
                 DECODE_ULABEL; // the jump offset if iteration finishes; for labels are always forward
                 printf("FOR_ITER %lu", ip + unum - ip_start);
                 break;
 
                 /*
-            case PYBC_POP_BLOCK:
+            case MP_BC_POP_BLOCK:
                 // pops block and restores the stack
                 assert(0);
                 break;
 
-            case PYBC_POP_EXCEPT:
+            case MP_BC_POP_EXCEPT:
                 // TODO need to work out how blocks work etc
                 // pops block, checks it's an exception block, and restores the stack, saving the 3 exception values to local threadstate
                 assert(exc_sp >= &exc_stack[0]);
-                //sp = (py_obj_t*)(*exc_sp--);
+                //sp = (mp_obj_t*)(*exc_sp--);
                 //exc_sp--; // discard ip
                 exc_sp -= 2;
                 //sp += 3; // pop 3 exception values
                 break;
 
-            case PYBC_UNARY_OP:
+            case MP_BC_UNARY_OP:
                 unum = *ip++;
                 *sp = rt_unary_op(unum, *sp);
                 break;
                 */
 
-            case PYBC_BINARY_OP:
+            case MP_BC_BINARY_OP:
                 unum = *ip++;
                 printf("BINARY_OP %lu", unum);
                 break;
 
-            case PYBC_COMPARE_OP:
+            case MP_BC_COMPARE_OP:
                 unum = *ip++;
                 printf("COMPARE_OP %lu", unum);
                 break;
 
-            case PYBC_BUILD_TUPLE:
+            case MP_BC_BUILD_TUPLE:
                 DECODE_UINT;
                 printf("BUILD_TUPLE %lu", unum);
                 break;
 
-            case PYBC_BUILD_LIST:
+            case MP_BC_BUILD_LIST:
                 DECODE_UINT;
                 printf("BUILD_LIST %lu", unum);
                 break;
 
                 /*
-            case PYBC_LIST_APPEND:
+            case MP_BC_LIST_APPEND:
                 DECODE_UINT;
                 // I think it's guaranteed by the compiler that sp[unum] is a list
                 rt_list_append(sp[unum], sp[0]);
                 sp++;
                 break;
 
-            case PYBC_BUILD_MAP:
+            case MP_BC_BUILD_MAP:
                 DECODE_UINT;
                 PUSH(rt_build_map(unum));
                 break;
 
-            case PYBC_STORE_MAP:
+            case MP_BC_STORE_MAP:
                 sp += 2;
                 rt_store_map(sp[0], sp[-2], sp[-1]);
                 break;
 
-            case PYBC_MAP_ADD:
+            case MP_BC_MAP_ADD:
                 DECODE_UINT;
                 // I think it's guaranteed by the compiler that sp[unum + 1] is a map
                 rt_store_map(sp[unum + 1], sp[0], sp[1]);
                 sp += 2;
                 break;
 
-            case PYBC_BUILD_SET:
+            case MP_BC_BUILD_SET:
                 DECODE_UINT;
                 obj1 = rt_build_set(unum, sp);
                 sp += unum - 1;
                 *sp = obj1;
                 break;
 
-            case PYBC_SET_ADD:
+            case MP_BC_SET_ADD:
                 DECODE_UINT;
                 // I think it's guaranteed by the compiler that sp[unum] is a set
                 rt_store_set(sp[unum], sp[0]);
@@ -304,32 +303,32 @@ void py_show_byte_code(const byte *ip, int len) {
                 break;
                 */
 
-            case PYBC_UNPACK_SEQUENCE:
+            case MP_BC_UNPACK_SEQUENCE:
                 DECODE_UINT;
                 printf("UNPACK_SEQUENCE %lu", unum);
                 break;
 
-            case PYBC_MAKE_FUNCTION:
+            case MP_BC_MAKE_FUNCTION:
                 DECODE_UINT;
                 printf("MAKE_FUNCTION %lu", unum);
                 break;
 
-            case PYBC_CALL_FUNCTION:
+            case MP_BC_CALL_FUNCTION:
                 DECODE_UINT;
                 printf("CALL_FUNCTION n=%lu nkw=%lu", unum & 0xff, (unum >> 8) & 0xff);
                 break;
 
-            case PYBC_CALL_METHOD:
+            case MP_BC_CALL_METHOD:
                 DECODE_UINT;
                 printf("CALL_METHOD n=%lu nkw=%lu", unum & 0xff, (unum >> 8) & 0xff);
                 break;
 
-            case PYBC_RETURN_VALUE:
+            case MP_BC_RETURN_VALUE:
                 printf("RETURN_VALUE");
                 break;
 
                 /*
-            case PYBC_YIELD_VALUE:
+            case MP_BC_YIELD_VALUE:
                 nlr_pop();
                 *ip_in_out = ip;
                 fastn[0] = fast0;
diff --git a/py/vm.c b/py/vm.c
index 7db6a7d9c406e7ff5d0ad13dcd9d624bc627bcde..b10d641ca16704e86f582660f2476264552dde62 100644
--- a/py/vm.c
+++ b/py/vm.c
@@ -6,8 +6,10 @@
 
 #include "nlr.h"
 #include "misc.h"
-#include "mpyconfig.h"
+#include "mpconfig.h"
+#include "obj.h"
 #include "runtime.h"
+#include "bc0.h"
 #include "bc.h"
 
 // (value) stack grows down (to be compatible with native code when passing pointers to the stack), top element is pointed to
@@ -23,12 +25,12 @@
 #define SET_TOP(val) *sp = (val)
 
 // args are in reverse order in array
-py_obj_t py_execute_byte_code(const byte *code, const py_obj_t *args, uint n_args, uint n_state) {
-    py_obj_t temp_state[10]; // TODO allocate properly
-    py_obj_t *state = &temp_state[0];
-    py_obj_t *sp = &state[10];
+mp_obj_t mp_execute_byte_code(const byte *code, const mp_obj_t *args, uint n_args, uint n_state) {
+    mp_obj_t temp_state[10]; // TODO allocate properly
+    mp_obj_t *state = &temp_state[0];
+    mp_obj_t *sp = &state[10];
     if (n_state > 10) {
-        state = m_new(py_obj_t, n_state);
+        state = m_new(mp_obj_t, n_state);
         sp = &state[n_state];
     }
     // init args
@@ -37,7 +39,7 @@ py_obj_t py_execute_byte_code(const byte *code, const py_obj_t *args, uint n_arg
         state[i] = args[n_args - 1 - i];
     }
     const byte *ip = code;
-    if (py_execute_byte_code_2(&ip, &state[0], &sp)) {
+    if (mp_execute_byte_code_2(&ip, &state[0], &sp)) {
         // it shouldn't yield
         assert(0);
     }
@@ -48,15 +50,15 @@ py_obj_t py_execute_byte_code(const byte *code, const py_obj_t *args, uint n_arg
 
 // fastn has items in normal order
 // sp points to top of stack which grows down
-bool py_execute_byte_code_2(const byte **ip_in_out, py_obj_t *fastn, py_obj_t **sp_in_out) {
+bool mp_execute_byte_code_2(const byte **ip_in_out, mp_obj_t *fastn, mp_obj_t **sp_in_out) {
     // careful: be sure to declare volatile any variables read in the exception handler (written is ok, I think)
 
     const byte *ip = *ip_in_out;
-    py_obj_t *sp = *sp_in_out;
+    mp_obj_t *sp = *sp_in_out;
     machine_uint_t unum;
     qstr qstr;
-    py_obj_t obj1, obj2;
-    py_obj_t fast0 = fastn[0], fast1 = fastn[1], fast2 = fastn[2];
+    mp_obj_t obj1, obj2;
+    mp_obj_t fast0 = fastn[0], fast1 = fastn[1], fast2 = fastn[2];
     nlr_buf_t nlr;
 
     // on the exception stack we store (ip, sp) for each block
@@ -70,182 +72,182 @@ bool py_execute_byte_code_2(const byte **ip_in_out, py_obj_t *fastn, py_obj_t **
             for (;;) {
                 int op = *ip++;
                 switch (op) {
-                    case PYBC_LOAD_CONST_FALSE:
-                        PUSH(py_const_false);
+                    case MP_BC_LOAD_CONST_FALSE:
+                        PUSH(mp_const_false);
                         break;
 
-                    case PYBC_LOAD_CONST_NONE:
-                        PUSH(py_const_none);
+                    case MP_BC_LOAD_CONST_NONE:
+                        PUSH(mp_const_none);
                         break;
 
-                    case PYBC_LOAD_CONST_TRUE:
-                        PUSH(py_const_true);
+                    case MP_BC_LOAD_CONST_TRUE:
+                        PUSH(mp_const_true);
                         break;
 
-                    case PYBC_LOAD_CONST_SMALL_INT:
+                    case MP_BC_LOAD_CONST_SMALL_INT:
                         unum = (ip[0] | (ip[1] << 8) | (ip[2] << 16)) - 0x800000;
                         ip += 3;
-                        PUSH((py_obj_t)(unum << 1 | 1));
+                        PUSH((mp_obj_t)(unum << 1 | 1));
                         break;
 
-                    case PYBC_LOAD_CONST_DEC:
+                    case MP_BC_LOAD_CONST_DEC:
                         DECODE_QSTR;
                         PUSH(rt_load_const_dec(qstr));
                         break;
 
-                    case PYBC_LOAD_CONST_ID:
+                    case MP_BC_LOAD_CONST_ID:
                         DECODE_QSTR;
                         PUSH(rt_load_const_str(qstr)); // TODO
                         break;
 
-                    case PYBC_LOAD_CONST_STRING:
+                    case MP_BC_LOAD_CONST_STRING:
                         DECODE_QSTR;
                         PUSH(rt_load_const_str(qstr));
                         break;
 
-                    case PYBC_LOAD_FAST_0:
+                    case MP_BC_LOAD_FAST_0:
                         PUSH(fast0);
                         break;
 
-                    case PYBC_LOAD_FAST_1:
+                    case MP_BC_LOAD_FAST_1:
                         PUSH(fast1);
                         break;
 
-                    case PYBC_LOAD_FAST_2:
+                    case MP_BC_LOAD_FAST_2:
                         PUSH(fast2);
                         break;
 
-                    case PYBC_LOAD_FAST_N:
+                    case MP_BC_LOAD_FAST_N:
                         DECODE_UINT;
                         PUSH(fastn[unum]);
                         break;
 
-                    case PYBC_LOAD_DEREF:
+                    case MP_BC_LOAD_DEREF:
                         DECODE_UINT;
                         PUSH(rt_get_cell(fastn[unum]));
                         break;
 
-                    case PYBC_LOAD_CLOSURE:
+                    case MP_BC_LOAD_CLOSURE:
                         DECODE_UINT;
                         PUSH(fastn[unum]);
                         break;
 
-                    case PYBC_LOAD_NAME:
+                    case MP_BC_LOAD_NAME:
                         DECODE_QSTR;
                         PUSH(rt_load_name(qstr));
                         break;
 
-                    case PYBC_LOAD_GLOBAL:
+                    case MP_BC_LOAD_GLOBAL:
                         DECODE_QSTR;
                         PUSH(rt_load_global(qstr));
                         break;
 
-                    case PYBC_LOAD_ATTR:
+                    case MP_BC_LOAD_ATTR:
                         DECODE_QSTR;
                         SET_TOP(rt_load_attr(TOP(), qstr));
                         break;
 
-                    case PYBC_LOAD_METHOD:
+                    case MP_BC_LOAD_METHOD:
                         DECODE_QSTR;
                         sp -= 1;
                         rt_load_method(sp[1], qstr, sp);
                         break;
 
-                    case PYBC_LOAD_BUILD_CLASS:
+                    case MP_BC_LOAD_BUILD_CLASS:
                         PUSH(rt_load_build_class());
                         break;
 
-                    case PYBC_STORE_FAST_0:
+                    case MP_BC_STORE_FAST_0:
                         fast0 = POP();
                         break;
 
-                    case PYBC_STORE_FAST_1:
+                    case MP_BC_STORE_FAST_1:
                         fast1 = POP();
                         break;
 
-                    case PYBC_STORE_FAST_2:
+                    case MP_BC_STORE_FAST_2:
                         fast2 = POP();
                         break;
 
-                    case PYBC_STORE_FAST_N:
+                    case MP_BC_STORE_FAST_N:
                         DECODE_UINT;
                         fastn[unum] = POP();
                         break;
 
-                    case PYBC_STORE_DEREF:
+                    case MP_BC_STORE_DEREF:
                         DECODE_UINT;
                         rt_set_cell(fastn[unum], POP());
                         break;
 
-                    case PYBC_STORE_NAME:
+                    case MP_BC_STORE_NAME:
                         DECODE_QSTR;
                         rt_store_name(qstr, POP());
                         break;
 
-                    case PYBC_STORE_GLOBAL:
+                    case MP_BC_STORE_GLOBAL:
                         DECODE_QSTR;
                         rt_store_global(qstr, POP());
                         break;
 
-                    case PYBC_STORE_ATTR:
+                    case MP_BC_STORE_ATTR:
                         DECODE_QSTR;
                         rt_store_attr(sp[0], qstr, sp[1]);
                         sp += 2;
                         break;
 
-                    case PYBC_STORE_SUBSCR:
+                    case MP_BC_STORE_SUBSCR:
                         rt_store_subscr(sp[1], sp[0], sp[2]);
                         sp += 3;
                         break;
 
-                    case PYBC_DUP_TOP:
+                    case MP_BC_DUP_TOP:
                         obj1 = TOP();
                         PUSH(obj1);
                         break;
 
-                    case PYBC_DUP_TOP_TWO:
+                    case MP_BC_DUP_TOP_TWO:
                         sp -= 2;
                         sp[0] = sp[2];
                         sp[1] = sp[3];
                         break;
 
-                    case PYBC_POP_TOP:
+                    case MP_BC_POP_TOP:
                         ++sp;
                         break;
 
-                    case PYBC_ROT_TWO:
+                    case MP_BC_ROT_TWO:
                         obj1 = sp[0];
                         sp[0] = sp[1];
                         sp[1] = obj1;
                         break;
 
-                    case PYBC_ROT_THREE:
+                    case MP_BC_ROT_THREE:
                         obj1 = sp[0];
                         sp[0] = sp[1];
                         sp[1] = sp[2];
                         sp[2] = obj1;
                         break;
 
-                    case PYBC_JUMP:
+                    case MP_BC_JUMP:
                         DECODE_SLABEL;
                         ip += unum;
                         break;
 
-                    case PYBC_POP_JUMP_IF_TRUE:
+                    case MP_BC_POP_JUMP_IF_TRUE:
                         DECODE_SLABEL;
                         if (rt_is_true(POP())) {
                             ip += unum;
                         }
                         break;
 
-                    case PYBC_POP_JUMP_IF_FALSE:
+                    case MP_BC_POP_JUMP_IF_FALSE:
                         DECODE_SLABEL;
                         if (!rt_is_true(POP())) {
                             ip += unum;
                         }
                         break;
 
-                    case PYBC_JUMP_IF_TRUE_OR_POP:
+                    case MP_BC_JUMP_IF_TRUE_OR_POP:
                         DECODE_SLABEL;
                         if (rt_is_true(TOP())) {
                             ip += unum;
@@ -254,7 +256,7 @@ bool py_execute_byte_code_2(const byte **ip_in_out, py_obj_t *fastn, py_obj_t **
                         }
                         break;
 
-                    case PYBC_JUMP_IF_FALSE_OR_POP:
+                    case MP_BC_JUMP_IF_FALSE_OR_POP:
                         DECODE_SLABEL;
                         if (rt_is_true(TOP())) {
                             sp++;
@@ -264,19 +266,19 @@ bool py_execute_byte_code_2(const byte **ip_in_out, py_obj_t *fastn, py_obj_t **
                         break;
 
                         /* we are trying to get away without using this opcode
-                    case PYBC_SETUP_LOOP:
+                    case MP_BC_SETUP_LOOP:
                         DECODE_UINT;
-                        // push_block(PYBC_SETUP_LOOP, ip + unum, sp)
+                        // push_block(MP_BC_SETUP_LOOP, ip + unum, sp)
                         break;
                         */
 
-                    case PYBC_SETUP_EXCEPT:
+                    case MP_BC_SETUP_EXCEPT:
                         DECODE_ULABEL; // except labels are always forward
                         *++exc_sp = (machine_uint_t)ip + unum;
                         *++exc_sp = (machine_uint_t)sp;
                         break;
 
-                    case PYBC_END_FINALLY:
+                    case MP_BC_END_FINALLY:
                         // not implemented
                         // if TOS is an exception, reraises the exception (3 values on TOS)
                         // if TOS is an integer, does something else
@@ -285,14 +287,14 @@ bool py_execute_byte_code_2(const byte **ip_in_out, py_obj_t *fastn, py_obj_t **
                         assert(0);
                         break;
 
-                    case PYBC_GET_ITER:
+                    case MP_BC_GET_ITER:
                         SET_TOP(rt_getiter(TOP()));
                         break;
 
-                    case PYBC_FOR_ITER:
+                    case MP_BC_FOR_ITER:
                         DECODE_ULABEL; // the jump offset if iteration finishes; for labels are always forward
                         obj1 = rt_iternext(TOP());
-                        if (obj1 == py_const_stop_iteration) {
+                        if (obj1 == mp_const_stop_iteration) {
                             ++sp; // pop the exhausted iterator
                             ip += unum; // jump to after for-block
                         } else {
@@ -300,110 +302,110 @@ bool py_execute_byte_code_2(const byte **ip_in_out, py_obj_t *fastn, py_obj_t **
                         }
                         break;
 
-                    case PYBC_POP_BLOCK:
+                    case MP_BC_POP_BLOCK:
                         // pops block and restores the stack
                         assert(0);
                         break;
 
-                    case PYBC_POP_EXCEPT:
+                    case MP_BC_POP_EXCEPT:
                         // TODO need to work out how blocks work etc
                         // pops block, checks it's an exception block, and restores the stack, saving the 3 exception values to local threadstate
                         assert(exc_sp >= &exc_stack[0]);
-                        //sp = (py_obj_t*)(*exc_sp--);
+                        //sp = (mp_obj_t*)(*exc_sp--);
                         //exc_sp--; // discard ip
                         exc_sp -= 2;
                         //sp += 3; // pop 3 exception values
                         break;
 
-                    case PYBC_UNARY_OP:
+                    case MP_BC_UNARY_OP:
                         unum = *ip++;
                         SET_TOP(rt_unary_op(unum, TOP()));
                         break;
 
-                    case PYBC_BINARY_OP:
+                    case MP_BC_BINARY_OP:
                         unum = *ip++;
                         obj2 = POP();
                         obj1 = TOP();
                         SET_TOP(rt_binary_op(unum, obj1, obj2));
                         break;
 
-                    case PYBC_COMPARE_OP:
+                    case MP_BC_COMPARE_OP:
                         unum = *ip++;
                         obj2 = POP();
                         obj1 = TOP();
                         SET_TOP(rt_compare_op(unum, obj1, obj2));
                         break;
 
-                    case PYBC_BUILD_TUPLE:
+                    case MP_BC_BUILD_TUPLE:
                         DECODE_UINT;
                         obj1 = rt_build_tuple(unum, sp);
                         sp += unum - 1;
                         SET_TOP(obj1);
                         break;
 
-                    case PYBC_BUILD_LIST:
+                    case MP_BC_BUILD_LIST:
                         DECODE_UINT;
                         obj1 = rt_build_list(unum, sp);
                         sp += unum - 1;
                         SET_TOP(obj1);
                         break;
 
-                    case PYBC_LIST_APPEND:
+                    case MP_BC_LIST_APPEND:
                         DECODE_UINT;
                         // I think it's guaranteed by the compiler that sp[unum] is a list
                         rt_list_append(sp[unum], sp[0]);
                         sp++;
                         break;
 
-                    case PYBC_BUILD_MAP:
+                    case MP_BC_BUILD_MAP:
                         DECODE_UINT;
                         PUSH(rt_build_map(unum));
                         break;
 
-                    case PYBC_STORE_MAP:
+                    case MP_BC_STORE_MAP:
                         sp += 2;
                         rt_store_map(sp[0], sp[-2], sp[-1]);
                         break;
 
-                    case PYBC_MAP_ADD:
+                    case MP_BC_MAP_ADD:
                         DECODE_UINT;
                         // I think it's guaranteed by the compiler that sp[unum + 1] is a map
                         rt_store_map(sp[unum + 1], sp[0], sp[1]);
                         sp += 2;
                         break;
 
-                    case PYBC_BUILD_SET:
+                    case MP_BC_BUILD_SET:
                         DECODE_UINT;
                         obj1 = rt_build_set(unum, sp);
                         sp += unum - 1;
                         SET_TOP(obj1);
                         break;
 
-                    case PYBC_SET_ADD:
+                    case MP_BC_SET_ADD:
                         DECODE_UINT;
                         // I think it's guaranteed by the compiler that sp[unum] is a set
                         rt_store_set(sp[unum], sp[0]);
                         sp++;
                         break;
 
-                    case PYBC_UNPACK_SEQUENCE:
+                    case MP_BC_UNPACK_SEQUENCE:
                         DECODE_UINT;
                         rt_unpack_sequence(sp[0], unum, sp - unum + 1);
                         sp -= unum - 1;
                         break;
 
-                    case PYBC_MAKE_FUNCTION:
+                    case MP_BC_MAKE_FUNCTION:
                         DECODE_UINT;
                         PUSH(rt_make_function_from_id(unum));
                         break;
 
-                    case PYBC_MAKE_CLOSURE:
+                    case MP_BC_MAKE_CLOSURE:
                         DECODE_UINT;
                         obj1 = POP();
                         PUSH(rt_make_closure_from_id(unum, obj1));
                         break;
 
-                    case PYBC_CALL_FUNCTION:
+                    case MP_BC_CALL_FUNCTION:
                         DECODE_UINT;
                         assert((unum & 0xff00) == 0); // n_keyword
                         unum &= 0xff; // n_positional
@@ -411,7 +413,7 @@ bool py_execute_byte_code_2(const byte **ip_in_out, py_obj_t *fastn, py_obj_t **
                         *sp = rt_call_function_n(*sp, unum, sp - unum);
                         break;
 
-                    case PYBC_CALL_METHOD:
+                    case MP_BC_CALL_METHOD:
                         DECODE_UINT;
                         if ((unum & 0xff00) == 0) {
                             // no keywords
@@ -426,13 +428,13 @@ bool py_execute_byte_code_2(const byte **ip_in_out, py_obj_t *fastn, py_obj_t **
                         SET_TOP(obj1);
                         break;
 
-                    case PYBC_RETURN_VALUE:
+                    case MP_BC_RETURN_VALUE:
                         nlr_pop();
                         *sp_in_out = sp;
                         assert(exc_sp == &exc_stack[-1]);
                         return false;
 
-                    case PYBC_YIELD_VALUE:
+                    case MP_BC_YIELD_VALUE:
                         nlr_pop();
                         *ip_in_out = ip;
                         fastn[0] = fast0;
@@ -441,13 +443,13 @@ bool py_execute_byte_code_2(const byte **ip_in_out, py_obj_t *fastn, py_obj_t **
                         *sp_in_out = sp;
                         return true;
 
-                    case PYBC_IMPORT_NAME:
+                    case MP_BC_IMPORT_NAME:
                         DECODE_QSTR;
                         obj1 = POP();
                         SET_TOP(rt_import_name(qstr, obj1, TOP()));
                         break;
 
-                    case PYBC_IMPORT_FROM:
+                    case MP_BC_IMPORT_FROM:
                         DECODE_QSTR;
                         obj1 = rt_import_from(TOP(), qstr);
                         PUSH(obj1);
@@ -466,12 +468,12 @@ bool py_execute_byte_code_2(const byte **ip_in_out, py_obj_t *fastn, py_obj_t **
 
             if (exc_sp >= &exc_stack[0]) {
                 // catch exception and pass to byte code
-                sp = (py_obj_t*)(exc_sp[0]);
+                sp = (mp_obj_t*)(exc_sp[0]);
                 ip = (const byte*)(exc_sp[-1]);
                 // push(traceback, exc-val, exc-type)
-                PUSH(py_const_none);
+                PUSH(mp_const_none);
                 PUSH(nlr.ret_val);
-                PUSH(py_const_none);
+                PUSH(mp_const_none);
             } else {
                 // re-raise exception
                 // TODO what to do if this is a generator??
diff --git a/stm/Makefile b/stm/Makefile
index 8817717950989854af3217dcbdf310ec06f154fc..1e0831159e9417f9961a7fb13f0874cb32ed1e0b 100644
--- a/stm/Makefile
+++ b/stm/Makefile
@@ -23,6 +23,7 @@ SRC_C = \
 	lexerstm.c \
 	led.c \
 	lcd.c \
+	servo.c \
 	flash.c \
 	storage.c \
 	mma.c \
@@ -57,6 +58,25 @@ PY_O = \
 	runtime.o \
 	map.o \
 	obj.o \
+	objbool.o \
+	objboundmeth.o \
+	objcell.o \
+	objclass.o \
+	objclosure.o \
+	objcomplex.o \
+	objdict.o \
+	objexcept.o \
+	objfloat.o \
+	objfun.o \
+	objgenerator.o \
+	objinstance.o \
+	objlist.o \
+	objnone.o \
+	objrange.o \
+	objset.o \
+	objstr.o \
+	objtuple.o \
+	objtype.o \
 	builtin.o \
 	vm.o \
 	repl.o \
@@ -150,7 +170,7 @@ $(BUILD)/%.o: $(CC3KSRC)/%.c
 $(BUILD)/%.o: $(PYSRC)/%.s
 	$(AS) -o $@ $<
 
-$(BUILD)/%.o: $(PYSRC)/%.c mpyconfig.h
+$(BUILD)/%.o: $(PYSRC)/%.c mpconfig.h
 	$(CC) $(CFLAGS) -c -o $@ $<
 
 $(BUILD)/emitnthumb.o: $(PYSRC)/emitnative.c $(PYSRC)/emit.h
diff --git a/stm/audio.c b/stm/audio.c
index 41614416e2c3b31f9de24f683430db48ed09f0ac..62d2ff2f861fc26da6c540847fdb0cfb89e62df1 100644
--- a/stm/audio.c
+++ b/stm/audio.c
@@ -6,9 +6,10 @@
 
 #include "nlr.h"
 #include "misc.h"
-#include "mpyconfig.h"
+#include "mpconfig.h"
 #include "parse.h"
 #include "compile.h"
+#include "obj.h"
 #include "runtime.h"
 
 #include "audio.h"
@@ -44,22 +45,22 @@ void audio_drain(void) {
 }
 
 // direct access to DAC
-py_obj_t pyb_audio_dac(py_obj_t val) {
-    DAC_SetChannel2Data(DAC_Align_8b_R, py_obj_get_int(val));
-    return py_const_none;
+mp_obj_t pyb_audio_dac(mp_obj_t val) {
+    DAC_SetChannel2Data(DAC_Align_8b_R, mp_obj_get_int(val));
+    return mp_const_none;
 }
 
-py_obj_t pyb_audio_is_full(void) {
+mp_obj_t pyb_audio_is_full(void) {
     if (audio_is_full()) {
-        return py_const_true;
+        return mp_const_true;
     } else {
-        return py_const_false;
+        return mp_const_false;
     }
 }
 
-py_obj_t pyb_audio_fill(py_obj_t val) {
-    audio_fill(py_obj_get_int(val));
-    return py_const_none;
+mp_obj_t pyb_audio_fill(mp_obj_t val) {
+    audio_fill(mp_obj_get_int(val));
+    return mp_const_none;
 }
 
 void audio_init(void) {
@@ -90,7 +91,7 @@ void audio_init(void) {
     // enable interrupt
 
     // Python interface
-    py_obj_t m = py_module_new();
+    mp_obj_t m = mp_module_new();
     rt_store_attr(m, qstr_from_str_static("dac"), rt_make_function_1(pyb_audio_dac));
     rt_store_attr(m, qstr_from_str_static("is_full"), rt_make_function_0(pyb_audio_is_full));
     rt_store_attr(m, qstr_from_str_static("fill"), rt_make_function_1(pyb_audio_fill));
diff --git a/stm/lcd.c b/stm/lcd.c
index 5d2c677349cd02e2af088323c42626b7aa7736d0..cec5bf548d426a23fb25a6faf4f83e51b456b01f 100644
--- a/stm/lcd.c
+++ b/stm/lcd.c
@@ -3,9 +3,10 @@
 
 #include "nlr.h"
 #include "misc.h"
-#include "mpyconfig.h"
+#include "mpconfig.h"
 #include "parse.h"
 #include "compile.h"
+#include "obj.h"
 #include "runtime.h"
 
 #include "systick.h"
@@ -88,16 +89,16 @@ static void lcd_data_out(uint8_t i) {
 
 // writes 8 vertical pixels
 // pos 0 is upper left, pos 1 is 8 pixels to right of that, pos 128 is 8 pixels below that
-py_obj_t lcd_draw_pixel_8(py_obj_t py_pos, py_obj_t py_val) {
-    int pos = py_obj_get_int(py_pos);
-    int val = py_obj_get_int(py_val);
+mp_obj_t lcd_draw_pixel_8(mp_obj_t mp_pos, mp_obj_t mp_val) {
+    int pos = mp_obj_get_int(mp_pos);
+    int val = mp_obj_get_int(mp_val);
     int page = pos / 128;
     int offset = pos - (page * 128);
     lcd_out(LCD_INSTR, 0xb0 | page); // page address set
     lcd_out(LCD_INSTR, 0x10 | ((offset >> 4) & 0x0f)); // column address set upper
     lcd_out(LCD_INSTR, 0x00 | (offset & 0x0f)); // column address set lower
     lcd_out(LCD_DATA, val); // write data
-    return py_const_none;
+    return mp_const_none;
 }
 
 #define LCD_BUF_W (16)
@@ -112,45 +113,45 @@ int lcd_next_line;
 byte lcd_pix_buf[LCD_PIX_BUF_SIZE];
 byte lcd_pix_buf2[LCD_PIX_BUF_SIZE];
 
-py_obj_t lcd_pix_clear(void) {
+mp_obj_t lcd_pix_clear(void) {
     memset(lcd_pix_buf, 0, LCD_PIX_BUF_SIZE);
     memset(lcd_pix_buf2, 0, LCD_PIX_BUF_SIZE);
-    return py_const_none;
+    return mp_const_none;
 }
 
-py_obj_t lcd_pix_get(py_obj_t py_x, py_obj_t py_y) {
-    int x = py_obj_get_int(py_x);
-    int y = py_obj_get_int(py_y);
+mp_obj_t lcd_pix_get(mp_obj_t mp_x, mp_obj_t mp_y) {
+    int x = mp_obj_get_int(mp_x);
+    int y = mp_obj_get_int(mp_y);
     if (0 <= x && x <= 127 && 0 <= y && y <= 31) {
         uint byte_pos = x + 128 * ((uint)y >> 3);
         if (lcd_pix_buf[byte_pos] & (1 << (y & 7))) {
-            return py_obj_new_int(1);
+            return mp_obj_new_int(1);
         }
     }
-    return py_obj_new_int(0);
+    return mp_obj_new_int(0);
 }
 
-py_obj_t lcd_pix_set(py_obj_t py_x, py_obj_t py_y) {
-    int x = py_obj_get_int(py_x);
-    int y = py_obj_get_int(py_y);
+mp_obj_t lcd_pix_set(mp_obj_t mp_x, mp_obj_t mp_y) {
+    int x = mp_obj_get_int(mp_x);
+    int y = mp_obj_get_int(mp_y);
     if (0 <= x && x <= 127 && 0 <= y && y <= 31) {
         uint byte_pos = x + 128 * ((uint)y >> 3);
         lcd_pix_buf2[byte_pos] |= 1 << (y & 7);
     }
-    return py_const_none;
+    return mp_const_none;
 }
 
-py_obj_t lcd_pix_reset(py_obj_t py_x, py_obj_t py_y) {
-    int x = py_obj_get_int(py_x);
-    int y = py_obj_get_int(py_y);
+mp_obj_t lcd_pix_reset(mp_obj_t mp_x, mp_obj_t mp_y) {
+    int x = mp_obj_get_int(mp_x);
+    int y = mp_obj_get_int(mp_y);
     if (0 <= x && x <= 127 && 0 <= y && y <= 31) {
         uint byte_pos = x + 128 * ((uint)y >> 3);
         lcd_pix_buf2[byte_pos] &= ~(1 << (y & 7));
     }
-    return py_const_none;
+    return mp_const_none;
 }
 
-py_obj_t lcd_pix_show(void) {
+mp_obj_t lcd_pix_show(void) {
     memcpy(lcd_pix_buf, lcd_pix_buf2, LCD_PIX_BUF_SIZE);
     for (uint page = 0; page < 4; page++) {
         lcd_out(LCD_INSTR, 0xb0 | page); // page address set
@@ -160,12 +161,12 @@ py_obj_t lcd_pix_show(void) {
             lcd_out(LCD_DATA, lcd_pix_buf[i + 128 * page]);
         }
     }
-    return py_const_none;
+    return mp_const_none;
 }
 
-py_obj_t lcd_print(py_obj_t text) {
-    lcd_print_str(qstr_str(py_obj_get_qstr(text)));
-    return py_const_none;
+mp_obj_t lcd_print(mp_obj_t text) {
+    lcd_print_str(qstr_str(mp_obj_get_qstr(text)));
+    return mp_const_none;
 }
 
 void lcd_init(void) {
@@ -219,7 +220,7 @@ void lcd_init(void) {
     lcd_next_line = 0;
 
     // Python interface
-    py_obj_t m = py_module_new();
+    mp_obj_t m = mp_module_new();
     rt_store_attr(m, qstr_from_str_static("lcd8"), rt_make_function_2(lcd_draw_pixel_8));
     rt_store_attr(m, qstr_from_str_static("clear"), rt_make_function_0(lcd_pix_clear));
     rt_store_attr(m, qstr_from_str_static("get"), rt_make_function_2(lcd_pix_get));
diff --git a/stm/led.c b/stm/led.c
index a48ccf5f0880b0886631913ba518e2611147182d..08077641a9cccbbad82f88e669fe07fc22da8364 100644
--- a/stm/led.c
+++ b/stm/led.c
@@ -1,5 +1,10 @@
+#include <stdio.h>
 #include <stm32f4xx.h>
 #include <stm32f4xx_gpio.h>
+
+#include "misc.h"
+#include "mpconfig.h"
+#include "obj.h"
 #include "led.h"
 
 #define PYB_LED_R_PORT (GPIOA)
@@ -64,3 +69,60 @@ void led_toggle(pyb_led_t led) {
         port->BSRRH = pin;
     }
 }
+
+/******************************************************************************/
+/* Micro Python bindings                                                      */
+
+typedef struct _pyb_led_obj_t {
+    mp_obj_base_t base;
+    uint led_id;
+} pyb_led_obj_t;
+
+void led_obj_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in) {
+    pyb_led_obj_t *self = self_in;
+    print(env, "<LED %lu>", self->led_id);
+}
+
+mp_obj_t led_obj_on(mp_obj_t self_in) {
+    pyb_led_obj_t *self = self_in;
+    switch (self->led_id) {
+        case 1: led_state(PYB_LED_G1, 1); break;
+        case 2: led_state(PYB_LED_G2, 1); break;
+    }
+    return mp_const_none;
+}
+
+mp_obj_t led_obj_off(mp_obj_t self_in) {
+    pyb_led_obj_t *self = self_in;
+    switch (self->led_id) {
+        case 1: led_state(PYB_LED_G1, 0); break;
+        case 2: led_state(PYB_LED_G2, 0); break;
+    }
+    return mp_const_none;
+}
+
+static MP_DEFINE_CONST_FUN_OBJ_1(led_obj_on_obj, led_obj_on);
+static MP_DEFINE_CONST_FUN_OBJ_1(led_obj_off_obj, led_obj_off);
+
+static const mp_obj_type_t led_obj_type = {
+    { &mp_const_type },
+    "Led",
+    led_obj_print, // print
+    NULL, // call_n
+    NULL, // unary_op
+    NULL, // binary_op
+    NULL, // getiter
+    NULL, // iternext
+    { // method list
+        { "on", &led_obj_on_obj },
+        { "off", &led_obj_off_obj },
+        { NULL, NULL },
+    }
+};
+
+mp_obj_t pyb_Led(mp_obj_t led_id) {
+    pyb_led_obj_t *o = m_new_obj(pyb_led_obj_t);
+    o->base.type = &led_obj_type;
+    o->led_id = mp_obj_get_int(led_id);
+    return o;
+}
diff --git a/stm/led.h b/stm/led.h
index c4b68196b2a4a443bb0717af93a7bbab09eec10b..e6d0c30bcabc073572e0989707b9a280d44904b9 100644
--- a/stm/led.h
+++ b/stm/led.h
@@ -8,3 +8,5 @@ typedef enum {
 void led_init(void);
 void led_state(pyb_led_t led, int state);
 void led_toggle(pyb_led_t led);
+
+mp_obj_t pyb_Led(mp_obj_t led_id);
diff --git a/stm/lexerstm.c b/stm/lexerstm.c
index 06ea04d1b977aa09126973de4527af271393e7ac..4e99052242230f5c913b9e2baa717ac8cf23888b 100644
--- a/stm/lexerstm.c
+++ b/stm/lexerstm.c
@@ -7,37 +7,37 @@
 #include "lexer.h"
 #include "lexerstm.h"
 
-unichar str_buf_next_char(py_lexer_str_buf_t *sb) {
+unichar str_buf_next_char(mp_lexer_str_buf_t *sb) {
     if (sb->src_cur < sb->src_end) {
         return *sb->src_cur++;
     } else {
-        return PY_LEXER_CHAR_EOF;
+        return MP_LEXER_CHAR_EOF;
     }
 }
 
-void str_buf_free(py_lexer_str_buf_t *sb) {
+void str_buf_free(mp_lexer_str_buf_t *sb) {
     if (sb->free) {
         m_free((char*)sb->src_beg);
     }
 }
 
-py_lexer_t *py_lexer_new_from_str_len(const char *src_name, const char *str, uint len, bool free_str, py_lexer_str_buf_t *sb) {
+mp_lexer_t *mp_lexer_new_from_str_len(const char *src_name, const char *str, uint len, bool free_str, mp_lexer_str_buf_t *sb) {
     sb->free = free_str;
     sb->src_beg = str;
     sb->src_cur = str;
     sb->src_end = str + len;
-    return py_lexer_new(src_name, sb, (py_lexer_stream_next_char_t)str_buf_next_char, (py_lexer_stream_close_t)str_buf_free);
+    return mp_lexer_new(src_name, sb, (mp_lexer_stream_next_char_t)str_buf_next_char, (mp_lexer_stream_close_t)str_buf_free);
 }
 
-unichar file_buf_next_char(py_lexer_file_buf_t *fb) {
+unichar file_buf_next_char(mp_lexer_file_buf_t *fb) {
     if (fb->pos >= fb->len) {
         if (fb->len < sizeof(fb->buf)) {
-            return PY_LEXER_CHAR_EOF;
+            return MP_LEXER_CHAR_EOF;
         } else {
             UINT n;
             f_read(&fb->fp, fb->buf, sizeof(fb->buf), &n);
             if (n == 0) {
-                return PY_LEXER_CHAR_EOF;
+                return MP_LEXER_CHAR_EOF;
             }
             fb->len = n;
             fb->pos = 0;
@@ -46,11 +46,11 @@ unichar file_buf_next_char(py_lexer_file_buf_t *fb) {
     return fb->buf[fb->pos++];
 }
 
-void file_buf_close(py_lexer_file_buf_t *fb) {
+void file_buf_close(mp_lexer_file_buf_t *fb) {
     f_close(&fb->fp);
 }
 
-py_lexer_t *py_lexer_new_from_file(const char *filename, py_lexer_file_buf_t *fb) {
+mp_lexer_t *mp_lexer_new_from_file(const char *filename, mp_lexer_file_buf_t *fb) {
     FRESULT res = f_open(&fb->fp, filename, FA_READ);
     if (res != FR_OK) {
         return NULL;
@@ -59,5 +59,5 @@ py_lexer_t *py_lexer_new_from_file(const char *filename, py_lexer_file_buf_t *fb
     f_read(&fb->fp, fb->buf, sizeof(fb->buf), &n);
     fb->len = n;
     fb->pos = 0;
-    return py_lexer_new(filename, fb, (py_lexer_stream_next_char_t)file_buf_next_char, (py_lexer_stream_close_t)file_buf_close);
+    return mp_lexer_new(filename, fb, (mp_lexer_stream_next_char_t)file_buf_next_char, (mp_lexer_stream_close_t)file_buf_close);
 }
diff --git a/stm/lexerstm.h b/stm/lexerstm.h
index f57d1faa9d2f46b5e3917a72f3d5768a838e8a47..7e090898a2fd2449b31ae2bb556e2cdf81ff32eb 100644
--- a/stm/lexerstm.h
+++ b/stm/lexerstm.h
@@ -3,14 +3,14 @@ typedef struct _py_lexer_str_buf_t {
     const char *src_beg;        // beginning of source
     const char *src_cur;        // current location in source
     const char *src_end;        // end (exclusive) of source
-} py_lexer_str_buf_t;
+} mp_lexer_str_buf_t;
 
 typedef struct _py_lexer_file_buf_t {
     FIL fp;
     char buf[20];
     uint16_t len;
     uint16_t pos;
-} py_lexer_file_buf_t;
+} mp_lexer_file_buf_t;
 
-py_lexer_t *py_lexer_new_from_str_len(const char *src_name, const char *str, uint len, bool free_str, py_lexer_str_buf_t *sb);
-py_lexer_t *py_lexer_new_from_file(const char *filename, py_lexer_file_buf_t *fb);
+mp_lexer_t *mp_lexer_new_from_str_len(const char *src_name, const char *str, uint len, bool free_str, mp_lexer_str_buf_t *sb);
+mp_lexer_t *mp_lexer_new_from_file(const char *filename, mp_lexer_file_buf_t *fb);
diff --git a/stm/main.c b/stm/main.c
index a262a917e787553ef9d5b55094b0e89a0a1d1134..f45e8998541d864f8c53f5d074114aefe0da943f 100644
--- a/stm/main.c
+++ b/stm/main.c
@@ -13,16 +13,27 @@
 #include "std.h"
 
 #include "misc.h"
-#include "mpyconfig.h"
+#include "ff.h"
+#include "mpconfig.h"
+#include "nlr.h"
+#include "misc.h"
+#include "lexer.h"
+#include "lexerstm.h"
+#include "parse.h"
+#include "compile.h"
+#include "obj.h"
+#include "runtime0.h"
+#include "runtime.h"
+#include "repl.h"
 #include "gc.h"
 #include "systick.h"
 #include "led.h"
+#include "servo.h"
 #include "lcd.h"
 #include "storage.h"
 #include "mma.h"
 #include "usart.h"
 #include "usb.h"
-#include "ff.h"
 #include "timer.h"
 #include "audio.h"
 #include "pybwlan.h"
@@ -114,128 +125,43 @@ void __fatal_error(const char *msg) {
     }
 }
 
-#include "nlr.h"
-#include "misc.h"
-#include "lexer.h"
-#include "lexerstm.h"
-#include "mpyconfig.h"
-#include "parse.h"
-#include "compile.h"
-#include "runtime.h"
-#include "obj.h"
-#include "repl.h"
-
 static qstr pyb_config_source_dir = 0;
 static qstr pyb_config_main = 0;
 
-py_obj_t pyb_source_dir(py_obj_t source_dir) {
-    pyb_config_source_dir = py_obj_get_qstr(source_dir);
-    return py_const_none;
+mp_obj_t pyb_source_dir(mp_obj_t source_dir) {
+    pyb_config_source_dir = mp_obj_get_qstr(source_dir);
+    return mp_const_none;
 }
 
-py_obj_t pyb_main(py_obj_t main) {
-    pyb_config_main = py_obj_get_qstr(main);
-    return py_const_none;
+mp_obj_t pyb_main(mp_obj_t main) {
+    pyb_config_main = mp_obj_get_qstr(main);
+    return mp_const_none;
 }
 
 // sync all file systems
-py_obj_t pyb_sync(void) {
+mp_obj_t pyb_sync(void) {
     storage_flush();
-    return py_const_none;
+    return mp_const_none;
 }
 
-py_obj_t pyb_delay(py_obj_t count) {
-    sys_tick_delay_ms(py_obj_get_int(count));
-    return py_const_none;
+mp_obj_t pyb_delay(mp_obj_t count) {
+    sys_tick_delay_ms(mp_obj_get_int(count));
+    return mp_const_none;
 }
 
-py_obj_t pyb_led(py_obj_t state) {
+mp_obj_t pyb_led(mp_obj_t state) {
     led_state(PYB_LED_G1, rt_is_true(state));
     return state;
 }
 
-void led_obj_print(py_obj_t self) {
-    machine_uint_t led_id;
-    py_user_get_data(self, &led_id, NULL);
-    printf("<LED %lu>", led_id);
-}
-
-py_obj_t led_obj_on(py_obj_t self) {
-    machine_uint_t led_id;
-    py_user_get_data(self, &led_id, NULL);
-    switch (led_id) {
-        case 1: led_state(PYB_LED_G1, 1); break;
-        case 2: led_state(PYB_LED_G2, 1); break;
-    }
-    return py_const_none;
-}
-
-py_obj_t led_obj_off(py_obj_t self) {
-    machine_uint_t led_id;
-    py_user_get_data(self, &led_id, NULL);
-    switch (led_id) {
-        case 1: led_state(PYB_LED_G1, 0); break;
-        case 2: led_state(PYB_LED_G2, 0); break;
-    }
-    return py_const_none;
-}
-
-const py_user_info_t led_obj_info = {
-    "Led",
-    led_obj_print,
-    {
-        {"on", 0, led_obj_on},
-        {"off", 0, led_obj_off},
-        {NULL, 0, NULL},
-    }
-};
-
-py_obj_t pyb_Led(py_obj_t led_id) {
-    return py_obj_new_user(&led_obj_info, (machine_uint_t)py_obj_get_int(led_id), 0);
-}
-
-py_obj_t pyb_sw(void) {
+mp_obj_t pyb_sw(void) {
     if (sw_get()) {
-        return py_const_true;
+        return mp_const_true;
     } else {
-        return py_const_false;
+        return mp_const_false;
     }
 }
 
-void servo_obj_print(py_obj_t self) {
-    machine_uint_t servo_id;
-    py_user_get_data(self, &servo_id, NULL);
-    printf("<Servo %lu>", servo_id);
-}
-
-py_obj_t servo_obj_angle(py_obj_t self, py_obj_t angle) {
-    machine_uint_t servo_id;
-    py_user_get_data(self, &servo_id, NULL);
-    machine_int_t v = 152 + 85.0 * py_obj_get_float(angle) / 90.0;
-    if (v < 65) { v = 65; }
-    if (v > 210) { v = 210; }
-    switch (servo_id) {
-        case 1: TIM2->CCR1 = v; break;
-        case 2: TIM2->CCR2 = v; break;
-        case 3: TIM2->CCR3 = v; break;
-        case 4: TIM2->CCR4 = v; break;
-    }
-    return py_const_none;
-}
-
-const py_user_info_t servo_obj_info = {
-    "Servo",
-    servo_obj_print,
-    {
-        {"angle", 1, servo_obj_angle},
-        {NULL, 0, NULL},
-    }
-};
-
-py_obj_t pyb_Servo(py_obj_t servo_id) {
-    return py_obj_new_user(&servo_obj_info, (machine_uint_t)py_obj_get_int(servo_id), 0);
-}
-
 /*
 void g(uint i) {
     printf("g:%d\n", i);
@@ -306,13 +232,13 @@ static const char *help_text =
 ;
 
 // get some help about available functions
-static py_obj_t pyb_help(void) {
+static mp_obj_t pyb_help(void) {
     printf("%s", help_text);
-    return py_const_none;
+    return mp_const_none;
 }
 
 // get lots of info about the board
-static py_obj_t pyb_info(void) {
+static mp_obj_t pyb_info(void) {
     // get and print unique id; 96 bits
     {
         byte *id = (byte*)0x1fff7a10;
@@ -364,14 +290,14 @@ static py_obj_t pyb_info(void) {
         printf("LFS free: %u bytes\n", (uint)(nclst * fatfs->csize * 512));
     }
 
-    return py_const_none;
+    return mp_const_none;
 }
 
 // SD card test
-static py_obj_t pyb_sd_test(void) {
+static mp_obj_t pyb_sd_test(void) {
     extern void sdio_init(void);
     sdio_init();
-    return py_const_none;
+    return mp_const_none;
 }
 
 static void SYSCLKConfig_STOP(void) {
@@ -398,7 +324,7 @@ static void SYSCLKConfig_STOP(void) {
     }
 }
 
-static py_obj_t pyb_stop(void) {
+static mp_obj_t pyb_stop(void) {
     PWR_EnterSTANDBYMode();
     //PWR_FlashPowerDownCmd(ENABLE); don't know what the logic is with this
 
@@ -411,28 +337,28 @@ static py_obj_t pyb_stop(void) {
 
     //PWR_FlashPowerDownCmd(DISABLE);
 
-    return py_const_none;
+    return mp_const_none;
 }
 
-static py_obj_t pyb_standby(void) {
+static mp_obj_t pyb_standby(void) {
     PWR_EnterSTANDBYMode();
-    return py_const_none;
+    return mp_const_none;
 }
 
-py_obj_t pyb_usart_send(py_obj_t data) {
-    usart_tx_char(py_obj_get_int(data));
-    return py_const_none;
+mp_obj_t pyb_usart_send(mp_obj_t data) {
+    usart_tx_char(mp_obj_get_int(data));
+    return mp_const_none;
 }
 
-py_obj_t pyb_usart_receive(void) {
-    return py_obj_new_int(usart_rx_char());
+mp_obj_t pyb_usart_receive(void) {
+    return mp_obj_new_int(usart_rx_char());
 }
 
-py_obj_t pyb_usart_status(void) {
+mp_obj_t pyb_usart_status(void) {
     if (usart_rx_any()) {
-        return py_const_true;
+        return mp_const_true;
     } else {
-        return py_const_false;
+        return mp_const_false;
     }
 }
 
@@ -544,7 +470,7 @@ void do_repl(void) {
             continue;
         }
 
-        if (py_repl_is_compound_stmt(vstr_str(&line))) {
+        if (mp_repl_is_compound_stmt(vstr_str(&line))) {
             for (;;) {
                 vstr_add_char(&line, '\n');
                 int len = vstr_len(&line);
@@ -556,16 +482,16 @@ void do_repl(void) {
             }
         }
 
-        py_lexer_str_buf_t sb;
-        py_lexer_t *lex = py_lexer_new_from_str_len("<stdin>", vstr_str(&line), vstr_len(&line), false, &sb);
-        py_parse_node_t pn = py_parse(lex, PY_PARSE_SINGLE_INPUT);
-        py_lexer_free(lex);
+        mp_lexer_str_buf_t sb;
+        mp_lexer_t *lex = mp_lexer_new_from_str_len("<stdin>", vstr_str(&line), vstr_len(&line), false, &sb);
+        mp_parse_node_t pn = mp_parse(lex, MP_PARSE_SINGLE_INPUT);
+        mp_lexer_free(lex);
 
-        if (pn != PY_PARSE_NODE_NULL) {
-            bool comp_ok = py_compile(pn, true);
+        if (pn != MP_PARSE_NODE_NULL) {
+            bool comp_ok = mp_compile(pn, true);
             if (comp_ok) {
-                py_obj_t module_fun = rt_make_function_from_id(1);
-                if (module_fun != py_const_none) {
+                mp_obj_t module_fun = rt_make_function_from_id(1);
+                if (module_fun != mp_const_none) {
                     nlr_buf_t nlr;
                     uint32_t start = sys_tick_counter;
                     if (nlr_push(&nlr) == 0) {
@@ -578,7 +504,7 @@ void do_repl(void) {
                         }
                     } else {
                         // uncaught exception
-                        py_obj_print((py_obj_t)nlr.ret_val);
+                        mp_obj_print((mp_obj_t)nlr.ret_val);
                         printf("\n");
                     }
                 }
@@ -590,28 +516,28 @@ void do_repl(void) {
 }
 
 bool do_file(const char *filename) {
-    py_lexer_file_buf_t fb;
-    py_lexer_t *lex = py_lexer_new_from_file(filename, &fb);
+    mp_lexer_file_buf_t fb;
+    mp_lexer_t *lex = mp_lexer_new_from_file(filename, &fb);
 
     if (lex == NULL) {
         printf("could not open file '%s' for reading\n", filename);
         return false;
     }
 
-    py_parse_node_t pn = py_parse(lex, PY_PARSE_FILE_INPUT);
-    py_lexer_free(lex);
+    mp_parse_node_t pn = mp_parse(lex, MP_PARSE_FILE_INPUT);
+    mp_lexer_free(lex);
 
-    if (pn == PY_PARSE_NODE_NULL) {
+    if (pn == MP_PARSE_NODE_NULL) {
         return false;
     }
 
-    bool comp_ok = py_compile(pn, false);
+    bool comp_ok = mp_compile(pn, false);
     if (!comp_ok) {
         return false;
     }
 
-    py_obj_t module_fun = rt_make_function_from_id(1);
-    if (module_fun == py_const_none) {
+    mp_obj_t module_fun = rt_make_function_from_id(1);
+    if (module_fun == mp_const_none) {
         return false;
     }
 
@@ -622,7 +548,7 @@ bool do_file(const char *filename) {
         return true;
     } else {
         // uncaught exception
-        py_obj_print((py_obj_t)nlr.ret_val);
+        mp_obj_print((mp_obj_t)nlr.ret_val);
         printf("\n");
         return false;
     }
@@ -655,115 +581,16 @@ void gc_collect(void) {
     }
 }
 
-py_obj_t pyb_gc(void) {
+mp_obj_t pyb_gc(void) {
     gc_collect();
-    return py_const_none;
-}
-
-// PWM
-// TIM2 and TIM5 have CH1, CH2, CH3, CH4 on PA0-PA3 respectively
-// they are both 32-bit counters
-// 16-bit prescaler
-// TIM2_CH3 also on PB10 (used below)
-void servo_init(void) {
-    // TIM2 clock enable
-    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
-
-    // for PB10
-    /*
-    // GPIOB Configuration: TIM2_CH3 (PB10)
-    GPIO_InitTypeDef GPIO_InitStructure;
-    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
-    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
-    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
-    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
-    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
-    GPIO_Init(GPIOB, &GPIO_InitStructure);
-
-    // Connect TIM2 pins to AF1
-    GPIO_PinAFConfig(GPIOB, GPIO_PinSource10, GPIO_AF_TIM2);
-    */
-
-    // for PA0, PA1, PA2, PA3
-    {
-        // GPIOA Configuration: TIM2_CH0, TIM2_CH1 (PA0, PA1)
-        GPIO_InitTypeDef GPIO_InitStructure;
-        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
-        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
-        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
-        GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
-        GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
-        GPIO_Init(GPIOA, &GPIO_InitStructure);
-
-        // Connect TIM2 pins to AF1
-        GPIO_PinAFConfig(GPIOA, GPIO_PinSource0, GPIO_AF_TIM2);
-        GPIO_PinAFConfig(GPIOA, GPIO_PinSource1, GPIO_AF_TIM2);
-        GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_TIM2);
-        GPIO_PinAFConfig(GPIOA, GPIO_PinSource3, GPIO_AF_TIM2);
-    }
-
-    // Compute the prescaler value so TIM2 runs at 100kHz
-    uint16_t PrescalerValue = (uint16_t) ((SystemCoreClock / 2) / 100000) - 1;
-
-    // Time base configuration
-    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
-    TIM_TimeBaseStructure.TIM_Period = 2000; // timer cycles at 50Hz
-    TIM_TimeBaseStructure.TIM_Prescaler = PrescalerValue;
-    TIM_TimeBaseStructure.TIM_ClockDivision = 0;
-    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
-    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
-
-    // PWM Mode configuration
-    TIM_OCInitTypeDef TIM_OCInitStructure;
-    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
-    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
-    TIM_OCInitStructure.TIM_Pulse = 150; // units of 10us
-    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
-    TIM_OC1Init(TIM2, &TIM_OCInitStructure); // channel 1
-    TIM_OC2Init(TIM2, &TIM_OCInitStructure); // channel 2
-    TIM_OC3Init(TIM2, &TIM_OCInitStructure); // channel 3
-    TIM_OC4Init(TIM2, &TIM_OCInitStructure); // channel 4
-
-    // ?
-    TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Enable); // channel 1
-    TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Enable); // channel 2
-    TIM_OC3PreloadConfig(TIM2, TIM_OCPreload_Enable); // channel 3
-    TIM_OC4PreloadConfig(TIM2, TIM_OCPreload_Enable); // channel 4
-
-    // ?
-    TIM_ARRPreloadConfig(TIM2, ENABLE);
-
-    // TIM2 enable counter
-    TIM_Cmd(TIM2, ENABLE);
-}
-
-py_obj_t pyb_servo_set(py_obj_t port, py_obj_t value) {
-    int p = py_obj_get_int(port);
-    int v = py_obj_get_int(value);
-    if (v < 50) { v = 50; }
-    if (v > 250) { v = 250; }
-    switch (p) {
-        case 1: TIM2->CCR1 = v; break;
-        case 2: TIM2->CCR2 = v; break;
-        case 3: TIM2->CCR3 = v; break;
-        case 4: TIM2->CCR4 = v; break;
-    }
-    return py_const_none;
-}
-
-py_obj_t pyb_pwm_set(py_obj_t period, py_obj_t pulse) {
-    int pe = py_obj_get_int(period);
-    int pu = py_obj_get_int(pulse);
-    TIM2->ARR = pe;
-    TIM2->CCR3 = pu;
-    return py_const_none;
+    return mp_const_none;
 }
 
 #define MMA_ADDR (0x4c)
 
 int mma_buf[12];
 
-py_obj_t pyb_mma_read(void) {
+mp_obj_t pyb_mma_read(void) {
     for (int i = 0; i <= 6; i += 3) {
         mma_buf[0 + i] = mma_buf[0 + i + 3];
         mma_buf[1 + i] = mma_buf[1 + i + 3];
@@ -782,24 +609,24 @@ py_obj_t pyb_mma_read(void) {
     }
     int jolt_info = mma_read_nack();
 
-    py_obj_t data[4];
-    data[0] = py_obj_new_int(jolt_info);
-    data[1] = py_obj_new_int(mma_buf[2] + mma_buf[5] + mma_buf[8] + mma_buf[11]);
-    data[2] = py_obj_new_int(mma_buf[1] + mma_buf[4] + mma_buf[7] + mma_buf[10]);
-    data[3] = py_obj_new_int(mma_buf[0] + mma_buf[3] + mma_buf[6] + mma_buf[9]);
+    mp_obj_t data[4];
+    data[0] = mp_obj_new_int(jolt_info);
+    data[1] = mp_obj_new_int(mma_buf[2] + mma_buf[5] + mma_buf[8] + mma_buf[11]);
+    data[2] = mp_obj_new_int(mma_buf[1] + mma_buf[4] + mma_buf[7] + mma_buf[10]);
+    data[3] = mp_obj_new_int(mma_buf[0] + mma_buf[3] + mma_buf[6] + mma_buf[9]);
 
     return rt_build_tuple(4, data); // items in reverse order in data
 }
 
-py_obj_t pyb_hid_send_report(py_obj_t arg) {
-    py_obj_t *items = py_obj_get_array_fixed_n(arg, 4);
+mp_obj_t pyb_hid_send_report(mp_obj_t arg) {
+    mp_obj_t *items = mp_obj_get_array_fixed_n(arg, 4);
     uint8_t data[4];
-    data[0] = py_obj_get_int(items[0]);
-    data[1] = py_obj_get_int(items[1]);
-    data[2] = py_obj_get_int(items[2]);
-    data[3] = py_obj_get_int(items[3]);
+    data[0] = mp_obj_get_int(items[0]);
+    data[1] = mp_obj_get_int(items[1]);
+    data[2] = mp_obj_get_int(items[2]);
+    data[3] = mp_obj_get_int(items[3]);
     usb_hid_send_report(data);
-    return py_const_none;
+    return mp_const_none;
 }
 
 static void rtc_init(void) {
@@ -855,90 +682,102 @@ static void rtc_init(void) {
     //RTC_WriteBackupRegister(RTC_BKP_DR0, 0x32F2);
 }
 
-py_obj_t pyb_rtc_read(void) {
+mp_obj_t pyb_rtc_read(void) {
     RTC_TimeTypeDef RTC_TimeStructure;
     RTC_GetTime(RTC_Format_BIN, &RTC_TimeStructure);
     printf("%02d:%02d:%02d\n", RTC_TimeStructure.RTC_Hours, RTC_TimeStructure.RTC_Minutes, RTC_TimeStructure.RTC_Seconds);
-    return py_const_none;
+    return mp_const_none;
 }
 
-void file_obj_print(py_obj_t o) {
-    FIL *fp;
-    py_user_get_data(o, (machine_uint_t*)&fp, NULL);
-    printf("<file %p>", fp);
+typedef struct _pyb_file_obj_t {
+    mp_obj_base_t base;
+    FIL fp;
+} pyb_file_obj_t;
+
+void file_obj_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in) {
+    printf("<file %p>", self_in);
 }
 
-py_obj_t file_obj_read(py_obj_t self, py_obj_t arg) {
-    FIL *fp;
-    py_user_get_data(self, (machine_uint_t*)&fp, NULL);
-    int n = py_obj_get_int(arg);
+mp_obj_t file_obj_read(mp_obj_t self_in, mp_obj_t arg) {
+    pyb_file_obj_t *self = self_in;
+    int n = mp_obj_get_int(arg);
     char *buf = m_new(char, n + 1);
     UINT n_out;
-    f_read(fp, buf, n, &n_out);
+    f_read(&self->fp, buf, n, &n_out);
     buf[n_out] = 0;
-    return py_obj_new_str(qstr_from_str_take(buf));
+    return mp_obj_new_str(qstr_from_str_take(buf));
 }
 
-py_obj_t file_obj_write(py_obj_t self, py_obj_t arg) {
-    FIL *fp;
-    py_user_get_data(self, (machine_uint_t*)&fp, NULL);
-    const char *s = qstr_str(py_obj_get_qstr(arg));
+mp_obj_t file_obj_write(mp_obj_t self_in, mp_obj_t arg) {
+    pyb_file_obj_t *self = self_in;
+    const char *s = qstr_str(mp_obj_get_qstr(arg));
     UINT n_out;
-    FRESULT res = f_write(fp, s, strlen(s), &n_out);
+    FRESULT res = f_write(&self->fp, s, strlen(s), &n_out);
     if (res != FR_OK) {
         printf("File error: could not write to file; error code %d\n", res);
     } else if (n_out != strlen(s)) {
         printf("File error: could not write all data to file; wrote %d / %d bytes\n", n_out, strlen(s));
     }
-    return py_const_none;
+    return mp_const_none;
 }
 
-py_obj_t file_obj_close(py_obj_t self) {
-    FIL *fp;
-    py_user_get_data(self, (machine_uint_t*)&fp, NULL);
-    f_close(fp);
-    return py_const_none;
+mp_obj_t file_obj_close(mp_obj_t self_in) {
+    pyb_file_obj_t *self = self_in;
+    f_close(&self->fp);
+    return mp_const_none;
 }
 
+static MP_DEFINE_CONST_FUN_OBJ_2(file_obj_read_obj, file_obj_read);
+static MP_DEFINE_CONST_FUN_OBJ_2(file_obj_write_obj, file_obj_write);
+static MP_DEFINE_CONST_FUN_OBJ_1(file_obj_close_obj, file_obj_close);
+
 // TODO gc hook to close the file if not already closed
-const py_user_info_t file_obj_info = {
+
+static const mp_obj_type_t file_obj_type = {
+    { &mp_const_type },
     "File",
-    file_obj_print,
-    {
-        {"read", 1, file_obj_read},
-        {"write", 1, file_obj_write},
-        {"close", 0, file_obj_close},
-        {NULL, 0, NULL},
+    file_obj_print, // print
+    NULL, // call_n
+    NULL, // unary_op
+    NULL, // binary_op
+    NULL, // getiter
+    NULL, // iternext
+    { // method list
+        { "read", &file_obj_read_obj },
+        { "write", &file_obj_write_obj },
+        { "close", &file_obj_close_obj },
+        {NULL, NULL},
     }
 };
 
-py_obj_t pyb_io_open(py_obj_t o_filename, py_obj_t o_mode) {
-    const char *filename = qstr_str(py_obj_get_qstr(o_filename));
-    const char *mode = qstr_str(py_obj_get_qstr(o_mode));
-    FIL *fp = m_new(FIL, 1);
+mp_obj_t pyb_io_open(mp_obj_t o_filename, mp_obj_t o_mode) {
+    const char *filename = qstr_str(mp_obj_get_qstr(o_filename));
+    const char *mode = qstr_str(mp_obj_get_qstr(o_mode));
+    pyb_file_obj_t *self = m_new_obj(pyb_file_obj_t);
+    self->base.type = &file_obj_type;
     if (mode[0] == 'r') {
         // open for reading
-        FRESULT res = f_open(fp, filename, FA_READ);
+        FRESULT res = f_open(&self->fp, filename, FA_READ);
         if (res != FR_OK) {
             printf("FileNotFoundError: [Errno 2] No such file or directory: '%s'\n", filename);
-            return py_const_none;
+            return mp_const_none;
         }
     } else if (mode[0] == 'w') {
         // open for writing, truncate the file first
-        FRESULT res = f_open(fp, filename, FA_WRITE | FA_CREATE_ALWAYS);
+        FRESULT res = f_open(&self->fp, filename, FA_WRITE | FA_CREATE_ALWAYS);
         if (res != FR_OK) {
             printf("?FileError: could not create file: '%s'\n", filename);
-            return py_const_none;
+            return mp_const_none;
         }
     } else {
         printf("ValueError: invalid mode: '%s'\n", mode);
-        return py_const_none;
+        return mp_const_none;
     }
-    return py_obj_new_user(&file_obj_info, (machine_uint_t)fp, 0);
+    return self;
 }
 
-py_obj_t pyb_rng_get(void) {
-    return py_obj_new_int(RNG_GetRandomNumber() >> 16);
+mp_obj_t pyb_rng_get(void) {
+    return mp_obj_new_int(RNG_GetRandomNumber() >> 16);
 }
 
 int main(void) {
@@ -1015,7 +854,7 @@ soft_reset:
     {
         rt_store_name(qstr_from_str_static("help"), rt_make_function_0(pyb_help));
 
-        py_obj_t m = py_module_new();
+        mp_obj_t m = mp_module_new();
         rt_store_attr(m, qstr_from_str_static("info"), rt_make_function_0(pyb_info));
         rt_store_attr(m, qstr_from_str_static("sd_test"), rt_make_function_0(pyb_sd_test));
         rt_store_attr(m, qstr_from_str_static("stop"), rt_make_function_0(pyb_stop));
@@ -1312,19 +1151,19 @@ soft_reset:
             "                pass\n"
             "f()\n";
 
-        py_lexer_str_buf_t py_lexer_str_buf;
-        py_lexer_t *lex = py_lexer_new_from_str_len("<stdin>", pysrc, strlen(pysrc), false, &py_lexer_str_buf);
+        mp_lexer_str_buf_t mp_lexer_str_buf;
+        mp_lexer_t *lex = mp_lexer_new_from_str_len("<stdin>", pysrc, strlen(pysrc), false, &mp_lexer_str_buf);
 
         // nalloc=1740;6340;6836 -> 140;4600;496 bytes for lexer, parser, compiler
         printf("lex; al=%u\n", m_get_total_bytes_allocated());
         sys_tick_delay_ms(1000);
-        py_parse_node_t pn = py_parse(lex, PY_PARSE_FILE_INPUT);
-        py_lexer_free(lex);
-        if (pn != PY_PARSE_NODE_NULL) {
+        mp_parse_node_t pn = mp_parse(lex, MP_PARSE_FILE_INPUT);
+        mp_lexer_free(lex);
+        if (pn != MP_PARSE_NODE_NULL) {
             printf("pars;al=%u\n", m_get_total_bytes_allocated());
             sys_tick_delay_ms(1000);
             //parse_node_show(pn, 0);
-            bool comp_ok = py_compile(pn, false);
+            bool comp_ok = mp_compile(pn, false);
             printf("comp;al=%u\n", m_get_total_bytes_allocated());
             sys_tick_delay_ms(1000);
 
@@ -1333,7 +1172,7 @@ soft_reset:
             } else {
                 // execute it!
 
-                py_obj_t module_fun = rt_make_function_from_id(1);
+                mp_obj_t module_fun = rt_make_function_from_id(1);
 
                 // flash once
                 led_state(PYB_LED_G1, 1);
@@ -1342,15 +1181,15 @@ soft_reset:
 
                 nlr_buf_t nlr;
                 if (nlr_push(&nlr) == 0) {
-                    py_obj_t ret = rt_call_function_0(module_fun);
+                    mp_obj_t ret = rt_call_function_0(module_fun);
                     printf("done! got: ");
-                    py_obj_print(ret);
+                    mp_obj_print(ret);
                     printf("\n");
                     nlr_pop();
                 } else {
                     // uncaught exception
                     printf("exception: ");
-                    py_obj_print((py_obj_t)nlr.ret_val);
+                    mp_obj_print((mp_obj_t)nlr.ret_val);
                     printf("\n");
                 }
 
diff --git a/stm/malloc0.c b/stm/malloc0.c
index 7157252ed20a04ab8e0d2f60cb407edf8d1aa8e4..7e3f620db25067534e0cdd397b733cc7f2d56365 100644
--- a/stm/malloc0.c
+++ b/stm/malloc0.c
@@ -1,6 +1,6 @@
 #include <stdint.h>
 #include "std.h"
-#include "mpyconfig.h"
+#include "mpconfig.h"
 #include "gc.h"
 
 #if 0
diff --git a/stm/mpyconfig.h b/stm/mpconfig.h
similarity index 89%
rename from stm/mpyconfig.h
rename to stm/mpconfig.h
index 3fa3ac6524ac3e3464493cf49cbb45c30791fbb8..1f9529e11ba8df1afdb692e129061534878b9870 100644
--- a/stm/mpyconfig.h
+++ b/stm/mpconfig.h
@@ -13,6 +13,7 @@
 typedef int32_t machine_int_t; // must be pointer size
 typedef uint32_t machine_uint_t; // must be pointer size
 typedef void *machine_ptr_t; // must be of pointer size
+typedef const void *machine_const_ptr_t; // must be of pointer size
 typedef float machine_float_t;
 
 machine_float_t machine_sqrt(machine_float_t x);
diff --git a/stm/pybwlan.c b/stm/pybwlan.c
index 3d1f216f881e6818d33f3152b58a6461cd17d567..863c241b60a2f591434f779908b279327c00be9b 100644
--- a/stm/pybwlan.c
+++ b/stm/pybwlan.c
@@ -11,7 +11,7 @@
 #include "std.h"
 
 #include "misc.h"
-#include "mpyconfig.h"
+#include "mpconfig.h"
 #include "systick.h"
 
 #include "nlr.h"
@@ -19,6 +19,7 @@
 #include "lexer.h"
 #include "parse.h"
 #include "compile.h"
+#include "obj.h"
 #include "runtime.h"
 
 #include "cc3k/ccspi.h"
@@ -28,27 +29,27 @@
 #include "cc3k/wlan.h"
 #include "cc3k/nvmem.h"
 
-py_obj_t pyb_wlan_connect(int n_args, const py_obj_t *args) {
+mp_obj_t pyb_wlan_connect(int n_args, const mp_obj_t *args) {
   const char *ap;
   const char *key;
     if (n_args == 2) {
-        ap = qstr_str(py_obj_get_qstr(args[0]));
-        key = qstr_str(py_obj_get_qstr(args[1]));
+        ap = qstr_str(mp_obj_get_qstr(args[0]));
+        key = qstr_str(mp_obj_get_qstr(args[1]));
     } else {
-        ap = "Rama3";
-        key = "underthechristmastree";
+        ap = "your-ssid";
+        key = "your-password";
     }
     // might want to set wlan_ioctl_set_connection_policy
     int ret = wlan_connect(WLAN_SEC_WPA2, ap, strlen(ap), NULL, (byte*)key, strlen(key));
-    return py_obj_new_int(ret);
+    return mp_obj_new_int(ret);
 }
 
-py_obj_t pyb_wlan_disconnect(void) {
+mp_obj_t pyb_wlan_disconnect(void) {
     int ret = wlan_disconnect();
-    return py_obj_new_int(ret);
+    return mp_obj_new_int(ret);
 }
 
-py_obj_t decode_addr(unsigned char *ip, int nBytes) {
+mp_obj_t decode_addr(unsigned char *ip, int nBytes) {
     char data[64] = "";
     if (nBytes == 4) {
         snprintf(data, 64, "%u.%u.%u.%u", ip[3], ip[2], ip[1], ip[0]);
@@ -57,21 +58,21 @@ py_obj_t decode_addr(unsigned char *ip, int nBytes) {
     } else if (nBytes == 32) {
         snprintf(data, 64, "%s", ip);
     }
-    return py_obj_new_str(qstr_from_strn_copy(data, strlen(data)));
+    return mp_obj_new_str(qstr_from_strn_copy(data, strlen(data)));
 }
 
-void _wlan_getIP_get_address(py_obj_t object, qstr q_attr, unsigned char *ip, int nBytes) {
+void _wlan_getIP_get_address(mp_obj_t object, qstr q_attr, unsigned char *ip, int nBytes) {
     rt_store_attr(object, q_attr, decode_addr(ip, nBytes));
 }
 
-py_obj_t pyb_wlan_get_ip(void) {
+mp_obj_t pyb_wlan_get_ip(void) {
   tNetappIpconfigRetArgs ipconfig;
   netapp_ipconfig(&ipconfig);
 
   /* If byte 1 is 0 we don't have a valid address */
-  if (ipconfig.aucIP[3] == 0) return py_const_none;
+  if (ipconfig.aucIP[3] == 0) return mp_const_none;
 
-  py_obj_t data = py_module_new(); // TODO should really be a class
+  mp_obj_t data = mp_module_new(); // TODO should really be a class
   _wlan_getIP_get_address(data, qstr_from_str_static("ip"), &ipconfig.aucIP[0], 4);
   _wlan_getIP_get_address(data, qstr_from_str_static("subnet"), &ipconfig.aucSubnetMask[0], 4);
   _wlan_getIP_get_address(data, qstr_from_str_static("gateway"), &ipconfig.aucDefaultGateway[0], 4);
@@ -84,16 +85,16 @@ py_obj_t pyb_wlan_get_ip(void) {
 }
 
 uint32_t last_ip = 0; // XXX such a hack!
-py_obj_t pyb_wlan_get_host(py_obj_t host_name) {
-    const char *host = qstr_str(py_obj_get_qstr(host_name));
+mp_obj_t pyb_wlan_get_host(mp_obj_t host_name) {
+    const char *host = qstr_str(mp_obj_get_qstr(host_name));
     uint32_t ip;
     if (gethostbyname(host, strlen(host), &ip) < 0) {
         printf("gethostbyname failed\n");
-        return py_const_none;
+        return mp_const_none;
     }
     if (ip == 0) {
         // unknown host
-        return py_const_none;
+        return mp_const_none;
     }
     last_ip = ip;
     byte ip_data[4];
@@ -104,19 +105,17 @@ py_obj_t pyb_wlan_get_host(py_obj_t host_name) {
     return decode_addr(ip_data, 4);
 }
 
-py_obj_t py_obj_new_exception_2(qstr, const char *, void*, void*);
-
-py_obj_t pyb_wlan_http_get(py_obj_t host_name, py_obj_t host_path) {
-    if (host_name == py_const_none) {
+mp_obj_t pyb_wlan_http_get(mp_obj_t host_name, mp_obj_t host_path) {
+    if (host_name == mp_const_none) {
         last_ip = (192 << 24) | (168 << 16) | (0 << 8) | (3);
     } else {
-        if (pyb_wlan_get_host(host_name) == py_const_none) {
-            nlr_jump(py_obj_new_exception_2(qstr_from_str_static("WlanError"), "unknown host", NULL, NULL));
+        if (pyb_wlan_get_host(host_name) == mp_const_none) {
+            nlr_jump(mp_obj_new_exception_msg(qstr_from_str_static("WlanError"), "unknown host"));
         }
     }
     int sd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
     if (sd < 0) {
-        nlr_jump(py_obj_new_exception_2(qstr_from_str_static("WlanError"), "socket failed: %d", (void*)sd, NULL));
+        nlr_jump(mp_obj_new_exception_msg_1_arg(qstr_from_str_static("WlanError"), "socket failed: %d", (void*)sd));
     }
     //printf("socket seemed to work\n");
     //sys_tick_delay_ms(200);
@@ -127,13 +126,13 @@ py_obj_t pyb_wlan_http_get(py_obj_t host_name, py_obj_t host_path) {
     remote.sin_addr.s_addr = htonl(last_ip);
     int ret = connect(sd, (sockaddr*)&remote, sizeof(sockaddr));
     if (ret != 0) {
-        nlr_jump(py_obj_new_exception_2(qstr_from_str_static("WlanError"), "connect failed: %d", (void*)ret, NULL));
+        nlr_jump(mp_obj_new_exception_msg_1_arg(qstr_from_str_static("WlanError"), "connect failed: %d", (void*)ret));
     }
     //printf("connect seemed to work\n");
     //sys_tick_delay_ms(200);
 
     vstr_t *vstr = vstr_new();
-    vstr_printf(vstr, "GET %s HTTP/1.1\r\nHost: %s\r\nUser-Agent: PYBv2\r\n\r\n", qstr_str(py_obj_get_qstr(host_path)), qstr_str(py_obj_get_qstr(host_name)));
+    vstr_printf(vstr, "GET %s HTTP/1.1\r\nHost: %s\r\nUser-Agent: PYBv2\r\n\r\n", qstr_str(mp_obj_get_qstr(host_path)), qstr_str(mp_obj_get_qstr(host_name)));
     const char *query = vstr_str(vstr);
 
     // send query
@@ -148,7 +147,7 @@ py_obj_t pyb_wlan_http_get(py_obj_t host_name, py_obj_t host_path) {
             ret = send(sd, query + sent, strlen(query + sent), 0);
             //printf("sent %d bytes\n", ret);
             if (ret < 0) {
-                nlr_jump(py_obj_new_exception_2(qstr_from_str_static("WlanError"), "send failed", NULL, NULL));
+                nlr_jump(mp_obj_new_exception_msg(qstr_from_str_static("WlanError"), "send failed"));
             }
             sent += ret;
             //sys_tick_delay_ms(200);
@@ -159,7 +158,7 @@ py_obj_t pyb_wlan_http_get(py_obj_t host_name, py_obj_t host_path) {
     //sys_tick_delay_ms(5000);
 
     // receive reply
-    py_obj_t py_ret = py_const_none;
+    mp_obj_t mp_ret = mp_const_none;
     {
         //printf("doing receive\n");
         char buf[64];
@@ -185,33 +184,33 @@ py_obj_t pyb_wlan_http_get(py_obj_t host_name, py_obj_t host_path) {
             // read data
             ret = recv(sd, buf, 64, 0);
             if (ret < 0) {
-                nlr_jump(py_obj_new_exception_2(qstr_from_str_static("WlanError"), "recv failed %d", (void*)ret, NULL));
+                nlr_jump(mp_obj_new_exception_msg_1_arg(qstr_from_str_static("WlanError"), "recv failed %d", (void*)ret));
             }
             vstr_add_strn(vstr, buf, ret);
         }
 
-        py_ret = py_obj_new_str(qstr_from_str_take(vstr_str(vstr)));
+        mp_ret = mp_obj_new_str(qstr_from_str_take(vstr_str(vstr)));
     }
 
     closesocket(sd);
 
-    return py_ret;
+    return mp_ret;
 }
 
-py_obj_t pyb_wlan_serve(void) {
+mp_obj_t pyb_wlan_serve(void) {
     printf("serve socket\n");
     int sd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
     printf("serve socket got %d\n", sd);
     sys_tick_delay_ms(500);
     if (sd < 0) {
         printf("socket fail\n");
-        nlr_jump(py_obj_new_exception_2(qstr_from_str_static("WlanError"), "socket failed: %d", (void*)sd, NULL));
+        nlr_jump(mp_obj_new_exception_msg_1_arg(qstr_from_str_static("WlanError"), "socket failed: %d", (void*)sd));
     }
 
     /*
     if (setsockopt(sd, SOL_SOCKET, SOCKOPT_ACCEPT_NONBLOCK, SOCK_ON, sizeof(SOCK_ON)) < 0) {
         printf("couldn't set socket as non-blocking\n");
-        return py_const_none;
+        return mp_const_none;
     }
     */
 
@@ -226,7 +225,7 @@ py_obj_t pyb_wlan_serve(void) {
     sys_tick_delay_ms(100);
     if (ret != 0) {
         printf("bind fail\n");
-        nlr_jump(py_obj_new_exception_2(qstr_from_str_static("WlanError"), "bind failed: %d", (void*)ret, NULL));
+        nlr_jump(mp_obj_new_exception_msg_1_arg(qstr_from_str_static("WlanError"), "bind failed: %d", (void*)ret));
     }
     printf("bind seemed to work\n");
 
@@ -268,7 +267,7 @@ py_obj_t pyb_wlan_serve(void) {
     closesocket(fd);
     closesocket(sd);
 
-    return py_const_none;
+    return mp_const_none;
 }
 
 //*****************************************************************************
@@ -344,7 +343,7 @@ void pyb_wlan_init(void) {
     SpiInit();
     wlan_init(CC3000_UsynchCallback, sendWLFWPatch, sendDriverPatch, sendBootLoaderPatch, ReadWlanInterruptPin, WlanInterruptEnable, WlanInterruptDisable, WriteWlanPin);
 
-    py_obj_t m = py_module_new();
+    mp_obj_t m = mp_module_new();
     rt_store_attr(m, qstr_from_str_static("connect"), rt_make_function_var(0, pyb_wlan_connect));
     rt_store_attr(m, qstr_from_str_static("disconnect"), rt_make_function_0(pyb_wlan_disconnect));
     rt_store_attr(m, qstr_from_str_static("ip"), rt_make_function_0(pyb_wlan_get_ip));
diff --git a/stm/servo.c b/stm/servo.c
new file mode 100644
index 0000000000000000000000000000000000000000..ae421048b92cebbbdca415eccd36d4d9f4d77e65
--- /dev/null
+++ b/stm/servo.c
@@ -0,0 +1,160 @@
+#include <stdio.h>
+#include <stm32f4xx.h>
+#include <stm32f4xx_rcc.h>
+#include <stm32f4xx_gpio.h>
+#include <stm32f4xx_tim.h>
+
+#include "misc.h"
+#include "mpconfig.h"
+#include "obj.h"
+#include "servo.h"
+
+// PWM
+// TIM2 and TIM5 have CH1, CH2, CH3, CH4 on PA0-PA3 respectively
+// they are both 32-bit counters
+// 16-bit prescaler
+// TIM2_CH3 also on PB10 (used below)
+void servo_init(void) {
+    // TIM2 clock enable
+    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
+
+    // for PB10
+    /*
+    // GPIOB Configuration: TIM2_CH3 (PB10)
+    GPIO_InitTypeDef GPIO_InitStructure;
+    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
+    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
+    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
+    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
+    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
+    GPIO_Init(GPIOB, &GPIO_InitStructure);
+
+    // Connect TIM2 pins to AF1
+    GPIO_PinAFConfig(GPIOB, GPIO_PinSource10, GPIO_AF_TIM2);
+    */
+
+    // for PA0, PA1, PA2, PA3
+    {
+        // GPIOA Configuration: TIM2_CH0, TIM2_CH1 (PA0, PA1)
+        GPIO_InitTypeDef GPIO_InitStructure;
+        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
+        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
+        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
+        GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
+        GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
+        GPIO_Init(GPIOA, &GPIO_InitStructure);
+
+        // Connect TIM2 pins to AF1
+        GPIO_PinAFConfig(GPIOA, GPIO_PinSource0, GPIO_AF_TIM2);
+        GPIO_PinAFConfig(GPIOA, GPIO_PinSource1, GPIO_AF_TIM2);
+        GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_TIM2);
+        GPIO_PinAFConfig(GPIOA, GPIO_PinSource3, GPIO_AF_TIM2);
+    }
+
+    // Compute the prescaler value so TIM2 runs at 100kHz
+    uint16_t PrescalerValue = (uint16_t) ((SystemCoreClock / 2) / 100000) - 1;
+
+    // Time base configuration
+    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
+    TIM_TimeBaseStructure.TIM_Period = 2000; // timer cycles at 50Hz
+    TIM_TimeBaseStructure.TIM_Prescaler = PrescalerValue;
+    TIM_TimeBaseStructure.TIM_ClockDivision = 0;
+    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
+    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
+
+    // PWM Mode configuration
+    TIM_OCInitTypeDef TIM_OCInitStructure;
+    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
+    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
+    TIM_OCInitStructure.TIM_Pulse = 150; // units of 10us
+    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
+    TIM_OC1Init(TIM2, &TIM_OCInitStructure); // channel 1
+    TIM_OC2Init(TIM2, &TIM_OCInitStructure); // channel 2
+    TIM_OC3Init(TIM2, &TIM_OCInitStructure); // channel 3
+    TIM_OC4Init(TIM2, &TIM_OCInitStructure); // channel 4
+
+    // ?
+    TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Enable); // channel 1
+    TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Enable); // channel 2
+    TIM_OC3PreloadConfig(TIM2, TIM_OCPreload_Enable); // channel 3
+    TIM_OC4PreloadConfig(TIM2, TIM_OCPreload_Enable); // channel 4
+
+    // ?
+    TIM_ARRPreloadConfig(TIM2, ENABLE);
+
+    // TIM2 enable counter
+    TIM_Cmd(TIM2, ENABLE);
+}
+
+/******************************************************************************/
+/* Micro Python bindings                                                      */
+
+mp_obj_t pyb_servo_set(mp_obj_t port, mp_obj_t value) {
+    int p = mp_obj_get_int(port);
+    int v = mp_obj_get_int(value);
+    if (v < 50) { v = 50; }
+    if (v > 250) { v = 250; }
+    switch (p) {
+        case 1: TIM2->CCR1 = v; break;
+        case 2: TIM2->CCR2 = v; break;
+        case 3: TIM2->CCR3 = v; break;
+        case 4: TIM2->CCR4 = v; break;
+    }
+    return mp_const_none;
+}
+
+mp_obj_t pyb_pwm_set(mp_obj_t period, mp_obj_t pulse) {
+    int pe = mp_obj_get_int(period);
+    int pu = mp_obj_get_int(pulse);
+    TIM2->ARR = pe;
+    TIM2->CCR3 = pu;
+    return mp_const_none;
+}
+
+typedef struct _pyb_servo_obj_t {
+    mp_obj_base_t base;
+    uint servo_id;
+} pyb_servo_obj_t;
+
+static void servo_obj_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in) {
+    pyb_servo_obj_t *self = self_in;
+    print(env, "<Servo %lu>", self->servo_id);
+}
+
+static mp_obj_t servo_obj_angle(mp_obj_t self_in, mp_obj_t angle) {
+    pyb_servo_obj_t *self = self_in;
+    machine_int_t v = 152 + 85.0 * mp_obj_get_float(angle) / 90.0;
+    if (v < 65) { v = 65; }
+    if (v > 210) { v = 210; }
+    switch (self->servo_id) {
+        case 1: TIM2->CCR1 = v; break;
+        case 2: TIM2->CCR2 = v; break;
+        case 3: TIM2->CCR3 = v; break;
+        case 4: TIM2->CCR4 = v; break;
+    }
+    return mp_const_none;
+}
+
+static MP_DEFINE_CONST_FUN_OBJ_2(servo_obj_angle_obj, servo_obj_angle);
+
+static const mp_obj_type_t servo_obj_type = {
+    { &mp_const_type },
+    "Servo",
+    servo_obj_print, // print
+    NULL, // call_n
+    NULL, // unary_op
+    NULL, // binary_op
+    NULL, // getiter
+    NULL, // iternext
+    { // method list
+        { "angle", &servo_obj_angle_obj },
+        { NULL, NULL },
+    }
+};
+
+mp_obj_t pyb_Servo(mp_obj_t servo_id) {
+    pyb_servo_obj_t *o = m_new_obj(pyb_servo_obj_t);
+    o->base.type = &servo_obj_type;
+    o->servo_id = mp_obj_get_int(servo_id);
+    return o;
+}
diff --git a/stm/servo.h b/stm/servo.h
new file mode 100644
index 0000000000000000000000000000000000000000..02702e067268a9c3f39872621e18c23454da023d
--- /dev/null
+++ b/stm/servo.h
@@ -0,0 +1,5 @@
+void servo_init(void);
+
+mp_obj_t pyb_servo_set(mp_obj_t port, mp_obj_t value);
+mp_obj_t pyb_pwm_set(mp_obj_t period, mp_obj_t pulse);
+mp_obj_t pyb_Servo(mp_obj_t servo_id);
diff --git a/stm/stm32fxxx_it.c b/stm/stm32fxxx_it.c
index 0f17681e5f85ccc8e63145ca51d6df53d3815121..4c185d0341d6f23d86668da901cc545e92b2fb3d 100644
--- a/stm/stm32fxxx_it.c
+++ b/stm/stm32fxxx_it.c
@@ -267,6 +267,9 @@ void TIM6_DAC_IRQHandler(void) {
 }
 
 #include "std.h"
+#include "misc.h"
+#include "mpconfig.h"
+#include "obj.h"
 #include "led.h"
 // EXTI
 // for USRSW on A13
diff --git a/stm/storage.c b/stm/storage.c
index ac0458d136970381d824cb686bf7461608a41432..6878de22e67fbd0df4b2fcc3d5a2c4e1af378456 100644
--- a/stm/storage.c
+++ b/stm/storage.c
@@ -3,6 +3,8 @@
 
 #include "misc.h"
 #include "systick.h"
+#include "mpconfig.h"
+#include "obj.h"
 #include "led.h"
 #include "flash.h"
 #include "storage.h"
diff --git a/stm/timer.c b/stm/timer.c
index ecfc2bb2894be5d78e3533767d95db8d3158e1ed..2236bbce47b2ae821d65333f7dc4659fc5a3a84b 100644
--- a/stm/timer.c
+++ b/stm/timer.c
@@ -7,37 +7,38 @@
 
 #include "nlr.h"
 #include "misc.h"
-#include "mpyconfig.h"
+#include "mpconfig.h"
 #include "parse.h"
 #include "compile.h"
+#include "obj.h"
 #include "runtime.h"
 
 #include "timer.h"
 
 // TIM6 is used as an internal interrup to schedule something at a specific rate
-py_obj_t timer_py_callback;
+mp_obj_t timer_py_callback;
 
-py_obj_t timer_py_set_callback(py_obj_t f) {
+mp_obj_t timer_py_set_callback(mp_obj_t f) {
     timer_py_callback = f;
-    return py_const_none;
+    return mp_const_none;
 }
 
-py_obj_t timer_py_set_period(py_obj_t period) {
-    TIM6->ARR = py_obj_get_int(period) & 0xffff;
-    return py_const_none;
+mp_obj_t timer_py_set_period(mp_obj_t period) {
+    TIM6->ARR = mp_obj_get_int(period) & 0xffff;
+    return mp_const_none;
 }
 
-py_obj_t timer_py_set_prescaler(py_obj_t prescaler) {
-    TIM6->PSC = py_obj_get_int(prescaler) & 0xffff;
-    return py_const_none;
+mp_obj_t timer_py_set_prescaler(mp_obj_t prescaler) {
+    TIM6->PSC = mp_obj_get_int(prescaler) & 0xffff;
+    return mp_const_none;
 }
 
-py_obj_t timer_py_get_value(void) {
-    return py_obj_new_int(TIM6->CNT & 0xfffff);
+mp_obj_t timer_py_get_value(void) {
+    return mp_obj_new_int(TIM6->CNT & 0xfffff);
 }
 
 void timer_init(void) {
-    timer_py_callback = py_const_none;
+    timer_py_callback = mp_const_none;
 
     // TIM6 clock enable
     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);
@@ -71,7 +72,7 @@ void timer_init(void) {
     TIM_Cmd(TIM6, ENABLE);
 
     // Python interface
-    py_obj_t m = py_module_new();
+    mp_obj_t m = mp_module_new();
     rt_store_attr(m, qstr_from_str_static("callback"), rt_make_function_1(timer_py_set_callback));
     rt_store_attr(m, qstr_from_str_static("period"), rt_make_function_1(timer_py_set_period));
     rt_store_attr(m, qstr_from_str_static("prescaler"), rt_make_function_1(timer_py_set_prescaler));
@@ -80,7 +81,7 @@ void timer_init(void) {
 }
 
 void timer_interrupt(void) {
-    if (timer_py_callback != py_const_none) {
+    if (timer_py_callback != mp_const_none) {
         nlr_buf_t nlr;
         if (nlr_push(&nlr) == 0) {
             // XXX what to do if the GC is in the middle of running??
@@ -89,7 +90,7 @@ void timer_interrupt(void) {
         } else {
             // uncaught exception
             printf("exception in timer interrupt\n");
-            py_obj_print((py_obj_t)nlr.ret_val);
+            mp_obj_print((mp_obj_t)nlr.ret_val);
             printf("\n");
         }
     }
diff --git a/unix/Makefile b/unix/Makefile
index 76cc0a385523af8ffb63b3e33b2bc18597cc4876..463f6b170bc6f5d8153b370d67b4fab258c882de 100644
--- a/unix/Makefile
+++ b/unix/Makefile
@@ -31,6 +31,25 @@ PY_O = \
 	runtime.o \
 	map.o \
 	obj.o \
+	objbool.o \
+	objboundmeth.o \
+	objcell.o \
+	objclass.o \
+	objclosure.o \
+	objcomplex.o \
+	objdict.o \
+	objexcept.o \
+	objfloat.o \
+	objfun.o \
+	objgenerator.o \
+	objinstance.o \
+	objlist.o \
+	objnone.o \
+	objrange.o \
+	objset.o \
+	objstr.o \
+	objtuple.o \
+	objtype.o \
 	builtin.o \
 	vm.o \
 	showbc.o \
@@ -42,6 +61,8 @@ PROG = py
 
 $(PROG): $(BUILD) $(OBJ)
 	$(CC) -o $@ $(OBJ) $(LIB) $(LDFLAGS)
+	strip $(PROG)
+	size $(PROG)
 
 $(BUILD):
 	mkdir $@
@@ -52,7 +73,7 @@ $(BUILD)/%.o: %.c
 $(BUILD)/%.o: $(PYSRC)/%.s
 	$(AS) -o $@ $<
 
-$(BUILD)/%.o: $(PYSRC)/%.c mpyconfig.h
+$(BUILD)/%.o: $(PYSRC)/%.c mpconfig.h
 	$(CC) $(CFLAGS) -c -o $@ $<
 
 $(BUILD)/emitnx64.o: $(PYSRC)/emitnative.c $(PYSRC)/emit.h
@@ -65,7 +86,7 @@ $(BUILD)/emitnthumb.o: $(PYSRC)/emitnative.c $(PYSRC)/emit.h
 $(BUILD)/vm.o: $(PYSRC)/vm.c
 	$(CC) $(CFLAGS) -O3 -c -o $@ $<
 
-$(BUILD)/main.o: mpyconfig.h
+$(BUILD)/main.o: mpconfig.h
 $(BUILD)/parse.o: $(PYSRC)/grammar.h
 $(BUILD)/compile.o: $(PYSRC)/grammar.h
 $(BUILD)/emitcpy.o: $(PYSRC)/emit.h
diff --git a/unix/lexerunix.c b/unix/lexerunix.c
index e76d7f2c2113e14fc1c80611a98bae69d6a99365..ac07781b5a3efeda7724cadfac7d7399eba752a7 100644
--- a/unix/lexerunix.c
+++ b/unix/lexerunix.c
@@ -17,7 +17,7 @@ unichar str_buf_next_char(str_buf_t *sb) {
     if (sb->src_cur < sb->src_end) {
         return *sb->src_cur++;
     } else {
-        return PY_LEXER_CHAR_EOF;
+        return MP_LEXER_CHAR_EOF;
     }
 }
 
@@ -30,16 +30,16 @@ void str_buf_free(str_buf_t *sb) {
     }
 }
 
-py_lexer_t *py_lexer_new_from_str_len(const char *src_name, const char *str, uint len, bool free_str) {
+mp_lexer_t *mp_lexer_new_from_str_len(const char *src_name, const char *str, uint len, bool free_str) {
     str_buf_t *sb = m_new(str_buf_t, 1);
     sb->free = free_str;
     sb->src_beg = str;
     sb->src_cur = str;
     sb->src_end = str + len;
-    return py_lexer_new(src_name, sb, (py_lexer_stream_next_char_t)str_buf_next_char, (py_lexer_stream_close_t)str_buf_free);
+    return mp_lexer_new(src_name, sb, (mp_lexer_stream_next_char_t)str_buf_next_char, (mp_lexer_stream_close_t)str_buf_free);
 }
 
-py_lexer_t *py_lexer_new_from_file(const char *filename) {
+mp_lexer_t *mp_lexer_new_from_file(const char *filename) {
     int fd = open(filename, O_RDONLY);
     if (fd < 0) {
         printf("cannot open file %s\n", filename);
@@ -51,5 +51,5 @@ py_lexer_t *py_lexer_new_from_file(const char *filename) {
     read(fd, data, size);
     close(fd);
 
-    return py_lexer_new_from_str_len(filename, data, size, true);
+    return mp_lexer_new_from_str_len(filename, data, size, true);
 }
diff --git a/unix/lexerunix.h b/unix/lexerunix.h
index aa7631cb0b514ec404e8721ddef42564c3dfb653..d86f202d53b4a8cc350c3f2b24b6c67929412bc9 100644
--- a/unix/lexerunix.h
+++ b/unix/lexerunix.h
@@ -1,2 +1,2 @@
-py_lexer_t *py_lexer_new_from_str_len(const char *src_name, const char *str, uint len, bool free_str);
-py_lexer_t *py_lexer_new_from_file(const char *filename);
+mp_lexer_t *mp_lexer_new_from_str_len(const char *src_name, const char *str, uint len, bool free_str);
+mp_lexer_t *mp_lexer_new_from_file(const char *filename);
diff --git a/unix/main.c b/unix/main.c
index e5aa9de15be1f058c71ee0de8ddc054bda08d30c..295e82d26a0435d68b39326e7dffbfbb6524cba5 100644
--- a/unix/main.c
+++ b/unix/main.c
@@ -4,18 +4,19 @@
 
 #include "nlr.h"
 #include "misc.h"
-#include "mpyconfig.h"
+#include "mpconfig.h"
 #include "lexer.h"
 #include "lexerunix.h"
 #include "parse.h"
 #include "compile.h"
-#include "runtime.h"
 #include "obj.h"
+#include "runtime0.h"
+#include "runtime.h"
 #include "repl.h"
 
 #include <readline/readline.h>
 
-char *str_join(const char *s1, int sep_char, const char *s2) {
+static char *str_join(const char *s1, int sep_char, const char *s2) {
     int l1 = strlen(s1);
     int l2 = strlen(s2);
     char *s = m_new(char, l1 + l2 + 2);
@@ -29,14 +30,14 @@ char *str_join(const char *s1, int sep_char, const char *s2) {
     return s;
 }
 
-void do_repl(void) {
+static void do_repl(void) {
     for (;;) {
         char *line = readline(">>> ");
         if (line == NULL) {
             // EOF
             return;
         }
-        if (py_repl_is_compound_stmt(line)) {
+        if (mp_repl_is_compound_stmt(line)) {
             for (;;) {
                 char *line2 = readline("... ");
                 if (line2 == NULL || strlen(line2) == 0) {
@@ -49,23 +50,23 @@ void do_repl(void) {
             }
         }
 
-        py_lexer_t *lex = py_lexer_new_from_str_len("<stdin>", line, strlen(line), false);
-        py_parse_node_t pn = py_parse(lex, PY_PARSE_SINGLE_INPUT);
-        py_lexer_free(lex);
+        mp_lexer_t *lex = mp_lexer_new_from_str_len("<stdin>", line, strlen(line), false);
+        mp_parse_node_t pn = mp_parse(lex, MP_PARSE_SINGLE_INPUT);
+        mp_lexer_free(lex);
 
-        if (pn != PY_PARSE_NODE_NULL) {
-            //py_parse_node_show(pn, 0);
-            bool comp_ok = py_compile(pn, true);
+        if (pn != MP_PARSE_NODE_NULL) {
+            //mp_parse_node_show(pn, 0);
+            bool comp_ok = mp_compile(pn, true);
             if (comp_ok) {
-                py_obj_t module_fun = rt_make_function_from_id(1);
-                if (module_fun != py_const_none) {
+                mp_obj_t module_fun = rt_make_function_from_id(1);
+                if (module_fun != mp_const_none) {
                     nlr_buf_t nlr;
                     if (nlr_push(&nlr) == 0) {
                         rt_call_function_0(module_fun);
                         nlr_pop();
                     } else {
                         // uncaught exception
-                        py_obj_print((py_obj_t)nlr.ret_val);
+                        mp_obj_print((mp_obj_t)nlr.ret_val);
                         printf("\n");
                     }
                 }
@@ -75,32 +76,32 @@ void do_repl(void) {
 }
 
 void do_file(const char *file) {
-    py_lexer_t *lex = py_lexer_new_from_file(file);
+    mp_lexer_t *lex = mp_lexer_new_from_file(file);
     //const char *pysrc = "def f():\n  x=x+1\n  print(42)\n";
-    //py_lexer_t *lex = py_lexer_from_str_len("<>", pysrc, strlen(pysrc), false);
+    //mp_lexer_t *lex = mp_lexer_from_str_len("<>", pysrc, strlen(pysrc), false);
     if (lex == NULL) {
         return;
     }
 
     if (0) {
         // just tokenise
-        while (!py_lexer_is_kind(lex, PY_TOKEN_END)) {
-            py_token_show(py_lexer_cur(lex));
-            py_lexer_to_next(lex);
+        while (!mp_lexer_is_kind(lex, MP_TOKEN_END)) {
+            mp_token_show(mp_lexer_cur(lex));
+            mp_lexer_to_next(lex);
         }
-        py_lexer_free(lex);
+        mp_lexer_free(lex);
 
     } else {
         // compile
 
-        py_parse_node_t pn = py_parse(lex, PY_PARSE_FILE_INPUT);
-        py_lexer_free(lex);
+        mp_parse_node_t pn = mp_parse(lex, MP_PARSE_FILE_INPUT);
+        mp_lexer_free(lex);
 
-        if (pn != PY_PARSE_NODE_NULL) {
+        if (pn != MP_PARSE_NODE_NULL) {
             //printf("----------------\n");
             //parse_node_show(pn, 0);
             //printf("----------------\n");
-            bool comp_ok = py_compile(pn, false);
+            bool comp_ok = mp_compile(pn, false);
             //printf("----------------\n");
 
 #if MICROPY_EMIT_CPYTHON
@@ -110,19 +111,19 @@ void do_file(const char *file) {
 #else
             if (1 && comp_ok) {
                 // execute it
-                py_obj_t module_fun = rt_make_function_from_id(1);
-                if (module_fun != py_const_none) {
+                mp_obj_t module_fun = rt_make_function_from_id(1);
+                if (module_fun != mp_const_none) {
                     nlr_buf_t nlr;
                     if (nlr_push(&nlr) == 0) {
-                        py_obj_t ret = rt_call_function_0(module_fun);
+                        mp_obj_t ret = rt_call_function_0(module_fun);
                         printf("done! got: ");
-                        py_obj_print(ret);
+                        mp_obj_print(ret);
                         printf("\n");
                         nlr_pop();
                     } else {
                         // uncaught exception
                         printf("exception: ");
-                        py_obj_print((py_obj_t)nlr.ret_val);
+                        mp_obj_print((mp_obj_t)nlr.ret_val);
                         printf("\n");
                     }
                 }
@@ -132,37 +133,58 @@ void do_file(const char *file) {
     }
 }
 
-void test_print(py_obj_t o_in) {
-    printf("<test>");
+typedef struct _test_obj_t {
+    mp_obj_base_t base;
+    bool value;
+} test_obj_t;
+
+static void test_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in) {
+    test_obj_t *self = self_in;
+    print(env, "<test %d>", self->value);
 }
 
-py_obj_t test_get(py_obj_t o_in) {
-    py_obj_t d1;
-    py_obj_t d2;
-    py_user_get_data(o_in, (machine_uint_t*)&d1, (machine_uint_t*)&d2);
-    return d1;
+static mp_obj_t test_get(mp_obj_t self_in) {
+    test_obj_t *self = self_in;
+    return mp_obj_new_int(self->value);
 }
 
-py_obj_t test_set(py_obj_t o_in, py_obj_t arg) {
-    py_user_set_data(o_in, (machine_uint_t)arg, (machine_uint_t)arg);
-    return py_const_none;
+static mp_obj_t test_set(mp_obj_t self_in, mp_obj_t arg) {
+    test_obj_t *self = self_in;
+    self->value = mp_obj_get_int(arg);
+    return mp_const_none;
 }
 
-const py_user_info_t test_obj_info = {
+static MP_DEFINE_CONST_FUN_OBJ_1(test_get_obj, test_get);
+static MP_DEFINE_CONST_FUN_OBJ_2(test_set_obj, test_set);
+
+static const mp_obj_type_t test_type = {
+    { &mp_const_type },
     "Test",
-    test_print,
-    {
-        { "get", 0, test_get },
-        { "set", 1, test_set },
-        { NULL, 0, NULL },
+    test_print, // print
+    NULL, // call_n
+    NULL, // unary_op
+    NULL, // binary_op
+    NULL, // getiter
+    NULL, // iternext
+    { // method list
+        { "get", &test_get_obj },
+        { "set", &test_set_obj },
+        { NULL, NULL },
     }
 };
 
+mp_obj_t test_obj_new(int value) {
+    test_obj_t *o = m_new_obj(test_obj_t);
+    o->base.type = &test_type;
+    o->value = value;
+    return o;
+}
+
 int main(int argc, char **argv) {
     qstr_init();
     rt_init();
 
-    rt_store_name(qstr_from_str_static("test"), py_obj_new_user(&test_obj_info, (machine_uint_t)py_obj_new_int(42), 0));
+    rt_store_name(qstr_from_str_static("test"), test_obj_new(42));
 
     if (argc == 1) {
         do_repl();
diff --git a/unix/mpyconfig.h b/unix/mpconfig.h
similarity index 89%
rename from unix/mpyconfig.h
rename to unix/mpconfig.h
index f187cbb4ea3fcae9824958cb856313aa982d5357..a358d7db814afdc66d1df13c4a982521d980f273 100644
--- a/unix/mpyconfig.h
+++ b/unix/mpconfig.h
@@ -13,6 +13,7 @@
 typedef int64_t machine_int_t; // must be pointer size
 typedef uint64_t machine_uint_t; // must be pointer size
 typedef void *machine_ptr_t; // must be of pointer size
+typedef const void *machine_const_ptr_t; // must be of pointer size
 typedef double machine_float_t;
 
 machine_float_t machine_sqrt(machine_float_t x);