1
0
blueloveTH 2 жил өмнө
parent
commit
8d3bb6faa5

+ 3 - 0
include/pocketpy/vm.h

@@ -144,6 +144,9 @@ public:
     PyObject* _last_exception;  // last exception
     PyObject* _curr_class;      // current class being defined
 
+    // this is for repr() recursion detection (no need to mark)
+    std::set<PyObject*> _repr_recursion_set;
+
     // cached code objects for FSTRING_EVAL
     std::map<std::string_view, CodeObject_> _cached_codes;
 

+ 65 - 86
src/collections.cpp

@@ -57,7 +57,6 @@ namespace pkpy
         void insertObj(bool front, bool back, int index, PyObject *item); // insert at index, used purely for internal purposes: append, appendleft, insert methods
         PyObject *popObj(bool front, bool back, PyObject *item, VM *vm);  // pop at index, used purely for internal purposes: pop, popleft, remove methods
         int findIndex(VM *vm, PyObject *obj, int start, int stop);        // find the index of the given object in the deque
-        std::string getRepr(VM *vm, PyObject* thisObj);                                // get the string representation of the deque
         // Special methods
         static void _register(VM *vm, PyObject *mod, PyObject *type); // register the type
         void _gc_mark() const;                                        // needed for container types, mark all objects in the deque for gc
@@ -74,80 +73,76 @@ namespace pkpy
                  });
         // gets the item at the given index, if index is negative, it will be treated as index + len(deque)
         // if the index is out of range, IndexError will be thrown --> required for [] operator
-        vm->bind(type, "__getitem__(self, index) -> PyObject",
-                 [](VM *vm, ArgsView args)
-                 {
-                     PyDeque &self = _CAST(PyDeque &, args[0]);
-                     int index = CAST(int, args[1]);
-                     index = vm->normalized_index(index, self.dequeItems.size()); // error is handled by the vm->normalized_index
-                     return self.dequeItems.at(index);
-                 });
+        vm->bind__getitem__(PK_OBJ_GET(Type, type), [](VM *vm, PyObject* _0, PyObject* _1)
+        {
+            PyDeque &self = _CAST(PyDeque &, _0);
+            int index = CAST(int, _1);
+            index = vm->normalized_index(index, self.dequeItems.size()); // error is handled by the vm->normalized_index
+            return self.dequeItems.at(index);
+        });
         // sets the item at the given index, if index is negative, it will be treated as index + len(deque)
         // if the index is out of range, IndexError will be thrown --> required for [] operator
-        vm->bind(type, "__setitem__(self, index, newValue) -> None",
-                 [](VM *vm, ArgsView args)
-                 {
-                     PyDeque &self = _CAST(PyDeque &, args[0]);
-                     int index = CAST(int, args[1]);
-                     PyObject *newValue = args[2];
-                     index = vm->normalized_index(index, self.dequeItems.size()); // error is handled by the vm->normalized_index
-                     self.dequeItems.at(index) = newValue;
-                     return vm->None;
-                 });
+        vm->bind__setitem__(PK_OBJ_GET(Type, type), [](VM *vm, PyObject* _0, PyObject* _1, PyObject* _2)
+        {
+            PyDeque &self = _CAST(PyDeque&, _0);
+            int index = CAST(int, _1);
+            index = vm->normalized_index(index, self.dequeItems.size()); // error is handled by the vm->normalized_index
+            self.dequeItems.at(index) = _2;
+        });
         // erases the item at the given index, if index is negative, it will be treated as index + len(deque)
         // if the index is out of range, IndexError will be thrown --> required for [] operator
-        vm->bind(type, "__delitem__(self, index) -> None",
-                 [](VM *vm, ArgsView args)
-                 {
-                     PyDeque &self = _CAST(PyDeque &, args[0]);
-                     int index = CAST(int, args[1]);
-                     index = vm->normalized_index(index, self.dequeItems.size()); // error is handled by the vm->normalized_index
-                     self.dequeItems.erase(self.dequeItems.begin() + index);
-                     return vm->None;
-                 });
-        // returns the length of the deque
-        vm->bind(type, "__len__(self) -> int",
-                 [](VM *vm, ArgsView args)
-                 {
-                     PyDeque &self = _CAST(PyDeque &, args[0]);
-                     return VAR(self.dequeItems.size());
-                 });
-        // returns an iterator for the deque
-        vm->bind(type, "__iter__(self) -> deque_iterator",
-                 [](VM *vm, ArgsView args)
-                 {
-                     PyDeque &self = _CAST(PyDeque &, args[0]);
-                     return vm->heap.gcnew<PyDequeIter>(
-                         PyDequeIter::_type(vm), args[0],
-                         self.dequeItems.begin(), self.dequeItems.end());
-                 });
-        // returns a string representation of the deque
-        vm->bind(type, "__repr__(self) -> str",
-                 [](VM *vm, ArgsView args)
-                 {
-                     PyDeque &self = _CAST(PyDeque &, args[0]);
-                     return VAR(self.getRepr(vm, args[0]));
-                 });
-        // returns a string representation of the deque
-        vm->bind(type, "__str__(self) -> str",
-                 [](VM *vm, ArgsView args)
-                 {
-                     PyDeque &self = _CAST(PyDeque &, args[0]);
-                     return VAR(self.getRepr(vm, args[0]));
-                 });
+        vm->bind__delitem__(PK_OBJ_GET(Type, type), [](VM *vm, PyObject* _0, PyObject* _1)
+        {
+            PyDeque &self = _CAST(PyDeque&, _0);
+            int index = CAST(int, _1);
+            index = vm->normalized_index(index, self.dequeItems.size()); // error is handled by the vm->normalized_index
+            self.dequeItems.erase(self.dequeItems.begin() + index);
+        });
+
+        vm->bind__len__(PK_OBJ_GET(Type, type), [](VM *vm, PyObject* _0)
+        {
+            PyDeque &self = _CAST(PyDeque&, _0);
+            return (i64)self.dequeItems.size();
+        });
+
+        vm->bind__iter__(PK_OBJ_GET(Type, type), [](VM *vm, PyObject* _0)
+        {
+            PyDeque &self = _CAST(PyDeque &, _0);
+            return vm->heap.gcnew<PyDequeIter>(
+                PyDequeIter::_type(vm), _0,
+                self.dequeItems.begin(), self.dequeItems.end());
+        });
+
+        vm->bind__repr__(PK_OBJ_GET(Type, type), [](VM *vm, PyObject* _0)
+        {
+            if(vm->_repr_recursion_set.count(_0)) return VAR("[...]");
+            const PyDeque &self = _CAST(PyDeque&, _0);
+            SStream ss;
+            ss << "deque([";
+            vm->_repr_recursion_set.insert(_0);
+            for (auto it = self.dequeItems.begin(); it != self.dequeItems.end(); ++it)
+            {
+                ss << CAST(Str&, vm->py_repr(*it));
+                if (it != self.dequeItems.end() - 1) ss << ", ";
+            }
+            vm->_repr_recursion_set.erase(_0);
+            self.bounded ? ss << "], maxlen=" << self.maxlen << ")" : ss << "])";
+            return VAR(ss.str());
+        });
+
         // enables comparison between two deques, == and != are supported
-        vm->bind(type, "__eq__(self, other) -> bool",
-                 [](VM *vm, ArgsView args)
-                 {
-                     PyDeque &self = _CAST(PyDeque &, args[0]);
-                     PyDeque &other = _CAST(PyDeque &, args[1]);
-                     if (self.dequeItems.size() != other.dequeItems.size()) // trivial case
-                         return VAR(false);
-                     for (int i = 0; i < self.dequeItems.size(); i++)
-                         if (!vm->py_eq(self.dequeItems[i], other.dequeItems[i]))
-                             return VAR(false);
-                     return VAR(true);
-                 });
+        vm->bind__eq__(PK_OBJ_GET(Type, type), [](VM *vm, PyObject* _0, PyObject* _1)
+        {
+            const PyDeque &self = _CAST(PyDeque&, _0);
+            if(!is_non_tagged_type(_0, PyDeque::_type(vm))) return vm->NotImplemented;
+            const PyDeque &other = _CAST(PyDeque&, _1);
+            if (self.dequeItems.size() != other.dequeItems.size()) return vm->False;
+            for (int i = 0; i < self.dequeItems.size(); i++){
+                if (vm->py_ne(self.dequeItems[i], other.dequeItems[i])) return vm->False;
+            }
+            return vm->True;
+        });
+
         // clear the deque
         vm->bind(type, "clear(self) -> None",
                  [](VM *vm, ArgsView args)
@@ -426,22 +421,6 @@ namespace pkpy
             }
         }
     }
-    std::string PyDeque::getRepr(VM *vm, PyObject *thisObj)
-    {
-        std::stringstream ss;
-        ss << "deque([";
-        for (auto it = this->dequeItems.begin(); it != this->dequeItems.end(); ++it)
-        {
-            if (*it == thisObj)
-                ss << "[...]";
-            else
-                ss << CAST(Str &, vm->py_repr(*it));
-            if (it != this->dequeItems.end() - 1)
-                ss << ", ";
-        }
-        this->bounded ? ss << "], maxlen=" << this->maxlen << ")" : ss << "])";
-        return ss.str();
-    }
     int PyDeque::findIndex(VM *vm, PyObject *obj, int start, int stop)
     {
         // the following code is special purpose normalization for this method, taken from CPython: _collectionsmodule.c file

+ 11 - 11
src/pocketpy.cpp

@@ -659,17 +659,16 @@ void init_builtins(VM* _vm) {
     });
 
     _vm->bind__repr__(VM::tp_list, [](VM* vm, PyObject* _0){
+        if(vm->_repr_recursion_set.count(_0)) return VAR("[...]");
         List& iterable = _CAST(List&, _0);
         SStream ss;
         ss << '[';
+        vm->_repr_recursion_set.insert(_0);
         for(int i=0; i<iterable.size(); i++){
-            if(iterable[i] == _0){
-                ss << "[...]";
-            }else{
-                ss << CAST(Str&, vm->py_repr(iterable[i]));
-            }
+            ss << CAST(Str&, vm->py_repr(iterable[i]));
             if(i != iterable.size()-1) ss << ", ";
         }
+        vm->_repr_recursion_set.erase(_0);
         ss << ']';
         return VAR(ss.str());
     });
@@ -1080,16 +1079,19 @@ void init_builtins(VM* _vm) {
     });
 
     _vm->bind__repr__(VM::tp_mappingproxy, [](VM* vm, PyObject* _0) {
+        if(vm->_repr_recursion_set.count(_0)) return VAR("{...}");
         MappingProxy& self = _CAST(MappingProxy&, _0);
         SStream ss;
         ss << "mappingproxy({";
         bool first = true;
+        vm->_repr_recursion_set.insert(_0);
         for(auto& item : self.attr().items()){
             if(!first) ss << ", ";
             first = false;
             ss << item.first.escape() << ": ";
             ss << CAST(Str, vm->py_repr(item.second));
         }
+        vm->_repr_recursion_set.erase(_0);
         ss << "})";
         return VAR(ss.str());
     });
@@ -1227,20 +1229,18 @@ void init_builtins(VM* _vm) {
     });
 
     _vm->bind__repr__(VM::tp_dict, [](VM* vm, PyObject* _0) {
+        if(vm->_repr_recursion_set.count(_0)) return VAR("{...}");
         Dict& self = _CAST(Dict&, _0);
         SStream ss;
         ss << "{";
         bool first = true;
+        vm->_repr_recursion_set.insert(_0);
         self.apply([&](PyObject* k, PyObject* v){
             if(!first) ss << ", ";
             first = false;
-            ss << CAST(Str&, vm->py_repr(k)) << ": ";
-            if(v == _0){
-                ss << "{...}";
-            }else{
-                ss << CAST(Str&, vm->py_repr(v));
-            }
+            ss << CAST(Str&, vm->py_repr(k)) << ": " << CAST(Str&, vm->py_repr(v));
         });
+        vm->_repr_recursion_set.erase(_0);
         ss << "}";
         return VAR(ss.str());
     });

+ 3 - 3
tests/05_list.py

@@ -97,7 +97,7 @@ assert b[
     0] == 1
 
 a = []
-a.append(1)
-a.append(a)
+a.append(0)
+a.append([1, 2, a])
 
-assert repr(a) == '[1, [...]]'
+assert repr(a) == "[0, [1, 2, [...]]]"