From 3bac1d7388e40e664df249fecc34ffc3dbc260d2 Mon Sep 17 00:00:00 2001 From: blueloveTH Date: Fri, 19 May 2023 20:42:04 +0800 Subject: [PATCH] ... --- src/ceval.h | 6 + src/cffi.h | 22 ++-- src/linalg.h | 30 ++--- src/linalg.pyi | 15 +-- src/obj.h | 1 + src/pocketpy.h | 339 +++++++++++++++++++++++++++---------------------- src/str.h | 23 +--- src/vm.h | 107 ++++++++-------- 8 files changed, 268 insertions(+), 275 deletions(-) diff --git a/src/ceval.h b/src/ceval.h index 2238aacf..62a5c29c 100644 --- a/src/ceval.h +++ b/src/ceval.h @@ -229,12 +229,14 @@ __NEXT_STEP:; PUSH(_0); DISPATCH(); TARGET(BUILD_DICT) + const static StrName m_dict("dict"); _0 = VAR(STACK_VIEW(byte.arg).to_tuple()); _0 = call(builtins->attr(m_dict), _0); STACK_SHRINK(byte.arg); PUSH(_0); DISPATCH(); TARGET(BUILD_SET) + const static StrName m_set("set"); _0 = VAR(STACK_VIEW(byte.arg).to_tuple()); _0 = call(builtins->attr(m_set), _0); STACK_SHRINK(byte.arg); @@ -428,6 +430,7 @@ __NEXT_STEP:; call_method(SECOND(), __setitem__, t[0], t[1]); } DISPATCH(); TARGET(SET_ADD) + const static StrName m_add("add"); _0 = POPX(); call_method(SECOND(), m_add, _0); DISPATCH(); @@ -535,9 +538,11 @@ __NEXT_STEP:; /*****************************************/ // TODO: using "goto" inside with block may cause __exit__ not called TARGET(WITH_ENTER) + const static StrName __enter__("__enter__"); call_method(POPX(), __enter__); DISPATCH(); TARGET(WITH_EXIT) + const static StrName __exit__("__exit__"); call_method(POPX(), __exit__); DISPATCH(); /*****************************************/ @@ -567,6 +572,7 @@ __NEXT_STEP:; TARGET(RE_RAISE) _raise(); DISPATCH(); /*****************************************/ TARGET(SETUP_DOCSTRING) + const static StrName __doc__("__doc__"); TOP()->attr().set(__doc__, co_consts[byte.arg]); DISPATCH(); TARGET(FORMAT_STRING) { diff --git a/src/cffi.h b/src/cffi.h index 4f806489..2990d53a 100644 --- a/src/cffi.h +++ b/src/cffi.h @@ -49,8 +49,8 @@ struct VoidP{ static void _register(VM* vm, PyObject* mod, PyObject* type){ vm->bind_default_constructor(type); - vm->bind_method<0>(type, "__repr__", [](VM* vm, ArgsView args){ - VoidP& self = _CAST(VoidP&, args[0]); + vm->bind__repr__(OBJ_GET(Type, type), [](VM* vm, PyObject* obj){ + VoidP& self = _CAST(VoidP&, obj); std::stringstream ss; ss << "bind__add__(OBJ_GET(Type, type), [](VM* vm, PyObject* lhs, PyObject* rhs){ + VoidP& self = _CAST(VoidP&, lhs); + i64 offset = CAST(i64, rhs); return VAR_T(VoidP, (char*)self.ptr + offset); }); - vm->bind_method<1>(type, "__sub__", [](VM* vm, ArgsView args){ - VoidP& self = _CAST(VoidP&, args[0]); - i64 offset = CAST(i64, args[1]); + vm->bind__sub__(OBJ_GET(Type, type), [](VM* vm, PyObject* lhs, PyObject* rhs){ + VoidP& self = _CAST(VoidP&, lhs); + i64 offset = CAST(i64, rhs); return VAR_T(VoidP, (char*)self.ptr - offset); }); @@ -309,9 +309,9 @@ struct C99ReflType final: ReflType{ return VAR(self.size); }); - vm->bind_method<1>(type, "__getitem__", [](VM* vm, ArgsView args){ - C99ReflType& self = _CAST(C99ReflType&, args[0]); - const Str& name = CAST(Str&, args[1]); + vm->bind__getitem__(OBJ_GET(Type, type), [](VM* vm, PyObject* obj, PyObject* key){ + C99ReflType& self = _CAST(C99ReflType&, obj); + const Str& name = CAST(Str&, key); auto it = self.fields.find(name.sv()); if(it == self.fields.end()){ vm->_error("KeyError", name.escape()); diff --git a/src/linalg.h b/src/linalg.h index 0920d3de..690e4d1f 100644 --- a/src/linalg.h +++ b/src/linalg.h @@ -29,8 +29,7 @@ struct Vec2{ float cross(const Vec2& v) const { return x * v.y - y * v.x; } float length() const { return sqrtf(x * x + y * y); } float length_squared() const { return x * x + y * y; } - NoReturn normalize() { float l = length(); x /= l; y /= l; return {}; } - Vec2 normalized() const { float l = length(); return Vec2(x / l, y / l); } + Vec2 normalize() const { float l = length(); return Vec2(x / l, y / l); } }; struct Vec3{ @@ -54,8 +53,7 @@ struct Vec3{ Vec3 cross(const Vec3& v) const { return Vec3(y * v.z - z * v.y, z * v.x - x * v.z, x * v.y - y * v.x); } float length() const { return sqrtf(x * x + y * y + z * z); } float length_squared() const { return x * x + y * y + z * z; } - NoReturn normalize() { float l = length(); x /= l; y /= l; z /= l; return {}; } - Vec3 normalized() const { float l = length(); return Vec3(x / l, y / l, z / l); } + Vec3 normalize() const { float l = length(); return Vec3(x / l, y / l, z / l); } }; struct Mat3x3{ @@ -361,6 +359,13 @@ struct PyVec2: Vec2 { return VAR_T(PyVec2, self); }); + vm->bind_method<1>(type, "rotate", [](VM* vm, ArgsView args){ + Vec2 self = _CAST(PyVec2&, args[0]); + float radian = vm->num_to_float(args[1]); + self = Mat3x3::rotate(radian).transform_vector(self); + return VAR(self); + }); + BIND_VEC_VEC_OP(2, __add__, +) BIND_VEC_VEC_OP(2, __sub__, -) BIND_VEC_FLOAT_OP(2, __mul__, *) @@ -374,7 +379,6 @@ struct PyVec2: Vec2 { BIND_VEC_FUNCTION_0(2, length) BIND_VEC_FUNCTION_0(2, length_squared) BIND_VEC_FUNCTION_0(2, normalize) - BIND_VEC_FUNCTION_0(2, normalized) } }; @@ -419,7 +423,6 @@ struct PyVec3: Vec3 { BIND_VEC_FUNCTION_0(3, length) BIND_VEC_FUNCTION_0(3, length_squared) BIND_VEC_FUNCTION_0(3, normalize) - BIND_VEC_FUNCTION_0(3, normalized) } }; @@ -622,21 +625,6 @@ struct PyMat3x3: Mat3x3{ }); /*************** affine transformations ***************/ - vm->bind_func<1>(type, "translate", [](VM* vm, ArgsView args){ - PyVec2& v = CAST(PyVec2&, args[0]); - return VAR_T(PyMat3x3, Mat3x3::translate(v)); - }); - - vm->bind_func<1>(type, "rotate", [](VM* vm, ArgsView args){ - f64 angle = VAR_F(args[0]); - return VAR_T(PyMat3x3, Mat3x3::rotate(angle)); - }); - - vm->bind_func<1>(type, "scale", [](VM* vm, ArgsView args){ - PyVec2& v = CAST(PyVec2&, args[0]); - return VAR_T(PyMat3x3, Mat3x3::scale(v)); - }); - vm->bind_func<3>(type, "trs", [](VM* vm, ArgsView args){ PyVec2& t = CAST(PyVec2&, args[0]); f64 r = VAR_F(args[1]); diff --git a/src/linalg.pyi b/src/linalg.pyi index 9f2bdb22..de24efec 100644 --- a/src/linalg.pyi +++ b/src/linalg.pyi @@ -14,8 +14,8 @@ class vec2: def cross(self, other: vec2) -> float: ... def length(self) -> float: ... def length_squared(self) -> float: ... - def normalize(self) -> None: ... - def normalized(self) -> vec2: ... + def normalize(self) -> vec2: ... + def rotate(self, radians: float) -> vec2: ... class vec3: x: float @@ -32,8 +32,7 @@ class vec3: def cross(self, other: vec3) -> float: ... def length(self) -> float: ... def length_squared(self) -> float: ... - def normalize(self) -> None: ... - def normalized(self) -> vec3: ... + def normalize(self) -> vec3: ... class mat3x3: _11: float @@ -83,13 +82,7 @@ class mat3x3: @staticmethod def identity() -> mat3x3: ... - # affine transformations - @staticmethod - def translate(v: vec2) -> mat3x3: ... - @staticmethod - def rotate(rad: float) -> mat3x3: ... - @staticmethod - def scale(v: vec2) -> mat3x3: ... + # affine transformations @staticmethod def trs(t: vec2, r: float, s: vec2) -> mat3x3: ... diff --git a/src/obj.h b/src/obj.h index 71b0f9e7..69cf9ab2 100644 --- a/src/obj.h +++ b/src/obj.h @@ -187,6 +187,7 @@ Str obj_type_name(VM* vm, Type type); #if DEBUG_NO_BUILTIN_MODULES #define OBJ_NAME(obj) Str("") #else +const static StrName __name__("__name__"); #define OBJ_NAME(obj) OBJ_GET(Str, vm->getattr(obj, __name__)) #endif diff --git a/src/pocketpy.h b/src/pocketpy.h index df44e4a5..32d818ca 100644 --- a/src/pocketpy.h +++ b/src/pocketpy.h @@ -1,7 +1,6 @@ #pragma once #include "ceval.h" -#include "common.h" #include "compiler.h" #include "obj.h" #include "repl.h" @@ -13,6 +12,7 @@ #include "requests.h" #include "io.h" #include "_generated.h" +#include "vm.h" namespace pkpy { @@ -29,36 +29,38 @@ inline CodeObject_ VM::compile(Str source, Str filename, CompileMode mode, bool } } -#define BIND_NUM_ARITH_OPT(name, op) \ - _vm->bind_method<1>("int", #name, [](VM* vm, ArgsView args){ \ - if(is_int(args[1])){ \ - return VAR(_CAST(i64, args[0]) op _CAST(i64, args[1])); \ - }else{ \ - return VAR(vm->num_to_float(args[0]) op vm->num_to_float(args[1])); \ - } \ - }); \ - _vm->bind_method<1>("float", #name, [](VM* vm, ArgsView args){ \ - return VAR(_CAST(f64, args[0]) op vm->num_to_float(args[1])); \ - }); - inline void init_builtins(VM* _vm) { +#define BIND_NUM_ARITH_OPT(name, op) \ + _vm->bind##name(_vm->tp_int, [](VM* vm, PyObject* lhs, PyObject* rhs) { \ + if(is_int(rhs)){ \ + return VAR(_CAST(i64, lhs) op _CAST(i64, rhs)); \ + }else{ \ + return VAR(_CAST(i64, lhs) op vm->num_to_float(rhs)); \ + } \ + }); \ + _vm->bind##name(_vm->tp_float, [](VM* vm, PyObject* lhs, PyObject* rhs) { \ + return VAR(_CAST(f64, lhs) op vm->num_to_float(rhs)); \ + }); + BIND_NUM_ARITH_OPT(__add__, +) BIND_NUM_ARITH_OPT(__sub__, -) BIND_NUM_ARITH_OPT(__mul__, *) +#undef BIND_NUM_ARITH_OPT + #define BIND_NUM_LOGICAL_OPT(name, op, is_eq) \ _vm->bind##name(_vm->tp_int, [](VM* vm, PyObject* lhs, PyObject* rhs) { \ if(is_int(rhs)) return _CAST(i64, lhs) op _CAST(i64, rhs); \ if(is_float(rhs)) return _CAST(i64, lhs) op _CAST(f64, rhs); \ - if constexpr(is_eq) return false; \ + if constexpr(is_eq) return lhs op rhs; \ vm->TypeError("unsupported operand type(s) for " #op ); \ return false; \ }); \ _vm->bind##name(_vm->tp_float, [](VM* vm, PyObject* lhs, PyObject* rhs) { \ if(is_int(rhs)) return _CAST(f64, lhs) == _CAST(i64, rhs); \ if(is_float(rhs)) return _CAST(f64, lhs) == _CAST(f64, rhs); \ - if constexpr(is_eq) return false; \ + if constexpr(is_eq) return lhs op rhs; \ vm->TypeError("unsupported operand type(s) for " #op ); \ return false; \ }); @@ -130,12 +132,15 @@ inline void init_builtins(VM* _vm) { }); _vm->bind_builtin_func<1>("repr", CPP_LAMBDA(vm->asRepr(args[0]))); + _vm->bind_builtin_func<1>("len", [](VM* vm, ArgsView args){ + const PyTypeInfo* ti = vm->_inst_type_info(args[0]); + if(ti->m__len__) return VAR(ti->m__len__(vm, args[0])); return vm->call_method(args[0], __len__); }); _vm->bind_builtin_func<1>("hash", [](VM* vm, ArgsView args){ - i64 value = vm->hash(args[0]); + i64 value = vm->py_hash(args[0]); if(((value << 2) >> 2) != value) value >>= 2; return VAR(value); }); @@ -194,8 +199,7 @@ inline void init_builtins(VM* _vm) { return VAR(std::move(ret)); }); - _vm->bind_method<0>("object", "__repr__", [](VM* vm, ArgsView args) { - PyObject* self = args[0]; + _vm->bind__repr__(_vm->tp_object, [](VM* vm, PyObject* self) { if(is_tagged(self)) self = nullptr; std::stringstream ss; ss << "<" << OBJ_NAME(vm->_t(self)) << " object at " << std::hex << self << ">"; @@ -204,6 +208,7 @@ inline void init_builtins(VM* _vm) { _vm->bind__eq__(_vm->tp_object, [](VM* vm, PyObject* lhs, PyObject* rhs) { return lhs == rhs; }); _vm->bind__ne__(_vm->tp_object, [](VM* vm, PyObject* lhs, PyObject* rhs) { return lhs != rhs; }); + _vm->bind__hash__(_vm->tp_object, [](VM* vm, PyObject* obj) { return BITS(obj); }); _vm->bind_constructor<2>("type", CPP_LAMBDA(vm->_t(args[1]))); @@ -219,29 +224,29 @@ inline void init_builtins(VM* _vm) { return VAR(r); }); - _vm->bind_method<0>("range", "__iter__", CPP_LAMBDA( - vm->PyIter(RangeIter(vm, args[0])) - )); - - _vm->bind_method<0>("NoneType", "__repr__", CPP_LAMBDA(VAR("None"))); - _vm->bind_method<0>("NoneType", "__json__", CPP_LAMBDA(VAR("null"))); - - _vm->bind_method<1>("int", "__truediv__", [](VM* vm, ArgsView args) { - f64 rhs = VAR_F(args[1]); - if (rhs == 0) vm->ZeroDivisionError(); - return VAR(_CAST(i64, args[0]) / rhs); + _vm->bind__iter__(_vm->tp_range, [](VM* vm, PyObject* obj) { + return vm->PyIter(RangeIter(vm, obj)); }); - _vm->bind_method<1>("float", "__truediv__", [](VM* vm, ArgsView args) { - f64 rhs = VAR_F(args[1]); - if (rhs == 0) vm->ZeroDivisionError(); - return VAR(_CAST(f64, args[0]) / rhs); + _vm->bind__repr__(_vm->_type("NoneType"), [](VM* vm, PyObject* obj) { return VAR("None"); }); + _vm->bind__json__(_vm->_type("NoneType"), [](VM* vm, PyObject* obj) { return VAR("null"); }); + + _vm->bind__truediv__(_vm->tp_float, [](VM* vm, PyObject* lhs, PyObject* rhs) { + f64 value = VAR_F(rhs); + if (value == 0) vm->ZeroDivisionError(); + return VAR(_CAST(f64, lhs) / value); }); - auto py_number_pow = [](VM* vm, ArgsView args) { - if(is_both_int(args[0], args[1])){ - i64 lhs = _CAST(i64, args[0]); - i64 rhs = _CAST(i64, args[1]); + _vm->bind__truediv__(_vm->tp_int, [](VM* vm, PyObject* lhs, PyObject* rhs) { + f64 value = VAR_F(rhs); + if (value == 0) vm->ZeroDivisionError(); + return VAR(_CAST(i64, lhs) / value); + }); + + auto py_number_pow = [](VM* vm, PyObject* lhs_, PyObject* rhs_) { + if(is_both_int(lhs_, rhs_)){ + i64 lhs = _CAST(i64, lhs_); + i64 rhs = _CAST(i64, rhs_); bool flag = false; if(rhs < 0) {flag = true; rhs = -rhs;} i64 ret = 1; @@ -253,12 +258,12 @@ inline void init_builtins(VM* _vm) { if(flag) return VAR((f64)(1.0 / ret)); return VAR(ret); }else{ - return VAR((f64)std::pow(VAR_F(args[0]), VAR_F(args[1]))); + return VAR((f64)std::pow(VAR_F(lhs_), VAR_F(rhs_))); } }; - _vm->bind_method<1>("int", "__pow__", py_number_pow); - _vm->bind_method<1>("float", "__pow__", py_number_pow); + _vm->bind__pow__(_vm->tp_int, py_number_pow); + _vm->bind__pow__(_vm->tp_float, py_number_pow); /************ PyInt ************/ _vm->bind_constructor<2>("int", [](VM* vm, ArgsView args) { @@ -280,23 +285,27 @@ inline void init_builtins(VM* _vm) { return vm->None; }); - _vm->bind_method<1>("int", "__floordiv__", [](VM* vm, ArgsView args) { - i64 rhs = CAST(i64, args[1]); + _vm->bind__floordiv__(_vm->tp_int, [](VM* vm, PyObject* lhs_, PyObject* rhs_) { + i64 rhs = CAST(i64, rhs_); if(rhs == 0) vm->ZeroDivisionError(); - return VAR(CAST(i64, args[0]) / rhs); + return VAR(_CAST(i64, lhs_) / rhs); }); - _vm->bind_method<1>("int", "__mod__", [](VM* vm, ArgsView args) { - i64 rhs = CAST(i64, args[1]); + _vm->bind__mod__(_vm->tp_int, [](VM* vm, PyObject* lhs_, PyObject* rhs_) { + i64 rhs = CAST(i64, rhs_); if(rhs == 0) vm->ZeroDivisionError(); - return VAR(CAST(i64, args[0]) % rhs); + return VAR(_CAST(i64, lhs_) % rhs); }); - _vm->bind_method<0>("int", "__repr__", CPP_LAMBDA(VAR(std::to_string(CAST(i64, args[0]))))); - _vm->bind_method<0>("int", "__json__", CPP_LAMBDA(VAR(std::to_string(CAST(i64, args[0]))))); + _vm->bind__repr__(_vm->tp_int, [](VM* vm, PyObject* obj) { return VAR(std::to_string(_CAST(i64, obj))); }); + _vm->bind__json__(_vm->tp_int, [](VM* vm, PyObject* obj) { return VAR(std::to_string(_CAST(i64, obj))); }); -#define INT_BITWISE_OP(name,op) \ - _vm->bind_method<1>("int", #name, CPP_LAMBDA(VAR(CAST(i64, args[0]) op CAST(i64, args[1])))); + _vm->bind__hash__(_vm->tp_int, [](VM* vm, PyObject* obj) { return _CAST(i64, obj); }); + +#define INT_BITWISE_OP(name, op) \ + _vm->bind##name(_vm->tp_int, [](VM* vm, PyObject* lhs, PyObject* rhs) { \ + return VAR(_CAST(i64, lhs) op CAST(i64, rhs)); \ + }); INT_BITWISE_OP(__lshift__, <<) INT_BITWISE_OP(__rshift__, >>) @@ -319,15 +328,20 @@ inline void init_builtins(VM* _vm) { f64 val = Number::stof(s.str()); return VAR(val); }catch(std::invalid_argument&){ - vm->ValueError("invalid literal for float(): '" + s + "'"); + vm->ValueError("invalid literal for float(): " + s.escape()); } } vm->TypeError("float() argument must be a int, float, bool or str"); return vm->None; }); - _vm->bind_method<0>("float", "__repr__", [](VM* vm, ArgsView args) { - f64 val = CAST(f64, args[0]); + _vm->bind__hash__(_vm->tp_float, [](VM* vm, PyObject* obj) { + f64 val = _CAST(f64, obj); + return (i64)std::hash()(val); + }); + + _vm->bind__repr__(_vm->tp_float, [](VM* vm, PyObject* obj) { + f64 val = _CAST(f64, obj); if(std::isinf(val) || std::isnan(val)) return VAR(std::to_string(val)); std::stringstream ss; ss << std::setprecision(std::numeric_limits::max_digits10-1-2) << val; @@ -335,9 +349,8 @@ inline void init_builtins(VM* _vm) { if(std::all_of(s.begin()+1, s.end(), isdigit)) s += ".0"; return VAR(s); }); - - _vm->bind_method<0>("float", "__json__", [](VM* vm, ArgsView args) { - f64 val = CAST(f64, args[0]); + _vm->bind__json__(_vm->tp_float, [](VM* vm, PyObject* obj) { + f64 val = _CAST(f64, obj); if(std::isinf(val) || std::isnan(val)) vm->ValueError("cannot jsonify 'nan' or 'inf'"); return VAR(std::to_string(val)); }); @@ -345,36 +358,31 @@ inline void init_builtins(VM* _vm) { /************ PyString ************/ _vm->bind_constructor<2>("str", CPP_LAMBDA(vm->asStr(args[1]))); - _vm->bind_method<1>("str", "__add__", [](VM* vm, ArgsView args) { - const Str& lhs = _CAST(Str&, args[0]); - const Str& rhs = CAST(Str&, args[1]); - return VAR(lhs + rhs); + _vm->bind__hash__(_vm->tp_str, [](VM* vm, PyObject* obj) { + return (i64)_CAST(Str&, obj).hash(); }); - _vm->bind_method<0>("str", "__len__", [](VM* vm, ArgsView args) { - const Str& self = _CAST(Str&, args[0]); - return VAR(self.u8_length()); + _vm->bind__add__(_vm->tp_str, [](VM* vm, PyObject* lhs, PyObject* rhs) { + return VAR(_CAST(Str&, lhs) + CAST(Str&, rhs)); }); - - _vm->bind_method<1>("str", "__contains__", [](VM* vm, ArgsView args) { - const Str& self = _CAST(Str&, args[0]); - const Str& other = CAST(Str&, args[1]); - return VAR(self.index(other) != -1); + _vm->bind__len__(_vm->tp_str, [](VM* vm, PyObject* obj) { + return (i64)_CAST(Str&, obj).u8_length(); }); - - _vm->bind_method<0>("str", "__str__", CPP_LAMBDA(args[0])); - _vm->bind_method<0>("str", "__iter__", CPP_LAMBDA(vm->PyIter(StringIter(vm, args[0])))); - - _vm->bind_method<0>("str", "__repr__", [](VM* vm, ArgsView args) { - const Str& self = _CAST(Str&, args[0]); - return VAR(self.escape()); + _vm->bind__contains__(_vm->tp_str, [](VM* vm, PyObject* lhs, PyObject* rhs) { + const Str& self = _CAST(Str&, lhs); + const Str& other = CAST(Str&, rhs); + return self.index(other) != -1; }); - - _vm->bind_method<0>("str", "__json__", [](VM* vm, ArgsView args) { - const Str& self = _CAST(Str&, args[0]); + _vm->bind__str__(_vm->tp_str, [](VM* vm, PyObject* obj) { return obj; }); + _vm->bind__iter__(_vm->tp_str, [](VM* vm, PyObject* obj) { return vm->PyIter(StringIter(vm, obj)); }); + _vm->bind__repr__(_vm->tp_str, [](VM* vm, PyObject* obj) { + const Str& self = _CAST(Str&, obj); + return VAR(self.escape(true)); + }); + _vm->bind__json__(_vm->tp_str, [](VM* vm, PyObject* obj) { + const Str& self = _CAST(Str&, obj); return VAR(self.escape(false)); }); - _vm->bind__eq__(_vm->tp_str, [](VM* vm, PyObject* lhs, PyObject* rhs) { if(!is_non_tagged_type(rhs, vm->tp_str)) return false; return _CAST(Str&, lhs) == _CAST(Str&, rhs); @@ -396,10 +404,18 @@ inline void init_builtins(VM* _vm) { return _CAST(Str&, lhs) <= CAST(Str&, rhs); }); - _vm->bind_method<1>("str", "__getitem__", [](VM* vm, ArgsView args) { - return PyStrGetItem(vm, args[0], args[1]); + _vm->bind__getitem__(_vm->tp_str, [](VM* vm, PyObject* obj, PyObject* index) { + const Str& self = _CAST(Str&, obj); + if(is_non_tagged_type(index, vm->tp_slice)){ + const Slice& s = _CAST(Slice&, index); + int start, stop, step; + vm->parse_int_slice(s, self.u8_length(), start, stop, step); + return VAR(self.u8_slice(start, stop, step)); + } + int i = CAST(int, index); + i = vm->normalized_index(i, self.u8_length()); + return VAR(self.u8_getitem(i)); }); - _vm->_type_info("str")->m__getitem__ = PyStrGetItem; _vm->bind_method<-1>("str", "replace", [](VM* vm, ArgsView args) { if(args.size() != 1+2 && args.size() != 1+3) vm->TypeError("replace() takes 2 or 3 arguments"); @@ -493,9 +509,9 @@ inline void init_builtins(VM* _vm) { return vm->None; }); - _vm->bind_method<1>("list", "__mul__", [](VM* vm, ArgsView args) { - const List& self = _CAST(List&, args[0]); - int n = CAST(int, args[1]); + _vm->bind__mul__(_vm->tp_list, [](VM* vm, PyObject* lhs, PyObject* rhs) { + const List& self = _CAST(List&, lhs); + int n = CAST(int, rhs); List result; result.reserve(self.size() * n); for(int i = 0; i < n; i++) result.extend(self); @@ -519,38 +535,37 @@ inline void init_builtins(VM* _vm) { _vm->bind_method<0>("list", "copy", CPP_LAMBDA(VAR(_CAST(List, args[0])))); - _vm->bind_method<1>("list", "__add__", [](VM* vm, ArgsView args) { - const List& self = _CAST(List&, args[0]); - const List& other = CAST(List&, args[1]); + _vm->bind__hash__(_vm->tp_list, [](VM* vm, PyObject* obj) { + vm->TypeError("unhashable type: 'list'"); + return (i64)0; + }); + + _vm->bind__add__(_vm->tp_list, [](VM* vm, PyObject* lhs, PyObject* rhs) { + const List& self = _CAST(List&, lhs); + const List& other = CAST(List&, rhs); List new_list(self); // copy construct new_list.extend(other); return VAR(std::move(new_list)); }); - _vm->bind_method<0>("list", "__len__", [](VM* vm, ArgsView args) { - const List& self = _CAST(List&, args[0]); - return VAR(self.size()); + _vm->bind__len__(_vm->tp_list, [](VM* vm, PyObject* obj) { + return (i64)_CAST(List&, obj).size(); }); - - _vm->bind_method<0>("list", "__iter__", [](VM* vm, ArgsView args) { - return vm->PyIter(ArrayIter(vm, args[0])); + _vm->bind__iter__(_vm->tp_list, [](VM* vm, PyObject* obj) { + return vm->PyIter(ArrayIter(vm, obj)); }); - - _vm->bind_method<1>("list", "__getitem__", [](VM* vm, ArgsView args) { - return PyArrayGetItem(vm, args[0], args[1]); + _vm->bind__getitem__(_vm->tp_list, PyArrayGetItem); + _vm->bind__setitem__(_vm->tp_list, [](VM* vm, PyObject* obj, PyObject* index, PyObject* value){ + List& self = _CAST(List&, obj); + int i = CAST(int, index); + i = vm->normalized_index(i, self.size()); + self[i] = value; }); - _vm->bind_method<2>("list", "__setitem__", [](VM* vm, ArgsView args) { - return PyListSetItem(vm, args[0], args[1], args[2]); - }); - _vm->_type_info("list")->m__getitem__ = PyArrayGetItem; - _vm->_type_info("list")->m__setitem__ = PyListSetItem; - - _vm->bind_method<1>("list", "__delitem__", [](VM* vm, ArgsView args) { - List& self = _CAST(List&, args[0]); - int index = CAST(int, args[1]); - index = vm->normalized_index(index, self.size()); - self.erase(index); - return vm->None; + _vm->bind__delitem__(_vm->tp_list, [](VM* vm, PyObject* obj, PyObject* index){ + List& self = _CAST(List&, obj); + int i = CAST(int, index); + i = vm->normalized_index(i, self.size()); + self.erase(i); }); /************ PyTuple ************/ @@ -559,40 +574,53 @@ inline void init_builtins(VM* _vm) { return VAR(Tuple(std::move(list))); }); - _vm->bind_method<0>("tuple", "__iter__", [](VM* vm, ArgsView args) { - return vm->PyIter(ArrayIter(vm, args[0])); + _vm->bind__hash__(_vm->tp_tuple, [](VM* vm, PyObject* obj) { + i64 x = 1000003; + const Tuple& items = CAST(Tuple&, obj); + for (int i=0; ipy_hash(items[i]); + // recommended by Github Copilot + x = x ^ (y + 0x9e3779b9 + (x << 6) + (x >> 2)); + } + return x; }); - _vm->bind_method<1>("tuple", "__getitem__", [](VM* vm, ArgsView args) { - return PyArrayGetItem(vm, args[0], args[1]); + _vm->bind__iter__(_vm->tp_tuple, [](VM* vm, PyObject* self) { + return vm->PyIter(ArrayIter(vm, self)); }); - _vm->_type_info("tuple")->m__getitem__ = PyArrayGetItem; - - _vm->bind_method<0>("tuple", "__len__", [](VM* vm, ArgsView args) { - const Tuple& self = _CAST(Tuple&, args[0]); - return VAR(self.size()); + _vm->bind__getitem__(_vm->tp_tuple, PyArrayGetItem); + _vm->bind__len__(_vm->tp_tuple, [](VM* vm, PyObject* self) { + const Tuple& tuple = _CAST(Tuple&, self); + return (i64)tuple.size(); }); /************ bool ************/ _vm->bind_constructor<2>("bool", CPP_LAMBDA(VAR(vm->asBool(args[1])))); - - _vm->bind_method<0>("bool", "__repr__", [](VM* vm, ArgsView args) { - bool val = _CAST(bool, args[0]); + _vm->bind__hash__(_vm->tp_bool, [](VM* vm, PyObject* obj) { + return (i64)_CAST(bool, obj); + }); + _vm->bind__repr__(_vm->tp_bool, [](VM* vm, PyObject* self) { + bool val = _CAST(bool, self); return VAR(val ? "True" : "False"); }); - - _vm->bind_method<0>("bool", "__json__", [](VM* vm, ArgsView args) { - bool val = _CAST(bool, args[0]); + _vm->bind__json__(_vm->tp_bool, [](VM* vm, PyObject* self) { + bool val = _CAST(bool, self); return VAR(val ? "true" : "false"); }); - _vm->bind_method<1>("bool", "__xor__", [](VM* vm, ArgsView args) { - bool self = _CAST(bool, args[0]); - bool other = CAST(bool, args[1]); - return VAR(self ^ other); + _vm->bind__and__(_vm->tp_bool, [](VM* vm, PyObject* lhs, PyObject* rhs) { + return VAR(_CAST(bool, lhs) && CAST(bool, rhs)); + }); + _vm->bind__or__(_vm->tp_bool, [](VM* vm, PyObject* lhs, PyObject* rhs) { + return VAR(_CAST(bool, lhs) || CAST(bool, rhs)); + }); + _vm->bind__xor__(_vm->tp_bool, [](VM* vm, PyObject* lhs, PyObject* rhs) { + return VAR(_CAST(bool, lhs) != CAST(bool, rhs)); }); - _vm->bind_method<0>("ellipsis", "__repr__", CPP_LAMBDA(VAR("Ellipsis"))); + _vm->bind__repr__(_vm->_type("ellipsis"), [](VM* vm, PyObject* self) { + return VAR("Ellipsis"); + }); /************ bytes ************/ _vm->bind_constructor<2>("bytes", [](VM* vm, ArgsView args){ @@ -606,15 +634,20 @@ inline void init_builtins(VM* _vm) { return VAR(Bytes(std::move(buffer))); }); - _vm->bind_method<1>("bytes", "__getitem__", [](VM* vm, ArgsView args) { - const Bytes& self = _CAST(Bytes&, args[0]); - int index = CAST(int, args[1]); - index = vm->normalized_index(index, self.size()); - return VAR(self[index]); + _vm->bind__getitem__(_vm->tp_bytes, [](VM* vm, PyObject* obj, PyObject* index) { + const Bytes& self = _CAST(Bytes&, obj); + int i = CAST(int, index); + i = vm->normalized_index(i, self.size()); + return VAR(self[i]); }); - _vm->bind_method<0>("bytes", "__repr__", [](VM* vm, ArgsView args) { - const Bytes& self = _CAST(Bytes&, args[0]); + _vm->bind__hash__(_vm->tp_bytes, [](VM* vm, PyObject* obj) { + const Bytes& self = _CAST(Bytes&, obj); + return (i64)std::hash()(self.str()); + }); + + _vm->bind__repr__(_vm->tp_bytes, [](VM* vm, PyObject* obj) { + const Bytes& self = _CAST(Bytes&, obj); std::stringstream ss; ss << "b'"; for(int i=0; ibind_method<0>("bytes", "__len__", [](VM* vm, ArgsView args) { - const Bytes& self = _CAST(Bytes&, args[0]); - return VAR(self.size()); + _vm->bind__len__(_vm->tp_bytes, [](VM* vm, PyObject* obj) { + return (i64)_CAST(Bytes&, obj).size(); }); _vm->bind_method<0>("bytes", "decode", [](VM* vm, ArgsView args) { @@ -662,8 +693,8 @@ inline void init_builtins(VM* _vm) { return VAR(Slice(args[1], args[2], args[3])); }); - _vm->bind_method<0>("slice", "__repr__", [](VM* vm, ArgsView args) { - const Slice& self = _CAST(Slice&, args[0]); + _vm->bind__repr__(_vm->tp_slice, [](VM* vm, PyObject* obj) { + const Slice& self = _CAST(Slice&, obj); std::stringstream ss; ss << "slice("; ss << CAST(Str, vm->asRepr(self.start)) << ", "; @@ -697,21 +728,20 @@ inline void init_builtins(VM* _vm) { return VAR(std::move(items)); }); - _vm->bind_method<0>("mappingproxy", "__len__", [](VM* vm, ArgsView args) { - MappingProxy& self = _CAST(MappingProxy&, args[0]); - return VAR(self.attr().size()); + _vm->bind__len__(_vm->tp_mappingproxy, [](VM* vm, PyObject* obj) { + return (i64)_CAST(MappingProxy&, obj).attr().size(); }); - _vm->bind_method<1>("mappingproxy", "__getitem__", [](VM* vm, ArgsView args) { - MappingProxy& self = _CAST(MappingProxy&, args[0]); - StrName key = CAST(Str&, args[1]); + _vm->bind__getitem__(_vm->tp_mappingproxy, [](VM* vm, PyObject* obj, PyObject* index) { + MappingProxy& self = _CAST(MappingProxy&, obj); + StrName key = CAST(Str&, index); PyObject* ret = self.attr().try_get(key); if(ret == nullptr) vm->AttributeError(key.sv()); return ret; }); - _vm->bind_method<0>("mappingproxy", "__repr__", [](VM* vm, ArgsView args) { - MappingProxy& self = _CAST(MappingProxy&, args[0]); + _vm->bind__repr__(_vm->tp_mappingproxy, [](VM* vm, PyObject* obj) { + MappingProxy& self = _CAST(MappingProxy&, obj); std::stringstream ss; ss << "mappingproxy({"; bool first = true; @@ -724,10 +754,9 @@ inline void init_builtins(VM* _vm) { return VAR(ss.str()); }); - _vm->bind_method<1>("mappingproxy", "__contains__", [](VM* vm, ArgsView args) { - MappingProxy& self = _CAST(MappingProxy&, args[0]); - StrName key = CAST(Str&, args[1]); - return VAR(self.attr().contains(key)); + _vm->bind__contains__(_vm->tp_mappingproxy, [](VM* vm, PyObject* obj, PyObject* key) { + MappingProxy& self = _CAST(MappingProxy&, obj); + return self.attr().contains(CAST(Str&, key)); }); } @@ -1027,7 +1056,7 @@ inline void VM::post_init(){ const PyTypeInfo& info = vm->_all_types[OBJ_GET(Type, args[0])]; return info.base.index == -1 ? vm->None : vm->_all_types[info.base].obj; })); - _t(tp_type)->attr().set(__name__, property([](VM* vm, ArgsView args){ + _t(tp_type)->attr().set("__name__", property([](VM* vm, ArgsView args){ const PyTypeInfo& info = vm->_all_types[OBJ_GET(Type, args[0])]; return VAR(info.name); })); diff --git a/src/str.h b/src/str.h index 4d680e32..91b956f0 100644 --- a/src/str.h +++ b/src/str.h @@ -302,7 +302,6 @@ inline bool is_unicode_Lo_char(uint32_t c) { return c >= kLoRangeA[index] && c <= kLoRangeB[index]; } - struct StrName { uint16_t index; StrName(): index(0) {} @@ -338,8 +337,8 @@ struct StrName { return this->index > other.index; } - static std::map> _interned; - static std::vector _r_interned; + inline static std::map> _interned; + inline static std::vector _r_interned; static StrName get(std::string_view s){ auto it = _interned.find(s); @@ -378,24 +377,6 @@ struct FastStrStream{ } }; -inline std::map> StrName::_interned; -inline std::vector StrName::_r_interned; - -// other specials -const StrName __init__ = StrName::get("__init__"); -const StrName __call__ = StrName::get("__call__"); -const StrName __doc__ = StrName::get("__doc__"); -const StrName __enter__ = StrName::get("__enter__"); -const StrName __exit__ = StrName::get("__exit__"); -const StrName __name__ = StrName::get("__name__"); -const StrName __get__ = StrName::get("__get__"); -const StrName __set__ = StrName::get("__set__"); - -// special names -const StrName m_dict = StrName::get("dict"); -const StrName m_set = StrName::get("set"); -const StrName m_add = StrName::get("add"); - // unary operators const StrName __repr__ = StrName::get("__repr__"); const StrName __str__ = StrName::get("__str__"); diff --git a/src/vm.h b/src/vm.h index efc35bf5..a92909bd 100644 --- a/src/vm.h +++ b/src/vm.h @@ -71,8 +71,8 @@ struct PyTypeInfo{ // unary operators PyObject* (*m__repr__)(VM* vm, PyObject*) = nullptr; PyObject* (*m__str__)(VM* vm, PyObject*) = nullptr; - PyObject* (*m__hash__)(VM* vm, PyObject*) = nullptr; - PyObject* (*m__len__)(VM* vm, PyObject*) = nullptr; + i64 (*m__hash__)(VM* vm, PyObject*) = nullptr; + i64 (*m__len__)(VM* vm, PyObject*) = nullptr; PyObject* (*m__iter__)(VM* vm, PyObject*) = nullptr; PyObject* (*m__next__)(VM* vm, PyObject*) = nullptr; PyObject* (*m__json__)(VM* vm, PyObject*) = nullptr; @@ -298,8 +298,9 @@ public: return OBJ_GET(Type, obj); } - PyObject* _find_type(const Str& type){ + PyObject* _find_type_object(const Str& type){ PyObject* obj = builtins->attr().try_get(type); + check_non_tagged_type(obj, tp_type); if(obj == nullptr){ for(auto& t: _all_types) if(t.name == type) return t.obj; throw std::runtime_error(fmt("type not found: ", type)); @@ -307,6 +308,11 @@ public: return obj; } + Type _type(const Str& type){ + PyObject* obj = _find_type_object(type); + return OBJ_GET(Type, obj); + } + PyTypeInfo* _type_info(const Str& type){ PyObject* obj = builtins->attr().try_get(type); if(obj == nullptr){ @@ -322,7 +328,7 @@ public: return &_all_types[obj->type]; } -#define BIND_UNARY_SPECIAL(name) \ +#define BIND_UNARY_SPECIAL(name) \ void bind##name(Type type, PyObject* (*f)(VM* vm, PyObject*)){ \ PyObject* obj = _t(type); \ _all_types[type].m##name = f; \ @@ -333,14 +339,14 @@ public: BIND_UNARY_SPECIAL(__repr__) BIND_UNARY_SPECIAL(__str__) - BIND_UNARY_SPECIAL(__hash__) - BIND_UNARY_SPECIAL(__len__) BIND_UNARY_SPECIAL(__iter__) BIND_UNARY_SPECIAL(__next__) BIND_UNARY_SPECIAL(__json__) BIND_UNARY_SPECIAL(__neg__) BIND_UNARY_SPECIAL(__bool__) + void bind__hash__(Type type, i64 (*f)(VM* vm, PyObject*)); + void bind__len__(Type type, i64 (*f)(VM* vm, PyObject*)); #undef BIND_UNARY_SPECIAL @@ -433,12 +439,12 @@ public: template void bind_func(Str type, Str name, NativeFuncC fn) { - bind_func(_find_type(type), name, fn); + bind_func(_find_type_object(type), name, fn); } template void bind_method(Str type, Str name, NativeFuncC fn) { - bind_method(_find_type(type), name, fn); + bind_method(_find_type_object(type), name, fn); } template @@ -554,7 +560,7 @@ public: PyObject* num_negated(PyObject* obj); f64 num_to_float(PyObject* obj); bool asBool(PyObject* obj); - i64 hash(PyObject* obj); + i64 py_hash(PyObject* obj); PyObject* asRepr(PyObject*); PyObject* asList(PyObject*); PyObject* new_module(StrName name); @@ -795,28 +801,11 @@ inline void VM::parse_int_slice(const Slice& s, int length, int& start, int& sto } } -inline i64 VM::hash(PyObject* obj){ - if (is_non_tagged_type(obj, tp_str)) return CAST(Str&, obj).hash(); - if (is_int(obj)) return CAST(i64, obj); - if (is_non_tagged_type(obj, tp_tuple)) { - i64 x = 1000003; - const Tuple& items = CAST(Tuple&, obj); - for (int i=0; i> 2)); - } - return x; - } - if (is_non_tagged_type(obj, tp_type)) return BITS(obj); - if (is_non_tagged_type(obj, tp_iterator)) return BITS(obj); - if (is_non_tagged_type(obj, tp_bool)) return _CAST(bool, obj) ? 1 : 0; - if (is_float(obj)){ - f64 val = CAST(f64, obj); - return (i64)std::hash()(val); - } - TypeError("unhashable type: " + OBJ_NAME(_t(obj)).escape()); - return 0; +inline i64 VM::py_hash(PyObject* obj){ + const PyTypeInfo* ti = _inst_type_info(obj); + if(ti->m__hash__) return ti->m__hash__(this, obj); + PyObject* ret = call_method(obj, __hash__); + return CAST(i64, ret); } inline PyObject* VM::asRepr(PyObject* obj){ @@ -891,10 +880,10 @@ inline PyObject* VM::format(Str spec, PyObject* obj){ inline PyObject* VM::new_module(StrName name) { PyObject* obj = heap._new(tp_module, DummyModule()); - obj->attr().set(__name__, VAR(name.sv())); + obj->attr().set("__name__", VAR(name.sv())); // we do not allow override in order to avoid memory leak // it is because Module objects are not garbage collected - if(_modules.contains(name)) FATAL_ERROR(); + if(_modules.contains(name)) throw std::runtime_error("module already exists"); _modules.set(name, obj); return obj; } @@ -1128,9 +1117,8 @@ inline PyObject* VM::vectorcall(int ARGC, int KWARGC, bool op_call){ if(method_call) FATAL_ERROR(); // [type, NULL, args..., kwargs...] - // __new__ - const static StrName m_new("__new__"); - PyObject* new_f = find_name_in_mro(callable, m_new); + const static StrName __new__("__new__"); + PyObject* new_f = find_name_in_mro(callable, __new__); PyObject* obj; if(new_f != nullptr){ PUSH(new_f); @@ -1148,6 +1136,7 @@ inline PyObject* VM::vectorcall(int ARGC, int KWARGC, bool op_call){ // __init__ PyObject* self; + const static StrName __init__("__init__"); callable = get_unbound_method(obj, __init__, &self, false); if (self != PY_NULL) { // replace `NULL` with `self` @@ -1166,6 +1155,7 @@ inline PyObject* VM::vectorcall(int ARGC, int KWARGC, bool op_call){ // handle `__call__` overload PyObject* self; + const static StrName __call__("__call__"); PyObject* call_f = get_unbound_method(callable, __call__, &self, false); if(self != PY_NULL){ p1[-(ARGC + 2)] = call_f; @@ -1255,6 +1245,9 @@ inline PyObject* VM::_py_call(PyObject** p0, PyObject* callable, ArgsView args, return nullptr; } +const static StrName __get__("__get__"); +const static StrName __set__("__set__"); + // https://docs.python.org/3/howto/descriptor.html#invocation-from-an-instance inline PyObject* VM::getattr(PyObject* obj, StrName name, bool throw_err){ PyObject* objtype = _t(obj); @@ -1352,11 +1345,17 @@ inline void VM::setattr(PyObject* obj, StrName name, PyObject* value){ template void VM::bind_method(PyObject* obj, Str name, NativeFuncC fn) { check_non_tagged_type(obj, tp_type); + if(obj->attr().contains(name)){ + throw std::runtime_error(fmt("bind_method() failed: ", name.escape(), " already exists")); + } obj->attr().set(name, VAR(NativeFunc(fn, ARGC, true))); } template void VM::bind_func(PyObject* obj, Str name, NativeFuncC fn) { + if(obj->attr().contains(name)){ + throw std::runtime_error(fmt("bind_func() failed: ", name.escape(), " already exists")); + } obj->attr().set(name, VAR(NativeFunc(fn, ARGC, false))); } @@ -1404,32 +1403,28 @@ PyObject* PyArrayGetItem(VM* vm, PyObject* obj, PyObject* index){ return self[i]; } -inline PyObject* PyListSetItem(VM* vm, PyObject* obj, PyObject* index, PyObject* value){ - List& self = _CAST(List&, obj); - int i = CAST(int, index); - i = vm->normalized_index(i, self.size()); - self[i] = value; - return vm->None; +inline void VM::bind__hash__(Type type, i64 (*f)(VM* vm, PyObject*)){ + PyObject* obj = _t(type); + _all_types[type].m__hash__ = f; + bind_method<0>(obj, "__hash__", [](VM* vm, ArgsView args){ + i64 ret = vm->_inst_type_info(args[0])->m__hash__(vm, args[0]); + return VAR(ret); + }); } -inline PyObject* PyStrGetItem(VM* vm, PyObject* obj, PyObject* index){ - const Str& self = _CAST(Str&, obj); - - if(is_type(index, vm->tp_slice)){ - const Slice& s = _CAST(Slice&, index); - int start, stop, step; - vm->parse_int_slice(s, self.u8_length(), start, stop, step); - return VAR(self.u8_slice(start, stop, step)); - } - - int i = CAST(int, index); - i = vm->normalized_index(i, self.u8_length()); - return VAR(self.u8_getitem(i)); +inline void VM::bind__len__(Type type, i64 (*f)(VM* vm, PyObject*)){ + PyObject* obj = _t(type); + _all_types[type].m__len__ = f; + bind_method<0>(obj, "__len__", [](VM* vm, ArgsView args){ + i64 ret = vm->_inst_type_info(args[0])->m__len__(vm, args[0]); + return VAR(ret); + }); } + inline void Dict::_probe(PyObject *key, bool &ok, int &i) const{ ok = false; - i = vm->hash(key) & _mask; + i = vm->py_hash(key) & _mask; while(_items[i].first != nullptr) { if(vm->py_equals(_items[i].first, key)) { ok = true; break; } i = (i + 1) & _mask;