diff --git a/include/pocketpy/interpreter/vm.h b/include/pocketpy/interpreter/vm.h index c5fd7fb2..d6abd09f 100644 --- a/include/pocketpy/interpreter/vm.h +++ b/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(); diff --git a/include/pocketpy/pocketpy.h b/include/pocketpy/pocketpy.h index d008fad7..9d5f8e0c 100644 --- a/include/pocketpy/pocketpy.h +++ b/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, diff --git a/src/interpreter/ceval.c b/src/interpreter/ceval.c index fe991c83..d46d3131 100644 --- a/src/interpreter/ceval.c +++ b/src/interpreter/ceval.c @@ -6,6 +6,7 @@ #include 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; -} \ No newline at end of file +} + +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; +} diff --git a/src/interpreter/ceval.cpp b/src/interpreter/ceval.cpp deleted file mode 100644 index 868e0a43..00000000 --- a/src/interpreter/ceval.cpp +++ /dev/null @@ -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(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(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(); - 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(); - 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((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_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, , 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()]; - 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(); - _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(); - 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 diff --git a/src/interpreter/vm.c b/src/interpreter/vm.c index da5fe077..6fceec9d 100644 --- a/src/interpreter/vm.c +++ b/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 */ diff --git a/src/public/modules.c b/src/public/modules.c index 16afc95b..6c10fc82 100644 --- a/src/public/modules.c +++ b/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() { diff --git a/src/public/py_object.c b/src/public/py_object.c index fceb86b6..726dc614 100644 --- a/src/public/py_object.c +++ b/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, "", 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__); } \ No newline at end of file diff --git a/src/public/py_ops.c b/src/public/py_ops.c index 13a5cfc0..8d545ec6 100644 --- a/src/public/py_ops.c +++ b/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; } diff --git a/src/public/py_range.c b/src/public/py_range.c new file mode 100644 index 00000000..1c0be65b --- /dev/null +++ b/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; +} \ No newline at end of file diff --git a/src/public/py_str.c b/src/public/py_str.c index d1fbb42c..560df674 100644 --- a/src/public/py_str.c +++ b/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; diff --git a/src/public/vm.c b/src/public/vm.c index c8e7ae7e..d625f574 100644 --- a/src/public/vm.c +++ b/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; +} \ No newline at end of file