From 140255650dfc9f91dbea35b01c2b07ce26640fe8 Mon Sep 17 00:00:00 2001 From: blueloveTH Date: Sun, 7 Jul 2024 23:51:48 +0800 Subject: [PATCH] ... --- include/pocketpy/interpreter/vm.h | 8 +- scripts/run_tests.py | 6 +- src/compiler/compiler.c | 8 +- src/interpreter/ceval.c | 50 +++++++++--- src/interpreter/vm.c | 2 +- src/public/py_list.c | 60 +++++++++------ src/public/py_tuple.c | 121 ++++++++++++++++++++++++++++-- src/public/stack_ops.c | 32 ++++---- src/public/vm.c | 12 ++- tests/05_list.py | 19 +++++ tests/06_tuple.py | 18 ++--- 11 files changed, 260 insertions(+), 76 deletions(-) diff --git a/include/pocketpy/interpreter/vm.h b/include/pocketpy/interpreter/vm.h index 0c2d714e..b2d107c0 100644 --- a/include/pocketpy/interpreter/vm.h +++ b/include/pocketpy/interpreter/vm.h @@ -42,8 +42,8 @@ typedef struct pk_VM { pk_NameDict modules; c11_vector /*T=pk_TypeInfo*/ types; - py_TValue builtins; // builtins module - py_TValue main; // __main__ module + py_TValue builtins; // builtins module + py_TValue main; // __main__ module void (*_ceval_on_step)(Frame*, Bytecode); unsigned char* (*_import_file)(const char*); @@ -53,7 +53,7 @@ typedef struct pk_VM { py_TValue last_retval; bool has_error; bool is_stopiteration; - + py_TValue reg[8]; // users' registers py_TValue __curr_class; @@ -93,6 +93,7 @@ pk_FrameResult pk_VM__vectorcall(pk_VM* self, uint16_t argc, uint16_t kwargc, bo const char* pk_opname(Opcode op); py_TValue* pk_arrayview(py_Ref self, int* length); +int pk_arrayeq(py_TValue* lhs, int lhs_length, py_TValue* rhs, int rhs_length); /// Assumes [a, b] are on the stack, performs a binary op. /// The result is stored in `self->last_retval`. @@ -106,6 +107,7 @@ py_Type pk_str__register(); py_Type pk_str_iterator__register(); py_Type pk_bytes__register(); py_Type pk_list__register(); +py_Type pk_tuple__register(); py_Type pk_function__register(); py_Type pk_nativefunc__register(); py_Type pk_range__register(); diff --git a/scripts/run_tests.py b/scripts/run_tests.py index e0e29b11..63f01f73 100644 --- a/scripts/run_tests.py +++ b/scripts/run_tests.py @@ -33,8 +33,10 @@ def test_dir(path): else: if not test_file(filepath): print('-' * 50) - print("TEST FAILED! Press any key to continue...") - input() + print("TEST FAILED!") + exit(1) + # print("TEST FAILED! Press any key to continue...") + # input() print('CPython:', str(sys.version).replace('\n', '')) print('System:', '64-bit' if sys.maxsize > 2**32 else '32-bit') diff --git a/src/compiler/compiler.c b/src/compiler/compiler.c index 9e95ce75..ab484bef 100644 --- a/src/compiler/compiler.c +++ b/src/compiler/compiler.c @@ -179,10 +179,16 @@ bool StarredExpr__emit_store(Expr* self_, Ctx* ctx) { return vtemit_store(self->child, ctx); } +void StarredExpr__dtor(Expr* self_) { + StarredExpr* self = (StarredExpr*)self_; + vtdelete(self->child); +} + StarredExpr* StarredExpr__new(int line, Expr* child, int level) { const static ExprVt Vt = {.emit_ = StarredExpr__emit_, .emit_store = StarredExpr__emit_store, - .is_starred = true}; + .is_starred = true, + .dtor = StarredExpr__dtor}; static_assert_expr_size(StarredExpr); StarredExpr* self = PoolExpr_alloc(); self->vt = &Vt; diff --git a/src/interpreter/ceval.c b/src/interpreter/ceval.c index 78537d41..52b8da00 100644 --- a/src/interpreter/ceval.c +++ b/src/interpreter/ceval.c @@ -98,7 +98,7 @@ pk_FrameResult pk_VM__run_top_frame(pk_VM* self) { break; } case tp_tuple: { - pk_sprintf(&buf, "tuple(%d)", py_list__len(p)); + pk_sprintf(&buf, "tuple(%d)", py_tuple__len(p)); break; } case tp_function: { @@ -313,14 +313,15 @@ pk_FrameResult pk_VM__run_top_frame(pk_VM* self) { py_Ref magic = py_tpfindmagic(SECOND()->type, __getitem__); if(magic) { if(magic->type == tp_nativefunc) { + py_TValue* next_sp = TOP(); bool ok = magic->_cfunc(2, SECOND()); if(!ok) goto __ERROR; - POP(); + SP() = next_sp; *TOP() = self->last_retval; } else { INSERT_THIRD(); // [?, a, b] *THIRD() = *magic; // [__getitem__, a, b] - vectorcall_opcall(2, 0); + vectorcall_opcall(1, 0); } DISPATCH(); } @@ -366,14 +367,14 @@ pk_FrameResult pk_VM__run_top_frame(pk_VM* self) { py_Ref magic = py_tpfindmagic(SECOND()->type, __setitem__); if(magic) { if(magic->type == tp_nativefunc) { + py_TValue* next_sp = THIRD(); bool ok = magic->_cfunc(3, THIRD()); if(!ok) goto __ERROR; - STACK_SHRINK(3); + SP() = next_sp; *TOP() = self->last_retval; } else { - INSERT_THIRD(); // [?, a, b] *FOURTH() = *magic; // [__selitem__, a, b, val] - vectorcall_opcall(3, 0); + vectorcall_opcall(2, 0); POP(); // discard retval } DISPATCH(); @@ -437,13 +438,14 @@ pk_FrameResult pk_VM__run_top_frame(pk_VM* self) { py_Ref magic = py_tpfindmagic(SECOND()->type, __delitem__); if(magic) { if(magic->type == tp_nativefunc) { + py_TValue* next_sp = SECOND(); bool ok = magic->_cfunc(2, SECOND()); if(!ok) goto __ERROR; - STACK_SHRINK(2); + SP() = next_sp; } else { INSERT_THIRD(); // [?, a, b] *THIRD() = *magic; // [__delitem__, a, b] - vectorcall_opcall(2, 0); + vectorcall_opcall(1, 0); POP(); // discard retval } DISPATCH(); @@ -567,18 +569,19 @@ pk_FrameResult pk_VM__run_top_frame(pk_VM* self) { DISPATCH(); } case OP_CONTAINS_OP: { - // [b, a] -> b __contains__ a (a in b) + // [b, a] -> b __contains__ a (a in b) -> [retval] py_Ref magic = py_tpfindmagic(SECOND()->type, __contains__); if(magic) { if(magic->type == tp_nativefunc) { + py_TValue* next_sp = TOP(); bool ok = magic->_cfunc(2, SECOND()); if(!ok) goto __ERROR; - POP(); + SP() = next_sp; *TOP() = self->last_retval; } else { INSERT_THIRD(); // [?, b, a] *THIRD() = *magic; // [__contains__, a, b] - vectorcall_opcall(2, 0); + vectorcall_opcall(1, 0); } bool res = py_tobool(TOP()); if(byte.arg) py_newbool(TOP(), !res); @@ -713,6 +716,28 @@ pk_FrameResult pk_VM__run_top_frame(pk_VM* self) { if(!stack_unpack_sequence(self, byte.arg)) goto __ERROR; DISPATCH(); } + case OP_UNPACK_EX: { + int length; + py_TValue* p = pk_arrayview(TOP(), &length); + if(!p) { + TypeError("expected list or tuple to unpack, got '%t'", TOP()->type); + goto __ERROR; + } + int exceed = length - byte.arg; + if(exceed < 0) { + ValueError("not enough values to unpack"); + goto __ERROR; + } + POP(); + for(int i = 0; i < byte.arg; i++) { + PUSH(p + i); + } + py_newlistn(SP()++, exceed); + for(int i = 0; i < exceed; i++) { + py_list__setitem(TOP(), i, p + byte.arg + i); + } + DISPATCH(); + } /////////// case OP_RAISE_ASSERT: { @@ -781,7 +806,8 @@ static bool stack_unpack_sequence(pk_VM* self, uint16_t arg) { if(!p) return TypeError("expected list or tuple to unpack, got '%t'", TOP()->type); if(length != arg) return ValueError("expected %d values to unpack, got %d", arg, length); POP(); - for(int i = 0; i < length; i++) + for(int i = 0; i < length; i++) { PUSH(p + i); + } return true; } diff --git a/src/interpreter/vm.c b/src/interpreter/vm.c index 49bbb0e2..01dc2e8f 100644 --- a/src/interpreter/vm.c +++ b/src/interpreter/vm.c @@ -99,7 +99,7 @@ void pk_VM__ctor(pk_VM* self) { validate(tp_str_iterator, pk_str_iterator__register()); validate(tp_list, pk_list__register()); - validate(tp_tuple, pk_VM__new_type(self, "tuple", tp_object, NULL, false)); + validate(tp_tuple, pk_tuple__register()); validate(tp_slice, pk_VM__new_type(self, "slice", tp_object, NULL, false)); validate(tp_range, pk_range__register()); diff --git a/src/public/py_list.c b/src/public/py_list.c index 8e6a3b49..c97f5760 100644 --- a/src/public/py_list.c +++ b/src/public/py_list.c @@ -3,6 +3,7 @@ #include "pocketpy/common/utils.h" #include "pocketpy/objects/object.h" #include "pocketpy/interpreter/vm.h" +#include "pocketpy/common/sstream.h" typedef c11_vector List; @@ -68,25 +69,13 @@ static bool _py_list__len__(int argc, py_Ref argv) { static bool _py_list__eq__(int argc, py_Ref argv) { PY_CHECK_ARGC(2); - py_Ref _0 = py_arg(0); - py_Ref _1 = py_arg(1); - if(py_istype(_1, tp_list)) { - int length = py_list__len(_0); - if(length != py_list__len(_1)) { - py_newbool(py_retval(), false); - return true; - } - for(int i = 0; i < length; i++) { - py_Ref a = py_list__getitem(_0, i); - py_Ref b = py_list__getitem(_1, i); - int res = py_eq(a, b); - if(res == -1) return false; - if(res == 0) { - py_newbool(py_retval(), false); - return true; - } - } - py_newbool(py_retval(), true); + if(py_istype(py_arg(1), tp_list)) { + int length0, length1; + py_TValue* a0 = pk_arrayview(py_arg(0), &length0); + py_TValue* a1 = pk_arrayview(py_arg(1), &length1); + int res = pk_arrayeq(a0, length0, a1, length1); + if(res == -1) return false; + py_newbool(py_retval(), res); } else { py_newnotimplemented(py_retval()); } @@ -225,6 +214,30 @@ static bool _py_list__append(int argc, py_Ref argv) { return true; } +static bool _py_list__repr__(int argc, py_Ref argv) { + List* self = py_touserdata(py_arg(0)); + c11_sbuf buf; + c11_sbuf__ctor(&buf); + c11_sbuf__write_char(&buf, '['); + for(int i = 0; i < self->count; i++) { + py_TValue* val = c11__at(py_TValue, self, i); + bool ok = py_repr(val); + if(!ok) { + c11_sbuf__dtor(&buf); + return false; + } + int size; + const char* data = py_tostrn(py_retval(), &size); + c11_sbuf__write_cstrn(&buf, data, size); + if(i != self->count - 1) c11_sbuf__write_cstr(&buf, ", "); + } + c11_sbuf__write_char(&buf, ']'); + c11_string* res = c11_sbuf__submit(&buf); + py_newstrn(py_retval(), res->data, res->size); + c11_string__delete(res); + return true; +} + static bool _py_list__extend(int argc, py_Ref argv) { PY_CHECK_ARGC(2); List* self = py_touserdata(py_arg(0)); @@ -355,10 +368,10 @@ static bool _py_list__sort(int argc, py_Ref argv) { if(py_isnone(key)) key = NULL; bool ok = c11__stable_sort(self->data, - self->count, - sizeof(py_TValue), - (int (*)(const void*, const void*, void*))_py_lt_with_key, - key); + self->count, + sizeof(py_TValue), + (int (*)(const void*, const void*, void*))_py_lt_with_key, + key); if(!ok) return false; PY_CHECK_ARG_TYPE(2, tp_bool); @@ -384,6 +397,7 @@ py_Type pk_list__register() { py_bindmagic(type, __add__, _py_list__add__); py_bindmagic(type, __mul__, _py_list__mul__); py_bindmagic(type, __rmul__, _py_list__rmul__); + py_bindmagic(type, __repr__, _py_list__repr__); py_bindmethod(type, "append", _py_list__append); py_bindmethod(type, "extend", _py_list__extend); diff --git a/src/public/py_tuple.c b/src/public/py_tuple.c index cc6e8c22..87c05409 100644 --- a/src/public/py_tuple.c +++ b/src/public/py_tuple.c @@ -1,6 +1,7 @@ #include "pocketpy/pocketpy.h" #include "pocketpy/common/utils.h" +#include "pocketpy/common/sstream.h" #include "pocketpy/objects/object.h" #include "pocketpy/interpreter/vm.h" @@ -12,14 +13,122 @@ void py_newtuple(py_Ref out, int n) { out->_obj = obj; } -py_Ref py_tuple__getitem(const py_Ref self, int i){ - return py_getslot(self, i); +py_Ref py_tuple__getitem(const py_Ref self, int i) { return py_getslot(self, i); } + +void py_tuple__setitem(py_Ref self, int i, const py_Ref val) { py_setslot(self, i, val); } + +int py_tuple__len(const py_Ref self) { return self->_obj->slots; } + +////////////// +static bool _py_tuple__len__(int argc, py_Ref argv) { + py_newint(py_retval(), py_tuple__len(argv)); + return true; } -void py_tuple__setitem(py_Ref self, int i, const py_Ref val){ - py_setslot(self, i, val); +static bool _py_tuple__repr__(int argc, py_Ref argv) { + c11_sbuf buf; + c11_sbuf__ctor(&buf); + c11_sbuf__write_char(&buf, '('); + int length = py_tuple__len(argv); + for(int i = 0; i < length; i++) { + py_TValue* val = py_getslot(argv, i); + bool ok = py_repr(val); + if(!ok) { + c11_sbuf__dtor(&buf); + return false; + } + int size; + const char* data = py_tostrn(py_retval(), &size); + c11_sbuf__write_cstrn(&buf, data, size); + if(i != length - 1) c11_sbuf__write_cstr(&buf, ", "); + } + if(length == 1) c11_sbuf__write_char(&buf, ','); + c11_sbuf__write_char(&buf, ')'); + c11_string* res = c11_sbuf__submit(&buf); + py_newstrn(py_retval(), res->data, res->size); + c11_string__delete(res); + return true; } -int py_tuple__len(const py_Ref self){ - return self->_obj->slots; +static bool _py_tuple__new__(int argc, py_Ref argv) { + if(argc == 1 + 0) { + py_newtuple(py_retval(), 0); + return true; + } + if(argc == 1 + 1) { + bool ok = py_tpcall(tp_list, 1, py_arg(1)); + if(!ok) return false; + py_Ref tmp = py_pushtmp(); + *tmp = *py_retval(); // backup the list + int length = py_list__len(tmp); + py_newtuple(py_retval(), length); + for(int i = 0; i < py_tuple__len(py_retval()); i++) { + py_tuple__setitem(py_retval(), i, py_list__getitem(tmp, i)); + } + return true; + } + return TypeError("tuple() takes at most 1 argument"); +} + +static bool _py_tuple__getitem__(int argc, py_Ref argv) { + PY_CHECK_ARGC(2); + int length = py_tuple__len(argv); + py_Ref _1 = py_arg(1); + if(_1->type == tp_int) { + int index = py_toint(py_arg(1)); + if(!pk__normalize_index(&index, length)) return false; + *py_retval() = *py_getslot(argv, index); + return true; + } else if(_1->type == tp_slice) { + int start, stop, step; + bool ok = pk__parse_int_slice(_1, length, &start, &stop, &step); + if(!ok) return false; + py_Ref tmp = py_pushtmp(); + py_newlist(tmp); + PK_SLICE_LOOP(i, start, stop, step) py_list__append(tmp, py_getslot(argv, i)); + // convert list to tuple + py_newtuple(py_retval(), py_list__len(tmp)); + for(int i = 0; i < py_tuple__len(py_retval()); i++) { + py_tuple__setitem(py_retval(), i, py_list__getitem(tmp, i)); + } + return true; + } else { + return TypeError("tuple indices must be integers"); + } +} + +static bool _py_tuple__eq__(int argc, py_Ref argv) { + PY_CHECK_ARGC(2); + if(py_istype(py_arg(1), tp_tuple)) { + int length0, length1; + py_TValue* a0 = pk_arrayview(py_arg(0), &length0); + py_TValue* a1 = pk_arrayview(py_arg(1), &length1); + int res = pk_arrayeq(a0, length0, a1, length1); + if(res == -1) return false; + py_newbool(py_retval(), res); + } else { + py_newnotimplemented(py_retval()); + } + return true; +} + +static bool _py_tuple__ne__(int argc, py_Ref argv) { + bool ok = _py_tuple__eq__(argc, argv); + if(!ok) return false; + py_Ref retval = py_retval(); + py_newbool(retval, !py_tobool(retval)); + return true; +} + +py_Type pk_tuple__register() { + pk_VM* vm = pk_current_vm; + py_Type type = pk_VM__new_type(vm, "tuple", tp_object, NULL, false); + + py_bindmagic(type, __len__, _py_tuple__len__); + py_bindmagic(type, __repr__, _py_tuple__repr__); + py_bindmagic(type, __new__, _py_tuple__new__); + py_bindmagic(type, __getitem__, _py_tuple__getitem__); + py_bindmagic(type, __eq__, _py_tuple__eq__); + py_bindmagic(type, __ne__, _py_tuple__ne__); + return type; } diff --git a/src/public/stack_ops.c b/src/public/stack_ops.c index adb9581d..e72f67e4 100644 --- a/src/public/stack_ops.c +++ b/src/public/stack_ops.c @@ -4,13 +4,11 @@ #include "pocketpy/objects/object.h" #include "pocketpy/interpreter/vm.h" -py_Ref py_reg(int i){ - return pk_current_vm->reg + i; -} +py_Ref py_reg(int i) { return pk_current_vm->reg + i; } -py_Ref py_getdict(const py_Ref self, py_Name name){ +py_Ref py_getdict(const py_Ref self, py_Name name) { assert(self && self->is_ptr); - if(self->type == tp_type && py_ismagicname(name)){ + if(self->type == tp_type && py_ismagicname(name)) { py_Type* ud = py_touserdata(self); py_Ref slot = py_tpmagic(*ud, name); return py_isnil(slot) ? NULL : slot; @@ -18,58 +16,56 @@ py_Ref py_getdict(const py_Ref self, py_Name name){ return pk_NameDict__try_get(PyObject__dict(self->_obj), name); } -void py_setdict(py_Ref self, py_Name name, const py_Ref val){ +void py_setdict(py_Ref self, py_Name name, const py_Ref val) { assert(self && self->is_ptr); - if(self->type == tp_type && py_ismagicname(name)){ + if(self->type == tp_type && py_ismagicname(name)) { py_Type* ud = py_touserdata(self); *py_tpmagic(*ud, name) = *val; } pk_NameDict__set(PyObject__dict(self->_obj), name, *val); } -py_Ref py_getslot(const py_Ref self, int i){ +py_Ref py_getslot(const py_Ref self, int i) { assert(self && self->is_ptr); assert(i >= 0 && i < self->_obj->slots); return PyObject__slots(self->_obj) + i; } -void py_setslot(py_Ref self, int i, const py_Ref val){ +void py_setslot(py_Ref self, int i, const py_Ref val) { assert(self && self->is_ptr); assert(i >= 0 && i < self->_obj->slots); PyObject__slots(self->_obj)[i] = *val; } -void py_assign(py_Ref dst, const py_Ref src){ - *dst = *src; -} +void py_assign(py_Ref dst, const py_Ref src) { *dst = *src; } /* Stack References */ -py_Ref py_peek(int i){ +py_Ref py_peek(int i) { assert(i < 0); return pk_current_vm->stack.sp + i; } -void py_pop(){ +void py_pop() { pk_VM* vm = pk_current_vm; vm->stack.sp--; } -void py_shrink(int n){ +void py_shrink(int n) { pk_VM* vm = pk_current_vm; vm->stack.sp -= n; } -void py_push(const py_Ref src){ +void py_push(const py_Ref src) { pk_VM* vm = pk_current_vm; *vm->stack.sp++ = *src; } -void py_pushnil(){ +void py_pushnil() { pk_VM* vm = pk_current_vm; py_newnil(vm->stack.sp++); } -py_Ref py_pushtmp(){ +py_Ref py_pushtmp() { pk_VM* vm = pk_current_vm; py_newnil(vm->stack.sp++); return py_gettop(); diff --git a/src/public/vm.c b/src/public/vm.c index 872d5c82..fd078314 100644 --- a/src/public/vm.c +++ b/src/public/vm.c @@ -42,11 +42,21 @@ py_TValue* pk_arrayview(py_Ref self, int* length) { } if(self->type == tp_tuple) { *length = py_tuple__len(self); - return py_tuple__getitem(self, 0); + return PyObject__slots(self->_obj); } return NULL; } +int pk_arrayeq(py_TValue* lhs, int lhs_length, py_TValue* rhs, int rhs_length) { + if(lhs_length != rhs_length) return false; + for(int i = 0; i < lhs_length; i++) { + int res = py_eq(lhs + i, rhs + i); + if(res == -1) return -1; + if(!res) return false; + } + return true; +} + static void disassemble(CodeObject* co) { c11_vector /*T=int*/ jumpTargets; c11_vector__ctor(&jumpTargets, sizeof(int)); diff --git a/tests/05_list.py b/tests/05_list.py index cd830686..2b3d589f 100644 --- a/tests/05_list.py +++ b/tests/05_list.py @@ -133,6 +133,25 @@ assert sorted(a, key=key) == [9, 8, 4, 2, 2] assert sorted(a, key=key, reverse=True) == [2, 2, 4, 8, 9] assert a == [8, 2, 4, 2, 9] +# test unpack ex +a, *b = [1,2,3,4] +assert a == 1 +assert b == [2,3,4] + +a, b, *c = [1, 2] +assert a == 1 +assert b == 2 +assert c == [] + +a, b, c, *d = [1, 2, 3, 4, 5] +assert d == [4, 5] + +# test repr +assert repr([1, 2, 3]) == "[1, 2, 3]" +assert repr([1, [2, 3], 4]) == "[1, [2, 3], 4]" +assert repr([1, [2, [3, 4]], 5]) == "[1, [2, [3, 4]], 5]" +assert repr([]) == "[]" + # b = [(1, 2), (3, 3), (5, 1)] # b.sort(key=lambda x:x[1]) # assert b == [(5, 1), (1, 2), (3,3)] diff --git a/tests/06_tuple.py b/tests/06_tuple.py index 9ec50679..8c07a0b5 100644 --- a/tests/06_tuple.py +++ b/tests/06_tuple.py @@ -7,15 +7,9 @@ assert a == 2 assert b == 1 assert len(tup) == 6 -# unpacking builder -a = 1, 2, 3 -b = *a, 4, 5 -assert b == (1, 2, 3, 4, 5) - +# empty tuple a = tuple([]) -b = *a, 1, 2, 3, *a, *a -assert b == (1, 2, 3) - +assert len(a) == 0 assert (1,) == tuple([1]) assert (1,2,) == tuple([1,2]) @@ -30,4 +24,10 @@ assert l[:32] == (1,2,3,4) assert l[32:] == tuple([]) assert l[1:4] == (2,3,4) assert l[-1:-3] == tuple([]) -assert l[-3:-1] == (2,3) \ No newline at end of file +assert l[-3:-1] == (2,3) + +# test repr +assert repr((1,)) == '(1,)' +assert repr((1,2,)) == '(1, 2)' +assert repr((1,2,(3,4))) == '(1, 2, (3, 4))' +assert repr(tuple()) == '()' \ No newline at end of file