blueloveTH 1 rok temu
rodzic
commit
140255650d

+ 5 - 3
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();

+ 4 - 2
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')

+ 7 - 1
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;

+ 38 - 12
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;
 }

+ 1 - 1
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());

+ 37 - 23
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);

+ 115 - 6
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;
+}
+
+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;
+}
+
+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");
 }
 
-void py_tuple__setitem(py_Ref self, int i, const py_Ref val){
-    py_setslot(self, i, val);
+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");
+    }
 }
 
-int py_tuple__len(const py_Ref self){
-    return self->_obj->slots;
+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;
 }

+ 14 - 18
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();

+ 11 - 1
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));

+ 19 - 0
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)]

+ 9 - 9
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)
+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()) == '()'