blueloveTH 1 年之前
父節點
當前提交
400b8fbef4

+ 4 - 1
include/pocketpy/interpreter/vm.h

@@ -42,7 +42,6 @@ typedef struct pk_VM {
     pk_NameDict modules;
     c11_vector /*T=pk_TypeInfo*/ types;
 
-    py_TValue StopIteration;  // a special Exception class
     py_TValue builtins;       // builtins module
     py_TValue main;           // __main__ module
 
@@ -53,6 +52,7 @@ typedef struct pk_VM {
 
     py_TValue last_retval;
     bool has_error;
+    bool is_stopiteration;
     
     py_TValue reg[8];  // users' registers
 
@@ -91,6 +91,7 @@ py_Type pk_VM__new_type(pk_VM* self,
 pk_FrameResult pk_VM__vectorcall(pk_VM* self, uint16_t argc, uint16_t kwargc, bool opcall);
 
 const char* pk_opname(Opcode op);
+py_TValue* pk_arrayview(py_Ref self, int* size);
 
 // type registration
 void pk_object__register();
@@ -101,6 +102,8 @@ py_Type pk_bytes__register();
 py_Type pk_list__register();
 py_Type pk_function__register();
 py_Type pk_nativefunc__register();
+py_Type pk_range__register();
+py_Type pk_range_iterator__register();
 
 py_TValue pk_builtins__register();
 

+ 3 - 1
include/pocketpy/pocketpy.h

@@ -244,13 +244,14 @@ void py_formatexc(char* out);
 #define TypeError(...) py_exception("TypeError", __VA_ARGS__)
 #define ValueError(...) py_exception("ValueError", __VA_ARGS__)
 #define IndexError(...) py_exception("IndexError", __VA_ARGS__)
-#define StopIteration() py_exception("StopIteration", "")
 #define NotImplementedError() py_exception("NotImplementedError", "")
 #define AttributeError(self, n)                                                                    \
     py_exception("AttributeError", "'%t' object has no attribute '%n'", (self)->type, (n))
 #define UnboundLocalError(n)                                                                       \
     py_exception("UnboundLocalError", "local variable '%n' referenced before assignment", (n))
 
+bool StopIteration();
+
 /************* Operators *************/
 /// Equivalent to `bool(val)`.
 /// Returns 1 if `val` is truthy, otherwise 0.
@@ -385,6 +386,7 @@ enum py_PredefinedTypes {
     tp_tuple,  // N slots
     tp_slice,  // 3 slots (start, stop, step)
     tp_range,
+    tp_range_iterator,
     tp_module,
     tp_function,
     tp_nativefunc,

+ 21 - 2
src/interpreter/ceval.c

@@ -6,6 +6,7 @@
 #include <stdbool.h>
 
 static bool stack_binaryop(pk_VM* self, py_Name op, py_Name rop);
+static bool stack_unpack_sequence(pk_VM* self, uint16_t arg);
 
 #define DISPATCH()                                                                                 \
     do {                                                                                           \
@@ -32,7 +33,7 @@ static bool stack_binaryop(pk_VM* self, py_Name op, py_Name rop);
 #define STACK_SHRINK(n) (self->stack.sp -= n)
 #define PUSH(v)                                                                                    \
     do {                                                                                           \
-        *self->stack.sp = *v;                                                                      \
+        *self->stack.sp = *(v);                                                                    \
         self->stack.sp++;                                                                          \
     } while(0)
 #define POP() (--self->stack.sp)
@@ -693,6 +694,13 @@ pk_FrameResult pk_VM__run_top_frame(pk_VM* self) {
                 DISPATCH();
             }
 
+                //
+
+            case OP_UNPACK_SEQUENCE: {
+                if(!stack_unpack_sequence(self, byte.arg)) goto __ERROR;
+                DISPATCH();
+            }
+
             ///////////
             case OP_RAISE_ASSERT: {
                 if(byte.arg) {
@@ -755,4 +763,15 @@ bool py_binaryop(const py_Ref lhs, const py_Ref rhs, py_Name op, py_Name rop) {
     bool ok = stack_binaryop(self, op, rop);
     STACK_SHRINK(2);
     return ok;
-}
+}
+
+static bool stack_unpack_sequence(pk_VM* self, uint16_t arg) {
+    int length;
+    py_TValue* p = pk_arrayview(TOP(), &length);
+    if(!p) return TypeError("expected list or tuple to unpack, got '%t'", TOP()->type);
+    if(length != arg) return ValueError("expected %d values to unpack, got %d", arg, length);
+    POP();
+    for(int i = 0; i < length; i++)
+        PUSH(p + i);
+    return true;
+}

+ 0 - 1151
src/interpreter/ceval.cpp

@@ -1,1151 +0,0 @@
-#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

+ 3 - 3
src/interpreter/vm.c

@@ -62,7 +62,6 @@ void pk_VM__ctor(pk_VM* self) {
     pk_NameDict__ctor(&self->modules);
     c11_vector__ctor(&self->types, sizeof(pk_TypeInfo));
 
-    self->StopIteration = PY_NIL;
     self->builtins = PY_NIL;
     self->main = PY_NIL;
 
@@ -73,6 +72,7 @@ void pk_VM__ctor(pk_VM* self) {
 
     self->last_retval = PY_NIL;
     self->has_error = false;
+    self->is_stopiteration = false;
 
     self->__curr_class = PY_NIL;
     self->__dynamic_func_decl = NULL;
@@ -102,7 +102,8 @@ void pk_VM__ctor(pk_VM* self) {
     validate(tp_tuple, pk_VM__new_type(self, "tuple", tp_object, NULL, false));
 
     validate(tp_slice, pk_VM__new_type(self, "slice", tp_object, NULL, false));
-    validate(tp_range, pk_VM__new_type(self, "range", tp_object, NULL, false));
+    validate(tp_range, pk_range__register());
+    validate(tp_range_iterator, pk_range_iterator__register());
     validate(tp_module, pk_VM__new_type(self, "module", tp_object, NULL, false));
 
     validate(tp_function, pk_function__register());
@@ -130,7 +131,6 @@ void pk_VM__ctor(pk_VM* self) {
     validate(tp_stop_iteration, pk_VM__new_type(self, "StopIteration", tp_exception, NULL, false));
 #undef validate
 
-    self->StopIteration = *py_tpobject(tp_stop_iteration);
     self->builtins = pk_builtins__register();
 
     /* Setup Public Builtin Types */

+ 2 - 5
src/public/modules.c

@@ -117,11 +117,8 @@ static bool _py_builtins__next(int argc, py_Ref argv) {
     PY_CHECK_ARGC(1);
     int res = py_next(argv);
     if(res == -1) return false;
-    if(res) {
-        return true;
-    } else {
-        return StopIteration();
-    }
+    if(res) return true;
+    return py_exception("StopIteration", "");
 }
 
 py_TValue pk_builtins__register() {

+ 15 - 1
src/public/py_object.c

@@ -4,7 +4,7 @@
 
 static bool _py_object__new__(int argc, py_Ref argv) {
     assert(argc >= 1);
-    py_Type cls = argv[0].type;
+    py_Type cls = py_totype(py_arg(0));
     py_newobject(py_retval(), cls, 0, 0);
     return true;
 }
@@ -42,10 +42,24 @@ static bool _py_object__repr__(int argc, py_Ref argv) {
     return true;
 }
 
+static bool _py_type__repr__(int argc, py_Ref argv) {
+    PY_CHECK_ARGC(1);
+    c11_sbuf buf;
+    c11_sbuf__ctor(&buf);
+    pk_sprintf(&buf, "<class '%t'>", py_totype(argv));
+    c11_string* res = c11_sbuf__submit(&buf);
+    py_newstrn(py_retval(), res->data, res->size);
+    c11_string__delete(res);
+    return true;
+}
+
 void pk_object__register() {
     py_bindmagic(tp_object, __new__, _py_object__new__);
     py_bindmagic(tp_object, __hash__, _py_object__hash__);
     py_bindmagic(tp_object, __eq__, _py_object__eq__);
     py_bindmagic(tp_object, __ne__, _py_object__ne__);
     py_bindmagic(tp_object, __repr__, _py_object__repr__);
+
+    // type patch...
+    py_bindmagic(tp_type, __repr__, _py_type__repr__);
 }

+ 4 - 6
src/public/py_ops.c

@@ -67,15 +67,13 @@ bool py_iter(const py_Ref val) {
 }
 
 int py_next(const py_Ref val) {
+    pk_VM* vm = pk_current_vm;
+    vm->is_stopiteration = false;
     py_Ref tmp = py_tpfindmagic(val->type, __next__);
     if(!tmp) return TypeError("'%t' object is not an iterator", val->type);
     bool ok = py_call(tmp, 1, val);
-    if(ok) {
-        py_Ref retval = py_retval();
-        bool is_end = retval->type == tp_type && py_totype(retval) == tp_stop_iteration;
-        return !is_end;
-    }
-    return -1;
+    if(ok) return true;
+    return vm->is_stopiteration ? 0 : -1;
 }
 
 int py_getattr(const py_Ref self, py_Name name, py_Ref out) { return -1; }

+ 99 - 0
src/public/py_range.c

@@ -0,0 +1,99 @@
+#include "pocketpy/pocketpy.h"
+
+#include "pocketpy/common/utils.h"
+#include "pocketpy/objects/object.h"
+#include "pocketpy/interpreter/vm.h"
+
+typedef struct Range {
+    py_i64 start;
+    py_i64 stop;
+    py_i64 step;
+} Range;
+
+static bool _py_range__new__(int argc, py_Ref argv) {
+    Range* ud = py_newobject(py_retval(), tp_range, 0, sizeof(Range));
+    switch(argc - 1) {  // skip cls
+        case 1: {
+            PY_CHECK_ARG_TYPE(1, tp_int);
+            ud->start = 0;
+            ud->stop = py_toint(py_arg(1));
+            ud->step = 1;
+            break;
+        }
+        case 2:
+            PY_CHECK_ARG_TYPE(1, tp_int);
+            PY_CHECK_ARG_TYPE(2, tp_int);
+            ud->start = py_toint(py_arg(1));
+            ud->stop = py_toint(py_arg(2));
+            ud->step = 1;
+            break;
+        case 3:
+            PY_CHECK_ARG_TYPE(1, tp_int);
+            PY_CHECK_ARG_TYPE(2, tp_int);
+            PY_CHECK_ARG_TYPE(3, tp_int);
+            ud->start = py_toint(py_arg(1));
+            ud->stop = py_toint(py_arg(2));
+            ud->step = py_toint(py_arg(3));
+            break;
+        default: return TypeError("range() expected at most 3 arguments, got %d", argc - 1);
+    }
+    if(ud->step == 0) return ValueError("range() arg 3 must not be zero");
+    return true;
+}
+
+static bool _py_range__iter__(int argc, py_Ref argv) {
+    PY_CHECK_ARGC(1);
+    return py_tpcall(tp_range_iterator, 1, argv);
+}
+
+py_Type pk_range__register() {
+    pk_VM* vm = pk_current_vm;
+    py_Type type = pk_VM__new_type(vm, "range", tp_object, NULL, false);
+
+    py_bindmagic(type, __new__, _py_range__new__);
+    py_bindmagic(type, __iter__, _py_range__iter__);
+    return type;
+}
+
+typedef struct RangeIterator {
+    Range range;
+    py_i64 current;
+} RangeIterator;
+
+static bool _py_range_iterator__new__(int argc, py_Ref argv) {
+    PY_CHECK_ARGC(2);
+    PY_CHECK_ARG_TYPE(1, tp_range);
+    RangeIterator* ud = py_newobject(py_retval(), tp_range_iterator, 0, sizeof(RangeIterator));
+    ud->range = *(Range*)py_touserdata(py_arg(1));
+    ud->current = ud->range.start;
+    return true;
+}
+
+static bool _py_range_iterator__iter__(int argc, py_Ref argv) {
+    PY_CHECK_ARGC(1);
+    *py_retval() = *py_arg(0);
+    return true;
+}
+
+static bool _py_range_iterator__next__(int argc, py_Ref argv) {
+    PY_CHECK_ARGC(1);
+    RangeIterator* ud = py_touserdata(py_arg(0));
+    if(ud->range.step > 0) {
+        if(ud->current >= ud->range.stop) return StopIteration();
+    } else {
+        if(ud->current <= ud->range.stop) return StopIteration();
+    }
+    py_newint(py_retval(), ud->current);
+    ud->current += ud->range.step;
+    return true;
+}
+
+py_Type pk_range_iterator__register() {
+    pk_VM* vm = pk_current_vm;
+    py_Type type = pk_VM__new_type(vm, "range_iterator", tp_object, NULL, false);
+
+    py_bindmagic(type, __new__, _py_range_iterator__new__);
+    py_bindmagic(type, __iter__, _py_range_iterator__iter__);
+    py_bindmagic(type, __next__, _py_range_iterator__next__);
+    return type;
+}

+ 1 - 4
src/public/py_str.c

@@ -521,10 +521,7 @@ static bool _py_str_iterator__next__(int argc, py_Ref argv) {
     int* ud = py_touserdata(&argv[0]);
     int size;
     const char* data = py_tostrn(py_getslot(argv, 0), &size);
-    if(*ud == size) {
-        *py_retval() = pk_current_vm->StopIteration;
-        return true;
-    }
+    if(*ud == size) return StopIteration();
     int start = *ud;
     int len = c11__u8_header(data[*ud], false);
     *ud += len;

+ 20 - 1
src/public/vm.c

@@ -35,6 +35,18 @@ const char* pk_opname(Opcode op) {
     return OP_NAMES[op];
 }
 
+py_TValue* pk_arrayview(py_Ref self, int* length) {
+    if(self->type == tp_list) {
+        *length = py_list__len(self);
+        return py_list__getitem(self, 0);
+    }
+    if(self->type == tp_tuple) {
+        *length = py_tuple__len(self);
+        return py_tuple__getitem(self, 0);
+    }
+    return NULL;
+}
+
 static void disassemble(CodeObject* co) {
     c11_vector /*T=int*/ jumpTargets;
     c11_vector__ctor(&jumpTargets, sizeof(int));
@@ -294,7 +306,7 @@ const char* py_tpname(py_Type type) {
     return py_name2str(name);
 }
 
-bool py_tpcall(py_Type type, int argc, py_Ref argv){
+bool py_tpcall(py_Type type, int argc, py_Ref argv) {
     return py_call(py_tpobject(type), argc, argv);
 }
 
@@ -305,3 +317,10 @@ bool py_callmagic(py_Name name, int argc, py_Ref argv) {
     if(!tmp) return AttributeError(argv, name);
     return py_call(tmp, argc, argv);
 }
+
+bool StopIteration() {
+    pk_VM* vm = pk_current_vm;
+    assert(!vm->is_stopiteration);  // flag is already set
+    vm->is_stopiteration = true;
+    return false;
+}