Compare commits

...

2 Commits

Author SHA1 Message Date
blueloveTH
f12a379760 add maxlen for deque 2025-09-08 20:07:54 +08:00
blueloveTH
d3d61dde0c refactor code 2025-09-08 15:41:44 +08:00
36 changed files with 1356 additions and 1283 deletions

View File

@ -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,184 +256,6 @@ 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);
/// 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);
/************* Type 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.
PK_API void* py_totrivial(py_Ref);
/// Convert a `float` object in python to `double`.
PK_API py_f64 py_tofloat(py_Ref);
/// Cast a `int` or `float` object in python to `double`.
/// If successful, return true and set the value to `out`.
/// Otherwise, return false and raise `TypeError`.
PK_API bool py_castfloat(py_Ref, py_f64* out) PY_RAISE;
/// 32-bit version of `py_castfloat`.
PK_API bool py_castfloat32(py_Ref, float* out) PY_RAISE;
/// Cast a `int` object in python to `int64_t`.
PK_API bool py_castint(py_Ref, py_i64* out) PY_RAISE;
/// Convert a `bool` object in python to `bool`.
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 `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.
PK_API const char* py_tostrn(py_Ref, int* size);
/// Convert a `str` object in python to `c11_sv`.
PK_API c11_sv py_tosv(py_Ref);
/// Convert a `bytes` object in python to char array.
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)
/// 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);
/// Check if the object is exactly the given type.
PK_API bool py_istype(py_Ref, py_Type);
/// 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 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);
/// Search the magic method from the given type to the base type.
/// Return `NULL` if not found.
PK_API py_GlobalRef py_tpfindmagic(py_Type, py_Name name);
/// Search the name from the given type to the base type.
/// Return `NULL` if not found.
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.
PK_API void py_tphookattributes(py_Type type,
bool (*getattribute)(py_Ref self, py_Name name) PY_RAISE PY_RETURN,
bool (*setattribute)(py_Ref self, py_Name name, py_Ref val)
PY_RAISE PY_RETURN,
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_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 *************/
/// 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);
#define py_r0() py_getreg(0)
#define py_r1() py_getreg(1)
#define py_r2() py_getreg(2)
#define py_r3() py_getreg(3)
#define py_r4() py_getreg(4)
#define py_r5() py_getreg(5)
#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);
/// Set an item to the object's `__dict__`.
PK_API void py_setdict(py_Ref self, py_Name name, py_Ref val);
/// Delete an item from the object's `__dict__`.
/// Return `true` if the deletion is successful.
PK_API bool py_deldict(py_Ref self, py_Name name);
/// Prepare an insertion to the object's `__dict__`.
PK_API py_ItemRef py_emplacedict(py_Ref self, py_Name name);
/// Apply a function to all items in the object's `__dict__`.
/// Return `true` if the function is successful for all items.
/// NOTE: Be careful if `f` modifies the object's `__dict__`.
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);
/************* 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 *************/
/// Bind a function to the object via "decl-based" style.
@ -487,6 +287,225 @@ 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);
/************* 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 (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);
/// Cast a `int` or `float` object in python to `double`.
/// If successful, return true and set the value to `out`.
/// Otherwise, return false and raise `TypeError`.
PK_API bool py_castfloat(py_Ref, py_f64* out) PY_RAISE;
/// 32-bit version of `py_castfloat`.
PK_API bool py_castfloat32(py_Ref, float* out) PY_RAISE;
/// Cast a `int` object in python to `int64_t`.
PK_API bool py_castint(py_Ref, py_i64* out) PY_RAISE;
/// Convert a `bool` object in python to `bool`.
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.
PK_API const char* py_tostrn(py_Ref, int* size);
/// Convert a `str` object in python to `c11_sv`.
PK_API c11_sv py_tosv(py_Ref);
/// Convert a `bytes` object in python to char array.
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);
/************* Type System *************/
/// 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);
/// Search the magic method from the given type to the base type.
/// Return `NULL` if not found.
PK_API py_GlobalRef py_tpfindmagic(py_Type, py_Name name);
/// Search the name from the given type to the base type.
/// Return `NULL` if not found.
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);
/// Disable the type for subclassing.
PK_API void py_tpsetfinal(py_Type type);
/// Set attribute hooks for the given type.
PK_API void py_tphookattributes(py_Type type,
bool (*getattribute)(py_Ref self, py_Name name) PY_RAISE PY_RETURN,
bool (*setattribute)(py_Ref self, py_Name name, py_Ref val)
PY_RAISE PY_RETURN,
bool (*delattribute)(py_Ref self, py_Name name) PY_RAISE,
bool (*getunboundmethod)(py_Ref self, py_Name name) PY_RETURN);
#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)
/************* 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)
#define py_r2() py_getreg(2)
#define py_r3() py_getreg(3)
#define py_r4() py_getreg(4)
#define py_r5() py_getreg(5)
#define py_r6() py_getreg(6)
#define py_r7() py_getreg(7)
/// 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);
/// Set an item to the object's `__dict__`.
PK_API void py_setdict(py_Ref self, py_Name name, py_Ref val);
/// Delete an item from the object's `__dict__`.
/// Return `true` if the deletion is successful.
PK_API bool py_deldict(py_Ref self, py_Name name);
/// Prepare an insertion to the object's `__dict__`.
PK_API py_ItemRef py_emplacedict(py_Ref self, py_Name name);
/// Apply a function to all items in the object's `__dict__`.
/// Return `true` if the function is successful for all items.
/// NOTE: Be careful if `f` modifies the object's `__dict__`.
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);
/************* Stack Ops *************/
/// 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 *************/
/************* Debugger *************/
/// 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();
/************* 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.

View File

@ -27,20 +27,29 @@ class defaultdict(dict):
class deque[T]:
_data: list[T]
_head: int
_tail: int
_maxlen: int | None
_capacity: int
_data: list[T]
def __init__(self, iterable: Iterable[T] = None, maxlen: int | None = None):
if maxlen is not None:
assert maxlen > 0
def __init__(self, iterable: Iterable[T] = None):
self._data = [None] * 8 # type: ignore
self._head = 0
self._tail = 0
self._capacity = len(self._data)
self._maxlen = maxlen
self._capacity = 8 if maxlen is None else maxlen + 1
self._data = [None] * self._capacity # type: ignore
if iterable is not None:
self.extend(iterable)
@property
def maxlen(self) -> int | None:
return self._maxlen
def __resize_2x(self):
backup = list(self)
self._capacity *= 2
@ -51,19 +60,25 @@ class deque[T]:
self._data.extend([None] * (self._capacity - len(backup)))
def append(self, x: T):
if (self._tail + 1) % self._capacity == self._head:
if self._maxlen is None:
self.__resize_2x()
else:
self.popleft()
self._data[self._tail] = x
self._tail = (self._tail + 1) % self._capacity
if (self._tail + 1) % self._capacity == self._head:
self.__resize_2x()
def appendleft(self, x: T):
if (self._tail + 1) % self._capacity == self._head:
if self._maxlen is None:
self.__resize_2x()
else:
self.pop()
self._head = (self._head - 1) % self._capacity
self._data[self._head] = x
if (self._tail + 1) % self._capacity == self._head:
self.__resize_2x()
def copy(self):
return deque(self)
return deque(self, maxlen=self.maxlen)
def count(self, x: T) -> int:
n = 0
@ -84,12 +99,15 @@ class deque[T]:
if self._head == self._tail:
raise IndexError("pop from an empty deque")
self._tail = (self._tail - 1) % self._capacity
return self._data[self._tail]
x = self._data[self._tail]
self._data[self._tail] = None
return x
def popleft(self) -> T:
if self._head == self._tail:
raise IndexError("pop from an empty deque")
x = self._data[self._head]
self._data[self._head] = None
self._head = (self._head + 1) % self._capacity
return x
@ -144,5 +162,7 @@ class deque[T]:
return not self == other
def __repr__(self) -> str:
return f"deque({list(self)!r})"
if self.maxlen is None:
return f"deque({list(self)!r})"
return f"deque({list(self)!r}, maxlen={self.maxlen})"

View File

@ -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

File diff suppressed because one or more lines are too long

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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++) {

View File

@ -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);
}

View File

@ -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
src/public/Bindings.c Normal file
View File

@ -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);
}

View File

@ -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;
}

View File

@ -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_Ref py_getbuiltin(py_Name name) { return py_getdict(pk_current_vm->builtins, name); }
py_GlobalRef py_inspect_currentmodule() {
py_Frame* frame = pk_current_vm->top_frame;
if(!frame) return NULL;
return frame->module;
}
py_Ref py_getglobal(py_Name name) { return py_getdict(pk_current_vm->main, 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_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
src/public/FrameOps.c Normal file
View File

@ -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
src/public/GlobalSetup.c Normal file
View File

@ -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
src/public/Inspection.c Normal file
View File

@ -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
src/public/ModuleSystem.c Normal file
View File

@ -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;
}

View File

@ -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());
}

View File

@ -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) {

View File

@ -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());
}

View File

@ -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();
}
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() {
PK_INLINE void py_push(py_Ref src) {
VM* vm = pk_current_vm;
VM__dtor(vm);
memset(vm, 0, sizeof(VM));
VM__ctor(vm);
*vm->stack.sp++ = *src;
}
void py_resetallvm() {
for(int i = 0; i < 16; i++) {
py_switchvm(i);
py_resetvm();
}
py_switchvm(0);
PK_INLINE void py_pushnil() {
VM* vm = pk_current_vm;
py_newnil(vm->stack.sp++);
}
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_pushnone() {
VM* vm = pk_current_vm;
py_newnone(vm->stack.sp++);
}
void* py_getvmctx() { return pk_current_vm->ctx; }
void py_setvmctx(void* ctx) { pk_current_vm->ctx = ctx; }
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 void py_pushname(py_Name name) {
VM* vm = pk_current_vm;
py_newint(vm->stack.sp++, (uintptr_t)name);
}
py_Callbacks* py_callbacks() { return &pk_current_vm->callbacks; }
PK_INLINE void py_pop() {
VM* vm = pk_current_vm;
vm->stack.sp--;
}
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 void py_shrink(int n) {
VM* vm = pk_current_vm;
vm->stack.sp -= n;
}
PK_INLINE py_Ref py_pushtmp() {
VM* vm = pk_current_vm;
return vm->stack.sp++;
}
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;
}
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;
}
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
src/public/TypeSystem.c Normal file
View File

@ -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;
}

View File

@ -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;
}

View File

@ -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,

View File

@ -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;
}

View File

@ -516,3 +516,31 @@ d = deque()
for i in range(100):
d.append(1)
gc.collect()
# test maxlen
q = deque(maxlen=3)
assertEqual(q.maxlen, 3)
q.append(1)
q.append(2)
q.append(3)
assertEqual(list(q), [1, 2, 3])
assertEqual(q._data, [1, 2, 3, None])
q.append(4)
assertEqual(list(q), [2, 3, 4])
assertEqual(q._data, [None, 2, 3, 4])
q.appendleft(1)
assertEqual(list(q), [1, 2, 3])
assertEqual(q._data, [1, 2, 3, None])
q.appendleft(0)
assertEqual(list(q), [0, 1, 2])
assertEqual(q._data, [1, 2, None, 0])
q.pop()
assertEqual(list(q), [0, 1])
assertEqual(q._data, [1, None, None, 0])
assertEqual(len(q), 2)
assertEqual(q._capacity, 4)
q.popleft()
assertEqual(list(q), [1])
assertEqual(q._data, [1, None, None, None])