blueloveTH 2 년 전
부모
커밋
1107cea256
8개의 변경된 파일237개의 추가작업 그리고 138개의 파일을 삭제
  1. 2 1
      docs/features/ub.md
  2. 6 2
      src/ceval.h
  3. 26 10
      src/cffi.h
  4. 14 19
      src/dict.h
  5. 5 11
      src/namedict.h
  6. 52 67
      src/pocketpy.h
  7. 32 26
      src/str.h
  8. 100 2
      src/vm.h

+ 2 - 1
docs/features/ub.md

@@ -9,4 +9,5 @@ These are the undefined behaviours of pkpy. The behaviour of pkpy is undefined i
 2. Call an unbound method with the wrong type of `self`. For example, `int.__add__('1', 2)`.
 3. Use goto statement to jump out of a context block.
 4. Type `T`'s `__new__` returns an object that is not an instance of `T`.
-5. Call `__new__` with a type that is not a subclass of `type`.
+5. Call `__new__` with a type that is not a subclass of `type`.
+6. `__eq__`, `__ne__` or `__contains__`, etc.. returns a value that is not a boolean.

+ 6 - 2
src/ceval.h

@@ -308,10 +308,14 @@ __NEXT_STEP:;
         INT_BINARY_OP(<=, __le__)
         DISPATCH()
     TARGET(COMPARE_EQ)
-        INT_BINARY_OP(==, __eq__)
+        _1 = POPX();
+        _0 = TOP();
+        TOP() = VAR(py_equals(_0, _1));
         DISPATCH()
     TARGET(COMPARE_NE)
-        INT_BINARY_OP(!=, __ne__)
+        _1 = POPX();
+        _0 = TOP();
+        TOP() = VAR(py_not_equals(_0, _1));
         DISPATCH()
     TARGET(COMPARE_GT)
         INT_BINARY_OP(>, __gt__)

+ 26 - 10
src/cffi.h

@@ -11,7 +11,7 @@ namespace pkpy {
         static const StrName __x1(#name);       \
         return OBJ_GET(Type, vm->_modules[__x0]->attr(__x1));               \
     }                                                                       \
-    static void _check_type(VM* vm, PyObject* val){                          \
+    static void _check_type(VM* vm, PyObject* val){                         \
         if(!vm->isinstance(val, T::_type(vm))){                             \
             vm->TypeError("expected '" #mod "." #name "', got " + OBJ_NAME(vm->_t(val)).escape());  \
         }                                                                   \
@@ -39,6 +39,13 @@ struct VoidP{
     VoidP(void* ptr): ptr(ptr), base_offset(1){}
     VoidP(): ptr(nullptr), base_offset(1){}
 
+    bool operator==(const VoidP& other) const {
+        return ptr == other.ptr && base_offset == other.base_offset;
+    }
+    bool operator!=(const VoidP& other) const {
+        return ptr != other.ptr || base_offset != other.base_offset;
+    }
+
     static void _register(VM* vm, PyObject* mod, PyObject* type){
         vm->bind_default_constructor<VoidP>(type);
 
@@ -51,16 +58,25 @@ struct VoidP{
             return VAR(ss.str());
         });
 
-        vm->bind_method<1>(type, "__eq__", [](VM* vm, ArgsView args){
-            VoidP& self = _CAST(VoidP&, args[0]);
-            VoidP& other = CAST(VoidP&, args[1]);
-            return VAR(self.ptr == other.ptr && self.base_offset == other.base_offset);
+        vm->bind__eq__(OBJ_GET(Type, type), [](VM* vm, PyObject* lhs, PyObject* rhs){
+            if(!is_non_tagged_type(rhs, VoidP::_type(vm))) return false;
+            return _CAST(VoidP&, lhs) == _CAST(VoidP&, rhs);
         });
-
-        vm->bind_method<1>(type, "__ne__", [](VM* vm, ArgsView args){
-            VoidP& self = _CAST(VoidP&, args[0]);
-            VoidP& other = CAST(VoidP&, args[1]);
-            return VAR(self.ptr != other.ptr || self.base_offset != other.base_offset);
+        vm->bind__ne__(OBJ_GET(Type, type), [](VM* vm, PyObject* lhs, PyObject* rhs){
+            if(!is_non_tagged_type(rhs, VoidP::_type(vm))) return true;
+            return _CAST(VoidP&, lhs) != _CAST(VoidP&, rhs);
+        });
+        vm->bind__gt__(OBJ_GET(Type, type), [](VM* vm, PyObject* lhs, PyObject* rhs){
+            return _CAST(VoidP&, lhs).ptr > CAST(VoidP&, rhs).ptr;
+        });
+        vm->bind__lt__(OBJ_GET(Type, type), [](VM* vm, PyObject* lhs, PyObject* rhs){
+            return _CAST(VoidP&, lhs).ptr < CAST(VoidP&, rhs).ptr;
+        });
+        vm->bind__ge__(OBJ_GET(Type, type), [](VM* vm, PyObject* lhs, PyObject* rhs){
+            return _CAST(VoidP&, lhs).ptr >= CAST(VoidP&, rhs).ptr;
+        });
+        vm->bind__le__(OBJ_GET(Type, type), [](VM* vm, PyObject* lhs, PyObject* rhs){
+            return _CAST(VoidP&, lhs).ptr <= CAST(VoidP&, rhs).ptr;
         });
 
         vm->bind_method<1>(type, "set_base_offset", [](VM* vm, ArgsView args){

+ 14 - 19
src/dict.h

@@ -5,39 +5,31 @@
 #include "memory.h"
 #include "str.h"
 
+namespace pkpy{
+
 struct Dict{
     using Item = std::pair<PyObject*, PyObject*>;
     static constexpr int __Capacity = 8;
     static constexpr float __LoadFactor = 0.67;
     static_assert(sizeof(Item) * __Capacity <= 128);
 
+    VM* vm;
     int _capacity;
     int _mask;
     int _size;
     int _critical_size;
     Item* _items;
-
-    void _alloc(int cap){
-        _items = (Item*)pool128.alloc(cap * sizeof(Item));
-        memset(_items, 0, cap * sizeof(Item));
-    }
-
-    Dict(): _capacity(__Capacity),
+    
+    Dict(VM* vm): vm(vm), _capacity(__Capacity),
             _mask(__Capacity-1),
             _size(0), _critical_size(__Capacity*__LoadFactor+0.5f){
-        _alloc(__Capacity);
+        _items = (Item*)pool128.alloc(_capacity * sizeof(Item));
+        memset(_items, 0, _capacity * sizeof(Item));
     }
 
     int size() const { return _size; }
 
-    void _probe(PyObject* key, bool& ok, int& i){
-        ok = false;
-        i = PyHash(key) & _mask;
-        while(_items[i].first != nullptr) {
-            if(PyEquals(_items[i].first, key)) { ok = true; break; }
-            i = (i + 1) & _mask;
-        }
-    }
+    void _probe(PyObject* key, bool& ok, int& i) const;
 
     void set(PyObject* key, PyObject* val){
         bool ok; int i;
@@ -56,10 +48,11 @@ struct Dict{
     void _rehash(){
         Item* old_items = _items;
         int old_capacity = _capacity;
-        _capacity = find_next_power_of_2(_capacity * 2);
+        _capacity *= 2;
         _mask = _capacity - 1;
         _critical_size = _capacity * __LoadFactor + 0.5f;
-        _alloc(_capacity);
+        _items = (Item*)pool128.alloc(_capacity * sizeof(Item));
+        memset(_items, 0, _capacity * sizeof(Item));
         for(int i=0; i<old_capacity; i++){
             if(old_items[i].first == nullptr) continue;
             bool ok; int j;
@@ -78,4 +71,6 @@ struct Dict{
     }
 
     ~Dict(){ pool128.dealloc(_items); }
-};
+};
+
+} // namespace pkpy

+ 5 - 11
src/namedict.h

@@ -8,12 +8,6 @@ namespace pkpy{
 
 const std::vector<uint16_t> kHashSeeds = {9629, 43049, 13267, 59509, 39251, 1249, 35803, 54469, 27689, 9719, 34897, 18973, 30661, 19913, 27919, 32143};
 
-inline uint16_t find_next_power_of_2(uint16_t n){
-    uint16_t x = 2;
-    while(x < n) x <<= 1;
-    return x;
-}
-
 #define _hash(key, mask, hash_seed) ( ( (key).index * (hash_seed) >> 8 ) & (mask) )
 
 inline uint16_t find_perfect_hash_seed(uint16_t capacity, const std::vector<StrName>& keys){
@@ -53,10 +47,10 @@ struct NameDictImpl {
         memset(_items, 0, cap * sizeof(Item));
     }
 
-    NameDictImpl(float load_factor=0.67, uint16_t capacity=__Capacity, uint16_t hash_seed=kHashSeeds[0]):
-        _load_factor(load_factor), _capacity(capacity), _size(0), 
-        _hash_seed(hash_seed), _mask(capacity-1) {
-        _alloc(capacity);
+    NameDictImpl(float load_factor=0.67):
+        _load_factor(load_factor), _capacity(__Capacity), _size(0), 
+        _hash_seed(kHashSeeds[0]), _mask(__Capacity-1) {
+        _alloc(__Capacity);
     }
 
     NameDictImpl(const NameDictImpl& other) {
@@ -116,7 +110,7 @@ while(!_items[i].first.empty()) {       \
         Item* old_items = _items;
         uint16_t old_capacity = _capacity;
         if(resize){
-            _capacity = find_next_power_of_2(_capacity * 2);
+            _capacity *= 2;
             _mask = _capacity - 1;
         }
         _alloc(_capacity);

+ 52 - 67
src/pocketpy.h

@@ -1,6 +1,7 @@
 #pragma once
 
 #include "ceval.h"
+#include "common.h"
 #include "compiler.h"
 #include "obj.h"
 #include "repl.h"
@@ -39,24 +40,6 @@ inline CodeObject_ VM::compile(Str source, Str filename, CompileMode mode, bool
     _vm->bind_method<1>("float", #name, [](VM* vm, ArgsView args){                                      \
         return VAR(_CAST(f64, args[0]) op vm->num_to_float(args[1]));                                   \
     });
-
-
-
-#define BIND_NUM_LOGICAL_OPT(name, op, is_eq)                                                           \
-    _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]));                   \
-        if(is_float(args[1])) return VAR(vm->num_to_float(args[0]) op _CAST(f64, args[1]));             \
-        if constexpr(is_eq)   return VAR(args[0] op args[1]);                                           \
-        vm->TypeError("unsupported operand type(s) for " #op );                                         \
-        return vm->None;                                                                                \
-    });                                                                                                 \
-    _vm->bind_method<1>("float", #name, [](VM* vm, ArgsView args){                                      \
-        if(is_float(args[1])) return VAR(_CAST(f64, args[0]) op _CAST(f64, args[1]));                   \
-        if(is_int(args[1]))   return VAR(_CAST(f64, args[0]) op _CAST(i64, args[1]));                   \
-        if constexpr(is_eq)   return VAR(args[0] op args[1]);                                           \
-        vm->TypeError("unsupported operand type(s) for " #op );                                         \
-        return vm->None;                                                                                \
-    });
     
 
 inline void init_builtins(VM* _vm) {
@@ -64,6 +47,22 @@ inline void init_builtins(VM* _vm) {
     BIND_NUM_ARITH_OPT(__sub__, -)
     BIND_NUM_ARITH_OPT(__mul__, *)
 
+#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;                                   \
+        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;                                       \
+        vm->TypeError("unsupported operand type(s) for " #op );                 \
+        return false;                                                           \
+    });
+
     BIND_NUM_LOGICAL_OPT(__lt__, <, false)
     BIND_NUM_LOGICAL_OPT(__le__, <=, false)
     BIND_NUM_LOGICAL_OPT(__gt__, >, false)
@@ -203,8 +202,8 @@ inline void init_builtins(VM* _vm) {
         return VAR(ss.str());
     });
 
-    _vm->bind_method<1>("object", "__eq__", CPP_LAMBDA(VAR(args[0] == args[1])));
-    _vm->bind_method<1>("object", "__ne__", CPP_LAMBDA(VAR(args[0] != args[1])));
+    _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_constructor<2>("type", CPP_LAMBDA(vm->_t(args[1])));
 
@@ -376,16 +375,25 @@ inline void init_builtins(VM* _vm) {
         return VAR(self.escape(false));
     });
 
-    _vm->bind_method<1>("str", "__eq__", [](VM* vm, ArgsView args) {
-        const Str& self = _CAST(Str&, args[0]);
-        if(!is_type(args[1], vm->tp_str)) return VAR(false);
-        return VAR(self == CAST(Str&, args[1]));
+    _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);
     });
-
-    _vm->bind_method<1>("str", "__ne__", [](VM* vm, ArgsView args) {
-        const Str& self = _CAST(Str&, args[0]);
-        if(!is_type(args[1], vm->tp_str)) return VAR(true);
-        return VAR(self != CAST(Str&, args[1]));
+    _vm->bind__ne__(_vm->tp_str, [](VM* vm, PyObject* lhs, PyObject* rhs) {
+        if(!is_non_tagged_type(rhs, vm->tp_str)) return true;
+        return _CAST(Str&, lhs) != _CAST(Str&, rhs);
+    });
+    _vm->bind__gt__(_vm->tp_str, [](VM* vm, PyObject* lhs, PyObject* rhs) {
+        return _CAST(Str&, lhs) > CAST(Str&, rhs);
+    });
+    _vm->bind__lt__(_vm->tp_str, [](VM* vm, PyObject* lhs, PyObject* rhs) {
+        return _CAST(Str&, lhs) < CAST(Str&, rhs);
+    });
+    _vm->bind__ge__(_vm->tp_str, [](VM* vm, PyObject* lhs, PyObject* rhs) {
+        return _CAST(Str&, lhs) >= CAST(Str&, rhs);
+    });
+    _vm->bind__le__(_vm->tp_str, [](VM* vm, PyObject* lhs, PyObject* rhs) {
+        return _CAST(Str&, lhs) <= CAST(Str&, rhs);
     });
 
     _vm->bind_method<1>("str", "__getitem__", [](VM* vm, ArgsView args) {
@@ -393,18 +401,6 @@ inline void init_builtins(VM* _vm) {
     });
     _vm->_type_info("str")->m__getitem__ = PyStrGetItem;
 
-    _vm->bind_method<1>("str", "__gt__", [](VM* vm, ArgsView args) {
-        const Str& self = _CAST(Str&, args[0]);
-        const Str& obj = CAST(Str&, args[1]);
-        return VAR(self > obj);
-    });
-
-    _vm->bind_method<1>("str", "__lt__", [](VM* vm, ArgsView args) {
-        const Str& self = _CAST(Str&, args[0]);
-        const Str& obj = CAST(Str&, args[1]);
-        return VAR(self < obj);
-    });
-
     _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");
         const Str& self = _CAST(Str&, args[0]);
@@ -654,18 +650,11 @@ inline void init_builtins(VM* _vm) {
         return VAR(Bytes(std::move(buffer)));
     });
 
-    _vm->bind_method<1>("bytes", "__eq__", [](VM* vm, ArgsView args) {
-        const Bytes& self = _CAST(Bytes&, args[0]);
-        if(!is_type(args[1], vm->tp_bytes)) return VAR(false);
-        const Bytes& other = CAST(Bytes&, args[1]);
-        return VAR(self == other);
+    _vm->bind__eq__(_vm->tp_bytes, [](VM* vm, PyObject* lhs, PyObject* rhs) {
+        return _CAST(Bytes&, lhs) == _CAST(Bytes&, rhs);
     });
-
-    _vm->bind_method<1>("bytes", "__ne__", [](VM* vm, ArgsView args) {
-        const Bytes& self = _CAST(Bytes&, args[0]);
-        if(!is_type(args[1], vm->tp_bytes)) return VAR(true);
-        const Bytes& other = CAST(Bytes&, args[1]);
-        return VAR(self != other);
+    _vm->bind__ne__(_vm->tp_bytes, [](VM* vm, PyObject* lhs, PyObject* rhs) {
+        return _CAST(Bytes&, lhs) != _CAST(Bytes&, rhs);
     });
 
     /************ slice ************/
@@ -886,7 +875,7 @@ struct ReMatch {
     ReMatch(i64 start, i64 end, std::cmatch m) : start(start), end(end), m(m) {}
 
     static void _register(VM* vm, PyObject* mod, PyObject* type){
-        vm->bind_method<-1>(type, "__init__", CPP_NOT_IMPLEMENTED());
+        vm->bind_constructor<-1>(type, CPP_NOT_IMPLEMENTED());
         vm->bind_method<0>(type, "start", CPP_LAMBDA(VAR(_CAST(ReMatch&, args[0]).start)));
         vm->bind_method<0>(type, "end", CPP_LAMBDA(VAR(_CAST(ReMatch&, args[0]).end)));
 
@@ -1033,8 +1022,8 @@ inline void VM::post_init(){
     this->_exec(code, this->builtins);
 
     // property is defined in builtins.py so we need to add it after builtins is loaded
-    _t(tp_object)->attr().set(__class__, property(CPP_LAMBDA(vm->_t(args[0]))));
-    _t(tp_type)->attr().set(__base__, property([](VM* vm, ArgsView args){
+    _t(tp_object)->attr().set("__class__", property(CPP_LAMBDA(vm->_t(args[0]))));
+    _t(tp_type)->attr().set("__base__", property([](VM* vm, ArgsView args){
         const PyTypeInfo& info = vm->_all_types[OBJ_GET(Type, args[0])];
         return info.base.index == -1 ? vm->None : vm->_all_types[info.base].obj;
     }));
@@ -1050,16 +1039,13 @@ inline void VM::post_init(){
         return CAST(BoundMethod&, args[0]).func;
     }));
 
-    vm->bind_method<1>(_t(tp_bound_method), "__eq__", [](VM* vm, ArgsView args){
-        if(!is_non_tagged_type(args[1], vm->tp_bound_method)) return vm->False;
-        bool ok = _CAST(BoundMethod&, args[0]) == _CAST(BoundMethod&, args[1]);
-        return VAR(ok);
+    bind__eq__(tp_bound_method, [](VM* vm, PyObject* lhs, PyObject* rhs){
+        if(!is_non_tagged_type(rhs, vm->tp_bound_method)) return false;
+        return _CAST(BoundMethod&, lhs) == _CAST(BoundMethod&, rhs);
     });
-
-    vm->bind_method<1>(_t(tp_bound_method), "__ne__", [](VM* vm, ArgsView args){
-        if(!is_non_tagged_type(args[1], vm->tp_bound_method)) return vm->True;
-        bool ok = _CAST(BoundMethod&, args[0]) != _CAST(BoundMethod&, args[1]);
-        return VAR(ok);
+    bind__ne__(tp_bound_method, [](VM* vm, PyObject* lhs, PyObject* rhs){
+        if(!is_non_tagged_type(rhs, vm->tp_bound_method)) return true;
+        return _CAST(BoundMethod&, lhs) != _CAST(BoundMethod&, rhs);
     });
 
     _t(tp_slice)->attr().set("start", property([](VM* vm, ArgsView args){
@@ -1071,10 +1057,9 @@ inline void VM::post_init(){
     _t(tp_slice)->attr().set("step", property([](VM* vm, ArgsView args){
         return CAST(Slice&, args[0]).step;
     }));
+
     _t(tp_object)->attr().set("__dict__", property([](VM* vm, ArgsView args){
-        if(is_tagged(args[0]) || !args[0]->is_attr_valid()){
-            vm->AttributeError("__dict__");
-        }
+        if(is_tagged(args[0]) || !args[0]->is_attr_valid()) vm->AttributeError("__dict__");
         return VAR(MappingProxy(args[0]));
     }));
 

+ 32 - 26
src/str.h

@@ -381,34 +381,42 @@ struct FastStrStream{
 inline std::map<Str, uint16_t, std::less<>> StrName::_interned;
 inline std::vector<Str> StrName::_r_interned;
 
-const StrName __class__ = StrName::get("__class__");
-const StrName __base__ = StrName::get("__base__");
-const StrName __iter__ = StrName::get("__iter__");
-const StrName __next__ = StrName::get("__next__");
-const StrName __str__ = StrName::get("__str__");
-const StrName __repr__ = StrName::get("__repr__");
-const StrName __getitem__ = StrName::get("__getitem__");
-const StrName __setitem__ = StrName::get("__setitem__");
-const StrName __delitem__ = StrName::get("__delitem__");
-const StrName __contains__ = StrName::get("__contains__");
+// other specials
 const StrName __init__ = StrName::get("__init__");
-const StrName __json__ = StrName::get("__json__");
+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 __len__ = StrName::get("__len__");
 const StrName __get__ = StrName::get("__get__");
 const StrName __set__ = StrName::get("__set__");
-const StrName __getattr__ = StrName::get("__getattr__");
-const StrName __setattr__ = StrName::get("__setattr__");
-const StrName __call__ = StrName::get("__call__");
-const StrName __doc__ = StrName::get("__doc__");
 
-const StrName m_self = StrName::get("self");
+// special names
 const StrName m_dict = StrName::get("dict");
 const StrName m_set = StrName::get("set");
 const StrName m_add = StrName::get("add");
-const StrName __enter__ = StrName::get("__enter__");
-const StrName __exit__ = StrName::get("__exit__");
 
+// unary operators
+const StrName __repr__ = StrName::get("__repr__");
+const StrName __str__ = StrName::get("__str__");
+const StrName __hash__ = StrName::get("__hash__");      // unused
+const StrName __len__ = StrName::get("__len__");
+const StrName __iter__ = StrName::get("__iter__");
+const StrName __next__ = StrName::get("__next__");      // unused
+const StrName __json__ = StrName::get("__json__");
+const StrName __neg__ = StrName::get("__neg__");        // unused
+const StrName __bool__ = StrName::get("__bool__");      // unused
+
+// logical operators
+const StrName __eq__ = StrName::get("__eq__");
+const StrName __ne__ = StrName::get("__ne__");
+const StrName __lt__ = StrName::get("__lt__");
+const StrName __le__ = StrName::get("__le__");
+const StrName __gt__ = StrName::get("__gt__");
+const StrName __ge__ = StrName::get("__ge__");
+const StrName __contains__ = StrName::get("__contains__");
+
+// binary operators
 const StrName __add__ = StrName::get("__add__");
 const StrName __sub__ = StrName::get("__sub__");
 const StrName __mul__ = StrName::get("__mul__");
@@ -418,17 +426,15 @@ const StrName __mod__ = StrName::get("__mod__");
 const StrName __pow__ = StrName::get("__pow__");
 const StrName __matmul__ = StrName::get("__matmul__");
 
-const StrName __lt__ = StrName::get("__lt__");
-const StrName __le__ = StrName::get("__le__");
-const StrName __eq__ = StrName::get("__eq__");
-const StrName __ne__ = StrName::get("__ne__");
-const StrName __gt__ = StrName::get("__gt__");
-const StrName __ge__ = StrName::get("__ge__");
-
 const StrName __lshift__ = StrName::get("__lshift__");
 const StrName __rshift__ = StrName::get("__rshift__");
 const StrName __and__ = StrName::get("__and__");
 const StrName __or__ = StrName::get("__or__");
 const StrName __xor__ = StrName::get("__xor__");
 
+// indexer
+const StrName __getitem__ = StrName::get("__getitem__");
+const StrName __setitem__ = StrName::get("__setitem__");
+const StrName __delitem__ = StrName::get("__delitem__");
+
 } // namespace pkpy

+ 100 - 2
src/vm.h

@@ -9,6 +9,7 @@
 #include "obj.h"
 #include "str.h"
 #include "tuplelist.h"
+#include "dict.h"
 
 namespace pkpy{
 
@@ -65,9 +66,47 @@ struct PyTypeInfo{
     Type base;
     Str name;
     bool subclass_enabled;
+
     // cached special methods
+    // 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;
+    PyObject* (*m__iter__)(VM* vm, PyObject*) = nullptr;
+    PyObject* (*m__next__)(VM* vm, PyObject*) = nullptr;
+    PyObject* (*m__json__)(VM* vm, PyObject*) = nullptr;
+    PyObject* (*m__neg__)(VM* vm, PyObject*) = nullptr;
+    PyObject* (*m__bool__)(VM* vm, PyObject*) = nullptr;
+
+    bool (*m__eq__)(VM* vm, PyObject*, PyObject*) = nullptr;
+    bool (*m__ne__)(VM* vm, PyObject*, PyObject*) = nullptr;
+    bool (*m__lt__)(VM* vm, PyObject*, PyObject*) = nullptr;
+    bool (*m__le__)(VM* vm, PyObject*, PyObject*) = nullptr;
+    bool (*m__gt__)(VM* vm, PyObject*, PyObject*) = nullptr;
+    bool (*m__ge__)(VM* vm, PyObject*, PyObject*) = nullptr;
+    bool (*m__contains__)(VM* vm, PyObject*, PyObject*) = nullptr;
+
+    // binary operators
+    PyObject* (*m__add__)(VM* vm, PyObject*, PyObject*) = nullptr;
+    PyObject* (*m__sub__)(VM* vm, PyObject*, PyObject*) = nullptr;
+    PyObject* (*m__mul__)(VM* vm, PyObject*, PyObject*) = nullptr;
+    PyObject* (*m__truediv__)(VM* vm, PyObject*, PyObject*) = nullptr;
+    PyObject* (*m__floordiv__)(VM* vm, PyObject*, PyObject*) = nullptr;
+    PyObject* (*m__mod__)(VM* vm, PyObject*, PyObject*) = nullptr;
+    PyObject* (*m__pow__)(VM* vm, PyObject*, PyObject*) = nullptr;
+    PyObject* (*m__matmul__)(VM* vm, PyObject*, PyObject*) = nullptr;
+
+    PyObject* (*m__lshift__)(VM* vm, PyObject*, PyObject*) = nullptr;
+    PyObject* (*m__rshift__)(VM* vm, PyObject*, PyObject*) = nullptr;
+    PyObject* (*m__and__)(VM* vm, PyObject*, PyObject*) = nullptr;
+    PyObject* (*m__or__)(VM* vm, PyObject*, PyObject*) = nullptr;
+    PyObject* (*m__xor__)(VM* vm, PyObject*, PyObject*) = nullptr;
+
+    // indexer
     PyObject* (*m__getitem__)(VM* vm, PyObject*, PyObject*) = nullptr;
     PyObject* (*m__setitem__)(VM* vm, PyObject*, PyObject*, PyObject*) = nullptr;
+    PyObject* (*m__delitem__)(VM* vm, PyObject*, PyObject*) = nullptr;
 };
 
 struct FrameId{
@@ -283,6 +322,55 @@ public:
         return &_all_types[obj->type];
     }
 
+#define BIND_LOGICAL_SPECIAL(name)                                                      \
+    void bind##name(Type type, bool (*f)(VM* vm, PyObject* lhs, PyObject* rhs)){        \
+        PyObject* obj = _t(type);                                                       \
+        _all_types[type].m##name = f;                                                   \
+        bind_method<1>(obj, #name, [](VM* vm, ArgsView args){                           \
+            bool ok = vm->_inst_type_info(args[0])->m##name(vm, args[0], args[1]);      \
+            return ok ? vm->True : vm->False;                                           \
+        });                                                                             \
+    }
+
+    BIND_LOGICAL_SPECIAL(__eq__)
+    BIND_LOGICAL_SPECIAL(__ne__)
+    BIND_LOGICAL_SPECIAL(__lt__)
+    BIND_LOGICAL_SPECIAL(__le__)
+    BIND_LOGICAL_SPECIAL(__gt__)
+    BIND_LOGICAL_SPECIAL(__ge__)
+    BIND_LOGICAL_SPECIAL(__contains__)
+
+#undef BIND_LOGICAL_SPECIAL
+
+
+#define BIND_BINARY_SPECIAL(name)                                                       \
+    void bind##name(Type type, PyObject* (*f)(VM* vm, PyObject* lhs, PyObject* rhs)){   \
+        PyObject* obj = _t(type);                                                       \
+        _all_types[type].m##name = f;                                                   \
+        bind_method<1>(obj, #name, [](VM* vm, ArgsView args){                           \
+            return vm->_inst_type_info(args[0])->m##name(vm, args[0], args[1]);         \
+        });                                                                             \
+    }
+
+    BIND_BINARY_SPECIAL(__add__)
+    BIND_BINARY_SPECIAL(__sub__)
+    BIND_BINARY_SPECIAL(__mul__)
+    BIND_BINARY_SPECIAL(__truediv__)
+
+    bool py_equals(PyObject* lhs, PyObject* rhs){
+        if(lhs == rhs) return true;
+        const PyTypeInfo* ti = _inst_type_info(lhs);
+        if(ti->m__eq__) return ti->m__eq__(this, lhs, rhs);
+        return call_method(lhs, __eq__, rhs) == True;
+    }
+
+    bool py_not_equals(PyObject* lhs, PyObject* rhs){
+        if(lhs == rhs) return false;
+        const PyTypeInfo* ti = _inst_type_info(lhs);
+        if(ti->m__ne__) return ti->m__ne__(this, lhs, rhs);
+        return call_method(lhs, __ne__, rhs) == True;
+    }
+
     template<int ARGC>
     void bind_func(Str type, Str name, NativeFuncC fn) {
         bind_func<ARGC>(_find_type(type), name, fn);
@@ -353,6 +441,7 @@ public:
     void IndexError(const Str& msg){ _error("IndexError", msg); }
     void ValueError(const Str& msg){ _error("ValueError", msg); }
     void NameError(StrName name){ _error("NameError", fmt("name ", name.escape() + " is not defined")); }
+    void KeyError(const Str& msg){ _error("KeyError", msg); }
 
     void AttributeError(PyObject* obj, StrName name){
         // OBJ_NAME calls getattr, which may lead to a infinite recursion
@@ -373,12 +462,12 @@ public:
 
     void check_int(PyObject* obj){
         if(is_int(obj)) return;
-        check_type(obj, tp_int);
+        check_type(obj, tp_int);    // if failed, redirect to check_type to raise TypeError
     }
 
     void check_float(PyObject* obj){
         if(is_float(obj)) return;
-        check_type(obj, tp_float);
+        check_type(obj, tp_float);  // if failed, redirect to check_type to raise TypeError
     }
 
     PyObject* _t(Type t){
@@ -1278,4 +1367,13 @@ inline PyObject* PyStrGetItem(VM* vm, PyObject* obj, PyObject* index){
     return VAR(self.u8_getitem(i));
 }
 
+inline void Dict::_probe(PyObject *key, bool &ok, int &i) const{
+    ok = false;
+    i = vm->hash(key) & _mask;
+    while(_items[i].first != nullptr) {
+        if(vm->py_equals(_items[i].first, key)) { ok = true; break; }
+        i = (i + 1) & _mask;
+    }
+}
+
 }   // namespace pkpy