Skip to content

Commit 20632ed

Browse files
qingshi163youknowone
authored andcommitted
Number protocol for PyInt
1 parent beeb449 commit 20632ed

File tree

4 files changed

+285
-121
lines changed

4 files changed

+285
-121
lines changed

vm/src/builtins/int.rs

Lines changed: 114 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ use bstr::ByteSlice;
1717
use num_bigint::{BigInt, BigUint, Sign};
1818
use num_integer::Integer;
1919
use num_traits::{One, Pow, PrimInt, Signed, ToPrimitive, Zero};
20+
use std::borrow::Cow;
2021
use std::fmt;
2122

2223
/// int(x=0) -> integer
@@ -263,7 +264,10 @@ impl Constructor for PyInt {
263264
val
264265
};
265266

266-
try_int(&val, vm)
267+
// try_int(&val, vm)
268+
PyNumber::from(val.as_ref())
269+
.int(vm)
270+
.map(|x| x.as_bigint().clone())
267271
}
268272
} else if let OptionalArg::Present(_) = options.base {
269273
Err(vm.new_type_error("int() missing string argument".to_owned()))
@@ -346,7 +350,7 @@ impl PyInt {
346350
}
347351
}
348352

349-
#[pyimpl(flags(BASETYPE), with(Comparable, Hashable, Constructor))]
353+
#[pyimpl(flags(BASETYPE), with(Comparable, Hashable, Constructor, AsNumber))]
350354
impl PyInt {
351355
#[pymethod(name = "__radd__")]
352356
#[pymethod(magic)]
@@ -751,6 +755,114 @@ impl Hashable for PyInt {
751755
}
752756
}
753757

758+
impl AsNumber for PyInt {
759+
fn as_number(_zelf: &crate::Py<Self>, _vm: &VirtualMachine) -> Cow<'static, PyNumberMethods> {
760+
Cow::Borrowed(&Self::NUMBER_METHODS)
761+
}
762+
}
763+
764+
impl PyInt {
765+
fn number_protocol_binop<F>(
766+
number: &PyNumber,
767+
other: &PyObject,
768+
op: &str,
769+
f: F,
770+
vm: &VirtualMachine,
771+
) -> PyResult
772+
where
773+
F: FnOnce(&BigInt, &BigInt) -> BigInt,
774+
{
775+
let (a, b) = Self::downcast_or_binop_error(number, other, op, vm)?;
776+
let ret = f(&a.value, &b.value);
777+
Ok(vm.ctx.new_int(ret).into())
778+
}
779+
780+
fn number_protocol_int(number: &PyNumber, vm: &VirtualMachine) -> PyIntRef {
781+
if let Some(zelf) = number.obj.downcast_ref_if_exact::<Self>(vm) {
782+
zelf.to_owned()
783+
} else {
784+
let zelf = Self::number_downcast(number);
785+
vm.ctx.new_int(zelf.value.clone())
786+
}
787+
}
788+
789+
const NUMBER_METHODS: PyNumberMethods = PyNumberMethods {
790+
add: Some(|number, other, vm| {
791+
Self::number_protocol_binop(number, other, "+", |a, b| a + b, vm)
792+
}),
793+
subtract: Some(|number, other, vm| {
794+
Self::number_protocol_binop(number, other, "-", |a, b| a - b, vm)
795+
}),
796+
multiply: Some(|number, other, vm| {
797+
Self::number_protocol_binop(number, other, "*", |a, b| a * b, vm)
798+
}),
799+
remainder: Some(|number, other, vm| {
800+
let (a, b) = Self::downcast_or_binop_error(number, other, "%", vm)?;
801+
inner_mod(&a.value, &b.value, vm)
802+
}),
803+
divmod: Some(|number, other, vm| {
804+
let (a, b) = Self::downcast_or_binop_error(number, other, "divmod()", vm)?;
805+
inner_divmod(&a.value, &b.value, vm)
806+
}),
807+
power: Some(|number, other, vm| {
808+
let (a, b) = Self::downcast_or_binop_error(number, other, "** or pow()", vm)?;
809+
inner_pow(&a.value, &b.value, vm)
810+
}),
811+
negative: Some(|number, vm| {
812+
let zelf = Self::number_downcast(number);
813+
Ok(vm.ctx.new_int(-&zelf.value).into())
814+
}),
815+
positive: Some(|number, vm| Ok(Self::number_protocol_int(number, vm).into())),
816+
absolute: Some(|number, vm| {
817+
let zelf = Self::number_downcast(number);
818+
Ok(vm.ctx.new_int(zelf.value.abs()).into())
819+
}),
820+
boolean: Some(|number, _vm| {
821+
let zelf = Self::number_downcast(number);
822+
Ok(zelf.value.is_zero())
823+
}),
824+
invert: Some(|number, vm| {
825+
let zelf = Self::number_downcast(number);
826+
Ok(vm.ctx.new_int(!&zelf.value).into())
827+
}),
828+
lshift: Some(|number, other, vm| {
829+
let (a, b) = Self::downcast_or_binop_error(number, other, "<<", vm)?;
830+
inner_lshift(&a.value, &b.value, vm)
831+
}),
832+
rshift: Some(|number, other, vm| {
833+
let (a, b) = Self::downcast_or_binop_error(number, other, ">>", vm)?;
834+
inner_rshift(&a.value, &b.value, vm)
835+
}),
836+
and: Some(|number, other, vm| {
837+
Self::number_protocol_binop(number, other, "&", |a, b| a & b, vm)
838+
}),
839+
xor: Some(|number, other, vm| {
840+
Self::number_protocol_binop(number, other, "^", |a, b| a ^ b, vm)
841+
}),
842+
or: Some(|number, other, vm| {
843+
Self::number_protocol_binop(number, other, "|", |a, b| a | b, vm)
844+
}),
845+
int: Some(|number, other| Ok(Self::number_protocol_int(number, other))),
846+
float: Some(|number, vm| {
847+
let zelf = number
848+
.obj
849+
.downcast_ref::<Self>()
850+
.ok_or_else(|| vm.new_type_error("an integer is required".to_owned()))?;
851+
try_to_float(&zelf.value, vm).map(|x| vm.ctx.new_float(x))
852+
}),
853+
floor_divide: Some(|number, other, vm| {
854+
let (a, b) = Self::downcast_or_binop_error(number, other, "//", vm)?;
855+
inner_floordiv(&a.value, &b.value, vm)
856+
}),
857+
true_divide: Some(|number, other, vm| {
858+
let (a, b) = Self::downcast_or_binop_error(number, other, "/", vm)?;
859+
inner_truediv(&a.value, &b.value, vm)
860+
}),
861+
index: Some(|number, vm| Ok(Self::number_protocol_int(number, vm))),
862+
..*PyNumberMethods::not_implemented()
863+
};
864+
}
865+
754866
#[derive(FromArgs)]
755867
pub struct IntOptions {
756868
#[pyarg(positional, optional)]
@@ -926,65 +1038,6 @@ fn i2f(int: &BigInt) -> Option<f64> {
9261038
int.to_f64().filter(|f| f.is_finite())
9271039
}
9281040

929-
pub(crate) fn try_int(obj: &PyObject, vm: &VirtualMachine) -> PyResult<BigInt> {
930-
fn try_convert(obj: &PyObject, lit: &[u8], vm: &VirtualMachine) -> PyResult<BigInt> {
931-
let base = 10;
932-
match bytes_to_int(lit, base) {
933-
Some(i) => Ok(i),
934-
None => Err(vm.new_value_error(format!(
935-
"invalid literal for int() with base {}: {}",
936-
base,
937-
obj.repr(vm)?,
938-
))),
939-
}
940-
}
941-
942-
// test for strings and bytes
943-
if let Some(s) = obj.downcast_ref::<PyStr>() {
944-
return try_convert(obj, s.as_str().as_bytes(), vm);
945-
}
946-
if let Ok(r) = obj.try_bytes_like(vm, |x| try_convert(obj, x, vm)) {
947-
return r;
948-
}
949-
// strict `int` check
950-
if let Some(int) = obj.payload_if_exact::<PyInt>(vm) {
951-
return Ok(int.as_bigint().clone());
952-
}
953-
// call __int__, then __index__, then __trunc__ (converting the __trunc__ result via __index__ if needed)
954-
// TODO: using __int__ is deprecated and removed in Python 3.10
955-
if let Some(method) = vm.get_method(obj.to_owned(), identifier!(vm, __int__)) {
956-
let result = vm.invoke(&method?, ())?;
957-
return match result.payload::<PyInt>() {
958-
Some(int_obj) => Ok(int_obj.as_bigint().clone()),
959-
None => Err(vm.new_type_error(format!(
960-
"__int__ returned non-int (type '{}')",
961-
result.class().name()
962-
))),
963-
};
964-
}
965-
// TODO: returning strict subclasses of int in __index__ is deprecated
966-
if let Some(r) = vm.to_index_opt(obj.to_owned()).transpose()? {
967-
return Ok(r.as_bigint().clone());
968-
}
969-
if let Some(method) = vm.get_method(obj.to_owned(), identifier!(vm, __trunc__)) {
970-
let result = vm.invoke(&method?, ())?;
971-
return vm
972-
.to_index_opt(result.clone())
973-
.unwrap_or_else(|| {
974-
Err(vm.new_type_error(format!(
975-
"__trunc__ returned non-Integral (type {})",
976-
result.class().name()
977-
)))
978-
})
979-
.map(|int_obj| int_obj.as_bigint().clone());
980-
}
981-
982-
Err(vm.new_type_error(format!(
983-
"int() argument must be a string, a bytes-like object or a number, not '{}'",
984-
obj.class().name()
985-
)))
986-
}
987-
9881041
pub(crate) fn init(context: &Context) {
9891042
PyInt::extend_class(context, context.types.int_type);
9901043
}

vm/src/protocol/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
mod buffer;
22
mod iter;
33
mod mapping;
4+
mod number;
45
mod object;
56
mod sequence;
67

78
pub use buffer::{BufferDescriptor, BufferMethods, BufferResizeGuard, PyBuffer, VecBuffer};
89
pub use iter::{PyIter, PyIterIter, PyIterReturn};
910
pub use mapping::{PyMapping, PyMappingMethods};
11+
pub use number::{PyNumber, PyNumberMethods};
1012
pub use sequence::{PySequence, PySequenceMethods};

0 commit comments

Comments
 (0)