Merge branch 'pocketpy:main' into gsoc-2025-debugger

This commit is contained in:
lightovernight 2025-09-09 22:44:39 +08:00 committed by GitHub
commit 789b9a7fb6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
39 changed files with 1360 additions and 1285 deletions

View File

@ -30,7 +30,7 @@ pocketpy provides a C-API `py_debugger_waitforattach`,
which starts a debug server and waits for the VSCode extension to attach. which starts a debug server and waits for the VSCode extension to attach.
When the debugger is attached, the program will continue to run. When the debugger is attached, the program will continue to run.
+ If you are using pocketpy's standalone executable `main.exe`, you can pass `--debug` flag to it. This will automatically call `py_debugger_waitforattach("localhost", 6110)` before running your program. + If you are using pocketpy's standalone executable `main.exe`, you can pass `--debug` flag to it. This will automatically call `py_debugger_waitforattach("127.0.0.1", 6110)` before running your program.
+ If you are embedding pocketpy as a library, you need to call `py_debugger_waitforattach` manually in your C/C++ code. + If you are embedding pocketpy as a library, you need to call `py_debugger_waitforattach` manually in your C/C++ code.
## Configuration ## Configuration

View File

@ -3,7 +3,7 @@ output: .retype
url: https://pocketpy.dev url: https://pocketpy.dev
branding: branding:
title: pocketpy title: pocketpy
label: v2.1.1 label: v2.1.2
logo: "./static/logo.png" logo: "./static/logo.png"
favicon: "./static/logo.png" favicon: "./static/logo.png"
meta: meta:

View File

@ -123,14 +123,15 @@ PK_API void py_resetallvm();
PK_API void* py_getvmctx(); PK_API void* py_getvmctx();
/// Set the current VM context. This is used for user-defined data. /// Set the current VM context. This is used for user-defined data.
PK_API void py_setvmctx(void* ctx); 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. /// Set `sys.argv`. Used for storing command-line arguments.
PK_API void py_sys_setargv(int argc, char** argv); PK_API void py_sys_setargv(int argc, char** argv);
/// Set the trace function for the current VM. /// Set the trace function for the current VM.
PK_API void py_sys_settrace(py_TraceFunc func, bool reset); PK_API void py_sys_settrace(py_TraceFunc func, bool reset);
/// Invoke the garbage collector. /// Invoke the garbage collector.
PK_API int py_gc_collect(); PK_API int py_gc_collect();
/// Setup the callbacks for the current VM.
PK_API py_Callbacks* py_callbacks();
/// Wrapper for `PK_MALLOC(size)`. /// Wrapper for `PK_MALLOC(size)`.
PK_API void* py_malloc(size_t 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)`. /// Wrapper for `PK_FREE(ptr)`.
PK_API void py_free(void* ptr); PK_API void py_free(void* ptr);
/// Begin the watchdog with `timeout` in milliseconds. /// A shorthand for `True`.
/// `PK_ENABLE_WATCHDOG` must be defined to `1` to use this feature. PK_API py_GlobalRef py_True();
/// You need to call `py_watchdog_end()` later. /// A shorthand for `False`.
/// If `timeout` is reached, `TimeoutError` will be raised. PK_API py_GlobalRef py_False();
PK_API void py_watchdog_begin(py_i64 timeout); /// A shorthand for `None`.
/// Reset the watchdog. PK_API py_GlobalRef py_None();
PK_API void py_watchdog_end(); /// 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. /************* Frame Ops *************/
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);
/// Get the current source location of the frame. /// Get the current source location of the frame.
PK_API const char* py_Frame_sourceloc(py_Frame* frame, int* lineno); 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. /// Returns `NULL` if not available.
PK_API py_StackRef py_Frame_function(py_Frame* frame); 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. /// Run a source string.
/// @param source source string. /// @param source source string.
/// @param filename filename (for error messages). /// @param filename filename (for error messages).
@ -172,10 +179,8 @@ PK_API bool py_exec(const char* source,
const char* filename, const char* filename,
enum py_CompileMode mode, enum py_CompileMode mode,
py_Ref module) PY_RAISE PY_RETURN; py_Ref module) PY_RAISE PY_RETURN;
/// Evaluate a source string. Equivalent to `py_exec(source, "<string>", EVAL_MODE, module)`. /// 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; PK_API bool py_eval(const char* source, py_Ref module) PY_RAISE PY_RETURN;
/// Run a source string with smart interpretation. /// Run a source string with smart interpretation.
/// Example: /// Example:
/// `py_newstr(py_r0(), "abc");` /// `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`. /// `// res will be 3`.
PK_API bool py_smarteval(const char* source, py_Ref module, ...) PY_RAISE PY_RETURN; PK_API bool py_smarteval(const char* source, py_Ref module, ...) PY_RAISE PY_RETURN;
/// Compile a source string into a code object. /************* Value Creation *************/
/// 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();
/// Create an `int` object. /// Create an `int` object.
PK_API void py_newint(py_OutRef, py_i64); 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. /// Create a `nil` object. `nil` is an invalid representation of an object.
/// Don't use it unless you know what you are doing. /// Don't use it unless you know what you are doing.
PK_API void py_newnil(py_OutRef); 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. /// Create a `nativefunc` object.
PK_API void py_newnativefunc(py_OutRef, py_CFunction); PK_API void py_newnativefunc(py_OutRef, py_CFunction);
/// Create a `function` object. /// Create a `function` object.
@ -264,8 +235,15 @@ PK_API py_Name py_newfunction(py_OutRef out,
int slots); int slots);
/// Create a `boundmethod` object. /// Create a `boundmethod` object.
PK_API void py_newboundmethod(py_OutRef out, py_Ref self, py_Ref func); 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. /// Convert a null-terminated string to a name.
PK_API py_Name py_name(const char*); 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`. /// Convert a name to a `c11_sv`.
PK_API c11_sv py_name2sv(py_Name); 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 *************/ /************* Bindings *************/
/// Bind a function to the object via "decl-based" style. /// 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); py_bindproperty(py_Type type, const char* name, py_CFunction getter, py_CFunction setter);
/// Bind a magic method to type. /// Bind a magic method to type.
PK_API void py_bindmagic(py_Type type, py_Name name, py_CFunction f); 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) \ #define PY_CHECK_ARGC(n) \
if(argc != n) return TypeError("expected %d arguments, got %d", n, argc) 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_arg(i) (&argv[i])
#define py_assign(dst, src) *(dst) = *(src) #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. /// Perform a binary operation.
/// The result will be set to `py_retval()`. /// The result will be set to `py_retval()`.
/// The stack remains unchanged after the operation. /// 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; 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 /// lhs + rhs
PK_API bool py_binaryadd(py_Ref lhs, py_Ref rhs) PY_RAISE PY_RETURN; PK_API bool py_binaryadd(py_Ref lhs, py_Ref rhs) PY_RAISE PY_RETURN;
/// lhs - rhs /// 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; PK_API bool py_binaryxor(py_Ref lhs, py_Ref rhs) PY_RAISE PY_RETURN;
/// lhs @ rhs /// lhs @ rhs
PK_API bool py_binarymatmul(py_Ref lhs, py_Ref rhs) PY_RAISE PY_RETURN; 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. /// Python equivalent to `getattr(self, name)`.
/// `i` should be negative, e.g. (-1) means TOS. PK_API bool py_getattr(py_Ref self, py_Name name) PY_RAISE PY_RETURN;
PK_API py_StackRef py_peek(int i); /// Python equivalent to `setattr(self, name, val)`.
/// Push the object to the stack. PK_API bool py_setattr(py_Ref self, py_Name name, py_Ref val) PY_RAISE;
PK_API void py_push(py_Ref src); /// Python equivalent to `delattr(self, name)`.
/// Push a `nil` object to the stack. PK_API bool py_delattr(py_Ref self, py_Name name) PY_RAISE;
PK_API void py_pushnil(); /// Python equivalent to `self[key]`.
/// Push a `None` object to the stack. PK_API bool py_getitem(py_Ref self, py_Ref key) PY_RAISE PY_RETURN;
PK_API void py_pushnone(); /// Python equivalent to `self[key] = val`.
/// Push a `py_Name` to the stack. This is used for keyword arguments. PK_API bool py_setitem(py_Ref self, py_Ref key, py_Ref val) PY_RAISE;
PK_API void py_pushname(py_Name name); /// Python equivalent to `del self[key]`.
/// Pop an object from the stack. PK_API bool py_delitem(py_Ref self, py_Ref key) PY_RAISE;
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;
/************* Modules *************/ /************* Module System *************/
/// Create a new module.
PK_API py_GlobalRef py_newmodule(const char* path);
/// Get a module by path. /// Get a module by path.
PK_API py_GlobalRef py_getmodule(const char* 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. /// Reload an existing module.
PK_API bool py_importlib_reload(py_Ref module) PY_RAISE PY_RETURN; PK_API bool py_importlib_reload(py_Ref module) PY_RAISE PY_RETURN;
/// Import a module. /// Import a module.
/// The result will be set to `py_retval()`. /// The result will be set to `py_retval()`.
/// -1: error, 0: not found, 1: success /// -1: error, 0: not found, 1: success
PK_API int py_import(const char* path) PY_RAISE PY_RETURN; 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. /// Check if there is an unhandled exception.
PK_API bool py_checkexc(); PK_API bool py_checkexc();
/// Check if the unhandled exception is an instance of the given type. /// 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. /// Clear the unhandled exception.
/// @param p0 the unwinding point. Use `NULL` if not needed. /// @param p0 the unwinding point. Use `NULL` if not needed.
PK_API void py_clearexc(py_StackRef p0); 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 NameError(n) py_exception(tp_NameError, "name '%n' is not defined", (n))
#define TypeError(...) py_exception(tp_TypeError, __VA_ARGS__) #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", \ "cannot access local variable '%n' where it is not associated with a value", \
(n)) (n))
PK_API bool StopIteration() PY_RAISE;
PK_API bool KeyError(py_Ref key) 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 #if PK_ENABLE_OS
PK_API void py_debugger_waitforattach(const char* hostname, unsigned short port); PK_API void py_debugger_waitforattach(const char* hostname, unsigned short port);
PK_API bool py_debugger_isattached(); PK_API bool py_debugger_isattached();
PK_API void py_debugger_exceptionbreakpoint(py_Ref exc); 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 #else
#define py_debugger_waitforattach(hostname, port) #define py_debugger_waitforattach(hostname, port)
#define py_debugger_isattached() (false) #define py_debugger_isattached() (false)
#define py_debugger_exceptionbreakpoint(exc) #define py_debugger_exceptionbreakpoint(exc)
#define py_debugger_exit(exitCode) #define py_debugger_exit(code)
#endif #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_data(py_Ref self);
PK_API py_ObjectRef py_tuple_getitem(py_Ref self, int i); 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 void py_tuple_setitem(py_Ref self, int i, py_Ref val);
PK_API int py_tuple_len(py_Ref self); 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_data(py_Ref self);
PK_API py_ItemRef py_list_getitem(py_Ref self, int i); 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); 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_clear(py_Ref self);
PK_API void py_list_insert(py_Ref self, int i, py_Ref val); 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 /// -1: error, 0: not found, 1: found
PK_API int py_dict_getitem(py_Ref self, py_Ref key) PY_RAISE PY_RETURN; PK_API int py_dict_getitem(py_Ref self, py_Ref key) PY_RAISE PY_RETURN;
/// true: success, false: error /// true: success, false: error
@ -761,6 +727,14 @@ PK_API bool
/// noexcept /// noexcept
PK_API int py_dict_len(py_Ref self); 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 *************/ /************* random module *************/
PK_API void py_newRandom(py_OutRef out); PK_API void py_newRandom(py_OutRef out);
PK_API void py_Random_seed(py_Ref self, py_i64 seed); 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_mat3x3* py_tomat3x3(py_Ref self);
PK_API c11_color32 py_tocolor32(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 *************/ /************* Others *************/
/// An utility function to read a line from stdin for REPL. /// An utility function to read a line from stdin for REPL.

View File

@ -27,20 +27,29 @@ class defaultdict(dict):
class deque[T]: class deque[T]:
_data: list[T]
_head: int _head: int
_tail: int _tail: int
_maxlen: int | None
_capacity: int _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._head = 0
self._tail = 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: if iterable is not None:
self.extend(iterable) self.extend(iterable)
@property
def maxlen(self) -> int | None:
return self._maxlen
def __resize_2x(self): def __resize_2x(self):
backup = list(self) backup = list(self)
self._capacity *= 2 self._capacity *= 2
@ -51,19 +60,25 @@ class deque[T]:
self._data.extend([None] * (self._capacity - len(backup))) self._data.extend([None] * (self._capacity - len(backup)))
def append(self, x: T): 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._data[self._tail] = x
self._tail = (self._tail + 1) % self._capacity self._tail = (self._tail + 1) % self._capacity
if (self._tail + 1) % self._capacity == self._head:
self.__resize_2x()
def appendleft(self, x: T): 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._head = (self._head - 1) % self._capacity
self._data[self._head] = x self._data[self._head] = x
if (self._tail + 1) % self._capacity == self._head:
self.__resize_2x()
def copy(self): def copy(self):
return deque(self) return deque(self, maxlen=self.maxlen)
def count(self, x: T) -> int: def count(self, x: T) -> int:
n = 0 n = 0
@ -84,12 +99,15 @@ class deque[T]:
if self._head == self._tail: if self._head == self._tail:
raise IndexError("pop from an empty deque") raise IndexError("pop from an empty deque")
self._tail = (self._tail - 1) % self._capacity 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: def popleft(self) -> T:
if self._head == self._tail: if self._head == self._tail:
raise IndexError("pop from an empty deque") raise IndexError("pop from an empty deque")
x = self._data[self._head] x = self._data[self._head]
self._data[self._head] = None
self._head = (self._head + 1) % self._capacity self._head = (self._head + 1) % self._capacity
return x return x
@ -144,5 +162,7 @@ class deque[T]:
return not self == other return not self == other
def __repr__(self) -> str: def __repr__(self) -> str:
if self.maxlen is None:
return f"deque({list(self)!r})" 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/common/str.h"
#include "pocketpy/pocketpy.h" #include "pocketpy/pocketpy.h"
#include "pocketpy/common/utils.h"
#include "pocketpy/objects/object.h" #include "pocketpy/objects/object.h"
#include "pocketpy/interpreter/vm.h" #include "pocketpy/interpreter/vm.h"
#include "pocketpy/common/sstream.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) { c11_string* pk_tostr(py_Ref self) {
assert(self->type == tp_str); assert(self->type == tp_str);
if(!self->is_ptr) { 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) { static bool str__new__(int argc, py_Ref argv) {
assert(argc >= 1); assert(argc >= 1);
@ -673,18 +597,4 @@ py_Type pk_bytes__register() {
return type; 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 #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: { case OP_BUILD_SLICE: {
// [start, stop, step] // [start, stop, step]
py_TValue tmp; py_TValue tmp;
py_newslice(&tmp); py_ObjectRef slots = py_newslice(&tmp);
py_setslot(&tmp, 0, THIRD()); slots[0] = *THIRD();
py_setslot(&tmp, 1, SECOND()); slots[1] = *SECOND();
py_setslot(&tmp, 2, TOP()); slots[2] = *TOP();
STACK_SHRINK(3); STACK_SHRINK(3);
PUSH(&tmp); PUSH(&tmp);
DISPATCH(); DISPATCH();
@ -1298,57 +1298,6 @@ bool pk_stack_binaryop(VM* self, py_Name op, py_Name rop) {
rhs_t); 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) { static bool stack_format_object(VM* self, c11_sv spec) {
// format TOS via `spec` inplace // format TOS via `spec` inplace
// spec: '!r:.2f', '.2f' // spec: '!r:.2f', '.2f'
@ -1514,14 +1463,3 @@ static bool stack_format_object(VM* self, c11_sv spec) {
#undef INSERT_THIRD #undef INSERT_THIRD
#undef vectorcall_opcall #undef vectorcall_opcall
#undef RESET_CO_CACHE #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; loc.src = self->co->src;
return loc; 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; 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) { PK_INLINE py_TypeInfo* pk_typeinfo(py_Type type) {
#ifndef NDEBUG #ifndef NDEBUG
int length = pk_current_vm->types.length; int length = pk_current_vm->types.length;
if(type < 0 || type >= length) { if(type <= 0 || type >= length) {
c11__abort("type index %d is out of bounds [0, %d)", type, length); c11__abort("type index %d is out of bounds (0, %d)", type, length);
} }
#endif #endif
return c11__getitem(TypePointer, &pk_current_vm->types, type).ti; 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); 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/interpreter/vm.h"
#include "pocketpy/common/_generated.h" #include "pocketpy/common/_generated.h"
#include <ctype.h>
#include <math.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) { static bool builtins_exit(int argc, py_Ref argv) {
int code = 0; int code = 0;
@ -399,18 +209,6 @@ static bool builtins_issubclass(int argc, py_Ref argv) {
return true; 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) { static bool builtins_callable(int argc, py_Ref argv) {
PY_CHECK_ARGC(1); PY_CHECK_ARGC(1);
bool res = py_callable(py_arg(0)); bool res = py_callable(py_arg(0));
@ -519,16 +317,6 @@ static bool builtins_locals(int argc, py_Ref argv) {
return true; 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() { static void pk_push_special_locals() {
py_Frame* frame = pk_current_vm->top_frame; py_Frame* frame = pk_current_vm->top_frame;
if(!frame) { if(!frame) {
@ -622,70 +410,6 @@ static bool builtins_eval(int argc, py_Ref argv) {
return _builtins_execdyn("eval", argc, argv, EVAL_MODE); 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) { static bool builtins_compile(int argc, py_Ref argv) {
PY_CHECK_ARGC(3); PY_CHECK_ARGC(3);
for(int i = 0; i < 3; i++) { 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, "disable", gc_disable);
py_bindfunc(mod, "isenabled", gc_isenabled); 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); 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;
}

View File

@ -1089,7 +1089,7 @@ static bool color32_alpha_blend_STATIC(int argc, py_Ref argv) {
res.r = (unsigned char)(src.r * alpha + dst.r * (1 - alpha)); res.r = (unsigned char)(src.r * alpha + dst.r * (1 - alpha));
res.g = (unsigned char)(src.g * alpha + dst.g * (1 - alpha)); res.g = (unsigned char)(src.g * alpha + dst.g * (1 - alpha));
res.b = (unsigned char)(src.b * alpha + dst.b * (1 - alpha)); res.b = (unsigned char)(src.b * alpha + dst.b * (1 - alpha));
res.a = (unsigned char)(src.a * alpha + dst.a * (1 - alpha)); res.a = (unsigned char)(src.a + dst.a * (1 - alpha));
py_newcolor32(py_retval(), res); py_newcolor32(py_retval(), res);
return true; 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/interpreter/vm.h"
#include "pocketpy/compiler/compiler.h" #include "pocketpy/compiler/compiler.h"
#include <assert.h> #include <assert.h>
#include <ctype.h>
py_Type pk_code__register() { py_Type pk_code__register() {
py_Type type = pk_newtype("code", tp_object, NULL, (py_Dtor)CodeObject__dtor, false, true); py_Type type = pk_newtype("code", tp_object, NULL, (py_Dtor)CodeObject__dtor, false, true);
return type; return type;
} }
bool _py_compile(CodeObject* out, static bool _py_compile(CodeObject* out,
const char* source, const char* source,
const char* filename, const char* filename,
enum py_CompileMode mode, enum py_CompileMode mode,
@ -33,20 +34,6 @@ bool _py_compile(CodeObject* out,
return true; 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) { bool pk_exec(CodeObject* co, py_Ref module) {
VM* vm = pk_current_vm; VM* vm = pk_current_vm;
if(!module) module = vm->main; 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; 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) { bool py_exec(const char* source, const char* filename, enum py_CompileMode mode, py_Ref module) {
CodeObject co; CodeObject co;
if(!_py_compile(&co, source, filename, mode, false)) return false; if(!_py_compile(&co, source, filename, mode, false)) return false;
@ -103,3 +152,19 @@ bool py_exec(const char* source, const char* filename, enum py_CompileMode mode,
bool py_eval(const char* source, py_Ref module) { bool py_eval(const char* source, py_Ref module) {
return py_exec(source, "<string>", EVAL_MODE, 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" #include "pocketpy/interpreter/vm.h"
py_Ref py_getreg(int i) { return pk_current_vm->reg + i; } py_Ref py_getreg(int i) { return pk_current_vm->reg + i; }
void py_setreg(int i, py_Ref val) { pk_current_vm->reg[i] = *val; } 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) { PK_INLINE py_Ref py_getdict(py_Ref self, py_Name name) {
assert(self && self->is_ptr); assert(self && self->is_ptr);
return NameDict__try_get(PyObject__dict(self->_obj), name); 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); 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_ItemRef py_emplacedict(py_Ref self, py_Name name) {
py_setdict(self, name, py_NIL()); py_setdict(self, name, py_NIL());
return py_getdict(self, name); return py_getdict(self, name);
@ -41,11 +44,6 @@ void py_cleardict(py_Ref self) {
NameDict__clear(dict); 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) { py_Ref py_getslot(py_Ref self, int i) {
assert(self && self->is_ptr); assert(self && self->is_ptr);
assert(i >= 0 && i < self->_obj->slots); 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; PyObject__slots(self->_obj)[i] = *val;
} }
py_StackRef py_inspect_currentfunction() { py_Ref py_getbuiltin(py_Name name) { return py_getdict(pk_current_vm->builtins, name); }
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_Ref py_getglobal(py_Name name) { return py_getdict(pk_current_vm->main, name); }
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_setglobal(py_Name name, py_Ref val) { py_setdict(pk_current_vm->main, name, val); }
/* 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++;
}

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,29 +141,9 @@ py_Type pk_StopIteration__register() {
return type; 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) vm->stack.sp = p0;
}
static void c11_sbuf__write_exc(c11_sbuf* self, py_Ref exc) { static void c11_sbuf__write_exc(c11_sbuf* self, py_Ref exc) {
if(true) { c11_sbuf__write_cstr(self, "Traceback (most recent call last):\n"); } c11_sbuf__write_cstr(self, "Traceback (most recent call last):\n");
BaseException* ud = py_touserdata(exc); BaseException* ud = py_touserdata(exc);
for(int i = ud->stacktrace.length - 1; i >= 0; i--) { for(int i = ud->stacktrace.length - 1; i >= 0; i--) {
@ -210,6 +190,29 @@ char* safe_stringify_exception(py_Ref exc) {
return c11_strdup(message); 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() { void py_printexc() {
char* msg = py_formatexc(); char* msg = py_formatexc();
if(!msg) return; if(!msg) return;
@ -298,3 +301,9 @@ bool KeyError(py_Ref key) {
if(!ok) return false; if(!ok) return false;
return py_raise(py_retval()); 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/objects/object.h"
#include "pocketpy/interpreter/vm.h" #include "pocketpy/interpreter/vm.h"
void py_newslice(py_OutRef out) { py_ObjectRef py_newslice(py_OutRef out) {
VM* vm = pk_current_vm; VM* vm = pk_current_vm;
PyObject* obj = ManagedHeap__gcnew(&vm->heap, tp_slice, 3, 0); PyObject* obj = ManagedHeap__gcnew(&vm->heap, tp_slice, 3, 0);
out->type = tp_slice; out->type = tp_slice;
out->is_ptr = true; out->is_ptr = true;
out->_obj = obj; 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) { static bool slice__new__(int argc, py_Ref argv) {

View File

@ -4,6 +4,48 @@
#include "pocketpy/objects/base.h" #include "pocketpy/objects/base.h"
#include "pocketpy/pocketpy.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) { bool py_isidentical(py_Ref lhs, py_Ref rhs) {
if(lhs->type != rhs->type) return false; if(lhs->type != rhs->type) return false;
switch(lhs->type) { 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) { bool py_hash(py_Ref val, int64_t* out) {
py_TypeInfo* ti = pk_typeinfo(val->type); py_TypeInfo* ti = pk_typeinfo(val->type);
do { do {
@ -119,6 +184,20 @@ int py_next(py_Ref val) {
return -1; 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) { bool py_getattr(py_Ref self, py_Name name) {
// https://docs.python.org/3/howto/descriptor.html#invocation-from-an-instance // https://docs.python.org/3/howto/descriptor.html#invocation-from-an-instance
py_TypeInfo* ti = pk_typeinfo(self->type); py_TypeInfo* ti = pk_typeinfo(self->type);
@ -277,14 +356,3 @@ bool py_delitem(py_Ref self, py_Ref key) {
py_shrink(2); py_shrink(2);
return ok; 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/pocketpy.h"
#include "pocketpy/common/utils.h"
#include "pocketpy/common/name.h"
#include "pocketpy/interpreter/vm.h" #include "pocketpy/interpreter/vm.h"
_Thread_local VM* pk_current_vm; PK_INLINE py_Ref py_peek(int i) {
assert(i <= 0);
static bool pk_initialized; return pk_current_vm->stack.sp + i;
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(); PK_INLINE void py_push(py_Ref src) {
// 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_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() {
VM* vm = pk_current_vm; VM* vm = pk_current_vm;
VM__dtor(vm); *vm->stack.sp++ = *src;
memset(vm, 0, sizeof(VM));
VM__ctor(vm);
} }
void py_resetallvm() { PK_INLINE void py_pushnil() {
for(int i = 0; i < 16; i++) { VM* vm = pk_current_vm;
py_switchvm(i); py_newnil(vm->stack.sp++);
py_resetvm();
}
py_switchvm(0);
} }
int py_currentvm() { PK_INLINE void py_pushnone() {
for(int i = 0; i < 16; i++) { VM* vm = pk_current_vm;
if(pk_all_vm[i] == pk_current_vm) return i; py_newnone(vm->stack.sp++);
}
return -1;
} }
void* py_getvmctx() { return pk_current_vm->ctx; } PK_INLINE void py_pushname(py_Name name) {
VM* vm = pk_current_vm;
void py_setvmctx(void* ctx) { pk_current_vm->ctx = ctx; } py_newint(vm->stack.sp++, (uintptr_t)name);
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]);
}
} }
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) { PK_INLINE void py_shrink(int n) {
const static char* OP_NAMES[] = { VM* vm = pk_current_vm;
#define OPCODE(name) #name, vm->stack.sp -= n;
#include "pocketpy/xmacros/opcodes.h" }
#undef OPCODE
}; PK_INLINE py_Ref py_pushtmp() {
return OP_NAMES[op]; 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) { 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 #endif
bool py_vectorcall(uint16_t argc, uint16_t kwargc) { bool py_tpcall(py_Type type, int argc, py_Ref argv) {
return VM__vectorcall(pk_current_vm, argc, kwargc, false) != RES_ERROR; return py_call(py_tpobject(type), argc, argv);
} }
PK_INLINE py_Ref py_retval() { return &pk_current_vm->last_retval; } bool py_binaryop(py_Ref lhs, py_Ref rhs, py_Name op, py_Name rop) {
py_push(lhs);
bool py_pushmethod(py_Name name) { py_push(rhs);
bool ok = pk_loadmethod(py_peek(-1), name); bool ok = pk_stack_binaryop(pk_current_vm, op, rop);
if(ok) pk_current_vm->stack.sp++; py_shrink(2);
return ok; return ok;
} }
@ -267,10 +188,6 @@ bool pk_loadmethod(py_StackRef self, py_Name name) {
return false; 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) { bool pk_callmagic(py_Name name, int argc, py_Ref argv) {
assert(argc >= 1); assert(argc >= 1);
// assert(py_ismagicname(name)); // 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); if(!tmp) return AttributeError(argv, name);
return py_call(tmp, argc, argv); 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/objects/object.h"
#include "pocketpy/interpreter/vm.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); assert(self->type == tp_int);
return self->_i64; return self->_i64;
} }
@ -55,3 +55,30 @@ void* py_touserdata(py_Ref self) {
assert(self && self->is_ptr); assert(self && self->is_ptr);
return PyObject__userdata(self->_obj); 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; 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) { void py_newnone(py_OutRef out) {
out->type = tp_NoneType; out->type = tp_NoneType;
out->is_ptr = false; out->is_ptr = false;
@ -58,66 +106,6 @@ void py_newnativefunc(py_OutRef out, py_CFunction f) {
out->_cfunc = 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, py_Name py_newfunction(py_OutRef out,
const char* sig, const char* sig,
py_CFunction f, 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): for i in range(100):
d.append(1) d.append(1)
gc.collect() 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])