blueloveTH 5 місяців тому
батько
коміт
d3d61dde0c

+ 289 - 289
include/pocketpy/pocketpy.h

@@ -123,14 +123,15 @@ PK_API void py_resetallvm();
 PK_API void* py_getvmctx();
 /// Set the current VM context. This is used for user-defined data.
 PK_API void py_setvmctx(void* ctx);
+/// Setup the callbacks for the current VM.
+PK_API py_Callbacks* py_callbacks();
+
 /// Set `sys.argv`. Used for storing command-line arguments.
 PK_API void py_sys_setargv(int argc, char** argv);
 /// Set the trace function for the current VM.
 PK_API void py_sys_settrace(py_TraceFunc func, bool reset);
 /// Invoke the garbage collector.
 PK_API int py_gc_collect();
-/// Setup the callbacks for the current VM.
-PK_API py_Callbacks* py_callbacks();
 
 /// Wrapper for `PK_MALLOC(size)`.
 PK_API void* py_malloc(size_t size);
@@ -139,18 +140,16 @@ PK_API void* py_realloc(void* ptr, size_t size);
 /// Wrapper for `PK_FREE(ptr)`.
 PK_API void py_free(void* ptr);
 
-/// Begin the watchdog with `timeout` in milliseconds.
-/// `PK_ENABLE_WATCHDOG` must be defined to `1` to use this feature.
-/// You need to call `py_watchdog_end()` later.
-/// If `timeout` is reached, `TimeoutError` will be raised.
-PK_API void py_watchdog_begin(py_i64 timeout);
-/// Reset the watchdog.
-PK_API void py_watchdog_end();
+/// A shorthand for `True`.
+PK_API py_GlobalRef py_True();
+/// A shorthand for `False`.
+PK_API py_GlobalRef py_False();
+/// A shorthand for `None`.
+PK_API py_GlobalRef py_None();
+/// A shorthand for `nil`. `nil` is not a valid python object.
+PK_API py_GlobalRef py_NIL();
 
-/// Bind a compile-time function via "decl-based" style.
-PK_API void py_macrobind(const char* sig, py_CFunction f);
-/// Get a compile-time function by name.
-PK_API py_ItemRef py_macroget(py_Name name);
+/************* Frame Ops *************/
 
 /// Get the current source location of the frame.
 PK_API const char* py_Frame_sourceloc(py_Frame* frame, int* lineno);
@@ -162,6 +161,14 @@ PK_API void py_Frame_newlocals(py_Frame* frame, py_OutRef out);
 /// Returns `NULL` if not available.
 PK_API py_StackRef py_Frame_function(py_Frame* frame);
 
+/************* Code Execution *************/
+
+/// Compile a source string into a code object.
+/// Use python's `exec()` or `eval()` to execute it.
+PK_API bool py_compile(const char* source,
+                       const char* filename,
+                       enum py_CompileMode mode,
+                       bool is_dynamic) PY_RAISE PY_RETURN;
 /// Run a source string.
 /// @param source source string.
 /// @param filename filename (for error messages).
@@ -172,10 +179,8 @@ PK_API bool py_exec(const char* source,
                     const char* filename,
                     enum py_CompileMode mode,
                     py_Ref module) PY_RAISE PY_RETURN;
-
 /// Evaluate a source string. Equivalent to `py_exec(source, "<string>", EVAL_MODE, module)`.
 PK_API bool py_eval(const char* source, py_Ref module) PY_RAISE PY_RETURN;
-
 /// Run a source string with smart interpretation.
 /// Example:
 /// `py_newstr(py_r0(), "abc");`
@@ -191,28 +196,7 @@ PK_API bool py_smartexec(const char* source, py_Ref module, ...) PY_RAISE PY_RET
 /// `// res will be 3`.
 PK_API bool py_smarteval(const char* source, py_Ref module, ...) PY_RAISE PY_RETURN;
 
-/// Compile a source string into a code object.
-/// Use python's `exec()` or `eval()` to execute it.
-PK_API bool py_compile(const char* source,
-                       const char* filename,
-                       enum py_CompileMode mode,
-                       bool is_dynamic) PY_RAISE PY_RETURN;
-
-/// Python equivalent to `globals()`.
-PK_API void py_newglobals(py_OutRef);
-/// Python equivalent to `locals()`.
-PK_API void py_newlocals(py_OutRef);
-
-/************* Values Creation *************/
-
-/// A shorthand for `True`.
-PK_API py_GlobalRef py_True();
-/// A shorthand for `False`.
-PK_API py_GlobalRef py_False();
-/// A shorthand for `None`.
-PK_API py_GlobalRef py_None();
-/// A shorthand for `nil`. `nil` is not a valid python object.
-PK_API py_GlobalRef py_NIL();
+/************* Value Creation *************/
 
 /// Create an `int` object.
 PK_API void py_newint(py_OutRef, py_i64);
@@ -241,19 +225,6 @@ PK_API void py_newellipsis(py_OutRef);
 /// Create a `nil` object. `nil` is an invalid representation of an object.
 /// Don't use it unless you know what you are doing.
 PK_API void py_newnil(py_OutRef);
-/// Create a `tuple` with `n` UNINITIALIZED elements.
-/// You should initialize all elements before using it.
-PK_API py_ObjectRef py_newtuple(py_OutRef, int n);
-/// Create an empty `list`.
-PK_API void py_newlist(py_OutRef);
-/// Create a `list` with `n` UNINITIALIZED elements.
-/// You should initialize all elements before using it.
-PK_API void py_newlistn(py_OutRef, int n);
-/// Create an empty `dict`.
-PK_API void py_newdict(py_OutRef);
-/// Create an UNINITIALIZED `slice` object.
-/// You should use `py_setslot()` to set `start`, `stop`, and `step`.
-PK_API void py_newslice(py_OutRef);
 /// Create a `nativefunc` object.
 PK_API void py_newnativefunc(py_OutRef, py_CFunction);
 /// Create a `function` object.
@@ -264,8 +235,15 @@ PK_API py_Name py_newfunction(py_OutRef out,
                               int slots);
 /// Create a `boundmethod` object.
 PK_API void py_newboundmethod(py_OutRef out, py_Ref self, py_Ref func);
+/// Create a new object.
+/// @param out output reference.
+/// @param type type of the object.
+/// @param slots number of slots. Use `-1` to create a `__dict__`.
+/// @param udsize size of your userdata.
+/// @return pointer to the userdata.
+PK_API void* py_newobject(py_OutRef out, py_Type type, int slots, int udsize);
 
-/************* Name Conversions *************/
+/************* Name Conversion *************/
 
 /// Convert a null-terminated string to a name.
 PK_API py_Name py_name(const char*);
@@ -278,28 +256,47 @@ PK_API py_Name py_namev(c11_sv);
 /// Convert a name to a `c11_sv`.
 PK_API c11_sv py_name2sv(py_Name);
 
-/************* Meta Operations *************/
-
-/// Create a new type.
-/// @param name name of the type.
-/// @param base base type.
-/// @param module module where the type is defined. Use `NULL` for built-in types.
-/// @param dtor destructor function. Use `NULL` if not needed.
-PK_API py_Type py_newtype(const char* name, py_Type base, const py_GlobalRef module, py_Dtor dtor);
+/************* Bindings *************/
 
-/// Create a new object.
-/// @param out output reference.
-/// @param type type of the object.
-/// @param slots number of slots. Use `-1` to create a `__dict__`.
-/// @param udsize size of your userdata.
-/// @return pointer to the userdata.
-PK_API void* py_newobject(py_OutRef out, py_Type type, int slots, int udsize);
+/// Bind a function to the object via "decl-based" style.
+/// @param obj the target object.
+/// @param sig signature of the function. e.g. `add(x, y)`.
+/// @param f function to bind.
+PK_API void py_bind(py_Ref obj, const char* sig, py_CFunction f);
+/// Bind a method to type via "argc-based" style.
+/// @param type the target type.
+/// @param name name of the method.
+/// @param f function to bind.
+PK_API void py_bindmethod(py_Type type, const char* name, py_CFunction f);
+/// Bind a static method to type via "argc-based" style.
+/// @param type the target type.
+/// @param name name of the method.
+/// @param f function to bind.
+PK_API void py_bindstaticmethod(py_Type type, const char* name, py_CFunction f);
+/// Bind a function to the object via "argc-based" style.
+/// @param obj the target object.
+/// @param name name of the function.
+/// @param f function to bind.
+PK_API void py_bindfunc(py_Ref obj, const char* name, py_CFunction f);
+/// Bind a property to type.
+/// @param type the target type.
+/// @param name name of the property.
+/// @param getter getter function.
+/// @param setter setter function. Use `NULL` if not needed.
+PK_API void
+    py_bindproperty(py_Type type, const char* name, py_CFunction getter, py_CFunction setter);
+/// Bind a magic method to type.
+PK_API void py_bindmagic(py_Type type, py_Name name, py_CFunction f);
+/// Bind a compile-time function via "decl-based" style.
+PK_API void py_macrobind(const char* sig, py_CFunction f);
+/// Get a compile-time function by name.
+PK_API py_ItemRef py_macroget(py_Name name);
 
-/************* Type Cast *************/
+/************* Value Cast *************/
 
 /// Convert an `int` object in python to `int64_t`.
 PK_API py_i64 py_toint(py_Ref);
-/// Get the address of the trivial value object.
+/// Get the address of the trivial value object (16 bytes).
 PK_API void* py_totrivial(py_Ref);
 /// Convert a `float` object in python to `double`.
 PK_API py_f64 py_tofloat(py_Ref);
@@ -315,6 +312,8 @@ PK_API bool py_castint(py_Ref, py_i64* out) PY_RAISE;
 PK_API bool py_tobool(py_Ref);
 /// Convert a `type` object in python to `py_Type`.
 PK_API py_Type py_totype(py_Ref);
+/// Convert a user-defined object to its userdata.
+PK_API void* py_touserdata(py_Ref);
 /// Convert a `str` object in python to null-terminated string.
 PK_API const char* py_tostr(py_Ref);
 /// Convert a `str` object in python to char array.
@@ -325,32 +324,32 @@ PK_API c11_sv py_tosv(py_Ref);
 PK_API unsigned char* py_tobytes(py_Ref, int* size);
 /// Resize a `bytes` object. It can only be resized down.
 PK_API void py_bytes_resize(py_Ref, int size);
-/// Convert a user-defined object to its userdata.
-PK_API void* py_touserdata(py_Ref);
-
-#define py_isint(self) py_istype(self, tp_int)
-#define py_isfloat(self) py_istype(self, tp_float)
-#define py_isbool(self) py_istype(self, tp_bool)
-#define py_isstr(self) py_istype(self, tp_str)
-#define py_islist(self) py_istype(self, tp_list)
-#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)
+/************* Type System *************/
 
-/// Get the type of the object.
-PK_API py_Type py_typeof(py_Ref self);
-/// Get type by module and name. e.g. `py_gettype("time", py_name("struct_time"))`.
-/// Return `0` if not found.
-PK_API py_Type py_gettype(const char* module, py_Name name);
+/// Create a new type.
+/// @param name name of the type.
+/// @param base base type.
+/// @param module module where the type is defined. Use `NULL` for built-in types.
+/// @param dtor destructor function. Use `NULL` if not needed.
+PK_API py_Type py_newtype(const char* name, py_Type base, const py_GlobalRef module, py_Dtor dtor);
 /// Check if the object is exactly the given type.
 PK_API bool py_istype(py_Ref, py_Type);
+/// Get the type of the object.
+PK_API py_Type py_typeof(py_Ref self);
 /// Check if the object is an instance of the given type.
 PK_API bool py_isinstance(py_Ref obj, py_Type type);
 /// Check if the derived type is a subclass of the base type.
 PK_API bool py_issubclass(py_Type derived, py_Type base);
-
+/// Get type by module and name. e.g. `py_gettype("time", py_name("struct_time"))`.
+/// Return `0` if not found.
+PK_API py_Type py_gettype(const char* module, py_Name name);
+/// Check if the object is an instance of the given type exactly.
+/// Raise `TypeError` if the check fails.
+PK_API bool py_checktype(py_Ref self, py_Type type) PY_RAISE;
+/// Check if the object is an instance of the given type or its subclass.
+/// Raise `TypeError` if the check fails.
+PK_API bool py_checkinstance(py_Ref self, py_Type type) PY_RAISE;
 /// Get the magic method from the given type only.
 /// Return `nil` if not found.
 PK_API PK_DEPRECATED py_GlobalRef py_tpgetmagic(py_Type type, py_Name name);
@@ -362,13 +361,10 @@ PK_API py_GlobalRef py_tpfindmagic(py_Type, py_Name name);
 PK_API py_ItemRef py_tpfindname(py_Type, py_Name name);
 /// Get the base type of the given type.
 PK_API py_Type py_tpbase(py_Type type);
-
 /// Get the type object of the given type.
 PK_API py_GlobalRef py_tpobject(py_Type type);
 /// Get the type name.
 PK_API const char* py_tpname(py_Type type);
-/// Call a type to create a new instance.
-PK_API bool py_tpcall(py_Type type, int argc, py_Ref argv) PY_RAISE PY_RETURN;
 /// Disable the type for subclassing.
 PK_API void py_tpsetfinal(py_Type type);
 /// Set attribute hooks for the given type.
@@ -379,26 +375,48 @@ PK_API void py_tphookattributes(py_Type type,
                                 bool (*delattribute)(py_Ref self, py_Name name) PY_RAISE,
                                 bool (*getunboundmethod)(py_Ref self, py_Name name) PY_RETURN);
 
-/// Check if the object is an instance of the given type exactly.
-/// Raise `TypeError` if the check fails.
-PK_API bool py_checktype(py_Ref self, py_Type type) PY_RAISE;
-
-/// Check if the object is an instance of the given type or its subclass.
-/// Raise `TypeError` if the check fails.
-PK_API bool py_checkinstance(py_Ref self, py_Type type) PY_RAISE;
+#define py_isint(self) py_istype(self, tp_int)
+#define py_isfloat(self) py_istype(self, tp_float)
+#define py_isbool(self) py_istype(self, tp_bool)
+#define py_isstr(self) py_istype(self, tp_str)
+#define py_islist(self) py_istype(self, tp_list)
+#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)
 
 #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 *************/
+/************* Inspection *************/
+
+/// Get the current `function` object on the stack.
+/// Return `NULL` if not available.
+/// NOTE: This function should be placed at the beginning of your decl-based bindings.
+PK_API py_StackRef py_inspect_currentfunction();
+/// Get the current `module` object where the code is executed.
+/// Return `NULL` if not available.
+PK_API py_GlobalRef py_inspect_currentmodule();
+/// Get the current frame object.
+/// Return `NULL` if not available.
+PK_API py_Frame* py_inspect_currentframe();
+/// Python equivalent to `globals()`.
+PK_API void py_newglobals(py_OutRef);
+/// Python equivalent to `locals()`.
+PK_API void py_newlocals(py_OutRef);
+
+/************* Dict & Slots *************/
 
 /// Get the i-th register.
 /// All registers are located in a contiguous memory.
 PK_API py_GlobalRef py_getreg(int i);
 /// Set the i-th register.
 PK_API void py_setreg(int i, py_Ref val);
+/// Get the last return value.
+/// Please note that `py_retval()` cannot be used as input argument.
+PK_API py_GlobalRef py_retval();
 
 #define py_r0() py_getreg(0)
 #define py_r1() py_getreg(1)
@@ -409,17 +427,6 @@ PK_API void py_setreg(int i, py_Ref val);
 #define py_r6() py_getreg(6)
 #define py_r7() py_getreg(7)
 
-/// Get variable in the `__main__` module.
-PK_API py_ItemRef py_getglobal(py_Name name);
-/// Set variable in the `__main__` module.
-PK_API void py_setglobal(py_Name name, py_Ref val);
-/// Get variable in the `builtins` module.
-PK_API py_ItemRef py_getbuiltin(py_Name name);
-
-/// Get the last return value.
-/// Please note that `py_retval()` cannot be used as input argument.
-PK_API py_GlobalRef py_retval();
-
 /// Get an item from the object's `__dict__`.
 /// Return `NULL` if not found.
 PK_API py_ItemRef py_getdict(py_Ref self, py_Name name);
@@ -437,56 +444,68 @@ PK_API bool
     py_applydict(py_Ref self, bool (*f)(py_Name name, py_Ref val, void* ctx), void* ctx) PY_RAISE;
 /// Clear the object's `__dict__`. This function is dangerous.
 PK_API void py_cleardict(py_Ref self);
-
 /// Get the i-th slot of the object.
 /// The object must have slots and `i` must be in valid range.
 PK_API py_ObjectRef py_getslot(py_Ref self, int i);
 /// Set the i-th slot of the object.
 PK_API void py_setslot(py_Ref self, int i, py_Ref val);
+/// Get variable in the `builtins` module.
+PK_API py_ItemRef py_getbuiltin(py_Name name);
+/// Get variable in the `__main__` module.
+PK_API py_ItemRef py_getglobal(py_Name name);
+/// Set variable in the `__main__` module.
+PK_API void py_setglobal(py_Name name, py_Ref val);
 
-/************* Inspection *************/
-
-/// Get the current `function` object on the stack.
-/// Return `NULL` if not available.
-/// NOTE: This function should be placed at the beginning of your decl-based bindings.
-PK_API py_StackRef py_inspect_currentfunction();
-/// Get the current `module` object where the code is executed.
-/// Return `NULL` if not available.
-PK_API py_GlobalRef py_inspect_currentmodule();
-/// Get the current frame object.
-/// Return `NULL` if not available.
-PK_API py_Frame* py_inspect_currentframe();
-/************* Bindings *************/
+/************* Stack Ops *************/
 
-/// Bind a function to the object via "decl-based" style.
-/// @param obj the target object.
-/// @param sig signature of the function. e.g. `add(x, y)`.
-/// @param f function to bind.
-PK_API void py_bind(py_Ref obj, const char* sig, py_CFunction f);
-/// Bind a method to type via "argc-based" style.
-/// @param type the target type.
-/// @param name name of the method.
-/// @param f function to bind.
-PK_API void py_bindmethod(py_Type type, const char* name, py_CFunction f);
-/// Bind a static method to type via "argc-based" style.
-/// @param type the target type.
-/// @param name name of the method.
-/// @param f function to bind.
-PK_API void py_bindstaticmethod(py_Type type, const char* name, py_CFunction f);
-/// Bind a function to the object via "argc-based" style.
-/// @param obj the target object.
-/// @param name name of the function.
-/// @param f function to bind.
-PK_API void py_bindfunc(py_Ref obj, const char* name, py_CFunction f);
-/// Bind a property to type.
-/// @param type the target type.
-/// @param name name of the property.
-/// @param getter getter function.
-/// @param setter setter function. Use `NULL` if not needed.
-PK_API void
-    py_bindproperty(py_Type type, const char* name, py_CFunction getter, py_CFunction setter);
-/// Bind a magic method to type.
-PK_API void py_bindmagic(py_Type type, py_Name name, py_CFunction f);
+/// Get the i-th object from the top of the stack.
+/// `i` should be negative, e.g. (-1) means TOS.
+PK_API py_StackRef py_peek(int i);
+/// Push the object to the stack.
+PK_API void py_push(py_Ref src);
+/// Push a `nil` object to the stack.
+PK_API void py_pushnil();
+/// Push a `None` object to the stack.
+PK_API void py_pushnone();
+/// Push a `py_Name` to the stack. This is used for keyword arguments.
+PK_API void py_pushname(py_Name name);
+/// Pop an object from the stack.
+PK_API void py_pop();
+/// Shrink the stack by n.
+PK_API void py_shrink(int n);
+/// Get a temporary variable from the stack.
+PK_API py_StackRef py_pushtmp();
+/// Get the unbound method of the object.
+/// Assume the object is located at the top of the stack.
+/// If return true:  `[self] -> [unbound, self]`.
+/// If return false: `[self] -> [self]` (no change).
+PK_API bool py_pushmethod(py_Name name);
+/// Evaluate an expression and push the result to the stack.
+/// This function is used for testing.
+PK_API bool py_pusheval(const char* expr, py_GlobalRef module) PY_RAISE;
+/// Call a callable object via pocketpy's calling convention.
+/// You need to prepare the stack using the following format:
+/// `callable, self/nil, arg1, arg2, ..., k1, v1, k2, v2, ...`.
+/// `argc` is the number of positional arguments excluding `self`.
+/// `kwargc` is the number of keyword arguments.
+/// The result will be set to `py_retval()`.
+/// The stack size will be reduced by `2 + argc + kwargc * 2`.
+PK_API bool py_vectorcall(uint16_t argc, uint16_t kwargc) PY_RAISE PY_RETURN;
+/// 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 if successful.
+PK_API bool py_call(py_Ref f, int argc, py_Ref argv) PY_RAISE PY_RETURN;
+/// Call a type to create a new instance.
+PK_API bool py_tpcall(py_Type type, int argc, py_Ref argv) PY_RAISE PY_RETURN;
+
+#ifndef NDEBUG
+/// Call a `py_CFunction` in a safe way.
+/// This function does extra checks to help you debug `py_CFunction`.
+PK_API bool py_callcfunc(py_CFunction f, int argc, py_Ref argv) PY_RAISE PY_RETURN;
+#else
+#define py_callcfunc(f, argc, argv) (f((argc), (argv)))
+#endif
 
 #define PY_CHECK_ARGC(n)                                                                           \
     if(argc != n) return TypeError("expected %d arguments, got %d", n, argc)
@@ -498,25 +517,13 @@ PK_API void py_bindmagic(py_Type type, py_Name name, py_CFunction f);
 #define py_arg(i) (&argv[i])
 #define py_assign(dst, src) *(dst) = *(src)
 
-/************* Python Equivalents *************/
-
-/// Python equivalent to `getattr(self, name)`.
-PK_API bool py_getattr(py_Ref self, py_Name name) PY_RAISE PY_RETURN;
-/// Python equivalent to `setattr(self, name, val)`.
-PK_API bool py_setattr(py_Ref self, py_Name name, py_Ref val) PY_RAISE;
-/// Python equivalent to `delattr(self, name)`.
-PK_API bool py_delattr(py_Ref self, py_Name name) PY_RAISE;
-/// Python equivalent to `self[key]`.
-PK_API bool py_getitem(py_Ref self, py_Ref key) PY_RAISE PY_RETURN;
-/// Python equivalent to `self[key] = val`.
-PK_API bool py_setitem(py_Ref self, py_Ref key, py_Ref val) PY_RAISE;
-/// Python equivalent to `del self[key]`.
-PK_API bool py_delitem(py_Ref self, py_Ref key) PY_RAISE;
-
 /// Perform a binary operation.
 /// The result will be set to `py_retval()`.
 /// The stack remains unchanged after the operation.
 PK_API bool py_binaryop(py_Ref lhs, py_Ref rhs, py_Name op, py_Name rop) PY_RAISE PY_RETURN;
+
+/************* Python Ops *************/
+
 /// lhs + rhs
 PK_API bool py_binaryadd(py_Ref lhs, py_Ref rhs) PY_RAISE PY_RETURN;
 /// lhs - rhs
@@ -543,68 +550,74 @@ PK_API bool py_binaryor(py_Ref lhs, py_Ref rhs) PY_RAISE PY_RETURN;
 PK_API bool py_binaryxor(py_Ref lhs, py_Ref rhs) PY_RAISE PY_RETURN;
 /// lhs @ rhs
 PK_API bool py_binarymatmul(py_Ref lhs, py_Ref rhs) PY_RAISE PY_RETURN;
+/// lhs == rhs
+PK_API bool py_eq(py_Ref lhs, py_Ref rhs) PY_RAISE PY_RETURN;
+/// lhs != rhs
+PK_API bool py_ne(py_Ref lhs, py_Ref rhs) PY_RAISE PY_RETURN;
+/// lhs < rhs
+PK_API bool py_lt(py_Ref lhs, py_Ref rhs) PY_RAISE PY_RETURN;
+/// lhs <= rhs
+PK_API bool py_le(py_Ref lhs, py_Ref rhs) PY_RAISE PY_RETURN;
+/// lhs > rhs
+PK_API bool py_gt(py_Ref lhs, py_Ref rhs) PY_RAISE PY_RETURN;
+/// lhs >= rhs
+PK_API bool py_ge(py_Ref lhs, py_Ref rhs) PY_RAISE PY_RETURN;
 
-/************* Stack Operations *************/
+/// Python equivalent to `lhs is rhs`.
+PK_API bool py_isidentical(py_Ref, py_Ref);
+/// Python equivalent to `bool(val)`.
+/// 1: true, 0: false, -1: error
+PK_API int py_bool(py_Ref val) PY_RAISE;
+/// Compare two objects.
+/// 1: lhs == rhs, 0: lhs != rhs, -1: error
+PK_API int py_equal(py_Ref lhs, py_Ref rhs) PY_RAISE;
+/// Compare two objects.
+/// 1: lhs < rhs, 0: lhs >= rhs, -1: error
+PK_API int py_less(py_Ref lhs, py_Ref rhs) PY_RAISE;
+/// Python equivalent to `callable(val)`.
+PK_API bool py_callable(py_Ref val);
+/// Get the hash value of the object.
+PK_API bool py_hash(py_Ref, py_i64* out) PY_RAISE;
+/// Get the iterator of the object.
+PK_API bool py_iter(py_Ref) PY_RAISE PY_RETURN;
+/// Get the next element from the iterator.
+/// 1: success, 0: StopIteration, -1: error
+PK_API int py_next(py_Ref) PY_RAISE PY_RETURN;
+/// Python equivalent to `str(val)`.
+PK_API bool py_str(py_Ref val) PY_RAISE PY_RETURN;
+/// Python equivalent to `repr(val)`.
+PK_API bool py_repr(py_Ref val) PY_RAISE PY_RETURN;
+/// Python equivalent to `len(val)`.
+PK_API bool py_len(py_Ref val) PY_RAISE PY_RETURN;
 
-/// Get the i-th object from the top of the stack.
-/// `i` should be negative, e.g. (-1) means TOS.
-PK_API py_StackRef py_peek(int i);
-/// Push the object to the stack.
-PK_API void py_push(py_Ref src);
-/// Push a `nil` object to the stack.
-PK_API void py_pushnil();
-/// Push a `None` object to the stack.
-PK_API void py_pushnone();
-/// Push a `py_Name` to the stack. This is used for keyword arguments.
-PK_API void py_pushname(py_Name name);
-/// Pop an object from the stack.
-PK_API void py_pop();
-/// Shrink the stack by n.
-PK_API void py_shrink(int n);
-/// Get a temporary variable from the stack.
-PK_API py_StackRef py_pushtmp();
-/// Get the unbound method of the object.
-/// Assume the object is located at the top of the stack.
-/// If return true:  `[self] -> [unbound, self]`.
-/// If return false: `[self] -> [self]` (no change).
-PK_API bool py_pushmethod(py_Name name);
-/// Call a callable object via pocketpy's calling convention.
-/// You need to prepare the stack using the following format:
-/// `callable, self/nil, arg1, arg2, ..., k1, v1, k2, v2, ...`.
-/// `argc` is the number of positional arguments excluding `self`.
-/// `kwargc` is the number of keyword arguments.
-/// The result will be set to `py_retval()`.
-/// The stack size will be reduced by `2 + argc + kwargc * 2`.
-PK_API bool py_vectorcall(uint16_t argc, uint16_t kwargc) PY_RAISE PY_RETURN;
-/// Evaluate an expression and push the result to the stack.
-/// This function is used for testing.
-PK_API bool py_pusheval(const char* expr, py_GlobalRef module) PY_RAISE;
+/// Python equivalent to `getattr(self, name)`.
+PK_API bool py_getattr(py_Ref self, py_Name name) PY_RAISE PY_RETURN;
+/// Python equivalent to `setattr(self, name, val)`.
+PK_API bool py_setattr(py_Ref self, py_Name name, py_Ref val) PY_RAISE;
+/// Python equivalent to `delattr(self, name)`.
+PK_API bool py_delattr(py_Ref self, py_Name name) PY_RAISE;
+/// Python equivalent to `self[key]`.
+PK_API bool py_getitem(py_Ref self, py_Ref key) PY_RAISE PY_RETURN;
+/// Python equivalent to `self[key] = val`.
+PK_API bool py_setitem(py_Ref self, py_Ref key, py_Ref val) PY_RAISE;
+/// Python equivalent to `del self[key]`.
+PK_API bool py_delitem(py_Ref self, py_Ref key) PY_RAISE;
 
-/************* Modules *************/
+/************* Module System *************/
 
-/// Create a new module.
-PK_API py_GlobalRef py_newmodule(const char* path);
 /// Get a module by path.
 PK_API py_GlobalRef py_getmodule(const char* path);
+/// Create a new module.
+PK_API py_GlobalRef py_newmodule(const char* path);
 /// Reload an existing module.
 PK_API bool py_importlib_reload(py_Ref module) PY_RAISE PY_RETURN;
-
 /// Import a module.
 /// The result will be set to `py_retval()`.
 /// -1: error, 0: not found, 1: success
 PK_API int py_import(const char* path) PY_RAISE PY_RETURN;
 
-/************* Errors *************/
+/************* PyException *************/
 
-/// Raise an exception by type and message. Always return false.
-PK_API bool py_exception(py_Type type, const char* fmt, ...) PY_RAISE;
-/// Raise an exception object. Always return false.
-PK_API bool py_raise(py_Ref) PY_RAISE;
-/// Print the unhandled exception.
-PK_API void py_printexc();
-/// Format the unhandled exception and return a null-terminated string.
-/// The returned string should be freed by the caller.
-PK_API char* py_formatexc();
 /// Check if there is an unhandled exception.
 PK_API bool py_checkexc();
 /// Check if the unhandled exception is an instance of the given type.
@@ -613,6 +626,15 @@ PK_API bool py_matchexc(py_Type type) PY_RETURN;
 /// Clear the unhandled exception.
 /// @param p0 the unwinding point. Use `NULL` if not needed.
 PK_API void py_clearexc(py_StackRef p0);
+/// Print the unhandled exception.
+PK_API void py_printexc();
+/// Format the unhandled exception and return a null-terminated string.
+/// The returned string should be freed by the caller.
+PK_API char* py_formatexc();
+/// Raise an exception by type and message. Always return false.
+PK_API bool py_exception(py_Type type, const char* fmt, ...) PY_RAISE;
+/// Raise an exception object. Always return false.
+PK_API bool py_raise(py_Ref) PY_RAISE;
 
 #define NameError(n) py_exception(tp_NameError, "name '%n' is not defined", (n))
 #define TypeError(...) py_exception(tp_TypeError, __VA_ARGS__)
@@ -630,100 +652,40 @@ PK_API void py_clearexc(py_StackRef p0);
                  "cannot access local variable '%n' where it is not associated with a value",      \
                  (n))
 
-PK_API bool StopIteration() PY_RAISE;
 PK_API bool KeyError(py_Ref key) PY_RAISE;
+PK_API bool StopIteration() PY_RAISE;
 
-/************* Operators *************/
-
-/// Python equivalent to `bool(val)`.
-/// 1: true, 0: false, -1: error
-PK_API int py_bool(py_Ref val) PY_RAISE;
-/// Compare two objects.
-/// 1: lhs == rhs, 0: lhs != rhs, -1: error
-PK_API int py_equal(py_Ref lhs, py_Ref rhs) PY_RAISE;
-/// Compare two objects.
-/// 1: lhs < rhs, 0: lhs >= rhs, -1: error
-PK_API int py_less(py_Ref lhs, py_Ref rhs) PY_RAISE;
-
-/// lhs == rhs
-PK_API bool py_eq(py_Ref lhs, py_Ref rhs) PY_RAISE PY_RETURN;
-/// lhs != rhs
-PK_API bool py_ne(py_Ref lhs, py_Ref rhs) PY_RAISE PY_RETURN;
-/// lhs < rhs
-PK_API bool py_lt(py_Ref lhs, py_Ref rhs) PY_RAISE PY_RETURN;
-/// lhs <= rhs
-PK_API bool py_le(py_Ref lhs, py_Ref rhs) PY_RAISE PY_RETURN;
-/// lhs > rhs
-PK_API bool py_gt(py_Ref lhs, py_Ref rhs) PY_RAISE PY_RETURN;
-/// lhs >= rhs
-PK_API bool py_ge(py_Ref lhs, py_Ref rhs) PY_RAISE PY_RETURN;
-
-/// Python equivalent to `callable(val)`.
-PK_API bool py_callable(py_Ref val);
-/// Get the hash value of the object.
-PK_API bool py_hash(py_Ref, py_i64* out) PY_RAISE;
-/// Get the iterator of the object.
-PK_API bool py_iter(py_Ref) PY_RAISE PY_RETURN;
-/// Get the next element from the iterator.
-/// 1: success, 0: StopIteration, -1: error
-PK_API int py_next(py_Ref) PY_RAISE PY_RETURN;
-/// Python equivalent to `lhs is rhs`.
-PK_API bool py_isidentical(py_Ref, py_Ref);
-/// 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 if successful.
-PK_API bool py_call(py_Ref f, int argc, py_Ref argv) PY_RAISE PY_RETURN;
-
-#ifndef NDEBUG
-/// Call a `py_CFunction` in a safe way.
-/// This function does extra checks to help you debug `py_CFunction`.
-PK_API bool py_callcfunc(py_CFunction f, int argc, py_Ref argv) PY_RAISE PY_RETURN;
-#else
-#define py_callcfunc(f, argc, argv) (f((argc), (argv)))
-#endif
-
-/// Python equivalent to `str(val)`.
-PK_API bool py_str(py_Ref val) PY_RAISE PY_RETURN;
-/// Python equivalent to `repr(val)`.
-PK_API bool py_repr(py_Ref val) PY_RAISE PY_RETURN;
-/// Python equivalent to `len(val)`.
-PK_API bool py_len(py_Ref val) PY_RAISE PY_RETURN;
-/// Python equivalent to `json.dumps(val)`.
-PK_API bool py_json_dumps(py_Ref val, int indent) PY_RAISE PY_RETURN;
-/// Python equivalent to `json.loads(val)`.
-PK_API bool py_json_loads(const char* source) PY_RAISE PY_RETURN;
-/// Python equivalent to `pickle.dumps(val)`.
-PK_API bool py_pickle_dumps(py_Ref val) PY_RAISE PY_RETURN;
-/// Python equivalent to `pickle.loads(val)`.
-PK_API bool py_pickle_loads(const unsigned char* data, int size) PY_RAISE PY_RETURN;
-
-/************* Profiler *************/
-PK_API void py_profiler_begin();
-PK_API void py_profiler_end();
-PK_API void py_profiler_reset();
-PK_API char* py_profiler_report();
+/************* Debugger *************/
 
-/************* DAP *************/
 #if PK_ENABLE_OS
 PK_API void py_debugger_waitforattach(const char* hostname, unsigned short port);
 PK_API bool py_debugger_isattached();
 PK_API void py_debugger_exceptionbreakpoint(py_Ref exc);
-PK_API void py_debugger_exit(int exitCode);
+PK_API void py_debugger_exit(int code);
 #else
 #define py_debugger_waitforattach(hostname, port)
 #define py_debugger_isattached() (false)
 #define py_debugger_exceptionbreakpoint(exc)
-#define py_debugger_exit(exitCode)
+#define py_debugger_exit(code)
 #endif
 
-/************* Unchecked Functions *************/
+/************* PyTuple *************/
 
+/// Create a `tuple` with `n` UNINITIALIZED elements.
+/// You should initialize all elements before using it.
+PK_API py_ObjectRef py_newtuple(py_OutRef, int n);
 PK_API py_ObjectRef py_tuple_data(py_Ref self);
 PK_API py_ObjectRef py_tuple_getitem(py_Ref self, int i);
 PK_API void py_tuple_setitem(py_Ref self, int i, py_Ref val);
 PK_API int py_tuple_len(py_Ref self);
 
+/************* PyList *************/
+
+/// Create an empty `list`.
+PK_API void py_newlist(py_OutRef);
+/// Create a `list` with `n` UNINITIALIZED elements.
+/// You should initialize all elements before using it.
+PK_API void py_newlistn(py_OutRef, int n);
 PK_API py_ItemRef py_list_data(py_Ref self);
 PK_API py_ItemRef py_list_getitem(py_Ref self, int i);
 PK_API void py_list_setitem(py_Ref self, int i, py_Ref val);
@@ -735,6 +697,10 @@ PK_API py_ItemRef py_list_emplace(py_Ref self);
 PK_API void py_list_clear(py_Ref self);
 PK_API void py_list_insert(py_Ref self, int i, py_Ref val);
 
+/************* PyDict *************/
+
+/// Create an empty `dict`.
+PK_API void py_newdict(py_OutRef);
 /// -1: error, 0: not found, 1: found
 PK_API int py_dict_getitem(py_Ref self, py_Ref key) PY_RAISE PY_RETURN;
 /// true: success, false: error
@@ -761,6 +727,14 @@ PK_API bool
 /// noexcept
 PK_API int py_dict_len(py_Ref self);
 
+/************* PySlice *************/
+
+/// Create an UNINITIALIZED `slice` object.
+/// You should use `py_setslot()` to set `start`, `stop`, and `step`.
+PK_API py_ObjectRef py_newslice(py_OutRef);
+/// Create a `slice` object from 3 integers.
+PK_API void py_newsliceint(py_OutRef out, py_i64 start, py_i64 stop, py_i64 step);
+
 /************* random module *************/
 PK_API void py_newRandom(py_OutRef out);
 PK_API void py_Random_seed(py_Ref self, py_i64 seed);
@@ -789,6 +763,32 @@ PK_API c11_vec3i py_tovec3i(py_Ref self);
 PK_API c11_mat3x3* py_tomat3x3(py_Ref self);
 PK_API c11_color32 py_tocolor32(py_Ref self);
 
+/************* json module *************/
+/// Python equivalent to `json.dumps(val)`.
+PK_API bool py_json_dumps(py_Ref val, int indent) PY_RAISE PY_RETURN;
+/// Python equivalent to `json.loads(val)`.
+PK_API bool py_json_loads(const char* source) PY_RAISE PY_RETURN;
+
+/************* pickle module *************/
+/// Python equivalent to `pickle.dumps(val)`.
+PK_API bool py_pickle_dumps(py_Ref val) PY_RAISE PY_RETURN;
+/// Python equivalent to `pickle.loads(val)`.
+PK_API bool py_pickle_loads(const unsigned char* data, int size) PY_RAISE PY_RETURN;
+
+/************* pkpy module *************/
+/// Begin the watchdog with `timeout` in milliseconds.
+/// `PK_ENABLE_WATCHDOG` must be defined to `1` to use this feature.
+/// You need to call `py_watchdog_end()` later.
+/// If `timeout` is reached, `TimeoutError` will be raised.
+PK_API void py_watchdog_begin(py_i64 timeout);
+/// Reset the watchdog.
+PK_API void py_watchdog_end();
+
+PK_API void py_profiler_begin();
+PK_API void py_profiler_end();
+PK_API void py_profiler_reset();
+PK_API char* py_profiler_report();
+
 /************* Others *************/
 
 /// An utility function to read a line from stdin for REPL.

+ 0 - 0
src/public/py_array.c → src/bindings/py_array.c


+ 0 - 0
src/public/py_mappingproxy.c → src/bindings/py_mappingproxy.c


+ 0 - 0
src/public/py_method.c → src/bindings/py_method.c


+ 0 - 0
src/public/py_number.c → src/bindings/py_number.c


+ 0 - 0
src/public/py_object.c → src/bindings/py_object.c


+ 0 - 0
src/public/py_property.c → src/bindings/py_property.c


+ 0 - 0
src/public/py_range.c → src/bindings/py_range.c


+ 0 - 90
src/public/py_str.c → src/bindings/py_str.c

@@ -1,59 +1,10 @@
 #include "pocketpy/common/str.h"
 #include "pocketpy/pocketpy.h"
 
-#include "pocketpy/common/utils.h"
 #include "pocketpy/objects/object.h"
 #include "pocketpy/interpreter/vm.h"
 #include "pocketpy/common/sstream.h"
 
-void py_newstr(py_OutRef out, const char* data) { py_newstrv(out, (c11_sv){data, strlen(data)}); }
-
-char* py_newstrn(py_OutRef out, int size) {
-    if(size < 16) {
-        out->type = tp_str;
-        out->is_ptr = false;
-        c11_string* ud = (c11_string*)(&out->extra);
-        c11_string__ctor3(ud, size);
-        return ud->data;
-    }
-    ManagedHeap* heap = &pk_current_vm->heap;
-    int total_size = sizeof(c11_string) + size + 1;
-    PyObject* obj = ManagedHeap__gcnew(heap, tp_str, 0, total_size);
-    c11_string* ud = PyObject__userdata(obj);
-    c11_string__ctor3(ud, size);
-    out->type = tp_str;
-    out->is_ptr = true;
-    out->_obj = obj;
-    return ud->data;
-}
-
-void py_newstrv(py_OutRef out, c11_sv sv) {
-    char* data = py_newstrn(out, sv.size);
-    memcpy(data, sv.data, sv.size);
-}
-
-void py_newfstr(py_OutRef out, const char* fmt, ...) {
-    c11_sbuf buf;
-    c11_sbuf__ctor(&buf);
-    va_list args;
-    va_start(args, fmt);
-    pk_vsprintf(&buf, fmt, args);
-    va_end(args);
-    c11_sbuf__py_submit(&buf, out);
-}
-
-unsigned char* py_newbytes(py_OutRef out, int size) {
-    ManagedHeap* heap = &pk_current_vm->heap;
-    // 4 bytes size + data
-    PyObject* obj = ManagedHeap__gcnew(heap, tp_bytes, 0, sizeof(c11_bytes) + size);
-    c11_bytes* ud = PyObject__userdata(obj);
-    ud->size = size;
-    out->type = tp_bytes;
-    out->is_ptr = true;
-    out->_obj = obj;
-    return ud->data;
-}
-
 c11_string* pk_tostr(py_Ref self) {
     assert(self->type == tp_str);
     if(!self->is_ptr) {
@@ -63,33 +14,6 @@ c11_string* pk_tostr(py_Ref self) {
     }
 }
 
-const char* py_tostr(py_Ref self) { return pk_tostr(self)->data; }
-
-const char* py_tostrn(py_Ref self, int* size) {
-    c11_string* ud = pk_tostr(self);
-    *size = ud->size;
-    return ud->data;
-}
-
-c11_sv py_tosv(py_Ref self) {
-    c11_string* ud = pk_tostr(self);
-    return c11_string__sv(ud);
-}
-
-unsigned char* py_tobytes(py_Ref self, int* size) {
-    assert(self->type == tp_bytes);
-    c11_bytes* ud = PyObject__userdata(self->_obj);
-    *size = ud->size;
-    return ud->data;
-}
-
-void py_bytes_resize(py_Ref self, int size) {
-    assert(self->type == tp_bytes);
-    c11_bytes* ud = PyObject__userdata(self->_obj);
-    if(size > ud->size) c11__abort("bytes can only be resized down: %d > %d", ud->size, size);
-    ud->size = size;
-}
-
 ////////////////////////////////
 static bool str__new__(int argc, py_Ref argv) {
     assert(argc >= 1);
@@ -673,18 +597,4 @@ py_Type pk_bytes__register() {
     return type;
 }
 
-bool py_str(py_Ref val) {
-    if(val->type == tp_str) {
-        py_assign(py_retval(), val);
-        return true;
-    }
-    py_Ref tmp = py_tpfindmagic(val->type, __str__);
-    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); }
-
 #undef DEF_STR_CMP_OP

+ 4 - 66
src/interpreter/ceval.c

@@ -586,10 +586,10 @@ __NEXT_STEP:
         case OP_BUILD_SLICE: {
             // [start, stop, step]
             py_TValue tmp;
-            py_newslice(&tmp);
-            py_setslot(&tmp, 0, THIRD());
-            py_setslot(&tmp, 1, SECOND());
-            py_setslot(&tmp, 2, TOP());
+            py_ObjectRef slots = py_newslice(&tmp);
+            slots[0] = *THIRD();
+            slots[1] = *SECOND();
+            slots[2] = *TOP();
             STACK_SHRINK(3);
             PUSH(&tmp);
             DISPATCH();
@@ -1298,57 +1298,6 @@ bool pk_stack_binaryop(VM* self, py_Name op, py_Name rop) {
                      rhs_t);
 }
 
-bool py_binaryop(py_Ref lhs, py_Ref rhs, py_Name op, py_Name rop) {
-    VM* self = pk_current_vm;
-    PUSH(lhs);
-    PUSH(rhs);
-    bool ok = pk_stack_binaryop(self, op, rop);
-    STACK_SHRINK(2);
-    return ok;
-}
-
-bool py_binaryadd(py_Ref lhs, py_Ref rhs) { return py_binaryop(lhs, rhs, __add__, __radd__); }
-
-bool py_binarysub(py_Ref lhs, py_Ref rhs) { return py_binaryop(lhs, rhs, __sub__, __rsub__); }
-
-bool py_binarymul(py_Ref lhs, py_Ref rhs) { return py_binaryop(lhs, rhs, __mul__, __rmul__); }
-
-bool py_binarytruediv(py_Ref lhs, py_Ref rhs) {
-    return py_binaryop(lhs, rhs, __truediv__, __rtruediv__);
-}
-
-bool py_binaryfloordiv(py_Ref lhs, py_Ref rhs) {
-    return py_binaryop(lhs, rhs, __floordiv__, __rfloordiv__);
-}
-
-bool py_binarymod(py_Ref lhs, py_Ref rhs) { return py_binaryop(lhs, rhs, __mod__, __rmod__); }
-
-bool py_binarypow(py_Ref lhs, py_Ref rhs) { return py_binaryop(lhs, rhs, __pow__, __rpow__); }
-
-bool py_binarylshift(py_Ref lhs, py_Ref rhs) { return py_binaryop(lhs, rhs, __lshift__, 0); }
-
-bool py_binaryrshift(py_Ref lhs, py_Ref rhs) { return py_binaryop(lhs, rhs, __rshift__, 0); }
-
-bool py_binaryand(py_Ref lhs, py_Ref rhs) { return py_binaryop(lhs, rhs, __and__, 0); }
-
-bool py_binaryor(py_Ref lhs, py_Ref rhs) { return py_binaryop(lhs, rhs, __or__, 0); }
-
-bool py_binaryxor(py_Ref lhs, py_Ref rhs) { return py_binaryop(lhs, rhs, __xor__, 0); }
-
-bool py_binarymatmul(py_Ref lhs, py_Ref rhs) { return py_binaryop(lhs, rhs, __matmul__, 0); }
-
-bool py_eq(py_Ref lhs, py_Ref rhs) { return py_binaryop(lhs, rhs, __eq__, __eq__); }
-
-bool py_ne(py_Ref lhs, py_Ref rhs) { return py_binaryop(lhs, rhs, __ne__, __ne__); }
-
-bool py_lt(py_Ref lhs, py_Ref rhs) { return py_binaryop(lhs, rhs, __lt__, __gt__); }
-
-bool py_le(py_Ref lhs, py_Ref rhs) { return py_binaryop(lhs, rhs, __le__, __ge__); }
-
-bool py_gt(py_Ref lhs, py_Ref rhs) { return py_binaryop(lhs, rhs, __gt__, __lt__); }
-
-bool py_ge(py_Ref lhs, py_Ref rhs) { return py_binaryop(lhs, rhs, __ge__, __le__); }
-
 static bool stack_format_object(VM* self, c11_sv spec) {
     // format TOS via `spec` inplace
     // spec: '!r:.2f', '.2f'
@@ -1514,14 +1463,3 @@ static bool stack_format_object(VM* self, c11_sv spec) {
 #undef INSERT_THIRD
 #undef vectorcall_opcall
 #undef RESET_CO_CACHE
-
-void py_sys_settrace(py_TraceFunc func, bool reset) {
-    TraceInfo* info = &pk_current_vm->trace_info;
-    info->func = func;
-    if(!reset) return;
-    if(info->prev_loc.src) {
-        PK_DECREF(info->prev_loc.src);
-        info->prev_loc.src = NULL;
-    }
-    info->prev_loc.lineno = -1;
-}

+ 0 - 41
src/interpreter/frame.c

@@ -162,44 +162,3 @@ SourceLocation Frame__source_location(py_Frame* self) {
     loc.src = self->co->src;
     return loc;
 }
-
-const char* py_Frame_sourceloc(py_Frame* self, int* lineno) {
-    SourceLocation loc = Frame__source_location(self);
-    *lineno = loc.lineno;
-    return loc.src->filename->data;
-}
-
-void py_Frame_newglobals(py_Frame* frame, py_OutRef out) {
-    if(!frame) {
-        pk_mappingproxy__namedict(out, pk_current_vm->main);
-        return;
-    }
-    if(frame->globals->type == tp_module) {
-        pk_mappingproxy__namedict(out, frame->globals);
-    } else {
-        *out = *frame->globals;  // dict
-    }
-}
-
-void py_Frame_newlocals(py_Frame* frame, py_OutRef out) {
-    if(!frame) {
-        py_newdict(out);
-        return;
-    }
-    if(frame->is_locals_special) {
-        switch(frame->locals->type) {
-            case tp_locals: frame = frame->locals->_ptr; break;
-            case tp_dict: *out = *frame->locals; return;
-            case tp_nil: py_newdict(out); return;
-            default: c11__unreachable();
-        }
-    }
-    FastLocals__to_dict(frame->locals, frame->co);
-    py_assign(out, py_retval());
-}
-
-py_StackRef py_Frame_function(py_Frame* self) {
-    if(self->is_locals_special) return NULL;
-    assert(self->p0->type == tp_function);
-    return self->p0;
-}

+ 2 - 61
src/interpreter/typeinfo.c

@@ -11,45 +11,11 @@ py_ItemRef pk_tpfindname(py_TypeInfo* ti, py_Name name) {
     return NULL;
 }
 
-PK_INLINE py_ItemRef py_tpfindname(py_Type type, py_Name name) {
-    py_TypeInfo* ti = pk_typeinfo(type);
-    return pk_tpfindname(ti, name);
-}
-
-PK_INLINE py_Ref py_tpfindmagic(py_Type t, py_Name name) {
-    // assert(py_ismagicname(name));
-    return py_tpfindname(t, name);
-}
-
-PK_INLINE py_Type py_tpbase(py_Type t) {
-    assert(t);
-    py_TypeInfo* ti = pk_typeinfo(t);
-    return ti->base;
-}
-
-PK_DEPRECATED py_Ref py_tpgetmagic(py_Type type, py_Name name) {
-    // assert(py_ismagicname(name));
-    py_TypeInfo* ti = pk_typeinfo(type);
-    py_Ref retval = py_getdict(&ti->self, name);
-    return retval != NULL ? retval : py_NIL();
-}
-
-py_Ref py_tpobject(py_Type type) {
-    assert(type);
-    return &pk_typeinfo(type)->self;
-}
-
-const char* py_tpname(py_Type type) {
-    if(!type) return "nil";
-    py_Name name = pk_typeinfo(type)->name;
-    return py_name2str(name);
-}
-
 PK_INLINE py_TypeInfo* pk_typeinfo(py_Type type) {
 #ifndef NDEBUG
     int length = pk_current_vm->types.length;
-    if(type < 0 || type >= length) {
-        c11__abort("type index %d is out of bounds [0, %d)", type, length);
+    if(type <= 0 || type >= length) {
+        c11__abort("type index %d is out of bounds (0, %d)", type, length);
     }
 #endif
     return c11__getitem(TypePointer, &pk_current_vm->types, type).ti;
@@ -150,28 +116,3 @@ py_Type pk_newtypewithmode(py_Name name,
     return pk_newtype(py_name2str(name), base, module, dtor, is_python, is_final);
 }
 
-py_Type py_newtype(const char* name, py_Type base, const py_GlobalRef module, void (*dtor)(void*)) {
-    if(strlen(name) == 0) c11__abort("type name cannot be empty");
-    py_Type type = pk_newtype(name, base, module, dtor, false, false);
-    if(module) py_setdict(module, py_name(name), py_tpobject(type));
-    return type;
-}
-
-void py_tpsetfinal(py_Type type) {
-    assert(type);
-    py_TypeInfo* ti = pk_typeinfo(type);
-    ti->is_final = true;
-}
-
-void py_tphookattributes(py_Type type,
-                         bool (*getattribute)(py_Ref self, py_Name name),
-                         bool (*setattribute)(py_Ref self, py_Name name, py_Ref val),
-                         bool (*delattribute)(py_Ref self, py_Name name),
-                         bool (*getunboundmethod)(py_Ref self, py_Name name)) {
-    assert(type);
-    py_TypeInfo* ti = pk_typeinfo(type);
-    ti->getattribute = getattribute;
-    ti->setattribute = setattribute;
-    ti->delattribute = delattribute;
-    ti->getunboundmethod = getunboundmethod;
-}

+ 0 - 276
src/public/modules.c → src/modules/builtins.c

@@ -8,198 +8,8 @@
 #include "pocketpy/interpreter/vm.h"
 #include "pocketpy/common/_generated.h"
 
-#include <ctype.h>
 #include <math.h>
 
-py_Ref py_getmodule(const char* path) {
-    VM* vm = pk_current_vm;
-    return BinTree__try_get(&vm->modules, (void*)path);
-}
-
-py_Ref py_getbuiltin(py_Name name) { return py_getdict(pk_current_vm->builtins, name); }
-
-py_Ref py_getglobal(py_Name name) { return py_getdict(pk_current_vm->main, name); }
-
-void py_setglobal(py_Name name, py_Ref val) { py_setdict(pk_current_vm->main, name, val); }
-
-static void py_ModuleInfo__dtor(py_ModuleInfo* mi) {
-    c11_string__delete(mi->name);
-    c11_string__delete(mi->package);
-    c11_string__delete(mi->path);
-}
-
-py_Type pk_module__register() {
-    py_Type type = pk_newtype("module", tp_object, NULL, (py_Dtor)py_ModuleInfo__dtor, false, true);
-    return type;
-}
-
-py_Ref py_newmodule(const char* path) {
-    int path_len = strlen(path);
-    if(path_len > PK_MAX_MODULE_PATH_LEN) c11__abort("module path too long: %s", path);
-    if(path_len == 0) c11__abort("module path cannot be empty");
-
-    py_ModuleInfo* mi = py_newobject(py_retval(), tp_module, -1, sizeof(py_ModuleInfo));
-
-    int last_dot = c11_sv__rindex((c11_sv){path, path_len}, '.');
-    if(last_dot == -1) {
-        mi->name = c11_string__new(path);
-        mi->package = c11_string__new("");
-    } else {
-        const char* start = path + last_dot + 1;
-        mi->name = c11_string__new(start);
-        mi->package = c11_string__new2(path, last_dot);
-    }
-
-    mi->path = c11_string__new(path);
-    path = mi->path->data;
-
-    // we do not allow override in order to avoid memory leak
-    // it is because Module objects are not garbage collected
-    bool exists = BinTree__contains(&pk_current_vm->modules, (void*)path);
-    if(exists) c11__abort("module '%s' already exists", path);
-
-    BinTree__set(&pk_current_vm->modules, (void*)path, py_retval());
-    py_GlobalRef retval = py_getmodule(path);
-    mi->self = retval;
-
-    // setup __name__
-    py_newstrv(py_emplacedict(retval, __name__), c11_string__sv(mi->name));
-    // setup __package__
-    py_newstrv(py_emplacedict(retval, __package__), c11_string__sv(mi->package));
-    // setup __path__
-    py_newstrv(py_emplacedict(retval, __path__), c11_string__sv(mi->path));
-    return retval;
-}
-
-int load_module_from_dll_desktop_only(const char* path) PY_RAISE PY_RETURN;
-
-int py_import(const char* path_cstr) {
-    VM* vm = pk_current_vm;
-    c11_sv path = {path_cstr, strlen(path_cstr)};
-    if(path.size == 0) return ValueError("empty module name");
-
-    if(path.data[0] == '.') {
-        // try relative import
-        int dot_count = 1;
-        while(dot_count < path.size && path.data[dot_count] == '.')
-            dot_count++;
-
-        c11_sv top_filename = c11_string__sv(vm->top_frame->co->src->filename);
-        int is_init = c11_sv__endswith(top_filename, (c11_sv){"__init__.py", 11});
-
-        py_ModuleInfo* mi = py_touserdata(vm->top_frame->module);
-        c11_sv package_sv = c11_string__sv(mi->path);
-        if(package_sv.size == 0) {
-            return ImportError("attempted relative import with no known parent package");
-        }
-
-        c11_vector /* T=c11_sv */ cpnts = c11_sv__split(package_sv, '.');
-        for(int i = is_init; i < dot_count; i++) {
-            if(cpnts.length == 0)
-                return ImportError("attempted relative import beyond top-level package");
-            c11_vector__pop(&cpnts);
-        }
-
-        if(dot_count < path.size) {
-            c11_sv last_cpnt = c11_sv__slice(path, dot_count);
-            c11_vector__push(c11_sv, &cpnts, last_cpnt);
-        }
-
-        // join cpnts
-        c11_sbuf buf;
-        c11_sbuf__ctor(&buf);
-        for(int i = 0; i < cpnts.length; i++) {
-            if(i > 0) c11_sbuf__write_char(&buf, '.');
-            c11_sbuf__write_sv(&buf, c11__getitem(c11_sv, &cpnts, i));
-        }
-
-        c11_vector__dtor(&cpnts);
-        c11_string* new_path = c11_sbuf__submit(&buf);
-        int res = py_import(new_path->data);
-        c11_string__delete(new_path);
-        return res;
-    }
-
-    assert(path.data[0] != '.' && path.data[path.size - 1] != '.');
-
-    // check existing module
-    py_GlobalRef ext_mod = py_getmodule(path.data);
-    if(ext_mod) {
-        py_assign(py_retval(), ext_mod);
-        return true;
-    }
-
-    if(vm->callbacks.lazyimport) {
-        py_GlobalRef lazymod = vm->callbacks.lazyimport(path_cstr);
-        if(lazymod) {
-            c11__rtassert(py_istype(lazymod, tp_module));
-            py_assign(py_retval(), lazymod);
-            return 1;
-        }
-    }
-
-    // try import
-    c11_string* slashed_path = c11_sv__replace(path, '.', PK_PLATFORM_SEP);
-    c11_string* filename = c11_string__new3("%s.py", slashed_path->data);
-
-    bool need_free = true;
-    const char* data = load_kPythonLib(path_cstr);
-    if(data != NULL) {
-        need_free = false;
-        goto __SUCCESS;
-    }
-
-    data = vm->callbacks.importfile(filename->data);
-    if(data != NULL) goto __SUCCESS;
-
-    c11_string__delete(filename);
-    filename = c11_string__new3("%s%c__init__.py", slashed_path->data, PK_PLATFORM_SEP);
-    data = vm->callbacks.importfile(filename->data);
-    if(data != NULL) goto __SUCCESS;
-
-    c11_string__delete(filename);
-    c11_string__delete(slashed_path);
-    // not found
-    return load_module_from_dll_desktop_only(path_cstr);
-
-__SUCCESS:
-    do {
-    } while(0);
-    py_GlobalRef mod = py_newmodule(path_cstr);
-    bool ok = py_exec((const char*)data, filename->data, EXEC_MODE, mod);
-    py_assign(py_retval(), mod);
-
-    c11_string__delete(filename);
-    c11_string__delete(slashed_path);
-    if(need_free) PK_FREE((void*)data);
-    return ok ? 1 : -1;
-}
-
-bool py_importlib_reload(py_Ref module) {
-    VM* vm = pk_current_vm;
-    py_ModuleInfo* mi = py_touserdata(module);
-    // We should ensure that the module is its original py_GlobalRef
-    module = mi->self;
-    c11_sv path = c11_string__sv(mi->path);
-    c11_string* slashed_path = c11_sv__replace(path, '.', PK_PLATFORM_SEP);
-    c11_string* filename = c11_string__new3("%s.py", slashed_path->data);
-    char* data = vm->callbacks.importfile(filename->data);
-    if(data == NULL) {
-        c11_string__delete(filename);
-        filename = c11_string__new3("%s%c__init__.py", slashed_path->data, PK_PLATFORM_SEP);
-        data = vm->callbacks.importfile(filename->data);
-    }
-    c11_string__delete(slashed_path);
-    if(data == NULL) return ImportError("module '%v' not found", path);
-    // py_cleardict(module); BUG: removing old classes will cause RELOAD_MODE to fail
-    bool ok = py_exec(data, filename->data, RELOAD_MODE, module);
-    c11_string__delete(filename);
-    PK_FREE(data);
-    py_assign(py_retval(), module);
-    return ok;
-}
-
-//////////////////////////
 
 static bool builtins_exit(int argc, py_Ref argv) {
     int code = 0;
@@ -399,18 +209,6 @@ static bool builtins_issubclass(int argc, py_Ref argv) {
     return true;
 }
 
-bool py_callable(py_Ref val) {
-    switch(val->type) {
-        case tp_nativefunc: return true;
-        case tp_function: return true;
-        case tp_type: return true;
-        case tp_boundmethod: return true;
-        case tp_staticmethod: return true;
-        case tp_classmethod: return true;
-        default: return py_tpfindmagic(val->type, __call__);
-    }
-}
-
 static bool builtins_callable(int argc, py_Ref argv) {
     PY_CHECK_ARGC(1);
     bool res = py_callable(py_arg(0));
@@ -519,16 +317,6 @@ static bool builtins_locals(int argc, py_Ref argv) {
     return true;
 }
 
-void py_newglobals(py_OutRef out) {
-    py_Frame* frame = pk_current_vm->top_frame;
-    py_Frame_newglobals(frame, out);
-}
-
-void py_newlocals(py_OutRef out) {
-    py_Frame* frame = pk_current_vm->top_frame;
-    py_Frame_newlocals(frame, out);
-}
-
 static void pk_push_special_locals() {
     py_Frame* frame = pk_current_vm->top_frame;
     if(!frame) {
@@ -622,70 +410,6 @@ static bool builtins_eval(int argc, py_Ref argv) {
     return _builtins_execdyn("eval", argc, argv, EVAL_MODE);
 }
 
-static bool
-    pk_smartexec(const char* source, py_Ref module, enum py_CompileMode mode, va_list args) {
-    if(module == NULL) module = pk_current_vm->main;
-    pk_mappingproxy__namedict(py_pushtmp(), module);  // globals
-    py_newdict(py_pushtmp());                         // locals
-    bool ok = py_compile(source, "<string>", mode, true);
-    if(!ok) return false;
-    py_push(py_retval());
-    // [globals, locals, code]
-    CodeObject* co = py_touserdata(py_peek(-1));
-    py_StackRef locals = py_peek(-2);
-    int max_index = -1;
-    c11__foreach(Bytecode, &co->codes, bc) {
-        if(bc->op == OP_LOAD_NAME) {
-            c11_sv name = py_name2sv(c11__getitem(py_Name, &co->names, bc->arg));
-            if(name.data[0] != '_') continue;
-            int index;
-            if(name.size == 1) {
-                index = 0;
-            } else if(name.size == 2 && isdigit(name.data[1])) {
-                index = name.data[1] - '0';
-            } else {
-                continue;
-            }
-            max_index = c11__max(max_index, index);
-        }
-    }
-
-    if(max_index == -1) return ValueError("no placeholder found in the source");
-
-    for(int i = 0; i <= max_index; i++) {
-        py_Ref val = va_arg(args, py_Ref);
-        char buf[3];
-        buf[0] = '_';
-        buf[1] = '0' + i;
-        buf[2] = '\0';
-        py_dict_setitem_by_str(locals, buf, val);
-        if(i == 0) {
-            // _ => _0
-            py_dict_setitem_by_str(locals, "_", val);
-        }
-    }
-    ok = pk_execdyn(co, module, py_peek(-3), locals);
-    if(!ok) return false;
-    py_shrink(3);
-    return true;
-}
-
-bool py_smartexec(const char* source, py_Ref module, ...) {
-    va_list args;
-    va_start(args, module);
-    bool ok = pk_smartexec(source, module, EXEC_MODE, args);
-    va_end(args);
-    return ok;
-}
-
-bool py_smarteval(const char* source, py_Ref module, ...) {
-    va_list args;
-    va_start(args, module);
-    bool ok = pk_smartexec(source, module, EVAL_MODE, args);
-    va_end(args);
-    return ok;
-}
-
 static bool builtins_compile(int argc, py_Ref argv) {
     PY_CHECK_ARGC(3);
     for(int i = 0; i < 3; i++) {

+ 0 - 5
src/modules/gc.c

@@ -40,8 +40,3 @@ void pk__add_module_gc() {
     py_bindfunc(mod, "disable", gc_disable);
     py_bindfunc(mod, "isenabled", gc_isenabled);
 }
-
-int py_gc_collect() {
-    ManagedHeap* heap = &pk_current_vm->heap;
-    return ManagedHeap__collect(heap);
-}

+ 0 - 6
src/modules/json.c

@@ -189,9 +189,3 @@ bool py_json_loads(const char* source) {
     return py_exec(source, "<json>", EVAL_MODE, mod);
 }
 
-bool py_pusheval(const char* expr, py_GlobalRef module) {
-    bool ok = py_exec(expr, "<string>", EVAL_MODE, module);
-    if(!ok) return false;
-    py_push(py_retval());
-    return true;
-}

+ 61 - 0
src/public/Bindings.c

@@ -0,0 +1,61 @@
+#include "pocketpy/interpreter/vm.h"
+
+void py_bind(py_Ref obj, const char* sig, py_CFunction f) {
+    py_Ref tmp = py_pushtmp();
+    py_Name name = py_newfunction(tmp, sig, f, NULL, 0);
+    py_setdict(obj, name, tmp);
+    py_pop();
+}
+
+void py_bindmethod(py_Type type, const char* name, py_CFunction f) {
+    py_TValue tmp;
+    py_newnativefunc(&tmp, f);
+    py_setdict(py_tpobject(type), py_name(name), &tmp);
+}
+
+void py_bindstaticmethod(py_Type type, const char* name, py_CFunction f) {
+    py_TValue tmp;
+    py_newnativefunc(&tmp, f);
+    bool ok = py_tpcall(tp_staticmethod, 1, &tmp);
+    if(!ok) {
+        py_printexc();
+        c11__abort("py_bindstaticmethod(): failed to create staticmethod");
+    }
+    py_setdict(py_tpobject(type), py_name(name), py_retval());
+}
+
+void py_bindfunc(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_bindproperty(py_Type type, const char* name, py_CFunction getter, py_CFunction setter) {
+    py_TValue tmp;
+    py_newobject(&tmp, tp_property, 2, 0);
+    py_newnativefunc(py_getslot(&tmp, 0), getter);
+    if(setter) {
+        py_newnativefunc(py_getslot(&tmp, 1), setter);
+    } else {
+        py_setslot(&tmp, 1, py_None());
+    }
+    py_setdict(py_tpobject(type), py_name(name), &tmp);
+}
+
+void py_bindmagic(py_Type type, py_Name name, py_CFunction f) {
+    py_Ref tmp = py_emplacedict(py_tpobject(type), name);
+    py_newnativefunc(tmp, f);
+}
+
+void py_macrobind(const char* sig, py_CFunction f) {
+    py_Ref tmp = py_pushtmp();
+    py_Name name = py_newfunction(tmp, sig, f, NULL, 0);
+    NameDict__set(&pk_current_vm->compile_time_funcs, name, tmp);
+    py_pop();
+}
+
+py_ItemRef py_macroget(py_Name name) {
+    NameDict* d = &pk_current_vm->compile_time_funcs;
+    if(d->length == 0) return NULL;
+    return NameDict__try_get(d, name);
+}

+ 81 - 16
src/public/exec.c → src/public/CodeExecution.c

@@ -6,13 +6,14 @@
 #include "pocketpy/interpreter/vm.h"
 #include "pocketpy/compiler/compiler.h"
 #include <assert.h>
+#include <ctype.h>
 
 py_Type pk_code__register() {
     py_Type type = pk_newtype("code", tp_object, NULL, (py_Dtor)CodeObject__dtor, false, true);
     return type;
 }
 
-bool _py_compile(CodeObject* out,
+static bool _py_compile(CodeObject* out,
                  const char* source,
                  const char* filename,
                  enum py_CompileMode mode,
@@ -33,20 +34,6 @@ bool _py_compile(CodeObject* out,
     return true;
 }
 
-bool py_compile(const char* source,
-                const char* filename,
-                enum py_CompileMode mode,
-                bool is_dynamic) {
-    CodeObject co;
-    bool ok = _py_compile(&co, source, filename, mode, is_dynamic);
-    if(ok) {
-        // compile success
-        CodeObject* ud = py_newobject(py_retval(), tp_code, 0, sizeof(CodeObject));
-        *ud = co;
-    }
-    return ok;
-}
-
 bool pk_exec(CodeObject* co, py_Ref module) {
     VM* vm = pk_current_vm;
     if(!module) module = vm->main;
@@ -92,6 +79,68 @@ bool pk_execdyn(CodeObject* co, py_Ref module, py_Ref globals, py_Ref locals) {
     return true;
 }
 
+static bool
+    pk_smartexec(const char* source, py_Ref module, enum py_CompileMode mode, va_list args) {
+    if(module == NULL) module = pk_current_vm->main;
+    pk_mappingproxy__namedict(py_pushtmp(), module);  // globals
+    py_newdict(py_pushtmp());                         // locals
+    bool ok = py_compile(source, "<string>", mode, true);
+    if(!ok) return false;
+    py_push(py_retval());
+    // [globals, locals, code]
+    CodeObject* co = py_touserdata(py_peek(-1));
+    py_StackRef locals = py_peek(-2);
+    int max_index = -1;
+    c11__foreach(Bytecode, &co->codes, bc) {
+        if(bc->op == OP_LOAD_NAME) {
+            c11_sv name = py_name2sv(c11__getitem(py_Name, &co->names, bc->arg));
+            if(name.data[0] != '_') continue;
+            int index;
+            if(name.size == 1) {
+                index = 0;
+            } else if(name.size == 2 && isdigit(name.data[1])) {
+                index = name.data[1] - '0';
+            } else {
+                continue;
+            }
+            max_index = c11__max(max_index, index);
+        }
+    }
+
+    if(max_index == -1) return ValueError("no placeholder found in the source");
+
+    for(int i = 0; i <= max_index; i++) {
+        py_Ref val = va_arg(args, py_Ref);
+        char buf[3];
+        buf[0] = '_';
+        buf[1] = '0' + i;
+        buf[2] = '\0';
+        py_dict_setitem_by_str(locals, buf, val);
+        if(i == 0) {
+            // _ => _0
+            py_dict_setitem_by_str(locals, "_", val);
+        }
+    }
+    ok = pk_execdyn(co, module, py_peek(-3), locals);
+    if(!ok) return false;
+    py_shrink(3);
+    return true;
+}
+
+bool py_compile(const char* source,
+                const char* filename,
+                enum py_CompileMode mode,
+                bool is_dynamic) {
+    CodeObject co;
+    bool ok = _py_compile(&co, source, filename, mode, is_dynamic);
+    if(ok) {
+        // compile success
+        CodeObject* ud = py_newobject(py_retval(), tp_code, 0, sizeof(CodeObject));
+        *ud = co;
+    }
+    return ok;
+}
+
 bool py_exec(const char* source, const char* filename, enum py_CompileMode mode, py_Ref module) {
     CodeObject co;
     if(!_py_compile(&co, source, filename, mode, false)) return false;
@@ -102,4 +151,20 @@ bool py_exec(const char* source, const char* filename, enum py_CompileMode mode,
 
 bool py_eval(const char* source, py_Ref module) {
     return py_exec(source, "<string>", EVAL_MODE, module);
-}
+}
+
+bool py_smartexec(const char* source, py_Ref module, ...) {
+    va_list args;
+    va_start(args, module);
+    bool ok = pk_smartexec(source, module, EXEC_MODE, args);
+    va_end(args);
+    return ok;
+}
+
+bool py_smarteval(const char* source, py_Ref module, ...) {
+    va_list args;
+    va_start(args, module);
+    bool ok = pk_smartexec(source, module, EVAL_MODE, args);
+    va_end(args);
+    return ok;
+}

+ 10 - 63
src/public/stack_ops.c → src/public/DictSlots.c

@@ -1,13 +1,11 @@
-#include "pocketpy/pocketpy.h"
-
-#include "pocketpy/common/utils.h"
-#include "pocketpy/objects/object.h"
 #include "pocketpy/interpreter/vm.h"
 
 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; }
 
+PK_INLINE py_Ref py_retval() { return &pk_current_vm->last_retval; }
+
 PK_INLINE py_Ref py_getdict(py_Ref self, py_Name name) {
     assert(self && self->is_ptr);
     return NameDict__try_get(PyObject__dict(self->_obj), name);
@@ -18,6 +16,11 @@ PK_INLINE void py_setdict(py_Ref self, py_Name name, py_Ref val) {
     NameDict__set(PyObject__dict(self->_obj), name, val);
 }
 
+bool py_deldict(py_Ref self, py_Name name) {
+    assert(self && self->is_ptr);
+    return NameDict__del(PyObject__dict(self->_obj), name);
+}
+
 py_ItemRef py_emplacedict(py_Ref self, py_Name name) {
     py_setdict(self, name, py_NIL());
     return py_getdict(self, name);
@@ -41,11 +44,6 @@ void py_cleardict(py_Ref self) {
     NameDict__clear(dict);
 }
 
-bool py_deldict(py_Ref self, py_Name name) {
-    assert(self && self->is_ptr);
-    return NameDict__del(PyObject__dict(self->_obj), name);
-}
-
 py_Ref py_getslot(py_Ref self, int i) {
     assert(self && self->is_ptr);
     assert(i >= 0 && i < self->_obj->slots);
@@ -58,59 +56,8 @@ void py_setslot(py_Ref self, int i, py_Ref val) {
     PyObject__slots(self->_obj)[i] = *val;
 }
 
-py_StackRef py_inspect_currentfunction() {
-    VM* vm = pk_current_vm;
-    if(vm->curr_decl_based_function) return vm->curr_decl_based_function;
-    py_Frame* frame = vm->top_frame;
-    if(!frame || frame->is_locals_special) return NULL;
-    return frame->p0;
-}
-
-py_GlobalRef py_inspect_currentmodule() {
-    py_Frame* frame = pk_current_vm->top_frame;
-    if(!frame) return NULL;
-    return frame->module;
-}
+py_Ref py_getbuiltin(py_Name name) { return py_getdict(pk_current_vm->builtins, name); }
 
-py_Frame* py_inspect_currentframe() { return pk_current_vm->top_frame; }
-
-/* Stack References */
-py_Ref py_peek(int i) {
-    assert(i <= 0);
-    return pk_current_vm->stack.sp + i;
-}
-
-void py_pop() {
-    VM* vm = pk_current_vm;
-    vm->stack.sp--;
-}
-
-void py_shrink(int n) {
-    VM* vm = pk_current_vm;
-    vm->stack.sp -= n;
-}
-
-void py_push(py_Ref src) {
-    VM* vm = pk_current_vm;
-    *vm->stack.sp++ = *src;
-}
-
-void py_pushnil() {
-    VM* vm = pk_current_vm;
-    py_newnil(vm->stack.sp++);
-}
-
-void py_pushnone() {
-    VM* vm = pk_current_vm;
-    py_newnone(vm->stack.sp++);
-}
-
-void py_pushname(py_Name name) {
-    VM* vm = pk_current_vm;
-    py_newint(vm->stack.sp++, (uintptr_t)name);
-}
+py_Ref py_getglobal(py_Name name) { return py_getdict(pk_current_vm->main, name); }
 
-py_Ref py_pushtmp() {
-    VM* vm = pk_current_vm;
-    return vm->stack.sp++;
-}
+void py_setglobal(py_Name name, py_Ref val) { py_setdict(pk_current_vm->main, name, val); }

+ 43 - 0
src/public/FrameOps.c

@@ -0,0 +1,43 @@
+#include "pocketpy/interpreter/frame.h"
+#include "pocketpy/interpreter/vm.h"
+
+const char* py_Frame_sourceloc(py_Frame* self, int* lineno) {
+    SourceLocation loc = Frame__source_location(self);
+    *lineno = loc.lineno;
+    return loc.src->filename->data;
+}
+
+void py_Frame_newglobals(py_Frame* frame, py_OutRef out) {
+    if(!frame) {
+        pk_mappingproxy__namedict(out, pk_current_vm->main);
+        return;
+    }
+    if(frame->globals->type == tp_module) {
+        pk_mappingproxy__namedict(out, frame->globals);
+    } else {
+        *out = *frame->globals;  // dict
+    }
+}
+
+void py_Frame_newlocals(py_Frame* frame, py_OutRef out) {
+    if(!frame) {
+        py_newdict(out);
+        return;
+    }
+    if(frame->is_locals_special) {
+        switch(frame->locals->type) {
+            case tp_locals: frame = frame->locals->_ptr; break;
+            case tp_dict: *out = *frame->locals; return;
+            case tp_nil: py_newdict(out); return;
+            default: c11__unreachable();
+        }
+    }
+    FastLocals__to_dict(frame->locals, frame->co);
+    py_assign(out, py_retval());
+}
+
+py_StackRef py_Frame_function(py_Frame* self) {
+    if(self->is_locals_special) return NULL;
+    assert(self->p0->type == tp_function);
+    return self->p0;
+}

+ 161 - 0
src/public/GlobalSetup.c

@@ -0,0 +1,161 @@
+#include "pocketpy/objects/codeobject.h"
+#include "pocketpy/pocketpy.h"
+
+#include "pocketpy/common/utils.h"
+#include "pocketpy/common/name.h"
+#include "pocketpy/interpreter/vm.h"
+
+_Thread_local VM* pk_current_vm;
+
+static bool pk_initialized;
+static bool pk_finalized;
+
+static VM pk_default_vm;
+static VM* pk_all_vm[16];
+static py_TValue _True, _False, _None, _NIL;
+
+void py_initialize() {
+    c11__rtassert(!pk_finalized);
+
+    if(pk_initialized) {
+        // c11__abort("py_initialize() can only be called once!");
+        return;
+    }
+
+    pk_names_initialize();
+
+    // check endianness
+    int x = 1;
+    bool is_little_endian = *(char*)&x == 1;
+    if(!is_little_endian) c11__abort("is_little_endian != true");
+
+    static_assert(sizeof(py_TValue) == 24, "sizeof(py_TValue) != 24");
+    static_assert(offsetof(py_TValue, extra) == 4, "offsetof(py_TValue, extra) != 4");
+
+    pk_current_vm = pk_all_vm[0] = &pk_default_vm;
+
+    // initialize some convenient references
+    py_newbool(&_True, true);
+    py_newbool(&_False, false);
+    py_newnone(&_None);
+    py_newnil(&_NIL);
+    VM__ctor(&pk_default_vm);
+
+    pk_initialized = true;
+}
+
+void py_finalize() {
+    if(pk_finalized) c11__abort("py_finalize() can only be called once!");
+    pk_finalized = true;
+
+    for(int i = 1; i < 16; i++) {
+        VM* vm = pk_all_vm[i];
+        if(vm) {
+            // temp fix https://github.com/pocketpy/pocketpy/issues/315
+            // TODO: refactor VM__ctor and VM__dtor
+            pk_current_vm = vm;
+            VM__dtor(vm);
+            PK_FREE(vm);
+        }
+    }
+    pk_current_vm = &pk_default_vm;
+    VM__dtor(&pk_default_vm);
+    pk_current_vm = NULL;
+
+    pk_names_finalize();
+}
+
+int py_currentvm() {
+    for(int i = 0; i < 16; i++) {
+        if(pk_all_vm[i] == pk_current_vm) return i;
+    }
+    return -1;
+}
+
+void py_switchvm(int index) {
+    if(index < 0 || index >= 16) c11__abort("invalid vm index");
+    if(!pk_all_vm[index]) {
+        pk_current_vm = pk_all_vm[index] = PK_MALLOC(sizeof(VM));
+        memset(pk_current_vm, 0, sizeof(VM));
+        VM__ctor(pk_all_vm[index]);
+    } else {
+        pk_current_vm = pk_all_vm[index];
+    }
+}
+
+void py_resetvm() {
+    VM* vm = pk_current_vm;
+    VM__dtor(vm);
+    memset(vm, 0, sizeof(VM));
+    VM__ctor(vm);
+}
+
+void py_resetallvm() {
+    for(int i = 0; i < 16; i++) {
+        py_switchvm(i);
+        py_resetvm();
+    }
+    py_switchvm(0);
+}
+
+void* py_getvmctx() { return pk_current_vm->ctx; }
+
+void py_setvmctx(void* ctx) { pk_current_vm->ctx = ctx; }
+
+py_Callbacks* py_callbacks() { return &pk_current_vm->callbacks; }
+
+/////////////////////////////
+
+void py_sys_setargv(int argc, char** argv) {
+    py_GlobalRef sys = py_getmodule("sys");
+    py_Ref argv_list = py_getdict(sys, py_name("argv"));
+    py_list_clear(argv_list);
+    for(int i = 0; i < argc; i++) {
+        py_newstr(py_list_emplace(argv_list), argv[i]);
+    }
+}
+
+void py_sys_settrace(py_TraceFunc func, bool reset) {
+    TraceInfo* info = &pk_current_vm->trace_info;
+    info->func = func;
+    if(!reset) return;
+    if(info->prev_loc.src) {
+        PK_DECREF(info->prev_loc.src);
+        info->prev_loc.src = NULL;
+    }
+    info->prev_loc.lineno = -1;
+}
+
+int py_gc_collect() {
+    ManagedHeap* heap = &pk_current_vm->heap;
+    return ManagedHeap__collect(heap);
+}
+
+/////////////////////////////
+
+void* py_malloc(size_t size) { return PK_MALLOC(size); }
+
+void* py_realloc(void* ptr, size_t size) { return PK_REALLOC(ptr, size); }
+
+void py_free(void* ptr) { PK_FREE(ptr); }
+
+/////////////////////////////
+
+py_GlobalRef py_True() { return &_True; }
+
+py_GlobalRef py_False() { return &_False; }
+
+py_GlobalRef py_None() { return &_None; }
+
+py_GlobalRef py_NIL() { return &_NIL; }
+
+/////////////////////////////
+
+const char* pk_opname(Opcode op) {
+    const static char* OP_NAMES[] = {
+#define OPCODE(name) #name,
+#include "pocketpy/xmacros/opcodes.h"
+#undef OPCODE
+    };
+    return OP_NAMES[op];
+}

+ 28 - 0
src/public/Inspection.c

@@ -0,0 +1,28 @@
+#include "pocketpy/interpreter/frame.h"
+#include "pocketpy/interpreter/vm.h"
+
+py_StackRef py_inspect_currentfunction() {
+    VM* vm = pk_current_vm;
+    if(vm->curr_decl_based_function) return vm->curr_decl_based_function;
+    py_Frame* frame = vm->top_frame;
+    if(!frame || frame->is_locals_special) return NULL;
+    return frame->p0;
+}
+
+py_GlobalRef py_inspect_currentmodule() {
+    py_Frame* frame = pk_current_vm->top_frame;
+    if(!frame) return NULL;
+    return frame->module;
+}
+
+py_Frame* py_inspect_currentframe() { return pk_current_vm->top_frame; }
+
+void py_newglobals(py_OutRef out) {
+    py_Frame* frame = pk_current_vm->top_frame;
+    py_Frame_newglobals(frame, out);
+}
+
+void py_newlocals(py_OutRef out) {
+    py_Frame* frame = pk_current_vm->top_frame;
+    py_Frame_newlocals(frame, out);
+}

+ 191 - 0
src/public/ModuleSystem.c

@@ -0,0 +1,191 @@
+#include "pocketpy/common/str.h"
+#include "pocketpy/objects/base.h"
+#include "pocketpy/objects/codeobject.h"
+#include "pocketpy/pocketpy.h"
+#include "pocketpy/common/utils.h"
+#include "pocketpy/common/sstream.h"
+#include "pocketpy/interpreter/vm.h"
+#include "pocketpy/common/_generated.h"
+
+
+py_Ref py_getmodule(const char* path) {
+    VM* vm = pk_current_vm;
+    return BinTree__try_get(&vm->modules, (void*)path);
+}
+
+py_Ref py_newmodule(const char* path) {
+    int path_len = strlen(path);
+    if(path_len > PK_MAX_MODULE_PATH_LEN) c11__abort("module path too long: %s", path);
+    if(path_len == 0) c11__abort("module path cannot be empty");
+
+    py_ModuleInfo* mi = py_newobject(py_retval(), tp_module, -1, sizeof(py_ModuleInfo));
+
+    int last_dot = c11_sv__rindex((c11_sv){path, path_len}, '.');
+    if(last_dot == -1) {
+        mi->name = c11_string__new(path);
+        mi->package = c11_string__new("");
+    } else {
+        const char* start = path + last_dot + 1;
+        mi->name = c11_string__new(start);
+        mi->package = c11_string__new2(path, last_dot);
+    }
+
+    mi->path = c11_string__new(path);
+    path = mi->path->data;
+
+    // we do not allow override in order to avoid memory leak
+    // it is because Module objects are not garbage collected
+    bool exists = BinTree__contains(&pk_current_vm->modules, (void*)path);
+    if(exists) c11__abort("module '%s' already exists", path);
+
+    BinTree__set(&pk_current_vm->modules, (void*)path, py_retval());
+    py_GlobalRef retval = py_getmodule(path);
+    mi->self = retval;
+
+    // setup __name__
+    py_newstrv(py_emplacedict(retval, __name__), c11_string__sv(mi->name));
+    // setup __package__
+    py_newstrv(py_emplacedict(retval, __package__), c11_string__sv(mi->package));
+    // setup __path__
+    py_newstrv(py_emplacedict(retval, __path__), c11_string__sv(mi->path));
+    return retval;
+}
+
+static void py_ModuleInfo__dtor(py_ModuleInfo* mi) {
+    c11_string__delete(mi->name);
+    c11_string__delete(mi->package);
+    c11_string__delete(mi->path);
+}
+
+py_Type pk_module__register() {
+    py_Type type = pk_newtype("module", tp_object, NULL, (py_Dtor)py_ModuleInfo__dtor, false, true);
+    return type;
+}
+
+int load_module_from_dll_desktop_only(const char* path) PY_RAISE PY_RETURN;
+
+int py_import(const char* path_cstr) {
+    VM* vm = pk_current_vm;
+    c11_sv path = {path_cstr, strlen(path_cstr)};
+    if(path.size == 0) return ValueError("empty module name");
+
+    if(path.data[0] == '.') {
+        // try relative import
+        int dot_count = 1;
+        while(dot_count < path.size && path.data[dot_count] == '.')
+            dot_count++;
+
+        c11_sv top_filename = c11_string__sv(vm->top_frame->co->src->filename);
+        int is_init = c11_sv__endswith(top_filename, (c11_sv){"__init__.py", 11});
+
+        py_ModuleInfo* mi = py_touserdata(vm->top_frame->module);
+        c11_sv package_sv = c11_string__sv(mi->path);
+        if(package_sv.size == 0) {
+            return ImportError("attempted relative import with no known parent package");
+        }
+
+        c11_vector /* T=c11_sv */ cpnts = c11_sv__split(package_sv, '.');
+        for(int i = is_init; i < dot_count; i++) {
+            if(cpnts.length == 0)
+                return ImportError("attempted relative import beyond top-level package");
+            c11_vector__pop(&cpnts);
+        }
+
+        if(dot_count < path.size) {
+            c11_sv last_cpnt = c11_sv__slice(path, dot_count);
+            c11_vector__push(c11_sv, &cpnts, last_cpnt);
+        }
+
+        // join cpnts
+        c11_sbuf buf;
+        c11_sbuf__ctor(&buf);
+        for(int i = 0; i < cpnts.length; i++) {
+            if(i > 0) c11_sbuf__write_char(&buf, '.');
+            c11_sbuf__write_sv(&buf, c11__getitem(c11_sv, &cpnts, i));
+        }
+
+        c11_vector__dtor(&cpnts);
+        c11_string* new_path = c11_sbuf__submit(&buf);
+        int res = py_import(new_path->data);
+        c11_string__delete(new_path);
+        return res;
+    }
+
+    assert(path.data[0] != '.' && path.data[path.size - 1] != '.');
+
+    // check existing module
+    py_GlobalRef ext_mod = py_getmodule(path.data);
+    if(ext_mod) {
+        py_assign(py_retval(), ext_mod);
+        return true;
+    }
+
+    if(vm->callbacks.lazyimport) {
+        py_GlobalRef lazymod = vm->callbacks.lazyimport(path_cstr);
+        if(lazymod) {
+            c11__rtassert(py_istype(lazymod, tp_module));
+            py_assign(py_retval(), lazymod);
+            return 1;
+        }
+    }
+
+    // try import
+    c11_string* slashed_path = c11_sv__replace(path, '.', PK_PLATFORM_SEP);
+    c11_string* filename = c11_string__new3("%s.py", slashed_path->data);
+
+    bool need_free = true;
+    const char* data = load_kPythonLib(path_cstr);
+    if(data != NULL) {
+        need_free = false;
+        goto __SUCCESS;
+    }
+
+    data = vm->callbacks.importfile(filename->data);
+    if(data != NULL) goto __SUCCESS;
+
+    c11_string__delete(filename);
+    filename = c11_string__new3("%s%c__init__.py", slashed_path->data, PK_PLATFORM_SEP);
+    data = vm->callbacks.importfile(filename->data);
+    if(data != NULL) goto __SUCCESS;
+
+    c11_string__delete(filename);
+    c11_string__delete(slashed_path);
+    // not found
+    return load_module_from_dll_desktop_only(path_cstr);
+
+__SUCCESS:
+    do {
+    } while(0);
+    py_GlobalRef mod = py_newmodule(path_cstr);
+    bool ok = py_exec((const char*)data, filename->data, EXEC_MODE, mod);
+    py_assign(py_retval(), mod);
+
+    c11_string__delete(filename);
+    c11_string__delete(slashed_path);
+    if(need_free) PK_FREE((void*)data);
+    return ok ? 1 : -1;
+}
+
+bool py_importlib_reload(py_Ref module) {
+    VM* vm = pk_current_vm;
+    py_ModuleInfo* mi = py_touserdata(module);
+    // We should ensure that the module is its original py_GlobalRef
+    module = mi->self;
+    c11_sv path = c11_string__sv(mi->path);
+    c11_string* slashed_path = c11_sv__replace(path, '.', PK_PLATFORM_SEP);
+    c11_string* filename = c11_string__new3("%s.py", slashed_path->data);
+    char* data = vm->callbacks.importfile(filename->data);
+    if(data == NULL) {
+        c11_string__delete(filename);
+        filename = c11_string__new3("%s%c__init__.py", slashed_path->data, PK_PLATFORM_SEP);
+        data = vm->callbacks.importfile(filename->data);
+    }
+    c11_string__delete(slashed_path);
+    if(data == NULL) return ImportError("module '%v' not found", path);
+    // py_cleardict(module); BUG: removing old classes will cause RELOAD_MODE to fail
+    bool ok = py_exec(data, filename->data, RELOAD_MODE, module);
+    c11_string__delete(filename);
+    PK_FREE(data);
+    py_assign(py_retval(), module);
+    return ok;
+}

+ 0 - 0
src/public/py_dict.c → src/public/PyDict.c


+ 30 - 23
src/public/py_exception.c → src/public/PyException.c

@@ -141,28 +141,6 @@ py_Type pk_StopIteration__register() {
     return type;
 }
 
-//////////////////////////////////////////////////
-bool py_checkexc() {
-    VM* vm = pk_current_vm;
-    return !py_isnil(&vm->unhandled_exc);
-}
-
-bool py_matchexc(py_Type type) {
-    VM* vm = pk_current_vm;
-    if(py_isnil(&vm->unhandled_exc)) return false;
-    bool ok = py_issubclass(vm->unhandled_exc.type, type);
-    if(ok) vm->last_retval = vm->unhandled_exc;
-    return ok;
-}
-
-void py_clearexc(py_StackRef p0) {
-    VM* vm = pk_current_vm;
-    py_newnil(&vm->unhandled_exc);
-    if(p0) {
-        c11__rtassert(p0 >= vm->stack.begin && p0 <= vm->stack.sp);
-        vm->stack.sp = p0;
-    }
-}
 
 static void c11_sbuf__write_exc(c11_sbuf* self, py_Ref exc) {
     c11_sbuf__write_cstr(self, "Traceback (most recent call last):\n");
@@ -212,6 +190,29 @@ char* safe_stringify_exception(py_Ref exc) {
     return c11_strdup(message);
 }
 
+//////////////////////////////////////////////////
+bool py_checkexc() {
+    VM* vm = pk_current_vm;
+    return !py_isnil(&vm->unhandled_exc);
+}
+
+bool py_matchexc(py_Type type) {
+    VM* vm = pk_current_vm;
+    if(py_isnil(&vm->unhandled_exc)) return false;
+    bool ok = py_issubclass(vm->unhandled_exc.type, type);
+    if(ok) vm->last_retval = vm->unhandled_exc;
+    return ok;
+}
+
+void py_clearexc(py_StackRef p0) {
+    VM* vm = pk_current_vm;
+    py_newnil(&vm->unhandled_exc);
+    if(p0) {
+        c11__rtassert(p0 >= vm->stack.begin && p0 <= vm->stack.sp);
+        vm->stack.sp = p0;
+    }
+}
+
 void py_printexc() {
     char* msg = py_formatexc();
     if(!msg) return;
@@ -299,4 +300,10 @@ bool KeyError(py_Ref key) {
     bool ok = py_tpcall(tp_KeyError, 1, key);
     if(!ok) return false;
     return py_raise(py_retval());
-}
+}
+
+bool StopIteration() {
+    bool ok = py_tpcall(tp_StopIteration, 0, NULL);
+    if(!ok) return false;
+    return py_raise(py_retval());
+}

+ 0 - 0
src/public/py_list.c → src/public/PyList.c


+ 9 - 1
src/public/py_slice.c → src/public/PySlice.c

@@ -4,12 +4,20 @@
 #include "pocketpy/objects/object.h"
 #include "pocketpy/interpreter/vm.h"
 
-void py_newslice(py_OutRef out) {
+py_ObjectRef py_newslice(py_OutRef out) {
     VM* vm = pk_current_vm;
     PyObject* obj = ManagedHeap__gcnew(&vm->heap, tp_slice, 3, 0);
     out->type = tp_slice;
     out->is_ptr = true;
     out->_obj = obj;
+    return PyObject__slots(obj);
+}
+
+void py_newsliceint(py_OutRef out, py_i64 start, py_i64 stop, py_i64 step) {
+    py_Ref slots = py_newslice(out);
+    py_newint(&slots[0], start);
+    py_newint(&slots[1], stop);
+    py_newint(&slots[2], step);
 }
 
 static bool slice__new__(int argc, py_Ref argv) {

+ 0 - 0
src/public/py_tuple.c → src/public/PyTuple.c


+ 79 - 11
src/public/py_ops.c → src/public/PythonOps.c

@@ -4,6 +4,48 @@
 #include "pocketpy/objects/base.h"
 #include "pocketpy/pocketpy.h"
 
+bool py_binaryadd(py_Ref lhs, py_Ref rhs) { return py_binaryop(lhs, rhs, __add__, __radd__); }
+
+bool py_binarysub(py_Ref lhs, py_Ref rhs) { return py_binaryop(lhs, rhs, __sub__, __rsub__); }
+
+bool py_binarymul(py_Ref lhs, py_Ref rhs) { return py_binaryop(lhs, rhs, __mul__, __rmul__); }
+
+bool py_binarytruediv(py_Ref lhs, py_Ref rhs) {
+    return py_binaryop(lhs, rhs, __truediv__, __rtruediv__);
+}
+
+bool py_binaryfloordiv(py_Ref lhs, py_Ref rhs) {
+    return py_binaryop(lhs, rhs, __floordiv__, __rfloordiv__);
+}
+
+bool py_binarymod(py_Ref lhs, py_Ref rhs) { return py_binaryop(lhs, rhs, __mod__, __rmod__); }
+
+bool py_binarypow(py_Ref lhs, py_Ref rhs) { return py_binaryop(lhs, rhs, __pow__, __rpow__); }
+
+bool py_binarylshift(py_Ref lhs, py_Ref rhs) { return py_binaryop(lhs, rhs, __lshift__, 0); }
+
+bool py_binaryrshift(py_Ref lhs, py_Ref rhs) { return py_binaryop(lhs, rhs, __rshift__, 0); }
+
+bool py_binaryand(py_Ref lhs, py_Ref rhs) { return py_binaryop(lhs, rhs, __and__, 0); }
+
+bool py_binaryor(py_Ref lhs, py_Ref rhs) { return py_binaryop(lhs, rhs, __or__, 0); }
+
+bool py_binaryxor(py_Ref lhs, py_Ref rhs) { return py_binaryop(lhs, rhs, __xor__, 0); }
+
+bool py_binarymatmul(py_Ref lhs, py_Ref rhs) { return py_binaryop(lhs, rhs, __matmul__, 0); }
+
+bool py_eq(py_Ref lhs, py_Ref rhs) { return py_binaryop(lhs, rhs, __eq__, __eq__); }
+
+bool py_ne(py_Ref lhs, py_Ref rhs) { return py_binaryop(lhs, rhs, __ne__, __ne__); }
+
+bool py_lt(py_Ref lhs, py_Ref rhs) { return py_binaryop(lhs, rhs, __lt__, __gt__); }
+
+bool py_le(py_Ref lhs, py_Ref rhs) { return py_binaryop(lhs, rhs, __le__, __ge__); }
+
+bool py_gt(py_Ref lhs, py_Ref rhs) { return py_binaryop(lhs, rhs, __gt__, __lt__); }
+
+bool py_ge(py_Ref lhs, py_Ref rhs) { return py_binaryop(lhs, rhs, __ge__, __le__); }
+
 bool py_isidentical(py_Ref lhs, py_Ref rhs) {
     if(lhs->type != rhs->type) return false;
     switch(lhs->type) {
@@ -52,6 +94,29 @@ int py_bool(py_Ref val) {
     }
 }
 
+int py_equal(py_Ref lhs, py_Ref rhs) {
+    if(py_isidentical(lhs, rhs)) return 1;
+    if(!py_eq(lhs, rhs)) return -1;
+    return py_bool(py_retval());
+}
+
+int py_less(py_Ref lhs, py_Ref rhs) {
+    if(!py_lt(lhs, rhs)) return -1;
+    return py_bool(py_retval());
+}
+
+bool py_callable(py_Ref val) {
+    switch(val->type) {
+        case tp_nativefunc: return true;
+        case tp_function: return true;
+        case tp_type: return true;
+        case tp_boundmethod: return true;
+        case tp_staticmethod: return true;
+        case tp_classmethod: return true;
+        default: return py_tpfindmagic(val->type, __call__);
+    }
+}
+
 bool py_hash(py_Ref val, int64_t* out) {
     py_TypeInfo* ti = pk_typeinfo(val->type);
     do {
@@ -119,6 +184,20 @@ int py_next(py_Ref val) {
     return -1;
 }
 
+bool py_str(py_Ref val) {
+    if(val->type == tp_str) {
+        py_assign(py_retval(), val);
+        return true;
+    }
+    py_Ref tmp = py_tpfindmagic(val->type, __str__);
+    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); }
+
 bool py_getattr(py_Ref self, py_Name name) {
     // https://docs.python.org/3/howto/descriptor.html#invocation-from-an-instance
     py_TypeInfo* ti = pk_typeinfo(self->type);
@@ -277,14 +356,3 @@ bool py_delitem(py_Ref self, py_Ref key) {
     py_shrink(2);
     return ok;
 }
-
-int py_equal(py_Ref lhs, py_Ref rhs) {
-    if(py_isidentical(lhs, rhs)) return 1;
-    if(!py_eq(lhs, rhs)) return -1;
-    return py_bool(py_retval());
-}
-
-int py_less(py_Ref lhs, py_Ref rhs) {
-    if(!py_lt(lhs, rhs)) return -1;
-    return py_bool(py_retval());
-}

+ 44 - 133
src/public/internal.c → src/public/StackOps.c

@@ -1,140 +1,61 @@
-#include "pocketpy/interpreter/typeinfo.h"
-#include "pocketpy/objects/codeobject.h"
 #include "pocketpy/pocketpy.h"
-
-#include "pocketpy/common/utils.h"
-#include "pocketpy/common/name.h"
 #include "pocketpy/interpreter/vm.h"
 
-_Thread_local VM* pk_current_vm;
-
-static bool pk_initialized;
-static bool pk_finalized;
-
-static VM pk_default_vm;
-static VM* pk_all_vm[16];
-static py_TValue _True, _False, _None, _NIL;
-
-void py_initialize() {
-    c11__rtassert(!pk_finalized);
-
-    if(pk_initialized) {
-        // c11__abort("py_initialize() can only be called once!");
-        return;
-    }
-
-    pk_names_initialize();
-
-    // check endianness
-    int x = 1;
-    bool is_little_endian = *(char*)&x == 1;
-    if(!is_little_endian) c11__abort("is_little_endian != true");
-
-    static_assert(sizeof(py_TValue) == 24, "sizeof(py_TValue) != 24");
-    static_assert(offsetof(py_TValue, extra) == 4, "offsetof(py_TValue, extra) != 4");
-
-    pk_current_vm = pk_all_vm[0] = &pk_default_vm;
-
-    // initialize some convenient references
-    py_newbool(&_True, true);
-    py_newbool(&_False, false);
-    py_newnone(&_None);
-    py_newnil(&_NIL);
-    VM__ctor(&pk_default_vm);
-
-    pk_initialized = true;
+PK_INLINE py_Ref py_peek(int i) {
+    assert(i <= 0);
+    return pk_current_vm->stack.sp + i;
 }
 
-void* py_malloc(size_t size) { return PK_MALLOC(size); }
-
-void* py_realloc(void* ptr, size_t size) { return PK_REALLOC(ptr, size); }
-
-void py_free(void* ptr) { PK_FREE(ptr); }
-
-py_GlobalRef py_True() { return &_True; }
-
-py_GlobalRef py_False() { return &_False; }
-
-py_GlobalRef py_None() { return &_None; }
-
-py_GlobalRef py_NIL() { return &_NIL; }
-
-void py_finalize() {
-    if(pk_finalized) c11__abort("py_finalize() can only be called once!");
-    pk_finalized = true;
-
-    for(int i = 1; i < 16; i++) {
-        VM* vm = pk_all_vm[i];
-        if(vm) {
-            // temp fix https://github.com/pocketpy/pocketpy/issues/315
-            // TODO: refactor VM__ctor and VM__dtor
-            pk_current_vm = vm;
-            VM__dtor(vm);
-            PK_FREE(vm);
-        }
-    }
-    pk_current_vm = &pk_default_vm;
-    VM__dtor(&pk_default_vm);
-    pk_current_vm = NULL;
-
-    pk_names_finalize();
+PK_INLINE void py_push(py_Ref src) {
+    VM* vm = pk_current_vm;
+    *vm->stack.sp++ = *src;
 }
 
-void py_switchvm(int index) {
-    if(index < 0 || index >= 16) c11__abort("invalid vm index");
-    if(!pk_all_vm[index]) {
-        pk_current_vm = pk_all_vm[index] = PK_MALLOC(sizeof(VM));
-        memset(pk_current_vm, 0, sizeof(VM));
-        VM__ctor(pk_all_vm[index]);
-    } else {
-        pk_current_vm = pk_all_vm[index];
-    }
+PK_INLINE void py_pushnil() {
+    VM* vm = pk_current_vm;
+    py_newnil(vm->stack.sp++);
 }
 
-void py_resetvm() {
+PK_INLINE void py_pushnone() {
     VM* vm = pk_current_vm;
-    VM__dtor(vm);
-    memset(vm, 0, sizeof(VM));
-    VM__ctor(vm);
+    py_newnone(vm->stack.sp++);
 }
 
-void py_resetallvm() {
-    for(int i = 0; i < 16; i++) {
-        py_switchvm(i);
-        py_resetvm();
-    }
-    py_switchvm(0);
+PK_INLINE void py_pushname(py_Name name) {
+    VM* vm = pk_current_vm;
+    py_newint(vm->stack.sp++, (uintptr_t)name);
 }
 
-int py_currentvm() {
-    for(int i = 0; i < 16; i++) {
-        if(pk_all_vm[i] == pk_current_vm) return i;
-    }
-    return -1;
+PK_INLINE void py_pop() {
+    VM* vm = pk_current_vm;
+    vm->stack.sp--;
 }
 
-void* py_getvmctx() { return pk_current_vm->ctx; }
+PK_INLINE void py_shrink(int n) {
+    VM* vm = pk_current_vm;
+    vm->stack.sp -= n;
+}
 
-void py_setvmctx(void* ctx) { pk_current_vm->ctx = ctx; }
+PK_INLINE py_Ref py_pushtmp() {
+    VM* vm = pk_current_vm;
+    return vm->stack.sp++;
+}
 
-void py_sys_setargv(int argc, char** argv) {
-    py_GlobalRef sys = py_getmodule("sys");
-    py_Ref argv_list = py_getdict(sys, py_name("argv"));
-    py_list_clear(argv_list);
-    for(int i = 0; i < argc; i++) {
-        py_newstr(py_list_emplace(argv_list), argv[i]);
-    }
+PK_INLINE bool py_pushmethod(py_Name name) {
+    bool ok = pk_loadmethod(py_peek(-1), name);
+    if(ok) pk_current_vm->stack.sp++;
+    return ok;
 }
 
-py_Callbacks* py_callbacks() { return &pk_current_vm->callbacks; }
+bool py_pusheval(const char* expr, py_GlobalRef module) {
+    bool ok = py_exec(expr, "<string>", EVAL_MODE, module);
+    if(!ok) return false;
+    py_push(py_retval());
+    return true;
+}
 
-const char* pk_opname(Opcode op) {
-    const static char* OP_NAMES[] = {
-#define OPCODE(name) #name,
-#include "pocketpy/xmacros/opcodes.h"
-#undef OPCODE
-    };
-    return OP_NAMES[op];
+PK_INLINE bool py_vectorcall(uint16_t argc, uint16_t kwargc) {
+    return VM__vectorcall(pk_current_vm, argc, kwargc, false) != RES_ERROR;
 }
 
 bool py_call(py_Ref f, int argc, py_Ref argv) {
@@ -180,15 +101,15 @@ bool py_callcfunc(py_CFunction f, int argc, py_Ref argv) {
 }
 #endif
 
-bool py_vectorcall(uint16_t argc, uint16_t kwargc) {
-    return VM__vectorcall(pk_current_vm, argc, kwargc, false) != RES_ERROR;
+bool py_tpcall(py_Type type, int argc, py_Ref argv) {
+    return py_call(py_tpobject(type), argc, argv);
 }
 
-PK_INLINE py_Ref py_retval() { return &pk_current_vm->last_retval; }
-
-bool py_pushmethod(py_Name name) {
-    bool ok = pk_loadmethod(py_peek(-1), name);
-    if(ok) pk_current_vm->stack.sp++;
+bool py_binaryop(py_Ref lhs, py_Ref rhs, py_Name op, py_Name rop) {
+    py_push(lhs);
+    py_push(rhs);
+    bool ok = pk_stack_binaryop(pk_current_vm, op, rop);
+    py_shrink(2);
     return ok;
 }
 
@@ -267,10 +188,6 @@ bool pk_loadmethod(py_StackRef self, py_Name name) {
     return false;
 }
 
-bool py_tpcall(py_Type type, int argc, py_Ref argv) {
-    return py_call(py_tpobject(type), argc, argv);
-}
-
 bool pk_callmagic(py_Name name, int argc, py_Ref argv) {
     assert(argc >= 1);
     // assert(py_ismagicname(name));
@@ -278,9 +195,3 @@ bool pk_callmagic(py_Name name, int argc, py_Ref argv) {
     if(!tmp) return AttributeError(argv, name);
     return py_call(tmp, argc, argv);
 }
-
-bool StopIteration() {
-    bool ok = py_tpcall(tp_StopIteration, 0, NULL);
-    if(!ok) return false;
-    return py_raise(py_retval());
-}

+ 104 - 0
src/public/TypeSystem.c

@@ -0,0 +1,104 @@
+#include "pocketpy/objects/base.h"
+#include "pocketpy/pocketpy.h"
+
+#include "pocketpy/interpreter/vm.h"
+
+py_Type py_newtype(const char* name, py_Type base, const py_GlobalRef module, void (*dtor)(void*)) {
+    if(strlen(name) == 0) c11__abort("type name cannot be empty");
+    py_Type type = pk_newtype(name, base, module, dtor, false, false);
+    if(module) py_setdict(module, py_name(name), py_tpobject(type));
+    return type;
+}
+
+PK_INLINE bool py_istype(py_Ref self, py_Type type) { return self->type == type; }
+
+PK_INLINE py_Type py_typeof(py_Ref self) { return self->type; }
+
+bool py_isinstance(py_Ref obj, py_Type type) { return py_issubclass(obj->type, type); }
+
+bool py_issubclass(py_Type derived, py_Type base) {
+    assert(derived != 0 && base != 0);
+    py_TypeInfo* derived_ti = pk_typeinfo(derived);
+    py_TypeInfo* base_ti = pk_typeinfo(base);
+    do {
+        if(derived_ti == base_ti) return true;
+        derived_ti = derived_ti->base_ti;
+    } while(derived_ti);
+    return false;
+}
+
+py_Type py_gettype(const char* module, py_Name name) {
+    py_Ref mod;
+    if(module != NULL) {
+        mod = py_getmodule(module);
+        if(!mod) return tp_nil;
+    } else {
+        mod = pk_current_vm->builtins;
+    }
+    py_Ref object = py_getdict(mod, name);
+    if(object && py_istype(object, tp_type)) return py_totype(object);
+    return tp_nil;
+}
+
+bool py_checktype(py_Ref self, py_Type type) {
+    if(self->type == type) return true;
+    return TypeError("expected '%t', got '%t'", type, self->type);
+}
+
+bool py_checkinstance(py_Ref self, py_Type type) {
+    if(py_isinstance(self, type)) return true;
+    return TypeError("expected '%t' or its subclass, got '%t'", type, self->type);
+}
+
+PK_DEPRECATED py_Ref py_tpgetmagic(py_Type type, py_Name name) {
+    // assert(py_ismagicname(name));
+    py_TypeInfo* ti = pk_typeinfo(type);
+    py_Ref retval = py_getdict(&ti->self, name);
+    return retval != NULL ? retval : py_NIL();
+}
+
+PK_INLINE py_Ref py_tpfindmagic(py_Type t, py_Name name) {
+    // assert(py_ismagicname(name));
+    return py_tpfindname(t, name);
+}
+
+PK_INLINE py_ItemRef py_tpfindname(py_Type type, py_Name name) {
+    py_TypeInfo* ti = pk_typeinfo(type);
+    return pk_tpfindname(ti, name);
+}
+
+PK_INLINE py_Type py_tpbase(py_Type t) {
+    assert(t);
+    py_TypeInfo* ti = pk_typeinfo(t);
+    return ti->base;
+}
+
+py_Ref py_tpobject(py_Type type) {
+    assert(type);
+    return &pk_typeinfo(type)->self;
+}
+
+const char* py_tpname(py_Type type) {
+    if(!type) return "nil";
+    py_Name name = pk_typeinfo(type)->name;
+    return py_name2str(name);
+}
+
+void py_tpsetfinal(py_Type type) {
+    assert(type);
+    py_TypeInfo* ti = pk_typeinfo(type);
+    ti->is_final = true;
+}
+
+void py_tphookattributes(py_Type type,
+                         bool (*getattribute)(py_Ref self, py_Name name),
+                         bool (*setattribute)(py_Ref self, py_Name name, py_Ref val),
+                         bool (*delattribute)(py_Ref self, py_Name name),
+                         bool (*getunboundmethod)(py_Ref self, py_Name name)) {
+    assert(type);
+    py_TypeInfo* ti = pk_typeinfo(type);
+    ti->getattribute = getattribute;
+    ti->setattribute = setattribute;
+    ti->delattribute = delattribute;
+    ti->getunboundmethod = getunboundmethod;
+}

+ 28 - 1
src/public/cast.c → src/public/ValueCast.c

@@ -4,7 +4,7 @@
 #include "pocketpy/objects/object.h"
 #include "pocketpy/interpreter/vm.h"
 
-int64_t py_toint(py_Ref self) {
+py_i64 py_toint(py_Ref self) {
     assert(self->type == tp_int);
     return self->_i64;
 }
@@ -55,3 +55,30 @@ void* py_touserdata(py_Ref self) {
     assert(self && self->is_ptr);
     return PyObject__userdata(self->_obj);
 }
+
+const char* py_tostr(py_Ref self) { return pk_tostr(self)->data; }
+
+const char* py_tostrn(py_Ref self, int* size) {
+    c11_string* ud = pk_tostr(self);
+    *size = ud->size;
+    return ud->data;
+}
+
+c11_sv py_tosv(py_Ref self) {
+    c11_string* ud = pk_tostr(self);
+    return c11_string__sv(ud);
+}
+
+unsigned char* py_tobytes(py_Ref self, int* size) {
+    assert(self->type == tp_bytes);
+    c11_bytes* ud = PyObject__userdata(self->_obj);
+    *size = ud->size;
+    return ud->data;
+}
+
+void py_bytes_resize(py_Ref self, int size) {
+    assert(self->type == tp_bytes);
+    c11_bytes* ud = PyObject__userdata(self->_obj);
+    if(size > ud->size) c11__abort("bytes can only be resized down: %d > %d", ud->size, size);
+    ud->size = size;
+}

+ 48 - 60
src/public/values.c → src/public/ValueCreation.c

@@ -32,6 +32,54 @@ void py_newbool(py_OutRef out, bool val) {
     out->_bool = val;
 }
 
+void py_newstr(py_OutRef out, const char* data) { py_newstrv(out, (c11_sv){data, strlen(data)}); }
+
+char* py_newstrn(py_OutRef out, int size) {
+    if(size < 16) {
+        out->type = tp_str;
+        out->is_ptr = false;
+        c11_string* ud = (c11_string*)(&out->extra);
+        c11_string__ctor3(ud, size);
+        return ud->data;
+    }
+    ManagedHeap* heap = &pk_current_vm->heap;
+    int total_size = sizeof(c11_string) + size + 1;
+    PyObject* obj = ManagedHeap__gcnew(heap, tp_str, 0, total_size);
+    c11_string* ud = PyObject__userdata(obj);
+    c11_string__ctor3(ud, size);
+    out->type = tp_str;
+    out->is_ptr = true;
+    out->_obj = obj;
+    return ud->data;
+}
+
+void py_newstrv(py_OutRef out, c11_sv sv) {
+    char* data = py_newstrn(out, sv.size);
+    memcpy(data, sv.data, sv.size);
+}
+
+void py_newfstr(py_OutRef out, const char* fmt, ...) {
+    c11_sbuf buf;
+    c11_sbuf__ctor(&buf);
+    va_list args;
+    va_start(args, fmt);
+    pk_vsprintf(&buf, fmt, args);
+    va_end(args);
+    c11_sbuf__py_submit(&buf, out);
+}
+
+unsigned char* py_newbytes(py_OutRef out, int size) {
+    ManagedHeap* heap = &pk_current_vm->heap;
+    // 4 bytes size + data
+    PyObject* obj = ManagedHeap__gcnew(heap, tp_bytes, 0, sizeof(c11_bytes) + size);
+    c11_bytes* ud = PyObject__userdata(obj);
+    ud->size = size;
+    out->type = tp_bytes;
+    out->is_ptr = true;
+    out->_obj = obj;
+    return ud->data;
+}
+
 void py_newnone(py_OutRef out) {
     out->type = tp_NoneType;
     out->is_ptr = false;
@@ -58,66 +106,6 @@ void py_newnativefunc(py_OutRef out, py_CFunction f) {
     out->_cfunc = f;
 }
 
-void py_bindmethod(py_Type type, const char* name, py_CFunction f) {
-    py_TValue tmp;
-    py_newnativefunc(&tmp, f);
-    py_setdict(py_tpobject(type), py_name(name), &tmp);
-}
-
-void py_bindstaticmethod(py_Type type, const char* name, py_CFunction f) {
-    py_TValue tmp;
-    py_newnativefunc(&tmp, f);
-    bool ok = py_tpcall(tp_staticmethod, 1, &tmp);
-    if(!ok) {
-        py_printexc();
-        c11__abort("py_bindstaticmethod(): failed to create staticmethod");
-    }
-    py_setdict(py_tpobject(type), py_name(name), py_retval());
-}
-
-void py_bindfunc(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_bindproperty(py_Type type, const char* name, py_CFunction getter, py_CFunction setter) {
-    py_TValue tmp;
-    py_newobject(&tmp, tp_property, 2, 0);
-    py_newnativefunc(py_getslot(&tmp, 0), getter);
-    if(setter) {
-        py_newnativefunc(py_getslot(&tmp, 1), setter);
-    } else {
-        py_setslot(&tmp, 1, py_None());
-    }
-    py_setdict(py_tpobject(type), py_name(name), &tmp);
-}
-
-void py_bindmagic(py_Type type, py_Name name, py_CFunction f) {
-    py_Ref tmp = py_emplacedict(py_tpobject(type), name);
-    py_newnativefunc(tmp, f);
-}
-
-void py_bind(py_Ref obj, const char* sig, py_CFunction f) {
-    py_Ref tmp = py_pushtmp();
-    py_Name name = py_newfunction(tmp, sig, f, NULL, 0);
-    py_setdict(obj, name, tmp);
-    py_pop();
-}
-
-void py_macrobind(const char* sig, py_CFunction f) {
-    py_Ref tmp = py_pushtmp();
-    py_Name name = py_newfunction(tmp, sig, f, NULL, 0);
-    NameDict__set(&pk_current_vm->compile_time_funcs, name, tmp);
-    py_pop();
-}
-
-py_ItemRef py_macroget(py_Name name) {
-    NameDict* d = &pk_current_vm->compile_time_funcs;
-    if(d->length == 0) return NULL;
-    return NameDict__try_get(d, name);
-}
-
 py_Name py_newfunction(py_OutRef out,
                        const char* sig,
                        py_CFunction f,

+ 0 - 45
src/public/typecast.c

@@ -1,45 +0,0 @@
-#include "pocketpy/objects/base.h"
-#include "pocketpy/pocketpy.h"
-
-#include "pocketpy/objects/object.h"
-#include "pocketpy/interpreter/vm.h"
-
-PK_INLINE bool py_istype(py_Ref self, py_Type type) { return self->type == type; }
-
-bool py_checktype(py_Ref self, py_Type type) {
-    if(self->type == type) return true;
-    return TypeError("expected '%t', got '%t'", type, self->type);
-}
-
-bool py_checkinstance(py_Ref self, py_Type type) {
-    if(py_isinstance(self, type)) return true;
-    return TypeError("expected '%t' or its subclass, got '%t'", type, self->type);
-}
-
-bool py_isinstance(py_Ref obj, py_Type type) { return py_issubclass(obj->type, type); }
-
-bool py_issubclass(py_Type derived, py_Type base) {
-    assert(derived != 0 && base != 0);
-    py_TypeInfo* derived_ti = pk_typeinfo(derived);
-    py_TypeInfo* base_ti = pk_typeinfo(base);
-    do {
-        if(derived_ti == base_ti) return true;
-        derived_ti = derived_ti->base_ti;
-    } while(derived_ti);
-    return false;
-}
-
-py_Type py_typeof(py_Ref self) { return self->type; }
-
-py_Type py_gettype(const char* module, py_Name name) {
-    py_Ref mod;
-    if(module != NULL) {
-        mod = py_getmodule(module);
-        if(!mod) return tp_nil;
-    } else {
-        mod = pk_current_vm->builtins;
-    }
-    py_Ref object = py_getdict(mod, name);
-    if(object && py_istype(object, tp_type)) return py_totype(object);
-    return tp_nil;
-}