blueloveTH 1 год назад
Родитель
Сommit
2605382a79

+ 3 - 2
include/pocketpy/interpreter/vm.h

@@ -63,7 +63,7 @@ void pk_VM__dtor(pk_VM* self);
 void pk_VM__push_frame(pk_VM* self, Frame* frame);
 void pk_VM__pop_frame(pk_VM* self);
 
-bool pk__parse_int_slice(const py_Ref slice, int length, int* start, int* stop, int* step);
+bool pk__parse_int_slice(py_Ref slice, int length, int* start, int* stop, int* step);
 bool pk__normalize_index(int* index, int length);
 
 void pk_list__mark(void* ud, void (*marker)(py_TValue*));
@@ -90,8 +90,9 @@ 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);
+int pk_arrayequal(py_TValue* lhs, int lhs_length, py_TValue* rhs, int rhs_length);
 bool pk_arrayiter(py_Ref val);
+bool pk_arraycontains(py_Ref self, py_Ref val);
 
 /// Assumes [a, b] are on the stack, performs a binary op.
 /// The result is stored in `self->last_retval`.

+ 48 - 50
include/pocketpy/pocketpy.h

@@ -105,17 +105,17 @@ py_Type py_newtype(const char* name, py_Type base, const py_GlobalRef module, vo
 /// @param udsize size of your userdata. You can use `py_touserdata()` to get the pointer to it.
 void* py_newobject(py_Ref out, py_Type type, int slots, int udsize);
 /************* Type Cast *************/
-py_i64 py_toint(const py_Ref);
-py_f64 py_tofloat(const py_Ref);
-bool py_castfloat(const py_Ref, py_f64* out);
-bool py_tobool(const py_Ref);
-py_Type py_totype(const py_Ref);
-const char* py_tostr(const py_Ref);
-const char* py_tostrn(const py_Ref, int* size);
-c11_sv py_tosv(const py_Ref);
-unsigned char* py_tobytes(const py_Ref, int* size);
-
-void* py_touserdata(const py_Ref);
+py_i64 py_toint(py_Ref);
+py_f64 py_tofloat(py_Ref);
+bool py_castfloat(py_Ref, py_f64* out);
+bool py_tobool(py_Ref);
+py_Type py_totype(py_Ref);
+const char* py_tostr(py_Ref);
+const char* py_tostrn(py_Ref, int* size);
+c11_sv py_tosv(py_Ref);
+unsigned char* py_tobytes(py_Ref, int* size);
+
+void* py_touserdata(py_Ref);
 
 #define py_isint(self) py_istype(self, tp_int)
 #define py_isfloat(self) py_istype(self, tp_float)
@@ -125,8 +125,8 @@ void* py_touserdata(const py_Ref);
 #define py_istuple(self) py_istype(self, tp_tuple)
 #define py_isdict(self) py_istype(self, tp_dict)
 
-bool py_istype(const py_Ref, py_Type);
-bool py_isinstance(const py_Ref obj, py_Type type);
+bool py_istype(py_Ref, py_Type);
+bool py_isinstance(py_Ref obj, py_Type type);
 bool py_issubclass(py_Type derived, py_Type base);
 
 extern py_GlobalRef py_True;
@@ -182,35 +182,35 @@ py_GlobalRef py_reg(int i);
 /// Get the reference of the object's `__dict__`.
 /// The object must have a `__dict__`.
 /// Returns a reference to the value or NULL if not found.
-py_ObjectRef py_getdict(const py_Ref self, py_Name name);
-void py_setdict(py_Ref self, py_Name name, const py_Ref val);
+py_ObjectRef py_getdict(py_Ref self, py_Name name);
+void py_setdict(py_Ref self, py_Name name, py_Ref val);
 bool py_deldict(py_Ref self, py_Name name);
 
 /// Get the reference of the i-th slot of the object.
 /// The object must have slots and `i` must be in range.
-py_ObjectRef py_getslot(const py_Ref self, int i);
-void py_setslot(py_Ref self, int i, const py_Ref val);
+py_ObjectRef py_getslot(py_Ref self, int i);
+void py_setslot(py_Ref self, int i, py_Ref val);
 
 py_TmpRef py_getupvalue(py_StackRef argv);
-void py_setupvalue(py_StackRef argv, const py_Ref val);
+void py_setupvalue(py_StackRef argv, py_Ref val);
 
 /// Gets the attribute of the object.
 bool py_getattr(py_Ref self, py_Name name);
 /// Sets the attribute of the object.
-bool py_setattr(py_Ref self, py_Name name, const py_Ref val);
+bool py_setattr(py_Ref self, py_Name name, py_Ref val);
 /// Deletes the attribute of the object.
 bool py_delattr(py_Ref self, py_Name name);
 /// Gets the unbound method of the object.
 bool py_getunboundmethod(py_Ref self, py_Name name, py_Ref out, py_Ref out_self);
 
-bool py_getitem(const py_Ref self, const py_Ref key);
-bool py_setitem(py_Ref self, const py_Ref key, const py_Ref val);
-bool py_delitem(py_Ref self, const py_Ref key);
+bool py_getitem(py_Ref self, py_Ref key);
+bool py_setitem(py_Ref self, py_Ref key, py_Ref val);
+bool py_delitem(py_Ref self, py_Ref key);
 
 /// Perform a binary operation on the stack.
 /// It assumes `lhs` and `rhs` are already pushed to the stack.
 /// The result will be set to `py_retval()`.
-bool py_binaryop(const py_Ref lhs, const py_Ref rhs, py_Name op, py_Name rop);
+bool py_binaryop(py_Ref lhs, py_Ref rhs, py_Name op, py_Name rop);
 
 #define py_binaryadd(lhs, rhs) py_binaryop(lhs, rhs, __add__, __radd__)
 #define py_binarysub(lhs, rhs) py_binaryop(lhs, rhs, __sub__, __rsub__)
@@ -228,14 +228,14 @@ bool py_binaryop(const py_Ref lhs, const py_Ref rhs, py_Name op, py_Name rop);
 #define py_binarymatmul(lhs, rhs) py_binaryop(lhs, rhs, __matmul__, 0)
 
 /// Equivalent to `*dst = *src`.
-void py_assign(py_Ref dst, const py_Ref src);
+void py_assign(py_Ref dst, py_Ref src);
 
 /************* Stack Operations *************/
 /// Return a reference to the i-th object from the top of the stack.
 /// i should be negative, e.g. (-1) means TOS.
 py_StackRef py_peek(int i);
 /// Push the object to the stack.
-void py_push(const py_Ref src);
+void py_push(py_Ref src);
 /// Push a nil object to the stack.
 void py_pushnil();
 /// Pop an object from the stack.
@@ -286,7 +286,7 @@ bool KeyError(py_Ref key);
 /// Equivalent to `bool(val)`.
 /// Returns 1 if `val` is truthy, otherwise 0.
 /// Returns -1 if an error occurred.
-int py_bool(const py_Ref val);
+int py_bool(py_Ref val);
 
 #define py_eq(lhs, rhs) py_binaryop(lhs, rhs, __eq__, __eq__)
 #define py_ne(lhs, rhs) py_binaryop(lhs, rhs, __ne__, __ne__)
@@ -295,19 +295,19 @@ int py_bool(const py_Ref val);
 #define py_gt(lhs, rhs) py_binaryop(lhs, rhs, __gt__, __lt__)
 #define py_ge(lhs, rhs) py_binaryop(lhs, rhs, __ge__, __le__)
 
-int py_equal(const py_Ref lhs, const py_Ref rhs);
-int py_less(const py_Ref lhs, const py_Ref rhs);
+int py_equal(py_Ref lhs, py_Ref rhs);
+int py_less(py_Ref lhs, py_Ref rhs);
 
-bool py_hash(const py_Ref, py_i64* out);
+bool py_hash(py_Ref, py_i64* out);
 
 /// Get the iterator of the object.
-bool py_iter(const py_Ref);
+bool py_iter(py_Ref);
 /// Get the next element from the iterator.
 /// 1: success, 0: StopIteration, -1: error
-int py_next(const py_Ref);
+int py_next(py_Ref);
 
 /// Python equivalent to `lhs is rhs`.
-bool py_isidentical(const py_Ref, const py_Ref);
+bool py_isidentical(py_Ref, py_Ref);
 
 /// A stack operation that calls a function.
 /// It assumes `argc + kwargc` arguments are already pushed to the stack.
@@ -344,30 +344,28 @@ py_GlobalRef py_retval();
 /* tuple */
 
 // unchecked functions, if self is not a tuple, the behavior is undefined
-py_ObjectRef py_tuple__data(const py_Ref self);
-py_ObjectRef py_tuple__getitem(const py_Ref self, int i);
-void py_tuple__setitem(py_Ref self, int i, const py_Ref val);
-int py_tuple__len(const py_Ref self);
+py_ObjectRef py_tuple__data(py_Ref self);
+py_ObjectRef py_tuple__getitem(py_Ref self, int i);
+void py_tuple__setitem(py_Ref self, int i, py_Ref val);
+int py_tuple__len(py_Ref self);
 
 // unchecked functions, if self is not a list, the behavior is undefined
-py_TmpRef py_list__data(const py_Ref self);
-py_TmpRef py_list__getitem(const py_Ref self, int i);
-void py_list__setitem(py_Ref self, int i, const py_Ref val);
+py_TmpRef py_list__data(py_Ref self);
+py_TmpRef py_list__getitem(py_Ref self, int i);
+void py_list__setitem(py_Ref self, int i, py_Ref val);
 void py_list__delitem(py_Ref self, int i);
-int py_list__len(const py_Ref self);
-void py_list__append(py_Ref self, const py_Ref val);
+int py_list__len(py_Ref self);
+void py_list__append(py_Ref self, py_Ref val);
 void py_list__clear(py_Ref self);
-void py_list__insert(py_Ref self, int i, const py_Ref val);
+void py_list__insert(py_Ref self, int i, py_Ref val);
 void py_list__reverse(py_Ref self);
 
 // unchecked functions, if self is not a dict, the behavior is undefined
-py_TmpRef py_dict__getitem(const py_Ref self, const py_Ref key);
-void py_dict__setitem(py_Ref self, const py_Ref key, const py_Ref val);
-bool py_dict__contains(const py_Ref self, const py_Ref key);
-int py_dict__len(const py_Ref self);
-bool py_dict__apply(const py_Ref self,
-                    bool (*f)(const py_Ref key, const py_Ref val, void* ctx),
-                    void* ctx);
+py_TmpRef py_dict__getitem(py_Ref self, py_Ref key);
+void py_dict__setitem(py_Ref self, py_Ref key, py_Ref val);
+bool py_dict__contains(py_Ref self, py_Ref key);
+int py_dict__len(py_Ref self);
+bool py_dict__apply(py_Ref self, bool (*f)(py_Ref key, py_Ref val, void* ctx), void* ctx);
 
 /// Search the magic method from the given type to the base type.
 /// Return the reference or NULL if not found.
@@ -387,7 +385,7 @@ const char* py_tpname(py_Type type);
 bool py_tpcall(py_Type type, int argc, py_Ref argv);
 
 /// Check if the object is an instance of the given type.
-bool py_checktype(const py_Ref self, py_Type type);
+bool py_checktype(py_Ref self, py_Type type);
 
 #define py_checkint(self) py_checktype(self, tp_int)
 #define py_checkfloat(self) py_checktype(self, tp_float)

+ 3 - 4
src/interpreter/ceval.c

@@ -60,7 +60,7 @@ static bool format_object(py_Ref obj, c11_sv spec);
         }                                                                                          \
     } while(0)
 
-static bool unpack_dict_to_buffer(const py_Ref key, const py_Ref val, void* ctx) {
+static bool unpack_dict_to_buffer(py_Ref key, py_Ref val, void* ctx) {
     py_TValue** p = ctx;
     if(py_isstr(key)) {
         py_Name name = py_namev(py_tosv(key));
@@ -553,8 +553,7 @@ pk_FrameResult pk_VM__run_top_frame(pk_VM* self) {
                     if(byte.arg) py_newbool(TOP(), !res);
                     DISPATCH();
                 }
-                // TODO: fallback to __iter__?
-                TypeError("argument of type '%t' is not iterable", SECOND()->type);
+                TypeError("'%t' type does not support '__contains__'", SECOND()->type);
                 goto __ERROR;
             }
                 /*****************************************/
@@ -922,7 +921,7 @@ bool pk_stack_binaryop(pk_VM* self, py_Name op, py_Name rop) {
     return py_exception("TypeError", "unsupported operand type(s) for '%n'", op);
 }
 
-bool py_binaryop(const py_Ref lhs, const py_Ref rhs, py_Name op, py_Name rop) {
+bool py_binaryop(py_Ref lhs, py_Ref rhs, py_Name op, py_Name rop) {
     pk_VM* self = pk_current_vm;
     PUSH(lhs);
     PUSH(rhs);

+ 1 - 1
src/interpreter/vm.c

@@ -200,7 +200,7 @@ static void _clip_int(int* value, int min, int max) {
     if(*value > max) *value = max;
 }
 
-bool pk__parse_int_slice(const py_Ref slice, int length, int* start, int* stop, int* step) {
+bool pk__parse_int_slice(py_Ref slice, int length, int* start, int* stop, int* step) {
     py_Ref s_start = py_getslot(slice, 0);
     py_Ref s_stop = py_getslot(slice, 1);
     py_Ref s_step = py_getslot(slice, 2);

+ 8 - 8
src/public/cast.c

@@ -5,17 +5,17 @@
 #include "pocketpy/objects/object.h"
 #include "pocketpy/interpreter/vm.h"
 
-int64_t py_toint(const py_Ref self) {
+int64_t py_toint(py_Ref self) {
     assert(self->type == tp_int);
     return self->_i64;
 }
 
-double py_tofloat(const py_Ref self) {
+double py_tofloat(py_Ref self) {
     assert(self->type == tp_float);
     return self->_f64;
 }
 
-bool py_castfloat(const py_Ref self, double* out) {
+bool py_castfloat(py_Ref self, double* out) {
     switch(self->type) {
         case tp_int: *out = (double)self->_i64; return true;
         case tp_float: *out = self->_f64; return true;
@@ -23,25 +23,25 @@ bool py_castfloat(const py_Ref self, double* out) {
     }
 }
 
-bool py_tobool(const py_Ref self) {
+bool py_tobool(py_Ref self) {
     assert(self->type == tp_bool);
     return self->_bool;
 }
 
-py_Type py_totype(const py_Ref self) {
+py_Type py_totype(py_Ref self) {
     assert(self->type == tp_type);
     py_Type* ud = py_touserdata(self);
     return *ud;
 }
 
-void* py_touserdata(const py_Ref self) {
+void* py_touserdata(py_Ref self) {
     assert(self && self->is_ptr);
     return PyObject__userdata(self->_obj);
 }
 
-bool py_istype(const py_Ref self, py_Type type) { return self->type == type; }
+bool py_istype(py_Ref self, py_Type type) { return self->type == type; }
 
-bool py_checktype(const py_Ref self, py_Type type) {
+bool py_checktype(py_Ref self, py_Type type) {
     if(self->type != type) {
         return TypeError("expected %t, got %t", type, self->type);
     }

+ 17 - 1
src/public/py_array.c

@@ -23,7 +23,7 @@ py_TValue* pk_arrayview(py_Ref self, int* length) {
     return NULL;
 }
 
-int pk_arrayeq(py_TValue* lhs, int lhs_length, py_TValue* rhs, int rhs_length) {
+int pk_arrayequal(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_equal(lhs + i, rhs + i);
@@ -45,6 +45,22 @@ bool pk_arrayiter(py_Ref val) {
     return true;
 }
 
+bool pk_arraycontains(py_Ref self, py_Ref val) {
+    int length;
+    py_TValue* p = pk_arrayview(self, &length);
+    if(!p) return TypeError("expected list or tuple, got %t", self->type);
+    for(int i = 0; i < length; i++) {
+        int res = py_equal(p + i, val);
+        if(res == -1) return false;
+        if(res) {
+            py_newbool(py_retval(), true);
+            return true;
+        }
+    }
+    py_newbool(py_retval(), false);
+    return true;
+}
+
 static bool _py_array_iterator__iter__(int argc, py_Ref argv) {
     PY_CHECK_ARGC(1);
     *py_retval() = *argv;

+ 5 - 5
src/public/py_dict.c

@@ -491,7 +491,7 @@ void py_newdict(py_Ref out) {
     Dict__ctor(ud, 8);
 }
 
-py_Ref py_dict__getitem(const py_Ref self, const py_Ref key) {
+py_Ref py_dict__getitem(py_Ref self, py_Ref key) {
     assert(py_isdict(self));
     Dict* ud = py_touserdata(self);
     DictEntry* entry;
@@ -500,13 +500,13 @@ py_Ref py_dict__getitem(const py_Ref self, const py_Ref key) {
     return NULL;
 }
 
-void py_dict__setitem(py_Ref self, const py_Ref key, const py_Ref val) {
+void py_dict__setitem(py_Ref self, py_Ref key, py_Ref val) {
     assert(py_isdict(self));
     Dict* ud = py_touserdata(self);
     Dict__set(ud, key, val);
 }
 
-bool py_dict__contains(const py_Ref self, const py_Ref key) {
+bool py_dict__contains(py_Ref self, py_Ref key) {
     assert(py_isdict(self));
     Dict* ud = py_touserdata(self);
     DictEntry* entry;
@@ -514,13 +514,13 @@ bool py_dict__contains(const py_Ref self, const py_Ref key) {
     return ok && entry != NULL;
 }
 
-int py_dict__len(const py_Ref self) {
+int py_dict__len(py_Ref self) {
     assert(py_isdict(self));
     Dict* ud = py_touserdata(self);
     return ud->length;
 }
 
-bool py_dict__apply(const py_Ref self, bool (*f)(const py_Ref, const py_Ref, void *), void *ctx){
+bool py_dict__apply(py_Ref self, bool (*f)(py_Ref, py_Ref, void *), void *ctx){
     Dict* ud = py_touserdata(self);
     for(int i = 0; i < ud->entries.count; i++) {
         DictEntry* entry = c11__at(DictEntry, &ud->entries, i);

+ 13 - 7
src/public/py_list.c

@@ -19,17 +19,17 @@ void py_newlistn(py_Ref out, int n) {
     userdata->count = n;
 }
 
-py_Ref py_list__data(const py_Ref self) {
+py_Ref py_list__data(py_Ref self) {
     List* userdata = py_touserdata(self);
     return userdata->data;
 }
 
-py_Ref py_list__getitem(const py_Ref self, int i) {
+py_Ref py_list__getitem(py_Ref self, int i) {
     List* userdata = py_touserdata(self);
     return c11__at(py_TValue, userdata, i);
 }
 
-void py_list__setitem(py_Ref self, int i, const py_Ref val) {
+void py_list__setitem(py_Ref self, int i, py_Ref val) {
     List* userdata = py_touserdata(self);
     c11__setitem(py_TValue, userdata, i, *val);
 }
@@ -39,12 +39,12 @@ void py_list__delitem(py_Ref self, int i) {
     c11_vector__erase(py_TValue, userdata, i);
 }
 
-int py_list__len(const py_Ref self) {
+int py_list__len(py_Ref self) {
     List* userdata = py_touserdata(self);
     return userdata->count;
 }
 
-void py_list__append(py_Ref self, const py_Ref val) {
+void py_list__append(py_Ref self, py_Ref val) {
     List* userdata = py_touserdata(self);
     c11_vector__push(py_TValue, userdata, *val);
 }
@@ -54,7 +54,7 @@ void py_list__clear(py_Ref self) {
     c11_vector__clear(userdata);
 }
 
-void py_list__insert(py_Ref self, int i, const py_Ref val) {
+void py_list__insert(py_Ref self, int i, py_Ref val) {
     List* userdata = py_touserdata(self);
     c11_vector__insert(py_TValue, userdata, i, *val);
 }
@@ -78,7 +78,7 @@ static bool _py_list__eq__(int argc, py_Ref argv) {
         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);
+        int res = pk_arrayequal(a0, length0, a1, length1);
         if(res == -1) return false;
         py_newbool(py_retval(), res);
     } else {
@@ -401,6 +401,11 @@ static bool _py_list__iter__(int argc, py_Ref argv) {
     return pk_arrayiter(argv);
 }
 
+static bool _py_list__contains__(int argc, py_Ref argv) {
+    PY_CHECK_ARGC(2);
+    return pk_arraycontains(py_arg(0), py_arg(1));
+}
+
 py_Type pk_list__register() {
     py_Type type =
         pk_newtype("list", tp_object, NULL, (void (*)(void*))c11_vector__dtor, false, true);
@@ -417,6 +422,7 @@ py_Type pk_list__register() {
     py_bindmagic(type, __rmul__, _py_list__rmul__);
     py_bindmagic(type, __repr__, _py_list__repr__);
     py_bindmagic(type, __iter__, _py_list__iter__);
+    py_bindmagic(type, __contains__, _py_list__contains__);
 
     py_bindmethod(type, "append", _py_list__append);
     py_bindmethod(type, "extend", _py_list__extend);

+ 11 - 11
src/public/py_ops.c

@@ -2,7 +2,7 @@
 #include "pocketpy/objects/base.h"
 #include "pocketpy/pocketpy.h"
 
-bool py_isidentical(const py_Ref lhs, const py_Ref rhs) {
+bool py_isidentical(py_Ref lhs, py_Ref rhs) {
     if(lhs->type != rhs->type) return false;
     switch(lhs->type) {
         case tp_int: return lhs->_i64 == rhs->_i64;
@@ -17,7 +17,7 @@ bool py_isidentical(const py_Ref lhs, const py_Ref rhs) {
     }
 }
 
-int py_bool(const py_Ref val) {
+int py_bool(py_Ref val) {
     switch(val->type) {
         case tp_bool: return val->_bool;
         case tp_int: return val->_i64 != 0;
@@ -43,7 +43,7 @@ int py_bool(const py_Ref val) {
     }
 }
 
-bool py_hash(const py_Ref val, int64_t* out) {
+bool py_hash(py_Ref val, int64_t* out) {
     py_Type t = val->type;
     pk_TypeInfo* types = (pk_TypeInfo*)pk_current_vm->types.data;
     do {
@@ -61,13 +61,13 @@ bool py_hash(const py_Ref val, int64_t* out) {
     return TypeError("unhashable type: '%t'", val->type);
 }
 
-bool py_iter(const py_Ref val) {
+bool py_iter(py_Ref val) {
     py_Ref tmp = py_tpfindmagic(val->type, __iter__);
     if(!tmp) return TypeError("'%t' object is not iterable", val->type);
     return py_call(tmp, 1, val);
 }
 
-int py_next(const py_Ref val) {
+int py_next(py_Ref val) {
     pk_VM* vm = pk_current_vm;
     vm->is_stopiteration = false;
     py_Ref tmp = py_tpfindmagic(val->type, __next__);
@@ -134,7 +134,7 @@ bool py_getattr(py_Ref self, py_Name name) {
     return AttributeError(self, name);
 }
 
-bool py_setattr(py_Ref self, py_Name name, const py_Ref val) {
+bool py_setattr(py_Ref self, py_Name name, py_Ref val) {
     py_Type type = self->type;
     // handle super() proxy
     if(py_istype(self, tp_super)) {
@@ -175,7 +175,7 @@ bool py_delattr(py_Ref self, py_Name name) {
     return TypeError("cannot delete attribute");
 }
 
-bool py_getitem(const py_Ref self, const py_Ref key) {
+bool py_getitem(py_Ref self, py_Ref key) {
     py_push(self);
     py_push(key);
     bool ok = py_callmagic(__getitem__, 2, py_peek(-2));
@@ -183,7 +183,7 @@ bool py_getitem(const py_Ref self, const py_Ref key) {
     return ok;
 }
 
-bool py_setitem(py_Ref self, const py_Ref key, const py_Ref val) {
+bool py_setitem(py_Ref self, py_Ref key, py_Ref val) {
     py_push(self);
     py_push(key);
     py_push(val);
@@ -192,7 +192,7 @@ bool py_setitem(py_Ref self, const py_Ref key, const py_Ref val) {
     return ok;
 }
 
-bool py_delitem(py_Ref self, const py_Ref key) {
+bool py_delitem(py_Ref self, py_Ref key) {
     py_push(self);
     py_push(key);
     bool ok = py_callmagic(__delitem__, 2, py_peek(-2));
@@ -200,12 +200,12 @@ bool py_delitem(py_Ref self, const py_Ref key) {
     return ok;
 }
 
-int py_equal(const py_Ref lhs, const py_Ref rhs){
+int py_equal(py_Ref lhs, py_Ref rhs){
     if(!py_eq(lhs, rhs)) return -1;
     return py_bool(py_retval());
 }
 
-int py_less(const py_Ref lhs, const py_Ref rhs){
+int py_less(py_Ref lhs, py_Ref rhs){
     if(!py_lt(lhs, rhs)) return -1;
     return py_bool(py_retval());
 }

+ 4 - 4
src/public/py_str.c

@@ -31,26 +31,26 @@ unsigned char* py_newbytes(py_Ref out, int size) {
     return ud->data;
 }
 
-const char* py_tostr(const py_Ref self) {
+const char* py_tostr(py_Ref self) {
     assert(self->type == tp_str);
     c11_string* ud = PyObject__userdata(self->_obj);
     return ud->data;
 }
 
-const char* py_tostrn(const py_Ref self, int* size) {
+const char* py_tostrn(py_Ref self, int* size) {
     assert(self->type == tp_str);
     c11_string* ud = PyObject__userdata(self->_obj);
     *size = ud->size;
     return ud->data;
 }
 
-c11_sv py_tosv(const py_Ref self) {
+c11_sv py_tosv(py_Ref self) {
     assert(self->type == tp_str);
     c11_string* ud = PyObject__userdata(self->_obj);
     return c11_string__sv(ud);
 }
 
-unsigned char* py_tobytes(const py_Ref self, int* size) {
+unsigned char* py_tobytes(py_Ref self, int* size) {
     assert(self->type == tp_bytes);
     c11_bytes* ud = PyObject__userdata(self->_obj);
     *size = ud->size;

+ 11 - 5
src/public/py_tuple.c

@@ -13,13 +13,13 @@ 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(py_Ref self, int i) { return py_getslot(self, i); }
 
-py_Ref py_tuple__data(const py_Ref self) { return PyObject__slots(self->_obj); }
+py_Ref py_tuple__data(py_Ref self) { return PyObject__slots(self->_obj); }
 
-void py_tuple__setitem(py_Ref self, int i, const py_Ref val) { py_setslot(self, i, val); }
+void py_tuple__setitem(py_Ref self, int i, py_Ref val) { py_setslot(self, i, val); }
 
-int py_tuple__len(const py_Ref self) { return self->_obj->slots; }
+int py_tuple__len(py_Ref self) { return self->_obj->slots; }
 
 //////////////
 static bool _py_tuple__len__(int argc, py_Ref argv) {
@@ -103,7 +103,7 @@ static bool _py_tuple__eq__(int argc, py_Ref argv) {
         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);
+        int res = pk_arrayequal(a0, length0, a1, length1);
         if(res == -1) return false;
         py_newbool(py_retval(), res);
     } else {
@@ -126,6 +126,11 @@ static bool _py_tuple__iter__(int argc, py_Ref argv) {
     return pk_arrayiter(argv);
 }
 
+static bool _py_tuple__contains__(int argc, py_Ref argv) {
+    PY_CHECK_ARGC(2);
+    return pk_arraycontains(py_arg(0), py_arg(1));
+}
+
 py_Type pk_tuple__register() {
     py_Type type = pk_newtype("tuple", tp_object, NULL, NULL, false, true);
 
@@ -136,5 +141,6 @@ py_Type pk_tuple__register() {
     py_bindmagic(type, __eq__, _py_tuple__eq__);
     py_bindmagic(type, __ne__, _py_tuple__ne__);
     py_bindmagic(type, __iter__, _py_tuple__iter__);
+    py_bindmagic(type, __contains__, _py_tuple__contains__);
     return type;
 }

+ 6 - 6
src/public/stack_ops.c

@@ -6,7 +6,7 @@
 
 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(py_Ref self, py_Name name) {
     assert(self && self->is_ptr);
     if(!py_ismagicname(name) || self->type != tp_type) {
         return pk_NameDict__try_get(PyObject__dict(self->_obj), name);
@@ -17,7 +17,7 @@ py_Ref py_getdict(const py_Ref self, py_Name name) {
     }
 }
 
-void py_setdict(py_Ref self, py_Name name, const py_Ref val) {
+void py_setdict(py_Ref self, py_Name name, py_Ref val) {
     assert(self && self->is_ptr);
     if(!py_ismagicname(name) || self->type != tp_type) {
         pk_NameDict__set(PyObject__dict(self->_obj), name, *val);
@@ -39,19 +39,19 @@ bool py_deldict(py_Ref self, py_Name name) {
     }
 }
 
-py_Ref py_getslot(const py_Ref self, int i) {
+py_Ref py_getslot(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, 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, py_Ref src) { *dst = *src; }
 
 /* Stack References */
 py_Ref py_peek(int i) {
@@ -69,7 +69,7 @@ void py_shrink(int n) {
     vm->stack.sp -= n;
 }
 
-void py_push(const py_Ref src) {
+void py_push(py_Ref src) {
     pk_VM* vm = pk_current_vm;
     *vm->stack.sp++ = *src;
 }

+ 7 - 3
tests/05_list.py

@@ -152,9 +152,13 @@ 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)]
+# test in and not in
+assert 1 in [1, 2, 3]
+assert 4 not in [1, 2, 3]
+
+b = [(1, 2), (3, 3), (5, 1)]
+b.sort(key=lambda x:x[1])
+assert b == [(5, 1), (1, 2), (3,3)]
 
 # test cyclic reference
 # a = []

+ 5 - 1
tests/06_tuple.py

@@ -30,4 +30,8 @@ assert l[-3:-1] == (2,3)
 assert repr((1,)) == '(1,)'
 assert repr((1,2,)) == '(1, 2)'
 assert repr((1,2,(3,4))) == '(1, 2, (3, 4))'
-assert repr(tuple()) == '()'
+assert repr(tuple()) == '()'
+
+# test in and not in
+assert 1 in (1, 2, 3)
+assert 4 not in (1, 2, 3)