rustpython_vm/builtins/
bool.rs1use super::{PyInt, PyStrRef, PyType, PyTypeRef};
2use crate::{
3 class::PyClassImpl,
4 convert::{IntoPyException, ToPyObject, ToPyResult},
5 function::OptionalArg,
6 identifier,
7 protocol::PyNumberMethods,
8 types::{AsNumber, Constructor, Representable},
9 AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyResult, TryFromBorrowedObject,
10 VirtualMachine,
11};
12use malachite_bigint::Sign;
13use num_traits::Zero;
14use rustpython_format::FormatSpec;
15use std::fmt::{Debug, Formatter};
16
17impl ToPyObject for bool {
18 fn to_pyobject(self, vm: &VirtualMachine) -> PyObjectRef {
19 vm.ctx.new_bool(self).into()
20 }
21}
22
23impl<'a> TryFromBorrowedObject<'a> for bool {
24 fn try_from_borrowed_object(vm: &VirtualMachine, obj: &'a PyObject) -> PyResult<bool> {
25 if obj.fast_isinstance(vm.ctx.types.int_type) {
26 Ok(get_value(obj))
27 } else {
28 Err(vm.new_type_error(format!("Expected type bool, not {}", obj.class().name())))
29 }
30 }
31}
32
33impl PyObjectRef {
34 pub fn try_to_bool(self, vm: &VirtualMachine) -> PyResult<bool> {
36 if self.is(&vm.ctx.true_value) {
37 return Ok(true);
38 }
39 if self.is(&vm.ctx.false_value) {
40 return Ok(false);
41 }
42 let rs_bool = match vm.get_method(self.clone(), identifier!(vm, __bool__)) {
43 Some(method_or_err) => {
44 let method = method_or_err?;
46 let bool_obj = method.call((), vm)?;
47 if !bool_obj.fast_isinstance(vm.ctx.types.bool_type) {
48 return Err(vm.new_type_error(format!(
49 "__bool__ should return bool, returned type {}",
50 bool_obj.class().name()
51 )));
52 }
53
54 get_value(&bool_obj)
55 }
56 None => match vm.get_method(self, identifier!(vm, __len__)) {
57 Some(method_or_err) => {
58 let method = method_or_err?;
59 let bool_obj = method.call((), vm)?;
60 let int_obj = bool_obj.payload::<PyInt>().ok_or_else(|| {
61 vm.new_type_error(format!(
62 "'{}' object cannot be interpreted as an integer",
63 bool_obj.class().name()
64 ))
65 })?;
66
67 let len_val = int_obj.as_bigint();
68 if len_val.sign() == Sign::Minus {
69 return Err(vm.new_value_error("__len__() should return >= 0".to_owned()));
70 }
71 !len_val.is_zero()
72 }
73 None => true,
74 },
75 };
76 Ok(rs_bool)
77 }
78}
79
80#[pyclass(name = "bool", module = false, base = "PyInt")]
81pub struct PyBool;
82
83impl PyPayload for PyBool {
84 fn class(ctx: &Context) -> &'static Py<PyType> {
85 ctx.types.bool_type
86 }
87}
88
89impl Debug for PyBool {
90 fn fmt(&self, _f: &mut Formatter<'_>) -> std::fmt::Result {
91 todo!()
92 }
93}
94
95impl Constructor for PyBool {
96 type Args = OptionalArg<PyObjectRef>;
97
98 fn py_new(zelf: PyTypeRef, x: Self::Args, vm: &VirtualMachine) -> PyResult {
99 if !zelf.fast_isinstance(vm.ctx.types.type_type) {
100 let actual_class = zelf.class();
101 let actual_type = &actual_class.name();
102 return Err(vm.new_type_error(format!(
103 "requires a 'type' object but received a '{actual_type}'"
104 )));
105 }
106 let val = x.map_or(Ok(false), |val| val.try_to_bool(vm))?;
107 Ok(vm.ctx.new_bool(val).into())
108 }
109}
110
111#[pyclass(with(Constructor, AsNumber, Representable))]
112impl PyBool {
113 #[pymethod(magic)]
114 fn format(obj: PyObjectRef, spec: PyStrRef, vm: &VirtualMachine) -> PyResult<String> {
115 let new_bool = obj.try_to_bool(vm)?;
116 FormatSpec::parse(spec.as_str())
117 .and_then(|format_spec| format_spec.format_bool(new_bool))
118 .map_err(|err| err.into_pyexception(vm))
119 }
120
121 #[pymethod(name = "__ror__")]
122 #[pymethod(magic)]
123 fn or(lhs: PyObjectRef, rhs: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef {
124 if lhs.fast_isinstance(vm.ctx.types.bool_type)
125 && rhs.fast_isinstance(vm.ctx.types.bool_type)
126 {
127 let lhs = get_value(&lhs);
128 let rhs = get_value(&rhs);
129 (lhs || rhs).to_pyobject(vm)
130 } else {
131 get_py_int(&lhs).or(rhs, vm).to_pyobject(vm)
132 }
133 }
134
135 #[pymethod(name = "__rand__")]
136 #[pymethod(magic)]
137 fn and(lhs: PyObjectRef, rhs: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef {
138 if lhs.fast_isinstance(vm.ctx.types.bool_type)
139 && rhs.fast_isinstance(vm.ctx.types.bool_type)
140 {
141 let lhs = get_value(&lhs);
142 let rhs = get_value(&rhs);
143 (lhs && rhs).to_pyobject(vm)
144 } else {
145 get_py_int(&lhs).and(rhs, vm).to_pyobject(vm)
146 }
147 }
148
149 #[pymethod(name = "__rxor__")]
150 #[pymethod(magic)]
151 fn xor(lhs: PyObjectRef, rhs: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef {
152 if lhs.fast_isinstance(vm.ctx.types.bool_type)
153 && rhs.fast_isinstance(vm.ctx.types.bool_type)
154 {
155 let lhs = get_value(&lhs);
156 let rhs = get_value(&rhs);
157 (lhs ^ rhs).to_pyobject(vm)
158 } else {
159 get_py_int(&lhs).xor(rhs, vm).to_pyobject(vm)
160 }
161 }
162}
163
164impl AsNumber for PyBool {
165 fn as_number() -> &'static PyNumberMethods {
166 static AS_NUMBER: PyNumberMethods = PyNumberMethods {
167 and: Some(|a, b, vm| PyBool::and(a.to_owned(), b.to_owned(), vm).to_pyresult(vm)),
168 xor: Some(|a, b, vm| PyBool::xor(a.to_owned(), b.to_owned(), vm).to_pyresult(vm)),
169 or: Some(|a, b, vm| PyBool::or(a.to_owned(), b.to_owned(), vm).to_pyresult(vm)),
170 ..PyInt::AS_NUMBER
171 };
172 &AS_NUMBER
173 }
174}
175
176impl Representable for PyBool {
177 #[inline]
178 fn slot_repr(zelf: &PyObject, vm: &VirtualMachine) -> PyResult<PyStrRef> {
179 let name = if get_value(zelf.as_object()) {
180 vm.ctx.names.True
181 } else {
182 vm.ctx.names.False
183 };
184 Ok(name.to_owned())
185 }
186
187 #[cold]
188 fn repr_str(_zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<String> {
189 unreachable!("use slot_repr instead")
190 }
191}
192
193pub(crate) fn init(context: &Context) {
194 PyBool::extend_class(context, context.types.bool_type);
195}
196
197pub(crate) fn get_value(obj: &PyObject) -> bool {
208 !obj.payload::<PyInt>().unwrap().as_bigint().is_zero()
209}
210
211fn get_py_int(obj: &PyObject) -> &PyInt {
212 obj.payload::<PyInt>().unwrap()
213}