|
| 1 | +use std::borrow::Cow; |
| 2 | + |
1 | 3 | use super::{
|
2 | 4 | try_bigint_to_f64, PyByteArray, PyBytes, PyInt, PyIntRef, PyStr, PyStrRef, PyType, PyTypeRef,
|
3 | 5 | };
|
4 |
| -use crate::common::{float_ops, hash}; |
5 | 6 | use crate::{
|
6 | 7 | class::PyClassImpl,
|
7 |
| - convert::ToPyObject, |
| 8 | + common::{float_ops, hash}, |
| 9 | + convert::{ToPyObject, ToPyResult}, |
8 | 10 | format::FormatSpec,
|
9 | 11 | function::{
|
10 | 12 | ArgBytesLike, OptionalArg, OptionalOption,
|
11 | 13 | PyArithmeticValue::{self, *},
|
12 | 14 | PyComparisonValue,
|
13 | 15 | },
|
14 |
| - identifier, |
15 |
| - types::{Comparable, Constructor, Hashable, PyComparisonOp}, |
| 16 | + protocol::{PyNumber, PyNumberMethods}, |
| 17 | + types::{AsNumber, Comparable, Constructor, Hashable, PyComparisonOp}, |
16 | 18 | AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult,
|
17 | 19 | TryFromBorrowedObject, TryFromObject, VirtualMachine,
|
18 | 20 | };
|
@@ -58,32 +60,13 @@ impl From<f64> for PyFloat {
|
58 | 60 | }
|
59 | 61 |
|
60 | 62 | impl PyObject {
|
61 |
| - pub fn try_to_f64(&self, vm: &VirtualMachine) -> PyResult<Option<f64>> { |
62 |
| - if let Some(float) = self.payload_if_exact::<PyFloat>(vm) { |
63 |
| - return Ok(Some(float.value)); |
64 |
| - } |
65 |
| - if let Some(method) = vm.get_method(self.to_owned(), identifier!(vm, __float__)) { |
66 |
| - let result = vm.invoke(&method?, ())?; |
67 |
| - // TODO: returning strict subclasses of float in __float__ is deprecated |
68 |
| - return match result.payload::<PyFloat>() { |
69 |
| - Some(float_obj) => Ok(Some(float_obj.value)), |
70 |
| - None => Err(vm.new_type_error(format!( |
71 |
| - "__float__ returned non-float (type '{}')", |
72 |
| - result.class().name() |
73 |
| - ))), |
74 |
| - }; |
75 |
| - } |
76 |
| - if let Some(r) = vm.to_index_opt(self.to_owned()).transpose()? { |
77 |
| - return Ok(Some(try_bigint_to_f64(r.as_bigint(), vm)?)); |
78 |
| - } |
79 |
| - Ok(None) |
| 63 | + pub fn try_float_opt(&self, vm: &VirtualMachine) -> PyResult<Option<PyRef<PyFloat>>> { |
| 64 | + PyNumber::from(self).float_opt(vm) |
80 | 65 | }
|
81 |
| -} |
82 | 66 |
|
83 |
| -pub fn try_float(obj: &PyObject, vm: &VirtualMachine) -> PyResult<f64> { |
84 |
| - obj.try_to_f64(vm)?.ok_or_else(|| { |
85 |
| - vm.new_type_error(format!("must be real number, not {}", obj.class().name())) |
86 |
| - }) |
| 67 | + pub fn try_float(&self, vm: &VirtualMachine) -> PyResult<PyRef<PyFloat>> { |
| 68 | + PyNumber::from(self).float(vm) |
| 69 | + } |
87 | 70 | }
|
88 | 71 |
|
89 | 72 | pub(crate) fn to_op_float(obj: &PyObject, vm: &VirtualMachine) -> PyResult<Option<f64>> {
|
@@ -170,17 +153,10 @@ impl Constructor for PyFloat {
|
170 | 153 | let float_val = match arg {
|
171 | 154 | OptionalArg::Missing => 0.0,
|
172 | 155 | OptionalArg::Present(val) => {
|
173 |
| - let val = if cls.is(vm.ctx.types.float_type) { |
174 |
| - match val.downcast_exact::<PyFloat>(vm) { |
175 |
| - Ok(f) => return Ok(f.into()), |
176 |
| - Err(val) => val, |
177 |
| - } |
178 |
| - } else { |
179 |
| - val |
180 |
| - }; |
181 |
| - |
182 |
| - if let Some(f) = val.try_to_f64(vm)? { |
183 |
| - f |
| 156 | + if cls.is(vm.ctx.types.float_type) && val.class().is(PyFloat::class(vm)) { |
| 157 | + unsafe { val.downcast_unchecked::<PyFloat>().value } |
| 158 | + } else if let Some(f) = val.try_float_opt(vm)? { |
| 159 | + f.value |
184 | 160 | } else {
|
185 | 161 | float_from_string(val, vm)?
|
186 | 162 | }
|
@@ -220,7 +196,7 @@ fn float_from_string(val: PyObjectRef, vm: &VirtualMachine) -> PyResult<f64> {
|
220 | 196 | })
|
221 | 197 | }
|
222 | 198 |
|
223 |
| -#[pyimpl(flags(BASETYPE), with(Comparable, Hashable, Constructor))] |
| 199 | +#[pyimpl(flags(BASETYPE), with(Comparable, Hashable, Constructor, AsNumber))] |
224 | 200 | impl PyFloat {
|
225 | 201 | #[pymethod(magic)]
|
226 | 202 | fn format(&self, spec: PyStrRef, vm: &VirtualMachine) -> PyResult<String> {
|
@@ -562,6 +538,75 @@ impl Hashable for PyFloat {
|
562 | 538 | }
|
563 | 539 | }
|
564 | 540 |
|
| 541 | +impl AsNumber for PyFloat { |
| 542 | + fn as_number(_zelf: &crate::Py<Self>, _vm: &VirtualMachine) -> Cow<'static, PyNumberMethods> { |
| 543 | + Cow::Borrowed(&Self::NUMBER_METHODS) |
| 544 | + } |
| 545 | +} |
| 546 | + |
| 547 | +impl PyFloat { |
| 548 | + fn np_general_op<F, R>( |
| 549 | + number: &PyNumber, |
| 550 | + other: &PyObject, |
| 551 | + op: F, |
| 552 | + vm: &VirtualMachine, |
| 553 | + ) -> PyResult |
| 554 | + where |
| 555 | + F: FnOnce(f64, f64, &VirtualMachine) -> R, |
| 556 | + R: ToPyResult, |
| 557 | + { |
| 558 | + if let (Some(a), Some(b)) = (to_op_float(number.obj, vm)?, to_op_float(other, vm)?) { |
| 559 | + op(a, b, vm).to_pyresult(vm) |
| 560 | + } else { |
| 561 | + Ok(vm.ctx.not_implemented()) |
| 562 | + } |
| 563 | + } |
| 564 | + |
| 565 | + fn np_float_op<F>(number: &PyNumber, other: &PyObject, op: F, vm: &VirtualMachine) -> PyResult |
| 566 | + where |
| 567 | + F: FnOnce(f64, f64) -> f64, |
| 568 | + { |
| 569 | + Self::np_general_op(number, other, |a, b, _vm| op(a, b), vm) |
| 570 | + } |
| 571 | + |
| 572 | + fn np_float(number: &PyNumber, vm: &VirtualMachine) -> PyRef<PyFloat> { |
| 573 | + if let Some(zelf) = number.obj.downcast_ref_if_exact::<Self>(vm) { |
| 574 | + zelf.to_owned() |
| 575 | + } else { |
| 576 | + vm.ctx.new_float(Self::number_downcast(number).value) |
| 577 | + } |
| 578 | + } |
| 579 | + |
| 580 | + const NUMBER_METHODS: PyNumberMethods = PyNumberMethods { |
| 581 | + add: Some(|number, other, vm| Self::np_float_op(number, other, |a, b| a + b, vm)), |
| 582 | + subtract: Some(|number, other, vm| Self::np_float_op(number, other, |a, b| a - b, vm)), |
| 583 | + multiply: Some(|number, other, vm| Self::np_float_op(number, other, |a, b| a * b, vm)), |
| 584 | + remainder: Some(|number, other, vm| Self::np_general_op(number, other, inner_mod, vm)), |
| 585 | + divmod: Some(|number, other, vm| Self::np_general_op(number, other, inner_divmod, vm)), |
| 586 | + power: Some(|number, other, vm| Self::np_general_op(number, other, float_pow, vm)), |
| 587 | + negative: Some(|number, vm| { |
| 588 | + let value = Self::number_downcast(number).value; |
| 589 | + (-value).to_pyresult(vm) |
| 590 | + }), |
| 591 | + positive: Some(|number, vm| Self::np_float(number, vm).to_pyresult(vm)), |
| 592 | + absolute: Some(|number, vm| { |
| 593 | + let value = Self::number_downcast(number).value; |
| 594 | + value.abs().to_pyresult(vm) |
| 595 | + }), |
| 596 | + boolean: Some(|number, _vm| Ok(Self::number_downcast(number).value.is_zero())), |
| 597 | + int: Some(|number, vm| { |
| 598 | + let value = Self::number_downcast(number).value; |
| 599 | + try_to_bigint(value, vm).map(|x| vm.ctx.new_int(x)) |
| 600 | + }), |
| 601 | + float: Some(|number, vm| Ok(Self::np_float(number, vm))), |
| 602 | + floor_divide: Some(|number, other, vm| { |
| 603 | + Self::np_general_op(number, other, inner_floordiv, vm) |
| 604 | + }), |
| 605 | + true_divide: Some(|number, other, vm| Self::np_general_op(number, other, inner_div, vm)), |
| 606 | + ..*PyNumberMethods::not_implemented() |
| 607 | + }; |
| 608 | +} |
| 609 | + |
565 | 610 | // Retrieve inner float value:
|
566 | 611 | pub(crate) fn get_value(obj: &PyObject) -> f64 {
|
567 | 612 | obj.payload::<PyFloat>().unwrap().value
|
|
0 commit comments