From 35f973059c60d76b652f2f9cc6b271bc14d1e824 Mon Sep 17 00:00:00 2001 From: blueloveTH Date: Wed, 13 Nov 2024 23:17:54 +0800 Subject: [PATCH] add `TValue[T]` --- include/pocketpy/interpreter/modules.h | 3 +- include/typings/pkpy.pyi | 7 +++ src/interpreter/vm.c | 1 + src/modules/pkpy.c | 69 ++++++++++++++++++++++++++ src/public/internal.c | 32 +++++++----- src/public/py_ops.c | 20 ++++---- tests/99_extras.py | 18 ++++++- 7 files changed, 125 insertions(+), 25 deletions(-) create mode 100644 include/typings/pkpy.pyi create mode 100644 src/modules/pkpy.c diff --git a/include/pocketpy/interpreter/modules.h b/include/pocketpy/interpreter/modules.h index c9657468..1b86291b 100644 --- a/include/pocketpy/interpreter/modules.h +++ b/include/pocketpy/interpreter/modules.h @@ -14,4 +14,5 @@ void pk__add_module_enum(); void pk__add_module_linalg(); void pk__add_module_array2d(); -void pk__add_module_conio(); \ No newline at end of file +void pk__add_module_conio(); +void pk__add_module_pkpy(); \ No newline at end of file diff --git a/include/typings/pkpy.pyi b/include/typings/pkpy.pyi new file mode 100644 index 00000000..e0e018e6 --- /dev/null +++ b/include/typings/pkpy.pyi @@ -0,0 +1,7 @@ +from typing import Self + +class TValue[T]: + def __new__(cls, value: T) -> Self: ... + + @property + def value(self) -> T: ... diff --git a/src/interpreter/vm.c b/src/interpreter/vm.c index e1708aae..0fc431f8 100644 --- a/src/interpreter/vm.c +++ b/src/interpreter/vm.c @@ -217,6 +217,7 @@ void VM__ctor(VM* self) { pk__add_module_enum(); pk__add_module_conio(); + pk__add_module_pkpy(); // add python builtins do { diff --git a/src/modules/pkpy.c b/src/modules/pkpy.c new file mode 100644 index 00000000..104c1219 --- /dev/null +++ b/src/modules/pkpy.c @@ -0,0 +1,69 @@ +#include "pocketpy/pocketpy.h" + +#include "pocketpy/common/utils.h" +#include "pocketpy/objects/object.h" +#include "pocketpy/common/sstream.h" +#include "pocketpy/interpreter/vm.h" + +#define DEF_TVALUE_METHODS(T, Field) \ + static bool TValue_##T##__new__(int argc, py_Ref argv) { \ + PY_CHECK_ARGC(2); \ + PY_CHECK_ARG_TYPE(0, tp_type); \ + PY_CHECK_ARG_TYPE(1, tp_##T); \ + *py_retval() = (py_TValue){ \ + .type = py_totype(&argv[0]), \ + .is_ptr = false, \ + .Field = py_to##T(&argv[1]), \ + }; \ + return true; \ + } \ + static bool TValue_##T##_value(int argc, py_Ref argv) { \ + PY_CHECK_ARGC(1); \ + py_new##T(py_retval(), argv->Field); \ + return true; \ + } \ + static bool TValue_##T##__repr__(int argc, py_Ref argv) { \ + PY_CHECK_ARGC(1); \ + py_newstr(py_retval(), ""); \ + return true; \ + } + +DEF_TVALUE_METHODS(int, _i64) +DEF_TVALUE_METHODS(float, _f64) +DEF_TVALUE_METHODS(vec2, _vec2) +DEF_TVALUE_METHODS(vec2i, _vec2i) + +void pk__add_module_pkpy() { + py_Ref mod = py_newmodule("pkpy"); + + py_Type ttype; + py_Ref TValue_dict = py_pushtmp(); + py_newdict(TValue_dict); + + ttype = pk_newtype("TValue_int", tp_object, mod, NULL, false, false); + py_bindmagic(ttype, __new__, TValue_int__new__); + py_bindmagic(ttype, __repr__, TValue_int__repr__); + py_bindproperty(ttype, "value", TValue_int_value, NULL); + py_dict_setitem(TValue_dict, py_tpobject(tp_int), py_tpobject(ttype)); + + ttype = pk_newtype("TValue_float", tp_object, mod, NULL, false, false); + py_bindmagic(ttype, __new__, TValue_float__new__); + py_bindmagic(ttype, __repr__, TValue_float__repr__); + py_bindproperty(ttype, "value", TValue_float_value, NULL); + py_dict_setitem(TValue_dict, py_tpobject(tp_float), py_tpobject(ttype)); + + ttype = pk_newtype("TValue_vec2", tp_object, mod, NULL, false, false); + py_bindmagic(ttype, __new__, TValue_vec2__new__); + py_bindmagic(ttype, __repr__, TValue_vec2__repr__); + py_bindproperty(ttype, "value", TValue_vec2_value, NULL); + py_dict_setitem(TValue_dict, py_tpobject(tp_vec2), py_tpobject(ttype)); + + ttype = pk_newtype("TValue_vec2i", tp_object, mod, NULL, false, false); + py_bindmagic(ttype, __new__, TValue_vec2i__new__); + py_bindmagic(ttype, __repr__, TValue_vec2i__repr__); + py_bindproperty(ttype, "value", TValue_vec2i_value, NULL); + py_dict_setitem(TValue_dict, py_tpobject(tp_vec2i), py_tpobject(ttype)); + + py_setdict(mod, py_name("TValue"), TValue_dict); + py_pop(); +} \ No newline at end of file diff --git a/src/public/internal.c b/src/public/internal.c index 051f446b..fbdefbbc 100644 --- a/src/public/internal.c +++ b/src/public/internal.c @@ -12,7 +12,7 @@ static VM* pk_all_vm[16]; static py_TValue _True, _False, _None, _NIL; void py_initialize() { - if(pk_current_vm){ + if(pk_current_vm) { // c11__abort("py_initialize() can only be called once!"); return; } @@ -31,8 +31,11 @@ void py_initialize() { } 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() { @@ -59,7 +62,7 @@ void py_switchvm(int index) { pk_current_vm = pk_all_vm[index] = malloc(sizeof(VM)); memset(pk_current_vm, 0, sizeof(VM)); VM__ctor(pk_all_vm[index]); - }else{ + } else { pk_current_vm = pk_all_vm[index]; } } @@ -78,13 +81,9 @@ int py_currentvm() { return -1; } -void* py_getvmctx(){ - return pk_current_vm->ctx; -} +void* py_getvmctx() { return pk_current_vm->ctx; } -void py_setvmctx(void* ctx){ - pk_current_vm->ctx = ctx; -} +void py_setvmctx(void* ctx) { pk_current_vm->ctx = ctx; } void py_sys_setargv(int argc, char** argv) { py_GlobalRef sys = py_getmodule("sys"); @@ -161,11 +160,21 @@ bool py_pushmethod(py_Name name) { bool pk_loadmethod(py_StackRef self, py_Name name) { // NOTE: `out` and `out_self` may overlap with `self` + py_Type type; - if(name == __new__ && py_istype(self, tp_type)) { + if(name == __new__) { // __new__ acts like a @staticmethod - // T.__new__(...) - py_Ref cls_var = py_tpfindmagic(py_totype(self), name); + if(py_istype(self, tp_type)) { + // T.__new__(...) + type = py_totype(self); + } else if(py_istype(self, tp_super)) { + // super().__new__(...) + type = *(py_Type*)py_touserdata(self); + } else { + // invalid usage of `__new__` + return false; + } + py_Ref cls_var = py_tpfindmagic(type, name); if(cls_var) { self[0] = *cls_var; self[1] = *py_NIL(); @@ -174,7 +183,6 @@ bool pk_loadmethod(py_StackRef self, py_Name name) { return false; } - py_Type type; // handle super() proxy if(py_istype(self, tp_super)) { type = *(py_Type*)py_touserdata(self); diff --git a/src/public/py_ops.c b/src/public/py_ops.c index ace7b388..dc6b385f 100644 --- a/src/public/py_ops.c +++ b/src/public/py_ops.c @@ -87,11 +87,11 @@ int py_next(py_Ref val) { bool py_getattr(py_Ref self, py_Name name) { // https://docs.python.org/3/howto/descriptor.html#invocation-from-an-instance py_Type type = self->type; - // handle super() proxy - if(py_istype(self, tp_super)) { - self = py_getslot(self, 0); - type = *(py_Type*)py_touserdata(self); - } + // handle super() proxy (disabled) + // if(py_istype(self, tp_super)) { + // self = py_getslot(self, 0); + // type = *(py_Type*)py_touserdata(self); + // } py_Ref cls_var = py_tpfindname(type, name); if(cls_var) { @@ -183,11 +183,11 @@ bool py_getattr(py_Ref self, py_Name name) { bool py_setattr(py_Ref self, py_Name name, py_Ref val) { py_Type type = self->type; - // handle super() proxy - if(py_istype(self, tp_super)) { - self = py_getslot(self, 0); - type = *(py_Type*)py_touserdata(self); - } + // handle super() proxy (disabled) + // if(py_istype(self, tp_super)) { + // self = py_getslot(self, 0); + // type = *(py_Type*)py_touserdata(self); + // } py_Ref cls_var = py_tpfindname(type, name); if(cls_var) { diff --git a/tests/99_extras.py b/tests/99_extras.py index 0f421f8b..7a3bd3e5 100644 --- a/tests/99_extras.py +++ b/tests/99_extras.py @@ -6,7 +6,7 @@ except ValueError: pass # test some python magics -class A: +class TestMagics: def __init__(self): self.d = {} @@ -22,7 +22,7 @@ class A: def __delitem__(self, index): del self.d[index] -a = A() +a = TestMagics() a['1'] = 3 assert '1' in a assert '2' not in a @@ -72,7 +72,21 @@ assert int(Number()) == 2 assert round(Number()) == tuple() assert round(Number(), 1) == (1,) +class Z: + def __new__(cls, x): + return cls, x +class B(Z): + def __new__(cls, x): + return super().__new__(cls, x) +assert Z(1) == (Z, 1) +assert B(1) == (B, 1) +from pkpy import TValue + +class fixed(TValue[int]): + def __new__(cls, value: str): + return super().__new__(cls, int(value)) +assert fixed('123').value == 123