Cleaned up and added __str__() support

This commit is contained in:
S. Mahmudul Hasan 2023-10-19 23:38:30 -04:00
parent a5a18154f4
commit 385e9492c8
2 changed files with 73 additions and 110 deletions

View File

@ -1,19 +1,13 @@
#include "pocketpy/collections.h"
namespace pkpy
{
struct PyDequeIter
struct PyDequeIter // Iterator for the deque type
{
// Iterator for the deque type
PY_CLASS(PyDequeIter, builtins, "_deque_iterator")
PyObject *ref;
bool is_reversed;
std::deque<PyObject *>::iterator begin;
std::deque<PyObject *>::iterator end;
std::deque<PyObject *>::iterator current;
std::deque<PyObject *>::reverse_iterator rbegin;
std::deque<PyObject *>::reverse_iterator rend;
std::deque<PyObject *>::reverse_iterator rcurrent;
std::deque<PyObject *>::iterator begin, end, current;
std::deque<PyObject *>::reverse_iterator rbegin, rend, rcurrent;
PyDequeIter(PyObject *ref, std::deque<PyObject *>::iterator begin, std::deque<PyObject *>::iterator end)
: ref(ref), begin(begin), end(end), current(begin)
{
@ -24,7 +18,6 @@ namespace pkpy
{
this->is_reversed = true;
}
void _gc_mark() const { PK_OBJ_MARK(ref); }
static void _register(VM *vm, PyObject *mod, PyObject *type);
};
@ -52,7 +45,6 @@ namespace pkpy
return ret;
} });
}
// STARTING HERE
struct PyDeque
{
PY_CLASS(PyDeque, collections, deque);
@ -64,7 +56,7 @@ 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::stringstream getRepr(VM *vm); // get the string representation of the deque
std::stringstream 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,50 +66,11 @@ namespace pkpy
vm->bind(type, "__new__(cls, iterable=None, maxlen=None)",
[](VM *vm, ArgsView args)
{
// printf("NEW CALLED!!\n");
Type cls_t = PK_OBJ_GET(Type, args[0]);
PyObject *iterable = args[1];
PyObject *maxlen = args[2];
return vm->heap.gcnew<PyDeque>(cls_t, vm, iterable, maxlen);
});
// vm->bind(type, "__init__(self, iterable=None, maxlen=None)",
// [](VM *vm, ArgsView args)
// {
// // printf("INIT CALLED!!\n");
// PyDeque &self = _CAST(PyDeque &, args[0]);
// PyObject *iterable = args[1];
// PyObject *maxlen = args[2];
// if (!vm->py_equals(maxlen, vm->None)) // fix the maxlen first
// {
// int tmp = CAST(int, maxlen);
// if (tmp < 0)
// vm->ValueError("maxlen must be non-negative");
// else
// {
// self.maxlen = tmp;
// self.bounded = true;
// }
// }
// else
// {
// self.bounded = false;
// self.maxlen = -1;
// }
// if (!vm->py_equals(iterable, vm->None))
// {
// self.dequeItems.clear(); // clear the deque
// auto _lock = vm->heap.gc_scope_lock(); // locking the heap
// PyObject *it = vm->py_iter(args[1]); // strong ref
// PyObject *obj = vm->py_next(it);
// while (obj != vm->StopIteration)
// {
// self.insertObj(false, true, -1, obj);
// obj = vm->py_next(it);
// }
// }
// return args[0];
// });
// 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",
@ -172,18 +125,15 @@ namespace pkpy
[](VM *vm, ArgsView args)
{
PyDeque &self = _CAST(PyDeque &, args[0]);
std::stringstream ss;
ss << "deque([";
for (auto it = self.dequeItems.begin(); it != self.dequeItems.end(); ++it)
{
if (*it == args[0])
ss << "[...]";
else
ss << CAST(Str &, vm->py_repr(*it));
if (it != self.dequeItems.end() - 1)
ss << ", ";
}
self.bounded ? ss << "], maxlen=" << self.maxlen << ")" : ss << "])";
std::stringstream ss = self.getRepr(vm, args[0]);
return VAR(ss.str());
});
// returns a string representation of the deque
vm->bind(type, "__str__(self) -> str",
[](VM *vm, ArgsView args)
{
PyDeque &self = _CAST(PyDeque &, args[0]);
std::stringstream ss = self.getRepr(vm, args[0]);
return VAR(ss.str());
});
// enables comparison between two deques, == and != are supported
@ -287,8 +237,8 @@ namespace pkpy
{
if (vm->py_equals((*it), obj))
cnt++;
if (sz != self.dequeItems.size()) // mutating the deque during iteration is not allowed
vm->RuntimeError("deque mutated during iteration"); // TODO replace this with RuntimeError
if (sz != self.dequeItems.size())// mutating the deque during iteration is not allowed
vm->RuntimeError("deque mutated during iteration");
}
return VAR(cnt);
});
@ -477,7 +427,22 @@ namespace pkpy
}
}
}
std::stringstream 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;
}
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
@ -504,8 +469,8 @@ namespace pkpy
{
if (vm->py_equals(this->dequeItems[i], obj))
return i;
if (sz != this->dequeItems.size()) // mutating the deque during iteration is not allowed
vm->RuntimeError("deque mutated during iteration"); // TODO replace this with RuntimeError
if (sz != this->dequeItems.size())// mutating the deque during iteration is not allowed
vm->RuntimeError("deque mutated during iteration");
}
return -1;
}

View File

@ -23,16 +23,10 @@ assert q == deque([1, 2])
def assertEqual(a, b):
assert a == b
def assertNotEqual(a, b):
assert a != b
def assertRaises(callable, *args, **kwargs):
callable(*args, **kwargs)
print("X Failed Tests for {} for args: {} {}".format(
str(callable), str(args), str(kwargs)))
def printFailed(function_name, *args, **kwargs):
print("X Failed Tests for {} for args: {} {}".format(str(function_name), str(args), str(kwargs)))
BIG = 100000
@ -67,12 +61,16 @@ assertEqual(list(d), list(range(50, 150)))
try:
dq = deque()
dq.maxlen = -1
printFailed("deque.maxlen", -1)
exit(1)
except AttributeError:
pass
try:
dq = deque()
dq.maxlen = -2
printFailed("deque.maxlen", -2)
exit(1)
except AttributeError:
pass
@ -122,7 +120,7 @@ assertEqual(deque('abc', maxlen=0).maxlen, 0)
try:
d = deque('abc')
d.maxlen = 10
print("X Failed Tests!!")
printFailed("deque.maxlen", 10)
exit(1)
except AttributeError:
pass
@ -135,14 +133,14 @@ for s in ('', 'abracadabra', 'simsalabim'*500+'abc'):
assertEqual(s.count(letter), d.count(letter))
try:
d.count()
print("X Failed Tests!!")
printFailed("deque.count")
exit(1)
except TypeError:
pass
try:
d.count(1, 2)
print("X Failed Tests!!")
printFailed("deque.count", 1, 2)
exit(1)
except TypeError:
pass
@ -157,7 +155,7 @@ d = deque([1, 2, BadCompare(), 3])
try:
d.count(2)
print("X Failed Tests!!")
printFailed("deque.count", 2)
exit(1)
except ArithmeticError:
pass
@ -165,7 +163,7 @@ except ArithmeticError:
d = deque([1, 2, 3])
try:
d.count(BadCompare())
print("X Failed Tests!!")
printFailed("deque.count", "BadCompare()")
exit(1)
except ArithmeticError:
pass
@ -183,7 +181,7 @@ m.d = d
try:
d.count(3)
print("X Failed Tests!")
printFailed("deque.count", "MutatingCompare()")
exit(1)
except RuntimeError:
pass
@ -239,7 +237,7 @@ d = deque(range(n))
d[n//2] = MutateCmp(d, False)
try:
n in d
print("X Failed Tests!")
printFailed("deque.__contains__", n)
exit(1)
except RuntimeError:
pass
@ -255,7 +253,7 @@ d = deque(range(n))
d[n//2] = BadCmp()
try:
n in d
print("X Failed Tests!")
printFailed("deque.__contains__", n)
exit(1)
except RuntimeError:
pass
@ -273,7 +271,7 @@ d = deque([A(), A()])
try:
_ = 3 in d
print("X Failed Tests!")
printFailed("deque.__contains__", 3)
exit(1)
except RuntimeError:
pass
@ -281,7 +279,7 @@ except RuntimeError:
d = deque([A(), A()])
try:
_ = d.count(3)
print("X Failed Tests!")
printFailed("deque.count", 3)
exit(1)
except RuntimeError:
pass
@ -293,7 +291,7 @@ except RuntimeError:
d = deque('a')
try:
d.extend(1)
print("X Failed Tests!")
printFailed("deque.extend", 1)
exit(1)
except TypeError:
pass
@ -307,7 +305,7 @@ assertEqual(list(d), list('abcdabcd'))
d = deque('a')
try:
d.extendleft(1)
print("X Failed Tests!")
printFailed("deque.extendleft", 1)
exit(1)
except TypeError:
pass
@ -320,7 +318,7 @@ d.extendleft(range(1000))
assertEqual(list(d), list(reversed(range(1000))))
try:
d.extendleft(fail())
print("X Failed Tests!")
printFailed("deque.extendleft", fail())
exit(1)
except SyntaxError:
pass
@ -345,13 +343,13 @@ assertEqual(d[-1], 'n')
d = deque()
try:
d.__getitem__(0)
print("X Failed Tests!")
printFailed("deque.__getitem__", 0)
exit(1)
except IndexError:
pass
try:
d.__getitem__(-1)
print("X Failed Tests!")
printFailed("deque.__getitem__", -1)
exit(1)
except IndexError:
pass
@ -366,7 +364,7 @@ for n in 1, 2, 30, 40, 200:
try:
d.index(n+1)
print("X Failed Tests!")
printFailed("deque.index", n+1)
exit(1)
except ValueError:
pass
@ -377,7 +375,7 @@ for n in 1, 2, 30, 40, 200:
try:
d.index(n)
print("X Failed Tests!")
printFailed("deque.index", n)
exit(1)
except RuntimeError:
pass
@ -388,7 +386,7 @@ for n in 1, 2, 30, 40, 200:
try:
d.index(n)
print("X Failed Tests!")
printFailed("deque.index", n)
exit(1)
except RuntimeError:
pass
@ -430,7 +428,7 @@ for step in range(100):
d = deque('A' * 3)
try:
d.index('A', 1, 0)
print("X Failed Tests!")
printFailed("deque.index", 'A', 1, 0)
exit(1)
except ValueError:
pass
@ -451,7 +449,7 @@ data = 'ABC'
d = deque(data, maxlen=len(data))
try:
d.insert(0, 'Z')
print("X Failed Tests!")
printFailed("deque.insert", 0, 'Z')
exit(1)
except IndexError:
pass
@ -484,14 +482,14 @@ n = 500 # O(n**2) test, don't make this too big
d = deque(range(n))
try:
d.__delitem__(-n-1)
print("X Failed Tests!")
printFailed("deque.__delitem__", -n-1)
exit(1)
except IndexError:
pass
try:
d.__delitem__(n)
print("X Failed Tests!")
printFailed("deque.__delitem__", n)
exit(1)
except IndexError:
pass
@ -518,7 +516,7 @@ for i in range(n):
assertEqual(list(d), data[:i])
try:
d.reverse(1)
print("X Failed Tests!")
printFailed("deque.reverse", 1)
exit(1)
except TypeError:
pass
@ -570,14 +568,14 @@ for i in range(BIG+17):
assertEqual(tuple(d), tuple(e))
try:
d.rotate(1, 2)
print("X Failed Tests!")
printFailed("deque.rotate", 1, 2)
exit(1)
except TypeError:
pass
try:
d.rotate(1, 10)
print("X Failed Tests!")
printFailed("deque.rotate", 1, 10)
exit(1)
except TypeError:
pass
@ -596,7 +594,7 @@ d.pop()
assertEqual(len(d), 0)
try:
d.pop()
print("X Failed Tests!")
printFailed("deque.pop")
exit(1)
except IndexError:
pass
@ -613,13 +611,13 @@ assertEqual(len(d), 0)
d = deque()
try:
d.pop()
print("X Failed Tests!")
printFailed("deque.pop")
exit(1)
except IndexError:
pass
try:
d.popleft()
print("X Failed Tests!")
printFailed("deque.popleft")
exit(1)
except IndexError:
pass
@ -642,7 +640,7 @@ d.remove('c')
assertEqual(d, deque('abdefghij'))
try:
d.remove('c')
print("X Failed Tests!")
printFailed("deque.remove", "c")
exit(1)
except ValueError:
pass
@ -654,7 +652,7 @@ e = deque(d)
try:
d.remove('c')
print("X Failed Tests!")
printFailed("deque.remove", "c")
exit(1)
except RuntimeError:
pass
@ -668,7 +666,7 @@ for match in (True, False):
d.extend([MutateCmp(d, match), 'c'])
try:
d.remove('c')
print("X Failed Tests!")
printFailed("deque.remove", "c")
exit(1)
except IndexError:
pass
@ -687,13 +685,13 @@ assertEqual(repr(d)[-20:], '7, 198, 199, [...]])')
try:
deque('abc', 2, 3)
print("X Failed Tests!")
printFailed("deque", 'abc', 2, 3)
exit(1)
except TypeError:
pass
try:
deque(1)
print("X Failed Tests!")
printFailed("deque", 1)
exit(1)
except TypeError:
pass
@ -701,7 +699,7 @@ except TypeError:
######### test hash #############
try:
assertRaises(hash, deque('abc'))
hash(deque('abcd'))
except TypeError:
pass