blueloveTH пре 1 година
родитељ
комит
25ceed2703

+ 18 - 17
include/pocketpy/interpreter/frame.h

@@ -15,7 +15,8 @@ py_TValue* FastLocals__try_get_by_name(py_TValue* locals, const CodeObject* co,
 pk_NameDict* FastLocals__to_namedict(py_TValue* locals, const CodeObject* co);
 
 typedef struct ValueStack {
-    // We allocate extra PK_VM_STACK_SIZE/128 places to keep `_sp` valid when `is_overflow() == true`.
+    // We allocate extra PK_VM_STACK_SIZE/128 places to keep `_sp` valid when `is_overflow() ==
+    // true`.
     py_TValue* sp;
     py_TValue* end;
     py_TValue begin[PK_VM_STACK_SIZE + PK_VM_STACK_SIZE / 128];
@@ -34,44 +35,44 @@ UnwindTarget* UnwindTarget__new(UnwindTarget* next, int iblock, int offset);
 void UnwindTarget__delete(UnwindTarget* self);
 
 typedef struct Frame {
-    struct Frame* f_back;   // TODO: set this
+    struct Frame* f_back;
     const Bytecode* ip;
     const CodeObject* co;
     PyObject* module;
-    PyObject* function;     // a function object or NULL (global scope)
-    py_TValue* p0;              // unwinding base
-    py_TValue* locals;          // locals base
+    PyObject* function;  // a function object or NULL (global scope)
+    py_TValue* p0;       // unwinding base
+    py_TValue* locals;   // locals base
     const CodeObject* locals_co;
     UnwindTarget* uw_list;
 } Frame;
 
-
-Frame* Frame__new(const CodeObject* co, const py_TValue* module, const py_TValue* function, py_TValue* p0, py_TValue* locals, const CodeObject* locals_co);
+Frame* Frame__new(const CodeObject* co,
+                  const py_TValue* module,
+                  const py_TValue* function,
+                  py_TValue* p0,
+                  py_TValue* locals,
+                  const CodeObject* locals_co);
 void Frame__delete(Frame* self);
 
-PK_INLINE int Frame__ip(const Frame* self){
-    return self->ip - (Bytecode*)self->co->codes.data;
-}
+PK_INLINE int Frame__ip(const Frame* self) { return self->ip - (Bytecode*)self->co->codes.data; }
 
-PK_INLINE int Frame__lineno(const Frame* self){
+PK_INLINE int Frame__lineno(const Frame* self) {
     int ip = Frame__ip(self);
     return c11__getitem(BytecodeEx, &self->co->codes_ex, ip).lineno;
 }
 
-PK_INLINE int Frame__iblock(const Frame* self){
+PK_INLINE int Frame__iblock(const Frame* self) {
     int ip = Frame__ip(self);
     return c11__getitem(BytecodeEx, &self->co->codes_ex, ip).iblock;
 }
 
-PK_INLINE pk_NameDict* Frame__f_globals(Frame* self){
-    return PyObject__dict(self->module);
-}
+PK_INLINE pk_NameDict* Frame__f_globals(Frame* self) { return PyObject__dict(self->module); }
 
-PK_INLINE py_TValue* Frame__f_globals_try_get(Frame* self, py_Name name){
+PK_INLINE py_TValue* Frame__f_globals_try_get(Frame* self, py_Name name) {
     return pk_NameDict__try_get(Frame__f_globals(self), name);
 }
 
-PK_INLINE py_TValue* Frame__f_locals_try_get(Frame* self, py_Name name){
+PK_INLINE py_TValue* Frame__f_locals_try_get(Frame* self, py_Name name) {
     return FastLocals__try_get_by_name(self->locals, self->locals_co, name);
 }
 

+ 18 - 14
include/pocketpy/interpreter/vm.h

@@ -21,21 +21,22 @@ typedef struct pk_TypeInfo{
 
     c11_vector/*T=StrName*/ annotated_fields;
 
+    py_CFunction on_end_subclass;   // backdoor for enum module
+
     /* Magic Caches */
-    // unary operators
-    py_CFunction m__repr__, m__str__, m__hash__, m__len__;
-    py_CFunction m__iter__, m__next__, m__neg__, m__invert__;
-    // binary operators
-    py_CFunction m__eq__, m__lt__, m__le__, m__gt__, m__ge__, m__contains__;
-    py_CFunction m__add__, m__sub__, m__mul__, m__truediv__, m__floordiv__;
-    py_CFunction m__mod__, m__pow__, m__matmul__;
-    py_CFunction m__lshift__, m__rshift__, m__and__, m__xor__, m__or__;
-    // indexer
-    py_CFunction m__getitem__, m__setitem__, m__delitem__;
-    // attribute access (internal use only)
-    py_CFunction m__getattr__, m__setattr__, m__delattr__;
-    // backdoors
-    py_CFunction on_end_subclass;   // for enum module
+    py_TValue magic[64];
+    // // unary operators
+    // py_CFunction m__repr__, m__str__, m__hash__, m__len__;
+    // py_CFunction m__iter__, m__next__, m__neg__, m__invert__;
+    // // binary operators
+    // py_CFunction m__eq__, m__lt__, m__le__, m__gt__, m__ge__, m__contains__;
+    // py_CFunction m__add__, m__sub__, m__mul__, m__truediv__, m__floordiv__;
+    // py_CFunction m__mod__, m__pow__, m__matmul__;
+    // py_CFunction m__lshift__, m__rshift__, m__and__, m__xor__, m__or__;
+    // // indexer
+    // py_CFunction m__getitem__, m__setitem__, m__delitem__;
+    // // attribute access (internal use only)
+    // py_CFunction m__getattr__, m__setattr__, m__delattr__;
 } pk_TypeInfo;
 
 void pk_TypeInfo__ctor(pk_TypeInfo* self, py_Name name, py_Type base, PyObject* obj, const py_TValue* module, bool subclass_enabled);
@@ -91,6 +92,9 @@ pk_FrameResult pk_VM__run_top_frame(pk_VM* self);
 
 py_Type pk_VM__new_type(pk_VM* self, const char* name, py_Type base, const py_TValue* module, bool subclass_enabled);
 
+// type registration
+py_Type pk_list__register();
+
 #ifdef __cplusplus
 }
 #endif

+ 16 - 13
include/pocketpy/objects/base.h

@@ -16,35 +16,38 @@ extern "C" {
 typedef int16_t py_Type;
 typedef struct PyObject PyObject;
 
-typedef struct py_TValue{
+typedef struct py_TValue {
     py_Type type;
     bool is_ptr;
     int extra;
+
     union {
         int64_t _i64;
         double _f64;
         PyObject* _obj;
         void* _ptr;
+        py_CFunction _cfunc;
         // Vec2
     };
 } py_TValue;
 
 // 16 bytes to make py_arg() macro work
+static_assert(sizeof(py_CFunction) <= 8, "sizeof(py_CFunction) > 8");
 static_assert(sizeof(py_TValue) == 16, "sizeof(py_TValue) != 16");
 
 /* predefined vars */
-static const py_Type tp_object = {1}, tp_type = {2};
-static const py_Type tp_int = {3}, tp_float = {4}, tp_bool = {5}, tp_str = {6};
-static const py_Type tp_list = {7}, tp_tuple = {8};
-static const py_Type tp_slice = {9}, tp_range = {10}, tp_module = {11};
-static const py_Type tp_function = {12}, tp_nativefunc = {13}, tp_bound_method = {14};
-static const py_Type tp_super = {15}, tp_exception = {16}, tp_bytes = {17}, tp_mappingproxy = {18};
-static const py_Type tp_dict = {19}, tp_property = {20}, tp_star_wrapper = {21};
-static const py_Type tp_staticmethod = {22}, tp_classmethod = {23};
-static const py_Type tp_none_type = {24}, tp_not_implemented_type = {25};
-static const py_Type tp_ellipsis = {26};
-static const py_Type tp_op_call = {27}, tp_op_yield = {28};
-static const py_Type tp_syntax_error = {29}, tp_stop_iteration = {30};
+const static py_Type tp_object = {1}, tp_type = {2};
+const static py_Type tp_int = {3}, tp_float = {4}, tp_bool = {5}, tp_str = {6};
+const static py_Type tp_list = {7}, tp_tuple = {8};
+const static py_Type tp_slice = {9}, tp_range = {10}, tp_module = {11};
+const static py_Type tp_function = {12}, tp_nativefunc = {13}, tp_bound_method = {14};
+const static py_Type tp_super = {15}, tp_exception = {16}, tp_bytes = {17}, tp_mappingproxy = {18};
+const static py_Type tp_dict = {19}, tp_property = {20}, tp_star_wrapper = {21};
+const static py_Type tp_staticmethod = {22}, tp_classmethod = {23};
+const static py_Type tp_none_type = {24}, tp_not_implemented_type = {25};
+const static py_Type tp_ellipsis = {26};
+const static py_Type tp_op_call = {27}, tp_op_yield = {28};
+const static py_Type tp_syntax_error = {29}, tp_stop_iteration = {30};
 
 extern py_TValue PY_NULL, PY_OP_CALL, PY_OP_YIELD;
 

+ 47 - 17
include/pocketpy/pocketpy.h

@@ -18,7 +18,8 @@ typedef struct py_Error {
 /// Native function signature.
 /// @param argc number of arguments.
 /// @param argv array of arguments. Use `py_arg(i)` macro to get the i-th argument.
-/// @param out output reference to the result.
+/// @param out output reference to the result. Please note that `out` could overlap with `argv`.
+/// Always set `out` after using `argv`.
 /// @return true if the function is successful.
 typedef bool (*py_CFunction)(int argc, py_TValue* argv, py_TValue* out);
 
@@ -74,13 +75,7 @@ void py_newfunction2(py_Ref out,
                      const char* docstring,
                      const py_Ref upvalue);
 // old style argc-based function
-void py_newnativefunc(py_Ref out, py_CFunction, int argc);
-void py_newnativefunc2(py_Ref out,
-                       py_CFunction,
-                       int argc,
-                       BindType bt,
-                       const char* docstring,
-                       const py_Ref upvalue);
+void py_newnativefunc(py_Ref out, py_CFunction);
 
 void py_newnotimplemented(py_Ref out);
 
@@ -110,7 +105,21 @@ bool py_istype(const py_Ref, py_Type);
 // bool py_issubclass(py_Type derived, py_Type base);
 
 /************* References *************/
-#define py_arg(i)       (py_Ref)((char*)argv+((i)<<4))
+#define TypeError(x) false
+#define py_arg(i) (py_Ref)((char*)argv + ((i) << 4))
+#define py_checkargc(n)                                                                            \
+    if(argc != n) return TypeError()
+
+py_Ref py_tpmagic(py_Type type, py_Name name);
+#define py_bindmagic(type, __magic__, f) py_newnativefunc(py_tpmagic((type), __magic__), (f))
+
+// new style decl-based bindings
+py_Ref py_bind(py_Ref obj, const char* sig, py_CFunction f);
+py_Ref py_bind2(py_Ref obj, const char* sig, py_CFunction f, BindType bt, const char* docstring, const py_Ref upvalue);
+// old style argc-based bindings
+void py_bindmethod(py_Type type, const char* name, py_CFunction f);
+void py_bindmethod2(py_Type type, const char* name, py_CFunction f, BindType bt);
+void py_bindnativefunc(py_Ref obj, const char* name, py_CFunction f);
 
 py_Ref py_reg(int i);
 
@@ -126,7 +135,11 @@ void py_setupvalue(py_Ref self, const py_Ref val);
 /// Gets the attribute of the object.
 bool py_getattr(const py_Ref self, py_Name name, py_Ref out);
 /// Gets the unbound method of the object.
-bool py_getunboundmethod(const py_Ref self, py_Name name, bool fallback, py_Ref out, py_Ref out_self);
+bool py_getunboundmethod(const py_Ref self,
+                         py_Name name,
+                         bool fallback,
+                         py_Ref out,
+                         py_Ref out_self);
 /// Sets the attribute of the object.
 bool py_setattr(py_Ref self, py_Name name, const py_Ref val);
 /// Deletes the attribute of the object.
@@ -177,21 +190,29 @@ int py_eq(const py_Ref, const py_Ref);
 int py_le(const py_Ref, const py_Ref);
 bool py_hash(const py_Ref, int64_t* out);
 
-bool py_str(const py_Ref, py_Ref out);
-bool py_repr(const py_Ref, py_Ref out);
-
 /// A stack operation that calls a function.
-/// It consumes `argc + kwargc` arguments from the stack.
+/// It assumes `argc + kwargc` arguments are already pushed to the stack.
 /// The result will be set to `vm->last_retval`.
-int pk_vectorcall(int argc, int kwargc, bool op_call);
+/// The stack size will be reduced by `argc + kwargc`.
+bool pk_vectorcall(int argc, int kwargc, bool op_call);
 /// Call a function.
 /// It prepares the stack and then performs a `vectorcall(argc, 0, false)`.
 /// The result will be set to `vm->last_retval`.
+/// The stack remains unchanged after the operation.
 bool py_call(py_Ref f, int argc, py_Ref argv);
-/// Call a method.
+/// Call a non-magic method.
 /// It prepares the stack and then performs a `vectorcall(argc+1, 0, false)`.
 /// The result will be set to `vm->last_retval`.
-bool py_callmethod(py_Ref self, py_Name name, int argc, py_Ref argv);
+/// The stack remains unchanged after the operation.
+bool py_callmethod(py_Ref self, py_Name, int argc, py_Ref argv);
+/// Call a magic method.
+/// The result will be set to `vm->last_retval`.
+/// The stack remains unchanged after the operation.
+bool py_callmagic(py_Name name, int argc, py_Ref argv);
+
+#define py_repr(self) py_callmagic(__repr__, 1, self)
+#define py_str(self) py_callmagic(__str__, 1, self)
+
 /// The return value of the most recent vectorcall.
 py_Ref py_lastretval();
 
@@ -217,6 +238,15 @@ void py_list__insert(py_Ref self, int i, const py_Ref val);
 typedef struct pk_TypeInfo pk_TypeInfo;
 pk_TypeInfo* pk_tpinfo(const py_Ref self);
 
+/// Search the magic method from the given type to the base type.
+/// Returns the reference or NULL if not found.
+/// @lifespan: Permanent.
+py_Ref py_tpfindmagic(py_Type, py_Name name);
+
+/// Get the type object of the given type.
+/// @lifespan: Permanent.
+py_Ref py_tpobject(py_Type type);
+
 #ifdef __cplusplus
 }
 #endif

+ 2 - 22
include/pocketpy/xmacros/opcodes.h

@@ -52,19 +52,7 @@ OPCODE(BUILD_SET)
 OPCODE(BUILD_SLICE)
 OPCODE(BUILD_STRING)
 /**************************/
-OPCODE(BUILD_TUPLE_UNPACK)
-OPCODE(BUILD_LIST_UNPACK)
-OPCODE(BUILD_DICT_UNPACK)
-OPCODE(BUILD_SET_UNPACK)
-/**************************/
-OPCODE(BINARY_TRUEDIV)
-OPCODE(BINARY_POW)
-
-OPCODE(BINARY_ADD)
-OPCODE(BINARY_SUB)
-OPCODE(BINARY_MUL)
-OPCODE(BINARY_FLOORDIV)
-OPCODE(BINARY_MOD)
+OPCODE(BINARY_OP)
 
 OPCODE(COMPARE_LT)
 OPCODE(COMPARE_LE)
@@ -73,14 +61,6 @@ OPCODE(COMPARE_NE)
 OPCODE(COMPARE_GT)
 OPCODE(COMPARE_GE)
 
-OPCODE(BITWISE_LSHIFT)
-OPCODE(BITWISE_RSHIFT)
-OPCODE(BITWISE_AND)
-OPCODE(BITWISE_OR)
-OPCODE(BITWISE_XOR)
-
-OPCODE(BINARY_MATMUL)
-
 OPCODE(IS_OP)
 OPCODE(IS_NOT_OP)
 OPCODE(IN_OP)
@@ -101,7 +81,7 @@ OPCODE(GOTO)
 OPCODE(FSTRING_EVAL)
 OPCODE(REPR)
 OPCODE(CALL)
-OPCODE(CALL_TP)
+OPCODE(CALL_VARGS)
 OPCODE(RETURN_VALUE)
 OPCODE(YIELD_VALUE)
 /**************************/

+ 17 - 16
src/common/strname.c

@@ -7,13 +7,13 @@
 
 // TODO: use a more efficient data structure
 static c11_smallmap_s2n _interned;
-static c11_vector/*T=char* */ _r_interned;
+static c11_vector /*T=char* */ _r_interned;
 static bool _initialized = false;
 
-void pk_StrName__initialize(){
+void pk_StrName__initialize() {
     if(_initialized) return;
     c11_smallmap_s2n__ctor(&_interned);
-    for(int i=0; i<_r_interned.count; i++){
+    for(int i = 0; i < _r_interned.count; i++) {
         free(c11__at(char*, &_r_interned, i));
     }
     c11_vector__ctor(&_r_interned, sizeof(c11_string));
@@ -75,33 +75,36 @@ void pk_StrName__initialize(){
     pk_id_set = pk_StrName__map("set");
     pk_id_long = pk_StrName__map("long");
     pk_id_complex = pk_StrName__map("complex");
+
+    // // print all names
+    // for(int i = 0; i < _interned.count; i++) {
+    //     printf("%d: %s\n", i+1, c11__getitem(char*, &_r_interned, i));
+    // }
 }
 
-void pk_StrName__finalize(){
+void pk_StrName__finalize() {
     if(!_initialized) return;
     // free all char*
-    for(int i=0; i<_r_interned.count; i++){
+    for(int i = 0; i < _r_interned.count; i++) {
         free(c11__getitem(char*, &_r_interned, i));
     }
     c11_smallmap_s2n__dtor(&_interned);
     c11_vector__dtor(&_r_interned);
 }
 
-uint16_t pk_StrName__map(const char* name){
+uint16_t pk_StrName__map(const char* name) {
     return pk_StrName__map2((c11_string){name, strlen(name)});
 }
 
-uint16_t pk_StrName__map2(c11_string name){
+uint16_t pk_StrName__map2(c11_string name) {
     // TODO: PK_GLOBAL_SCOPE_LOCK()
-    if(!_initialized){
-        pk_StrName__initialize(); // lazy init
+    if(!_initialized) {
+        pk_StrName__initialize();  // lazy init
     }
     uint16_t index = c11_smallmap_s2n__get(&_interned, name, 0);
     if(index != 0) return index;
     // generate new index
-    if(_interned.count > 65530){
-        PK_FATAL_ERROR("StrName index overflow\n");
-    }
+    if(_interned.count > 65530) { PK_FATAL_ERROR("StrName index overflow\n"); }
     // NOTE: we must allocate the string in the heap so iterators are not invalidated
     char* p = malloc(name.size + 1);
     memcpy(p, name.data, name.size);
@@ -114,19 +117,17 @@ uint16_t pk_StrName__map2(c11_string name){
     return index;
 }
 
-const char* pk_StrName__rmap(uint16_t index){
+const char* pk_StrName__rmap(uint16_t index) {
     assert(_initialized);
     assert(index > 0 && index <= _interned.count);
     return c11__getitem(char*, &_r_interned, index - 1);
 }
 
-c11_string pk_StrName__rmap2(uint16_t index){
+c11_string pk_StrName__rmap2(uint16_t index) {
     const char* p = pk_StrName__rmap(index);
     return (c11_string){p, strlen(p)};
 }
 
-
-
 // unary operators
 uint16_t __repr__;
 uint16_t __str__;

+ 34 - 52
src/compiler/compiler.c

@@ -898,15 +898,18 @@ static void BinaryExpr__emit_(Expr* self_, Ctx* ctx) {
     }
 
     vtemit_(self->rhs, ctx);
-    Opcode opcode;
+
+    Opcode opcode = OP_BINARY_OP;
+    uint16_t arg = BC_NOARG;
+
     switch(self->op) {
-        case TK_ADD: opcode = OP_BINARY_ADD; break;
-        case TK_SUB: opcode = OP_BINARY_SUB; break;
-        case TK_MUL: opcode = OP_BINARY_MUL; break;
-        case TK_DIV: opcode = OP_BINARY_TRUEDIV; break;
-        case TK_FLOORDIV: opcode = OP_BINARY_FLOORDIV; break;
-        case TK_MOD: opcode = OP_BINARY_MOD; break;
-        case TK_POW: opcode = OP_BINARY_POW; break;
+        case TK_ADD: arg = __add__ | (__radd__ << 8); break;
+        case TK_SUB: arg = __sub__ | (__rsub__ << 8); break;
+        case TK_MUL: arg = __mul__ | (__rmul__ << 8); break;
+        case TK_DIV: arg = __truediv__; break;
+        case TK_FLOORDIV: arg = __floordiv__; break;
+        case TK_MOD: arg = __mod__; break;
+        case TK_POW: arg = __pow__; break;
 
         case TK_LT: opcode = OP_COMPARE_LT; break;
         case TK_LE: opcode = OP_COMPARE_LE; break;
@@ -920,17 +923,16 @@ static void BinaryExpr__emit_(Expr* self_, Ctx* ctx) {
         case TK_IS: opcode = OP_IS_OP; break;
         case TK_IS_NOT: opcode = OP_IS_NOT_OP; break;
 
-        case TK_LSHIFT: opcode = OP_BITWISE_LSHIFT; break;
-        case TK_RSHIFT: opcode = OP_BITWISE_RSHIFT; break;
-        case TK_AND: opcode = OP_BITWISE_AND; break;
-        case TK_OR: opcode = OP_BITWISE_OR; break;
-        case TK_XOR: opcode = OP_BITWISE_XOR; break;
-
-        case TK_DECORATOR: opcode = OP_BINARY_MATMUL; break;
+        case TK_LSHIFT: arg = __lshift__; break;
+        case TK_RSHIFT: arg = __rshift__; break;
+        case TK_AND: arg = __and__; break;
+        case TK_OR: arg = __or__; break;
+        case TK_XOR: arg = __xor__; break;
+        case TK_DECORATOR: arg = __matmul__; break;
         default: assert(false);
     }
 
-    Ctx__emit_(ctx, opcode, BC_NOARG, self->line);
+    Ctx__emit_(ctx, opcode, arg, self->line);
 
     c11__foreach(int, &jmps, i) { Ctx__patch_jump(ctx, *i); }
 }
@@ -1137,8 +1139,8 @@ void CallExpr__dtor(Expr* self_) {
 void CallExpr__emit_(Expr* self_, Ctx* ctx) {
     CallExpr* self = (CallExpr*)self_;
 
-    bool vargs = false;
-    bool vkwargs = false;
+    bool vargs = false;    // whether there is *args as input
+    bool vkwargs = false;  // whether there is **kwargs as input
     c11__foreach(Expr*, &self->args, e) {
         if((*e)->vt->is_starred) vargs = true;
     }
@@ -1147,7 +1149,6 @@ void CallExpr__emit_(Expr* self_, Ctx* ctx) {
     }
 
     // if callable is a AttrExpr, we should try to use `fast_call` instead of use `boundmethod`
-    // proxy
     if(self->callable->vt->is_attrib) {
         AttribExpr* p = (AttribExpr*)self->callable;
         vtemit_(p->child, ctx);
@@ -1157,41 +1158,22 @@ void CallExpr__emit_(Expr* self_, Ctx* ctx) {
         Ctx__emit_(ctx, OP_LOAD_NULL, BC_NOARG, BC_KEEPLINE);
     }
 
-    c11__foreach(Expr*, &self->args, e) { vtemit_(*e, ctx); }
-
+    Opcode opcode = OP_CALL;
     if(vargs || vkwargs) {
-        Ctx__emit_(ctx, OP_BUILD_TUPLE_UNPACK, (uint16_t)self->args.count, self->line);
-
-        if(self->kwargs.count != 0) {
-            c11__foreach(CallExprKwArg, &self->kwargs, e) {
-                if(e->val->vt->is_starred) {
-                    // **kwargs
-                    StarredExpr* se = (StarredExpr*)e->val;
-                    assert(se->level == 2 && e->key == 0);
-                    vtemit_(e->val, ctx);
-                } else {
-                    // k=v
-                    int index = Ctx__add_const_string(ctx, pk_StrName__rmap2(e->key));
-                    Ctx__emit_(ctx, OP_LOAD_CONST, index, self->line);
-                    vtemit_(e->val, ctx);
-                    Ctx__emit_(ctx, OP_BUILD_TUPLE, 2, self->line);
-                }
-            }
-            Ctx__emit_(ctx, OP_BUILD_DICT_UNPACK, self->kwargs.count, self->line);
-            Ctx__emit_(ctx, OP_CALL_TP, 1, self->line);
-        } else {
-            Ctx__emit_(ctx, OP_CALL_TP, 0, self->line);
-        }
-    } else {
-        // vectorcall protocol
-        c11__foreach(CallExprKwArg, &self->kwargs, e) {
-            Ctx__emit_int(ctx, e->key, self->line);
-            vtemit_(e->val, ctx);
-        }
-        int KWARGC = self->kwargs.count;
-        int ARGC = self->args.count;
-        Ctx__emit_(ctx, OP_CALL, (KWARGC << 8) | ARGC, self->line);
+        // in this case, there is at least one *args or **kwargs as StarredExpr
+        // OP_CALL_VARGS needs to unpack them via __vectorcall_buffer
+        opcode = OP_CALL_VARGS;
+    }
+
+    c11__foreach(Expr*, &self->args, e) { vtemit_(*e, ctx); }
+    c11__foreach(CallExprKwArg, &self->kwargs, e) {
+        Ctx__emit_int(ctx, e->key, self->line);
+        vtemit_(e->val, ctx);
     }
+    int KWARGC = self->kwargs.count;
+    int ARGC = self->args.count;
+    assert(KWARGC < 256 && ARGC < 256);
+    Ctx__emit_(ctx, opcode, (KWARGC << 8) | ARGC, self->line);
 }
 
 CallExpr* CallExpr__new(int line, Expr* callable) {

+ 123 - 50
src/interpreter/ceval.c

@@ -9,6 +9,7 @@ int UnboundLocalError(py_Name name) { return -1; }
 int NameError(py_Name name) { return -1; }
 
 #define AttributeError(obj, name)
+#define BinaryOptError(op)
 
 #define DISPATCH()                                                                                 \
     do {                                                                                           \
@@ -38,6 +39,13 @@ int NameError(py_Name name) { return -1; }
 #define POPX() (*--self->stack.sp)
 #define SP() (self->stack.sp)
 
+// [a, b] -> [?, a, b]
+#define INSERT_THIRD()                                                                             \
+    do {                                                                                           \
+        PUSH(TOP());                                                                               \
+        *SECOND() = *THIRD();                                                                      \
+    } while(0)
+
 #define vectorcall_opcall(n)                                                                       \
     do {                                                                                           \
         pk_FrameResult res = pk_vectorcall(n, 0, true);                                            \
@@ -104,8 +112,9 @@ pk_FrameResult pk_VM__run_top_frame(pk_VM* self) {
             }
             case OP_PRINT_EXPR:
                 if(TOP()->type != tp_none_type) {
-                    py_TValue tmp;
-                    if(py_repr(TOP(), &tmp)) self->_stdout("%s\n", py_tostr(&tmp));
+                    bool ok = py_repr(TOP());
+                    if(!ok) goto __ERROR;
+                    self->_stdout("%s\n", py_tostr(&self->last_retval));
                 }
                 POP();
                 DISPATCH();
@@ -247,19 +256,21 @@ pk_FrameResult pk_VM__run_top_frame(pk_VM* self) {
             }
             case OP_LOAD_SUBSCR: {
                 // [a, b] -> a[b]
-                pk_TypeInfo* ti = pk_tpinfo(SECOND());
-                if(ti->m__getitem__) {
-                    if(!ti->m__getitem__(2, SECOND(), SECOND())) goto __ERROR;
-                } else {
-                    // [a, b] -> [?, a, b]
-                    PUSH(TOP());           // [a, b, b]
-                    *SECOND() = *THIRD();  // [a, a, b]
-                    bool ok = py_getunboundmethod(SECOND(), __getitem__, false, THIRD(), SECOND());
-                    // [__getitem__, self, b]
-                    if(!ok) goto __ERROR;
-                    vectorcall_opcall(2);
+                py_Ref magic = py_tpfindmagic(SECOND()->type, __getitem__);
+                if(magic) {
+                    if(magic->type == tp_nativefunc) {
+                        bool ok = magic->_cfunc(2, SECOND(), SECOND());
+                        if(!ok) goto __ERROR;
+                        POP();
+                    } else {
+                        INSERT_THIRD();     // [?, a, b]
+                        *THIRD() = *magic;  // [__getitem__, a, b]
+                        vectorcall_opcall(2);
+                    }
+                    DISPATCH();
                 }
-                DISPATCH();
+                TypeError();
+                goto __ERROR;
             }
             case OP_STORE_FAST: frame->locals[byte.arg] = POPX(); DISPATCH();
             case OP_STORE_NAME: {
@@ -296,19 +307,23 @@ pk_FrameResult pk_VM__run_top_frame(pk_VM* self) {
             }
             case OP_STORE_SUBSCR: {
                 // [val, a, b] -> a[b] = val
-                pk_TypeInfo* ti = pk_tpinfo(SECOND());
                 PUSH(THIRD());  // [val, a, b, val]
-                if(ti->m__setitem__) {
-                    if(!ti->m__setitem__(3, THIRD(), FOURTH())) goto __ERROR;
-                    STACK_SHRINK(3);  // [retval]
-                } else {
-                    bool ok = py_getunboundmethod(THIRD(), __setitem__, false, FOURTH(), THIRD());
-                    if(!ok) goto __ERROR;
-                    // [__setitem__, self, b, val]
-                    vectorcall_opcall(3);
-                    POP();  // discard retval
+                py_Ref magic = py_tpfindmagic(SECOND()->type, __setitem__);
+                if(magic) {
+                    if(magic->type == tp_nativefunc) {
+                        bool ok = magic->_cfunc(3, THIRD(), FOURTH());
+                        if(!ok) goto __ERROR;
+                        STACK_SHRINK(4);
+                    } else {
+                        INSERT_THIRD();      // [?, a, b]
+                        *FOURTH() = *magic;  // [__selitem__, a, b, val]
+                        vectorcall_opcall(3);
+                        POP();  // discard retval
+                    }
+                    DISPATCH();
                 }
-                DISPATCH();
+                TypeError();
+                goto __ERROR;
             }
             case OP_DELETE_FAST: {
                 py_Ref tmp = &frame->locals[byte.arg];
@@ -362,22 +377,23 @@ pk_FrameResult pk_VM__run_top_frame(pk_VM* self) {
 
             case OP_DELETE_SUBSCR: {
                 // [a, b] -> del a[b]
-                pk_TypeInfo* ti = pk_tpinfo(SECOND());
-                if(ti->m__delitem__) {
-                    if(!ti->m__delitem__(2, SECOND(), SECOND())) goto __ERROR;
-                    POP();
-                } else {
-                    PUSH(TOP());           // [a, b, b]
-                    *SECOND() = *THIRD();  // [a, a, b]
-                    bool ok = py_getunboundmethod(SECOND(), __delitem__, false, THIRD(), SECOND());
-                    // [__delitem__, self, b]
-                    if(!ok) goto __ERROR;
-                    vectorcall_opcall(2);
-                    POP();  // discard retval
+                py_Ref magic = py_tpfindmagic(SECOND()->type, __delitem__);
+                if(magic) {
+                    if(magic->type == tp_nativefunc) {
+                        bool ok = magic->_cfunc(2, SECOND(), SECOND());
+                        if(!ok) goto __ERROR;
+                        STACK_SHRINK(2);
+                    } else {
+                        INSERT_THIRD();     // [?, a, b]
+                        *THIRD() = *magic;  // [__delitem__, a, b]
+                        vectorcall_opcall(2);
+                        POP();  // discard retval
+                    }
+                    DISPATCH();
                 }
-                DISPATCH();
+                TypeError();
+                goto __ERROR;
             }
-
                 /*****************************************/
 
             case OP_BUILD_LONG: {
@@ -394,11 +410,11 @@ pk_FrameResult pk_VM__run_top_frame(pk_VM* self) {
                 py_Ref f = py_getdict(&self->builtins, pk_id_complex);
                 assert(f != NULL);
                 py_TValue tmp = *TOP();
-                *TOP() = *f;            // [complex]
-                py_newnull(SP()++);     // [complex, NULL]
-                py_newint(SP()++, 0);   // [complex, NULL, 0]
-                *SP()++ = tmp;          // [complex, NULL, 0, x]
-                vectorcall_opcall(2);   // [complex(x, 0)]
+                *TOP() = *f;           // [complex]
+                py_newnull(SP()++);    // [complex, NULL]
+                py_newint(SP()++, 0);  // [complex, NULL, 0]
+                *SP()++ = tmp;         // [complex, NULL, 0, x]
+                vectorcall_opcall(2);  // [complex(x)]
                 DISPATCH();
             }
             case OP_BUILD_BYTES: {
@@ -462,20 +478,77 @@ pk_FrameResult pk_VM__run_top_frame(pk_VM* self) {
             }
             case OP_BUILD_STRING: {
                 py_TValue* begin = SP() - byte.arg;
-                py_Ref tmp = py_pushtmp();
                 pk_SStream ss;
                 pk_SStream__ctor(&ss);
                 for(int i = 0; i < byte.arg; i++) {
-                    if(!py_str(begin + i, tmp)) goto __ERROR;
-                    py_Str* item = py_touserdata(tmp);
+                    if(!py_str(begin + i)) goto __ERROR;
+                    py_Str* item = py_touserdata(&self->last_retval);
                     pk_SStream__write_Str(&ss, item);
                 }
                 SP() = begin;
-                py_newStr_(tmp, pk_SStream__submit(&ss));
-                PUSH(tmp);
+                py_newStr_(SP()++, pk_SStream__submit(&ss));
                 DISPATCH();
             }
-            /**************************** */
+            /*****************************/
+            case OP_BINARY_OP: {
+                py_Name op = byte.arg & 0xFF;
+                // [a, b]
+                py_Ref _0 = py_tpfindmagic(SECOND()->type, op);
+                py_Ref _1;
+                py_TValue tmp;
+                if(_0) {
+                    if(_0->type == tp_nativefunc) {
+                        bool ok = _0->_cfunc(2, SECOND(), &tmp);
+                        if(!ok) goto __ERROR;
+                        if(tmp.type != tp_not_implemented_type) {
+                            POP();
+                            *TOP() = tmp;
+                            DISPATCH();
+                        }
+                    } else {
+                        // standard call
+                        bool ok = py_call(_0, 2, SECOND());
+                        if(!ok) goto __ERROR;
+                        if(self->last_retval.type != tp_not_implemented_type) {
+                            POP();
+                            *TOP() = self->last_retval;
+                            DISPATCH();
+                        }
+                    }
+                }
+                // try reverse operation
+                op = byte.arg >> 8;
+                if(op) {
+                    // [a, b] -> [b, a]
+                    tmp = *TOP();
+                    *TOP() = *SECOND();
+                    *SECOND() = tmp;
+                    _1 = py_tpfindmagic(SECOND()->type, op);
+                    if(_1) {
+                        if(_1->type == tp_nativefunc) {
+                            bool ok = _1->_cfunc(2, SECOND(), &tmp);
+                            if(!ok) goto __ERROR;
+                            if(tmp.type != tp_not_implemented_type) {
+                                POP();
+                                *TOP() = tmp;
+                                DISPATCH();
+                            }
+                        } else {
+                            // standard call
+                            bool ok = py_call(_1, 2, SECOND());
+                            if(!ok) goto __ERROR;
+                            if(self->last_retval.type != tp_not_implemented_type) {
+                                POP();
+                                *TOP() = self->last_retval;
+                                DISPATCH();
+                            }
+                        }
+                    }
+                }
+                BinaryOptError(byte.arg);
+                goto __ERROR;
+            }
+
             case OP_RETURN_VALUE: {
                 self->last_retval = byte.arg == BC_NOARG ? POPX() : self->None;
                 pk_VM__pop_frame(self);

+ 44 - 60
src/interpreter/py_number.c

@@ -41,6 +41,7 @@
 
 #define DEF_NUM_BINARY_OP(name, op)                                                                \
     static bool _py_int##name(int argc, py_Ref argv, py_Ref out) {                                 \
+        py_checkargc(2);                                                                           \
         if(py_isint(&argv[1])) {                                                                   \
             int64_t lhs = py_toint(&argv[0]);                                                      \
             int64_t rhs = py_toint(&argv[1]);                                                      \
@@ -55,6 +56,7 @@
         return true;                                                                               \
     }                                                                                              \
     static bool _py_float##name(int argc, py_Ref argv, py_Ref out) {                               \
+        py_checkargc(2);                                                                           \
         double lhs = py_tofloat(&argv[0]);                                                         \
         double rhs;                                                                                \
         if(py_castfloat(&argv[1], &rhs)) {                                                         \
@@ -78,18 +80,21 @@ DEF_NUM_BINARY_OP(__ge__, >=)
 #undef DEF_NUM_BINARY_OP
 
 static bool _py_int__neg__(int argc, py_Ref argv, py_Ref out) {
+    py_checkargc(1);
     int64_t val = py_toint(&argv[0]);
     py_newint(out, -val);
     return true;
 }
 
 static bool _py_float__neg__(int argc, py_Ref argv, py_Ref out) {
+    py_checkargc(1);
     double val = py_tofloat(&argv[0]);
     py_newfloat(out, -val);
     return true;
 }
 
 static bool _py_int__truediv__(int argc, py_Ref argv, py_Ref out) {
+    py_checkargc(2);
     int64_t lhs = py_toint(&argv[0]);
     double rhs;
     if(py_castfloat(&argv[1], &rhs)) {
@@ -101,6 +106,7 @@ static bool _py_int__truediv__(int argc, py_Ref argv, py_Ref out) {
 }
 
 static bool _py_float__truediv__(int argc, py_Ref argv, py_Ref out) {
+    py_checkargc(2);
     double lhs = py_tofloat(&argv[0]);
     double rhs;
     if(py_castfloat(&argv[1], &rhs)) {
@@ -114,6 +120,7 @@ static bool _py_float__truediv__(int argc, py_Ref argv, py_Ref out) {
 #define ZeroDivisionError(msg) false
 
 static bool _py_number__pow__(int argc, py_Ref argv, py_Ref out) {
+    py_checkargc(2);
     if(py_isint(&argv[0]) && py_isint(&argv[1])) {
         int64_t lhs = py_toint(&argv[0]);
         int64_t rhs = py_toint(&argv[1]);
@@ -145,6 +152,7 @@ static bool _py_number__pow__(int argc, py_Ref argv, py_Ref out) {
 }
 
 static bool _py_int__floordiv__(int argc, py_Ref argv, py_Ref out) {
+    py_checkargc(2);
     int64_t lhs = py_toint(&argv[0]);
     if(py_isint(&argv[1])) {
         int64_t rhs = py_toint(&argv[1]);
@@ -157,6 +165,7 @@ static bool _py_int__floordiv__(int argc, py_Ref argv, py_Ref out) {
 }
 
 static bool _py_int__mod__(int argc, py_Ref argv, py_Ref out) {
+    py_checkargc(2);
     int64_t lhs = py_toint(&argv[0]);
     if(py_isint(&argv[1])) {
         int64_t rhs = py_toint(&argv[1]);
@@ -169,12 +178,14 @@ static bool _py_int__mod__(int argc, py_Ref argv, py_Ref out) {
 }
 
 static bool _py_int__invert__(int argc, py_Ref argv, py_Ref out) {
+    py_checkargc(1);
     int64_t val = py_toint(&argv[0]);
     py_newint(out, ~val);
     return true;
 }
 
 static bool _py_int__bit_length(int argc, py_Ref argv, py_Ref out) {
+    py_checkargc(1);
     int64_t x = py_toint(py_arg(0));
     if(x < 0) x = -x;
     int bits = 0;
@@ -188,6 +199,7 @@ static bool _py_int__bit_length(int argc, py_Ref argv, py_Ref out) {
 
 #define DEF_INT_BITWISE_OP(name, op)                                                               \
     static bool _py_int##name(int argc, py_Ref argv, py_Ref out) {                                 \
+        py_checkargc(2);                                                                           \
         int64_t lhs = py_toint(&argv[0]);                                                          \
         if(py_isint(&argv[1])) {                                                                   \
             int64_t rhs = py_toint(&argv[1]);                                                      \
@@ -208,81 +220,53 @@ DEF_INT_BITWISE_OP(__rshift__, >>)
 
 void pk_VM__init_builtins(pk_VM* self) {
     /****** tp_int & tp_float ******/
-    py_Ref tmp = py_pushtmp();
-    py_Ref int_type = py_pushtmp();
-    *int_type = *py_getdict(&self->builtins, py_name("int"));
-    py_Ref float_type = py_pushtmp();
-    *float_type = *py_getdict(&self->builtins, py_name("float"));
-
-#define BIND_INT_BINARY_OP(name)                                                                   \
-    py_newnativefunc(tmp, _py_int##name, 2);                                                       \
-    py_setdict(int_type, name, tmp);
-
-#define BIND_FLOAT_BINARY_OP(name)                                                                 \
-    py_newnativefunc(tmp, _py_float##name, 2);                                                     \
-    py_setdict(float_type, name, tmp);
-
-    BIND_INT_BINARY_OP(__add__);
-    BIND_FLOAT_BINARY_OP(__add__);
-    BIND_INT_BINARY_OP(__sub__);
-    BIND_FLOAT_BINARY_OP(__sub__);
-    BIND_INT_BINARY_OP(__mul__);
-    BIND_FLOAT_BINARY_OP(__mul__);
-
-    BIND_INT_BINARY_OP(__eq__);
-    BIND_FLOAT_BINARY_OP(__eq__);
-    BIND_INT_BINARY_OP(__lt__);
-    BIND_FLOAT_BINARY_OP(__lt__);
-    BIND_INT_BINARY_OP(__le__);
-    BIND_FLOAT_BINARY_OP(__le__);
-    BIND_INT_BINARY_OP(__gt__);
-    BIND_FLOAT_BINARY_OP(__gt__);
-    BIND_INT_BINARY_OP(__ge__);
-    BIND_FLOAT_BINARY_OP(__ge__);
+    py_bindmagic(tp_int, __add__, _py_int__add__);
+    py_bindmagic(tp_float, __add__, _py_float__add__);
+    py_bindmagic(tp_int, __sub__, _py_int__sub__);
+    py_bindmagic(tp_float, __sub__, _py_float__sub__);
+    py_bindmagic(tp_int, __mul__, _py_int__mul__);
+    py_bindmagic(tp_float, __mul__, _py_float__mul__);
+
+    py_bindmagic(tp_int, __eq__, _py_int__eq__);
+    py_bindmagic(tp_float, __eq__, _py_float__eq__);
+    py_bindmagic(tp_int, __lt__, _py_int__lt__);
+    py_bindmagic(tp_float, __lt__, _py_float__lt__);
+    py_bindmagic(tp_int, __le__, _py_int__le__);
+    py_bindmagic(tp_float, __le__, _py_float__le__);
+    py_bindmagic(tp_int, __gt__, _py_int__gt__);
+    py_bindmagic(tp_float, __gt__, _py_float__gt__);
+    py_bindmagic(tp_int, __ge__, _py_int__ge__);
+    py_bindmagic(tp_float, __ge__, _py_float__ge__);
 
     // __neg__
-    py_newnativefunc(tmp, _py_int__neg__, 1);
-    py_setdict(int_type, __neg__, tmp);
-    py_newnativefunc(tmp, _py_float__neg__, 1);
-    py_setdict(float_type, __neg__, tmp);
+    py_bindmagic(tp_int, __neg__, _py_int__neg__);
+    py_bindmagic(tp_float, __neg__, _py_float__neg__);
 
     // TODO: __repr__, __new__, __hash__
 
     // __truediv__
-    py_newnativefunc(tmp, _py_int__truediv__, 2);
-    py_setdict(int_type, __truediv__, tmp);
-    py_newnativefunc(tmp, _py_float__truediv__, 2);
-    py_setdict(float_type, __truediv__, tmp);
+    py_bindmagic(tp_int, __truediv__, _py_int__truediv__);
+    py_bindmagic(tp_float, __truediv__, _py_float__truediv__);
 
     // __pow__
-    py_newnativefunc(tmp, _py_number__pow__, 2);
-    py_setdict(int_type, __pow__, tmp);
-    py_setdict(float_type, __pow__, tmp);
+    py_bindmagic(tp_int, __pow__, _py_number__pow__);
+    py_bindmagic(tp_float, __pow__, _py_number__pow__);
 
     // __floordiv__ & __mod__
-    py_newnativefunc(tmp, _py_int__floordiv__, 2);
-    py_setdict(int_type, __floordiv__, tmp);
-    py_newnativefunc(tmp, _py_int__mod__, 2);
-    py_setdict(int_type, __mod__, tmp);
+    py_bindmagic(tp_int, __floordiv__, _py_int__floordiv__);
+    py_bindmagic(tp_int, __mod__, _py_int__mod__);
 
     // int.__invert__ & int.<BITWISE OP>
-    py_newnativefunc(tmp, _py_int__invert__, 1);
-    py_setdict(int_type, __invert__, tmp);
+    py_bindmagic(tp_int, __invert__, _py_int__invert__);
 
-    BIND_INT_BINARY_OP(__and__);
-    BIND_INT_BINARY_OP(__or__);
-    BIND_INT_BINARY_OP(__xor__);
-    BIND_INT_BINARY_OP(__lshift__);
-    BIND_INT_BINARY_OP(__rshift__);
+    py_bindmagic(tp_int, __and__, _py_int__and__);
+    py_bindmagic(tp_int, __or__, _py_int__or__);
+    py_bindmagic(tp_int, __xor__, _py_int__xor__);
+    py_bindmagic(tp_int, __lshift__, _py_int__lshift__);
+    py_bindmagic(tp_int, __rshift__, _py_int__rshift__);
 
     // int.bit_length
-    py_newnativefunc(tmp, _py_int__bit_length, 1);
-    py_setdict(int_type, py_name("bit_length"), tmp);
-
-#undef BIND_INT_BINARY_OP
-#undef BIND_FLOAT_BINARY_OP
-
-    py_poptmp(3);
+    py_bindmethod(tp_int, "bit_length", _py_int__bit_length);
 
     // py_Ref builtins = py_getmodule("builtins");
     // py_newfunction(py_reg(0), _py_print,

+ 1 - 4
src/interpreter/vm.c

@@ -108,10 +108,7 @@ void pk_VM__ctor(pk_VM* self) {
     validate(tp_bool, pk_VM__new_type(self, "bool", tp_object, NULL, false));
     validate(tp_str, pk_VM__new_type(self, "str", tp_object, NULL, false));
 
-    validate(tp_list, pk_VM__new_type(self, "list", tp_object, NULL, false));
-    pk_TypeInfo* ti = c11__at(pk_TypeInfo, &self->types, tp_list);
-    ti->dtor = (void (*)(void*))c11_vector__dtor;
-
+    validate(tp_list, pk_list__register());
     validate(tp_tuple, pk_VM__new_type(self, "tuple", tp_object, NULL, false));
 
     validate(tp_slice, pk_VM__new_type(self, "slice", tp_object, NULL, false));

+ 8 - 0
src/public/py_list.c

@@ -6,6 +6,14 @@
 
 typedef c11_vector List;
 
+py_Type pk_list__register() {
+    pk_VM* vm = pk_current_vm;
+    py_Type type = pk_VM__new_type(vm, "list", tp_object, NULL, false);
+    pk_TypeInfo* ti = c11__at(pk_TypeInfo, &vm->types, type);
+    ti->dtor = (void (*)(void*))c11_vector__dtor;
+    return type;
+}
+
 void py_newlist(py_Ref out) {
     pk_VM* vm = pk_current_vm;
     PyObject* obj = pk_ManagedHeap__gcnew(&vm->heap, tp_list, 0, sizeof(List));

+ 0 - 13
src/public/py_ops.c

@@ -7,19 +7,6 @@ int py_le(const py_Ref lhs, const py_Ref rhs) { return 0; }
 
 bool py_hash(const py_Ref val, int64_t* out) { return 0; }
 
-bool py_str(const py_Ref val, py_Ref out) { return 0; }
-
-bool py_repr(const py_Ref val, py_Ref out) {
-    const pk_TypeInfo* ti = pk_tpinfo(val);
-    if(ti->m__repr__) return ti->m__repr__(1, val, out);
-    bool ok = py_callmethod(val, __repr__, 0, NULL);
-    if(ok) {
-        *out = pk_current_vm->last_retval;
-        return true;
-    }
-    return false;
-}
-
 bool py_getattr(const py_Ref self, py_Name name, py_Ref out) { return true; }
 
 bool py_setattr(py_Ref self, py_Name name, const py_Ref val) { return -1; }

+ 20 - 11
src/public/values.c

@@ -39,7 +39,7 @@ void py_newstrn(py_Ref out, const char* data, int size) {
     out->_obj = obj;
 }
 
-void py_newStr_(py_Ref out, py_Str input){
+void py_newStr_(py_Ref out, py_Str input) {
     pk_ManagedHeap* heap = &pk_current_vm->heap;
     PyObject* obj = pk_ManagedHeap__gcnew(heap, tp_str, 0, sizeof(py_Str));
     py_Str* userdata = PyObject__value(obj);
@@ -79,30 +79,39 @@ void py_newfunction2(py_Ref out,
                      const char* docstring,
                      const py_Ref upvalue) {}
 
-void py_newnativefunc(py_Ref out, py_CFunction f, int argc) {
-    py_newnativefunc2(out, f, argc, BindType_FUNCTION, NULL, NULL);
+void py_newnativefunc(py_Ref out, py_CFunction f) {
+
+}
+
+void py_bindmethod(py_Type type, const char *name, py_CFunction f){
+    py_bindmethod2(type, name, f, BindType_FUNCTION);
 }
 
-void py_newnativefunc2(py_Ref out,
-                       py_CFunction f,
-                       int argc,
-                       BindType bt,
-                       const char* docstring,
-                       const py_Ref upvalue) {}
+void py_bindmethod2(py_Type type, const char *name, py_CFunction f, BindType bt){
+    py_TValue tmp;
+    py_newnativefunc(&tmp, f);
+    py_setdict(py_tpobject(type), py_name(name), &tmp);
+}
+
+void py_bindnativefunc(py_Ref obj, const char *name, py_CFunction f){
+    py_TValue tmp;
+    py_newnativefunc(&tmp, f);
+    py_setdict(obj, py_name(name), &tmp);
+}
 
 void py_newnotimplemented(py_Ref out) {
     pk_VM* vm = pk_current_vm;
     *out = vm->NotImplemented;
 }
 
-void py_newslice(py_Ref out, const py_Ref start, const py_Ref stop, const py_Ref step){
+void py_newslice(py_Ref out, const py_Ref start, const py_Ref stop, const py_Ref step) {
     py_newobject(out, tp_slice, 3, 0);
     py_setslot(out, 0, start);
     py_setslot(out, 1, stop);
     py_setslot(out, 2, step);
 }
 
-void py_newobject(py_Ref out, py_Type type, int slots, int udsize){
+void py_newobject(py_Ref out, py_Type type, int slots, int udsize) {
     pk_ManagedHeap* heap = &pk_current_vm->heap;
     PyObject* obj = pk_ManagedHeap__gcnew(heap, type, slots, udsize);
     out->type = type;

+ 38 - 13
src/public/vm.c

@@ -40,35 +40,60 @@ int py_eval(const char* source, py_Ref out) {
     CodeObject__dtor(&co);
     PK_DECREF(src);
     if(res == RES_ERROR) return vm->last_error->type;
-    if(res == RES_RETURN){
+    if(res == RES_RETURN) {
         *out = vm->last_retval;
         return 0;
     }
     PK_UNREACHABLE();
 }
 
-bool py_call(py_Ref f, int argc, py_Ref argv){
-    return -1;
-}
+bool py_call(py_Ref f, int argc, py_Ref argv) { return -1; }
+
+bool py_callmethod(py_Ref self, py_Name name, int argc, py_Ref argv) { return -1; }
 
-bool py_callmethod(py_Ref self, py_Name name, int argc, py_Ref argv){
+bool pk_vectorcall(int argc, int kwargc, bool op_call) { return -1; }
+
+py_Ref py_lastretval() { return &pk_current_vm->last_retval; }
+
+bool py_getunboundmethod(const py_Ref self,
+                         py_Name name,
+                         bool fallback,
+                         py_Ref out,
+                         py_Ref out_self) {
     return -1;
 }
 
-int pk_vectorcall(int argc, int kwargc, bool op_call){
-    return -1;
+pk_TypeInfo* pk_tpinfo(const py_Ref self) {
+    pk_VM* vm = pk_current_vm;
+    return c11__at(pk_TypeInfo, &vm->types, self->type);
 }
 
-py_Ref py_lastretval(){
-    return &pk_current_vm->last_retval;
+py_Ref py_tpfindmagic(py_Type t, py_Name name) {
+    assert(name < 64);
+    pk_TypeInfo* types = (pk_TypeInfo*)pk_current_vm->types.data;
+    do {
+        py_Ref f = &types[t].magic[name];
+        if(!py_isnull(f)) return f;
+        t = types[t].base;
+    } while(t);
+    return NULL;
 }
 
-bool py_getunboundmethod(const py_Ref self, py_Name name, bool fallback, py_Ref out, py_Ref out_self){
-    return -1;
+py_Ref py_tpmagic(py_Type type, py_Name name) {
+    assert(name < 64);
+    pk_VM* vm = pk_current_vm;
+    return &c11__at(pk_TypeInfo, &vm->types, type)->magic[name];
 }
 
-pk_TypeInfo* pk_tpinfo(const py_Ref self){
+py_Ref py_tpobject(py_Type type) {
     pk_VM* vm = pk_current_vm;
-    return c11__at(pk_TypeInfo, &vm->types, self->type);
+    return &c11__at(pk_TypeInfo, &vm->types, type)->self;
 }
 
+bool py_callmagic(py_Name name, int argc, py_Ref argv) {
+    assert(argc >= 1);
+    py_Ref tmp = py_tpfindmagic(argv->type, name);
+    if(!tmp) return TypeError(name);
+    if(tmp->type == tp_nativefunc) { return tmp->_cfunc(argc, argv, &pk_current_vm->last_retval); }
+    return py_call(tmp, argc, argv);
+}