collections.cpp 27 KB

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