From ef9c5c98cc0ef6f0377302903981620237e7e486 Mon Sep 17 00:00:00 2001 From: blueloveTH Date: Fri, 16 Aug 2024 15:53:10 +0800 Subject: [PATCH] add `enum` module --- include/pocketpy/common/_generated.h | 1 - include/pocketpy/interpreter/modules.h | 3 +- include/pocketpy/interpreter/vm.h | 1 - include/pocketpy/pocketpy.h | 4 ++ python/_enum.py | 11 ---- src/common/_generated.c | 1 - src/interpreter/vm.c | 2 +- src/modules/enum.c | 91 ++++++++++++++++++++++++++ src/public/internal.c | 14 ++-- src/public/py_exception.c | 1 - src/public/py_ops.c | 9 +-- src/public/stack_ops.c | 11 ++++ tests/{90_enum.py => 82_enum.py} | 2 - 13 files changed, 119 insertions(+), 32 deletions(-) delete mode 100644 python/_enum.py create mode 100644 src/modules/enum.c rename tests/{90_enum.py => 82_enum.py} (98%) diff --git a/include/pocketpy/common/_generated.h b/include/pocketpy/common/_generated.h index 641b93fd..472043fc 100644 --- a/include/pocketpy/common/_generated.h +++ b/include/pocketpy/common/_generated.h @@ -3,7 +3,6 @@ const char* load_kPythonLib(const char* name); -extern const char kPythonLibs__enum[]; extern const char kPythonLibs_bisect[]; extern const char kPythonLibs_builtins[]; extern const char kPythonLibs_cmath[]; diff --git a/include/pocketpy/interpreter/modules.h b/include/pocketpy/interpreter/modules.h index c9971602..f7588be2 100644 --- a/include/pocketpy/interpreter/modules.h +++ b/include/pocketpy/interpreter/modules.h @@ -10,4 +10,5 @@ void pk__add_module_json(); void pk__add_module_gc(); void pk__add_module_time(); void pk__add_module_easing(); -void pk__add_module_traceback(); \ No newline at end of file +void pk__add_module_traceback(); +void pk__add_module_enum(); \ No newline at end of file diff --git a/include/pocketpy/interpreter/vm.h b/include/pocketpy/interpreter/vm.h index 71fcd083..d9d72683 100644 --- a/include/pocketpy/interpreter/vm.h +++ b/include/pocketpy/interpreter/vm.h @@ -48,7 +48,6 @@ typedef struct VM { py_TValue last_retval; py_TValue curr_exception; bool is_curr_exc_handled; // handled by try-except block but not cleared yet - bool is_stopiteration; py_TValue reg[8]; // users' registers diff --git a/include/pocketpy/pocketpy.h b/include/pocketpy/pocketpy.h index a397cade..ffd1f943 100644 --- a/include/pocketpy/pocketpy.h +++ b/include/pocketpy/pocketpy.h @@ -295,6 +295,10 @@ PK_EXPORT void py_setdict(py_Ref self, py_Name name, py_Ref val); PK_EXPORT bool py_deldict(py_Ref self, py_Name name); /// Prepare an insertion to the object's `__dict__`. PK_EXPORT 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_EXPORT bool py_applydict(py_Ref self, bool (*f)(py_Name name, py_Ref val, void* ctx), void* ctx) PY_RAISE; /// Get the i-th slot of the object. /// The object must have slots and `i` must be in valid range. diff --git a/python/_enum.py b/python/_enum.py deleted file mode 100644 index c8879770..00000000 --- a/python/_enum.py +++ /dev/null @@ -1,11 +0,0 @@ -class Enum: - def __init__(self, name, value): - self.name = name - self.value = value - - def __str__(self): - return f'{type(self).__name__}.{self.name}' - - def __repr__(self): - return f'<{str(self)}: {self.value!r}>' - diff --git a/src/common/_generated.c b/src/common/_generated.c index df85cc11..183f7690 100644 --- a/src/common/_generated.c +++ b/src/common/_generated.c @@ -1,7 +1,6 @@ // generated by prebuild.py #include "pocketpy/common/_generated.h" #include -const char kPythonLibs__enum[] = "class Enum:\n def __init__(self, name, value):\n self.name = name\n self.value = value\n\n def __str__(self):\n return f'{type(self).__name__}.{self.name}'\n \n def __repr__(self):\n return f'<{str(self)}: {self.value!r}>'\n \n"; const char kPythonLibs_bisect[] = "\"\"\"Bisection algorithms.\"\"\"\n\ndef insort_right(a, x, lo=0, hi=None):\n \"\"\"Insert item x in list a, and keep it sorted assuming a is sorted.\n\n If x is already in a, insert it to the right of the rightmost x.\n\n Optional args lo (default 0) and hi (default len(a)) bound the\n slice of a to be searched.\n \"\"\"\n\n lo = bisect_right(a, x, lo, hi)\n a.insert(lo, x)\n\ndef bisect_right(a, x, lo=0, hi=None):\n \"\"\"Return the index where to insert item x in list a, assuming a is sorted.\n\n The return value i is such that all e in a[:i] have e <= x, and all e in\n a[i:] have e > x. So if x already appears in the list, a.insert(x) will\n insert just after the rightmost x already there.\n\n Optional args lo (default 0) and hi (default len(a)) bound the\n slice of a to be searched.\n \"\"\"\n\n if lo < 0:\n raise ValueError('lo must be non-negative')\n if hi is None:\n hi = len(a)\n while lo < hi:\n mid = (lo+hi)//2\n if x < a[mid]: hi = mid\n else: lo = mid+1\n return lo\n\ndef insort_left(a, x, lo=0, hi=None):\n \"\"\"Insert item x in list a, and keep it sorted assuming a is sorted.\n\n If x is already in a, insert it to the left of the leftmost x.\n\n Optional args lo (default 0) and hi (default len(a)) bound the\n slice of a to be searched.\n \"\"\"\n\n lo = bisect_left(a, x, lo, hi)\n a.insert(lo, x)\n\n\ndef bisect_left(a, x, lo=0, hi=None):\n \"\"\"Return the index where to insert item x in list a, assuming a is sorted.\n\n The return value i is such that all e in a[:i] have e < x, and all e in\n a[i:] have e >= x. So if x already appears in the list, a.insert(x) will\n insert just before the leftmost x already there.\n\n Optional args lo (default 0) and hi (default len(a)) bound the\n slice of a to be searched.\n \"\"\"\n\n if lo < 0:\n raise ValueError('lo must be non-negative')\n if hi is None:\n hi = len(a)\n while lo < hi:\n mid = (lo+hi)//2\n if a[mid] < x: lo = mid+1\n else: hi = mid\n return lo\n\n# Create aliases\nbisect = bisect_right\ninsort = insort_right\n"; const char kPythonLibs_builtins[] = "from pkpy import next as __pkpy_next\n\ndef all(iterable):\n for i in iterable:\n if not i:\n return False\n return True\n\ndef any(iterable):\n for i in iterable:\n if i:\n return True\n return False\n\ndef enumerate(iterable, start=0):\n n = start\n for elem in iterable:\n yield n, elem\n n += 1\n\ndef sum(iterable):\n res = 0\n for i in iterable:\n res += i\n return res\n\ndef map(f, iterable):\n for i in iterable:\n yield f(i)\n\ndef filter(f, iterable):\n for i in iterable:\n if f(i):\n yield i\n\ndef zip(a, b):\n a = iter(a)\n b = iter(b)\n while True:\n ai = __pkpy_next(a)\n bi = __pkpy_next(b)\n if ai is StopIteration or bi is StopIteration:\n break\n yield ai, bi\n\ndef reversed(iterable):\n a = list(iterable)\n a.reverse()\n return a\n\ndef sorted(iterable, key=None, reverse=False):\n a = list(iterable)\n a.sort(key=key, reverse=reverse)\n return a\n\n##### str #####\ndef __format_string(self: str, *args, **kwargs) -> str:\n def tokenizeString(s: str):\n tokens = []\n L, R = 0,0\n \n mode = None\n curArg = 0\n # lookingForKword = False\n \n while(Rlast_retval = *py_NIL; self->curr_exception = *py_NIL; self->is_curr_exc_handled = false; - self->is_stopiteration = false; self->__curr_class = NULL; @@ -206,6 +205,7 @@ void VM__ctor(VM* self) { pk__add_module_time(); pk__add_module_easing(); pk__add_module_traceback(); + pk__add_module_enum(); // add python builtins do { diff --git a/src/modules/enum.c b/src/modules/enum.c new file mode 100644 index 00000000..ac1f5630 --- /dev/null +++ b/src/modules/enum.c @@ -0,0 +1,91 @@ +#include "pocketpy/pocketpy.h" +#include "pocketpy/interpreter/vm.h" +#include "pocketpy/common/sstream.h" + +static bool Enum__wrapper_field(py_Name name, py_Ref value, void* ctx) { + c11_sv name_sv = py_name2sv(name); + if(name_sv.size == 0 || name_sv.data[0] == '_') return true; + py_push(ctx); + py_pushnil(); + py_newstr(py_pushtmp(), py_name2str(name)); + py_push(value); + bool ok = py_vectorcall(2, 0); + if(!ok) return false; + py_assign(value, py_retval()); + return true; +} + +static void Enum__on_end_subclass(py_TypeInfo* derived_ti) { + derived_ti->is_sealed = true; + py_applydict(&derived_ti->self, Enum__wrapper_field, &derived_ti->self); +} + +static bool Enum__new__(int argc, py_Ref argv) { + py_newobject(py_retval(), py_totype(argv), 2, 0); + return true; +} + +static bool Enum__init__(int argc, py_Ref argv) { + PY_CHECK_ARGC(3); + PY_CHECK_ARG_TYPE(1, tp_str); + py_setslot(argv, 0, py_arg(1)); + py_setslot(argv, 1, py_arg(2)); + py_newnone(py_retval()); + return true; +} + +static bool Enum__str__(int argc, py_Ref argv) { + PY_CHECK_ARGC(1); + // f'{type(self).__name__}.{self.name}' + c11_sbuf buf; + c11_sbuf__ctor(&buf); + c11_sbuf__write_cstr(&buf, py_tpname(argv->type)); + c11_sbuf__write_char(&buf, '.'); + c11_sbuf__write_cstr(&buf, py_tostr(py_getslot(argv, 0))); + c11_sbuf__py_submit(&buf, py_retval()); + return true; +} + +static bool Enum__repr__(int argc, py_Ref argv) { + PY_CHECK_ARGC(1); + // f'<{str(self)}: {self.value!r}>' + if(!py_str(argv)) return false; + py_push(py_retval()); // str(self) + if(!py_repr(py_getslot(argv, 1))) return false; + py_push(py_retval()); // repr(self.value) + c11_sbuf buf; + c11_sbuf__ctor(&buf); + c11_sbuf__write_cstr(&buf, "<"); + c11_sbuf__write_cstr(&buf, py_tostr(py_peek(-2))); + c11_sbuf__write_cstr(&buf, ": "); + c11_sbuf__write_cstr(&buf, py_tostr(py_peek(-1))); + c11_sbuf__write_cstr(&buf, ">"); + c11_sbuf__py_submit(&buf, py_retval()); + py_shrink(2); + return true; +} + +static bool Enum__name(int argc, py_Ref argv) { + py_assign(py_retval(), py_getslot(argv, 0)); + return true; +} + +static bool Enum__value(int argc, py_Ref argv) { + py_assign(py_retval(), py_getslot(argv, 1)); + return true; +} + +void pk__add_module_enum() { + py_Ref mod = py_newmodule("enum"); + py_Type type = py_newtype("Enum", tp_object, mod, NULL); + + py_bindmagic(type, __new__, Enum__new__); + py_bindmagic(type, __init__, Enum__init__); + py_bindmagic(type, __str__, Enum__str__); + py_bindmagic(type, __repr__, Enum__repr__); + py_bindproperty(type, "name", Enum__name, NULL); + py_bindproperty(type, "value", Enum__value, NULL); + + py_TypeInfo* ti = c11__at(py_TypeInfo, &pk_current_vm->types, type); + ti->on_end_subclass = Enum__on_end_subclass; +} \ No newline at end of file diff --git a/src/public/internal.c b/src/public/internal.c index 2fac191f..e2b9bbcd 100644 --- a/src/public/internal.c +++ b/src/public/internal.c @@ -105,7 +105,12 @@ bool py_callcfunc(py_CFunction f, int argc, py_Ref argv) { py_StackRef p0 = py_peek(0); py_newnil(py_retval()); bool ok = f(argc, argv); - if(!ok) return false; + if(!ok) { + if(!py_checkexc(true)) { + c11__abort("py_CFunction returns `false` but no exception is set!"); + } + return false; + } if(py_peek(0) != p0) { c11__abort("py_CFunction corrupts the stack! Did you forget to call `py_pop()`?"); } @@ -219,9 +224,4 @@ bool pk_callmagic(py_Name name, int argc, py_Ref argv) { return py_call(tmp, argc, argv); } -bool StopIteration() { - VM* vm = pk_current_vm; - assert(!vm->is_stopiteration); // flag is already set - vm->is_stopiteration = true; - return false; -} \ No newline at end of file +bool StopIteration() { return py_exception(tp_StopIteration, ""); } \ No newline at end of file diff --git a/src/public/py_exception.c b/src/public/py_exception.c index d298f61a..70c3a8ca 100644 --- a/src/public/py_exception.c +++ b/src/public/py_exception.c @@ -152,7 +152,6 @@ void py_clearexc(py_StackRef p0) { vm->last_retval = *py_NIL; vm->curr_exception = *py_NIL; vm->is_curr_exc_handled = false; - vm->is_stopiteration = false; vm->__curr_class = NULL; if(p0) vm->stack.sp = p0; } diff --git a/src/public/py_ops.c b/src/public/py_ops.c index 99d17cf3..ccb86686 100644 --- a/src/public/py_ops.c +++ b/src/public/py_ops.c @@ -70,20 +70,17 @@ bool py_iter(py_Ref val) { int py_next(py_Ref val) { VM* vm = pk_current_vm; - vm->is_stopiteration = false; py_Ref tmp = py_tpfindmagic(val->type, __next__); if(!tmp) { TypeError("'%t' object is not an iterator", val->type); return -1; } - if(py_call(tmp, 1, val)) return true; + if(py_call(tmp, 1, val)) return 1; if(vm->curr_exception.type == tp_StopIteration) { py_clearexc(NULL); - vm->is_stopiteration = true; + return 0; } - int retval = vm->is_stopiteration ? 0 : -1; - vm->is_stopiteration = false; - return retval; + return -1; } bool py_getattr(py_Ref self, py_Name name) { diff --git a/src/public/stack_ops.c b/src/public/stack_ops.c index ea257647..c7f79a4b 100644 --- a/src/public/stack_ops.c +++ b/src/public/stack_ops.c @@ -34,6 +34,17 @@ py_ItemRef py_emplacedict(py_Ref self, py_Name name){ return py_getdict(self, name); } +bool py_applydict(py_Ref self, bool (*f)(py_Name, py_Ref, void *), void *ctx){ + assert(self && self->is_ptr); + NameDict* dict = PyObject__dict(self->_obj); + for(int i = 0; i < dict->length; i++){ + NameDict_KV* kv = c11__at(NameDict_KV, dict, i); + bool ok = f(kv->key, &kv->value, ctx); + if(!ok) return false; + } + return true; +} + bool py_deldict(py_Ref self, py_Name name) { assert(self && self->is_ptr); if(!py_ismagicname(name) || self->type != tp_type) { diff --git a/tests/90_enum.py b/tests/82_enum.py similarity index 98% rename from tests/90_enum.py rename to tests/82_enum.py index d9a197a7..4844c531 100644 --- a/tests/90_enum.py +++ b/tests/82_enum.py @@ -1,5 +1,3 @@ -exit() - from enum import Enum class A(Enum):