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

+ 1 - 0
include/pocketpy/interpreter/vm.h

@@ -94,6 +94,7 @@ bool pk_arrayiter(py_Ref val);
 bool pk_arraycontains(py_Ref self, py_Ref val);
 
 bool pk_pushmethod(py_StackRef self, py_Name name);
+bool pk_callmagic(py_Name name, int argc, py_Ref argv);
 
 /// Assumes [a, b] are on the stack, performs a binary op.
 /// The result is stored in `self->last_retval`.

+ 107 - 123
include/pocketpy/pocketpy.h

@@ -15,7 +15,8 @@ typedef int16_t py_Type;
 typedef int64_t py_i64;
 typedef double py_f64;
 
-/* string_view */
+#define PY_RAISE  // mark a function that can raise an exception
+
 typedef struct c11_sv {
     const char* data;
     int size;
@@ -36,26 +37,33 @@ typedef py_TValue* py_TmpRef;
 /// @param argc number of arguments.
 /// @param argv array of arguments. Use `py_arg(i)` macro to get the i-th argument.
 /// @return true if the function is successful.
-typedef bool (*py_CFunction)(int argc, py_StackRef argv);
+typedef bool (*py_CFunction)(int argc, py_StackRef argv) PY_RAISE;
 
-enum py_BindType {
-    bt_function,
-    bt_staticmethod,
-    bt_classmethod,
-};
+enum py_BindType { bt_function, bt_staticmethod, bt_classmethod };
 
 enum py_CompileMode { EXEC_MODE, EVAL_MODE, REPL_MODE, CELL_MODE };
 
-/************* Global VMs *************/
+extern py_GlobalRef py_True;
+extern py_GlobalRef py_False;
+extern py_GlobalRef py_None;
+extern py_GlobalRef py_NIL;
+
+/************* Global Setup *************/
+/// Initialize the VM.
 void py_initialize();
+/// Finalize the VM.
 void py_finalize();
 
-/// Run a simple source string. Do not change the stack.
-bool py_exec(const char* source);
-/// Eval a simple expression.
-/// The result will be set to `py_retval()`.
-bool py_eval(const char* source);
-bool py_exec2(const char* source, const char* filename, enum py_CompileMode mode);
+/// Run a source string.
+/// @param source source string.
+/// @param filename filename (for error messages).
+/// @param mode compile mode. Use `EXEC_MODE` for statements `EVAL_MODE` for expressions.
+/// @param module target module. Use NULL for the main module.
+/// @return true if the execution is successful.
+bool py_exec(const char* source,
+             const char* filename,
+             enum py_CompileMode mode,
+             py_Ref module) PY_RAISE;
 
 /************* Values Creation *************/
 void py_newint(py_Ref, py_i64);
@@ -78,6 +86,17 @@ void py_newlist(py_Ref);
 /// You should initialize all elements before using it.
 void py_newlistn(py_Ref, int n);
 
+void py_newdict(py_Ref);
+void py_newslice(py_Ref);
+void py_newnativefunc(py_Ref out, py_CFunction);
+py_Name py_newfunction(py_Ref out,
+                    const char* sig,
+                    py_CFunction f,
+                    enum py_BindType bt,
+                    const char* docstring,
+                    int slots);
+
+/************* Name Convertions *************/
 py_Name py_name(const char*);
 const char* py_name2str(py_Name);
 py_Name py_namev(c11_sv name);
@@ -85,12 +104,7 @@ c11_sv py_name2sv(py_Name);
 
 #define py_ismagicname(name) (name <= __missing__)
 
-// opaque types
-void py_newdict(py_Ref);
-void py_newslice(py_Ref);
-// old style argc-based function
-void py_newnativefunc(py_Ref out, py_CFunction);
-
+/************* Meta Operations *************/
 /// Create a new type.
 /// @param name name of the type.
 /// @param base base type.
@@ -104,6 +118,7 @@ py_Type py_newtype(const char* name, py_Type base, const py_GlobalRef module, vo
 /// @param slots number of slots. Use -1 to create a `__dict__`.
 /// @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(py_Ref);
 py_f64 py_tofloat(py_Ref);
@@ -125,15 +140,34 @@ void* py_touserdata(py_Ref);
 #define py_istuple(self) py_istype(self, tp_tuple)
 #define py_isdict(self) py_istype(self, tp_dict)
 
+#define py_isnil(self) py_istype(self, 0)
+#define py_isnone(self) py_istype(self, tp_NoneType)
+
+py_Type py_typeof(py_Ref self);
 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;
-extern py_GlobalRef py_False;
-extern py_GlobalRef py_None;
-extern py_GlobalRef py_NIL;
+/// Search the magic method from the given type to the base type.
+py_GlobalRef py_tpfindmagic(py_Type, py_Name name);
+/// Search the name from the given type to the base type.
+py_GlobalRef py_tpfindname(py_Type, py_Name name);
+/// Get the type object of the given type.
+py_GlobalRef py_tpobject(py_Type type);
+/// Get the type name.
+const char* py_tpname(py_Type type);
+/// Call a type to create a new instance.
+bool py_tpcall(py_Type type, int argc, py_Ref argv);
+/// Find the magic method from the given type to the base type.
+py_GlobalRef py_tpmagic(py_Type type, py_Name name);
+
+/// Check if the object is an instance of the given type.
+bool py_checktype(py_Ref self, py_Type type) PY_RAISE;
 
+#define py_checkint(self) py_checktype(self, tp_int)
+#define py_checkfloat(self) py_checktype(self, tp_float)
+#define py_checkbool(self) py_checktype(self, tp_bool)
+#define py_checkstr(self) py_checktype(self, tp_str)
 /************* References *************/
 #define PY_CHECK_ARGC(n)                                                                           \
     if(argc != n) return TypeError("expected %d arguments, got %d", n, argc)
@@ -144,27 +178,16 @@ extern py_GlobalRef py_NIL;
 #define py_offset(p, i) ((py_Ref)((char*)p + ((i) << 4)))
 #define py_arg(i) py_offset(argv, i)
 
-py_GlobalRef 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
-void py_bind(py_Ref obj, const char* sig, py_CFunction f);
-
-py_ObjectRef py_bind2(py_Ref obj,
-                      const char* sig,
-                      py_CFunction f,
-                      enum py_BindType bt,
-                      const char* docstring,
-                      int slots);
-
-// 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, enum py_BindType bt);
-void py_bindfunc(py_Ref obj, const char* name, py_CFunction f);
-
 /// Get the reference to the i-th register.
 /// All registers are located in a contiguous memory.
-py_GlobalRef py_reg(int i);
+py_GlobalRef py_getreg(int i);
+/// Set the reference to the i-th register.
+void py_setreg(int i, py_Ref val);
+
+/// Equivalent to `*dst = *src`.
+void py_assign(py_Ref dst, py_Ref src);
+/// The return value of the most recent call.
+py_GlobalRef py_retval();
 
 /// Get the reference of the object's `__dict__`.
 /// The object must have a `__dict__`.
@@ -178,21 +201,28 @@ bool py_deldict(py_Ref self, py_Name name);
 py_ObjectRef py_getslot(py_Ref self, int i);
 void py_setslot(py_Ref self, int i, 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, py_Ref val);
-/// Deletes the attribute of the object.
-bool py_delattr(py_Ref self, py_Name name);
+/************* Bindings *************/
+// new style decl-based bindings
+void py_bind(py_Ref obj, const char* sig, py_CFunction f);
+// old style argc-based bindings
+void py_bindmethod(py_Type type, const char* name, py_CFunction f);
+void py_bindfunc(py_Ref obj, const char* name, py_CFunction f);
+
+#define py_bindmagic(type, __magic__, f) py_newnativefunc(py_tpmagic((type), __magic__), (f))
 
-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);
+/************* Python Equivalents *************/
+bool py_getattr(py_Ref self, py_Name name) PY_RAISE;
+bool py_setattr(py_Ref self, py_Name name, py_Ref val) PY_RAISE;
+bool py_delattr(py_Ref self, py_Name name) PY_RAISE;
+
+bool py_getitem(py_Ref self, py_Ref key) PY_RAISE;
+bool py_setitem(py_Ref self, py_Ref key, py_Ref val) PY_RAISE;
+bool py_delitem(py_Ref self, py_Ref key) PY_RAISE;
 
 /// 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(py_Ref lhs, py_Ref rhs, py_Name op, py_Name rop);
+bool py_binaryop(py_Ref lhs, py_Ref rhs, py_Name op, py_Name rop) PY_RAISE;
 
 #define py_binaryadd(lhs, rhs) py_binaryop(lhs, rhs, __add__, __radd__)
 #define py_binarysub(lhs, rhs) py_binaryop(lhs, rhs, __sub__, __rsub__)
@@ -209,9 +239,6 @@ bool py_binaryop(py_Ref lhs, py_Ref rhs, py_Name op, py_Name rop);
 #define py_binaryxor(lhs, rhs) py_binaryop(lhs, rhs, __xor__, 0)
 #define py_binarymatmul(lhs, rhs) py_binaryop(lhs, rhs, __matmul__, 0)
 
-/// Equivalent to `*dst = *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.
@@ -238,13 +265,13 @@ py_TmpRef py_getmodule(const char* name);
 
 /// Import a module.
 /// The result will be set to `py_retval()`.
-bool py_import(const char* name);
+bool py_import(const char* name) PY_RAISE;
 
 /************* Errors *************/
 /// Raise an exception by name and message. Always returns false.
-bool py_exception(const char* name, const char* fmt, ...);
+bool py_exception(const char* name, const char* fmt, ...) PY_RAISE;
 /// Raise an expection object. Always returns false.
-bool py_raise(py_Ref);
+bool py_raise(py_Ref) PY_RAISE;
 /// Print the last error to the console.
 void py_printexc();
 /// Format the last error to a string.
@@ -264,13 +291,16 @@ bool py_checkexc();
     py_exception("UnboundLocalError", "local variable '%n' referenced before assignment", (n))
 
 bool StopIteration();
-bool KeyError(py_Ref key);
+bool KeyError(py_Ref key) PY_RAISE;
 
 /************* Operators *************/
+int py_equal(py_Ref lhs, py_Ref rhs) PY_RAISE;
+int py_less(py_Ref lhs, py_Ref rhs) PY_RAISE;
+
 /// Equivalent to `bool(val)`.
 /// Returns 1 if `val` is truthy, otherwise 0.
 /// Returns -1 if an error occurred.
-int py_bool(py_Ref val);
+int py_bool(py_Ref val) PY_RAISE;
 
 #define py_eq(lhs, rhs) py_binaryop(lhs, rhs, __eq__, __eq__)
 #define py_ne(lhs, rhs) py_binaryop(lhs, rhs, __ne__, __ne__)
@@ -279,61 +309,42 @@ int py_bool(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(py_Ref lhs, py_Ref rhs);
-int py_less(py_Ref lhs, py_Ref rhs);
-
-bool py_hash(py_Ref, py_i64* out);
-
+bool py_hash(py_Ref, py_i64* out) PY_RAISE;
 /// Get the iterator of the object.
-bool py_iter(py_Ref);
+bool py_iter(py_Ref) PY_RAISE;
 /// Get the next element from the iterator.
 /// 1: success, 0: StopIteration, -1: error
-int py_next(py_Ref);
-
+int py_next(py_Ref) PY_RAISE;
 /// Python equivalent to `lhs is rhs`.
 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.
 /// The result will be set to `py_retval()`.
 /// The stack size will be reduced by `argc + kwargc`.
-bool py_vectorcall(uint16_t argc, uint16_t kwargc);
+bool py_vectorcall(uint16_t argc, uint16_t kwargc) PY_RAISE;
 /// Call a function.
 /// It prepares the stack and then performs a `vectorcall(argc, 0, false)`.
 /// The result will be set to `py_retval()`.
 /// The stack remains unchanged after the operation.
-bool py_call(py_Ref f, int argc, py_Ref argv);
+bool py_call(py_Ref f, int argc, py_Ref argv) PY_RAISE;
 /// Call a non-magic method.
 /// It prepares the stack and then performs a `vectorcall(argc+1, 0, false)`.
 /// The result will be set to `py_retval()`.
 /// The stack remains unchanged after the operation.
-bool py_callmethod(py_Ref self, py_Name, int argc, py_Ref argv);
-/// Call a magic method using a continuous buffer.
-/// The result will be set to `py_retval()`.
-/// The stack remains unchanged after the operation.
-bool py_callmagic(py_Name name, int argc, py_Ref argv);
+bool py_callmethod(py_Ref self, py_Name name, int argc, py_Ref argv) PY_RAISE;
 /// Call a `py_CFunction` in a safe way.
-bool py_callcfunc(py_StackRef p0, py_CFunction cfunc, int argc, py_Ref argv);
-
-bool py_str(py_Ref val);
-#define py_repr(val) py_callmagic(__repr__, 1, val)
-#define py_len(val) py_callmagic(__len__, 1, val)
+bool py_callcfunc(py_StackRef p0, py_CFunction cfunc, int argc, py_Ref argv) PY_RAISE;
 
-/// The return value of the most recent call.
-py_GlobalRef py_retval();
+bool py_str(py_Ref val) PY_RAISE;
+bool py_repr(py_Ref val) PY_RAISE;
+bool py_len(py_Ref val) PY_RAISE;
 
-#define py_isnil(self) py_istype(self, 0)
-#define py_isnone(self) py_istype(self, tp_NoneType)
-
-/* tuple */
-
-// unchecked functions, if self is not a tuple, the behavior is undefined
+/************* Unchecked Functions *************/
 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(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);
@@ -344,44 +355,17 @@ void py_list__clear(py_Ref self);
 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(py_Ref self, py_Ref key);
-void py_dict__setitem(py_Ref self, py_Ref key, py_Ref val);
+py_TmpRef py_dict__getitem(py_Ref self, py_Ref key) PY_RAISE;
+void py_dict__setitem(py_Ref self, py_Ref key, py_Ref val) PY_RAISE;
+void py_dict__delitem(py_Ref self, py_Ref key) PY_RAISE;
 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.
-py_GlobalRef py_tpfindmagic(py_Type, py_Name name);
-
-/// Search the name from the given type to the base type.
-/// Return the reference or NULL if not found.
-py_GlobalRef py_tpfindname(py_Type, py_Name name);
-
-/// Get the type object of the given type.
-py_GlobalRef py_tpobject(py_Type type);
-
-/// Get the type name.
-const char* py_tpname(py_Type type);
-
-/// Call a type to create a new instance.
-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(py_Ref self, py_Type type);
-
-/// Get the type of the object.
-py_Type py_typeof(py_Ref self);
-
-#define py_checkint(self) py_checktype(self, tp_int)
-#define py_checkfloat(self) py_checktype(self, tp_float)
-#define py_checkbool(self) py_checktype(self, tp_bool)
-#define py_checkstr(self) py_checktype(self, tp_str)
-
+/************* Others *************/
 int py_replinput(char* buf);
 
-/// Python favored string formatting.
+/// Python favored string formatting. (just put here, not for users)
 /// %d: int
 /// %i: py_i64 (int64_t)
 /// %f: py_f64 (double)

+ 3 - 3
src/interpreter/ceval.c

@@ -717,7 +717,7 @@ pk_FrameResult pk_VM__run_top_frame(pk_VM* self) {
             }
             /////////
             case OP_UNARY_NEGATIVE: {
-                if(!py_callmagic(__neg__, 1, TOP())) goto __ERROR;
+                if(!pk_callmagic(__neg__, 1, TOP())) goto __ERROR;
                 *TOP() = self->last_retval;
                 DISPATCH();
             }
@@ -735,7 +735,7 @@ pk_FrameResult pk_VM__run_top_frame(pk_VM* self) {
                 DISPATCH();
             }
             case OP_UNARY_INVERT: {
-                if(!py_callmagic(__invert__, 1, TOP())) goto __ERROR;
+                if(!pk_callmagic(__invert__, 1, TOP())) goto __ERROR;
                 *TOP() = self->last_retval;
                 DISPATCH();
             }
@@ -889,7 +889,7 @@ pk_FrameResult pk_VM__run_top_frame(pk_VM* self) {
                 py_TValue* tmp = c11__at(py_TValue, &frame->co->consts, byte.arg);
                 const char* string = py_tostr(tmp);
                 // TODO: optimize this
-                if(!py_exec2(string, "<eval>", EVAL_MODE)) goto __ERROR;
+                if(!py_exec(string, "<eval>", EVAL_MODE, &frame->module)) goto __ERROR;
                 PUSH(py_retval());
                 DISPATCH();
             }

+ 6 - 15
src/public/vm.c → src/public/internal.c

@@ -174,8 +174,8 @@ static void disassemble(CodeObject* co) {
     c11_vector__dtor(&jumpTargets);
 }
 
-static bool
-    pk_VM__exec(pk_VM* vm, const char* source, const char* filename, enum py_CompileMode mode) {
+bool py_exec(const char* source, const char* filename, enum py_CompileMode mode, py_Ref module) {
+    pk_VM* vm = pk_current_vm;
     CodeObject co;
     pk_SourceData_ src = pk_SourceData__rcnew(source, filename, mode, false);
     Error* err = pk_compile(src, &co);
@@ -189,8 +189,9 @@ static bool
     }
 
     // disassemble(&co);
+    if(!module) module = &vm->main;
 
-    Frame* frame = Frame__new(&co, &vm->main, NULL, vm->stack.sp, vm->stack.sp, &co);
+    Frame* frame = Frame__new(&co, module, NULL, vm->stack.sp, vm->stack.sp, &co);
     pk_VM__push_frame(vm, frame);
     pk_FrameResult res = pk_VM__run_top_frame(vm);
     CodeObject__dtor(&co);
@@ -200,14 +201,6 @@ static bool
     c11__unreachedable();
 }
 
-bool py_exec(const char* source) { return pk_VM__exec(pk_current_vm, source, "<exec>", EXEC_MODE); }
-
-bool py_eval(const char* source) { return pk_VM__exec(pk_current_vm, source, "<eval>", EVAL_MODE); }
-
-bool py_exec2(const char* source, const char* filename, enum py_CompileMode mode) {
-    return pk_VM__exec(pk_current_vm, source, filename, mode);
-}
-
 bool py_call(py_Ref f, int argc, py_Ref argv) {
     if(f->type == tp_nativefunc) {
         py_TValue* p0 = pk_current_vm->stack.sp;
@@ -230,9 +223,7 @@ bool py_vectorcall(uint16_t argc, uint16_t kwargc) {
 
 py_Ref py_retval() { return &pk_current_vm->last_retval; }
 
-bool py_pushmethod(py_Name name){
-    return pk_pushmethod(py_peek(-1), name);
-}
+bool py_pushmethod(py_Name name) { return pk_pushmethod(py_peek(-1), name); }
 
 bool pk_pushmethod(py_StackRef self, py_Name name) {
     // NOTE: `out` and `out_self` may overlap with `self`
@@ -317,7 +308,7 @@ bool py_tpcall(py_Type type, int argc, py_Ref argv) {
     return py_call(py_tpobject(type), argc, argv);
 }
 
-bool py_callmagic(py_Name name, int argc, py_Ref argv) {
+bool pk_callmagic(py_Name name, int argc, py_Ref argv) {
     assert(argc >= 1);
     assert(py_ismagicname(name));
     py_Ref tmp = py_tpfindmagic(argv->type, name);

+ 3 - 5
src/public/modules.c

@@ -156,7 +156,7 @@ static bool _py_builtins__hash(int argc, py_Ref argv) {
 
 static bool _py_builtins__abs(int argc, py_Ref argv) {
     PY_CHECK_ARGC(1);
-    return py_callmagic(__abs__, 1, argv);
+    return pk_callmagic(__abs__, 1, argv);
 }
 
 static bool _py_builtins__sum(int argc, py_Ref argv) {
@@ -205,15 +205,13 @@ static bool _py_NoneType__repr__(int argc, py_Ref argv) {
 static bool _py_builtins__exec(int argc, py_Ref argv) {
     PY_CHECK_ARGC(1);
     PY_CHECK_ARG_TYPE(0, tp_str);
-    bool ok = py_exec(py_tostr(argv));
-    py_newnone(py_retval());
-    return ok;
+    return py_exec(py_tostr(argv), "<exec>", EXEC_MODE, NULL);
 }
 
 static bool _py_builtins__eval(int argc, py_Ref argv) {
     PY_CHECK_ARGC(1);
     PY_CHECK_ARG_TYPE(0, tp_str);
-    return py_eval(py_tostr(argv));
+    return py_exec(py_tostr(argv), "<eval>", EVAL_MODE, NULL);
 }
 
 py_TValue pk_builtins__register() {

+ 6 - 0
src/public/py_dict.c

@@ -500,6 +500,12 @@ py_Ref py_dict__getitem(py_Ref self, py_Ref key) {
     return NULL;
 }
 
+void py_dict__delitem(py_Ref self, py_Ref key) {
+    assert(py_isdict(self));
+    Dict* ud = py_touserdata(self);
+    Dict__pop(ud, key);
+}
+
 void py_dict__setitem(py_Ref self, py_Ref key, py_Ref val) {
     assert(py_isdict(self));
     Dict* ud = py_touserdata(self);

+ 3 - 3
src/public/py_ops.c

@@ -178,7 +178,7 @@ bool py_delattr(py_Ref self, py_Name name) {
 bool py_getitem(py_Ref self, py_Ref key) {
     py_push(self);
     py_push(key);
-    bool ok = py_callmagic(__getitem__, 2, py_peek(-2));
+    bool ok = pk_callmagic(__getitem__, 2, py_peek(-2));
     py_shrink(2);
     return ok;
 }
@@ -187,7 +187,7 @@ bool py_setitem(py_Ref self, py_Ref key, py_Ref val) {
     py_push(self);
     py_push(key);
     py_push(val);
-    bool ok = py_callmagic(__setitem__, 3, py_peek(-3));
+    bool ok = pk_callmagic(__setitem__, 3, py_peek(-3));
     py_shrink(3);
     return ok;
 }
@@ -195,7 +195,7 @@ bool py_setitem(py_Ref self, py_Ref key, py_Ref val) {
 bool py_delitem(py_Ref self, py_Ref key) {
     py_push(self);
     py_push(key);
-    bool ok = py_callmagic(__delitem__, 2, py_peek(-2));
+    bool ok = pk_callmagic(__delitem__, 2, py_peek(-2));
     py_shrink(2);
     return ok;
 }

+ 8 - 0
src/public/py_str.c

@@ -545,3 +545,11 @@ bool py_str(py_Ref val) {
     if(!tmp) return py_repr(val);
     return py_call(tmp, 1, val);
 }
+
+bool py_repr(py_Ref val) {
+    return pk_callmagic(__repr__, 1, val);
+}
+
+bool py_len(py_Ref val){
+    return pk_callmagic(__len__, 1, val);
+}

+ 3 - 1
src/public/stack_ops.c

@@ -4,7 +4,9 @@
 #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_getreg(int i) { return pk_current_vm->reg + i; }
+
+void py_setreg(int i, py_Ref val) { pk_current_vm->reg[i] = *val; }
 
 py_Ref py_getdict(py_Ref self, py_Name name) {
     assert(self && self->is_ptr);

+ 28 - 23
src/public/values.c

@@ -49,10 +49,6 @@ 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, bt_function);
-}
-
-void py_bindmethod2(py_Type type, const char* name, py_CFunction f, enum py_BindType bt) {
     py_TValue tmp;
     py_newnativefunc(&tmp, f);
     py_setdict(py_tpobject(type), py_name(name), &tmp);
@@ -66,28 +62,37 @@ void py_bindfunc(py_Ref obj, const char* name, py_CFunction f) {
 
 void py_bind(py_Ref obj, const char* sig, py_CFunction f) {
     py_TValue tmp;
-    do {
-        char buffer[256];
-        snprintf(buffer, sizeof(buffer), "def %s: pass", sig);
-        // fn(a, b, *c, d=1) -> None
-        CodeObject code;
-        pk_SourceData_ source = pk_SourceData__rcnew(buffer, "<bind>", EXEC_MODE, false);
-        Error* err = pk_compile(source, &code);
-        if(err) c11__abort("py_bind(): failed to compile signature '%s'", sig);
-        if(code.func_decls.count != 1) c11__abort("py_bind(): invalid signature '%s'", sig);
-        FuncDecl_ decl = c11__getitem(FuncDecl_, &code.func_decls, 0);
-        // construct the function
-        Function* ud = py_newobject(&tmp, tp_function, 0, sizeof(Function));
-        Function__ctor(ud, decl, NULL);
-        ud->cfunc = f;
-        CodeObject__dtor(&code);
-        PK_DECREF(source);
-    } while(0);
-    Function* ud = py_touserdata(&tmp);
-    py_Name name = py_name(ud->decl->code.name->data);
+    py_Name name = py_newfunction(&tmp, sig, f, bt_function, NULL, 0);
     py_setdict(obj, name, &tmp);
 }
 
+py_Name py_newfunction(py_Ref out,
+                       const char* sig,
+                       py_CFunction f,
+                       enum py_BindType bt,
+                       const char* docstring,
+                       int slots) {
+    char buffer[256];
+    snprintf(buffer, sizeof(buffer), "def %s: pass", sig);
+    // fn(a, b, *c, d=1) -> None
+    CodeObject code;
+    pk_SourceData_ source = pk_SourceData__rcnew(buffer, "<bind>", EXEC_MODE, false);
+    Error* err = pk_compile(source, &code);
+    if(err || code.func_decls.count != 1) {
+        c11__abort("py_newfunction(): invalid signature '%s'", sig);
+    }
+    FuncDecl_ decl = c11__getitem(FuncDecl_, &code.func_decls, 0);
+    decl->docstring = docstring;
+    // construct the function
+    Function* ud = py_newobject(out, tp_function, slots, sizeof(Function));
+    Function__ctor(ud, decl, NULL);
+    ud->cfunc = f;
+    CodeObject__dtor(&code);
+    PK_DECREF(source);
+    assert(decl->rc.count == 1);
+    return py_name(ud->decl->code.name->data);
+}
+
 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);

+ 2 - 2
src2/main.c

@@ -49,13 +49,13 @@ int main(int argc, char** argv) {
             int size = py_replinput(buf);
             assert(size < sizeof(buf));
             if(size >= 0) {
-                if(!py_exec2(buf, "<stdin>", REPL_MODE)) py_printexc();
+                if(!py_exec(buf, "<stdin>", REPL_MODE, NULL)) py_printexc();
             }
         }
     } else {
         char* source = read_file(argv[1]);
         if(source) {
-            if(!py_exec(source)) py_printexc();
+            if(!py_exec(source, argv[1], EXEC_MODE, NULL)) py_printexc();
             free(source);
         }
     }