From a12eb4c8bc750fae6f8cc332ef2b6e269a355199 Mon Sep 17 00:00:00 2001 From: blueloveTH Date: Sat, 28 Oct 2023 15:00:53 +0800 Subject: [PATCH] ... --- docs/cheatsheet.md | 2 +- include/pocketpy/common.h | 2 +- include/pocketpy/vm.h | 7 +++- python/builtins.py | 32 ------------------ src/ceval.cpp | 70 +++++++++++++++++++++++++++++++-------- src/collections.cpp | 18 +++++----- src/pocketpy.cpp | 42 ++++++++++++++++++----- src/vm.cpp | 6 ++-- tests/70_collections.py | 3 -- tests/99_builtin_func.py | 8 ++--- 10 files changed, 113 insertions(+), 77 deletions(-) diff --git a/docs/cheatsheet.md b/docs/cheatsheet.md index e3020917..1bc12475 100644 --- a/docs/cheatsheet.md +++ b/docs/cheatsheet.md @@ -231,7 +231,7 @@ Compare two python objects ```cpp PyObject* obj1 = py_var(vm, 1); PyObject* obj2 = py_var(vm, 2); -bool ok = vm->py_equals(obj1, obj2); +bool ok = vm->py_eq(obj1, obj2); ``` Convert a python object to string diff --git a/include/pocketpy/common.h b/include/pocketpy/common.h index 45afac97..2894d61f 100644 --- a/include/pocketpy/common.h +++ b/include/pocketpy/common.h @@ -23,7 +23,7 @@ #include #include -#define PK_VERSION "1.2.7" +#define PK_VERSION "1.2.9" #include "config.h" #include "export.h" diff --git a/include/pocketpy/vm.h b/include/pocketpy/vm.h index 8493e51f..8ab0710d 100644 --- a/include/pocketpy/vm.h +++ b/include/pocketpy/vm.h @@ -304,7 +304,12 @@ public: PK_OBJ_GET(NativeFunc, nf).set_userdata(f); } - bool py_equals(PyObject* lhs, PyObject* rhs); + bool py_eq(PyObject* lhs, PyObject* rhs); + // new in v1.2.9 + bool py_lt(PyObject* lhs, PyObject* rhs); + bool py_le(PyObject* lhs, PyObject* rhs); + bool py_gt(PyObject* lhs, PyObject* rhs); + bool py_ge(PyObject* lhs, PyObject* rhs); template PyObject* bind_func(Str type, Str name, NativeFuncC fn) { diff --git a/python/builtins.py b/python/builtins.py index c748af1c..a4eb7cc0 100644 --- a/python/builtins.py +++ b/python/builtins.py @@ -286,38 +286,6 @@ def __f(self, reverse=False, key=None): self.reverse() list.sort = __f -def __f(self, other): - for i, j in zip(self, other): - if i != j: - return i < j - return len(self) < len(other) -tuple.__lt__ = __f -list.__lt__ = __f - -def __f(self, other): - for i, j in zip(self, other): - if i != j: - return i > j - return len(self) > len(other) -tuple.__gt__ = __f -list.__gt__ = __f - -def __f(self, other): - for i, j in zip(self, other): - if i != j: - return i <= j - return len(self) <= len(other) -tuple.__le__ = __f -list.__le__ = __f - -def __f(self, other): - for i, j in zip(self, other): - if i != j: - return i >= j - return len(self) >= len(other) -tuple.__ge__ = __f -list.__ge__ = __f - type.__repr__ = lambda self: "" type.__getitem__ = lambda self, *args: self # for generics diff --git a/src/ceval.cpp b/src/ceval.cpp index 14103873..c785b92b 100644 --- a/src/ceval.cpp +++ b/src/ceval.cpp @@ -2,6 +2,48 @@ namespace pkpy{ +#define BINARY_F_COMPARE(func, op, rfunc) \ + PyObject* ret; \ + const PyTypeInfo* _ti = _inst_type_info(_0); \ + if(_ti->m##func){ \ + ret = _ti->m##func(this, _0, _1); \ + }else{ \ + PyObject* self; \ + PyObject* _2 = get_unbound_method(_0, func, &self, false); \ + if(_2 != nullptr) ret = call_method(self, _2, _1); \ + else ret = NotImplemented; \ + } \ + if(ret == NotImplemented){ \ + PyObject* self; \ + PyObject* _2 = get_unbound_method(_1, rfunc, &self, false); \ + if(_2 != nullptr) ret = call_method(self, _2, _0); \ + else BinaryOptError(op); \ + if(ret == NotImplemented) BinaryOptError(op); \ + } + + +bool VM::py_lt(PyObject* _0, PyObject* _1){ + BINARY_F_COMPARE(__lt__, "<", __gt__); + return CAST(bool, ret); +} + +bool VM::py_le(PyObject* _0, PyObject* _1){ + BINARY_F_COMPARE(__le__, "<=", __ge__); + return CAST(bool, ret); +} + +bool VM::py_gt(PyObject* _0, PyObject* _1){ + BINARY_F_COMPARE(__gt__, ">", __lt__); + return CAST(bool, ret); +} + +bool VM::py_ge(PyObject* _0, PyObject* _1){ + BINARY_F_COMPARE(__ge__, ">=", __le__); + return CAST(bool, ret); +} + +#undef BINARY_F_COMPARE + static i64 _py_sint(PyObject* obj) noexcept { return (i64)(PK_BITS(obj) >> 2); } @@ -386,34 +428,34 @@ __NEXT_STEP:; if(TOP() == NotImplemented) BinaryOptError("%"); } DISPATCH() TARGET(COMPARE_LT){ - PyObject* _0; PyObject* _1; const PyTypeInfo* _ti; - BINARY_OP_SPECIAL(__lt__); - BINARY_OP_RSPECIAL("<", __gt__); + PyObject* _1 = POPX(); + PyObject* _0 = TOP(); + TOP() = VAR(py_lt(_0, _1)); } DISPATCH() TARGET(COMPARE_LE){ - PyObject* _0; PyObject* _1; const PyTypeInfo* _ti; - BINARY_OP_SPECIAL(__le__); - BINARY_OP_RSPECIAL("<=", __ge__); + PyObject* _1 = POPX(); + PyObject* _0 = TOP(); + TOP() = VAR(py_le(_0, _1)); } DISPATCH() TARGET(COMPARE_EQ){ PyObject* _1 = POPX(); PyObject* _0 = TOP(); - TOP() = VAR(py_equals(_0, _1)); + TOP() = VAR(py_eq(_0, _1)); } DISPATCH() TARGET(COMPARE_NE){ PyObject* _1 = POPX(); PyObject* _0 = TOP(); - TOP() = VAR(!py_equals(_0, _1)); + TOP() = VAR(!py_eq(_0, _1)); } DISPATCH() TARGET(COMPARE_GT){ - PyObject* _0; PyObject* _1; const PyTypeInfo* _ti; - BINARY_OP_SPECIAL(__gt__); - BINARY_OP_RSPECIAL(">", __lt__); + PyObject* _1 = POPX(); + PyObject* _0 = TOP(); + TOP() = VAR(py_gt(_0, _1)); } DISPATCH() TARGET(COMPARE_GE){ - PyObject* _0; PyObject* _1; const PyTypeInfo* _ti; - BINARY_OP_SPECIAL(__ge__); - BINARY_OP_RSPECIAL(">=", __le__); + PyObject* _1 = POPX(); + PyObject* _0 = TOP(); + TOP() = VAR(py_ge(_0, _1)); } DISPATCH() TARGET(BITWISE_LSHIFT){ PyObject* _0; PyObject* _1; const PyTypeInfo* _ti; diff --git a/src/collections.cpp b/src/collections.cpp index d5d1f17c..9ad892ba 100644 --- a/src/collections.cpp +++ b/src/collections.cpp @@ -145,7 +145,7 @@ namespace pkpy if (self.dequeItems.size() != other.dequeItems.size()) // trivial case return VAR(false); for (int i = 0; i < self.dequeItems.size(); i++) - if (!vm->py_equals(self.dequeItems[i], other.dequeItems[i])) + if (!vm->py_eq(self.dequeItems[i], other.dequeItems[i])) return VAR(false); return VAR(true); }); @@ -235,7 +235,7 @@ namespace pkpy int cnt = 0, sz = self.dequeItems.size(); for (auto it = self.dequeItems.begin(); it != self.dequeItems.end(); ++it) { - if (vm->py_equals((*it), obj)) + if (vm->py_eq((*it), obj)) cnt++; if (sz != self.dequeItems.size())// mutating the deque during iteration is not allowed vm->RuntimeError("deque mutated during iteration"); @@ -265,9 +265,9 @@ namespace pkpy PyDeque &self = _CAST(PyDeque &, args[0]); PyObject *obj = args[1]; int start = 0, stop = self.dequeItems.size(); // default values - if (!vm->py_equals(args[2], vm->None)) + if (!vm->py_eq(args[2], vm->None)) start = CAST(int, args[2]); - if (!vm->py_equals(args[3], vm->None)) + if (!vm->py_eq(args[3], vm->None)) stop = CAST(int, args[3]); int index = self.findIndex(vm, obj, start, stop); if (index != -1) @@ -398,7 +398,7 @@ namespace pkpy PyDeque::PyDeque(VM *vm, PyObject *iterable, PyObject *maxlen) { - if (!vm->py_equals(maxlen, vm->None)) // fix the maxlen first + if (!vm->py_eq(maxlen, vm->None)) // fix the maxlen first { int tmp = CAST(int, maxlen); if (tmp < 0) @@ -414,7 +414,7 @@ namespace pkpy this->bounded = false; this->maxlen = -1; } - if (!vm->py_equals(iterable, vm->None)) + if (!vm->py_eq(iterable, vm->None)) { this->dequeItems.clear(); // clear the deque auto _lock = vm->heap.gc_scope_lock(); // locking the heap @@ -467,7 +467,7 @@ namespace pkpy int sz = this->dequeItems.size(); for (int i = start; i < loopSize; i++) { - if (vm->py_equals(this->dequeItems[i], obj)) + if (vm->py_eq(this->dequeItems[i], obj)) return i; if (sz != this->dequeItems.size())// mutating the deque during iteration is not allowed vm->RuntimeError("deque mutated during iteration"); @@ -479,7 +479,7 @@ namespace pkpy /// @param front if true, pop from the front of the deque /// @param back if true, pop from the back of the deque /// @param item if front and back is not set, remove the first occurence of item from the deque - /// @param vm is needed for the py_equals + /// @param vm is needed for the py_eq /// @return PyObject* if front or back is set, this is a pop operation and we return a PyObject*, if front and back are not set, this is a remove operation and we return the removed item or nullptr PyObject *PyDeque::popObj(bool front, bool back, PyObject *item, VM *vm) { @@ -510,7 +510,7 @@ namespace pkpy int sz = this->dequeItems.size(); for (auto it = this->dequeItems.begin(); it != this->dequeItems.end(); ++it) { - bool found = vm->py_equals((*it), item); + bool found = vm->py_eq((*it), item); if (sz != this->dequeItems.size()) // mutating the deque during iteration is not allowed vm->IndexError("deque mutated during iteration"); if (found) diff --git a/src/pocketpy.cpp b/src/pocketpy.cpp index de5d6ecd..d3d916bc 100644 --- a/src/pocketpy.cpp +++ b/src/pocketpy.cpp @@ -736,14 +736,14 @@ void init_builtins(VM* _vm) { _vm->bind__contains__(_vm->tp_list, [](VM* vm, PyObject* obj, PyObject* item) { List& self = _CAST(List&, obj); - for(PyObject* i: self) if(vm->py_equals(i, item)) return vm->True; + for(PyObject* i: self) if(vm->py_eq(i, item)) return vm->True; return vm->False; }); _vm->bind_method<1>("list", "count", [](VM* vm, ArgsView args) { List& self = _CAST(List&, args[0]); int count = 0; - for(PyObject* i: self) if(vm->py_equals(i, args[1])) count++; + for(PyObject* i: self) if(vm->py_eq(i, args[1])) count++; return VAR(count); }); @@ -753,7 +753,7 @@ void init_builtins(VM* _vm) { List& b = _CAST(List&, rhs); if(a.size() != b.size()) return vm->False; for(int i=0; ipy_equals(a[i], b[i])) return vm->False; + if(!vm->py_eq(a[i], b[i])) return vm->False; } return vm->True; }); @@ -762,7 +762,7 @@ void init_builtins(VM* _vm) { List& self = _CAST(List&, args[0]); PyObject* obj = args[1]; for(int i=0; ipy_equals(self[i], obj)) return VAR(i); + if(vm->py_eq(self[i], obj)) return VAR(i); } vm->ValueError(_CAST(Str&, vm->py_repr(obj)) + " is not in list"); return vm->None; @@ -772,7 +772,7 @@ void init_builtins(VM* _vm) { List& self = _CAST(List&, args[0]); PyObject* obj = args[1]; for(int i=0; ipy_equals(self[i], obj)){ + if(vm->py_eq(self[i], obj)){ self.erase(i); return vm->None; } @@ -858,6 +858,30 @@ void init_builtins(VM* _vm) { _vm->bind_method<0>("list", "copy", PK_LAMBDA(VAR(_CAST(List, args[0])))); +#define BIND_RICH_CMP(name, op, _t, _T) \ + _vm->bind__##name##__(_vm->_t, [](VM* vm, PyObject* lhs, PyObject* rhs){ \ + if(!is_non_tagged_type(rhs, vm->_t)) return vm->NotImplemented; \ + auto& a = _CAST(_T&, lhs); \ + auto& b = _CAST(_T&, rhs); \ + for(int i=0; ipy_eq(a[i], b[i])) continue; \ + return VAR(vm->py_##name(a[i], b[i])); \ + } \ + return VAR(a.size() op b.size()); \ + }); + + BIND_RICH_CMP(lt, <, tp_list, List) + BIND_RICH_CMP(le, <=, tp_list, List) + BIND_RICH_CMP(gt, >, tp_list, List) + BIND_RICH_CMP(ge, >=, tp_list, List) + + BIND_RICH_CMP(lt, <, tp_tuple, Tuple) + BIND_RICH_CMP(le, <=, tp_tuple, Tuple) + BIND_RICH_CMP(gt, >, tp_tuple, Tuple) + BIND_RICH_CMP(ge, >=, tp_tuple, Tuple) + +#undef BIND_RICH_CMP + _vm->bind__add__(_vm->tp_list, [](VM* vm, PyObject* lhs, PyObject* rhs) { const List& self = _CAST(List&, lhs); const List& other = CAST(List&, rhs); @@ -900,14 +924,14 @@ void init_builtins(VM* _vm) { _vm->bind__contains__(_vm->tp_tuple, [](VM* vm, PyObject* obj, PyObject* item) { Tuple& self = _CAST(Tuple&, obj); - for(PyObject* i: self) if(vm->py_equals(i, item)) return vm->True; + for(PyObject* i: self) if(vm->py_eq(i, item)) return vm->True; return vm->False; }); _vm->bind_method<1>("tuple", "count", [](VM* vm, ArgsView args) { Tuple& self = _CAST(Tuple&, args[0]); int count = 0; - for(PyObject* i: self) if(vm->py_equals(i, args[1])) count++; + for(PyObject* i: self) if(vm->py_eq(i, args[1])) count++; return VAR(count); }); @@ -917,7 +941,7 @@ void init_builtins(VM* _vm) { const Tuple& other = _CAST(Tuple&, rhs); if(self.size() != other.size()) return vm->False; for(int i = 0; i < self.size(); i++) { - if(!vm->py_equals(self[i], other[i])) return vm->False; + if(!vm->py_eq(self[i], other[i])) return vm->False; } return vm->True; }); @@ -1282,7 +1306,7 @@ void init_builtins(VM* _vm) { if(item.first == nullptr) continue; PyObject* value = other.try_get(item.first); if(value == nullptr) return vm->False; - if(!vm->py_equals(item.second, value)) return vm->False; + if(!vm->py_eq(item.second, value)) return vm->False; } return vm->True; }); diff --git a/src/vm.cpp b/src/vm.cpp index 50260316..8489aaa0 100644 --- a/src/vm.cpp +++ b/src/vm.cpp @@ -249,7 +249,7 @@ namespace pkpy{ return &_all_types[obj->type]; } - bool VM::py_equals(PyObject* lhs, PyObject* rhs){ + bool VM::py_eq(PyObject* lhs, PyObject* rhs){ if(lhs == rhs) return true; const PyTypeInfo* ti = _inst_type_info(lhs); PyObject* res; @@ -1230,7 +1230,7 @@ void Dict::_probe_0(PyObject *key, bool &ok, int &i) const{ // std::cout << CAST(Str, vm->py_repr(key)) << " " << hash << " " << i << std::endl; for(int j=0; j<_capacity; j++) { if(_items[i].first != nullptr){ - if(vm->py_equals(_items[i].first, key)) { ok = true; break; } + if(vm->py_eq(_items[i].first, key)) { ok = true; break; } }else{ if(_items[i].second == nullptr) break; } @@ -1244,7 +1244,7 @@ void Dict::_probe_1(PyObject *key, bool &ok, int &i) const{ ok = false; i = vm->py_hash(key) & _mask; while(_items[i].first != nullptr) { - if(vm->py_equals(_items[i].first, key)) { ok = true; break; } + if(vm->py_eq(_items[i].first, key)) { ok = true; break; } // https://github.com/python/cpython/blob/3.8/Objects/dictobject.c#L166 i = ((5*i) + 1) & _mask; } diff --git a/tests/70_collections.py b/tests/70_collections.py index 7d967d01..e9fe02b7 100644 --- a/tests/70_collections.py +++ b/tests/70_collections.py @@ -826,6 +826,3 @@ d = deque() for i in range(100): d.append(1) gc.collect() - - -print('✓', "ALL TEST PASSED!!") diff --git a/tests/99_builtin_func.py b/tests/99_builtin_func.py index 1ee8cd1f..f481690d 100644 --- a/tests/99_builtin_func.py +++ b/tests/99_builtin_func.py @@ -380,7 +380,7 @@ except: # #####: 649: List& self = _CAST(List&, args[0]); # #####: 650: PyObject* obj = args[1]; # #####: 651: for(int i=0; ipy_equals(self[i], obj)) return VAR(i); +# #####: 652: if(vm->py_eq(self[i], obj)) return VAR(i); # -: 653: } # #####: 654: vm->ValueError(_CAST(Str&, vm->py_repr(obj)) + " is not in list"); # #####: 655: return vm->None; @@ -401,7 +401,7 @@ except: # 1: 659: List& self = _CAST(List&, args[0]); # 1: 660: PyObject* obj = args[1]; # 2: 661: for(int i=0; ipy_equals(self[i], obj)){ +# 2: 662: if(vm->py_eq(self[i], obj)){ # 1: 663: self.erase(i); # 1: 664: return vm->None; # -: 665: } @@ -474,7 +474,7 @@ except: # 未完全测试准确性----------------------------------------------- # 118: 793: _vm->bind__contains__(_vm->tp_tuple, [](VM* vm, PyObject* obj, PyObject* item) { # 1: 794: Tuple& self = _CAST(Tuple&, obj); -# 3: 795: for(PyObject* i: self) if(vm->py_equals(i, item)) return vm->True; +# 3: 795: for(PyObject* i: self) if(vm->py_eq(i, item)) return vm->True; # #####: 796: return vm->False; # 1: 797: }); # test tuple.__contains__: @@ -485,7 +485,7 @@ assert (1,2,3).__contains__(5) == False # 116: 799: _vm->bind_method<1>("tuple", "count", [](VM* vm, ArgsView args) { # #####: 800: Tuple& self = _CAST(Tuple&, args[0]); # -: 801: int count = 0; -# #####: 802: for(PyObject* i: self) if(vm->py_equals(i, args[1])) count++; +# #####: 802: for(PyObject* i: self) if(vm->py_eq(i, args[1])) count++; # #####: 803: return VAR(count); # -: 804: }); # test tuple.count: