collections.cpp 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558
  1. #include "pocketpy/collections.h"
  2. namespace pkpy
  3. {
  4. struct PyDequeIter // Iterator for the deque type
  5. {
  6. PY_CLASS(PyDequeIter, collections, _deque_iterator)
  7. PyObject *ref;
  8. bool is_reversed;
  9. std::deque<PyObject *>::iterator begin, end, current;
  10. std::deque<PyObject *>::reverse_iterator rbegin, rend, rcurrent;
  11. PyDequeIter(PyObject *ref, std::deque<PyObject *>::iterator begin, std::deque<PyObject *>::iterator end)
  12. : ref(ref), begin(begin), end(end), current(begin)
  13. {
  14. this->is_reversed = false;
  15. }
  16. PyDequeIter(PyObject *ref, std::deque<PyObject *>::reverse_iterator rbegin, std::deque<PyObject *>::reverse_iterator rend)
  17. : ref(ref), rbegin(rbegin), rend(rend), rcurrent(rbegin)
  18. {
  19. this->is_reversed = true;
  20. }
  21. void _gc_mark() const { PK_OBJ_MARK(ref); }
  22. static void _register(VM *vm, PyObject *mod, PyObject *type);
  23. };
  24. void PyDequeIter::_register(VM *vm, PyObject *mod, PyObject *type)
  25. {
  26. // Iterator for the deque type
  27. vm->_all_types[PK_OBJ_GET(Type, type)].subclass_enabled = false;
  28. vm->bind_notimplemented_constructor<PyDequeIter>(type);
  29. vm->bind__iter__(PK_OBJ_GET(Type, type), [](VM *vm, PyObject *obj)
  30. { return obj; });
  31. vm->bind__next__(PK_OBJ_GET(Type, type), [](VM *vm, PyObject *obj) -> unsigned
  32. {
  33. PyDequeIter& self = _CAST(PyDequeIter&, obj);
  34. if(self.is_reversed){
  35. if(self.rcurrent == self.rend) return 0;
  36. vm->s_data.push(*self.rcurrent);
  37. ++self.rcurrent;
  38. return 1;
  39. }
  40. else{
  41. if(self.current == self.end) return 0;
  42. vm->s_data.push(*self.current);
  43. ++self.current;
  44. return 1;
  45. } });
  46. }
  47. struct PyDeque
  48. {
  49. PY_CLASS(PyDeque, collections, deque);
  50. PyDeque(VM *vm, PyObject *iterable, PyObject *maxlen); // constructor
  51. // PyDeque members
  52. std::deque<PyObject *> dequeItems;
  53. int maxlen = -1; // -1 means unbounded
  54. bool bounded = false; // if true, maxlen is not -1
  55. void insertObj(bool front, bool back, int index, PyObject *item); // insert at index, used purely for internal purposes: append, appendleft, insert methods
  56. PyObject *popObj(bool front, bool back, PyObject *item, VM *vm); // pop at index, used purely for internal purposes: pop, popleft, remove methods
  57. int findIndex(VM *vm, PyObject *obj, int start, int stop); // find the index of the given object in the deque
  58. // Special methods
  59. static void _register(VM *vm, PyObject *mod, PyObject *type); // register the type
  60. void _gc_mark() const; // needed for container types, mark all objects in the deque for gc
  61. };
  62. void PyDeque::_register(VM *vm, PyObject *mod, PyObject *type)
  63. {
  64. vm->bind(type, "__new__(cls, iterable=None, maxlen=None)",
  65. [](VM *vm, ArgsView args)
  66. {
  67. Type cls_t = PK_OBJ_GET(Type, args[0]);
  68. PyObject *iterable = args[1];
  69. PyObject *maxlen = args[2];
  70. return vm->heap.gcnew<PyDeque>(cls_t, vm, iterable, maxlen);
  71. });
  72. // gets the item at the given index, if index is negative, it will be treated as index + len(deque)
  73. // if the index is out of range, IndexError will be thrown --> required for [] operator
  74. vm->bind__getitem__(PK_OBJ_GET(Type, type), [](VM *vm, PyObject* _0, PyObject* _1)
  75. {
  76. PyDeque &self = _CAST(PyDeque &, _0);
  77. i64 index = CAST(i64, _1);
  78. index = vm->normalized_index(index, self.dequeItems.size()); // error is handled by the vm->normalized_index
  79. return self.dequeItems[index];
  80. });
  81. // sets the item at the given index, if index is negative, it will be treated as index + len(deque)
  82. // if the index is out of range, IndexError will be thrown --> required for [] operator
  83. vm->bind__setitem__(PK_OBJ_GET(Type, type), [](VM *vm, PyObject* _0, PyObject* _1, PyObject* _2)
  84. {
  85. PyDeque &self = _CAST(PyDeque&, _0);
  86. i64 index = CAST(i64, _1);
  87. index = vm->normalized_index(index, self.dequeItems.size()); // error is handled by the vm->normalized_index
  88. self.dequeItems[index] = _2;
  89. });
  90. // erases the item at the given index, if index is negative, it will be treated as index + len(deque)
  91. // if the index is out of range, IndexError will be thrown --> required for [] operator
  92. vm->bind__delitem__(PK_OBJ_GET(Type, type), [](VM *vm, PyObject* _0, PyObject* _1)
  93. {
  94. PyDeque &self = _CAST(PyDeque&, _0);
  95. i64 index = CAST(i64, _1);
  96. index = vm->normalized_index(index, self.dequeItems.size()); // error is handled by the vm->normalized_index
  97. self.dequeItems.erase(self.dequeItems.begin() + index);
  98. });
  99. vm->bind__len__(PK_OBJ_GET(Type, type), [](VM *vm, PyObject* _0)
  100. {
  101. PyDeque &self = _CAST(PyDeque&, _0);
  102. return (i64)self.dequeItems.size();
  103. });
  104. vm->bind__iter__(PK_OBJ_GET(Type, type), [](VM *vm, PyObject* _0)
  105. {
  106. PyDeque &self = _CAST(PyDeque &, _0);
  107. return vm->heap.gcnew<PyDequeIter>(
  108. PyDequeIter::_type(vm), _0,
  109. self.dequeItems.begin(), self.dequeItems.end());
  110. });
  111. vm->bind__repr__(PK_OBJ_GET(Type, type), [](VM *vm, PyObject* _0)
  112. {
  113. if(vm->_repr_recursion_set.count(_0)) return VAR("[...]");
  114. const PyDeque &self = _CAST(PyDeque&, _0);
  115. SStream ss;
  116. ss << "deque([";
  117. vm->_repr_recursion_set.insert(_0);
  118. for (auto it = self.dequeItems.begin(); it != self.dequeItems.end(); ++it)
  119. {
  120. ss << CAST(Str&, vm->py_repr(*it));
  121. if (it != self.dequeItems.end() - 1) ss << ", ";
  122. }
  123. vm->_repr_recursion_set.erase(_0);
  124. self.bounded ? ss << "], maxlen=" << self.maxlen << ")" : ss << "])";
  125. return VAR(ss.str());
  126. });
  127. // enables comparison between two deques, == and != are supported
  128. vm->bind__eq__(PK_OBJ_GET(Type, type), [](VM *vm, PyObject* _0, PyObject* _1)
  129. {
  130. const PyDeque &self = _CAST(PyDeque&, _0);
  131. if(!is_type(_0, PyDeque::_type(vm))) return vm->NotImplemented;
  132. const PyDeque &other = _CAST(PyDeque&, _1);
  133. if (self.dequeItems.size() != other.dequeItems.size()) return vm->False;
  134. for (int i = 0; i < self.dequeItems.size(); i++){
  135. if (vm->py_ne(self.dequeItems[i], other.dequeItems[i])) return vm->False;
  136. }
  137. return vm->True;
  138. });
  139. // clear the deque
  140. vm->bind(type, "clear(self) -> None",
  141. [](VM *vm, ArgsView args)
  142. {
  143. PyDeque &self = _CAST(PyDeque &, args[0]);
  144. self.dequeItems.clear();
  145. return vm->None;
  146. });
  147. // extend the deque with the given iterable
  148. vm->bind(type, "extend(self, iterable) -> None",
  149. [](VM *vm, ArgsView args)
  150. {
  151. auto _lock = vm->heap.gc_scope_lock(); // locking the heap
  152. PyDeque &self = _CAST(PyDeque &, args[0]);
  153. PyObject *it = vm->py_iter(args[1]); // strong ref
  154. PyObject *obj = vm->py_next(it);
  155. while (obj != vm->StopIteration)
  156. {
  157. self.insertObj(false, true, -1, obj);
  158. obj = vm->py_next(it);
  159. }
  160. return vm->None;
  161. });
  162. // append at the end of the deque
  163. vm->bind(type, "append(self, item) -> None",
  164. [](VM *vm, ArgsView args)
  165. {
  166. PyDeque &self = _CAST(PyDeque &, args[0]);
  167. PyObject *item = args[1];
  168. self.insertObj(false, true, -1, item);
  169. return vm->None;
  170. });
  171. // append at the beginning of the deque
  172. vm->bind(type, "appendleft(self, item) -> None",
  173. [](VM *vm, ArgsView args)
  174. {
  175. PyDeque &self = _CAST(PyDeque &, args[0]);
  176. PyObject *item = args[1];
  177. self.insertObj(true, false, -1, item);
  178. return vm->None;
  179. });
  180. // pop from the end of the deque
  181. vm->bind(type, "pop(self) -> PyObject",
  182. [](VM *vm, ArgsView args)
  183. {
  184. PyDeque &self = _CAST(PyDeque &, args[0]);
  185. if (self.dequeItems.empty())
  186. {
  187. vm->IndexError("pop from an empty deque");
  188. return vm->None;
  189. }
  190. return self.popObj(false, true, nullptr, vm);
  191. });
  192. // pop from the beginning of the deque
  193. vm->bind(type, "popleft(self) -> PyObject",
  194. [](VM *vm, ArgsView args)
  195. {
  196. PyDeque &self = _CAST(PyDeque &, args[0]);
  197. if (self.dequeItems.empty())
  198. {
  199. vm->IndexError("pop from an empty deque");
  200. return vm->None;
  201. }
  202. return self.popObj(true, false, nullptr, vm);
  203. });
  204. // shallow copy of the deque
  205. vm->bind(type, "copy(self) -> deque",
  206. [](VM *vm, ArgsView args)
  207. {
  208. auto _lock = vm->heap.gc_scope_lock(); // locking the heap
  209. PyDeque &self = _CAST(PyDeque &, args[0]);
  210. PyObject *newDequeObj = vm->heap.gcnew<PyDeque>(PyDeque::_type(vm), vm, vm->None, vm->None); // create the empty deque
  211. PyDeque &newDeque = _CAST(PyDeque &, newDequeObj); // cast it to PyDeque so we can use its methods
  212. for (auto it = self.dequeItems.begin(); it != self.dequeItems.end(); ++it)
  213. newDeque.insertObj(false, true, -1, *it);
  214. return newDequeObj;
  215. });
  216. // NEW: counts the number of occurrences of the given object in the deque
  217. vm->bind(type, "count(self, obj) -> int",
  218. [](VM *vm, ArgsView args)
  219. {
  220. PyDeque &self = _CAST(PyDeque &, args[0]);
  221. PyObject *obj = args[1];
  222. int cnt = 0, sz = self.dequeItems.size();
  223. for (auto it = self.dequeItems.begin(); it != self.dequeItems.end(); ++it)
  224. {
  225. if (vm->py_eq((*it), obj))
  226. cnt++;
  227. if (sz != self.dequeItems.size())// mutating the deque during iteration is not allowed
  228. vm->RuntimeError("deque mutated during iteration");
  229. }
  230. return VAR(cnt);
  231. });
  232. // NEW: extends the deque from the left
  233. vm->bind(type, "extendleft(self, iterable) -> None",
  234. [](VM *vm, ArgsView args)
  235. {
  236. auto _lock = vm->heap.gc_scope_lock();
  237. PyDeque &self = _CAST(PyDeque &, args[0]);
  238. PyObject *it = vm->py_iter(args[1]); // strong ref
  239. PyObject *obj = vm->py_next(it);
  240. while (obj != vm->StopIteration)
  241. {
  242. self.insertObj(true, false, -1, obj);
  243. obj = vm->py_next(it);
  244. }
  245. return vm->None;
  246. });
  247. // NEW: returns the index of the given object in the deque
  248. vm->bind(type, "index(self, obj, start=None, stop=None) -> int",
  249. [](VM *vm, ArgsView args)
  250. {
  251. // Return the position of x in the deque (at or after index start and before index stop). Returns the first match or raises ValueError if not found.
  252. PyDeque &self = _CAST(PyDeque &, args[0]);
  253. PyObject *obj = args[1];
  254. int start = CAST_DEFAULT(int, args[2], 0);
  255. int stop = CAST_DEFAULT(int, args[3], self.dequeItems.size());
  256. int index = self.findIndex(vm, obj, start, stop);
  257. if (index < 0) vm->ValueError(_CAST(Str &, vm->py_repr(obj)) + " is not in deque");
  258. return VAR(index);
  259. });
  260. // NEW: returns the index of the given object in the deque
  261. vm->bind(type, "__contains__(self, obj) -> bool",
  262. [](VM *vm, ArgsView args)
  263. {
  264. // Return the position of x in the deque (at or after index start and before index stop). Returns the first match or raises ValueError if not found.
  265. PyDeque &self = _CAST(PyDeque &, args[0]);
  266. PyObject *obj = args[1];
  267. int start = 0, stop = self.dequeItems.size(); // default values
  268. int index = self.findIndex(vm, obj, start, stop);
  269. if (index != -1)
  270. return VAR(true);
  271. return VAR(false);
  272. });
  273. // NEW: inserts the given object at the given index
  274. vm->bind(type, "insert(self, index, obj) -> None",
  275. [](VM *vm, ArgsView args)
  276. {
  277. PyDeque &self = _CAST(PyDeque &, args[0]);
  278. int index = CAST(int, args[1]);
  279. PyObject *obj = args[2];
  280. if (self.bounded && self.dequeItems.size() == self.maxlen)
  281. vm->IndexError("deque already at its maximum size");
  282. else
  283. self.insertObj(false, false, index, obj); // this index shouldn't be fixed using vm->normalized_index, pass as is
  284. return vm->None;
  285. });
  286. // NEW: removes the first occurrence of the given object from the deque
  287. vm->bind(type, "remove(self, obj) -> None",
  288. [](VM *vm, ArgsView args)
  289. {
  290. PyDeque &self = _CAST(PyDeque &, args[0]);
  291. PyObject *obj = args[1];
  292. PyObject *removed = self.popObj(false, false, obj, vm);
  293. if (removed == nullptr)
  294. vm->ValueError(_CAST(Str &, vm->py_repr(obj)) + " is not in list");
  295. return vm->None;
  296. });
  297. // NEW: reverses the deque
  298. vm->bind(type, "reverse(self) -> None",
  299. [](VM *vm, ArgsView args)
  300. {
  301. PyDeque &self = _CAST(PyDeque &, args[0]);
  302. if (self.dequeItems.empty() || self.dequeItems.size() == 1)
  303. return vm->None; // handle trivial cases
  304. int sz = self.dequeItems.size();
  305. for (int i = 0; i < sz / 2; i++)
  306. {
  307. PyObject *tmp = self.dequeItems[i];
  308. self.dequeItems[i] = self.dequeItems[sz - i - 1]; // swapping
  309. self.dequeItems[sz - i - 1] = tmp;
  310. }
  311. return vm->None;
  312. });
  313. // NEW: rotates the deque by n steps
  314. vm->bind(type, "rotate(self, n=1) -> None",
  315. [](VM *vm, ArgsView args)
  316. {
  317. PyDeque &self = _CAST(PyDeque &, args[0]);
  318. int n = CAST(int, args[1]);
  319. if (n != 0 && !self.dequeItems.empty()) // trivial case
  320. {
  321. PyObject *tmp; // holds the object to be rotated
  322. int direction = n > 0 ? 1 : -1;
  323. n = abs(n);
  324. n = n % self.dequeItems.size(); // make sure n is in range
  325. while (n--)
  326. {
  327. if (direction == 1)
  328. {
  329. tmp = self.dequeItems.back();
  330. self.dequeItems.pop_back();
  331. self.dequeItems.push_front(tmp);
  332. }
  333. else
  334. {
  335. tmp = self.dequeItems.front();
  336. self.dequeItems.pop_front();
  337. self.dequeItems.push_back(tmp);
  338. }
  339. }
  340. }
  341. return vm->None;
  342. });
  343. // NEW: getter and setter of property `maxlen`
  344. vm->bind_property(
  345. type, "maxlen: int",
  346. [](VM *vm, ArgsView args)
  347. {
  348. PyDeque &self = _CAST(PyDeque &, args[0]);
  349. if (self.bounded)
  350. return VAR(self.maxlen);
  351. return vm->None;
  352. },
  353. [](VM *vm, ArgsView args)
  354. {
  355. vm->AttributeError("attribute 'maxlen' of 'collections.deque' objects is not writable");
  356. return vm->None;
  357. });
  358. // NEW: support pickle
  359. vm->bind(type, "__getnewargs__(self) -> tuple[list, int]",
  360. [](VM *vm, ArgsView args)
  361. {
  362. PyDeque &self = _CAST(PyDeque &, args[0]);
  363. Tuple ret(2);
  364. List list;
  365. for (PyObject *obj : self.dequeItems)
  366. {
  367. list.push_back(obj);
  368. }
  369. ret[0] = VAR(std::move(list));
  370. if (self.bounded)
  371. ret[1] = VAR(self.maxlen);
  372. else
  373. ret[1] = vm->None;
  374. return VAR(ret);
  375. });
  376. }
  377. /// @brief initializes a new PyDeque object, actual initialization is done in __init__
  378. PyDeque::PyDeque(VM *vm, PyObject *iterable, PyObject *maxlen)
  379. {
  380. if (maxlen != vm->None) // fix the maxlen first
  381. {
  382. int tmp = CAST(int, maxlen);
  383. if (tmp < 0)
  384. vm->ValueError("maxlen must be non-negative");
  385. else
  386. {
  387. this->maxlen = tmp;
  388. this->bounded = true;
  389. }
  390. }
  391. else
  392. {
  393. this->bounded = false;
  394. this->maxlen = -1;
  395. }
  396. if (iterable != vm->None)
  397. {
  398. this->dequeItems.clear(); // clear the deque
  399. auto _lock = vm->heap.gc_scope_lock(); // locking the heap
  400. PyObject *it = vm->py_iter(iterable); // strong ref
  401. PyObject *obj = vm->py_next(it);
  402. while (obj != vm->StopIteration)
  403. {
  404. this->insertObj(false, true, -1, obj);
  405. obj = vm->py_next(it);
  406. }
  407. }
  408. }
  409. int PyDeque::findIndex(VM *vm, PyObject *obj, int start, int stop)
  410. {
  411. // the following code is special purpose normalization for this method, taken from CPython: _collectionsmodule.c file
  412. if (start < 0)
  413. {
  414. start = this->dequeItems.size() + start; // try to fix for negative indices
  415. if (start < 0)
  416. start = 0;
  417. }
  418. if (stop < 0)
  419. {
  420. stop = this->dequeItems.size() + stop; // try to fix for negative indices
  421. if (stop < 0)
  422. stop = 0;
  423. }
  424. if (stop > this->dequeItems.size())
  425. stop = this->dequeItems.size();
  426. if (start > stop)
  427. start = stop; // end of normalization
  428. PK_ASSERT(start >= 0 && start <= this->dequeItems.size() && stop >= 0 && stop <= this->dequeItems.size() && start <= stop); // sanity check
  429. int loopSize = std::min((int)(this->dequeItems.size()), stop);
  430. int sz = this->dequeItems.size();
  431. for (int i = start; i < loopSize; i++)
  432. {
  433. if (vm->py_eq(this->dequeItems[i], obj))
  434. return i;
  435. if (sz != this->dequeItems.size())// mutating the deque during iteration is not allowed
  436. vm->RuntimeError("deque mutated during iteration");
  437. }
  438. return -1;
  439. }
  440. /// @brief pops or removes an item from the deque
  441. /// @param front if true, pop from the front of the deque
  442. /// @param back if true, pop from the back of the deque
  443. /// @param item if front and back is not set, remove the first occurrence of item from the deque
  444. /// @param vm is needed for the py_eq
  445. /// @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
  446. PyObject *PyDeque::popObj(bool front, bool back, PyObject *item, VM *vm)
  447. {
  448. // error handling
  449. if (front && back)
  450. throw std::runtime_error("both front and back are set"); // this should never happen
  451. if (front || back)
  452. {
  453. // front or back is set, we don't care about item, this is a pop operation and we return a PyObject*
  454. if (this->dequeItems.empty())
  455. throw std::runtime_error("pop from an empty deque"); // shouldn't happen
  456. PyObject *obj;
  457. if (front)
  458. {
  459. obj = this->dequeItems.front();
  460. this->dequeItems.pop_front();
  461. }
  462. else
  463. {
  464. obj = this->dequeItems.back();
  465. this->dequeItems.pop_back();
  466. }
  467. return obj;
  468. }
  469. else
  470. {
  471. // front and back are not set, we care about item, this is a remove operation and we return the removed item or nullptr
  472. int sz = this->dequeItems.size();
  473. for (auto it = this->dequeItems.begin(); it != this->dequeItems.end(); ++it)
  474. {
  475. bool found = vm->py_eq((*it), item);
  476. if (sz != this->dequeItems.size()) // mutating the deque during iteration is not allowed
  477. vm->IndexError("deque mutated during iteration");
  478. if (found)
  479. {
  480. PyObject *obj = *it; // keep a reference to the object for returning
  481. this->dequeItems.erase(it);
  482. return obj;
  483. }
  484. }
  485. return nullptr; // not found
  486. }
  487. }
  488. /// @brief inserts an item into the deque
  489. /// @param front if true, insert at the front of the deque
  490. /// @param back if true, insert at the back of the deque
  491. /// @param index if front and back are not set, insert at the given index
  492. /// @param item the item to insert
  493. /// @return true if the item was inserted successfully, false if the deque is bounded and is already at its maximum size
  494. void PyDeque::insertObj(bool front, bool back, int index, PyObject *item) // assume index is not fixed using the vm->normalized_index
  495. {
  496. // error handling
  497. if (front && back)
  498. throw std::runtime_error("both front and back are set"); // this should never happen
  499. if (front || back)
  500. {
  501. // front or back is set, we don't care about index
  502. if (this->bounded)
  503. {
  504. if (this->maxlen == 0)
  505. return; // bounded and maxlen is 0, so we can't append
  506. else if (this->dequeItems.size() == this->maxlen)
  507. {
  508. if (front)
  509. this->dequeItems.pop_back(); // remove the last item
  510. else if (back)
  511. this->dequeItems.pop_front(); // remove the first item
  512. }
  513. }
  514. if (front)
  515. this->dequeItems.emplace_front(item);
  516. else if (back)
  517. this->dequeItems.emplace_back(item);
  518. }
  519. else
  520. {
  521. // front and back are not set, we care about index
  522. if (index < 0)
  523. index = this->dequeItems.size() + index; // try fixing for negative indices
  524. if (index < 0) // still negative means insert at the beginning
  525. this->dequeItems.push_front(item);
  526. else if (index >= this->dequeItems.size()) // still out of range means insert at the end
  527. this->dequeItems.push_back(item);
  528. else
  529. this->dequeItems.insert((this->dequeItems.begin() + index), item); // insert at the given index
  530. }
  531. }
  532. /// @brief marks the deque items for garbage collection
  533. void PyDeque::_gc_mark() const
  534. {
  535. for (PyObject *obj : this->dequeItems)
  536. PK_OBJ_MARK(obj);
  537. }
  538. /// @brief registers the PyDeque class
  539. /// @param vm is needed for the new_module and register_class
  540. void add_module_collections(VM *vm)
  541. {
  542. PyObject *mod = vm->new_module("collections");
  543. PyDeque::register_class(vm, mod);
  544. PyDequeIter::register_class(vm, mod);
  545. CodeObject_ code = vm->compile(kPythonLibs_collections, "collections.py", EXEC_MODE);
  546. vm->_exec(code, mod);
  547. }
  548. } // namespace pkpypkpy