rustpython_vm/builtins/
bool.rs

1use 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    /// Convert Python bool into Rust bool.
35    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                // If descriptor returns Error, propagate it further
45                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
197// pub fn not(vm: &VirtualMachine, obj: &PyObject) -> PyResult<bool> {
198//     if obj.fast_isinstance(vm.ctx.types.bool_type) {
199//         let value = get_value(obj);
200//         Ok(!value)
201//     } else {
202//         Err(vm.new_type_error(format!("Can only invert a bool, on {:?}", obj)))
203//     }
204// }
205
206// Retrieve inner int value:
207pub(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}
Morty Proxy This is a proxified and sanitized view of the page, visit original site.