Cleaned up and added more functionalities

This commit is contained in:
S. Mahmudul Hasan 2023-10-16 00:26:13 -04:00
parent 6681fb21af
commit 85c35db694
2 changed files with 305 additions and 210 deletions

View File

@ -18,33 +18,37 @@ namespace pkpy
// PyDeque members // PyDeque members
std::deque<PyObject *> dequeItems; std::deque<PyObject *> dequeItems;
int maxlen=-1; int maxlen=-1; // -1 means unbounded
bool bounded=false; bool bounded=false; // if true, maxlen is not -1
// PyDeque methods: add, remove, insert, etc. // PyDeque methods: add, remove, insert, etc.
void appendLeft(PyObject *item); void appendLeft(PyObject *item); // add to the left
void append(PyObject *item); void append(PyObject *item); // add to the right
PyObject *popLeft(); PyObject *popLeft(); // remove from the left
PyObject *pop(); PyObject *pop(); // remove from the right
bool insert(int index, PyObject *item); bool insert(int index, PyObject *item); // insert at index
bool remove(VM *vm, PyObject *item); bool remove(VM *vm, PyObject *item); // remove first occurence of item
void rotate(int n); void rotate(int n); // rotate n steps to the right
void reverse(); void reverse();// reverse the deque
void clear(); void clear(); // clear the deque
int count(VM *vm, PyObject *obj); // vm is needed for the py_equals int count(VM *vm, PyObject *obj); // count the number of occurences of obj
int findIndex(VM *vm, PyObject *obj, int startPos, int endPos); // vm is needed for the py_equals int findIndex(VM *vm, PyObject *obj, int startPos, int endPos); // find the index of obj in range starting from startPos and ending at endPos, default range is entire deque
PyObject* getItem(int index); // get item at index
bool setItem(int index, PyObject* item); // set item at index
bool eraseItem(int index);// erase item at index
std::stringstream getRepr(VM *vm); std::stringstream getRepr(VM *vm); // get the string representation of the deque
int fixIndex(int index); // for internal use only, returns -1 if index is out of range, handles negative indices
// Special methods // Special methods
static void _register(VM *vm, PyObject *mod, PyObject *type); static void _register(VM *vm, PyObject *mod, PyObject *type); // register the type
void _gc_mark() const; // needed for container types void _gc_mark() const; // needed for container types, mark all objects in the deque for gc
}; };
void add_module_mycollections(VM *vm); void add_module_mycollections(VM *vm);

View File

@ -10,174 +10,128 @@ namespace pkpy
Type cls_t = PK_OBJ_GET(Type, args[0]); Type cls_t = PK_OBJ_GET(Type, args[0]);
PyObject *iterable = args[1]; PyObject *iterable = args[1];
PyObject *maxlen = args[2]; PyObject *maxlen = args[2];
return vm->heap.gcnew<PyDeque>(cls_t, vm, iterable, maxlen); return vm->heap.gcnew<PyDeque>(cls_t, vm, iterable, maxlen);
}); });
vm->bind(type, "__add__(self, other) -> deque", // 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) [](VM *vm, ArgsView args)
{ {
auto _lock = vm->heap.gc_scope_lock();
PyObject *newDequeObj = vm->heap.gcnew<PyDeque>(PyDeque::_type(vm), vm, vm->None, vm->None); // create the new deque
PyDeque &newDeque = _CAST(PyDeque &, newDequeObj);
PyDeque &self = _CAST(PyDeque &, args[0]); PyDeque &self = _CAST(PyDeque &, args[0]);
PyDeque &other = _CAST(PyDeque &, args[1]); int index = CAST(int, args[1]);
for (auto it = self.dequeItems.begin(); it != self.dequeItems.end(); ++it) PyObject *item = self.getItem(index);
if (item == nullptr)
{ {
newDeque.append(*it); vm->IndexError("deque index out of range");
return vm->None;
} }
for (auto it = other.dequeItems.begin(); it != other.dequeItems.end(); ++it) return item;
});
// 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];
bool success = self.setItem(index, newValue);
if (!success)
{ {
newDeque.append(*it); vm->IndexError("deque index out of range");
return vm->None;
} }
return newDequeObj; return vm->None;
}); });
vm->bind(type, "__bool__(self) -> bool", // erases the item at the given index, if index is negative, it will be treated as index + len(deque)
[](VM *vm, ArgsView args) // if the index is out of range, IndexError will be thrown --> required for [] operator
{
PyDeque &self = _CAST(PyDeque &, args[0]);
return VAR(!self.dequeItems.empty());
});
vm->bind(type, "__class__(self) -> deque()", // creates a new empty deque
[](VM *vm, ArgsView args)
{
return vm->heap.gcnew<PyDeque>(PyDeque::_type(vm), vm, vm->None, vm->None);
});
vm->bind(type, "__contains__(self, obj) -> bool",
[](VM *vm, ArgsView args)
{
PyDeque &self = _CAST(PyDeque &, args[0]);
PyObject *obj = args[1];
bool found = self.findIndex(vm, obj, -1, -1) != -1;
return VAR(found);
});
vm->bind(type, "__copy__(self)", // shallow copy
[](VM *vm, ArgsView args)
{
auto _lock = vm->heap.gc_scope_lock(); // locking the heap
PyDeque &self = _CAST(PyDeque &, args[0]);
PyObject *newDequeObj = vm->heap.gcnew<PyDeque>(PyDeque::_type(vm), vm, vm->None, vm->None); // create the empty deque
PyDeque &newDeque = _CAST(PyDeque &, newDequeObj);
for (auto it = self.dequeItems.begin(); it != self.dequeItems.end(); ++it)
{
newDeque.append(*it);
}
return newDequeObj;
});
vm->bind(type, "__delitem__(self, index) -> None", vm->bind(type, "__delitem__(self, index) -> None",
[](VM *vm, ArgsView args) [](VM *vm, ArgsView args)
{ {
// TODO PyDeque &self = _CAST(PyDeque &, args[0]);
return vm->None; int index = CAST(int, args[1]);
});
vm->bind(type, "__eq__(self, other) -> bool",
[](VM *vm, ArgsView args)
{
//TODO
return vm->None;
});
vm->bind(type, "__getitem__(self, idx) -> PyObject*",
[](VM *vm, ArgsView args)
{
//TODO
return VM->None;
}
);
// TODO: __iadd__, __imul__, __reversed__, __str__, __mul__, __rmul__, __setitem__,
bool success = self.eraseItem(index);
if (!success)
{
vm->IndexError("deque index out of range");
return vm->None;
}
return vm->None;
});
// returns the length of the deque
vm->bind(type, "__len__(self) -> int", vm->bind(type, "__len__(self) -> int",
[](VM *vm, ArgsView args) [](VM *vm, ArgsView args)
{ {
PyDeque &self = _CAST(PyDeque &, args[0]); PyDeque &self = _CAST(PyDeque &, args[0]);
return VAR(self.dequeItems.size()); return VAR(self.dequeItems.size());
}); });
vm->bind(type, "__repr__(self) -> str", // returns an iterator for the deque
[](VM *vm, ArgsView args)
{
PyDeque &self = _CAST(PyDeque &, args[0]);
std::stringstream ss = self.getRepr(vm);
return VAR(ss.str());
});
vm->bind(type, "__iter__(self) -> deque_iterator", vm->bind(type, "__iter__(self) -> deque_iterator",
[](VM *vm, ArgsView args) [](VM *vm, ArgsView args)
{ {
PyDeque &self = _CAST(PyDeque &, args[0]); PyDeque &self = _CAST(PyDeque &, args[0]);
return vm->heap.gcnew<PyDequeIter>(PyDequeIter::_type(vm), args[0], self.dequeItems.begin(), self.dequeItems.end());
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, "append(self, item) -> None", vm->bind(type, "__repr__(self) -> str",
[](VM *vm, ArgsView args) [](VM *vm, ArgsView args)
{ {
PyDeque &self = _CAST(PyDeque &, args[0]); PyDeque &self = _CAST(PyDeque &, args[0]);
PyObject *item = args[1];
self.append(item); std::stringstream ss = self.getRepr(vm);
return vm->None; return VAR(ss.str());
}); });
vm->bind(type, "appendleft(self, item) -> None", // enables comparison between two deques, == and != are supported
vm->bind(type, "__eq__(self, other) -> bool",
[](VM *vm, ArgsView args) [](VM *vm, ArgsView args)
{ {
PyDeque &self = _CAST(PyDeque &, args[0]); PyDeque &self = _CAST(PyDeque &, args[0]);
PyObject *item = args[1]; PyDeque &other = _CAST(PyDeque &, args[1]);
self.appendLeft(item);
return vm->None; if (self.dequeItems.size() != other.dequeItems.size())
return VAR(false);
for (int i = 0; i < self.dequeItems.size(); i++)
{
if (!vm->py_equals(self.dequeItems[i], other.dequeItems[i]))
return VAR(false);
}
return VAR(true);
}); });
// clear the deque
vm->bind(type, "clear(self) -> None", vm->bind(type, "clear(self) -> None",
[](VM *vm, ArgsView args) [](VM *vm, ArgsView args)
{ {
PyDeque &self = _CAST(PyDeque &, args[0]); PyDeque &self = _CAST(PyDeque &, args[0]);
self.clear(); self.clear();
return vm->None; return vm->None;
}); });
vm->bind(type, "copy(self) -> deque", // extend the deque with the given iterable
[](VM *vm, ArgsView args)
{
auto _lock = vm->heap.gc_scope_lock(); // locking the heap
PyDeque &self = _CAST(PyDeque &, args[0]);
// shallow copy
PyObject *newDequeObj = vm->heap.gcnew<PyDeque>(PyDeque::_type(vm), vm, vm->None, vm->None); // create the empty deque
PyDeque &newDeque = _CAST(PyDeque &, newDequeObj);
for (auto it = self.dequeItems.begin(); it != self.dequeItems.end(); ++it)
{
newDeque.append(*it);
}
return newDequeObj;
});
vm->bind(type, "count(self, obj) -> int",
[](VM *vm, ArgsView args)
{
PyDeque &self = _CAST(PyDeque &, args[0]);
PyObject *obj = args[1];
int cnt = self.count(vm, obj);
return VAR(cnt);
});
vm->bind(type, "extend(self, iterable) -> None", vm->bind(type, "extend(self, iterable) -> None",
[](VM *vm, ArgsView args) [](VM *vm, ArgsView args)
{ {
auto _lock = vm->heap.gc_scope_lock(); auto _lock = vm->heap.gc_scope_lock(); // locking the heap
PyDeque &self = _CAST(PyDeque &, args[0]); PyDeque &self = _CAST(PyDeque &, args[0]);
PyObject *it = vm->py_iter(args[1]); // strong ref PyObject *it = vm->py_iter(args[1]); // strong ref
PyObject *obj = vm->py_next(it); PyObject *obj = vm->py_next(it);
while (obj != vm->StopIteration) while (obj != vm->StopIteration)
{ {
@ -187,12 +141,92 @@ namespace pkpy
return vm->None; return vm->None;
}); });
// append at the end of the deque
vm->bind(type, "append(self, item) -> None",
[](VM *vm, ArgsView args)
{
PyDeque &self = _CAST(PyDeque &, args[0]);
PyObject *item = args[1];
self.append(item);
return vm->None;
});
// append at the beginning of the deque
vm->bind(type, "appendleft(self, item) -> None",
[](VM *vm, ArgsView args)
{
PyDeque &self = _CAST(PyDeque &, args[0]);
PyObject *item = args[1];
self.appendLeft(item);
return vm->None;
});
// pop from the end of the deque
vm->bind(type, "pop(self) -> PyObject",
[](VM *vm, ArgsView args)
{
PyDeque &self = _CAST(PyDeque &, args[0]);
if (self.dequeItems.empty())
{
vm->IndexError("pop from an empty deque");
return vm->None;
}
return self.pop();
});
// pop from the beginning of the deque
vm->bind(type, "popleft(self) -> PyObject",
[](VM *vm, ArgsView args)
{
PyDeque &self = _CAST(PyDeque &, args[0]);
if (self.dequeItems.empty())
{
vm->IndexError("pop from an empty deque");
return vm->None;
}
return self.popLeft();
});
// shallow copy of the deque
vm->bind(type, "copy(self) -> deque",
[](VM *vm, ArgsView args)
{
auto _lock = vm->heap.gc_scope_lock(); // locking the heap
PyDeque &self = _CAST(PyDeque &, args[0]);
// shallow copy
PyObject *newDequeObj = vm->heap.gcnew<PyDeque>(PyDeque::_type(vm), vm, vm->None, vm->None); // create the empty deque
PyDeque &newDeque = _CAST(PyDeque &, newDequeObj); // cast it to PyDeque so we can use its methods
for (auto it = self.dequeItems.begin(); it != self.dequeItems.end(); ++it)
{
newDeque.append(*it); // append each item to the new deque
}
return newDequeObj;
});
//NEW: counts the number of occurences of the given object in the deque
vm->bind(type, "count(self, obj) -> int",
[](VM *vm, ArgsView args)
{
PyDeque &self = _CAST(PyDeque &, args[0]);
PyObject *obj = args[1];
return VAR(self.count(vm, obj));
});
// NEW: extends the deque from the left
vm->bind(type, "extendleft(self, iterable) -> None", vm->bind(type, "extendleft(self, iterable) -> None",
[](VM *vm, ArgsView args) [](VM *vm, ArgsView args)
{ {
auto _lock = vm->heap.gc_scope_lock(); auto _lock = vm->heap.gc_scope_lock();
PyDeque &self = _CAST(PyDeque &, args[0]); PyDeque &self = _CAST(PyDeque &, args[0]);
PyObject *it = vm->py_iter(args[1]); // strong ref PyObject *it = vm->py_iter(args[1]); // strong ref
PyObject *obj = vm->py_next(it); PyObject *obj = vm->py_next(it);
while (obj != vm->StopIteration) while (obj != vm->StopIteration)
{ {
@ -202,22 +236,27 @@ namespace pkpy
return vm->None; return vm->None;
}); });
// NEW: returns the index of the given object in the deque
vm->bind(type, "index(self, obj, start=-1, stop=-1) -> int", vm->bind(type, "index(self, obj, start=-1, stop=-1) -> int",
[](VM *vm, ArgsView args) [](VM *vm, ArgsView args)
{ {
// 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. // 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.
PyDeque &self = _CAST(PyDeque &, args[0]); PyDeque &self = _CAST(PyDeque &, args[0]);
PyObject *obj = args[1]; PyObject *obj = args[1];
int start = CAST(int, args[2]); int start = CAST(int, args[2]);
int stop = CAST(int, args[3]); int stop = CAST(int, args[3]);
int idx = self.findIndex(vm, obj, start, stop); int idx = self.findIndex(vm, obj, start, stop);
if (idx == -1) if (idx == -1)
vm->ValueError(_CAST(Str &, vm->py_repr(obj)) + " is not in list"); {
vm->ValueError(_CAST(Str &, vm->py_repr(obj)) + " is not in deque");
return vm->None;
}
return VAR(idx); return VAR(idx);
}); });
// NEW: inserts the given object at the given index
vm->bind(type, "insert(self, index, obj) -> None", vm->bind(type, "insert(self, index, obj) -> None",
[](VM *vm, ArgsView args) [](VM *vm, ArgsView args)
{ {
@ -225,57 +264,50 @@ namespace pkpy
int index = CAST(int, args[1]); int index = CAST(int, args[1]);
PyObject *obj = args[2]; PyObject *obj = args[2];
// TODO: HANDLE MAX SIZE CASE LATER -> Throw IndexError if (self.bounded && self.dequeItems.size() == self.maxlen)
{
vm->ValueError("deque already at its maximum size");
return vm->None;
}
self.insert(index, obj); self.insert(index, obj);
return vm->None; return vm->None;
}); });
vm->bind(type, "pop(self) -> PyObject", // NEW: removes the first occurence of the given object from the deque
[](VM *vm, ArgsView args)
{
PyDeque &self = _CAST(PyDeque &, args[0]);
return self.pop();
});
vm->bind(type, "popleft(self) -> PyObject",
[](VM *vm, ArgsView args)
{
PyDeque &self = _CAST(PyDeque &, args[0]);
return self.popLeft();
});
vm->bind(type, "remove(self, obj) -> None", vm->bind(type, "remove(self, obj) -> None",
[](VM *vm, ArgsView args) [](VM *vm, ArgsView args)
{ {
PyDeque &self = _CAST(PyDeque &, args[0]); PyDeque &self = _CAST(PyDeque &, args[0]);
PyObject *obj = args[1]; PyObject *obj = args[1];
bool removed = self.remove(vm, obj);
bool removed = self.remove(vm, obj);
if (!removed) if (!removed)
vm->ValueError(_CAST(Str &, vm->py_repr(obj)) + " is not in list"); vm->ValueError(_CAST(Str &, vm->py_repr(obj)) + " is not in list");
return vm->None; return vm->None;
}); });
// NEW: reverses the deque
vm->bind(type, "reverse(self) -> None", vm->bind(type, "reverse(self) -> None",
[](VM *vm, ArgsView args) [](VM *vm, ArgsView args)
{ {
PyDeque &self = _CAST(PyDeque &, args[0]); PyDeque &self = _CAST(PyDeque &, args[0]);
self.reverse(); self.reverse();
return vm->None; return vm->None;
}); });
// NEW: rotates the deque by n steps
vm->bind(type, "rotate(self, n=1) -> None", vm->bind(type, "rotate(self, n=1) -> None",
[](VM *vm, ArgsView args) [](VM *vm, ArgsView args)
{ {
PyDeque &self = _CAST(PyDeque &, args[0]); PyDeque &self = _CAST(PyDeque &, args[0]);
int n = CAST(int, args[1]); int n = CAST(int, args[1]);
self.rotate(n); self.rotate(n);
return vm->None; return vm->None;
}); });
// getter and setter of property `maxlen` // NEW: getter and setter of property `maxlen`
vm->bind_property( vm->bind_property(
type, "maxlen: int", type, "maxlen: int",
[](VM *vm, ArgsView args) [](VM *vm, ArgsView args)
@ -291,14 +323,19 @@ namespace pkpy
vm->AttributeError("attribute 'maxlen' of 'collections.deque' objects is not writable"); vm->AttributeError("attribute 'maxlen' of 'collections.deque' objects is not writable");
return vm->None; return vm->None;
}); });
} }
/// @brief initializes a new PyDeque object
/// @param vm required for the py_iter and max_len casting
/// @param iterable a list-like object to initialize the deque with
/// @param maxlen the maximum length of the deque, makes the deque bounded
PyDeque::PyDeque(VM *vm, PyObject *iterable, PyObject *maxlen) PyDeque::PyDeque(VM *vm, PyObject *iterable, PyObject *maxlen)
{ {
if (maxlen != vm->None) if (maxlen != vm->None)
{ {
this->maxlen = CAST(int, maxlen); this->maxlen = CAST(int, maxlen);
if (this->maxlen < 0)
vm->ValueError("maxlen must be non-negative");
this->bounded = true; this->bounded = true;
} }
else else
@ -309,7 +346,7 @@ namespace pkpy
if (iterable != vm->None) if (iterable != vm->None)
{ {
auto _lock = vm->heap.gc_scope_lock(); auto _lock = vm->heap.gc_scope_lock(); // locking the heap
PyObject *it = vm->py_iter(iterable); // strong ref PyObject *it = vm->py_iter(iterable); // strong ref
PyObject *obj = vm->py_next(it); PyObject *obj = vm->py_next(it);
while (obj != vm->StopIteration) while (obj != vm->StopIteration)
@ -319,15 +356,44 @@ namespace pkpy
} }
} }
} }
/// @brief returns the item at the given index, if index is negative, it will be treated as index + len(deque)
/// @param index the index of the item to get
/// @return PyObject* the item at the given index, nullptr if the index is out of range
PyObject *PyDeque::getItem(int index)
{
index = this->fixIndex(index);
if (index == -1) return nullptr;
return this->dequeItems.at(index);
}
/// @brief sets the item at the given index, if index is negative, it will be treated as index + len(deque)
/// @param index the index of the item to set
/// @param item the newValue for the item at the given index
/// @return true if the item was set successfully, false if the index is out of range
bool PyDeque::setItem(int index, PyObject *item)
{
index = this->fixIndex(index);
if (index == -1) return false;
this->dequeItems.at(index) = item;
return true;
}
/// @brief erases the item at the given index, if index is negative, it will be treated as index + len(deque)
/// @param index the index of the item to erase
/// @return true if the item was erased successfully, false if the index is out of range
bool PyDeque::eraseItem(int index)
{
index = this->fixIndex(index);
if (index == -1) return false;
this->dequeItems.erase(this->dequeItems.begin() + index);
return true;
}
/// @brief rotates the deque by n steps
/// @param n the number of steps to rotate the deque by, can be -ve for left rotation, +ve for right rotation, can be out of range
void PyDeque::rotate(int n) void PyDeque::rotate(int n)
{ {
int direction = n > 0 ? 1 : -1; int direction = n > 0 ? 1 : -1;
int sz = this->dequeItems.size(); int sz = this->dequeItems.size();
n = abs(n); n = abs(n);
n = n % sz; // make sure n is in range n = n % sz; // make sure n is in range
for (int i = 0; i < n; i++) for (int i = 0; i < n; i++)
{ {
if (direction == 1) if (direction == 1)
@ -344,52 +410,48 @@ namespace pkpy
} }
} }
} }
/// @brief removes the first occurence of the given item
bool PyDeque::remove(VM *vm, PyObject *item) /// @param vm is needed for the py_equals
/// @param item the item to remove
/// @return true if the item was removed successfully, false if the item was not found
bool PyDeque::remove(VM *vm, PyObject *item) // removes the first occurence of the given item
{ {
for (auto it = this->dequeItems.begin(); it != this->dequeItems.end(); ++it) for (auto it = this->dequeItems.begin(); it != this->dequeItems.end(); ++it)
{
if (vm->py_equals((*it), item)) if (vm->py_equals((*it), item))
{ {
this->dequeItems.erase(it); this->dequeItems.erase(it);
return true; return true;
} }
}
return false; return false;
} }
/// @brief inserts the given item at the given index, if index is negative, it will be treated as index + len(deque)
/// @param index index at which the item will be inserted
/// @param item the item to insert
/// @return true if the item was inserted successfully, false if the index is out of range
bool PyDeque::insert(int index, PyObject *item) bool PyDeque::insert(int index, PyObject *item)
{ {
if (index < 0)
index = this->dequeItems.size() + index; // adjust for the -ve indexing
if (index < 0) if (index < 0)
this->dequeItems.push_front(item); this->dequeItems.push_front(item);
else if (index >= this->dequeItems.size()) else if (index >= this->dequeItems.size())
this->dequeItems.push_back(item); this->dequeItems.push_back(item);
else else
this->dequeItems.insert((this->dequeItems.begin() + index), item); this->dequeItems.insert((this->dequeItems.begin() + index), item);
return true; return true;
} }
/// @brief returns a string representation of the deque
void PyDeque::_gc_mark() const /// @param vm is needed for the py_repr and String casting
{ /// @return std::stringstream the string representation of the deque
for (PyObject *obj : this->dequeItems)
{
PK_OBJ_MARK(obj);
}
}
std::stringstream PyDeque::getRepr(VM *vm) std::stringstream PyDeque::getRepr(VM *vm)
{ {
std::stringstream ss; std::stringstream ss;
int sz = this->dequeItems.size();
ss << "deque(["; ss << "deque([";
for (auto it = this->dequeItems.begin(); it != this->dequeItems.end(); ++it) for (auto it = this->dequeItems.begin(); it != this->dequeItems.end(); ++it)
{ {
ss << CAST(Str &, vm->py_repr(*it)); ss << CAST(Str &, vm->py_repr(*it));
if (it != this->dequeItems.end() - 1) if (it != this->dequeItems.end() - 1)
{
ss << ", "; ss << ", ";
}
} }
if (this->bounded) if (this->bounded)
ss << "], maxlen=" << this->maxlen << ")"; ss << "], maxlen=" << this->maxlen << ")";
@ -397,95 +459,124 @@ namespace pkpy
ss << "])"; ss << "])";
return ss; return ss;
} }
/// @brief returns the index of the given object in the deque, can search in a range
/// @param vm is needed for the py_equals
/// @param obj the object to search for
/// @param startPos start position of the search
/// @param endPos end position of the search
/// @return int the index of the given object in the deque, -1 if not found
int PyDeque::findIndex(VM *vm, PyObject *obj, int startPos = -1, int endPos = -1) int PyDeque::findIndex(VM *vm, PyObject *obj, int startPos = -1, int endPos = -1)
{ {
if (startPos == -1) if (startPos == -1)
startPos = 0; startPos = 0;
if (endPos == -1) if (endPos == -1)
endPos = this->dequeItems.size(); endPos = this->dequeItems.size();
if (!(startPos <= endPos)) if (!(startPos <= endPos))
{ return -1; // invalid range
throw std::runtime_error("startPos<=endPos"); int loopSize = min(this->dequeItems.size(), endPos);
} for (int i = startPos; i < loopSize; i++)
int dequeSize = this->dequeItems.size();
for (int i = startPos; i < dequeSize && i < endPos; i++)
{
if (vm->py_equals(this->dequeItems[i], obj)) if (vm->py_equals(this->dequeItems[i], obj))
{
return i; return i;
}
}
return -1; return -1;
} }
/// @brief reverses the deque
void PyDeque::reverse() void PyDeque::reverse()
{ {
if (this->dequeItems.empty() || this->dequeItems.size() == 1)
return; // handle trivial cases
int sz = this->dequeItems.size(); int sz = this->dequeItems.size();
for (int i = 0; i < sz / 2; i++) for (int i = 0; i < sz / 2; i++)
{ {
PyObject *tmp = this->dequeItems[i]; PyObject *tmp = this->dequeItems[i];
this->dequeItems[i] = this->dequeItems[sz - i - 1]; this->dequeItems[i] = this->dequeItems[sz - i - 1]; // swapping
this->dequeItems[sz - i - 1] = tmp; this->dequeItems[sz - i - 1] = tmp;
} }
} }
/// @brief appends the given item to the beginning of the deque
/// @param item the item to append
void PyDeque::appendLeft(PyObject *item) void PyDeque::appendLeft(PyObject *item)
{ {
if (this->bounded){ // handle bounded case
if(this->maxlen == 0) return; // bounded and maxlen is 0, so we can't append
else if (this->dequeItems.size() == this->maxlen)
this->dequeItems.pop_back(); // remove the last item
}
this->dequeItems.emplace_front(item); this->dequeItems.emplace_front(item);
} }
/// @brief appends the given item to the end of the deque
/// @param item the item to append
void PyDeque::append(PyObject *item) void PyDeque::append(PyObject *item)
{ {
if(this->bounded){ // handle bounded case
if(this->maxlen == 0) return; // bounded and maxlen is 0, so we can't append
else if (this->dequeItems.size() == this->maxlen)
this->dequeItems.pop_front(); // remove the first item
}
this->dequeItems.emplace_back(item); this->dequeItems.emplace_back(item);
} }
/// @brief pops the first item from the deque, i.e. beginning of the deque
/// @return PyObject* the popped item
PyObject *PyDeque::popLeft() PyObject *PyDeque::popLeft()
{ {
if (this->dequeItems.empty()) if (this->dequeItems.empty())
{ throw std::runtime_error("pop from an empty deque");//shouldn't happen
throw std::runtime_error("pop from an empty deque");
}
PyObject *obj = this->dequeItems.front(); PyObject *obj = this->dequeItems.front();
this->dequeItems.pop_front(); this->dequeItems.pop_front();
return obj; return obj;
} }
/// @brief pops the last item from the deque, i.e. end of the deque
/// @return PyObject* the popped item
PyObject *PyDeque::pop() PyObject *PyDeque::pop()
{ {
if (this->dequeItems.empty()) if (this->dequeItems.empty())
{ throw std::runtime_error("pop from an empty deque"); //shouldn't happen
throw std::runtime_error("pop from an empty deque");
}
PyObject *obj = this->dequeItems.back(); PyObject *obj = this->dequeItems.back();
this->dequeItems.pop_back(); this->dequeItems.pop_back();
return obj; return obj;
} }
/// @brief counts the number of occurences of the given object in the deque
/// @param vm is needed for the py_equals
/// @param obj the object to search for
/// @return int the number of occurences of the given object in the deque
int PyDeque::count(VM *vm, PyObject *obj) int PyDeque::count(VM *vm, PyObject *obj)
{ {
int cnt = 0; int cnt = 0;
for (auto it = this->dequeItems.begin(); it != this->dequeItems.end(); ++it) for (auto it = this->dequeItems.begin(); it != this->dequeItems.end(); ++it)
{
if (vm->py_equals((*it), obj)) if (vm->py_equals((*it), obj))
{
cnt++; cnt++;
}
}
return cnt; return cnt;
} }
/// @brief clears the deque
void PyDeque::clear() void PyDeque::clear()
{ {
this->dequeItems.clear(); this->dequeItems.clear();
} }
/// @brief fixes the given index, if index is negative, it will be treated as index + len(deque)
/// @param index the index to fix
/// @return int the fixed index, -1 if the index is out of range
int PyDeque::fixIndex(int index)
{
if (index < 0)
index = this->dequeItems.size() + index;
if (index < 0 || index >= this->dequeItems.size())
return -1;
return index;
}
/// @brief marks the deque items for garbage collection
void PyDeque::_gc_mark() const
{
for (PyObject *obj : this->dequeItems)
{
PK_OBJ_MARK(obj);
}
}
/// @brief registers the PyDeque class
/// @param vm is needed for the new_module and register_class
void add_module_mycollections(VM *vm) void add_module_mycollections(VM *vm)
{ {
PyObject *mycollections = vm->new_module("mycollections"); PyObject *mycollections = vm->new_module("mycollections"); // TODO: change this to collections
PyDeque::register_class(vm, mycollections); PyDeque::register_class(vm, mycollections);
} }
} // namespace pkpypkpy } // namespace pkpypkpy