Compare commits

...

4 Commits

Author SHA1 Message Date
blueloveTH
4285324209 ... 2024-08-16 16:52:24 +08:00
blueloveTH
317a37a851 support __getattr__ 2024-08-16 16:52:04 +08:00
blueloveTH
daf974657c ... 2024-08-16 16:40:06 +08:00
blueloveTH
ef9c5c98cc add enum module 2024-08-16 15:53:10 +08:00
16 changed files with 216 additions and 56 deletions

View File

@ -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[];

View File

@ -0,0 +1,35 @@
#pragma once
typedef struct c11_vec2i {
int x;
int y;
} c11_vec2i;
typedef struct c11_vec3i {
int x;
int y;
int z;
} c11_vec3i;
typedef struct c11_vec2 {
float x;
float y;
} c11_vec2;
typedef struct c11_vec3 {
float x;
float y;
float z;
} c11_vec3;
typedef struct c11_mat3x3 {
union {
struct {
float _11, _12, _13;
float _21, _22, _23;
float _31, _32, _33;
};
float m[3][3];
float v[9];
};
} c11_mat3x3;

View File

@ -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();
void pk__add_module_traceback();
void pk__add_module_enum();

View File

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

View File

@ -51,6 +51,8 @@ typedef py_TValue* py_GlobalRef;
typedef py_TValue* py_StackRef;
/// An item reference to a container object. It invalidates when the container is modified.
typedef py_TValue* py_ItemRef;
/// An output reference for returning a value.
typedef py_TValue* py_OutRef;
/// Native function signature.
/// @param argc number of arguments.
@ -109,54 +111,57 @@ PK_EXPORT bool py_compile(const char* source,
bool is_dynamic) PY_RAISE PY_RETURN;
/// Python equivalent to `globals()`.
PK_EXPORT void py_newglobals(py_Ref);
PK_EXPORT void py_newglobals(py_OutRef);
/// Python equivalent to `locals()`.
/// @return a temporary object, which expires on the associated function return.
PK_EXPORT void py_newlocals(py_Ref);
PK_EXPORT void py_newlocals(py_OutRef);
/************* Values Creation *************/
/// Create an `int` object.
PK_EXPORT void py_newint(py_Ref, py_i64);
PK_EXPORT void py_newint(py_OutRef, py_i64);
/// Create a `float` object.
PK_EXPORT void py_newfloat(py_Ref, py_f64);
PK_EXPORT void py_newfloat(py_OutRef, py_f64);
/// Create a `bool` object.
PK_EXPORT void py_newbool(py_Ref, bool);
PK_EXPORT void py_newbool(py_OutRef, bool);
/// Create a `str` object from a null-terminated string (utf-8).
PK_EXPORT void py_newstr(py_Ref, const char*);
PK_EXPORT void py_newstr(py_OutRef, const char*);
/// Create a `str` object from a char array (utf-8).
PK_EXPORT void py_newstrn(py_Ref, const char*, int);
PK_EXPORT void py_newstrn(py_OutRef, const char*, int);
/// Create a `bytes` object with `n` UNINITIALIZED bytes.
PK_EXPORT unsigned char* py_newbytes(py_Ref, int n);
PK_EXPORT unsigned char* py_newbytes(py_OutRef, int n);
/// Create a `None` object.
PK_EXPORT void py_newnone(py_Ref);
PK_EXPORT void py_newnone(py_OutRef);
/// Create a `NotImplemented` object.
PK_EXPORT void py_newnotimplemented(py_Ref out);
PK_EXPORT void py_newnotimplemented(py_OutRef);
/// Create a `...` object.
PK_EXPORT void py_newellipsis(py_Ref out);
PK_EXPORT void py_newellipsis(py_OutRef);
/// Create a `nil` object. `nil` is an invalid representation of an object.
/// Don't use it unless you know what you are doing.
PK_EXPORT void py_newnil(py_Ref);
PK_EXPORT void py_newnil(py_OutRef);
/// Create a `tuple` with `n` UNINITIALIZED elements.
/// You should initialize all elements before using it.
PK_EXPORT void py_newtuple(py_Ref, int n);
PK_EXPORT void py_newtuple(py_OutRef, int n);
/// Create an empty `list`.
PK_EXPORT void py_newlist(py_Ref);
PK_EXPORT void py_newlist(py_OutRef);
/// Create a `list` with `n` UNINITIALIZED elements.
/// You should initialize all elements before using it.
PK_EXPORT void py_newlistn(py_Ref, int n);
PK_EXPORT void py_newlistn(py_OutRef, int n);
/// Create an empty `dict`.
PK_EXPORT void py_newdict(py_Ref);
PK_EXPORT void py_newdict(py_OutRef);
/// Create an UNINITIALIZED `slice` object.
/// You should use `py_setslot()` to set `start`, `stop`, and `step`.
PK_EXPORT void py_newslice(py_Ref);
PK_EXPORT void py_newslice(py_OutRef);
/// Create a `nativefunc` object.
PK_EXPORT void py_newnativefunc(py_Ref out, py_CFunction);
PK_EXPORT void py_newnativefunc(py_OutRef, py_CFunction);
/// Create a `function` object.
PK_EXPORT py_Name
py_newfunction(py_Ref out, const char* sig, py_CFunction f, const char* docstring, int slots);
PK_EXPORT py_Name py_newfunction(py_OutRef out,
const char* sig,
py_CFunction f,
const char* docstring,
int slots);
/// Create a `boundmethod` object.
PK_EXPORT void py_newboundmethod(py_Ref out, py_Ref self, py_Ref func);
PK_EXPORT void py_newboundmethod(py_OutRef out, py_Ref self, py_Ref func);
/************* Name Convertions *************/
@ -189,7 +194,7 @@ PK_EXPORT py_Type py_newtype(const char* name,
/// @param slots number of slots. Use `-1` to create a `__dict__`.
/// @param udsize size of your userdata.
/// @return pointer to the userdata.
PK_EXPORT void* py_newobject(py_Ref out, py_Type type, int slots, int udsize);
PK_EXPORT void* py_newobject(py_OutRef out, py_Type type, int slots, int udsize);
/************* Type Cast *************/
@ -295,6 +300,11 @@ 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.
@ -462,7 +472,7 @@ PK_EXPORT void py_clearexc(py_StackRef p0);
#define UnboundLocalError(n) \
py_exception(tp_UnboundLocalError, "local variable '%n' referenced before assignment", (n))
PK_EXPORT bool StopIteration();
PK_EXPORT bool StopIteration() PY_RAISE;
PK_EXPORT bool KeyError(py_Ref key) PY_RAISE;
/************* Operators *************/
@ -470,7 +480,6 @@ PK_EXPORT bool KeyError(py_Ref key) PY_RAISE;
/// Python equivalent to `bool(val)`.
/// 1: true, 0: false, -1: error
PK_EXPORT int py_bool(py_Ref val) PY_RAISE;
/// Compare two objects.
/// 1: lhs == rhs, 0: lhs != rhs, -1: error
PK_EXPORT int py_equal(py_Ref lhs, py_Ref rhs) PY_RAISE;

View File

@ -59,6 +59,7 @@ MAGIC_METHOD(__package__)
MAGIC_METHOD(__path__)
MAGIC_METHOD(__class__)
MAGIC_METHOD(__abs__)
MAGIC_METHOD(__getattr__)
MAGIC_METHOD(__missing__)
#endif

View File

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

File diff suppressed because one or more lines are too long

View File

@ -68,7 +68,6 @@ void VM__ctor(VM* self) {
self->last_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 {

91
src/modules/enum.c Normal file
View File

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

View File

@ -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;
}
bool StopIteration() { return py_exception(tp_StopIteration, ""); }

View File

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

View File

@ -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) {
@ -154,6 +151,14 @@ bool py_getattr(py_Ref self, py_Name name) {
}
}
py_Ref fallback = py_tpfindmagic(type, __getattr__);
if(fallback){
py_push(fallback);
py_push(self);
py_newstr(py_pushtmp(), py_name2str(name));
return py_vectorcall(1, 0);
}
if(self->type == tp_module) {
py_Ref path = py_getdict(self, __path__);
c11_sbuf buf;

View File

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

View File

@ -26,3 +26,27 @@ except AttributeError:
pass
assert getattr(a, 'xxx', 1) == 1
class A:
def __init__(self, x):
self.x = x
def __getattr__(self, name):
if not name:
raise AttributeError
return name, None
a = A(1)
assert a.x == 1
assert a.y == ('y', None)
assert a.zzz == ('zzz', None)
assert getattr(a, 'x') == 1
assert getattr(a, 'zzz') == ('zzz', None)
assert hasattr(a, 'x')
assert hasattr(a, 'y')
assert hasattr(a, 'zzz')
assert not hasattr(a, '')

View File

@ -1,5 +1,3 @@
exit()
from enum import Enum
class A(Enum):