This commit is contained in:
blueloveTH 2024-05-05 11:36:17 +08:00
parent 9943516d79
commit eae0aa6c8b
15 changed files with 156 additions and 119 deletions

View File

@ -194,3 +194,13 @@ Enabling the profiler has a performance overhead. Only enable it when you need i
+ `vm->is_non_tagged_type` was removed. Use `vm->is_type` instead.
+ `vm->check_non_tagged_type` was removed. Use `vm->check_type` instead.
## Python Stringify functions
The following functions now return `Str` object instead of `PyObject*`:
+ `vm->py_str`
+ `vm->py_repr`
+ `vm->py_json`
Also, `vm->bind__str__` and `vm->bind__repr__` were changed to return `Str` object.

View File

@ -237,21 +237,21 @@ Convert a python object to string
```cpp
PyObject* obj = py_var(vm, 123);
obj = vm->py_str(obj); // 123
Str s = vm->py_str(obj); // 123
```
Get the string representation of a python object
```cpp
PyObject* obj = py_var(vm, "123");
obj = vm->py_repr(obj); // "'123'"
Str s = vm->py_repr(obj); // "'123'"
```
Get the JSON representation of a python object
```cpp
PyObject* obj = py_var(vm, 123);
obj = vm->py_json(obj); // "123"
Str s = vm->py_json(obj); // "123"
```
Get the hash value of a python object

View File

@ -14,17 +14,18 @@ auto _lock = vm->heap.gc_scope_lock();
The scope lock is required if you create a PyObject and then try to run python-level bytecodes.
For example, you create a temporary object on the stack and then call `vm->py_str`.
For example, you create a temporary object on the stack and then call `vm->py_next`.
```cpp
void some_func(VM* vm){
PyObject* obj = VAR(List(5));
// unsafe
PyObject obj_string = vm->py_str(obj);
PyObject iter = vm->py_iter(obj);
PyObject next = vm->py_next(iter);
}
```
Because users can have an overload of `__str__`, this call is unsafe.
Because users can have an overload of `__next__`, this call is unsafe.
When the vm is running python-level bytecodes, gc may start and delete your temporary object.
The scope lock prevents this from happening.
@ -34,6 +35,7 @@ void some_func(VM* vm){
PyObject* obj = VAR(List(5));
// safe
auto _lock = vm->heap.gc_scope_lock();
PyObject obj_string = vm->py_str(obj);
PyObject iter = vm->py_iter(obj);
PyObject next = vm->py_next(iter);
}
```

View File

@ -50,10 +50,9 @@ struct PyTypeInfo{
std::vector<StrName> annotated_fields = {};
// cached special methods
// unary operators
PyObject* (*m__repr__)(VM* vm, PyObject*) = nullptr;
PyObject* (*m__str__)(VM* vm, PyObject*) = nullptr;
Str (*m__repr__)(VM* vm, PyObject*) = nullptr;
Str (*m__str__)(VM* vm, PyObject*) = nullptr;
i64 (*m__hash__)(VM* vm, PyObject*) = nullptr;
i64 (*m__len__)(VM* vm, PyObject*) = nullptr;
PyObject* (*m__iter__)(VM* vm, PyObject*) = nullptr;
@ -183,9 +182,10 @@ public:
VM(bool enable_os=true);
#if PK_REGION("Python Equivalents")
PyObject* py_str(PyObject* obj); // x -> str(x)
PyObject* py_repr(PyObject* obj); // x -> repr(x)
PyObject* py_json(PyObject* obj); // x -> json.dumps(x)
Str py_str(PyObject* obj); // x -> str(x)
Str py_repr(PyObject* obj); // x -> repr(x)
Str py_json(PyObject* obj); // x -> json.dumps(x)
PyObject* py_iter(PyObject* obj); // x -> iter(x)
PyObject* py_next(PyObject*); // x -> next(x)
PyObject* _py_next(const PyTypeInfo*, PyObject*); // x -> next(x) with type info cache
@ -269,9 +269,10 @@ public:
#endif
#if PK_REGION("Magic Bindings")
void bind__repr__(Type type, PyObject* (*f)(VM*, PyObject*));
void bind__str__(Type type, PyObject* (*f)(VM*, PyObject*));
void bind__repr__(Type type, Str (*f)(VM*, PyObject*));
void bind__str__(Type type, Str (*f)(VM*, PyObject*));
void bind__iter__(Type type, PyObject* (*f)(VM*, PyObject*));
void bind__next__(Type type, unsigned (*f)(VM*, PyObject*));
[[deprecated]] void bind__next__(Type type, PyObject* (*f)(VM*, PyObject*));
void bind__neg__(Type type, PyObject* (*f)(VM*, PyObject*));

View File

@ -181,9 +181,9 @@ struct Array2d{
return (i64)self.numel;
});
vm->bind__repr__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* _0){
vm->bind__repr__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* _0) -> Str{
Array2d& self = PK_OBJ_GET(Array2d, _0);
return VAR(_S("array2d(", self.n_cols, ", ", self.n_rows, ')'));
return _S("array2d(", self.n_cols, ", ", self.n_rows, ')');
});
vm->bind(type, "map(self, f)", [](VM* vm, ArgsView args){

View File

@ -134,7 +134,7 @@ __NEXT_STEP:;
THIRD() = _0;
} DISPATCH()
case OP_PRINT_EXPR:{
if(TOP() != None) stdout_write(CAST(Str&, py_repr(TOP())) + "\n");
if(TOP() != None) stdout_write(py_repr(TOP()) + "\n");
POP();
} DISPATCH()
/*****************************************/
@ -383,7 +383,7 @@ __NEXT_STEP:;
case OP_BUILD_STRING: {
SStream ss;
ArgsView view = STACK_VIEW(byte.arg);
for(PyObject* obj : view) ss << CAST(Str&, py_str(obj));
for(PyObject* obj : view) ss << py_str(obj);
STACK_SHRINK(byte.arg);
PUSH(VAR(ss.str()));
} DISPATCH()
@ -657,7 +657,7 @@ __NEXT_STEP:;
PUSH(_0);
} DISPATCH()
case OP_REPR:
TOP() = py_repr(TOP());
TOP() = VAR(py_repr(TOP()));
DISPATCH()
case OP_CALL:{
if(heap._should_auto_collect()) heap._auto_collect();
@ -933,8 +933,9 @@ __NEXT_STEP:;
} DISPATCH()
case OP_RAISE_ASSERT:
if(byte.arg){
PyObject* _0 = py_str(POPX());
AssertionError(CAST(Str, _0));
Str msg = py_str(TOP());
POP();
AssertionError(msg);
}else{
AssertionError();
}

View File

@ -14,9 +14,9 @@ namespace pkpy{
return reinterpret_cast<i64>(self.ptr);
});
vm->bind__repr__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* obj){
vm->bind__repr__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* obj) -> Str{
VoidP& self = PK_OBJ_GET(VoidP, obj);
return VAR(_S("<void* at ", self.hex(), ">"));
return _S("<void* at ", self.hex(), ">");
});
#define BIND_CMP(name, op) \
@ -76,7 +76,7 @@ namespace pkpy{
Struct& self = _CAST(Struct&, obj);
SStream ss;
ss << "<struct object of " << self.size << " bytes>";
return VAR(ss.str());
return ss.str();
});
vm->bind_func(type, "addr", 1, [](VM* vm, ArgsView args){
@ -234,9 +234,9 @@ void add_module_c(VM* vm){
T* target = (T*)voidp.ptr; \
return vm->heap.gcnew<VoidP>(lhs->type, target - offset); \
}); \
vm->bind__repr__(type_t, [](VM* vm, PyObject* obj){ \
vm->bind__repr__(type_t, [](VM* vm, PyObject* obj) -> Str{ \
VoidP& self = _CAST(VoidP&, obj); \
return VAR(_S("<", CNAME, "* at ", self.hex(), ">")); \
return _S("<", CNAME, "* at ", self.hex(), ">"); \
}); \
BIND_PRIMITIVE(char, "char")

View File

@ -105,21 +105,21 @@ namespace pkpy
return vm->new_user_object<PyDequeIter>(_0, self.dequeItems.begin(), self.dequeItems.end());
});
vm->bind__repr__(PK_OBJ_GET(Type, type), [](VM *vm, PyObject* _0)
vm->bind__repr__(PK_OBJ_GET(Type, type), [](VM *vm, PyObject* _0) -> Str
{
if(vm->_repr_recursion_set.count(_0)) return VAR("[...]");
if(vm->_repr_recursion_set.count(_0)) return "[...]";
const PyDeque &self = _CAST(PyDeque&, _0);
SStream ss;
ss << "deque([";
vm->_repr_recursion_set.insert(_0);
for (auto it = self.dequeItems.begin(); it != self.dequeItems.end(); ++it)
{
ss << CAST(Str&, vm->py_repr(*it));
ss << vm->py_repr(*it);
if (it != self.dequeItems.end() - 1) ss << ", ";
}
vm->_repr_recursion_set.erase(_0);
self.bounded ? ss << "], maxlen=" << self.maxlen << ")" : ss << "])";
return VAR(ss.str());
return ss.str();
});
// enables comparison between two deques, == and != are supported
@ -253,7 +253,7 @@ namespace pkpy
int start = CAST_DEFAULT(int, args[2], 0);
int stop = CAST_DEFAULT(int, args[3], self.dequeItems.size());
int index = self.findIndex(vm, obj, start, stop);
if (index < 0) vm->ValueError(_CAST(Str &, vm->py_repr(obj)) + " is not in deque");
if (index < 0) vm->ValueError(vm->py_repr(obj) + " is not in deque");
return VAR(index);
});
// NEW: returns the index of the given object in the deque
@ -290,7 +290,7 @@ namespace pkpy
PyObject *obj = args[1];
PyObject *removed = self.popObj(false, false, obj, vm);
if (removed == nullptr)
vm->ValueError(_CAST(Str &, vm->py_repr(obj)) + " is not in list");
vm->ValueError(vm->py_repr(obj) + " is not in list");
return vm->None;
});
// NEW: reverses the deque

View File

@ -45,8 +45,7 @@ static void patch__init__(VM* vm, Type cls){
}
static void patch__repr__(VM* vm, Type cls){
vm->bind__repr__(cls, [](VM* vm, PyObject* _0){
auto _lock = vm->heap.gc_scope_lock();
vm->bind__repr__(cls, [](VM* vm, PyObject* _0) -> Str{
const PyTypeInfo* cls_info = &vm->_all_types[vm->_tp(_0)];
const auto& fields = cls_info->annotated_fields;
const NameDict& obj_d = _0->attr();
@ -56,10 +55,10 @@ static void patch__repr__(VM* vm, Type cls){
for(StrName field: fields){
if(first) first = false;
else ss << ", ";
ss << field << "=" << CAST(Str&, vm->py_repr(obj_d[field]));
ss << field << "=" << vm->py_repr(obj_d[field]);
}
ss << ")";
return VAR(ss.str());
return ss.str();
});
}

View File

@ -149,12 +149,12 @@ static Vec2 SmoothDamp(Vec2 current, Vec2 target, Vec2& currentVelocity, float s
return VAR(val);
}, {}, BindType::STATICMETHOD);
vm->bind__repr__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* obj){
vm->bind__repr__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* obj) -> Str{
Vec2 self = _CAST(Vec2&, obj);
SStream ss;
ss.setprecision(3);
ss << "vec2(" << self.x << ", " << self.y << ")";
return VAR(ss.str());
return ss.str();
});
vm->bind_func(type, "rotate", 2, [](VM* vm, ArgsView args){
@ -200,12 +200,12 @@ static Vec2 SmoothDamp(Vec2 current, Vec2 target, Vec2& currentVelocity, float s
return vm->heap.gcnew<Vec3>(PK_OBJ_GET(Type, args[0]), x, y, z);
});
vm->bind__repr__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* obj){
vm->bind__repr__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* obj) -> Str{
Vec3 self = _CAST(Vec3&, obj);
SStream ss;
ss.setprecision(3);
ss << "vec3(" << self.x << ", " << self.y << ", " << self.z << ")";
return VAR(ss.str());
return ss.str();
});
PY_FIELD(Vec3, "x", x)
@ -239,12 +239,12 @@ static Vec2 SmoothDamp(Vec2 current, Vec2 target, Vec2& currentVelocity, float s
return vm->heap.gcnew<Vec4>(PK_OBJ_GET(Type, args[0]), x, y, z, w);
});
vm->bind__repr__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* obj){
vm->bind__repr__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* obj) -> Str{
Vec4 self = _CAST(Vec4&, obj);
SStream ss;
ss.setprecision(3);
ss << "vec4(" << self.x << ", " << self.y << ", " << self.z << ", " << self.w << ")";
return VAR(ss.str());
return ss.str();
});
PY_FIELD(Vec4, "x", x)
@ -298,14 +298,14 @@ static Vec2 SmoothDamp(Vec2 current, Vec2 target, Vec2& currentVelocity, float s
return vm->None;
});
vm->bind__repr__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* obj){
vm->bind__repr__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* obj) -> Str{
const Mat3x3& self = _CAST(Mat3x3&, obj);
SStream ss;
ss.setprecision(3);
ss << "mat3x3([" << self._11 << ", " << self._12 << ", " << self._13 << ",\n";
ss << " " << self._21 << ", " << self._22 << ", " << self._23 << ",\n";
ss << " " << self._31 << ", " << self._32 << ", " << self._33 << "])";
return VAR(ss.str());
return ss.str();
});
vm->bind__getitem__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* obj, PyObject* index){

View File

@ -103,7 +103,7 @@ void add_module_json(VM* vm){
});
vm->bind_func(mod, "dumps", 1, [](VM* vm, ArgsView args) {
return vm->py_json(args[0]);
return VAR(vm->py_json(args[0]));
});
}

View File

@ -245,7 +245,7 @@ void __init_builtins(VM* _vm) {
});
_vm->bind_func(_vm->builtins, "repr", 1, [](VM* vm, ArgsView args){
return vm->py_repr(args[0]);
return VAR(vm->py_repr(args[0]));
});
_vm->bind_func(_vm->builtins, "len", 1, [](VM* vm, ArgsView args){
@ -343,13 +343,13 @@ void __init_builtins(VM* _vm) {
});
// tp_object
_vm->bind__repr__(VM::tp_object, [](VM* vm, PyObject* obj) {
_vm->bind__repr__(VM::tp_object, [](VM* vm, PyObject* obj) -> Str{
if(is_tagged(obj)) PK_FATAL_ERROR();
SStream ss;
ss << "<" << _type_name(vm, vm->_tp(obj)) << " object at ";
ss.write_hex(obj);
ss << ">";
return VAR(ss.str());
return ss.str();
});
_vm->bind__eq__(VM::tp_object, [](VM* vm, PyObject* _0, PyObject* _1) {
@ -382,8 +382,8 @@ void __init_builtins(VM* _vm) {
_vm->bind__iter__(VM::tp_range, [](VM* vm, PyObject* obj) { return vm->new_user_object<RangeIter>(PK_OBJ_GET(Range, obj)); });
// tp_nonetype
_vm->bind__repr__(_vm->_tp(_vm->None), [](VM* vm, PyObject* _0) {
return VAR("None");
_vm->bind__repr__(_vm->_tp(_vm->None), [](VM* vm, PyObject* _0) -> Str {
return "None";
});
// tp_float / tp_float
@ -475,7 +475,9 @@ void __init_builtins(VM* _vm) {
return VAR(bits);
});
_vm->bind__repr__(VM::tp_int, [](VM* vm, PyObject* obj) { return VAR(std::to_string(_CAST(i64, obj))); });
_vm->bind__repr__(VM::tp_int, [](VM* vm, PyObject* obj) -> Str{
return std::to_string(_CAST(i64, obj));
});
_vm->bind__neg__(VM::tp_int, [](VM* vm, PyObject* obj) { return VAR(-_CAST(i64, obj)); });
_vm->bind__hash__(VM::tp_int, [](VM* vm, PyObject* obj) { return _CAST(i64, obj); });
_vm->bind__invert__(VM::tp_int, [](VM* vm, PyObject* obj) { return VAR(~_CAST(i64, obj)); });
@ -532,18 +534,18 @@ void __init_builtins(VM* _vm) {
_vm->bind__neg__(VM::tp_float, [](VM* vm, PyObject* _0) { return VAR(-_CAST(f64, _0)); });
_vm->bind__repr__(VM::tp_float, [](VM* vm, PyObject* _0) {
_vm->bind__repr__(VM::tp_float, [](VM* vm, PyObject* _0) -> Str {
f64 val = _CAST(f64, _0);
SStream ss;
ss << val;
return VAR(ss.str());
return ss.str();
});
// tp_str
_vm->bind_func(VM::tp_str, __new__, -1, [](VM* vm, ArgsView args) {
if(args.size() == 1) return VAR(Str());
if(args.size() > 2) vm->TypeError("str() takes at most 1 argument");
return vm->py_str(args[1]);
return VAR(vm->py_str(args[1]));
});
_vm->bind__hash__(VM::tp_str, [](VM* vm, PyObject* _0) {
@ -574,11 +576,12 @@ void __init_builtins(VM* _vm) {
const Str& self = _CAST(Str&, _0);
return VAR(self.index(CAST(Str&, _1)) != -1);
});
_vm->bind__str__(VM::tp_str, [](VM* vm, PyObject* _0) { return _0; });
_vm->bind_func(VM::tp_str, __str__, 1, [](VM* vm, ArgsView args) { return args[0]; });
_vm->bind__iter__(VM::tp_str, [](VM* vm, PyObject* _0) { return vm->new_user_object<StringIter>(_0); });
_vm->bind__repr__(VM::tp_str, [](VM* vm, PyObject* _0) {
_vm->bind__repr__(VM::tp_str, [](VM* vm, PyObject* _0) -> Str {
const Str& self = _CAST(Str&, _0);
return VAR(self.escape());
return self.escape();
});
#define BIND_CMP_STR(name, op) \
@ -799,36 +802,36 @@ void __init_builtins(VM* _vm) {
return vm->None;
});
_vm->bind__repr__(VM::tp_list, [](VM* vm, PyObject* _0){
if(vm->_repr_recursion_set.count(_0)) return VAR("[...]");
_vm->bind__repr__(VM::tp_list, [](VM* vm, PyObject* _0) -> Str{
if(vm->_repr_recursion_set.count(_0)) return "[...]";
List& iterable = _CAST(List&, _0);
SStream ss;
ss << '[';
vm->_repr_recursion_set.insert(_0);
for(int i=0; i<iterable.size(); i++){
ss << CAST(Str&, vm->py_repr(iterable[i]));
ss << vm->py_repr(iterable[i]);
if(i != iterable.size()-1) ss << ", ";
}
vm->_repr_recursion_set.erase(_0);
ss << ']';
return VAR(ss.str());
return ss.str();
});
_vm->bind__repr__(VM::tp_tuple, [](VM* vm, PyObject* _0){
_vm->bind__repr__(VM::tp_tuple, [](VM* vm, PyObject* _0) -> Str{
Tuple& iterable = _CAST(Tuple&, _0);
SStream ss;
ss << '(';
if(iterable.size() == 1){
ss << CAST(Str&, vm->py_repr(iterable[0]));
ss << vm->py_repr(iterable[0]);
ss << ',';
}else{
for(int i=0; i<iterable.size(); i++){
ss << CAST(Str&, vm->py_repr(iterable[i]));
ss << vm->py_repr(iterable[i]);
if(i != iterable.size()-1) ss << ", ";
}
}
ss << ')';
return VAR(ss.str());
return ss.str();
});
_vm->bind_func(VM::tp_list, __new__, -1, [](VM* vm, ArgsView args) {
@ -869,7 +872,7 @@ void __init_builtins(VM* _vm) {
for(int i=start; i<self.size(); i++){
if(vm->py_eq(self[i], obj)) return VAR(i);
}
vm->ValueError(_CAST(Str&, vm->py_repr(obj)) + " is not in list");
vm->ValueError(vm->py_repr(obj) + " is not in list");
return vm->None;
});
@ -882,7 +885,7 @@ void __init_builtins(VM* _vm) {
return vm->None;
}
}
vm->ValueError(_CAST(Str&, vm->py_repr(obj)) + " is not in list");
vm->ValueError(vm->py_repr(obj) + " is not in list");
return vm->None;
});
@ -1075,9 +1078,9 @@ void __init_builtins(VM* _vm) {
_vm->bind__hash__(VM::tp_bool, [](VM* vm, PyObject* _0) {
return (i64)_CAST(bool, _0);
});
_vm->bind__repr__(VM::tp_bool, [](VM* vm, PyObject* _0) {
_vm->bind__repr__(VM::tp_bool, [](VM* vm, PyObject* _0) -> Str{
bool val = _CAST(bool, _0);
return VAR(val ? "True" : "False");
return val ? "True" : "False";
});
_vm->bind__and__(VM::tp_bool, [](VM* vm, PyObject* _0, PyObject* _1) {
@ -1096,11 +1099,11 @@ void __init_builtins(VM* _vm) {
});
// tp_ellipsis / tp_NotImplementedType
_vm->bind__repr__(_vm->_tp(_vm->Ellipsis), [](VM* vm, PyObject* _0) {
return VAR("...");
_vm->bind__repr__(_vm->_tp(_vm->Ellipsis), [](VM* vm, PyObject* _0) -> Str{
return "...";
});
_vm->bind__repr__(_vm->_tp(_vm->NotImplemented), [](VM* vm, PyObject* _0) {
return VAR("NotImplemented");
_vm->bind__repr__(_vm->_tp(_vm->NotImplemented), [](VM* vm, PyObject* _0) -> Str{
return "NotImplemented";
});
// tp_bytes
@ -1148,7 +1151,7 @@ void __init_builtins(VM* _vm) {
return (i64)std::hash<std::string_view>()(view);
});
_vm->bind__repr__(VM::tp_bytes, [](VM* vm, PyObject* _0) {
_vm->bind__repr__(VM::tp_bytes, [](VM* vm, PyObject* _0) -> Str {
const Bytes& self = _CAST(Bytes&, _0);
SStream ss;
ss << "b'";
@ -1157,7 +1160,7 @@ void __init_builtins(VM* _vm) {
ss.write_hex((unsigned char)self[i]);
}
ss << "'";
return VAR(ss.str());
return ss.str();
});
_vm->bind__len__(VM::tp_bytes, [](VM* vm, PyObject* _0) {
return (i64)_CAST(Bytes&, _0).size();
@ -1189,14 +1192,14 @@ void __init_builtins(VM* _vm) {
return vm->True;
});
_vm->bind__repr__(VM::tp_slice, [](VM* vm, PyObject* _0) {
_vm->bind__repr__(VM::tp_slice, [](VM* vm, PyObject* _0) -> Str {
const Slice& self = _CAST(Slice&, _0);
SStream ss;
ss << "slice(";
ss << CAST(Str, vm->py_repr(self.start)) << ", ";
ss << CAST(Str, vm->py_repr(self.stop)) << ", ";
ss << CAST(Str, vm->py_repr(self.step)) << ")";
return VAR(ss.str());
ss << vm->py_repr(self.start) << ", ";
ss << vm->py_repr(self.stop) << ", ";
ss << vm->py_repr(self.step) << ")";
return ss.str();
});
// tp_mappingproxy
@ -1251,8 +1254,8 @@ void __init_builtins(VM* _vm) {
return ret;
});
_vm->bind__repr__(VM::tp_mappingproxy, [](VM* vm, PyObject* _0) {
if(vm->_repr_recursion_set.count(_0)) return VAR("{...}");
_vm->bind__repr__(VM::tp_mappingproxy, [](VM* vm, PyObject* _0) -> Str{
if(vm->_repr_recursion_set.count(_0)) return "{...}";
MappingProxy& self = _CAST(MappingProxy&, _0);
SStream ss;
ss << "mappingproxy({";
@ -1262,11 +1265,11 @@ void __init_builtins(VM* _vm) {
if(!first) ss << ", ";
first = false;
ss << k.escape() << ": ";
ss << CAST(Str, vm->py_repr(v));
ss << vm->py_repr(v);
}
vm->_repr_recursion_set.erase(_0);
ss << "})";
return VAR(ss.str());
return ss.str();
});
_vm->bind__contains__(VM::tp_mappingproxy, [](VM* vm, PyObject* _0, PyObject* _1) {
@ -1412,8 +1415,8 @@ void __init_builtins(VM* _vm) {
return vm->None;
});
_vm->bind__repr__(VM::tp_dict, [](VM* vm, PyObject* _0) {
if(vm->_repr_recursion_set.count(_0)) return VAR("{...}");
_vm->bind__repr__(VM::tp_dict, [](VM* vm, PyObject* _0) -> Str{
if(vm->_repr_recursion_set.count(_0)) return "{...}";
Dict& self = _CAST(Dict&, _0);
SStream ss;
ss << "{";
@ -1422,11 +1425,11 @@ void __init_builtins(VM* _vm) {
self.apply([&](PyObject* k, PyObject* v){
if(!first) ss << ", ";
first = false;
ss << CAST(Str&, vm->py_repr(k)) << ": " << CAST(Str&, vm->py_repr(v));
ss << vm->py_repr(k) << ": " << vm->py_repr(v);
});
vm->_repr_recursion_set.erase(_0);
ss << "}";
return VAR(ss.str());
return ss.str();
});
_vm->bind__eq__(VM::tp_dict, [](VM* vm, PyObject* _0, PyObject* _1) {
@ -1444,9 +1447,9 @@ void __init_builtins(VM* _vm) {
return vm->True;
});
_vm->bind__repr__(VM::tp_module, [](VM* vm, PyObject* _0) {
_vm->bind__repr__(VM::tp_module, [](VM* vm, PyObject* _0) -> Str {
const Str& path = CAST(Str&, _0->attr(__path__));
return VAR(_S("<module ", path.escape(), ">"));
return _S("<module ", path.escape(), ">");
});
// tp_property
@ -1493,14 +1496,14 @@ void __init_builtins(VM* _vm) {
return vm->None;
});
_vm->bind__repr__(VM::tp_exception, [](VM* vm, PyObject* _0) {
_vm->bind__repr__(VM::tp_exception, [](VM* vm, PyObject* _0) -> Str {
Exception& self = _CAST(Exception&, _0);
return VAR(_S(_type_name(vm, _0->type), '(', self.msg.escape(), ')'));
return _S(_type_name(vm, _0->type), '(', self.msg.escape(), ')');
});
_vm->bind__str__(VM::tp_exception, [](VM* vm, PyObject* _0) {
_vm->bind__str__(VM::tp_exception, [](VM* vm, PyObject* _0) -> Str{
Exception& self = _CAST(Exception&, _0);
return VAR(self.msg);
return self.msg;
});
_vm->register_user_class<RangeIter>(_vm->builtins, "_range_iter");
@ -1533,11 +1536,11 @@ void VM::__post_init_builtin_types(){
return self; // for generics
});
bind__repr__(tp_type, [](VM* vm, PyObject* self){
bind__repr__(tp_type, [](VM* vm, PyObject* self) -> Str{
SStream ss;
const PyTypeInfo& info = vm->_all_types[PK_OBJ_GET(Type, self)];
ss << "<class '" << info.name << "'>";
return VAR(ss.str());
return ss.str();
});
bind_property(_t(tp_object), "__class__", PK_LAMBDA(vm->_t(args[0])));
@ -1589,7 +1592,7 @@ void VM::__post_init_builtin_types(){
const Str& _2 = CAST(Str&, args[2]);
SStream ss;
for(int i=0; i<_0.size(); i++){
ss << CAST(Str&, vm->py_str(_0[i]));
ss << vm->py_str(_0[i]);
if(i != _0.size()-1) ss << _1;
}
ss << _2;

View File

@ -473,7 +473,7 @@ bool pkpy_py_repr(pkpy_vm* vm_handle) {
PK_ASSERT_N_EXTRA_ELEMENTS(1)
PyObject* item = vm->s_data.top();
PK_PROTECTED(
item = vm->py_repr(item);
item = VAR(vm->py_repr(item));
)
vm->s_data.top() = item;
return true;
@ -485,7 +485,7 @@ bool pkpy_py_str(pkpy_vm* vm_handle) {
PK_ASSERT_N_EXTRA_ELEMENTS(1)
PyObject* item = vm->s_data.top();
PK_PROTECTED(
item = vm->py_str(item);
item = VAR(vm->py_str(item));
)
vm->s_data.top() = item;
return true;

View File

@ -83,24 +83,34 @@ namespace pkpy{
__init_builtin_types();
}
PyObject* VM::py_str(PyObject* obj){
Str VM::py_str(PyObject* obj){
const PyTypeInfo* ti = _tp_info(obj);
if(ti->m__str__) return ti->m__str__(this, obj);
PyObject* self;
PyObject* f = get_unbound_method(obj, __str__, &self, false);
if(self != PY_NULL) return call_method(self, f);
if(self != PY_NULL){
PyObject* retval = call_method(self, f);
if(!is_type(retval, tp_str)){
throw std::runtime_error("object.__str__ must return str");
}
return PK_OBJ_GET(Str, retval);
}
return py_repr(obj);
}
PyObject* VM::py_repr(PyObject* obj){
Str VM::py_repr(PyObject* obj){
const PyTypeInfo* ti = _tp_info(obj);
if(ti->m__repr__) return ti->m__repr__(this, obj);
return call_method(obj, __repr__);
PyObject* retval = call_method(obj, __repr__);
if(!is_type(retval, tp_str)){
throw std::runtime_error("object.__repr__ must return str");
}
return PK_OBJ_GET(Str, retval);
}
PyObject* VM::py_json(PyObject* obj){
Str VM::py_json(PyObject* obj){
auto j = JsonSerializer(this, obj);
return VAR(j.serialize());
return j.serialize();
}
PyObject* VM::py_iter(PyObject* obj){
@ -495,7 +505,7 @@ i64 VM::py_hash(PyObject* obj){
}
PyObject* VM::__format_object(PyObject* obj, Str spec){
if(spec.empty()) return py_str(obj);
if(spec.empty()) return VAR(py_str(obj));
char type;
switch(spec.end()[-1]){
case 'f': case 'd': case 's':
@ -560,7 +570,7 @@ PyObject* VM::__format_object(PyObject* obj, Str spec){
}else if(type == 's'){
ret = CAST(Str&, obj);
}else{
ret = CAST(Str&, py_str(obj));
ret = py_str(obj);
}
if(width != -1 && width > ret.length()){
int pad = width - ret.length();
@ -602,7 +612,7 @@ static std::string _opcode_argstr(VM* vm, Bytecode byte, const CodeObject* co){
switch(byte.op){
case OP_LOAD_CONST: case OP_FORMAT_STRING: case OP_IMPORT_PATH:
if(vm != nullptr){
argStr += _S(" (", CAST(Str, vm->py_repr(co->consts[byte.arg])), ")").sv();
argStr += _S(" (", vm->py_repr(co->consts[byte.arg]), ")").sv();
}
break;
case OP_LOAD_NAME: case OP_LOAD_GLOBAL: case OP_LOAD_NONLOCAL: case OP_STORE_GLOBAL:
@ -1398,14 +1408,27 @@ void VM::bind__next__(Type type, PyObject* (*f)(VM*, PyObject*)){
return lambda_get_userdata<PyObject*(*)(VM*, PyObject*)>(args.begin())(vm, args[0]); \
}, f); \
}
BIND_UNARY_SPECIAL(__repr__)
BIND_UNARY_SPECIAL(__str__)
BIND_UNARY_SPECIAL(__iter__)
BIND_UNARY_SPECIAL(__neg__)
BIND_UNARY_SPECIAL(__invert__)
#undef BIND_UNARY_SPECIAL
void VM::bind__str__(Type type, Str (*f)(VM*, PyObject*)){
_all_types[type].m__str__ = f;
bind_func(type, __str__, 1, [](VM* vm, ArgsView args){
Str s = lambda_get_userdata<decltype(f)>(args.begin())(vm, args[0]);
return VAR(s);
}, f);
}
void VM::bind__repr__(Type type, Str (*f)(VM*, PyObject*)){
_all_types[type].m__repr__ = f;
bind_func(type, __repr__, 1, [](VM* vm, ArgsView args){
Str s = lambda_get_userdata<decltype(f)>(args.begin())(vm, args[0]);
return VAR(s);
}, f);
}
void VM::bind__hash__(Type type, i64 (*f)(VM*, PyObject*)){
_all_types[type].m__hash__ = f;
bind_func(type, __hash__, 1, [](VM* vm, ArgsView args){
@ -1460,7 +1483,6 @@ void Dict::_probe_0(PyObject *key, bool &ok, int &i) const{
ok = false;
i64 hash = vm->py_hash(key);
i = hash & _mask;
// std::cout << CAST(Str, vm->py_repr(key)) << " " << hash << " " << i << std::endl;
for(int j=0; j<_capacity; j++) {
if(_items[i].first != nullptr){
if(vm->py_eq(_items[i].first, key)) { ok = true; break; }
@ -1469,7 +1491,6 @@ void Dict::_probe_0(PyObject *key, bool &ok, int &i) const{
}
// https://github.com/python/cpython/blob/3.8/Objects/dictobject.c#L166
i = ((5*i) + 1) & _mask;
// std::cout << CAST(Str, vm->py_repr(key)) << " next: " << i << std::endl;
}
}
@ -1600,7 +1621,7 @@ void VM::__breakpoint(){
for(PyObject* obj: frame_0->_locals){
if(obj == PY_NULL) continue;
StrName name = frame_0->co->varnames[i++];
stdout_write(_S(name.sv(), " = ", CAST(Str&, vm->py_repr(obj)), '\n'));
stdout_write(_S(name.sv(), " = ", vm->py_repr(obj), '\n'));
}
continue;
}
@ -1644,7 +1665,7 @@ void VM::__breakpoint(){
if(cmd == "p" || cmd == "print"){
CodeObject_ code = compile(arg, "<stdin>", EVAL_MODE, true);
PyObject* retval = vm->_exec(code.get(), frame_0->_module, frame_0->_callable, frame_0->_locals);
stdout_write(CAST(Str&, vm->py_repr(retval)));
stdout_write(vm->py_repr(retval));
stdout_write("\n");
}else if(cmd == "!"){
CodeObject_ code = compile(arg, "<stdin>", EXEC_MODE, true);

View File

@ -166,7 +166,7 @@ assert repr(a) == "{1: 2, 3: 4, 'a': {...}}"
# test gc
import gc
gc.collect()
x = gc.collect()
for k, v in a.items():
pass
assert x+1 == gc.collect()
assert gc.collect() == 1