blueloveTH před 1 rokem
rodič
revize
a55d3a5340

+ 2 - 2
include/pocketpy/common/config.h

@@ -43,8 +43,8 @@
 
 /*************** internal settings ***************/
 
-// This is the maximum size of the value stack in PyVar units
-// The actual size in bytes equals `sizeof(PyVar) * PK_VM_STACK_SIZE`
+// This is the maximum size of the value stack in py_TValue units
+// The actual size in bytes equals `sizeof(py_TValue) * PK_VM_STACK_SIZE`
 #define PK_VM_STACK_SIZE            16384
 
 // This is the maximum number of local variables in a function

+ 12 - 12
include/pocketpy/interpreter/frame.h

@@ -11,14 +11,14 @@
 extern "C" {
 #endif
 
-PyVar* FastLocals__try_get_by_name(PyVar* locals, const CodeObject* co, py_Name name);
-pk_NameDict* FastLocals__to_namedict(PyVar* locals, const CodeObject* co);
+py_TValue* FastLocals__try_get_by_name(py_TValue* locals, const CodeObject* co, py_Name name);
+pk_NameDict* FastLocals__to_namedict(py_TValue* locals, const CodeObject* co);
 
 typedef struct ValueStack {
     // We allocate extra PK_VM_STACK_SIZE/128 places to keep `_sp` valid when `is_overflow() == true`.
-    PyVar* sp;
-    PyVar* end;
-    PyVar begin[PK_VM_STACK_SIZE + PK_VM_STACK_SIZE / 128];
+    py_TValue* sp;
+    py_TValue* end;
+    py_TValue begin[PK_VM_STACK_SIZE + PK_VM_STACK_SIZE / 128];
 } ValueStack;
 
 void ValueStack__ctor(ValueStack* self);
@@ -39,14 +39,14 @@ typedef struct Frame {
     const CodeObject* co;
     PyObject* module;
     PyObject* function;     // a function object or NULL (global scope)
-    PyVar* p0;              // unwinding base
-    PyVar* locals;          // locals base
+    py_TValue* p0;              // unwinding base
+    py_TValue* locals;          // locals base
     const CodeObject* locals_co;
     UnwindTarget* uw_list;
 } Frame;
 
 
-Frame* Frame__new(const CodeObject* co, const PyVar* module, const PyVar* function, PyVar* p0, PyVar* locals, const CodeObject* locals_co);
+Frame* Frame__new(const CodeObject* co, const py_TValue* module, const py_TValue* function, py_TValue* p0, py_TValue* locals, const CodeObject* locals_co);
 void Frame__delete(Frame* self);
 
 PK_INLINE int Frame__ip(const Frame* self){
@@ -67,11 +67,11 @@ PK_INLINE pk_NameDict* Frame__f_globals(Frame* self){
     return PyObject__dict(self->module);
 }
 
-PK_INLINE PyVar* Frame__f_globals_try_get(Frame* self, py_Name name){
+PK_INLINE py_TValue* Frame__f_globals_try_get(Frame* self, py_Name name){
     return pk_NameDict__try_get(Frame__f_globals(self), name);
 }
 
-PyVar* Frame__f_closure_try_get(Frame* self, py_Name name);
+py_TValue* Frame__f_closure_try_get(Frame* self, py_Name name);
 
 int Frame__prepare_jump_exception_handler(Frame* self, ValueStack*);
 void Frame__prepare_jump_break(Frame* self, ValueStack*, int);
@@ -80,7 +80,7 @@ int Frame__exit_block(Frame* self, ValueStack*, int);
 
 void Frame__gc_mark(Frame* self);
 UnwindTarget* Frame__find_unwind_target(Frame* self, int iblock);
-void Frame__set_unwind_target(Frame* self, PyVar* sp);
+void Frame__set_unwind_target(Frame* self, py_TValue* sp);
 
 #ifdef __cplusplus
 }
@@ -92,7 +92,7 @@ void Frame__set_unwind_target(Frame* self, PyVar* sp);
 #include "pocketpy/objects/codeobject.hpp"
 
 extern "C"{
-    inline PyVar* Frame__f_closure_try_get(Frame* self, StrName name){
+    inline py_TValue* Frame__f_closure_try_get(Frame* self, StrName name){
         if(self->function == NULL) return NULL;
         pkpy::Function* fn = PyObject__as(pkpy::Function, self->function);
         if(fn->_closure == nullptr) return nullptr;

+ 11 - 11
include/pocketpy/interpreter/vm.h

@@ -12,8 +12,8 @@ typedef struct pk_TypeInfo{
     py_Name name;
     py_Type base;
 
-    PyVar self;      // the type object itself
-    PyVar module;    // the module where the type is defined
+    py_TValue self;      // the type object itself
+    py_TValue module;    // the module where the type is defined
     bool subclass_enabled;
 
     void (*dtor)(void*);
@@ -38,7 +38,7 @@ typedef struct pk_TypeInfo{
     py_CFunction on_end_subclass;   // for enum module
 } pk_TypeInfo;
 
-void pk_TypeInfo__ctor(pk_TypeInfo* self, py_Name name, py_Type base, PyObject* obj, const PyVar* module, bool subclass_enabled);
+void pk_TypeInfo__ctor(pk_TypeInfo* self, py_Name name, py_Type base, PyObject* obj, const py_TValue* module, bool subclass_enabled);
 void pk_TypeInfo__dtor(pk_TypeInfo* self);
 
 typedef struct pk_VM {
@@ -47,24 +47,24 @@ typedef struct pk_VM {
     pk_NameDict modules;
     c11_vector/*T=pk_TypeInfo*/ types;
 
-    PyVar StopIteration;    // a special Exception class
-    PyVar builtins;         // builtins module
-    PyVar main;             // __main__ module
+    py_TValue StopIteration;    // a special Exception class
+    py_TValue builtins;         // builtins module
+    py_TValue main;             // __main__ module
 
     void (*_ceval_on_step)(Frame*, Bytecode);
     unsigned char* (*_import_file)(const char*);
-    void (*_stdout)(const char*);
-    void (*_stderr)(const char*);
+    void (*_stdout)(const char*, ...);
+    void (*_stderr)(const char*, ...);
     
     // singleton objects
-    PyVar True, False, None, NotImplemented, Ellipsis;
+    py_TValue True, False, None, NotImplemented, Ellipsis;
     // last error
     py_Error* last_error;
 
     PyObject* __curr_class;
     PyObject* __cached_object_new;
     FuncDecl_ __dynamic_func_decl;
-    PyVar __vectorcall_buffer[PK_MAX_CO_VARNAMES];
+    py_TValue __vectorcall_buffer[PK_MAX_CO_VARNAMES];
 
     pk_ManagedHeap heap;
     ValueStack stack;   // put `stack` at the end for better cache locality
@@ -87,7 +87,7 @@ typedef enum pk_FrameResult{
 
 pk_FrameResult pk_VM__run_top_frame(pk_VM* self);
 
-py_Type pk_VM__new_type(pk_VM* self, const char* name, py_Type base, const PyVar* module, bool subclass_enabled);
+py_Type pk_VM__new_type(pk_VM* self, const char* name, py_Type base, const py_TValue* module, bool subclass_enabled);
 
 #ifdef __cplusplus
 }

+ 5 - 4
include/pocketpy/objects/base.h

@@ -14,8 +14,9 @@ extern "C" {
 #endif
 
 typedef int16_t py_Type;
+typedef struct PyObject PyObject;
 
-typedef struct PyVar{
+typedef struct py_TValue{
     py_Type type;
     bool is_ptr;
     int extra;
@@ -26,9 +27,9 @@ typedef struct PyVar{
         void* _ptr;
         // Vec2
     };
-} PyVar;
+} py_TValue;
 
-static_assert(sizeof(PyVar) <= 16, "!sizeof(PyVar) <= 16");
+static_assert(sizeof(py_TValue) <= 16, "!sizeof(py_TValue) <= 16");
 
 /* predefined vars */
 static const py_Type tp_object = {1}, tp_type = {2};
@@ -44,7 +45,7 @@ static const py_Type tp_ellipsis = {26};
 static const py_Type tp_op_call = {27}, tp_op_yield = {28};
 static const py_Type tp_syntax_error = {29}, tp_stop_iteration = {30};
 
-extern PyVar PY_NULL, PY_OP_CALL, PY_OP_YIELD;
+extern py_TValue PY_NULL, PY_OP_CALL, PY_OP_YIELD;
 
 #ifdef __cplusplus
 }

+ 3 - 3
include/pocketpy/objects/codeobject.h

@@ -74,7 +74,7 @@ typedef struct CodeObject {
     c11_vector/*T=Bytecode*/                codes;
     c11_vector/*T=CodeObjectByteCodeEx*/    codes_ex;
 
-    c11_vector/*T=PyVar*/   consts;     // constants
+    c11_vector/*T=py_TValue*/   consts;     // constants
     c11_vector/*T=StrName*/ varnames;   // local variables
     int nlocals;                        // cached varnames.size()
 
@@ -95,7 +95,7 @@ void CodeObject__gc_mark(const CodeObject* self);
 typedef struct FuncDeclKwArg{
     int index;    // index in co->varnames
     uint16_t key;  // name of this argument
-    PyVar value;  // default value
+    py_TValue value;  // default value
 } FuncDeclKwArg;
 
 typedef struct FuncDecl {
@@ -119,7 +119,7 @@ typedef FuncDecl* FuncDecl_;
 
 FuncDecl_ FuncDecl__rcnew(pk_SourceData_ src, c11_string name);
 void FuncDecl__dtor(FuncDecl* self);
-void FuncDecl__add_kwarg(FuncDecl* self, int index, uint16_t key, const PyVar* value);
+void FuncDecl__add_kwarg(FuncDecl* self, int index, uint16_t key, const py_TValue* value);
 void FuncDecl__gc_mark(const FuncDecl* self);
 
 #ifdef __cplusplus

+ 5 - 5
include/pocketpy/objects/dict.h

@@ -49,7 +49,7 @@ pkpy_Dict pkpy_Dict__copy(const pkpy_Dict* self);
  * @param val value to set
  * @return `true` if the key is newly added, `false` if the key already exists
  */
-bool pkpy_Dict__set(pkpy_Dict* self, PyVar key, PyVar val);
+bool pkpy_Dict__set(pkpy_Dict* self, py_TValue key, py_TValue val);
 
 /**
  * @brief Check if a key exists in the `pkpy_Dict`
@@ -58,7 +58,7 @@ bool pkpy_Dict__set(pkpy_Dict* self, PyVar key, PyVar val);
  * @param key key to check
  * @return `true` if the key exists, `false` otherwise
  */
-bool pkpy_Dict__contains(const pkpy_Dict* self, PyVar key);
+bool pkpy_Dict__contains(const pkpy_Dict* self, py_TValue key);
 
 /**
  * @brief Remove a key from the `pkpy_Dict`
@@ -67,7 +67,7 @@ bool pkpy_Dict__contains(const pkpy_Dict* self, PyVar key);
  * @param key key to remove
  * @return `true` if the key was found and removed, `false` if the key doesn't exist
  */
-bool pkpy_Dict__del(pkpy_Dict* self, PyVar key);
+bool pkpy_Dict__del(pkpy_Dict* self, py_TValue key);
 
 /**
  * @brief Try to get a value from the `pkpy_Dict`
@@ -76,7 +76,7 @@ bool pkpy_Dict__del(pkpy_Dict* self, PyVar key);
  * @param key key to get
  * @return the value associated with the key, `NULL` if the key doesn't exist
  */
-const PyVar* pkpy_Dict__try_get(const pkpy_Dict* self, PyVar key);
+const py_TValue* pkpy_Dict__try_get(const pkpy_Dict* self, py_TValue key);
 
 /**
  * @brief Update the `pkpy_Dict` with another one
@@ -106,7 +106,7 @@ pkpy_DictIter pkpy_Dict__iter(const pkpy_Dict* self);
  * @param value value will be filled with the current value, can be `NULL` if not needed
  * @return `true` if the iteration is still valid, `false` otherwise
  */
-bool pkpy_DictIter__next(pkpy_DictIter* self, PyVar* key, PyVar* value);
+bool pkpy_DictIter__next(pkpy_DictIter* self, py_TValue* key, py_TValue* value);
 
 #ifdef __cplusplus
 }

+ 1 - 1
include/pocketpy/objects/namedict.h

@@ -11,7 +11,7 @@ extern "C" {
 
 #define SMALLMAP_T__HEADER
 #define K uint16_t
-#define V PyVar
+#define V py_TValue
 #define NAME pk_NameDict
 #include "pocketpy/xmacros/smallmap.h"
 #undef SMALLMAP_T__HEADER

+ 4 - 4
include/pocketpy/objects/object.h

@@ -22,18 +22,18 @@ typedef struct PyObject{
 
 static_assert(sizeof(PyObject) <= 8, "!(sizeof(PyObject) <= 8)");
 
-PyVar* PyObject__slots(PyObject* self);
+py_TValue* PyObject__slots(PyObject* self);
 pk_NameDict* PyObject__dict(PyObject* self);
 void* PyObject__value(PyObject* self);
 
-#define PK_OBJ_HEADER_SIZE(slots) ((slots)>=0 ? 8+sizeof(PyVar)*(slots) : 8+sizeof(pk_NameDict))
+#define PK_OBJ_HEADER_SIZE(slots) ((slots)>=0 ? 8+sizeof(py_TValue)*(slots) : 8+sizeof(pk_NameDict))
 
 PyObject* PyObject__new(py_Type type, int slots, int size);
 void PyObject__delete(PyObject* self);
 
-PK_INLINE PyVar PyVar__fromobj(PyObject* obj){
+PK_INLINE py_TValue PyVar__fromobj(PyObject* obj){
     if(!obj) return PY_NULL;
-    PyVar retval = {
+    py_TValue retval = {
         .type = obj->type,
         .is_ptr = true,
         ._obj = obj

+ 5 - 5
include/pocketpy/pocketpy.h

@@ -4,12 +4,12 @@
 #include <stdbool.h>
 
 /************* Public Types *************/
-typedef struct PyObject PyObject;
-typedef struct PyVar PyVar;
+typedef struct py_TValue py_TValue;
 typedef struct pk_VM pk_VM;
 typedef uint16_t py_Name;
 typedef int16_t py_Type;
-typedef PyVar* py_Ref;
+typedef py_TValue* py_Ref;
+typedef struct py_Str py_Str;
 typedef int (*py_CFunction)(int argc, py_Ref argv);
 
 typedef struct py_Error{
@@ -175,8 +175,8 @@ void py_dict__setitem(py_Ref self, const py_Ref key, const py_Ref val);
 void py_dict__delitem(py_Ref self, const py_Ref key);
 void py_dict__clear(py_Ref self);
 
-int py_str(const py_Ref, char* out);
-int py_repr(const py_Ref, char* out);
+int py_str(const py_Ref, py_Str* out);
+int py_repr(const py_Ref, py_Str* out);
 
 #ifdef __cplusplus
 }

+ 6 - 6
src/compiler/compiler.c

@@ -251,7 +251,7 @@ typedef struct ImagExpr {
 
 void ImagExpr__emit_(Expr* self_, Ctx* ctx) {
     ImagExpr* self = (ImagExpr*)self_;
-    PyVar value;
+    py_TValue value;
     py_newfloat(&value, self->value);
     int index = Ctx__add_const(ctx, &value);
     Ctx__emit_(ctx, OP_LOAD_CONST, index, self->line);
@@ -282,7 +282,7 @@ void LiteralExpr__emit_(Expr* self_, Ctx* ctx) {
             break;
         }
         case TokenValue_F64: {
-            PyVar value;
+            py_TValue value;
             py_newfloat(&value, self->value->_f64);
             int index = Ctx__add_const(ctx, &value);
             Ctx__emit_(ctx, OP_LOAD_CONST, index, self->line);
@@ -1331,7 +1331,7 @@ int Ctx__emit_int(Ctx* self, int64_t value, int line) {
     if(is_small_int(value)) {
         return Ctx__emit_(self, OP_LOAD_SMALL_INT, (uint16_t)value, line);
     } else {
-        PyVar tmp;
+        py_TValue tmp;
         py_newint(&tmp, value);
         return Ctx__emit_(self, OP_LOAD_CONST, Ctx__add_const(self, &tmp), line);
     }
@@ -1366,9 +1366,9 @@ int Ctx__add_const_string(Ctx* self, c11_string key) {
     if(val) {
         return *val;
     } else {
-        PyVar tmp;
+        py_TValue tmp;
         py_newstrn(&tmp, key.data, key.size);
-        c11_vector__push(PyVar, &self->co->consts, tmp);
+        c11_vector__push(py_TValue, &self->co->consts, tmp);
         int index = self->co->consts.count - 1;
         c11_smallmap_s2n__set(&self->co_consts_string_dedup_map,
                               py_Str__sv(PyObject__value(tmp._obj)),
@@ -1379,7 +1379,7 @@ int Ctx__add_const_string(Ctx* self, c11_string key) {
 
 int Ctx__add_const(Ctx* self, py_Ref v) {
     assert(v->type != tp_str);
-    c11_vector__push(PyVar, &self->co->consts, *v);
+    c11_vector__push(py_TValue, &self->co->consts, *v);
     return self->co->consts.count - 1;
 }
 

+ 140 - 1151
src/interpreter/ceval.c

@@ -1,1151 +1,140 @@
-// #include "pocketpy/interpreter/ceval.h"
-// #include "pocketpy/objects/base.h"
-
-// namespace pkpy {
-
-// #define PREDICT_INT_OP(op)                                                                                             \
-//     if(is_int(_0) && is_int(_1)) {                                                                                     \
-//         TOP() = VAR(_0._i64 op _1._i64);                                                                     \
-//         DISPATCH()                                                                                                     \
-//     }
-
-// #define PREDICT_INT_DIV_OP(op)                                                                                         \
-//     if(is_int(_0) && is_int(_1)) {                                                                                     \
-//         i64 divisor = _1._i64;                                                                                    \
-//         if(divisor == 0) ZeroDivisionError();                                                                          \
-//         TOP() = VAR(_0._i64 op divisor);                                                                          \
-//         DISPATCH()                                                                                                     \
-//     }
-
-// #define BINARY_F_COMPARE(func, op, rfunc)                                                                              \
-//     PyVar ret;                                                                                                         \
-//     const PyTypeInfo* _ti = _tp_info(_0);                                                                              \
-//     if(_ti->m##func) {                                                                                                 \
-//         ret = _ti->m##func(this, _0, _1);                                                                              \
-//     } else {                                                                                                           \
-//         PyVar self;                                                                                                    \
-//         PyVar _2 = get_unbound_method(_0, func, &self, false);                                                         \
-//         if(_2.type)                                                                                              \
-//             ret = call_method(self, _2, _1);                                                                           \
-//         else                                                                                                           \
-//             ret = NotImplemented;                                                                                      \
-//     }                                                                                                                  \
-//     if(is_not_implemented(ret)) {                                                                                      \
-//         PyVar self;                                                                                                    \
-//         PyVar _2 = get_unbound_method(_1, rfunc, &self, false);                                                        \
-//         if(_2.type)                                                                                              \
-//             ret = call_method(self, _2, _0);                                                                           \
-//         else                                                                                                           \
-//             BinaryOptError(op, _0, _1);                                                                                \
-//         if(is_not_implemented(ret)) BinaryOptError(op, _0, _1);                                                        \
-//     }
-
-// void VM::__op_unpack_sequence(uint16_t arg) {
-//     PyVar _0 = POPX();
-//     if(is_type(_0, VM::tp_tuple)) {
-//         // fast path for tuple
-//         Tuple& tuple = PK_OBJ_GET(Tuple, _0);
-//         if(tuple.size() == arg) {
-//             for(PyVar obj: tuple)
-//                 PUSH(obj);
-//         } else {
-//             ValueError(_S("expected ", (int)arg, " values to unpack, got ", (int)tuple.size()));
-//         }
-//     } else {
-//         auto _lock = gc_scope_lock();  // lock the gc via RAII!!
-//         _0 = py_iter(_0);
-//         const PyTypeInfo* ti = _tp_info(_0);
-//         for(int i = 0; i < arg; i++) {
-//             PyVar _1 = _py_next(ti, _0);
-//             if(_1 == StopIteration) ValueError("not enough values to unpack");
-//             PUSH(_1);
-//         }
-//         if(_py_next(ti, _0) != StopIteration) ValueError("too many values to unpack");
-//     }
-// }
-
-// bool VM::py_lt(PyVar _0, PyVar _1) {
-//     BINARY_F_COMPARE(__lt__, "<", __gt__);
-//     assert(ret.type == tp_bool);
-//     return ret.extra;
-// }
-
-// bool VM::py_le(PyVar _0, PyVar _1) {
-//     BINARY_F_COMPARE(__le__, "<=", __ge__);
-//     assert(ret.type == tp_bool);
-//     return ret.extra;
-// }
-
-// bool VM::py_gt(PyVar _0, PyVar _1) {
-//     BINARY_F_COMPARE(__gt__, ">", __lt__);
-//     assert(ret.type == tp_bool);
-//     return ret.extra;
-// }
-
-// bool VM::py_ge(PyVar _0, PyVar _1) {
-//     BINARY_F_COMPARE(__ge__, ">=", __le__);
-//     assert(ret.type == tp_bool);
-//     return ret.extra;
-// }
-
-// #undef BINARY_F_COMPARE
-
-// #if PK_ENABLE_PROFILER
-// #define CEVAL_STEP_CALLBACK()                                                                                          \
-//     if(_ceval_on_step) _ceval_on_step(this, frame, byte);                                                              \
-//     if(_profiler) _profiler->_step(callstack.size(), frame);                                                           \
-//     if(!_next_breakpoint.empty()) { _next_breakpoint._step(this); }
-// #else
-// #define CEVAL_STEP_CALLBACK()                                                                                          \
-//     if(_ceval_on_step && _ceval_on_step) {                                                                             \
-//         if(_ceval_on_step)                                                                                             \
-//             if(_ceval_on_step) _ceval_on_step(this, frame, byte);                                                      \
-//     }
-// #endif
-
-// #define DISPATCH()                                                                                                     \
-//     {                                                                                                                  \
-//         frame->_ip++;                                                                                                  \
-//         goto __NEXT_STEP;                                                                                              \
-//     }
-// #define DISPATCH_JUMP(__offset)                                                                                        \
-//     {                                                                                                                  \
-//         frame->_ip += __offset;                                                                                        \
-//         goto __NEXT_STEP;                                                                                              \
-//     }
-// #define DISPATCH_JUMP_ABSOLUTE(__target)                                                                               \
-//     {                                                                                                                  \
-//         frame->_ip = c11__at(Bytecode, &frame->co->codes, __target);                                                   \
-//         goto __NEXT_STEP;                                                                                              \
-//     }
-
-// PyVar VM::__run_top_frame() {
-//     Frame* frame = &callstack.top();
-//     const Frame* base_frame = frame;
-//     InternalException __internal_exception;
-
-//     while(true) {
-//         try {
-//             /**********************************************************************/
-//             {
-//             __NEXT_FRAME:
-//                 Bytecode byte;
-
-//                 if(__internal_exception.type == InternalExceptionType::Null) {
-//                     // None
-//                     frame->_ip++;
-//                 } else if(__internal_exception.type == InternalExceptionType::Handled) {
-//                     // HandledException + continue
-//                     frame->_ip = c11__at(Bytecode, &frame->co->codes, __internal_exception.arg);
-//                     __internal_exception = {};
-//                 } else {
-//                     // UnhandledException + continue (need_raise = true)
-//                     // ToBeRaisedException + continue (need_raise = true)
-//                     __internal_exception = {};
-//                     __raise_exc();  // no return
-//                 }
-
-//             __NEXT_STEP:
-//                 byte = *frame->_ip;
-//                 CEVAL_STEP_CALLBACK()
-
-// #if PK_DEBUG_CEVAL_STEP
-//                 __log_s_data();
-// #endif
-//                 switch((Opcode)byte.op) {
-//                     case OP_NO_OP: DISPATCH()
-//                     /*****************************************/
-//                     case OP_POP_TOP: POP(); DISPATCH()
-//                     case OP_DUP_TOP: PUSH(TOP()); DISPATCH()
-//                     case OP_DUP_TOP_TWO:
-//                         // [a, b]
-//                         PUSH(SECOND());  // [a, b, a]
-//                         PUSH(SECOND());  // [a, b, a, b]
-//                         DISPATCH()
-//                     case OP_ROT_TWO: std::swap(TOP(), SECOND()); DISPATCH()
-//                     case OP_ROT_THREE: {
-//                         // [a, b, c] -> [c, a, b]
-//                         PyVar _0 = TOP();
-//                         TOP() = SECOND();
-//                         SECOND() = THIRD();
-//                         THIRD() = _0;
-//                     }
-//                         DISPATCH()
-//                     case OP_PRINT_EXPR:
-//                         if(!is_none(TOP())) stdout_write(py_repr(TOP()) + "\n");
-//                         POP();
-//                         DISPATCH()
-//                     /*****************************************/
-//                     case OP_LOAD_CONST:
-//                         PUSH(c11__getitem(PyVar, &frame->co->consts, byte.arg));
-//                         DISPATCH()
-//                     case OP_LOAD_NONE: PUSH(None); DISPATCH()
-//                     case OP_LOAD_TRUE: PUSH(True); DISPATCH()
-//                     case OP_LOAD_FALSE: PUSH(False); DISPATCH()
-//                     /*****************************************/
-//                     case OP_LOAD_SMALL_INT: s_data.emplace(tp_int, (i64)(int16_t)byte.arg); DISPATCH()
-//                     /*****************************************/
-//                     case OP_LOAD_ELLIPSIS: PUSH(Ellipsis); DISPATCH()
-//                     case OP_LOAD_FUNCTION: {
-//                         FuncDecl_ decl = c11__getitem(FuncDecl_, &frame->co->func_decls, byte.arg);
-//                         PyVar obj;
-//                         if(decl->nested) {
-//                             NameDict* captured = frame->_locals.to_namedict();
-//                             obj = new_object<Function>(tp_function, decl, frame->_module, nullptr, captured);
-//                             uint16_t name = pk_StrName__map2(py_Str__sv(&decl->code->name));
-//                             captured->set(name, obj);
-//                         } else {
-//                             obj = new_object<Function>(tp_function, decl, frame->_module, nullptr, nullptr);
-//                         }
-//                         PUSH(obj);
-//                     }
-//                         DISPATCH()
-//                     case OP_LOAD_NULL: PUSH(PY_NULL); DISPATCH()
-//                     /*****************************************/
-//                     case OP_LOAD_FAST: {
-//                         PyVar _0 = frame->_locals[byte.arg];
-//                         if(_0 == PY_NULL) vm->UnboundLocalError(c11__getitem(uint16_t, &frame->co->varnames, byte.arg));
-//                         PUSH(_0);
-//                     }
-//                         DISPATCH()
-//                     case OP_LOAD_NAME: {
-//                         StrName _name(byte.arg);
-//                         PyVar* slot = frame->_locals.try_get_name(_name);
-//                         if(slot != nullptr) {
-//                             if(*slot == PY_NULL) vm->UnboundLocalError(_name);
-//                             PUSH(*slot);
-//                             DISPATCH()
-//                         }
-//                         PyVar* _0 = frame->f_closure_try_get(_name);
-//                         if(_0 != nullptr) {
-//                             PUSH(*_0);
-//                             DISPATCH()
-//                         }
-//                         _0 = frame->f_globals().try_get_2_likely_found(_name);
-//                         if(_0 != nullptr) {
-//                             PUSH(*_0);
-//                             DISPATCH()
-//                         }
-//                         _0 = vm->builtins->attr().try_get_2_likely_found(_name);
-//                         if(_0 != nullptr) {
-//                             PUSH(*_0);
-//                             DISPATCH()
-//                         }
-//                         vm->NameError(_name);
-//                     }
-//                         DISPATCH()
-//                     case OP_LOAD_NONLOCAL: {
-//                         StrName _name(byte.arg);
-//                         PyVar* _0 = frame->f_closure_try_get(_name);
-//                         if(_0 != nullptr) {
-//                             PUSH(*_0);
-//                             DISPATCH()
-//                         }
-//                         _0 = frame->f_globals().try_get_2_likely_found(_name);
-//                         if(_0 != nullptr) {
-//                             PUSH(*_0);
-//                             DISPATCH()
-//                         }
-//                         _0 = vm->builtins->attr().try_get_2_likely_found(_name);
-//                         if(_0 != nullptr) {
-//                             PUSH(*_0);
-//                             DISPATCH()
-//                         }
-//                         vm->NameError(_name);
-//                     }
-//                         DISPATCH()
-//                     case OP_LOAD_GLOBAL: {
-//                         StrName _name(byte.arg);
-//                         PyVar _0 = frame->f_globals().try_get_likely_found(_name);
-//                         if(_0 != nullptr) {
-//                             PUSH(_0);
-//                             DISPATCH()
-//                         }
-//                         _0 = vm->builtins->attr().try_get_likely_found(_name);
-//                         if(_0 != nullptr) {
-//                             PUSH(_0);
-//                             DISPATCH()
-//                         }
-//                         vm->NameError(_name);
-//                     }
-//                         DISPATCH()
-//                     case OP_LOAD_ATTR: {
-//                         TOP() = getattr(TOP(), StrName(byte.arg));
-//                     }
-//                         DISPATCH()
-//                     case OP_LOAD_CLASS_GLOBAL: {
-//                         assert(__curr_class != nullptr);
-//                         StrName _name(byte.arg);
-//                         PyVar _0 = getattr(__curr_class, _name, false);
-//                         if(_0 != nullptr) {
-//                             PUSH(_0);
-//                             DISPATCH()
-//                         }
-//                         // load global if attribute not found
-//                         _0 = frame->f_globals().try_get_likely_found(_name);
-//                         if(_0 != nullptr) {
-//                             PUSH(_0);
-//                             DISPATCH()
-//                         }
-//                         _0 = vm->builtins->attr().try_get_likely_found(_name);
-//                         if(_0 != nullptr) {
-//                             PUSH(_0);
-//                             DISPATCH()
-//                         }
-//                         vm->NameError(_name);
-//                     }
-//                         DISPATCH()
-//                     case OP_LOAD_METHOD: {
-//                         PyVar _0;
-//                         TOP() = get_unbound_method(TOP(), StrName(byte.arg), &_0, true, true);
-//                         PUSH(_0);
-//                     }
-//                         DISPATCH()
-//                     case OP_LOAD_SUBSCR: {
-//                         PyVar _1 = POPX();  // b
-//                         PyVar _0 = TOP();   // a
-//                         auto _ti = _tp_info(_0);
-//                         if(_ti->m__getitem__) {
-//                             TOP() = _ti->m__getitem__(this, _0, _1);
-//                         } else {
-//                             TOP() = call_method(_0, __getitem__, _1);
-//                         }
-//                     }
-//                         DISPATCH()
-//                     case OP_LOAD_SUBSCR_FAST: {
-//                         PyVar _1 = frame->_locals[byte.arg];
-//                         if(_1 == PY_NULL) vm->UnboundLocalError(c11__getitem(uint16_t, &frame->co->varnames, byte.arg));
-//                         PyVar _0 = TOP();  // a
-//                         auto _ti = _tp_info(_0);
-//                         if(_ti->m__getitem__) {
-//                             TOP() = _ti->m__getitem__(this, _0, _1);
-//                         } else {
-//                             TOP() = call_method(_0, __getitem__, _1);
-//                         }
-//                     }
-//                         DISPATCH()
-//                     case OP_LOAD_SUBSCR_SMALL_INT: {
-//                         PyVar _1 = VAR((int16_t)byte.arg);
-//                         PyVar _0 = TOP();  // a
-//                         auto _ti = _tp_info(_0);
-//                         if(_ti->m__getitem__) {
-//                             TOP() = _ti->m__getitem__(this, _0, _1);
-//                         } else {
-//                             TOP() = call_method(_0, __getitem__, _1);
-//                         }
-//                     }
-//                         DISPATCH()
-//                     case OP_STORE_FAST: frame->_locals[byte.arg] = POPX(); DISPATCH()
-//                     case OP_STORE_NAME: {
-//                         StrName _name(byte.arg);
-//                         PyVar _0 = POPX();
-//                         if(frame->_callable != nullptr) {
-//                             PyVar* slot = frame->_locals.try_get_name(_name);
-//                             if(slot != nullptr) {
-//                                 *slot = _0;  // store in locals if possible
-//                             } else {
-//                                 Function& func = frame->_callable->as<Function>();
-//                                 if(func.decl == __dynamic_func_decl) {
-//                                     assert(func._closure != nullptr);
-//                                     func._closure->set(_name, _0);
-//                                 } else {
-//                                     vm->NameError(_name);
-//                                 }
-//                             }
-//                         } else {
-//                             frame->f_globals().set(_name, _0);
-//                         }
-//                     }
-//                         DISPATCH()
-//                     case OP_STORE_GLOBAL: frame->f_globals().set(StrName(byte.arg), POPX()); DISPATCH()
-//                     case OP_STORE_ATTR: {
-//                         PyVar _0 = TOP();     // a
-//                         PyVar _1 = SECOND();  // val
-//                         setattr(_0, StrName(byte.arg), _1);
-//                         STACK_SHRINK(2);
-//                     }
-//                         DISPATCH()
-//                     case OP_STORE_SUBSCR: {
-//                         PyVar _2 = POPX();  // b
-//                         PyVar _1 = POPX();  // a
-//                         PyVar _0 = POPX();  // val
-//                         auto _ti = _tp_info(_1);
-//                         if(_ti->m__setitem__) {
-//                             _ti->m__setitem__(this, _1, _2, _0);
-//                         } else {
-//                             call_method(_1, __setitem__, _2, _0);
-//                         }
-//                     }
-//                         DISPATCH()
-//                     case OP_STORE_SUBSCR_FAST: {
-//                         PyVar _2 = frame->_locals[byte.arg];  // b
-//                         if(_2 == PY_NULL) vm->UnboundLocalError(c11__getitem(uint16_t, &frame->co->varnames, byte.arg));
-//                         PyVar _1 = POPX();  // a
-//                         PyVar _0 = POPX();  // val
-//                         auto _ti = _tp_info(_1);
-//                         if(_ti->m__setitem__) {
-//                             _ti->m__setitem__(this, _1, _2, _0);
-//                         } else {
-//                             call_method(_1, __setitem__, _2, _0);
-//                         }
-//                     }
-//                         DISPATCH()
-//                     case OP_DELETE_FAST: {
-//                         PyVar _0 = frame->_locals[byte.arg];
-//                         if(_0 == PY_NULL) vm->UnboundLocalError(c11__getitem(uint16_t, &frame->co->varnames, byte.arg));
-//                         frame->_locals[byte.arg].set_null();
-//                     }
-//                         DISPATCH()
-//                     case OP_DELETE_NAME: {
-//                         StrName _name(byte.arg);
-//                         if(frame->_callable != nullptr) {
-//                             PyVar* slot = frame->_locals.try_get_name(_name);
-//                             if(slot != nullptr) {
-//                                 slot->set_null();
-//                             } else {
-//                                 Function& func = frame->_callable->as<Function>();
-//                                 if(func.decl == __dynamic_func_decl) {
-//                                     assert(func._closure != nullptr);
-//                                     bool ok = func._closure->del(_name);
-//                                     if(!ok) vm->NameError(_name);
-//                                 } else {
-//                                     vm->NameError(_name);
-//                                 }
-//                             }
-//                         } else {
-//                             if(!frame->f_globals().del(_name)) vm->NameError(_name);
-//                         }
-//                     }
-//                         DISPATCH()
-//                     case OP_DELETE_GLOBAL: {
-//                         StrName _name(byte.arg);
-//                         if(!frame->f_globals().del(_name)) vm->NameError(_name);
-//                     }
-//                         DISPATCH()
-//                     case OP_DELETE_ATTR: {
-//                         PyVar _0 = POPX();
-//                         delattr(_0, StrName(byte.arg));
-//                     }
-//                         DISPATCH()
-//                     case OP_DELETE_SUBSCR: {
-//                         PyVar _1 = POPX();
-//                         PyVar _0 = POPX();
-//                         auto _ti = _tp_info(_0);
-//                         if(_ti->m__delitem__) {
-//                             _ti->m__delitem__(this, _0, _1);
-//                         } else {
-//                             call_method(_0, __delitem__, _1);
-//                         }
-//                     }
-//                         DISPATCH()
-//                     /*****************************************/
-//                     case OP_BUILD_LONG: {
-//                         PyVar _0 = builtins->attr().try_get_likely_found(pk_id_long);
-//                         if(_0 == nullptr) AttributeError(builtins, pk_id_long);
-//                         TOP() = call(_0, TOP());
-//                     }
-//                         DISPATCH()
-//                     case OP_BUILD_IMAG: {
-//                         PyVar _0 = builtins->attr().try_get_likely_found(pk_id_complex);
-//                         if(_0 == nullptr) AttributeError(builtins, pk_id_long);
-//                         TOP() = call(_0, VAR(0), TOP());
-//                     }
-//                         DISPATCH()
-//                     case OP_BUILD_BYTES: {
-//                         const Str& s = CAST(Str&, TOP());
-//                         unsigned char* p = (unsigned char*)std::malloc(s.size);
-//                         std::memcpy(p, s.c_str(), s.size);
-//                         TOP() = VAR(Bytes(p, s.size));
-//                     }
-//                         DISPATCH()
-//                     case OP_BUILD_TUPLE: {
-//                         PyVar _0 = VAR(STACK_VIEW(byte.arg).to_tuple());
-//                         STACK_SHRINK(byte.arg);
-//                         PUSH(_0);
-//                     }
-//                         DISPATCH()
-//                     case OP_BUILD_LIST: {
-//                         PyVar _0 = VAR(STACK_VIEW(byte.arg).to_list());
-//                         STACK_SHRINK(byte.arg);
-//                         PUSH(_0);
-//                     }
-//                         DISPATCH()
-//                     case OP_BUILD_DICT: {
-//                         if(byte.arg == 0) {
-//                             PUSH(VAR(Dict()));
-//                             DISPATCH()
-//                         }
-//                         PyVar _0 = VAR(STACK_VIEW(byte.arg).to_list());
-//                         _0 = call(_t(tp_dict), _0);
-//                         STACK_SHRINK(byte.arg);
-//                         PUSH(_0);
-//                     }
-//                         DISPATCH()
-//                     case OP_BUILD_SET: {
-//                         PyVar _0 = VAR(STACK_VIEW(byte.arg).to_list());
-//                         _0 = call(builtins->attr()[pk_id_set], _0);
-//                         STACK_SHRINK(byte.arg);
-//                         PUSH(_0);
-//                     }
-//                         DISPATCH()
-//                     case OP_BUILD_SLICE: {
-//                         PyVar _2 = POPX();  // step
-//                         PyVar _1 = POPX();  // stop
-//                         PyVar _0 = POPX();  // start
-//                         PUSH(VAR(Slice(_0, _1, _2)));
-//                     }
-//                         DISPATCH()
-//                     case OP_BUILD_STRING: {
-//                         SStream ss;
-//                         ArgsView view = STACK_VIEW(byte.arg);
-//                         for(PyVar obj: view)
-//                             ss << py_str(obj);
-//                         STACK_SHRINK(byte.arg);
-//                         PUSH(VAR(ss.str()));
-//                     }
-//                         DISPATCH()
-//                     /*****************************************/
-//                     case OP_BUILD_TUPLE_UNPACK: {
-//                         List list;
-//                         __unpack_as_list(STACK_VIEW(byte.arg), list);
-//                         STACK_SHRINK(byte.arg);
-//                         PyVar _0 = VAR(list.to_tuple());
-//                         PUSH(_0);
-//                     }
-//                         DISPATCH()
-//                     case OP_BUILD_LIST_UNPACK: {
-//                         List list;
-//                         __unpack_as_list(STACK_VIEW(byte.arg), list);
-//                         STACK_SHRINK(byte.arg);
-//                         PyVar _0 = VAR(std::move(list));
-//                         PUSH(_0);
-//                     }
-//                         DISPATCH()
-//                     case OP_BUILD_DICT_UNPACK: {
-//                         Dict dict;
-//                         __unpack_as_dict(STACK_VIEW(byte.arg), dict);
-//                         STACK_SHRINK(byte.arg);
-//                         PyVar _0 = VAR(std::move(dict));
-//                         PUSH(_0);
-//                     }
-//                         DISPATCH()
-//                     case OP_BUILD_SET_UNPACK: {
-//                         List list;
-//                         __unpack_as_list(STACK_VIEW(byte.arg), list);
-//                         STACK_SHRINK(byte.arg);
-//                         PyVar _0 = VAR(std::move(list));
-//                         _0 = call(builtins->attr()[pk_id_set], _0);
-//                         PUSH(_0);
-//                     }
-//                         DISPATCH()
-//                         /*****************************************/
-// #define BINARY_OP_SPECIAL(func)                                                                                        \
-//     _ti = _tp_info(_0);                                                                                                \
-//     if(_ti->m##func) {                                                                                                 \
-//         TOP() = _ti->m##func(this, _0, _1);                                                                            \
-//     } else {                                                                                                           \
-//         PyVar self;                                                                                                    \
-//         PyVar _2 = get_unbound_method(_0, func, &self, false);                                                         \
-//         if(_2 != nullptr)                                                                                              \
-//             TOP() = call_method(self, _2, _1);                                                                         \
-//         else                                                                                                           \
-//             TOP() = NotImplemented;                                                                                    \
-//     }
-
-// #define BINARY_OP_RSPECIAL(op, func)                                                                                   \
-//     if(is_not_implemented(TOP())) {                                                                                    \
-//         PyVar self;                                                                                                    \
-//         PyVar _2 = get_unbound_method(_1, func, &self, false);                                                         \
-//         if(_2 != nullptr)                                                                                              \
-//             TOP() = call_method(self, _2, _0);                                                                         \
-//         else                                                                                                           \
-//             BinaryOptError(op, _0, _1);                                                                                \
-//         if(is_not_implemented(TOP())) BinaryOptError(op, _0, _1);                                                      \
-//     }
-
-//                     case OP_BINARY_TRUEDIV: {
-//                         PyVar _1 = POPX();
-//                         PyVar _0 = TOP();
-//                         const PyTypeInfo* _ti;
-//                         BINARY_OP_SPECIAL(__truediv__);
-//                         if(is_not_implemented(TOP())) BinaryOptError("/", _0, _1);
-//                     }
-//                         DISPATCH()
-//                     case OP_BINARY_POW: {
-//                         PyVar _1 = POPX();
-//                         PyVar _0 = TOP();
-//                         const PyTypeInfo* _ti;
-//                         BINARY_OP_SPECIAL(__pow__);
-//                         if(is_not_implemented(TOP())) BinaryOptError("**", _0, _1);
-//                     }
-//                         DISPATCH()
-//                     case OP_BINARY_ADD: {
-//                         PyVar _1 = POPX();
-//                         PyVar _0 = TOP();
-//                         PREDICT_INT_OP(+)
-//                         const PyTypeInfo* _ti;
-//                         BINARY_OP_SPECIAL(__add__);
-//                         BINARY_OP_RSPECIAL("+", __radd__);
-//                     }
-//                         DISPATCH()
-//                     case OP_BINARY_SUB: {
-//                         PyVar _1 = POPX();
-//                         PyVar _0 = TOP();
-//                         PREDICT_INT_OP(-)
-//                         const PyTypeInfo* _ti;
-//                         BINARY_OP_SPECIAL(__sub__);
-//                         BINARY_OP_RSPECIAL("-", __rsub__);
-//                     }
-//                         DISPATCH()
-//                     case OP_BINARY_MUL: {
-//                         PyVar _1 = POPX();
-//                         PyVar _0 = TOP();
-//                         PREDICT_INT_OP(*)
-//                         const PyTypeInfo* _ti;
-//                         BINARY_OP_SPECIAL(__mul__);
-//                         BINARY_OP_RSPECIAL("*", __rmul__);
-//                     }
-//                         DISPATCH()
-//                     case OP_BINARY_FLOORDIV: {
-//                         PyVar _1 = POPX();
-//                         PyVar _0 = TOP();
-//                         PREDICT_INT_DIV_OP(/)
-//                         const PyTypeInfo* _ti;
-//                         BINARY_OP_SPECIAL(__floordiv__);
-//                         if(is_not_implemented(TOP())) BinaryOptError("//", _0, _1);
-//                     }
-//                         DISPATCH()
-//                     case OP_BINARY_MOD: {
-//                         PyVar _1 = POPX();
-//                         PyVar _0 = TOP();
-//                         PREDICT_INT_DIV_OP(%)
-//                         const PyTypeInfo* _ti;
-//                         BINARY_OP_SPECIAL(__mod__);
-//                         if(is_not_implemented(TOP())) BinaryOptError("%", _0, _1);
-//                     }
-//                         DISPATCH()
-//                     case OP_COMPARE_LT: {
-//                         PyVar _1 = POPX();
-//                         PyVar _0 = TOP();
-//                         PREDICT_INT_OP(<)
-//                         TOP() = VAR(py_lt(_0, _1));
-//                     }
-//                         DISPATCH()
-//                     case OP_COMPARE_LE: {
-//                         PyVar _1 = POPX();
-//                         PyVar _0 = TOP();
-//                         PREDICT_INT_OP(<=)
-//                         TOP() = VAR(py_le(_0, _1));
-//                     }
-//                         DISPATCH()
-//                     case OP_COMPARE_EQ: {
-//                         PyVar _1 = POPX();
-//                         PyVar _0 = TOP();
-//                         TOP() = VAR(py_eq(_0, _1));
-//                     }
-//                         DISPATCH()
-//                     case OP_COMPARE_NE: {
-//                         PyVar _1 = POPX();
-//                         PyVar _0 = TOP();
-//                         TOP() = VAR(py_ne(_0, _1));
-//                     }
-//                         DISPATCH()
-//                     case OP_COMPARE_GT: {
-//                         PyVar _1 = POPX();
-//                         PyVar _0 = TOP();
-//                         PREDICT_INT_OP(>)
-//                         TOP() = VAR(py_gt(_0, _1));
-//                     }
-//                         DISPATCH()
-//                     case OP_COMPARE_GE: {
-//                         PyVar _1 = POPX();
-//                         PyVar _0 = TOP();
-//                         PREDICT_INT_OP(>=)
-//                         TOP() = VAR(py_ge(_0, _1));
-//                     }
-//                         DISPATCH()
-//                     case OP_BITWISE_LSHIFT: {
-//                         PyVar _1 = POPX();
-//                         PyVar _0 = TOP();
-//                         PREDICT_INT_OP(<<)
-//                         const PyTypeInfo* _ti;
-//                         BINARY_OP_SPECIAL(__lshift__);
-//                         if(is_not_implemented(TOP())) BinaryOptError("<<", _0, _1);
-//                     }
-//                         DISPATCH()
-//                     case OP_BITWISE_RSHIFT: {
-//                         PyVar _1 = POPX();
-//                         PyVar _0 = TOP();
-//                         PREDICT_INT_OP(>>)
-//                         const PyTypeInfo* _ti;
-//                         BINARY_OP_SPECIAL(__rshift__);
-//                         if(is_not_implemented(TOP())) BinaryOptError(">>", _0, _1);
-//                     }
-//                         DISPATCH()
-//                     case OP_BITWISE_AND: {
-//                         PyVar _1 = POPX();
-//                         PyVar _0 = TOP();
-//                         PREDICT_INT_OP(&)
-//                         const PyTypeInfo* _ti;
-//                         BINARY_OP_SPECIAL(__and__);
-//                         if(is_not_implemented(TOP())) BinaryOptError("&", _0, _1);
-//                     }
-//                         DISPATCH()
-//                     case OP_BITWISE_OR: {
-//                         PyVar _1 = POPX();
-//                         PyVar _0 = TOP();
-//                         PREDICT_INT_OP(|)
-//                         const PyTypeInfo* _ti;
-//                         BINARY_OP_SPECIAL(__or__);
-//                         if(is_not_implemented(TOP())) BinaryOptError("|", _0, _1);
-//                     }
-//                         DISPATCH()
-//                     case OP_BITWISE_XOR: {
-//                         PyVar _1 = POPX();
-//                         PyVar _0 = TOP();
-//                         PREDICT_INT_OP(^)
-//                         const PyTypeInfo* _ti;
-//                         BINARY_OP_SPECIAL(__xor__);
-//                         if(is_not_implemented(TOP())) BinaryOptError("^", _0, _1);
-//                     }
-//                         DISPATCH()
-//                     case OP_BINARY_MATMUL: {
-//                         PyVar _1 = POPX();
-//                         PyVar _0 = TOP();
-//                         const PyTypeInfo* _ti;
-//                         BINARY_OP_SPECIAL(__matmul__);
-//                         if(is_not_implemented(TOP())) BinaryOptError("@", _0, _1);
-//                     }
-//                         DISPATCH()
-
-// #undef BINARY_OP_SPECIAL
-// #undef BINARY_OP_RSPECIAL
-// #undef PREDICT_INT_OP
-
-//                     case OP_IS_OP: {
-//                         PyVar _1 = POPX();  // rhs
-//                         PyVar _0 = TOP();   // lhs
-//                         TOP() = PyVar__IS_OP(&_0, &_1) ? True : False;
-//                     }
-//                         DISPATCH()
-//                     case OP_IS_NOT_OP: {
-//                         PyVar _1 = POPX();  // rhs
-//                         PyVar _0 = TOP();   // lhs
-//                         TOP() = PyVar__IS_OP(&_0, &_1) ? False : True;
-//                     }
-//                         DISPATCH()
-//                     case OP_CONTAINS_OP: {
-//                         // a in b -> b __contains__ a
-//                         auto _ti = _tp_info(TOP());
-//                         PyVar _0;
-//                         if(_ti->m__contains__) {
-//                             _0 = _ti->m__contains__(this, TOP(), SECOND());
-//                         } else {
-//                             _0 = call_method(TOP(), __contains__, SECOND());
-//                         }
-//                         POP();
-//                         TOP() = VAR(static_cast<bool>((int)CAST(bool, _0) ^ byte.arg));
-//                     }
-//                         DISPATCH()
-//                     /*****************************************/
-//                     case OP_JUMP_FORWARD: DISPATCH_JUMP((int16_t)byte.arg)
-//                     case OP_POP_JUMP_IF_FALSE:
-//                         if(!py_bool(POPX())) DISPATCH_JUMP((int16_t)byte.arg)
-//                         DISPATCH()
-//                     case OP_POP_JUMP_IF_TRUE:
-//                         if(py_bool(POPX())) DISPATCH_JUMP((int16_t)byte.arg)
-//                         DISPATCH()
-//                     case OP_JUMP_IF_TRUE_OR_POP:
-//                         if(py_bool(TOP())) {
-//                             DISPATCH_JUMP((int16_t)byte.arg)
-//                         } else {
-//                             POP();
-//                             DISPATCH()
-//                         }
-//                     case OP_JUMP_IF_FALSE_OR_POP:
-//                         if(!py_bool(TOP())) {
-//                             DISPATCH_JUMP((int16_t)byte.arg)
-//                         } else {
-//                             POP();
-//                             DISPATCH()
-//                         }
-//                     case OP_SHORTCUT_IF_FALSE_OR_POP:
-//                         if(!py_bool(TOP())) {  // [b, False]
-//                             STACK_SHRINK(2);   // []
-//                             PUSH(vm->False);   // [False]
-//                             DISPATCH_JUMP((int16_t)byte.arg)
-//                         } else {
-//                             POP();  // [b]
-//                             DISPATCH()
-//                         }
-//                     case OP_LOOP_CONTINUE:
-//                         // just an alias of OP_JUMP_FORWARD
-//                         DISPATCH_JUMP((int16_t)byte.arg)
-//                     case OP_LOOP_BREAK: {
-//                         frame->prepare_jump_break(&s_data, frame->ip() + byte.arg);
-//                         DISPATCH_JUMP((int16_t)byte.arg)
-//                     }
-//                     case OP_JUMP_ABSOLUTE_TOP: DISPATCH_JUMP_ABSOLUTE(_CAST(int, POPX()))
-//                     case OP_GOTO: {
-//                         StrName _name(byte.arg);
-//                         int target = c11_smallmap_n2i__get(&frame->co->labels, byte.arg, -1);
-//                         if(target < 0) RuntimeError(_S("label ", _name.escape(), " not found"));
-//                         frame->prepare_jump_break(&s_data, target);
-//                         DISPATCH_JUMP_ABSOLUTE(target)
-//                     }
-//                     /*****************************************/
-//                     case OP_FSTRING_EVAL: {
-//                         PyVar _0 = c11__getitem(PyVar, &frame->co->consts, byte.arg);
-//                         std::string_view string = CAST(Str&, _0).sv();
-//                         // TODO: optimize this
-//                         CodeObject* code = vm->compile(string, "<eval>", EVAL_MODE, true);
-//                         _0 = vm->_exec(code, frame->_module, frame->_callable, frame->_locals);
-//                         CodeObject__delete(code);
-//                         PUSH(_0);
-//                     }
-//                         DISPATCH()
-//                     case OP_REPR: TOP() = VAR(py_repr(TOP())); DISPATCH()
-//                     case OP_CALL: {
-//                         pk_ManagedHeap__collect_if_needed(&heap);
-//                         PyVar _0 = vectorcall(byte.arg & 0xFF,         // ARGC
-//                                               (byte.arg >> 8) & 0xFF,  // KWARGC
-//                                               true);
-//                         if(_0.type == tp_op_call) {
-//                             frame = &callstack.top();
-//                             goto __NEXT_FRAME;
-//                         }
-//                         PUSH(_0);
-//                     }
-//                         DISPATCH()
-//                     case OP_CALL_TP: {
-//                         pk_ManagedHeap__collect_if_needed(&heap);
-//                         PyVar _0;
-//                         PyVar _1;
-//                         PyVar _2;
-//                         // [callable, <self>, args: tuple, kwargs: dict | NULL]
-//                         if(byte.arg) {
-//                             _2 = POPX();
-//                             _1 = POPX();
-//                             for(PyVar obj: _CAST(Tuple&, _1))
-//                                 PUSH(obj);
-//                             _CAST(Dict&, _2).apply([this](PyVar k, PyVar v) {
-//                                 PUSH(VAR(StrName(CAST(Str&, k)).index));
-//                                 PUSH(v);
-//                             });
-//                             _0 = vectorcall(_CAST(Tuple&, _1).size(),  // ARGC
-//                                             _CAST(Dict&, _2).size(),   // KWARGC
-//                                             true);
-//                         } else {
-//                             // no **kwargs
-//                             _1 = POPX();
-//                             for(PyVar obj: _CAST(Tuple&, _1))
-//                                 PUSH(obj);
-//                             _0 = vectorcall(_CAST(Tuple&, _1).size(),  // ARGC
-//                                             0,                         // KWARGC
-//                                             true);
-//                         }
-//                         if(_0.type == tp_op_call) {
-//                             frame = &callstack.top();
-//                             goto __NEXT_FRAME;
-//                         }
-//                         PUSH(_0);
-//                     }
-//                         DISPATCH()
-//                     case OP_RETURN_VALUE: {
-//                         PyVar _0 = byte.arg == BC_NOARG ? POPX() : None;
-//                         __pop_frame();
-//                         if(frame == base_frame) {  // [ frameBase<- ]
-//                             return _0;
-//                         } else {
-//                             frame = &callstack.top();
-//                             PUSH(_0);
-//                             goto __NEXT_FRAME;
-//                         }
-//                     }
-//                         DISPATCH()
-//                     case OP_YIELD_VALUE: return PY_OP_YIELD;
-//                     /*****************************************/
-//                     case OP_LIST_APPEND: {
-//                         PyVar _0 = POPX();
-//                         PK_OBJ_GET(List, SECOND()).push_back(_0);
-//                     }
-//                         DISPATCH()
-//                     case OP_DICT_ADD: {
-//                         PyVar _0 = POPX();
-//                         const Tuple& t = PK_OBJ_GET(Tuple, _0);
-//                         PK_OBJ_GET(Dict, SECOND()).set(this, t[0], t[1]);
-//                     }
-//                         DISPATCH()
-//                     case OP_SET_ADD: {
-//                         PyVar _0 = POPX();
-//                         call_method(SECOND(), pk_id_add, _0);
-//                     }
-//                         DISPATCH()
-//                     /*****************************************/
-//                     case OP_UNARY_NEGATIVE: TOP() = py_negate(TOP()); DISPATCH()
-//                     case OP_UNARY_NOT: TOP() = VAR(!py_bool(TOP())); DISPATCH()  
-//                     case OP_UNARY_STAR: TOP() = VAR(StarWrapper(byte.arg, TOP())); DISPATCH()
-//                     case OP_UNARY_INVERT: {
-//                         PyVar _0;
-//                         auto _ti = _tp_info(TOP());
-//                         if(_ti->m__invert__)
-//                             _0 = _ti->m__invert__(this, TOP());
-//                         else
-//                             _0 = call_method(TOP(), __invert__);
-//                         TOP() = _0;
-//                     }
-//                         DISPATCH()
-//                     /*****************************************/
-//                     case OP_GET_ITER: TOP() = py_iter(TOP()); DISPATCH()
-//                     case OP_GET_ITER_NEW: TOP() = py_iter(TOP()); DISPATCH()
-//                     case OP_FOR_ITER: {
-//                         PyVar _0 = py_next(TOP());
-//                         if(_0 == StopIteration) {
-//                             int target = frame->prepare_loop_break(&s_data);
-//                             DISPATCH_JUMP_ABSOLUTE(target)
-//                         } else {
-//                             PUSH(_0);
-//                             DISPATCH()
-//                         }
-//                     }
-//                     case OP_FOR_ITER_STORE_FAST: {
-//                         PyVar _0 = py_next(TOP());
-//                         if(_0 == StopIteration) {
-//                             int target = frame->prepare_loop_break(&s_data);
-//                             DISPATCH_JUMP_ABSOLUTE(target)
-//                         } else {
-//                             frame->_locals[byte.arg] = _0;
-//                             DISPATCH()
-//                         }
-//                     }
-//                     case OP_FOR_ITER_STORE_GLOBAL: {
-//                         PyVar _0 = py_next(TOP());
-//                         if(_0 == StopIteration) {
-//                             int target = frame->prepare_loop_break(&s_data);
-//                             DISPATCH_JUMP_ABSOLUTE(target)
-//                         } else {
-//                             frame->f_globals().set(StrName(byte.arg), _0);
-//                             DISPATCH()
-//                         }
-//                     }
-//                     case OP_FOR_ITER_YIELD_VALUE: {
-//                         PyVar _0 = py_next(TOP());
-//                         if(_0 == StopIteration) {
-//                             int target = frame->prepare_loop_break(&s_data);
-//                             DISPATCH_JUMP_ABSOLUTE(target)
-//                         } else {
-//                             PUSH(_0);
-//                             return PY_OP_YIELD;
-//                         }
-//                     }
-//                     case OP_FOR_ITER_UNPACK: {
-//                         PyVar _0 = TOP();
-//                         const PyTypeInfo* _ti = _tp_info(_0);
-//                         if(_ti->op__next__) {
-//                             unsigned n = _ti->op__next__(this, _0);
-//                             if(n == 0) {
-//                                 // StopIteration
-//                                 int target = frame->prepare_loop_break(&s_data);
-//                                 DISPATCH_JUMP_ABSOLUTE(target)
-//                             } else if(n == 1) {
-//                                 // UNPACK_SEQUENCE
-//                                 __op_unpack_sequence(byte.arg);
-//                             } else {
-//                                 if(n != byte.arg) {
-//                                     ValueError(_S("expected ", (int)byte.arg, " values to unpack, got ", (int)n));
-//                                 }
-//                             }
-//                         } else {
-//                             // FOR_ITER
-//                             _0 = call_method(_0, __next__);
-//                             if(_0 != StopIteration) {
-//                                 PUSH(_0);
-//                                 // UNPACK_SEQUENCE
-//                                 __op_unpack_sequence(byte.arg);
-//                             } else {
-//                                 int target = frame->prepare_loop_break(&s_data);
-//                                 DISPATCH_JUMP_ABSOLUTE(target)
-//                             }
-//                         }
-//                     }
-//                         DISPATCH()
-//                     /*****************************************/
-//                     case OP_IMPORT_PATH: {
-//                         PyVar _0 = c11__getitem(PyVar, &frame->co->consts, byte.arg);
-//                         PUSH(py_import(CAST(Str&, _0)));
-//                     }
-//                         DISPATCH()
-//                     case OP_POP_IMPORT_STAR: {
-//                         PyVar _0 = POPX();  // pop the module
-//                         PyVar _1 = _0->attr().try_get(__all__);
-//                         StrName _name;
-//                         if(_1 != nullptr) {
-//                             for(PyVar key: CAST(List&, _1)) {
-//                                 _name = StrName::get(CAST(Str&, key).sv());
-//                                 PyVar value = _0->attr().try_get_likely_found(_name);
-//                                 if(value == nullptr) {
-//                                     ImportError(_S("cannot import name ", _name.escape()));
-//                                 } else {
-//                                     frame->f_globals().set(_name, value);
-//                                 }
-//                             }
-//                         } else {
-//                             for(auto& [name, value]: _0->attr().items()) {
-//                                 std::string_view s = name.sv();
-//                                 if(s.empty() || s[0] == '_') continue;
-//                                 frame->f_globals().set(name, value);
-//                             }
-//                         }
-//                     }
-//                         DISPATCH()
-//                     /*****************************************/
-//                     case OP_UNPACK_SEQUENCE: {
-//                         __op_unpack_sequence(byte.arg);
-//                     }
-//                         DISPATCH()
-//                     case OP_UNPACK_EX: {
-//                         auto _lock = gc_scope_lock();  // lock the gc via RAII!!
-//                         PyVar _0 = py_iter(POPX());
-//                         const PyTypeInfo* _ti = _tp_info(_0);
-//                         PyVar _1;
-//                         for(int i = 0; i < byte.arg; i++) {
-//                             _1 = _py_next(_ti, _0);
-//                             if(_1 == StopIteration) ValueError("not enough values to unpack");
-//                             PUSH(_1);
-//                         }
-//                         List extras;
-//                         while(true) {
-//                             _1 = _py_next(_ti, _0);
-//                             if(_1 == StopIteration) break;
-//                             extras.push_back(_1);
-//                         }
-//                         PUSH(VAR(std::move(extras)));
-//                     }
-//                         DISPATCH()
-//                     /*****************************************/
-//                     case OP_BEGIN_CLASS: {
-//                         StrName _name(byte.arg);
-//                         PyVar _0 = POPX();  // super
-//                         if(is_none(_0)) _0 = _t(tp_object);
-//                         check_type(_0, tp_type);
-//                         __curr_class = new_type_object(frame->_module, _name, PK_OBJ_GET(Type, _0), true);
-//                     }
-//                         DISPATCH()
-//                     case OP_END_CLASS: {
-//                         assert(__curr_class != nullptr);
-//                         StrName _name(byte.arg);
-//                         frame->_module->attr().set(_name, __curr_class);
-//                         // call on_end_subclass
-//                         PyTypeInfo* ti = &_all_types[__curr_class->as<Type>()];
-//                         if(ti->base != tp_object) {
-//                             PyTypeInfo* base_ti = &_all_types[ti->base];
-//                             if(base_ti->on_end_subclass) base_ti->on_end_subclass(this, ti);
-//                         }
-//                         __curr_class = nullptr;
-//                     }
-//                         DISPATCH()
-//                     case OP_STORE_CLASS_ATTR: {
-//                         assert(__curr_class != nullptr);
-//                         StrName _name(byte.arg);
-//                         PyVar _0 = POPX();
-//                         if(is_type(_0, tp_function)) { PK_OBJ_GET(Function, _0)._class = __curr_class; }
-//                         __curr_class->attr().set(_name, _0);
-//                     }
-//                         DISPATCH()
-//                     case OP_BEGIN_CLASS_DECORATION: {
-//                         PUSH(__curr_class);
-//                     }
-//                         DISPATCH()
-//                     case OP_END_CLASS_DECORATION: {
-//                         __curr_class = POPX().get();
-//                     }
-//                         DISPATCH()
-//                     case OP_ADD_CLASS_ANNOTATION: {
-//                         assert(__curr_class != nullptr);
-//                         StrName _name(byte.arg);
-//                         Type type = __curr_class->as<Type>();
-//                         _all_types[type].annotated_fields.push_back(_name);
-//                     }
-//                         DISPATCH()
-//                     /*****************************************/
-//                     case OP_WITH_ENTER: PUSH(call_method(TOP(), __enter__)); DISPATCH()
-//                     case OP_WITH_EXIT:
-//                         call_method(TOP(), __exit__);
-//                         POP();
-//                         DISPATCH()
-//                     /*****************************************/
-//                     case OP_TRY_ENTER: {
-//                         frame->set_unwind_target(s_data.sp);
-//                         DISPATCH()
-//                     }
-//                     case OP_EXCEPTION_MATCH: {
-//                         PyVar assumed_type = POPX();
-//                         check_type(assumed_type, tp_type);
-//                         PyVar e_obj = TOP();
-//                         bool ok = isinstance(e_obj, PK_OBJ_GET(Type, assumed_type));
-//                         PUSH(VAR(ok));
-//                     }
-//                         DISPATCH()
-//                     case OP_RAISE: {
-//                         if(is_type(TOP(), tp_type)) { TOP() = call(TOP()); }
-//                         if(!isinstance(TOP(), tp_exception)) { TypeError("exceptions must derive from Exception"); }
-//                         _error(POPX());
-//                     }
-//                         DISPATCH()
-//                     case OP_RAISE_ASSERT:
-//                         if(byte.arg) {
-//                             Str msg = py_str(TOP());
-//                             POP();
-//                             AssertionError(msg);
-//                         } else {
-//                             AssertionError();
-//                         }
-//                         DISPATCH()
-//                     case OP_RE_RAISE: __raise_exc(true); DISPATCH()
-//                     case OP_POP_EXCEPTION: __last_exception = POPX().get(); DISPATCH()
-//                     /*****************************************/
-//                     case OP_FORMAT_STRING: {
-//                         PyVar _0 = POPX();
-//                         const Str& spec = CAST(Str&, c11__getitem(PyVar, &frame->co->consts, byte.arg));
-//                         PUSH(__format_object(_0, spec));
-//                     }
-//                         DISPATCH()
-//                     /*****************************************/
-//                     default: PK_UNREACHABLE()
-//                 }
-//             }
-//             /**********************************************************************/
-//             PK_UNREACHABLE()
-//         } catch(InternalException internal) {
-//             __internal_exception = internal;
-//             if(internal.type == InternalExceptionType::Unhandled) {
-//                 __last_exception = POPX().get();
-//                 Exception& _e = __last_exception->as<Exception>();
-//                 bool is_base_frame_to_be_popped = frame == base_frame;
-//                 __pop_frame();
-//                 if(callstack.empty()) {
-//                     // propagate to the top level
-//                     throw TopLevelException(this, &_e);
-//                 }
-//                 frame = &callstack.top();
-//                 PUSH(__last_exception);
-//                 if(is_base_frame_to_be_popped) { throw InternalException(InternalExceptionType::ToBeRaised); }
-//             }
-//         }
-//     }
-// }
-
-// #undef TOP
-// #undef SECOND
-// #undef THIRD
-// #undef STACK_SHRINK
-// #undef PUSH
-// #undef POP
-// #undef POPX
-// #undef STACK_VIEW
-
-// #undef DISPATCH
-// #undef DISPATCH_JUMP
-// #undef CEVAL_STEP_CALLBACK
-
-// }  // namespace pkpy
+#include "pocketpy/interpreter/vm.h"
+#include "pocketpy/common/memorypool.h"
+#include "pocketpy/common/sstream.h"
+#include "pocketpy/objects/codeobject.h"
+#include "pocketpy/pocketpy.h"
+
+#define DISPATCH()                                                                                 \
+    do {                                                                                           \
+        frame->ip++;                                                                               \
+        goto __NEXT_STEP;                                                                          \
+    } while(0)
+#define DISPATCH_JUMP(__offset)                                                                    \
+    do {                                                                                           \
+        frame->ip += __offset;                                                                     \
+        goto __NEXT_STEP;                                                                          \
+    } while(0)
+#define DISPATCH_JUMP_ABSOLUTE(__target)                                                           \
+    do {                                                                                           \
+        frame->ip = c11__at(Bytecode, &frame->co->codes, __target);                                \
+        goto __NEXT_STEP;                                                                          \
+    } while(0)
+
+/* Stack manipulation macros */
+// https://github.com/python/cpython/blob/3.9/Python/ceval.c#L1123
+#define TOP() self->stack.sp[-1]
+#define SECOND() self->stack.sp[-2]
+#define THIRD() self->stack.sp[-3]
+#define STACK_SHRINK(n) (self->stack.sp -= n)
+#define PUSH(v) (*self->stack.sp++ = v)
+#define POP() (--self->stack.sp)
+#define POPX() (*--self->stack.sp)
+#define SP() (self->stack.sp)
+
+pk_FrameResult pk_VM__run_top_frame(pk_VM* self) {
+    Frame* frame = self->top_frame;
+    const Frame* base_frame = frame;
+
+    while(true) {
+        Bytecode byte;
+    __NEXT_FRAME:
+        // if(__internal_exception.type == InternalExceptionType::Null) {
+        //     // None
+        //     frame->_ip++;
+        // } else if(__internal_exception.type == InternalExceptionType::Handled) {
+        //     // HandledException + continue
+        //     frame->_ip = c11__at(Bytecode, &frame->co->codes, __internal_exception.arg);
+        //     __internal_exception = {};
+        // } else {
+        //     // UnhandledException + continue (need_raise = true)
+        //     // ToBeRaisedException + continue (need_raise = true)
+        //     __internal_exception = {};
+        //     __raise_exc();  // no return
+        // }
+
+        frame->ip++;
+
+    __NEXT_STEP:
+        byte = *frame->ip;
+
+        switch((Opcode)byte.op) {
+            case OP_NO_OP: DISPATCH();
+            /*****************************************/
+            case OP_POP_TOP: POP(); DISPATCH();
+            case OP_DUP_TOP: PUSH(TOP()); DISPATCH();
+            case OP_DUP_TOP_TWO:
+                // [a, b]
+                PUSH(SECOND());  // [a, b, a]
+                PUSH(SECOND());  // [a, b, a, b]
+                DISPATCH();
+            case OP_ROT_TWO: {
+                py_TValue tmp = TOP();
+                TOP() = SECOND();
+                SECOND() = tmp;
+                DISPATCH();
+            }
+            case OP_ROT_THREE: {
+                // [a, b, c] -> [c, a, b]
+                py_TValue _0 = TOP();
+                TOP() = SECOND();
+                SECOND() = THIRD();
+                THIRD() = _0;
+                DISPATCH();
+            }
+            case OP_PRINT_EXPR:
+                if(TOP().type != tp_none_type) {
+                    py_Str out;
+                    int err = py_repr(&TOP(), &out);
+                    if(err) goto __ERROR;
+                    self->_stdout("%s\n", py_Str__data(&out));
+                    py_Str__dtor(&out);
+                }
+                POP();
+                DISPATCH();
+            /*****************************************/
+            case OP_LOAD_CONST: PUSH(c11__getitem(py_TValue, &frame->co->consts, byte.arg)); DISPATCH();
+            case OP_LOAD_NONE: PUSH(self->None); DISPATCH();
+            case OP_LOAD_TRUE: PUSH(self->True); DISPATCH();
+            case OP_LOAD_FALSE: PUSH(self->False); DISPATCH();
+            /*****************************************/
+            case OP_LOAD_SMALL_INT:
+                py_newint(SP(), (int64_t)(int16_t)byte.arg);
+                SP()++;
+                DISPATCH();
+            /*****************************************/
+            case OP_LOAD_ELLIPSIS: PUSH(self->Ellipsis); DISPATCH();
+            case OP_LOAD_FUNCTION: {
+                // FuncDecl_ decl = c11__getitem(FuncDecl_, &frame->co->func_decls, byte.arg);
+                // py_TValue obj;
+                // if(decl->nested) {
+                //     NameDict* captured = frame->_locals.to_namedict();
+                //     obj =
+                //         new_object<Function>(tp_function, decl, frame->_module, nullptr,
+                //         captured);
+                //     uint16_t name = pk_StrName__map2(py_Str__sv(&decl->code->name));
+                //     captured->set(name, obj);
+                // } else {
+                //     obj = new_object<Function>(tp_function, decl, frame->_module, nullptr,
+                //     nullptr);
+                // }
+                // PUSH(obj);DISPATCH();
+            }
+            case OP_LOAD_NULL:
+                py_newnull(SP());
+                SP()++;
+                DISPATCH();
+            /*****************************************/
+            default: PK_UNREACHABLE();
+        }
+
+        assert(false);  // should never reach here
+
+    __ERROR:
+        // 1. Exception can be handled inside the current frame
+        // 2. Exception need to be propagated to the upper frame
+        assert(false);
+        return RES_ERROR;
+    }
+
+    return RES_RETURN;
+}

+ 1151 - 0
src/interpreter/ceval.cpp

@@ -0,0 +1,1151 @@
+#include "pocketpy/interpreter/ceval.h"
+#include "pocketpy/objects/base.h"
+
+namespace pkpy {
+
+#define PREDICT_INT_OP(op)                                                                                             \
+    if(is_int(_0) && is_int(_1)) {                                                                                     \
+        TOP() = VAR(_0._i64 op _1._i64);                                                                     \
+        DISPATCH()                                                                                                     \
+    }
+
+#define PREDICT_INT_DIV_OP(op)                                                                                         \
+    if(is_int(_0) && is_int(_1)) {                                                                                     \
+        i64 divisor = _1._i64;                                                                                    \
+        if(divisor == 0) ZeroDivisionError();                                                                          \
+        TOP() = VAR(_0._i64 op divisor);                                                                          \
+        DISPATCH()                                                                                                     \
+    }
+
+#define BINARY_F_COMPARE(func, op, rfunc)                                                                              \
+    PyVar ret;                                                                                                         \
+    const PyTypeInfo* _ti = _tp_info(_0);                                                                              \
+    if(_ti->m##func) {                                                                                                 \
+        ret = _ti->m##func(this, _0, _1);                                                                              \
+    } else {                                                                                                           \
+        PyVar self;                                                                                                    \
+        PyVar _2 = get_unbound_method(_0, func, &self, false);                                                         \
+        if(_2.type)                                                                                              \
+            ret = call_method(self, _2, _1);                                                                           \
+        else                                                                                                           \
+            ret = NotImplemented;                                                                                      \
+    }                                                                                                                  \
+    if(is_not_implemented(ret)) {                                                                                      \
+        PyVar self;                                                                                                    \
+        PyVar _2 = get_unbound_method(_1, rfunc, &self, false);                                                        \
+        if(_2.type)                                                                                              \
+            ret = call_method(self, _2, _0);                                                                           \
+        else                                                                                                           \
+            BinaryOptError(op, _0, _1);                                                                                \
+        if(is_not_implemented(ret)) BinaryOptError(op, _0, _1);                                                        \
+    }
+
+void VM::__op_unpack_sequence(uint16_t arg) {
+    PyVar _0 = POPX();
+    if(is_type(_0, VM::tp_tuple)) {
+        // fast path for tuple
+        Tuple& tuple = PK_OBJ_GET(Tuple, _0);
+        if(tuple.size() == arg) {
+            for(PyVar obj: tuple)
+                PUSH(obj);
+        } else {
+            ValueError(_S("expected ", (int)arg, " values to unpack, got ", (int)tuple.size()));
+        }
+    } else {
+        auto _lock = gc_scope_lock();  // lock the gc via RAII!!
+        _0 = py_iter(_0);
+        const PyTypeInfo* ti = _tp_info(_0);
+        for(int i = 0; i < arg; i++) {
+            PyVar _1 = _py_next(ti, _0);
+            if(_1 == StopIteration) ValueError("not enough values to unpack");
+            PUSH(_1);
+        }
+        if(_py_next(ti, _0) != StopIteration) ValueError("too many values to unpack");
+    }
+}
+
+bool VM::py_lt(PyVar _0, PyVar _1) {
+    BINARY_F_COMPARE(__lt__, "<", __gt__);
+    assert(ret.type == tp_bool);
+    return ret.extra;
+}
+
+bool VM::py_le(PyVar _0, PyVar _1) {
+    BINARY_F_COMPARE(__le__, "<=", __ge__);
+    assert(ret.type == tp_bool);
+    return ret.extra;
+}
+
+bool VM::py_gt(PyVar _0, PyVar _1) {
+    BINARY_F_COMPARE(__gt__, ">", __lt__);
+    assert(ret.type == tp_bool);
+    return ret.extra;
+}
+
+bool VM::py_ge(PyVar _0, PyVar _1) {
+    BINARY_F_COMPARE(__ge__, ">=", __le__);
+    assert(ret.type == tp_bool);
+    return ret.extra;
+}
+
+#undef BINARY_F_COMPARE
+
+#if PK_ENABLE_PROFILER
+#define CEVAL_STEP_CALLBACK()                                                                                          \
+    if(_ceval_on_step) _ceval_on_step(this, frame, byte);                                                              \
+    if(_profiler) _profiler->_step(callstack.size(), frame);                                                           \
+    if(!_next_breakpoint.empty()) { _next_breakpoint._step(this); }
+#else
+#define CEVAL_STEP_CALLBACK()                                                                                          \
+    if(_ceval_on_step && _ceval_on_step) {                                                                             \
+        if(_ceval_on_step)                                                                                             \
+            if(_ceval_on_step) _ceval_on_step(this, frame, byte);                                                      \
+    }
+#endif
+
+#define DISPATCH()                                                                                                     \
+    {                                                                                                                  \
+        frame->_ip++;                                                                                                  \
+        goto __NEXT_STEP;                                                                                              \
+    }
+#define DISPATCH_JUMP(__offset)                                                                                        \
+    {                                                                                                                  \
+        frame->_ip += __offset;                                                                                        \
+        goto __NEXT_STEP;                                                                                              \
+    }
+#define DISPATCH_JUMP_ABSOLUTE(__target)                                                                               \
+    {                                                                                                                  \
+        frame->_ip = c11__at(Bytecode, &frame->co->codes, __target);                                                   \
+        goto __NEXT_STEP;                                                                                              \
+    }
+
+PyVar VM::__run_top_frame() {
+    Frame* frame = &callstack.top();
+    const Frame* base_frame = frame;
+    InternalException __internal_exception;
+
+    while(true) {
+        try {
+            /**********************************************************************/
+            {
+            __NEXT_FRAME:
+                Bytecode byte;
+
+                if(__internal_exception.type == InternalExceptionType::Null) {
+                    // None
+                    frame->_ip++;
+                } else if(__internal_exception.type == InternalExceptionType::Handled) {
+                    // HandledException + continue
+                    frame->_ip = c11__at(Bytecode, &frame->co->codes, __internal_exception.arg);
+                    __internal_exception = {};
+                } else {
+                    // UnhandledException + continue (need_raise = true)
+                    // ToBeRaisedException + continue (need_raise = true)
+                    __internal_exception = {};
+                    __raise_exc();  // no return
+                }
+
+            __NEXT_STEP:
+                byte = *frame->_ip;
+                CEVAL_STEP_CALLBACK()
+
+#if PK_DEBUG_CEVAL_STEP
+                __log_s_data();
+#endif
+                switch((Opcode)byte.op) {
+                    case OP_NO_OP: DISPATCH()
+                    /*****************************************/
+                    case OP_POP_TOP: POP(); DISPATCH()
+                    case OP_DUP_TOP: PUSH(TOP()); DISPATCH()
+                    case OP_DUP_TOP_TWO:
+                        // [a, b]
+                        PUSH(SECOND());  // [a, b, a]
+                        PUSH(SECOND());  // [a, b, a, b]
+                        DISPATCH()
+                    case OP_ROT_TWO: std::swap(TOP(), SECOND()); DISPATCH()
+                    case OP_ROT_THREE: {
+                        // [a, b, c] -> [c, a, b]
+                        PyVar _0 = TOP();
+                        TOP() = SECOND();
+                        SECOND() = THIRD();
+                        THIRD() = _0;
+                    }
+                        DISPATCH()
+                    case OP_PRINT_EXPR:
+                        if(!is_none(TOP())) stdout_write(py_repr(TOP()) + "\n");
+                        POP();
+                        DISPATCH()
+                    /*****************************************/
+                    case OP_LOAD_CONST:
+                        PUSH(c11__getitem(PyVar, &frame->co->consts, byte.arg));
+                        DISPATCH()
+                    case OP_LOAD_NONE: PUSH(None); DISPATCH()
+                    case OP_LOAD_TRUE: PUSH(True); DISPATCH()
+                    case OP_LOAD_FALSE: PUSH(False); DISPATCH()
+                    /*****************************************/
+                    case OP_LOAD_SMALL_INT: s_data.emplace(tp_int, (i64)(int16_t)byte.arg); DISPATCH()
+                    /*****************************************/
+                    case OP_LOAD_ELLIPSIS: PUSH(Ellipsis); DISPATCH()
+                    case OP_LOAD_FUNCTION: {
+                        FuncDecl_ decl = c11__getitem(FuncDecl_, &frame->co->func_decls, byte.arg);
+                        PyVar obj;
+                        if(decl->nested) {
+                            NameDict* captured = frame->_locals.to_namedict();
+                            obj = new_object<Function>(tp_function, decl, frame->_module, nullptr, captured);
+                            uint16_t name = pk_StrName__map2(py_Str__sv(&decl->code->name));
+                            captured->set(name, obj);
+                        } else {
+                            obj = new_object<Function>(tp_function, decl, frame->_module, nullptr, nullptr);
+                        }
+                        PUSH(obj);
+                    }
+                        DISPATCH()
+                    case OP_LOAD_NULL: PUSH(PY_NULL); DISPATCH()
+                    /*****************************************/
+                    case OP_LOAD_FAST: {
+                        PyVar _0 = frame->_locals[byte.arg];
+                        if(_0 == PY_NULL) vm->UnboundLocalError(c11__getitem(uint16_t, &frame->co->varnames, byte.arg));
+                        PUSH(_0);
+                    }
+                        DISPATCH()
+                    case OP_LOAD_NAME: {
+                        StrName _name(byte.arg);
+                        PyVar* slot = frame->_locals.try_get_name(_name);
+                        if(slot != nullptr) {
+                            if(*slot == PY_NULL) vm->UnboundLocalError(_name);
+                            PUSH(*slot);
+                            DISPATCH()
+                        }
+                        PyVar* _0 = frame->f_closure_try_get(_name);
+                        if(_0 != nullptr) {
+                            PUSH(*_0);
+                            DISPATCH()
+                        }
+                        _0 = frame->f_globals().try_get_2_likely_found(_name);
+                        if(_0 != nullptr) {
+                            PUSH(*_0);
+                            DISPATCH()
+                        }
+                        _0 = vm->builtins->attr().try_get_2_likely_found(_name);
+                        if(_0 != nullptr) {
+                            PUSH(*_0);
+                            DISPATCH()
+                        }
+                        vm->NameError(_name);
+                    }
+                        DISPATCH()
+                    case OP_LOAD_NONLOCAL: {
+                        StrName _name(byte.arg);
+                        PyVar* _0 = frame->f_closure_try_get(_name);
+                        if(_0 != nullptr) {
+                            PUSH(*_0);
+                            DISPATCH()
+                        }
+                        _0 = frame->f_globals().try_get_2_likely_found(_name);
+                        if(_0 != nullptr) {
+                            PUSH(*_0);
+                            DISPATCH()
+                        }
+                        _0 = vm->builtins->attr().try_get_2_likely_found(_name);
+                        if(_0 != nullptr) {
+                            PUSH(*_0);
+                            DISPATCH()
+                        }
+                        vm->NameError(_name);
+                    }
+                        DISPATCH()
+                    case OP_LOAD_GLOBAL: {
+                        StrName _name(byte.arg);
+                        PyVar _0 = frame->f_globals().try_get_likely_found(_name);
+                        if(_0 != nullptr) {
+                            PUSH(_0);
+                            DISPATCH()
+                        }
+                        _0 = vm->builtins->attr().try_get_likely_found(_name);
+                        if(_0 != nullptr) {
+                            PUSH(_0);
+                            DISPATCH()
+                        }
+                        vm->NameError(_name);
+                    }
+                        DISPATCH()
+                    case OP_LOAD_ATTR: {
+                        TOP() = getattr(TOP(), StrName(byte.arg));
+                    }
+                        DISPATCH()
+                    case OP_LOAD_CLASS_GLOBAL: {
+                        assert(__curr_class != nullptr);
+                        StrName _name(byte.arg);
+                        PyVar _0 = getattr(__curr_class, _name, false);
+                        if(_0 != nullptr) {
+                            PUSH(_0);
+                            DISPATCH()
+                        }
+                        // load global if attribute not found
+                        _0 = frame->f_globals().try_get_likely_found(_name);
+                        if(_0 != nullptr) {
+                            PUSH(_0);
+                            DISPATCH()
+                        }
+                        _0 = vm->builtins->attr().try_get_likely_found(_name);
+                        if(_0 != nullptr) {
+                            PUSH(_0);
+                            DISPATCH()
+                        }
+                        vm->NameError(_name);
+                    }
+                        DISPATCH()
+                    case OP_LOAD_METHOD: {
+                        PyVar _0;
+                        TOP() = get_unbound_method(TOP(), StrName(byte.arg), &_0, true, true);
+                        PUSH(_0);
+                    }
+                        DISPATCH()
+                    case OP_LOAD_SUBSCR: {
+                        PyVar _1 = POPX();  // b
+                        PyVar _0 = TOP();   // a
+                        auto _ti = _tp_info(_0);
+                        if(_ti->m__getitem__) {
+                            TOP() = _ti->m__getitem__(this, _0, _1);
+                        } else {
+                            TOP() = call_method(_0, __getitem__, _1);
+                        }
+                    }
+                        DISPATCH()
+                    case OP_LOAD_SUBSCR_FAST: {
+                        PyVar _1 = frame->_locals[byte.arg];
+                        if(_1 == PY_NULL) vm->UnboundLocalError(c11__getitem(uint16_t, &frame->co->varnames, byte.arg));
+                        PyVar _0 = TOP();  // a
+                        auto _ti = _tp_info(_0);
+                        if(_ti->m__getitem__) {
+                            TOP() = _ti->m__getitem__(this, _0, _1);
+                        } else {
+                            TOP() = call_method(_0, __getitem__, _1);
+                        }
+                    }
+                        DISPATCH()
+                    case OP_LOAD_SUBSCR_SMALL_INT: {
+                        PyVar _1 = VAR((int16_t)byte.arg);
+                        PyVar _0 = TOP();  // a
+                        auto _ti = _tp_info(_0);
+                        if(_ti->m__getitem__) {
+                            TOP() = _ti->m__getitem__(this, _0, _1);
+                        } else {
+                            TOP() = call_method(_0, __getitem__, _1);
+                        }
+                    }
+                        DISPATCH()
+                    case OP_STORE_FAST: frame->_locals[byte.arg] = POPX(); DISPATCH()
+                    case OP_STORE_NAME: {
+                        StrName _name(byte.arg);
+                        PyVar _0 = POPX();
+                        if(frame->_callable != nullptr) {
+                            PyVar* slot = frame->_locals.try_get_name(_name);
+                            if(slot != nullptr) {
+                                *slot = _0;  // store in locals if possible
+                            } else {
+                                Function& func = frame->_callable->as<Function>();
+                                if(func.decl == __dynamic_func_decl) {
+                                    assert(func._closure != nullptr);
+                                    func._closure->set(_name, _0);
+                                } else {
+                                    vm->NameError(_name);
+                                }
+                            }
+                        } else {
+                            frame->f_globals().set(_name, _0);
+                        }
+                    }
+                        DISPATCH()
+                    case OP_STORE_GLOBAL: frame->f_globals().set(StrName(byte.arg), POPX()); DISPATCH()
+                    case OP_STORE_ATTR: {
+                        PyVar _0 = TOP();     // a
+                        PyVar _1 = SECOND();  // val
+                        setattr(_0, StrName(byte.arg), _1);
+                        STACK_SHRINK(2);
+                    }
+                        DISPATCH()
+                    case OP_STORE_SUBSCR: {
+                        PyVar _2 = POPX();  // b
+                        PyVar _1 = POPX();  // a
+                        PyVar _0 = POPX();  // val
+                        auto _ti = _tp_info(_1);
+                        if(_ti->m__setitem__) {
+                            _ti->m__setitem__(this, _1, _2, _0);
+                        } else {
+                            call_method(_1, __setitem__, _2, _0);
+                        }
+                    }
+                        DISPATCH()
+                    case OP_STORE_SUBSCR_FAST: {
+                        PyVar _2 = frame->_locals[byte.arg];  // b
+                        if(_2 == PY_NULL) vm->UnboundLocalError(c11__getitem(uint16_t, &frame->co->varnames, byte.arg));
+                        PyVar _1 = POPX();  // a
+                        PyVar _0 = POPX();  // val
+                        auto _ti = _tp_info(_1);
+                        if(_ti->m__setitem__) {
+                            _ti->m__setitem__(this, _1, _2, _0);
+                        } else {
+                            call_method(_1, __setitem__, _2, _0);
+                        }
+                    }
+                        DISPATCH()
+                    case OP_DELETE_FAST: {
+                        PyVar _0 = frame->_locals[byte.arg];
+                        if(_0 == PY_NULL) vm->UnboundLocalError(c11__getitem(uint16_t, &frame->co->varnames, byte.arg));
+                        frame->_locals[byte.arg].set_null();
+                    }
+                        DISPATCH()
+                    case OP_DELETE_NAME: {
+                        StrName _name(byte.arg);
+                        if(frame->_callable != nullptr) {
+                            PyVar* slot = frame->_locals.try_get_name(_name);
+                            if(slot != nullptr) {
+                                slot->set_null();
+                            } else {
+                                Function& func = frame->_callable->as<Function>();
+                                if(func.decl == __dynamic_func_decl) {
+                                    assert(func._closure != nullptr);
+                                    bool ok = func._closure->del(_name);
+                                    if(!ok) vm->NameError(_name);
+                                } else {
+                                    vm->NameError(_name);
+                                }
+                            }
+                        } else {
+                            if(!frame->f_globals().del(_name)) vm->NameError(_name);
+                        }
+                    }
+                        DISPATCH()
+                    case OP_DELETE_GLOBAL: {
+                        StrName _name(byte.arg);
+                        if(!frame->f_globals().del(_name)) vm->NameError(_name);
+                    }
+                        DISPATCH()
+                    case OP_DELETE_ATTR: {
+                        PyVar _0 = POPX();
+                        delattr(_0, StrName(byte.arg));
+                    }
+                        DISPATCH()
+                    case OP_DELETE_SUBSCR: {
+                        PyVar _1 = POPX();
+                        PyVar _0 = POPX();
+                        auto _ti = _tp_info(_0);
+                        if(_ti->m__delitem__) {
+                            _ti->m__delitem__(this, _0, _1);
+                        } else {
+                            call_method(_0, __delitem__, _1);
+                        }
+                    }
+                        DISPATCH()
+                    /*****************************************/
+                    case OP_BUILD_LONG: {
+                        PyVar _0 = builtins->attr().try_get_likely_found(pk_id_long);
+                        if(_0 == nullptr) AttributeError(builtins, pk_id_long);
+                        TOP() = call(_0, TOP());
+                    }
+                        DISPATCH()
+                    case OP_BUILD_IMAG: {
+                        PyVar _0 = builtins->attr().try_get_likely_found(pk_id_complex);
+                        if(_0 == nullptr) AttributeError(builtins, pk_id_long);
+                        TOP() = call(_0, VAR(0), TOP());
+                    }
+                        DISPATCH()
+                    case OP_BUILD_BYTES: {
+                        const Str& s = CAST(Str&, TOP());
+                        unsigned char* p = (unsigned char*)std::malloc(s.size);
+                        std::memcpy(p, s.c_str(), s.size);
+                        TOP() = VAR(Bytes(p, s.size));
+                    }
+                        DISPATCH()
+                    case OP_BUILD_TUPLE: {
+                        PyVar _0 = VAR(STACK_VIEW(byte.arg).to_tuple());
+                        STACK_SHRINK(byte.arg);
+                        PUSH(_0);
+                    }
+                        DISPATCH()
+                    case OP_BUILD_LIST: {
+                        PyVar _0 = VAR(STACK_VIEW(byte.arg).to_list());
+                        STACK_SHRINK(byte.arg);
+                        PUSH(_0);
+                    }
+                        DISPATCH()
+                    case OP_BUILD_DICT: {
+                        if(byte.arg == 0) {
+                            PUSH(VAR(Dict()));
+                            DISPATCH()
+                        }
+                        PyVar _0 = VAR(STACK_VIEW(byte.arg).to_list());
+                        _0 = call(_t(tp_dict), _0);
+                        STACK_SHRINK(byte.arg);
+                        PUSH(_0);
+                    }
+                        DISPATCH()
+                    case OP_BUILD_SET: {
+                        PyVar _0 = VAR(STACK_VIEW(byte.arg).to_list());
+                        _0 = call(builtins->attr()[pk_id_set], _0);
+                        STACK_SHRINK(byte.arg);
+                        PUSH(_0);
+                    }
+                        DISPATCH()
+                    case OP_BUILD_SLICE: {
+                        PyVar _2 = POPX();  // step
+                        PyVar _1 = POPX();  // stop
+                        PyVar _0 = POPX();  // start
+                        PUSH(VAR(Slice(_0, _1, _2)));
+                    }
+                        DISPATCH()
+                    case OP_BUILD_STRING: {
+                        SStream ss;
+                        ArgsView view = STACK_VIEW(byte.arg);
+                        for(PyVar obj: view)
+                            ss << py_str(obj);
+                        STACK_SHRINK(byte.arg);
+                        PUSH(VAR(ss.str()));
+                    }
+                        DISPATCH()
+                    /*****************************************/
+                    case OP_BUILD_TUPLE_UNPACK: {
+                        List list;
+                        __unpack_as_list(STACK_VIEW(byte.arg), list);
+                        STACK_SHRINK(byte.arg);
+                        PyVar _0 = VAR(list.to_tuple());
+                        PUSH(_0);
+                    }
+                        DISPATCH()
+                    case OP_BUILD_LIST_UNPACK: {
+                        List list;
+                        __unpack_as_list(STACK_VIEW(byte.arg), list);
+                        STACK_SHRINK(byte.arg);
+                        PyVar _0 = VAR(std::move(list));
+                        PUSH(_0);
+                    }
+                        DISPATCH()
+                    case OP_BUILD_DICT_UNPACK: {
+                        Dict dict;
+                        __unpack_as_dict(STACK_VIEW(byte.arg), dict);
+                        STACK_SHRINK(byte.arg);
+                        PyVar _0 = VAR(std::move(dict));
+                        PUSH(_0);
+                    }
+                        DISPATCH()
+                    case OP_BUILD_SET_UNPACK: {
+                        List list;
+                        __unpack_as_list(STACK_VIEW(byte.arg), list);
+                        STACK_SHRINK(byte.arg);
+                        PyVar _0 = VAR(std::move(list));
+                        _0 = call(builtins->attr()[pk_id_set], _0);
+                        PUSH(_0);
+                    }
+                        DISPATCH()
+                        /*****************************************/
+#define BINARY_OP_SPECIAL(func)                                                                                        \
+    _ti = _tp_info(_0);                                                                                                \
+    if(_ti->m##func) {                                                                                                 \
+        TOP() = _ti->m##func(this, _0, _1);                                                                            \
+    } else {                                                                                                           \
+        PyVar self;                                                                                                    \
+        PyVar _2 = get_unbound_method(_0, func, &self, false);                                                         \
+        if(_2 != nullptr)                                                                                              \
+            TOP() = call_method(self, _2, _1);                                                                         \
+        else                                                                                                           \
+            TOP() = NotImplemented;                                                                                    \
+    }
+
+#define BINARY_OP_RSPECIAL(op, func)                                                                                   \
+    if(is_not_implemented(TOP())) {                                                                                    \
+        PyVar self;                                                                                                    \
+        PyVar _2 = get_unbound_method(_1, func, &self, false);                                                         \
+        if(_2 != nullptr)                                                                                              \
+            TOP() = call_method(self, _2, _0);                                                                         \
+        else                                                                                                           \
+            BinaryOptError(op, _0, _1);                                                                                \
+        if(is_not_implemented(TOP())) BinaryOptError(op, _0, _1);                                                      \
+    }
+
+                    case OP_BINARY_TRUEDIV: {
+                        PyVar _1 = POPX();
+                        PyVar _0 = TOP();
+                        const PyTypeInfo* _ti;
+                        BINARY_OP_SPECIAL(__truediv__);
+                        if(is_not_implemented(TOP())) BinaryOptError("/", _0, _1);
+                    }
+                        DISPATCH()
+                    case OP_BINARY_POW: {
+                        PyVar _1 = POPX();
+                        PyVar _0 = TOP();
+                        const PyTypeInfo* _ti;
+                        BINARY_OP_SPECIAL(__pow__);
+                        if(is_not_implemented(TOP())) BinaryOptError("**", _0, _1);
+                    }
+                        DISPATCH()
+                    case OP_BINARY_ADD: {
+                        PyVar _1 = POPX();
+                        PyVar _0 = TOP();
+                        PREDICT_INT_OP(+)
+                        const PyTypeInfo* _ti;
+                        BINARY_OP_SPECIAL(__add__);
+                        BINARY_OP_RSPECIAL("+", __radd__);
+                    }
+                        DISPATCH()
+                    case OP_BINARY_SUB: {
+                        PyVar _1 = POPX();
+                        PyVar _0 = TOP();
+                        PREDICT_INT_OP(-)
+                        const PyTypeInfo* _ti;
+                        BINARY_OP_SPECIAL(__sub__);
+                        BINARY_OP_RSPECIAL("-", __rsub__);
+                    }
+                        DISPATCH()
+                    case OP_BINARY_MUL: {
+                        PyVar _1 = POPX();
+                        PyVar _0 = TOP();
+                        PREDICT_INT_OP(*)
+                        const PyTypeInfo* _ti;
+                        BINARY_OP_SPECIAL(__mul__);
+                        BINARY_OP_RSPECIAL("*", __rmul__);
+                    }
+                        DISPATCH()
+                    case OP_BINARY_FLOORDIV: {
+                        PyVar _1 = POPX();
+                        PyVar _0 = TOP();
+                        PREDICT_INT_DIV_OP(/)
+                        const PyTypeInfo* _ti;
+                        BINARY_OP_SPECIAL(__floordiv__);
+                        if(is_not_implemented(TOP())) BinaryOptError("//", _0, _1);
+                    }
+                        DISPATCH()
+                    case OP_BINARY_MOD: {
+                        PyVar _1 = POPX();
+                        PyVar _0 = TOP();
+                        PREDICT_INT_DIV_OP(%)
+                        const PyTypeInfo* _ti;
+                        BINARY_OP_SPECIAL(__mod__);
+                        if(is_not_implemented(TOP())) BinaryOptError("%", _0, _1);
+                    }
+                        DISPATCH()
+                    case OP_COMPARE_LT: {
+                        PyVar _1 = POPX();
+                        PyVar _0 = TOP();
+                        PREDICT_INT_OP(<)
+                        TOP() = VAR(py_lt(_0, _1));
+                    }
+                        DISPATCH()
+                    case OP_COMPARE_LE: {
+                        PyVar _1 = POPX();
+                        PyVar _0 = TOP();
+                        PREDICT_INT_OP(<=)
+                        TOP() = VAR(py_le(_0, _1));
+                    }
+                        DISPATCH()
+                    case OP_COMPARE_EQ: {
+                        PyVar _1 = POPX();
+                        PyVar _0 = TOP();
+                        TOP() = VAR(py_eq(_0, _1));
+                    }
+                        DISPATCH()
+                    case OP_COMPARE_NE: {
+                        PyVar _1 = POPX();
+                        PyVar _0 = TOP();
+                        TOP() = VAR(py_ne(_0, _1));
+                    }
+                        DISPATCH()
+                    case OP_COMPARE_GT: {
+                        PyVar _1 = POPX();
+                        PyVar _0 = TOP();
+                        PREDICT_INT_OP(>)
+                        TOP() = VAR(py_gt(_0, _1));
+                    }
+                        DISPATCH()
+                    case OP_COMPARE_GE: {
+                        PyVar _1 = POPX();
+                        PyVar _0 = TOP();
+                        PREDICT_INT_OP(>=)
+                        TOP() = VAR(py_ge(_0, _1));
+                    }
+                        DISPATCH()
+                    case OP_BITWISE_LSHIFT: {
+                        PyVar _1 = POPX();
+                        PyVar _0 = TOP();
+                        PREDICT_INT_OP(<<)
+                        const PyTypeInfo* _ti;
+                        BINARY_OP_SPECIAL(__lshift__);
+                        if(is_not_implemented(TOP())) BinaryOptError("<<", _0, _1);
+                    }
+                        DISPATCH()
+                    case OP_BITWISE_RSHIFT: {
+                        PyVar _1 = POPX();
+                        PyVar _0 = TOP();
+                        PREDICT_INT_OP(>>)
+                        const PyTypeInfo* _ti;
+                        BINARY_OP_SPECIAL(__rshift__);
+                        if(is_not_implemented(TOP())) BinaryOptError(">>", _0, _1);
+                    }
+                        DISPATCH()
+                    case OP_BITWISE_AND: {
+                        PyVar _1 = POPX();
+                        PyVar _0 = TOP();
+                        PREDICT_INT_OP(&)
+                        const PyTypeInfo* _ti;
+                        BINARY_OP_SPECIAL(__and__);
+                        if(is_not_implemented(TOP())) BinaryOptError("&", _0, _1);
+                    }
+                        DISPATCH()
+                    case OP_BITWISE_OR: {
+                        PyVar _1 = POPX();
+                        PyVar _0 = TOP();
+                        PREDICT_INT_OP(|)
+                        const PyTypeInfo* _ti;
+                        BINARY_OP_SPECIAL(__or__);
+                        if(is_not_implemented(TOP())) BinaryOptError("|", _0, _1);
+                    }
+                        DISPATCH()
+                    case OP_BITWISE_XOR: {
+                        PyVar _1 = POPX();
+                        PyVar _0 = TOP();
+                        PREDICT_INT_OP(^)
+                        const PyTypeInfo* _ti;
+                        BINARY_OP_SPECIAL(__xor__);
+                        if(is_not_implemented(TOP())) BinaryOptError("^", _0, _1);
+                    }
+                        DISPATCH()
+                    case OP_BINARY_MATMUL: {
+                        PyVar _1 = POPX();
+                        PyVar _0 = TOP();
+                        const PyTypeInfo* _ti;
+                        BINARY_OP_SPECIAL(__matmul__);
+                        if(is_not_implemented(TOP())) BinaryOptError("@", _0, _1);
+                    }
+                        DISPATCH()
+
+#undef BINARY_OP_SPECIAL
+#undef BINARY_OP_RSPECIAL
+#undef PREDICT_INT_OP
+
+                    case OP_IS_OP: {
+                        PyVar _1 = POPX();  // rhs
+                        PyVar _0 = TOP();   // lhs
+                        TOP() = PyVar__IS_OP(&_0, &_1) ? True : False;
+                    }
+                        DISPATCH()
+                    case OP_IS_NOT_OP: {
+                        PyVar _1 = POPX();  // rhs
+                        PyVar _0 = TOP();   // lhs
+                        TOP() = PyVar__IS_OP(&_0, &_1) ? False : True;
+                    }
+                        DISPATCH()
+                    case OP_CONTAINS_OP: {
+                        // a in b -> b __contains__ a
+                        auto _ti = _tp_info(TOP());
+                        PyVar _0;
+                        if(_ti->m__contains__) {
+                            _0 = _ti->m__contains__(this, TOP(), SECOND());
+                        } else {
+                            _0 = call_method(TOP(), __contains__, SECOND());
+                        }
+                        POP();
+                        TOP() = VAR(static_cast<bool>((int)CAST(bool, _0) ^ byte.arg));
+                    }
+                        DISPATCH()
+                    /*****************************************/
+                    case OP_JUMP_FORWARD: DISPATCH_JUMP((int16_t)byte.arg)
+                    case OP_POP_JUMP_IF_FALSE:
+                        if(!py_bool(POPX())) DISPATCH_JUMP((int16_t)byte.arg)
+                        DISPATCH()
+                    case OP_POP_JUMP_IF_TRUE:
+                        if(py_bool(POPX())) DISPATCH_JUMP((int16_t)byte.arg)
+                        DISPATCH()
+                    case OP_JUMP_IF_TRUE_OR_POP:
+                        if(py_bool(TOP())) {
+                            DISPATCH_JUMP((int16_t)byte.arg)
+                        } else {
+                            POP();
+                            DISPATCH()
+                        }
+                    case OP_JUMP_IF_FALSE_OR_POP:
+                        if(!py_bool(TOP())) {
+                            DISPATCH_JUMP((int16_t)byte.arg)
+                        } else {
+                            POP();
+                            DISPATCH()
+                        }
+                    case OP_SHORTCUT_IF_FALSE_OR_POP:
+                        if(!py_bool(TOP())) {  // [b, False]
+                            STACK_SHRINK(2);   // []
+                            PUSH(vm->False);   // [False]
+                            DISPATCH_JUMP((int16_t)byte.arg)
+                        } else {
+                            POP();  // [b]
+                            DISPATCH()
+                        }
+                    case OP_LOOP_CONTINUE:
+                        // just an alias of OP_JUMP_FORWARD
+                        DISPATCH_JUMP((int16_t)byte.arg)
+                    case OP_LOOP_BREAK: {
+                        frame->prepare_jump_break(&s_data, frame->ip() + byte.arg);
+                        DISPATCH_JUMP((int16_t)byte.arg)
+                    }
+                    case OP_JUMP_ABSOLUTE_TOP: DISPATCH_JUMP_ABSOLUTE(_CAST(int, POPX()))
+                    case OP_GOTO: {
+                        StrName _name(byte.arg);
+                        int target = c11_smallmap_n2i__get(&frame->co->labels, byte.arg, -1);
+                        if(target < 0) RuntimeError(_S("label ", _name.escape(), " not found"));
+                        frame->prepare_jump_break(&s_data, target);
+                        DISPATCH_JUMP_ABSOLUTE(target)
+                    }
+                    /*****************************************/
+                    case OP_FSTRING_EVAL: {
+                        PyVar _0 = c11__getitem(PyVar, &frame->co->consts, byte.arg);
+                        std::string_view string = CAST(Str&, _0).sv();
+                        // TODO: optimize this
+                        CodeObject* code = vm->compile(string, "<eval>", EVAL_MODE, true);
+                        _0 = vm->_exec(code, frame->_module, frame->_callable, frame->_locals);
+                        CodeObject__delete(code);
+                        PUSH(_0);
+                    }
+                        DISPATCH()
+                    case OP_REPR: TOP() = VAR(py_repr(TOP())); DISPATCH()
+                    case OP_CALL: {
+                        pk_ManagedHeap__collect_if_needed(&heap);
+                        PyVar _0 = vectorcall(byte.arg & 0xFF,         // ARGC
+                                              (byte.arg >> 8) & 0xFF,  // KWARGC
+                                              true);
+                        if(_0.type == tp_op_call) {
+                            frame = &callstack.top();
+                            goto __NEXT_FRAME;
+                        }
+                        PUSH(_0);
+                    }
+                        DISPATCH()
+                    case OP_CALL_TP: {
+                        pk_ManagedHeap__collect_if_needed(&heap);
+                        PyVar _0;
+                        PyVar _1;
+                        PyVar _2;
+                        // [callable, <self>, args: tuple, kwargs: dict | NULL]
+                        if(byte.arg) {
+                            _2 = POPX();
+                            _1 = POPX();
+                            for(PyVar obj: _CAST(Tuple&, _1))
+                                PUSH(obj);
+                            _CAST(Dict&, _2).apply([this](PyVar k, PyVar v) {
+                                PUSH(VAR(StrName(CAST(Str&, k)).index));
+                                PUSH(v);
+                            });
+                            _0 = vectorcall(_CAST(Tuple&, _1).size(),  // ARGC
+                                            _CAST(Dict&, _2).size(),   // KWARGC
+                                            true);
+                        } else {
+                            // no **kwargs
+                            _1 = POPX();
+                            for(PyVar obj: _CAST(Tuple&, _1))
+                                PUSH(obj);
+                            _0 = vectorcall(_CAST(Tuple&, _1).size(),  // ARGC
+                                            0,                         // KWARGC
+                                            true);
+                        }
+                        if(_0.type == tp_op_call) {
+                            frame = &callstack.top();
+                            goto __NEXT_FRAME;
+                        }
+                        PUSH(_0);
+                    }
+                        DISPATCH()
+                    case OP_RETURN_VALUE: {
+                        PyVar _0 = byte.arg == BC_NOARG ? POPX() : None;
+                        __pop_frame();
+                        if(frame == base_frame) {  // [ frameBase<- ]
+                            return _0;
+                        } else {
+                            frame = &callstack.top();
+                            PUSH(_0);
+                            goto __NEXT_FRAME;
+                        }
+                    }
+                        DISPATCH()
+                    case OP_YIELD_VALUE: return PY_OP_YIELD;
+                    /*****************************************/
+                    case OP_LIST_APPEND: {
+                        PyVar _0 = POPX();
+                        PK_OBJ_GET(List, SECOND()).push_back(_0);
+                    }
+                        DISPATCH()
+                    case OP_DICT_ADD: {
+                        PyVar _0 = POPX();
+                        const Tuple& t = PK_OBJ_GET(Tuple, _0);
+                        PK_OBJ_GET(Dict, SECOND()).set(this, t[0], t[1]);
+                    }
+                        DISPATCH()
+                    case OP_SET_ADD: {
+                        PyVar _0 = POPX();
+                        call_method(SECOND(), pk_id_add, _0);
+                    }
+                        DISPATCH()
+                    /*****************************************/
+                    case OP_UNARY_NEGATIVE: TOP() = py_negate(TOP()); DISPATCH()
+                    case OP_UNARY_NOT: TOP() = VAR(!py_bool(TOP())); DISPATCH()  
+                    case OP_UNARY_STAR: TOP() = VAR(StarWrapper(byte.arg, TOP())); DISPATCH()
+                    case OP_UNARY_INVERT: {
+                        PyVar _0;
+                        auto _ti = _tp_info(TOP());
+                        if(_ti->m__invert__)
+                            _0 = _ti->m__invert__(this, TOP());
+                        else
+                            _0 = call_method(TOP(), __invert__);
+                        TOP() = _0;
+                    }
+                        DISPATCH()
+                    /*****************************************/
+                    case OP_GET_ITER: TOP() = py_iter(TOP()); DISPATCH()
+                    case OP_GET_ITER_NEW: TOP() = py_iter(TOP()); DISPATCH()
+                    case OP_FOR_ITER: {
+                        PyVar _0 = py_next(TOP());
+                        if(_0 == StopIteration) {
+                            int target = frame->prepare_loop_break(&s_data);
+                            DISPATCH_JUMP_ABSOLUTE(target)
+                        } else {
+                            PUSH(_0);
+                            DISPATCH()
+                        }
+                    }
+                    case OP_FOR_ITER_STORE_FAST: {
+                        PyVar _0 = py_next(TOP());
+                        if(_0 == StopIteration) {
+                            int target = frame->prepare_loop_break(&s_data);
+                            DISPATCH_JUMP_ABSOLUTE(target)
+                        } else {
+                            frame->_locals[byte.arg] = _0;
+                            DISPATCH()
+                        }
+                    }
+                    case OP_FOR_ITER_STORE_GLOBAL: {
+                        PyVar _0 = py_next(TOP());
+                        if(_0 == StopIteration) {
+                            int target = frame->prepare_loop_break(&s_data);
+                            DISPATCH_JUMP_ABSOLUTE(target)
+                        } else {
+                            frame->f_globals().set(StrName(byte.arg), _0);
+                            DISPATCH()
+                        }
+                    }
+                    case OP_FOR_ITER_YIELD_VALUE: {
+                        PyVar _0 = py_next(TOP());
+                        if(_0 == StopIteration) {
+                            int target = frame->prepare_loop_break(&s_data);
+                            DISPATCH_JUMP_ABSOLUTE(target)
+                        } else {
+                            PUSH(_0);
+                            return PY_OP_YIELD;
+                        }
+                    }
+                    case OP_FOR_ITER_UNPACK: {
+                        PyVar _0 = TOP();
+                        const PyTypeInfo* _ti = _tp_info(_0);
+                        if(_ti->op__next__) {
+                            unsigned n = _ti->op__next__(this, _0);
+                            if(n == 0) {
+                                // StopIteration
+                                int target = frame->prepare_loop_break(&s_data);
+                                DISPATCH_JUMP_ABSOLUTE(target)
+                            } else if(n == 1) {
+                                // UNPACK_SEQUENCE
+                                __op_unpack_sequence(byte.arg);
+                            } else {
+                                if(n != byte.arg) {
+                                    ValueError(_S("expected ", (int)byte.arg, " values to unpack, got ", (int)n));
+                                }
+                            }
+                        } else {
+                            // FOR_ITER
+                            _0 = call_method(_0, __next__);
+                            if(_0 != StopIteration) {
+                                PUSH(_0);
+                                // UNPACK_SEQUENCE
+                                __op_unpack_sequence(byte.arg);
+                            } else {
+                                int target = frame->prepare_loop_break(&s_data);
+                                DISPATCH_JUMP_ABSOLUTE(target)
+                            }
+                        }
+                    }
+                        DISPATCH()
+                    /*****************************************/
+                    case OP_IMPORT_PATH: {
+                        PyVar _0 = c11__getitem(PyVar, &frame->co->consts, byte.arg);
+                        PUSH(py_import(CAST(Str&, _0)));
+                    }
+                        DISPATCH()
+                    case OP_POP_IMPORT_STAR: {
+                        PyVar _0 = POPX();  // pop the module
+                        PyVar _1 = _0->attr().try_get(__all__);
+                        StrName _name;
+                        if(_1 != nullptr) {
+                            for(PyVar key: CAST(List&, _1)) {
+                                _name = StrName::get(CAST(Str&, key).sv());
+                                PyVar value = _0->attr().try_get_likely_found(_name);
+                                if(value == nullptr) {
+                                    ImportError(_S("cannot import name ", _name.escape()));
+                                } else {
+                                    frame->f_globals().set(_name, value);
+                                }
+                            }
+                        } else {
+                            for(auto& [name, value]: _0->attr().items()) {
+                                std::string_view s = name.sv();
+                                if(s.empty() || s[0] == '_') continue;
+                                frame->f_globals().set(name, value);
+                            }
+                        }
+                    }
+                        DISPATCH()
+                    /*****************************************/
+                    case OP_UNPACK_SEQUENCE: {
+                        __op_unpack_sequence(byte.arg);
+                    }
+                        DISPATCH()
+                    case OP_UNPACK_EX: {
+                        auto _lock = gc_scope_lock();  // lock the gc via RAII!!
+                        PyVar _0 = py_iter(POPX());
+                        const PyTypeInfo* _ti = _tp_info(_0);
+                        PyVar _1;
+                        for(int i = 0; i < byte.arg; i++) {
+                            _1 = _py_next(_ti, _0);
+                            if(_1 == StopIteration) ValueError("not enough values to unpack");
+                            PUSH(_1);
+                        }
+                        List extras;
+                        while(true) {
+                            _1 = _py_next(_ti, _0);
+                            if(_1 == StopIteration) break;
+                            extras.push_back(_1);
+                        }
+                        PUSH(VAR(std::move(extras)));
+                    }
+                        DISPATCH()
+                    /*****************************************/
+                    case OP_BEGIN_CLASS: {
+                        StrName _name(byte.arg);
+                        PyVar _0 = POPX();  // super
+                        if(is_none(_0)) _0 = _t(tp_object);
+                        check_type(_0, tp_type);
+                        __curr_class = new_type_object(frame->_module, _name, PK_OBJ_GET(Type, _0), true);
+                    }
+                        DISPATCH()
+                    case OP_END_CLASS: {
+                        assert(__curr_class != nullptr);
+                        StrName _name(byte.arg);
+                        frame->_module->attr().set(_name, __curr_class);
+                        // call on_end_subclass
+                        PyTypeInfo* ti = &_all_types[__curr_class->as<Type>()];
+                        if(ti->base != tp_object) {
+                            PyTypeInfo* base_ti = &_all_types[ti->base];
+                            if(base_ti->on_end_subclass) base_ti->on_end_subclass(this, ti);
+                        }
+                        __curr_class = nullptr;
+                    }
+                        DISPATCH()
+                    case OP_STORE_CLASS_ATTR: {
+                        assert(__curr_class != nullptr);
+                        StrName _name(byte.arg);
+                        PyVar _0 = POPX();
+                        if(is_type(_0, tp_function)) { PK_OBJ_GET(Function, _0)._class = __curr_class; }
+                        __curr_class->attr().set(_name, _0);
+                    }
+                        DISPATCH()
+                    case OP_BEGIN_CLASS_DECORATION: {
+                        PUSH(__curr_class);
+                    }
+                        DISPATCH()
+                    case OP_END_CLASS_DECORATION: {
+                        __curr_class = POPX().get();
+                    }
+                        DISPATCH()
+                    case OP_ADD_CLASS_ANNOTATION: {
+                        assert(__curr_class != nullptr);
+                        StrName _name(byte.arg);
+                        Type type = __curr_class->as<Type>();
+                        _all_types[type].annotated_fields.push_back(_name);
+                    }
+                        DISPATCH()
+                    /*****************************************/
+                    case OP_WITH_ENTER: PUSH(call_method(TOP(), __enter__)); DISPATCH()
+                    case OP_WITH_EXIT:
+                        call_method(TOP(), __exit__);
+                        POP();
+                        DISPATCH()
+                    /*****************************************/
+                    case OP_TRY_ENTER: {
+                        frame->set_unwind_target(s_data.sp);
+                        DISPATCH()
+                    }
+                    case OP_EXCEPTION_MATCH: {
+                        PyVar assumed_type = POPX();
+                        check_type(assumed_type, tp_type);
+                        PyVar e_obj = TOP();
+                        bool ok = isinstance(e_obj, PK_OBJ_GET(Type, assumed_type));
+                        PUSH(VAR(ok));
+                    }
+                        DISPATCH()
+                    case OP_RAISE: {
+                        if(is_type(TOP(), tp_type)) { TOP() = call(TOP()); }
+                        if(!isinstance(TOP(), tp_exception)) { TypeError("exceptions must derive from Exception"); }
+                        _error(POPX());
+                    }
+                        DISPATCH()
+                    case OP_RAISE_ASSERT:
+                        if(byte.arg) {
+                            Str msg = py_str(TOP());
+                            POP();
+                            AssertionError(msg);
+                        } else {
+                            AssertionError();
+                        }
+                        DISPATCH()
+                    case OP_RE_RAISE: __raise_exc(true); DISPATCH()
+                    case OP_POP_EXCEPTION: __last_exception = POPX().get(); DISPATCH()
+                    /*****************************************/
+                    case OP_FORMAT_STRING: {
+                        PyVar _0 = POPX();
+                        const Str& spec = CAST(Str&, c11__getitem(PyVar, &frame->co->consts, byte.arg));
+                        PUSH(__format_object(_0, spec));
+                    }
+                        DISPATCH()
+                    /*****************************************/
+                    default: PK_UNREACHABLE()
+                }
+            }
+            /**********************************************************************/
+            PK_UNREACHABLE()
+        } catch(InternalException internal) {
+            __internal_exception = internal;
+            if(internal.type == InternalExceptionType::Unhandled) {
+                __last_exception = POPX().get();
+                Exception& _e = __last_exception->as<Exception>();
+                bool is_base_frame_to_be_popped = frame == base_frame;
+                __pop_frame();
+                if(callstack.empty()) {
+                    // propagate to the top level
+                    throw TopLevelException(this, &_e);
+                }
+                frame = &callstack.top();
+                PUSH(__last_exception);
+                if(is_base_frame_to_be_popped) { throw InternalException(InternalExceptionType::ToBeRaised); }
+            }
+        }
+    }
+}
+
+#undef TOP
+#undef SECOND
+#undef THIRD
+#undef STACK_SHRINK
+#undef PUSH
+#undef POP
+#undef POPX
+#undef STACK_VIEW
+
+#undef DISPATCH
+#undef DISPATCH_JUMP
+#undef CEVAL_STEP_CALLBACK
+
+}  // namespace pkpy

+ 6 - 6
src/interpreter/frame.c

@@ -10,16 +10,16 @@ void ValueStack__clear(ValueStack* self) {
     self->sp = self->begin;
 }
 
-PyVar* FastLocals__try_get_by_name(PyVar* locals, const CodeObject* co, py_Name name){
+py_TValue* FastLocals__try_get_by_name(py_TValue* locals, const CodeObject* co, py_Name name){
     int index = c11_smallmap_n2i__get(&co->varnames_inv, name, -1);
     if(index == -1) return NULL;
     return &locals[index];
 }
 
-pk_NameDict* FastLocals__to_namedict(PyVar* locals, const CodeObject* co) {
+pk_NameDict* FastLocals__to_namedict(py_TValue* locals, const CodeObject* co) {
     pk_NameDict* dict = pk_NameDict__new();
     c11__foreach(c11_smallmap_n2i_KV, &co->varnames_inv, entry) {
-        PyVar value = locals[entry->value];
+        py_TValue value = locals[entry->value];
         if(!py_isnull(&value)){
             pk_NameDict__set(dict, entry->key, value);
         }
@@ -39,7 +39,7 @@ void UnwindTarget__delete(UnwindTarget* self){
     free(self);
 }
 
-Frame* Frame__new(const CodeObject* co, const PyVar* module, const PyVar* function, PyVar* p0, PyVar* locals, const CodeObject* locals_co){
+Frame* Frame__new(const CodeObject* co, const py_TValue* module, const py_TValue* function, py_TValue* p0, py_TValue* locals, const CodeObject* locals_co){
     static_assert(sizeof(Frame) <= kPoolFrameBlockSize, "!(sizeof(Frame) <= kPoolFrameBlockSize)");
     Frame* self = PoolFrame_alloc();
     self->f_back = NULL;
@@ -72,7 +72,7 @@ int Frame__prepare_jump_exception_handler(Frame* self, ValueStack* _s){
         iblock = block->parent;
     }
     if(iblock < 0) return -1;
-    PyVar obj = *--_s->sp;  // pop exception object
+    py_TValue obj = *--_s->sp;  // pop exception object
     UnwindTarget* uw = Frame__find_unwind_target(self, iblock);
     _s->sp = (self->locals + uw->offset);  // unwind the stack                          
     *(_s->sp++) = obj;      // push it back
@@ -121,7 +121,7 @@ UnwindTarget* Frame__find_unwind_target(Frame* self, int iblock){
     return NULL;
 }
 
-void Frame__set_unwind_target(Frame* self, PyVar* sp) {
+void Frame__set_unwind_target(Frame* self, py_TValue* sp) {
     int iblock = Frame__iblock(self);
     UnwindTarget* existing = Frame__find_unwind_target(self, iblock);
     if(existing) {

+ 1 - 1
src/interpreter/gc.c

@@ -119,7 +119,7 @@ PyObject* PyObject__new(py_Type type, int slots, int size){
     // initialize slots or dict
     void* p = (char*)self + 8;
     if(slots >= 0){
-        memset(p, 0, slots*sizeof(PyVar));
+        memset(p, 0, slots*sizeof(py_TValue));
     }else{
         pk_NameDict__ctor(p);
     }

+ 17 - 15
src/interpreter/vm.c

@@ -7,17 +7,23 @@ static unsigned char* pk_default_import_file(const char* path){
     return NULL;
 }
 
-static void pk_default_stdout(const char* s){
-    fprintf(stdout, "%s", s);
+static void pk_default_stdout(const char* fmt, ...){
+    va_list args;
+    va_start(args, fmt);
+    vfprintf(stdout, fmt, args);
+    va_end(args);
     fflush(stdout);
 }
 
-static void pk_default_stderr(const char* s){
-    fprintf(stderr, "%s", s);
+static void pk_default_stderr(const char* fmt, ...){
+    va_list args;
+    va_start(args, fmt);
+    vfprintf(stderr, fmt, args);
+    va_end(args);
     fflush(stderr);
 }
 
-void pk_TypeInfo__ctor(pk_TypeInfo *self, py_Name name, py_Type base, PyObject* obj, const PyVar* module, bool subclass_enabled){
+void pk_TypeInfo__ctor(pk_TypeInfo *self, py_Name name, py_Type base, PyObject* obj, const py_TValue* module, bool subclass_enabled){
     memset(self, 0, sizeof(pk_TypeInfo));
     
     self->name = name;
@@ -58,19 +64,19 @@ void pk_VM__ctor(pk_VM* self){
     pk_ManagedHeap__ctor(&self->heap, self);
     ValueStack__ctor(&self->stack);
 
-    self->True = (PyVar){.type=tp_bool, .is_ptr=true, .extra=1,
+    self->True = (py_TValue){.type=tp_bool, .is_ptr=true, .extra=1,
         ._obj=pk_ManagedHeap__gcnew(&self->heap, tp_bool, 0, 0),
     };
-    self->False = (PyVar){.type=tp_bool, .is_ptr=true, .extra=0,
+    self->False = (py_TValue){.type=tp_bool, .is_ptr=true, .extra=0,
         ._obj=pk_ManagedHeap__gcnew(&self->heap, tp_bool, 0, 0),
     };
-    self->None = (PyVar){.type=tp_none_type, .is_ptr=true,
+    self->None = (py_TValue){.type=tp_none_type, .is_ptr=true,
         ._obj=pk_ManagedHeap__gcnew(&self->heap, tp_none_type, 0, 0),
     };
-    self->NotImplemented = (PyVar){.type=tp_not_implemented_type, .is_ptr=true,
+    self->NotImplemented = (py_TValue){.type=tp_not_implemented_type, .is_ptr=true,
         ._obj=pk_ManagedHeap__gcnew(&self->heap, tp_not_implemented_type, 0, 0),
     };
-    self->Ellipsis = (PyVar){.type=tp_ellipsis, .is_ptr=true,
+    self->Ellipsis = (py_TValue){.type=tp_ellipsis, .is_ptr=true,
         ._obj=pk_ManagedHeap__gcnew(&self->heap, tp_ellipsis, 0, 0),
     };
 
@@ -169,11 +175,7 @@ void pk_VM__pop_frame(pk_VM* self){
     Frame__delete(frame);
 }
 
-pk_FrameResult pk_VM__run_top_frame(pk_VM* self){
-    return RES_RETURN;
-}
-
-py_Type pk_VM__new_type(pk_VM* self, const char* name, py_Type base, const PyVar* module, bool subclass_enabled){
+py_Type pk_VM__new_type(pk_VM* self, const char* name, py_Type base, const py_TValue* module, bool subclass_enabled){
     py_Type type = self->types.count;
     pk_TypeInfo* ti = c11_vector__emplace(&self->types);
     PyObject* typeobj = pk_ManagedHeap__gcnew(&self->heap, tp_type, -1, sizeof(py_Type));

+ 3 - 3
src/objects/base.c

@@ -1,6 +1,6 @@
 #include "pocketpy/objects/base.h"
 
-PyVar PY_NULL = {.type=0, .is_ptr=false, .extra=0, ._i64=0};
-PyVar PY_OP_CALL = {.type=27, .is_ptr=false, .extra=0, ._i64=0};
-PyVar PY_OP_YIELD = {.type=28, .is_ptr=false, .extra=0, ._i64=0};
+py_TValue PY_NULL = {.type=0, .is_ptr=false, .extra=0, ._i64=0};
+py_TValue PY_OP_CALL = {.type=27, .is_ptr=false, .extra=0, ._i64=0};
+py_TValue PY_OP_YIELD = {.type=28, .is_ptr=false, .extra=0, ._i64=0};
 

+ 2 - 2
src/objects/codeobject.c

@@ -40,7 +40,7 @@ void FuncDecl__dtor(FuncDecl* self) {
     c11_smallmap_n2i__dtor(&self->kw_to_index);
 }
 
-void FuncDecl__add_kwarg(FuncDecl* self, int index, uint16_t key, const PyVar* value) {
+void FuncDecl__add_kwarg(FuncDecl* self, int index, uint16_t key, const py_TValue* value) {
     c11_smallmap_n2i__set(&self->kw_to_index, key, index);
     FuncDeclKwArg item = {index, key, *value};
     c11_vector__push(FuncDeclKwArg, &self->kwargs, item);
@@ -54,7 +54,7 @@ void CodeObject__ctor(CodeObject* self, pk_SourceData_ src, c11_string name) {
     c11_vector__ctor(&self->codes, sizeof(Bytecode));
     c11_vector__ctor(&self->codes_ex, sizeof(BytecodeEx));
 
-    c11_vector__ctor(&self->consts, sizeof(PyVar));
+    c11_vector__ctor(&self->consts, sizeof(py_TValue));
     c11_vector__ctor(&self->varnames, sizeof(uint16_t));
     self->nlocals = 0;
 

+ 9 - 9
src/objects/dict.c

@@ -13,8 +13,8 @@
 #define pkpy_Var__set_null(self) do { (self)->type = 0; } while(0)
 
 struct pkpy_DictEntry {
-    PyVar key;
-    PyVar val;
+    py_TValue key;
+    py_TValue val;
 };
 
 inline extern int pkpy_Dict__idx_size(const pkpy_Dict* self) {
@@ -79,7 +79,7 @@ static void pkpy_Dict__htset(pkpy_Dict* self, int h, int v) {
 #endif
 }
 
-static int pkpy_Dict__probe0(const pkpy_Dict* self, PyVar key, int hash) {
+static int pkpy_Dict__probe0(const pkpy_Dict* self, py_TValue key, int hash) {
     const int null = pkpy_Dict__idx_null(self);
     const int mask = self->_htcap - 1;
     for(int h = hash & mask;; h = DICT_HASH_NEXT(h) & mask) {
@@ -92,7 +92,7 @@ static int pkpy_Dict__probe0(const pkpy_Dict* self, PyVar key, int hash) {
     PK_UNREACHABLE();
 }
 
-static int pkpy_Dict__probe1(const pkpy_Dict* self, PyVar key, int hash) {
+static int pkpy_Dict__probe1(const pkpy_Dict* self, py_TValue key, int hash) {
     const int null = pkpy_Dict__idx_null(self);
     const int mask = self->_htcap - 1;
     for(int h = hash & mask;; h = DICT_HASH_NEXT(h) & mask) {
@@ -124,7 +124,7 @@ static void pkpy_Dict__extendht(pkpy_Dict* self) {
     }
 }
 
-bool pkpy_Dict__set(pkpy_Dict* self, PyVar key, PyVar val) {
+bool pkpy_Dict__set(pkpy_Dict* self, py_TValue key, py_TValue val) {
     int64_t out;
     int err = py_hash(&key, &out);
     int hash = DICT_HASH_TRANS(out);
@@ -161,7 +161,7 @@ bool pkpy_Dict__set(pkpy_Dict* self, PyVar key, PyVar val) {
     return false;
 }
 
-bool pkpy_Dict__contains(const pkpy_Dict* self, PyVar key) {
+bool pkpy_Dict__contains(const pkpy_Dict* self, py_TValue key) {
     int64_t out;
     int err = py_hash(&key, &out);
     int hash = DICT_HASH_TRANS(out);
@@ -205,7 +205,7 @@ static bool pkpy_Dict__refactor(pkpy_Dict* self) {
     return true;
 }
 
-bool pkpy_Dict__del(pkpy_Dict* self, PyVar key) {
+bool pkpy_Dict__del(pkpy_Dict* self, py_TValue key) {
     int64_t out;
     int err = py_hash(&key, &out);
     int hash = DICT_HASH_TRANS(out);
@@ -220,7 +220,7 @@ bool pkpy_Dict__del(pkpy_Dict* self, PyVar key) {
     return true;
 }
 
-const PyVar *pkpy_Dict__try_get(const pkpy_Dict* self, PyVar key) {
+const py_TValue *pkpy_Dict__try_get(const pkpy_Dict* self, py_TValue key) {
     int64_t out;
     int err = py_hash(&key, &out);
     int hash = DICT_HASH_TRANS(out);
@@ -263,7 +263,7 @@ pkpy_DictIter pkpy_Dict__iter(const pkpy_Dict *self) {
     };
 }
 
-bool pkpy_DictIter__next(pkpy_DictIter *self, PyVar *key, PyVar *val) {
+bool pkpy_DictIter__next(pkpy_DictIter *self, py_TValue *key, py_TValue *val) {
     if(self->_index >= self->_dict->_entries.count) return false;
     
     struct pkpy_DictEntry* entry = &c11__getitem(struct pkpy_DictEntry, &self->_dict->_entries, self->_index);

+ 1 - 1
src/objects/namedict.c

@@ -2,7 +2,7 @@
 
 #define SMALLMAP_T__SOURCE
 #define K uint16_t
-#define V PyVar
+#define V py_TValue
 #define NAME pk_NameDict
 #include "pocketpy/xmacros/smallmap.h"
 #undef SMALLMAP_T__SOURCE

+ 2 - 2
src/objects/object.c

@@ -11,7 +11,7 @@ pk_NameDict* PyObject__dict(PyObject* self){
     return (pk_NameDict*)((char*)self + 8);
 }
 
-PyVar* PyObject__slots(PyObject* self){
+py_TValue* PyObject__slots(PyObject* self){
     assert(self->slots >= 0);
-    return (PyVar*)((char*)self + 8);
+    return (py_TValue*)((char*)self + 8);
 }