diff --git a/include/pocketpy/interpreter/vm.hpp b/include/pocketpy/interpreter/vm.hpp index d1d16da6..d2b5d1a8 100644 --- a/include/pocketpy/interpreter/vm.hpp +++ b/include/pocketpy/interpreter/vm.hpp @@ -242,7 +242,7 @@ public: List py_list(PyVar); // x -> list(x) bool py_callable(PyVar obj); // x -> callable(x) bool py_bool(PyVar obj){ // x -> bool(x) - if(obj.type == tp_bool) return obj._bool; + if(obj.type == tp_bool) return (bool)obj.extra; return __py_bool_non_trivial(obj); } i64 py_hash(PyVar obj); // x -> hash(x) @@ -604,7 +604,7 @@ __T _py_cast__internal(VM* vm, PyVar obj) { vm->TypeError("expected 'bool', got " + _type_name(vm, vm->_tp(obj)).escape()); } } - return obj._bool; + return (bool)obj.extra; } else if constexpr(is_integral_v) { static_assert(!std::is_reference_v<__T>); // int diff --git a/include/pocketpy/objects/base.h b/include/pocketpy/objects/base.h index 07d7640c..18024469 100644 --- a/include/pocketpy/objects/base.h +++ b/include/pocketpy/objects/base.h @@ -41,17 +41,18 @@ typedef struct PyVar{ static_assert(sizeof(PyVar) == 16, "sizeof(PyVar) != 16"); -extern const pkpy_Type tp_object, tp_type; -extern const pkpy_Type tp_int, tp_float, tp_bool, tp_str; -extern const pkpy_Type tp_list, tp_tuple; -extern const pkpy_Type tp_slice, tp_range, tp_module; -extern const pkpy_Type tp_function, tp_native_func, tp_bound_method; -extern const pkpy_Type tp_super, tp_exception, tp_bytes, tp_mappingproxy; -extern const pkpy_Type tp_dict, tp_property, tp_star_wrapper; -extern const pkpy_Type tp_staticmethod, tp_classmethod; -extern const pkpy_Type tp_none_type, tp_not_implemented_type; -extern const pkpy_Type tp_ellipsis; -extern const pkpy_Type tp_op_call, tp_op_yield; +/* predefined vars */ +static const pkpy_Type tp_object = 1, tp_type = 2; +static const pkpy_Type tp_int = 3, tp_float = 4, tp_bool = 5, tp_str = 6; +static const pkpy_Type tp_list = 7, tp_tuple = 8; +static const pkpy_Type tp_slice = 9, tp_range = 10, tp_module = 11; +static const pkpy_Type tp_function = 12, tp_native_func = 13, tp_bound_method = 14; +static const pkpy_Type tp_super = 15, tp_exception = 16, tp_bytes = 17, tp_mappingproxy = 18; +static const pkpy_Type tp_dict = 19, tp_property = 20, tp_star_wrapper = 21; +static const pkpy_Type tp_staticmethod = 22, tp_classmethod = 23; +static const pkpy_Type tp_none_type = 24, tp_not_implemented_type = 25; +static const pkpy_Type tp_ellipsis = 26; +static const pkpy_Type tp_op_call = 27, tp_op_yield = 28; PK_INLINE bool PyVar__is_null(const PyVar* self) { return self->type == 0; } PK_INLINE int64_t PyVar__hash(const PyVar* self) { return self->extra + self->_i64; } diff --git a/src/interpreter/ceval.cpp b/src/interpreter/ceval.cpp index e1c9bdac..2947278f 100644 --- a/src/interpreter/ceval.cpp +++ b/src/interpreter/ceval.cpp @@ -67,25 +67,25 @@ void VM::__op_unpack_sequence(uint16_t arg) { bool VM::py_lt(PyVar _0, PyVar _1) { BINARY_F_COMPARE(__lt__, "<", __gt__); assert(ret.type == tp_bool); - return ret._bool; + return ret.extra; } bool VM::py_le(PyVar _0, PyVar _1) { BINARY_F_COMPARE(__le__, "<=", __ge__); assert(ret.type == tp_bool); - return ret._bool; + return ret.extra; } bool VM::py_gt(PyVar _0, PyVar _1) { BINARY_F_COMPARE(__gt__, ">", __lt__); assert(ret.type == tp_bool); - return ret._bool; + return ret.extra; } bool VM::py_ge(PyVar _0, PyVar _1) { BINARY_F_COMPARE(__ge__, ">=", __le__); assert(ret.type == tp_bool); - return ret._bool; + return ret.extra; } #undef BINARY_F_COMPARE diff --git a/src/interpreter/vm.cpp b/src/interpreter/vm.cpp index e5c9ca42..7e8b1232 100644 --- a/src/interpreter/vm.cpp +++ b/src/interpreter/vm.cpp @@ -60,7 +60,7 @@ struct JsonSerializer { if(std::isinf(val) || std::isnan(val)) vm->ValueError("cannot jsonify 'nan' or 'inf'"); ss << val; } else if(obj_t == vm->tp_bool) { - ss << (obj._bool ? "true" : "false"); + ss << (obj.extra ? "true" : "false"); } else if(obj_t == vm->tp_str) { ss << _CAST(Str&, obj).escape('"'); } else if(obj_t == vm->tp_list) { @@ -235,18 +235,18 @@ bool VM::py_eq(PyVar lhs, PyVar rhs) { PyVar res; if(ti->m__eq__) { res = ti->m__eq__(this, lhs, rhs); - if(!is_not_implemented(res)) return res._bool; + if(!is_not_implemented(res)) return res.extra; } res = call_method(lhs, __eq__, rhs); - if(!is_not_implemented(res)) return res._bool; + if(!is_not_implemented(res)) return res.extra; ti = _tp_info(rhs); if(ti->m__eq__) { res = ti->m__eq__(this, rhs, lhs); - if(!is_not_implemented(res)) return res._bool; + if(!is_not_implemented(res)) return res.extra; } res = call_method(rhs, __eq__, lhs); - if(!is_not_implemented(res)) return res._bool; + if(!is_not_implemented(res)) return res.extra; return false; } diff --git a/src/objects/base.c b/src/objects/base.c index 21dc4956..a39f41e5 100644 --- a/src/objects/base.c +++ b/src/objects/base.c @@ -1,23 +1,5 @@ #include "pocketpy/objects/base.h" -/* predefined vars */ -const pkpy_Type tp_object = 1, tp_type = 2; -const pkpy_Type tp_int = 3, tp_float = 4, tp_bool = 5, tp_str = 6; -const pkpy_Type tp_list = 7, tp_tuple = 8; -const pkpy_Type tp_slice = 9, tp_range = 10, tp_module = 11; -const pkpy_Type tp_function = 12, tp_native_func = 13, tp_bound_method = 14; -const pkpy_Type tp_super = 15, tp_exception = 16, tp_bytes = 17, tp_mappingproxy = 18; -const pkpy_Type tp_dict = 19, tp_property = 20, tp_star_wrapper = 21; -const pkpy_Type tp_staticmethod = 22, tp_classmethod = 23; -const pkpy_Type tp_none_type = 24, tp_not_implemented_type = 25; -const pkpy_Type tp_ellipsis = 26; -const pkpy_Type tp_op_call = 27, tp_op_yield = 28; - -PyVar pkpy_True = {.type=tp_bool, .is_ptr=false, ._bool=true}; -PyVar pkpy_False = {.type=tp_bool, .is_ptr=false, ._bool=false}; -PyVar pkpy_None = {.type=tp_none_type, .is_ptr=false}; -PyVar pkpy_NotImplemented = {.type=tp_not_implemented_type, .is_ptr=false}; -PyVar pkpy_Ellipsis = {.type=tp_ellipsis, .is_ptr=false}; PyVar pkpy_NULL = {.type=0, .is_ptr=false}; PyVar pkpy_OP_CALL = {.type=tp_op_call, .is_ptr=false}; PyVar pkpy_OP_YIELD = {.type=tp_op_yield, .is_ptr=false}; diff --git a/src/objects/object.c b/src/objects/object.c index c6e8f25e..302acd22 100644 --- a/src/objects/object.c +++ b/src/objects/object.c @@ -6,3 +6,16 @@ void PyVar__ctor3(PyVar* self, PyObject* existing){ self->is_ptr = true; self->_obj = existing; } + +static PyObject __true_obj = {.type=tp_bool, .gc_is_large=false, .gc_marked=false, ._attr=NULL}; +static PyObject __false_obj = {.type=tp_bool, .gc_is_large=false, .gc_marked=false, ._attr=NULL}; +static PyObject __none_obj = {.type=tp_none_type, .gc_is_large=false, .gc_marked=false, ._attr=NULL}; +static PyObject __not_implemented_obj = {.type=tp_not_implemented_type, .gc_is_large=false, .gc_marked=false, ._attr=NULL}; +static PyObject __ellipsis_obj = {.type=tp_ellipsis, .gc_is_large=false, .gc_marked=false, ._attr=NULL}; + +/* Must be heap objects to support `==` and `is` and `is not` */ +PyVar pkpy_True = {.type=tp_bool, .is_ptr=true, .extra=1, ._obj=&__true_obj}; +PyVar pkpy_False = {.type=tp_bool, .is_ptr=true, .extra=0, ._obj=&__false_obj}; +PyVar pkpy_None = {.type=tp_none_type, .is_ptr=true, ._obj=&__none_obj}; +PyVar pkpy_NotImplemented = {.type=tp_not_implemented_type, .is_ptr=true, ._obj=&__not_implemented_obj}; +PyVar pkpy_Ellipsis = {.type=tp_ellipsis, .is_ptr=true, ._obj=&__ellipsis_obj}; \ No newline at end of file diff --git a/src/pocketpy.cpp b/src/pocketpy.cpp index bb35e4ab..69c5447d 100644 --- a/src/pocketpy.cpp +++ b/src/pocketpy.cpp @@ -375,7 +375,9 @@ void __init_builtins(VM* _vm) { }); _vm->bind__eq__(VM::tp_object, [](VM* vm, PyVar _0, PyVar _1) { - return PyVar__IS_OP(&_0, &_1) ? vm->True : vm->False; + if(!_0.is_ptr) vm->TypeError("cannot compare tagged object: _0"); + if(!_1.is_ptr) vm->TypeError("cannot compare tagged object: _1"); + return _0._obj == _1._obj ? vm->True : vm->False; }); _vm->__cached_object_new = _vm->bind_func(VM::tp_object, __new__, 1, [](VM* vm, ArgsView args) { @@ -462,7 +464,7 @@ void __init_builtins(VM* _vm) { switch(vm->_tp(args[1])) { case VM::tp_float: return VAR((i64)_CAST(f64, args[1])); case VM::tp_int: return args[1]; - case VM::tp_bool: return VAR(args[1]._bool ? 1 : 0); + case VM::tp_bool: return VAR(args[1].extra ? 1 : 0); case VM::tp_str: break; default: vm->TypeError("invalid arguments for int()"); } @@ -543,7 +545,7 @@ void __init_builtins(VM* _vm) { switch(vm->_tp(args[1])) { case VM::tp_int: return VAR((f64)CAST(i64, args[1])); case VM::tp_float: return args[1]; - case VM::tp_bool: return VAR(args[1]._bool ? 1.0 : 0.0); + case VM::tp_bool: return VAR(args[1].extra ? 1.0 : 0.0); case VM::tp_str: break; default: vm->TypeError("invalid arguments for float()"); } @@ -1151,7 +1153,7 @@ void __init_builtins(VM* _vm) { return VAR(_CAST(bool, _0) != CAST(bool, _1)); }); _vm->bind__eq__(VM::tp_bool, [](VM* vm, PyVar _0, PyVar _1) { - if(is_type(_1, vm->tp_bool)) return VAR(_0._bool == _1._bool); + if(is_type(_1, vm->tp_bool)) return VAR(_0.extra == _1.extra); if(is_int(_1)) return VAR(_CAST(bool, _0) == (bool)CAST(i64, _1)); return vm->NotImplemented; }); diff --git a/tests/03_bool.py b/tests/03_bool.py index a9b2f219..c69ea403 100644 --- a/tests/03_bool.py +++ b/tests/03_bool.py @@ -17,4 +17,9 @@ assert bool(1) == True assert bool([]) == False assert bool("abc") == True assert bool([1,2]) == True -assert bool('') == False \ No newline at end of file +assert bool('') == False + +# extra compare for None +assert None == None +assert ... == ... +assert NotImplemented == NotImplemented diff --git a/tests/99_bugs.py b/tests/99_bugs.py index 9470706f..a33574a9 100644 --- a/tests/99_bugs.py +++ b/tests/99_bugs.py @@ -42,9 +42,11 @@ if not inq: else: assert False -if inq is not 1: +a = object() +b = object() +if a is not b: assert True -if inq is not 0: +if a is 0: assert False def g(x):