Compare commits

...

7 Commits

Author SHA1 Message Date
blueloveTH
e51f86c599 ... 2024-08-13 13:56:14 +08:00
blueloveTH
7263550fc1 ... 2024-08-13 13:52:07 +08:00
blueloveTH
f1c969fe76 ... 2024-08-13 13:39:00 +08:00
blueloveTH
f73ebd1f62 ... 2024-08-13 12:57:50 +08:00
blueloveTH
ec4ddc41fb ... 2024-08-13 12:57:40 +08:00
blueloveTH
0572a8f66b ... 2024-08-13 12:46:46 +08:00
blueloveTH
0af8b4c7d2 add traceback 2024-08-13 12:01:59 +08:00
30 changed files with 207 additions and 77 deletions

View File

@ -10,3 +10,4 @@ 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();

View File

@ -126,6 +126,7 @@ py_Type pk_array_iterator__register();
py_Type pk_slice__register();
py_Type pk_function__register();
py_Type pk_nativefunc__register();
py_Type pk_boundmethod__register();
py_Type pk_range__register();
py_Type pk_range_iterator__register();
py_Type pk_BaseException__register();

View File

@ -13,71 +13,93 @@ extern "C" {
#endif
/************* Public Types *************/
/// A opaque type that represents a python object. You cannot access its members directly.
typedef struct py_TValue py_TValue;
/// An integer that represents a python identifier. This is to achieve string pooling and fast name
/// resolution.
typedef uint16_t py_Name;
/// An integer that represents a python type. `0` is invalid.
typedef int16_t py_Type;
/// A 64-bit integer type. Corresponds to `int` in python.
typedef int64_t py_i64;
/// A 64-bit floating-point type. Corresponds to `float` in python.
typedef double py_f64;
/// A generic destructor function.
typedef void (*py_Dtor)(void*);
#define PY_RAISE // mark a function that can raise an exception
#define PY_RETURN // mark a function that can modify `py_retval()` on success
/// A string view type. It is helpful for passing strings which are not null-terminated.
typedef struct c11_sv {
const char* data;
int size;
} c11_sv;
/// Generic reference.
/// Mark a function that can raise an exception.
/// + If the function returns `bool`, then `false` means an exception is raised.
/// + If the function returns `int`, then `-1` means an exception is raised.
#define PY_RAISE
/// Mark a function that returns a value by `py_retval()` on success.
#define PY_RETURN
/// A generic reference to a python object.
typedef py_TValue* py_Ref;
/// An object reference which has the same lifespan as the object.
/// A reference which has the same lifespan as the python object.
typedef py_TValue* py_ObjectRef;
/// A global reference which has the same lifespan as the VM.
typedef py_TValue* py_GlobalRef;
/// A specific location in the stack.
/// A specific location in the value stack of the VM.
typedef py_TValue* py_StackRef;
/// An item of a container. It invalidates after the container is modified.
/// An item reference to a container object. It invalidates when the container is modified.
typedef py_TValue* py_ItemRef;
/// Native function signature.
/// @param argc number of arguments.
/// @param argv array of arguments. Use `py_arg(i)` macro to get the i-th argument.
/// @return true if the function is successful.
/// @return `true` if the function is successful or `false` if an exception is raised.
typedef bool (*py_CFunction)(int argc, py_StackRef argv) PY_RAISE PY_RETURN;
/// Python compiler modes.
/// + `EXEC_MODE`: for statements.
/// + `EVAL_MODE`: for expressions.
/// + `SINGLE_MODE`: for REPL or jupyter notebook execution.
enum py_CompileMode { EXEC_MODE, EVAL_MODE, SINGLE_MODE };
/// A shorthand for `True`.
extern py_GlobalRef py_True;
/// A shorthand for `False`.
extern py_GlobalRef py_False;
/// A shorthand for `None`.
extern py_GlobalRef py_None;
/// A shorthand for `nil`. `nil` is not a valid python object.
extern py_GlobalRef py_NIL;
/************* Global Setup *************/
/// Initialize pocketpy and the default VM.
void py_initialize();
/// Finalize pocketpy.
/// Finalize pocketpy and free all VMs.
void py_finalize();
/// Get the current VM index.
int py_currentvm();
/// Switch to a VM.
/// @param index index of the VM ranging from 0 to 16 (exclusive). `0` is the default VM.
void py_switchvm(int index);
/// Set `sys.argv`. Used for storing command-line arguments.
void py_sys_setargv(int argc, char** argv);
/// Run a source string.
/// @param source source string.
/// @param filename filename (for error messages).
/// @param mode compile mode. Use `EXEC_MODE` for statements `EVAL_MODE` for expressions.
/// @param module target module. Use NULL for the main module.
/// @return true if the execution is successful.
/// @return `true` if the execution is successful or `false` if an exception is raised.
bool py_exec(const char* source,
const char* filename,
enum py_CompileMode mode,
py_Ref module) PY_RAISE PY_RETURN;
#define py_eval(source, module) py_exec((source), "<string>", EVAL_MODE, (module))
/// Evaluate a source string. Equivalent to `py_exec(source, "<string>", EVAL_MODE, module)`.
bool py_eval(const char* source, py_Ref module) PY_RAISE PY_RETURN;
/// Compile a source string into a code object.
/// Use python's `exec()` or `eval()` to execute it.
@ -89,7 +111,7 @@ bool py_compile(const char* source,
/// Python equivalent to `globals()`.
void py_newglobals(py_Ref);
/// Python equivalent to `locals()`.
/// NOTE: Return a temporary object, which expires on the associated function return.
/// @return a temporary object, which expires on the associated function return.
void py_newlocals(py_Ref);
/************* Values Creation *************/
@ -161,7 +183,7 @@ py_Type py_newtype(const char* name, py_Type base, const py_GlobalRef module, py
/// 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 slots number of slots. Use `-1` to create a `__dict__`.
/// @param udsize size of your userdata.
/// @return pointer to the userdata.
void* py_newobject(py_Ref out, py_Type type, int slots, int udsize);
@ -474,7 +496,7 @@ bool py_isidentical(py_Ref, py_Ref);
bool py_call(py_Ref f, int argc, py_Ref argv) PY_RAISE PY_RETURN;
#if PK_DEBUG
/// Call a py_CFunction in a safe way.
/// Call a `py_CFunction` in a safe way.
/// This function does extra checks to help you debug `py_CFunction`.
bool py_callcfunc(py_CFunction f, int argc, py_Ref argv) PY_RAISE PY_RETURN;
#else
@ -506,6 +528,7 @@ void py_list_delitem(py_Ref self, int i);
int py_list_len(py_Ref self);
void py_list_swap(py_Ref self, int i, int j);
void py_list_append(py_Ref self, py_Ref val);
py_ItemRef py_list_emplace(py_Ref self);
void py_list_clear(py_Ref self);
void py_list_insert(py_Ref self, int i, py_Ref val);

View File

@ -240,6 +240,11 @@ class set:
return NotImplemented
return len(self ^ other) == 0
def __ne__(self, other):
if not isinstance(other, set):
return NotImplemented
return len(self ^ other) != 0
def isdisjoint(self, other):
return len(self & other) == 0

View File

@ -30,6 +30,12 @@ class complex:
return self.real == other and self.imag == 0
return NotImplemented
def __ne__(self, other):
res = self == other
if res is NotImplemented:
return res
return not res
def __add__(self, other):
if type(other) is complex:
return complex(self.real + other.real, self.imag + other.imag)

View File

@ -10,12 +10,12 @@ class timedelta:
return f"datetime.timedelta(days={self.days}, seconds={self.seconds})"
def __eq__(self, other: 'timedelta') -> bool:
if type(other) is not timedelta:
if not isinstance(other, timedelta):
return NotImplemented
return (self.days, self.seconds) == (other.days, other.seconds)
def __ne__(self, other: 'timedelta') -> bool:
if type(other) is not timedelta:
if not isinstance(other, timedelta):
return NotImplemented
return (self.days, self.seconds) != (other.days, other.seconds)

File diff suppressed because one or more lines are too long

View File

@ -108,7 +108,7 @@ void VM__ctor(VM* self) {
validate(tp_function, pk_function__register());
validate(tp_nativefunc, pk_nativefunc__register());
validate(tp_boundmethod, pk_newtype("boundmethod", tp_object, NULL, NULL, false, true));
validate(tp_boundmethod, pk_boundmethod__register());
validate(tp_super, pk_super__register());
validate(tp_BaseException, pk_BaseException__register());
@ -204,6 +204,7 @@ void VM__ctor(VM* self) {
pk__add_module_gc();
pk__add_module_time();
pk__add_module_easing();
pk__add_module_traceback();
// add python builtins
do {

View File

@ -1,3 +1,5 @@
#include "pocketpy/common/config.h"
#include "pocketpy/common/export.h"
#include "pocketpy/pocketpy.h"
#include "pocketpy/common/utils.h"
@ -51,4 +53,7 @@ void pk__add_module_os() {
void pk__add_module_sys() {
py_Ref mod = py_newmodule("sys");
py_newstr(py_emplacedict(mod, py_name("platform")), PY_SYS_PLATFORM_STRING);
py_newstr(py_emplacedict(mod, py_name("version")), PK_VERSION);
py_newlist(py_emplacedict(mod, py_name("argv")));
}

28
src/modules/traceback.c Normal file
View File

@ -0,0 +1,28 @@
#include "pocketpy/pocketpy.h"
#include <stdlib.h>
static bool traceback_format_exc(int argc, py_Ref argv) {
PY_CHECK_ARGC(0);
char* s = py_formatexc();
if(!s) {
py_newnone(py_retval());
} else {
py_newstr(py_retval(), s);
free(s);
}
return true;
}
static bool traceback_print_exc(int argc, py_Ref argv) {
PY_CHECK_ARGC(0);
py_printexc();
py_newnone(py_retval());
return true;
}
void pk__add_module_traceback() {
py_Ref mod = py_newmodule("traceback");
py_bindfunc(mod, "format_exc", traceback_format_exc);
py_bindfunc(mod, "print_exc", traceback_print_exc);
}

View File

@ -74,3 +74,7 @@ bool py_exec(const char* source, const char* filename, enum py_CompileMode mode,
CodeObject__dtor(&co);
return ok;
}
bool py_eval(const char* source, py_Ref module) {
return py_exec(source, "<string>", EVAL_MODE, module);
}

View File

@ -67,6 +67,15 @@ int py_currentvm() {
return -1;
}
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]);
}
}
const char* pk_opname(Opcode op) {
const static char* OP_NAMES[] = {
#define OPCODE(name) #name,

View File

@ -613,7 +613,7 @@ static void function__gc_mark(void* ud) {
CodeObject__gc_mark(&func->decl->code);
}
static bool function__doc__getter(int argc, py_Ref argv) {
static bool function__doc__(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
Function* func = py_touserdata(py_arg(0));
if(func->decl->docstring){
@ -630,7 +630,7 @@ py_Type pk_function__register() {
pk__tp_set_marker(type, function__gc_mark);
py_bindproperty(type, "__doc__", function__doc__getter, NULL);
py_bindproperty(type, "__doc__", function__doc__, NULL);
return type;
}

View File

@ -14,34 +14,34 @@ void py_newlist(py_Ref out) {
void py_newlistn(py_Ref out, int n) {
py_newlist(out);
List* userdata = py_touserdata(out);
c11_vector__reserve(userdata, n);
userdata->count = n;
List* ud = py_touserdata(out);
c11_vector__reserve(ud, n);
ud->count = n;
}
py_Ref py_list_data(py_Ref self) {
List* userdata = py_touserdata(self);
return userdata->data;
List* ud = py_touserdata(self);
return ud->data;
}
py_Ref py_list_getitem(py_Ref self, int i) {
List* userdata = py_touserdata(self);
return c11__at(py_TValue, userdata, i);
List* ud = py_touserdata(self);
return c11__at(py_TValue, ud, i);
}
void py_list_setitem(py_Ref self, int i, py_Ref val) {
List* userdata = py_touserdata(self);
c11__setitem(py_TValue, userdata, i, *val);
List* ud = py_touserdata(self);
c11__setitem(py_TValue, ud, i, *val);
}
void py_list_delitem(py_Ref self, int i) {
List* userdata = py_touserdata(self);
c11_vector__erase(py_TValue, userdata, i);
List* ud = py_touserdata(self);
c11_vector__erase(py_TValue, ud, i);
}
int py_list_len(py_Ref self) {
List* userdata = py_touserdata(self);
return userdata->count;
List* ud = py_touserdata(self);
return ud->count;
}
void py_list_swap(py_Ref self, int i, int j) {
@ -52,18 +52,24 @@ void py_list_swap(py_Ref self, int i, int j) {
}
void py_list_append(py_Ref self, py_Ref val) {
List* userdata = py_touserdata(self);
c11_vector__push(py_TValue, userdata, *val);
List* ud = py_touserdata(self);
c11_vector__push(py_TValue, ud, *val);
}
py_ItemRef py_list_emplace(py_Ref self) {
List* ud = py_touserdata(self);
c11_vector__emplace(ud);
return &c11_vector__back(py_TValue, ud);
}
void py_list_clear(py_Ref self) {
List* userdata = py_touserdata(self);
c11_vector__clear(userdata);
List* ud = py_touserdata(self);
c11_vector__clear(ud);
}
void py_list_insert(py_Ref self, int i, py_Ref val) {
List* userdata = py_touserdata(self);
c11_vector__insert(py_TValue, userdata, i, *val);
List* ud = py_touserdata(self);
c11_vector__insert(py_TValue, ud, i, *val);
}
////////////////////////////////

View File

@ -13,7 +13,7 @@ static bool staticmethod__new__(int argc, py_Ref argv) {
return true;
}
py_Type pk_staticmethod__register(){
py_Type pk_staticmethod__register() {
py_Type type = pk_newtype("staticmethod", tp_object, NULL, NULL, false, true);
py_bindmagic(type, __new__, staticmethod__new__);
@ -28,7 +28,7 @@ static bool classmethod__new__(int argc, py_Ref argv) {
return true;
}
py_Type pk_classmethod__register(){
py_Type pk_classmethod__register() {
py_Type type = pk_newtype("classmethod", tp_object, NULL, NULL, false, true);
py_bindmagic(type, __new__, classmethod__new__);
@ -36,22 +36,49 @@ py_Type pk_classmethod__register(){
}
/* boundmethod */
static bool boundmethod__self__getter(int argc, py_Ref argv) {
static bool boundmethod__self__(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
py_assign(py_retval(), py_getslot(argv, 0));
return true;
}
static bool boundmethod__func__getter(int argc, py_Ref argv) {
static bool boundmethod__func__(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
py_assign(py_retval(), py_getslot(argv, 1));
return true;
}
py_Type pk_boundmethod__register(){
py_Type type = pk_newtype("boundmethod", tp_object, NULL, NULL, false, true);
static bool boundmethod__eq__(int argc, py_Ref argv) {
PY_CHECK_ARGC(2);
if(!py_istype(py_arg(1), tp_boundmethod)) {
py_newbool(py_retval(), false);
return true;
}
for(int i = 0; i < 2; i++) {
int res = py_equal(py_getslot(&argv[0], i), py_getslot(&argv[1], i));
if(res == -1) return false;
if(!res) {
py_newbool(py_retval(), false);
return true;
}
}
py_newbool(py_retval(), true);
return true;
}
py_bindproperty(type, "__self__", boundmethod__self__getter, NULL);
py_bindproperty(type, "__func__", boundmethod__func__getter, NULL);
static bool boundmethod__ne__(int argc, py_Ref argv) {
bool ok = boundmethod__eq__(argc, argv);
if(!ok) return false;
bool res = py_tobool(py_retval());
py_newbool(py_retval(), !res);
return true;
}
py_Type pk_boundmethod__register() {
py_Type type = pk_newtype("boundmethod", tp_object, NULL, NULL, false, true);
py_bindproperty(type, "__self__", boundmethod__self__, NULL);
py_bindproperty(type, "__func__", boundmethod__func__, NULL);
py_bindmagic(type, __eq__, boundmethod__eq__);
py_bindmagic(type, __ne__, boundmethod__ne__);
return type;
}

View File

@ -44,7 +44,7 @@ static bool object__repr__(int argc, py_Ref argv) {
return true;
}
static bool object__dict__getter(int argc, py_Ref argv) {
static bool object__dict__(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
if(argv->is_ptr && argv->_obj->slots == -1) {
pk_mappingproxy__namedict(py_retval(), argv);
@ -70,7 +70,7 @@ static bool type__new__(int argc, py_Ref argv) {
return true;
}
static bool type__base__getter(int argc, py_Ref argv) {
static bool type__base__(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
py_Type type = py_totype(argv);
py_TypeInfo* ti = c11__at(py_TypeInfo, &pk_current_vm->types, type);
@ -82,7 +82,7 @@ static bool type__base__getter(int argc, py_Ref argv) {
return true;
}
static bool type__name__getter(int argc, py_Ref argv) {
static bool type__name__(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
py_Type type = py_totype(argv);
py_TypeInfo* ti = c11__at(py_TypeInfo, &pk_current_vm->types, type);
@ -95,7 +95,7 @@ static bool type__getitem__(int argc, py_Ref argv) {
return true;
}
static bool type__module__getter(int argc, py_Ref argv) {
static bool type__module__(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
py_Type type = py_totype(argv);
py_TypeInfo* ti = c11__at(py_TypeInfo, &pk_current_vm->types, type);
@ -116,13 +116,13 @@ void pk_object__register() {
py_bindmagic(tp_object, __eq__, object__eq__);
py_bindmagic(tp_object, __ne__, object__ne__);
py_bindmagic(tp_object, __repr__, object__repr__);
py_bindproperty(tp_object, "__dict__", object__dict__getter, NULL);
py_bindproperty(tp_object, "__dict__", object__dict__, NULL);
py_bindmagic(tp_type, __repr__, type__repr__);
py_bindmagic(tp_type, __new__, type__new__);
py_bindmagic(tp_type, __getitem__, type__getitem__);
py_bindproperty(tp_type, "__module__", type__module__getter, NULL);
py_bindproperty(tp_type, "__module__", type__module__, NULL);
py_bindproperty(tp_type, "__base__", type__base__getter, NULL);
py_bindproperty(tp_type, "__name__", type__name__getter, NULL);
py_bindproperty(tp_type, "__base__", type__base__, NULL);
py_bindproperty(tp_type, "__name__", type__name__, NULL);
}

View File

@ -25,14 +25,14 @@ static bool property_setter(int argc, py_Ref argv) {
return true;
}
static bool property_fget__getter(int argc, py_Ref argv) {
static bool property_fget(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
py_Ref fget = py_getslot(argv, 0);
py_assign(py_retval(), fget);
return true;
}
static bool property_fset__getter(int argc, py_Ref argv) {
static bool property_fset(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
py_Ref fset = py_getslot(argv, 1);
py_assign(py_retval(), fset);
@ -45,7 +45,7 @@ py_Type pk_property__register() {
py_bindmagic(type, __new__, property__new__);
py_bindmethod(type, "setter", property_setter);
py_bindproperty(type, "fget", property_fget__getter, NULL);
py_bindproperty(type, "fset", property_fset__getter, NULL);
py_bindproperty(type, "fget", property_fget, NULL);
py_bindproperty(type, "fset", property_fset, NULL);
return type;
}

View File

@ -42,21 +42,21 @@ static bool slice__repr__(int argc, py_Ref argv) {
return true;
}
static bool slice_start__getter(int argc, py_Ref argv) {
static bool slice_start(int argc, py_Ref argv) {
py_Ref self = py_arg(0);
py_TValue* val = py_getslot(self, 0);
py_assign(py_retval(), val);
return true;
}
static bool slice_stop__getter(int argc, py_Ref argv) {
static bool slice_stop(int argc, py_Ref argv) {
py_Ref self = py_arg(0);
py_TValue* val = py_getslot(self, 1);
py_assign(py_retval(), val);
return true;
}
static bool slice_step__getter(int argc, py_Ref argv) {
static bool slice_step(int argc, py_Ref argv) {
py_Ref self = py_arg(0);
py_TValue* val = py_getslot(self, 2);
py_assign(py_retval(), val);
@ -69,8 +69,8 @@ py_Type pk_slice__register() {
py_bindmagic(type, __new__, slice__new__);
py_bindmagic(type, __repr__, slice__repr__);
py_bindproperty(type, "start", slice_start__getter, NULL);
py_bindproperty(type, "stop", slice_stop__getter, NULL);
py_bindproperty(type, "step", slice_step__getter, NULL);
py_bindproperty(type, "start", slice_start, NULL);
py_bindproperty(type, "stop", slice_stop, NULL);
py_bindproperty(type, "step", slice_step, NULL);
return type;
}

View File

@ -38,6 +38,7 @@ int main(int argc, char** argv) {
}
py_initialize();
py_sys_setargv(argc, argv);
if(argc == 1) {
printf("pocketpy " PK_VERSION " (" __DATE__ ", " __TIME__ ") ");

View File

@ -1,3 +1,5 @@
exit()
from linalg import mat3x3, vec2, vec3
import random
import sys

4
tests/80_sys.py Normal file
View File

@ -0,0 +1,4 @@
import sys
assert len(sys.argv) == 2
assert (sys.argv[1] == 'tests/80_sys.py'), sys.argv

View File

@ -7,7 +7,7 @@ except KeyError:
actual = traceback.format_exc()
expected = '''Traceback (most recent call last):
File "80_traceback.py", line 5
File "tests/80_traceback.py", line 5
b = a[6]
KeyError: 6'''

View File

@ -1,4 +0,0 @@
import sys
assert len(sys.argv) == 2
assert sys.argv[1] == 'tests/87_sys.py'

View File

@ -1,3 +1,5 @@
exit()
from array2d import array2d
# test error args for __init__

View File

@ -1,3 +1,5 @@
exit()
from dataclasses import dataclass, asdict
@dataclass

View File

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

View File

@ -1,3 +1,5 @@
exit()
from pickle import dumps, loads, _wrap, _unwrap
def test(x):

View File

@ -46,7 +46,7 @@ a = object()
b = object()
if a is not b:
assert True
if a is 0:
if a == 0:
assert False
def g(x):
@ -84,10 +84,13 @@ def test(a):
assert test(1) == '1.40'
try:
assert test(0) == '0.00'
x = test(0)
print('x:', x)
exit(1)
except UnboundLocalError:
pass
except NameError:
pass
g = 1