mirror of
https://github.com/RustPython/RustPython.git
synced 2026-06-17 01:51:39 +09:00
524 lines
18 KiB
Rust
524 lines
18 KiB
Rust
use std::hash::{Hash, Hasher};
|
|
|
|
use num_bigint::{BigInt, ToBigInt};
|
|
use num_integer::Integer;
|
|
use num_traits::{Pow, Signed, ToPrimitive, Zero};
|
|
|
|
use crate::format::FormatSpec;
|
|
use crate::function::{OptionalArg, PyFuncArgs};
|
|
use crate::pyobject::{
|
|
IntoPyObject, PyContext, PyObjectRef, PyRef, PyResult, PyValue, TryFromObject, TypeProtocol,
|
|
};
|
|
use crate::vm::VirtualMachine;
|
|
|
|
use super::objfloat;
|
|
use super::objstr;
|
|
use super::objtype;
|
|
use crate::obj::objtype::PyClassRef;
|
|
|
|
#[derive(Debug)]
|
|
pub struct PyInt {
|
|
// TODO: shouldn't be public
|
|
pub value: BigInt,
|
|
}
|
|
|
|
pub type PyIntRef = PyRef<PyInt>;
|
|
|
|
impl PyInt {
|
|
pub fn new<T: Into<BigInt>>(i: T) -> Self {
|
|
PyInt { value: i.into() }
|
|
}
|
|
}
|
|
|
|
impl IntoPyObject for BigInt {
|
|
fn into_pyobject(self, vm: &VirtualMachine) -> PyResult {
|
|
Ok(vm.ctx.new_int(self))
|
|
}
|
|
}
|
|
|
|
impl PyValue for PyInt {
|
|
fn class(vm: &VirtualMachine) -> PyClassRef {
|
|
vm.ctx.int_type()
|
|
}
|
|
}
|
|
|
|
macro_rules! impl_into_pyobject_int {
|
|
($($t:ty)*) => {$(
|
|
impl IntoPyObject for $t {
|
|
fn into_pyobject(self, vm: &VirtualMachine) -> PyResult {
|
|
Ok(vm.ctx.new_int(self))
|
|
}
|
|
}
|
|
)*};
|
|
}
|
|
|
|
impl_into_pyobject_int!(isize i8 i16 i32 i64 usize u8 u16 u32 u64) ;
|
|
|
|
macro_rules! impl_try_from_object_int {
|
|
($(($t:ty, $to_prim:ident),)*) => {$(
|
|
impl TryFromObject for $t {
|
|
fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
|
|
match PyRef::<PyInt>::try_from_object(vm, obj)?.value.$to_prim() {
|
|
Some(value) => Ok(value),
|
|
None => Err(
|
|
vm.new_overflow_error(concat!(
|
|
"Int value cannot fit into Rust ",
|
|
stringify!($t)
|
|
).to_string())
|
|
),
|
|
}
|
|
}
|
|
}
|
|
)*};
|
|
}
|
|
|
|
impl_try_from_object_int!(
|
|
(isize, to_isize),
|
|
(i8, to_i8),
|
|
(i16, to_i16),
|
|
(i32, to_i32),
|
|
(i64, to_i64),
|
|
(usize, to_usize),
|
|
(u8, to_u8),
|
|
(u16, to_u16),
|
|
(u32, to_u32),
|
|
(u64, to_u64),
|
|
);
|
|
|
|
impl PyIntRef {
|
|
fn pass_value(self, _vm: &VirtualMachine) -> Self {
|
|
self
|
|
}
|
|
|
|
fn eq(self, other: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef {
|
|
if objtype::isinstance(&other, &vm.ctx.int_type()) {
|
|
vm.ctx.new_bool(self.value == *get_value(&other))
|
|
} else {
|
|
vm.ctx.not_implemented()
|
|
}
|
|
}
|
|
|
|
fn ne(self, other: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef {
|
|
if objtype::isinstance(&other, &vm.ctx.int_type()) {
|
|
vm.ctx.new_bool(self.value != *get_value(&other))
|
|
} else {
|
|
vm.ctx.not_implemented()
|
|
}
|
|
}
|
|
|
|
fn lt(self, other: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef {
|
|
if objtype::isinstance(&other, &vm.ctx.int_type()) {
|
|
vm.ctx.new_bool(self.value < *get_value(&other))
|
|
} else {
|
|
vm.ctx.not_implemented()
|
|
}
|
|
}
|
|
|
|
fn le(self, other: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef {
|
|
if objtype::isinstance(&other, &vm.ctx.int_type()) {
|
|
vm.ctx.new_bool(self.value <= *get_value(&other))
|
|
} else {
|
|
vm.ctx.not_implemented()
|
|
}
|
|
}
|
|
|
|
fn gt(self, other: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef {
|
|
if objtype::isinstance(&other, &vm.ctx.int_type()) {
|
|
vm.ctx.new_bool(self.value > *get_value(&other))
|
|
} else {
|
|
vm.ctx.not_implemented()
|
|
}
|
|
}
|
|
|
|
fn ge(self, other: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef {
|
|
if objtype::isinstance(&other, &vm.ctx.int_type()) {
|
|
vm.ctx.new_bool(self.value >= *get_value(&other))
|
|
} else {
|
|
vm.ctx.not_implemented()
|
|
}
|
|
}
|
|
|
|
fn add(self, other: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef {
|
|
if objtype::isinstance(&other, &vm.ctx.int_type()) {
|
|
vm.ctx.new_int((&self.value) + get_value(&other))
|
|
} else {
|
|
vm.ctx.not_implemented()
|
|
}
|
|
}
|
|
|
|
fn sub(self, other: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef {
|
|
if objtype::isinstance(&other, &vm.ctx.int_type()) {
|
|
vm.ctx.new_int((&self.value) - get_value(&other))
|
|
} else {
|
|
vm.ctx.not_implemented()
|
|
}
|
|
}
|
|
|
|
fn rsub(self, other: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef {
|
|
if objtype::isinstance(&other, &vm.ctx.int_type()) {
|
|
vm.ctx.new_int(get_value(&other) - (&self.value))
|
|
} else {
|
|
vm.ctx.not_implemented()
|
|
}
|
|
}
|
|
|
|
fn mul(self, other: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef {
|
|
if objtype::isinstance(&other, &vm.ctx.int_type()) {
|
|
vm.ctx.new_int((&self.value) * get_value(&other))
|
|
} else {
|
|
vm.ctx.not_implemented()
|
|
}
|
|
}
|
|
|
|
fn truediv(self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult {
|
|
if objtype::isinstance(&other, &vm.ctx.int_type()) {
|
|
div_ints(vm, &self.value, &get_value(&other))
|
|
} else {
|
|
Ok(vm.ctx.not_implemented())
|
|
}
|
|
}
|
|
|
|
fn rtruediv(self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult {
|
|
if objtype::isinstance(&other, &vm.ctx.int_type()) {
|
|
div_ints(vm, &get_value(&other), &self.value)
|
|
} else {
|
|
Ok(vm.ctx.not_implemented())
|
|
}
|
|
}
|
|
|
|
fn floordiv(self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult {
|
|
if objtype::isinstance(&other, &vm.ctx.int_type()) {
|
|
let v2 = get_value(&other);
|
|
if *v2 != BigInt::zero() {
|
|
Ok(vm.ctx.new_int((&self.value) / v2))
|
|
} else {
|
|
Err(vm.new_zero_division_error("integer floordiv by zero".to_string()))
|
|
}
|
|
} else {
|
|
Ok(vm.ctx.not_implemented())
|
|
}
|
|
}
|
|
|
|
fn lshift(self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult {
|
|
if !objtype::isinstance(&other, &vm.ctx.int_type()) {
|
|
return Err(vm.new_type_error(format!(
|
|
"unsupported operand type(s) for << '{}' and '{}'",
|
|
objtype::get_type_name(&self.as_object().typ()),
|
|
objtype::get_type_name(&other.typ())
|
|
)));
|
|
}
|
|
|
|
if let Some(n_bits) = get_value(&other).to_usize() {
|
|
return Ok(vm.ctx.new_int((&self.value) << n_bits));
|
|
}
|
|
|
|
// i2 failed `to_usize()` conversion
|
|
match get_value(&other) {
|
|
v if *v < BigInt::zero() => Err(vm.new_value_error("negative shift count".to_string())),
|
|
v if *v > BigInt::from(usize::max_value()) => {
|
|
Err(vm.new_overflow_error("the number is too large to convert to int".to_string()))
|
|
}
|
|
_ => panic!("Failed converting {} to rust usize", get_value(&other)),
|
|
}
|
|
}
|
|
|
|
fn rshift(self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult {
|
|
if !objtype::isinstance(&other, &vm.ctx.int_type()) {
|
|
return Err(vm.new_type_error(format!(
|
|
"unsupported operand type(s) for >> '{}' and '{}'",
|
|
objtype::get_type_name(&self.as_object().typ()),
|
|
objtype::get_type_name(&other.typ())
|
|
)));
|
|
}
|
|
|
|
if let Some(n_bits) = get_value(&other).to_usize() {
|
|
return Ok(vm.ctx.new_int((&self.value) >> n_bits));
|
|
}
|
|
|
|
// i2 failed `to_usize()` conversion
|
|
match get_value(&other) {
|
|
v if *v < BigInt::zero() => Err(vm.new_value_error("negative shift count".to_string())),
|
|
v if *v > BigInt::from(usize::max_value()) => {
|
|
Err(vm.new_overflow_error("the number is too large to convert to int".to_string()))
|
|
}
|
|
_ => panic!("Failed converting {} to rust usize", get_value(&other)),
|
|
}
|
|
}
|
|
|
|
fn xor(self, other: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef {
|
|
if objtype::isinstance(&other, &vm.ctx.int_type()) {
|
|
vm.ctx.new_int((&self.value) ^ get_value(&other))
|
|
} else {
|
|
vm.ctx.not_implemented()
|
|
}
|
|
}
|
|
|
|
fn rxor(self, other: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef {
|
|
if objtype::isinstance(&other, &vm.ctx.int_type()) {
|
|
vm.ctx.new_int(get_value(&other) ^ (&self.value))
|
|
} else {
|
|
vm.ctx.not_implemented()
|
|
}
|
|
}
|
|
|
|
fn or(self, other: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef {
|
|
if objtype::isinstance(&other, &vm.ctx.int_type()) {
|
|
vm.ctx.new_int((&self.value) | get_value(&other))
|
|
} else {
|
|
vm.ctx.not_implemented()
|
|
}
|
|
}
|
|
|
|
fn and(self, other: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef {
|
|
if objtype::isinstance(&other, &vm.ctx.int_type()) {
|
|
let v2 = get_value(&other);
|
|
vm.ctx.new_int((&self.value) & v2)
|
|
} else {
|
|
vm.ctx.not_implemented()
|
|
}
|
|
}
|
|
|
|
fn pow(self, other: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef {
|
|
if objtype::isinstance(&other, &vm.ctx.int_type()) {
|
|
let v2 = get_value(&other).to_u32().unwrap();
|
|
vm.ctx.new_int(self.value.pow(v2))
|
|
} else if objtype::isinstance(&other, &vm.ctx.float_type()) {
|
|
let v2 = objfloat::get_value(&other);
|
|
vm.ctx.new_float((self.value.to_f64().unwrap()).powf(v2))
|
|
} else {
|
|
vm.ctx.not_implemented()
|
|
}
|
|
}
|
|
|
|
fn mod_(self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult {
|
|
if objtype::isinstance(&other, &vm.ctx.int_type()) {
|
|
let v2 = get_value(&other);
|
|
if *v2 != BigInt::zero() {
|
|
Ok(vm.ctx.new_int((&self.value) % v2))
|
|
} else {
|
|
Err(vm.new_zero_division_error("integer modulo by zero".to_string()))
|
|
}
|
|
} else {
|
|
Ok(vm.ctx.not_implemented())
|
|
}
|
|
}
|
|
|
|
fn divmod(self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult {
|
|
if objtype::isinstance(&other, &vm.ctx.int_type()) {
|
|
let v2 = get_value(&other);
|
|
if *v2 != BigInt::zero() {
|
|
let (r1, r2) = self.value.div_rem(v2);
|
|
Ok(vm
|
|
.ctx
|
|
.new_tuple(vec![vm.ctx.new_int(r1), vm.ctx.new_int(r2)]))
|
|
} else {
|
|
Err(vm.new_zero_division_error("integer divmod by zero".to_string()))
|
|
}
|
|
} else {
|
|
Ok(vm.ctx.not_implemented())
|
|
}
|
|
}
|
|
|
|
fn neg(self, _vm: &VirtualMachine) -> BigInt {
|
|
-(&self.value)
|
|
}
|
|
|
|
fn hash(self, _vm: &VirtualMachine) -> u64 {
|
|
let mut hasher = std::collections::hash_map::DefaultHasher::new();
|
|
self.value.hash(&mut hasher);
|
|
hasher.finish()
|
|
}
|
|
|
|
fn abs(self, _vm: &VirtualMachine) -> BigInt {
|
|
self.value.abs()
|
|
}
|
|
|
|
fn round(self, _precision: OptionalArg<PyObjectRef>, _vm: &VirtualMachine) -> Self {
|
|
self
|
|
}
|
|
|
|
fn float(self, _vm: &VirtualMachine) -> f64 {
|
|
self.value.to_f64().unwrap()
|
|
}
|
|
|
|
fn invert(self, _vm: &VirtualMachine) -> BigInt {
|
|
!(&self.value)
|
|
}
|
|
|
|
fn repr(self, _vm: &VirtualMachine) -> String {
|
|
self.value.to_string()
|
|
}
|
|
|
|
fn format(self, spec: PyRef<objstr::PyString>, vm: &VirtualMachine) -> PyResult<String> {
|
|
let format_spec = FormatSpec::parse(&spec.value);
|
|
match format_spec.format_int(&self.value) {
|
|
Ok(string) => Ok(string),
|
|
Err(err) => Err(vm.new_value_error(err.to_string())),
|
|
}
|
|
}
|
|
|
|
fn bool(self, _vm: &VirtualMachine) -> bool {
|
|
!self.value.is_zero()
|
|
}
|
|
|
|
fn bit_length(self, _vm: &VirtualMachine) -> usize {
|
|
self.value.bits()
|
|
}
|
|
|
|
fn imag(self, _vm: &VirtualMachine) -> usize {
|
|
0
|
|
}
|
|
}
|
|
|
|
fn int_new(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult {
|
|
arg_check!(
|
|
vm,
|
|
args,
|
|
required = [(cls, None)],
|
|
optional = [(val_option, None)]
|
|
);
|
|
|
|
let base = match args.get_optional_kwarg("base") {
|
|
Some(argument) => get_value(&argument).to_u32().unwrap(),
|
|
None => 10,
|
|
};
|
|
let val = match val_option {
|
|
Some(val) => to_int(vm, val, base)?,
|
|
None => Zero::zero(),
|
|
};
|
|
Ok(PyInt::new(val)
|
|
.into_ref_with_type(vm, cls.clone().downcast().unwrap())?
|
|
.into_object())
|
|
}
|
|
|
|
// Casting function:
|
|
pub fn to_int(vm: &VirtualMachine, obj: &PyObjectRef, base: u32) -> PyResult<BigInt> {
|
|
let val = if objtype::isinstance(obj, &vm.ctx.int_type()) {
|
|
get_value(obj).clone()
|
|
} else if objtype::isinstance(obj, &vm.ctx.float_type()) {
|
|
objfloat::get_value(obj).to_bigint().unwrap()
|
|
} else if objtype::isinstance(obj, &vm.ctx.str_type()) {
|
|
let s = objstr::get_value(obj);
|
|
match i32::from_str_radix(&s, base) {
|
|
Ok(v) => v.to_bigint().unwrap(),
|
|
Err(err) => {
|
|
trace!("Error occurred during int conversion {:?}", err);
|
|
return Err(vm.new_value_error(format!(
|
|
"invalid literal for int() with base {}: '{}'",
|
|
base, s
|
|
)));
|
|
}
|
|
}
|
|
} else {
|
|
let type_name = objtype::get_type_name(&obj.typ());
|
|
return Err(vm.new_type_error(format!(
|
|
"int() argument must be a string or a number, not '{}'",
|
|
type_name
|
|
)));
|
|
};
|
|
Ok(val)
|
|
}
|
|
|
|
// Retrieve inner int value:
|
|
pub fn get_value(obj: &PyObjectRef) -> &BigInt {
|
|
&obj.payload::<PyInt>().unwrap().value
|
|
}
|
|
|
|
#[inline]
|
|
fn div_ints(vm: &VirtualMachine, i1: &BigInt, i2: &BigInt) -> PyResult {
|
|
if i2.is_zero() {
|
|
return Err(vm.new_zero_division_error("integer division by zero".to_string()));
|
|
}
|
|
|
|
if let (Some(f1), Some(f2)) = (i1.to_f64(), i2.to_f64()) {
|
|
Ok(vm.ctx.new_float(f1 / f2))
|
|
} else {
|
|
let (quotient, mut rem) = i1.div_rem(i2);
|
|
let mut divisor = i2.clone();
|
|
|
|
if let Some(quotient) = quotient.to_f64() {
|
|
let rem_part = loop {
|
|
if rem.is_zero() {
|
|
break 0.0;
|
|
} else if let (Some(rem), Some(divisor)) = (rem.to_f64(), divisor.to_f64()) {
|
|
break rem / divisor;
|
|
} else {
|
|
// try with smaller numbers
|
|
rem /= 2;
|
|
divisor /= 2;
|
|
}
|
|
};
|
|
|
|
Ok(vm.ctx.new_float(quotient + rem_part))
|
|
} else {
|
|
Err(vm.new_overflow_error("int too large to convert to float".to_string()))
|
|
}
|
|
}
|
|
}
|
|
|
|
#[rustfmt::skip] // to avoid line splitting
|
|
pub fn init(context: &PyContext) {
|
|
let int_doc = "int(x=0) -> integer
|
|
int(x, base=10) -> integer
|
|
|
|
Convert a number or string to an integer, or return 0 if no arguments
|
|
are given. If x is a number, return x.__int__(). For floating point
|
|
numbers, this truncates towards zero.
|
|
|
|
If x is not a number or if base is given, then x must be a string,
|
|
bytes, or bytearray instance representing an integer literal in the
|
|
given base. The literal can be preceded by '+' or '-' and be surrounded
|
|
by whitespace. The base defaults to 10. Valid bases are 0 and 2-36.
|
|
Base 0 means to interpret the base from the string as an integer literal.
|
|
>>> int('0b100', base=0)
|
|
4";
|
|
let int_type = &context.int_type;
|
|
extend_class!(context, int_type, {
|
|
"__doc__" => context.new_str(int_doc.to_string()),
|
|
"__eq__" => context.new_rustfunc(PyIntRef::eq),
|
|
"__ne__" => context.new_rustfunc(PyIntRef::ne),
|
|
"__lt__" => context.new_rustfunc(PyIntRef::lt),
|
|
"__le__" => context.new_rustfunc(PyIntRef::le),
|
|
"__gt__" => context.new_rustfunc(PyIntRef::gt),
|
|
"__ge__" => context.new_rustfunc(PyIntRef::ge),
|
|
"__abs__" => context.new_rustfunc(PyIntRef::abs),
|
|
"__add__" => context.new_rustfunc(PyIntRef::add),
|
|
"__radd__" => context.new_rustfunc(PyIntRef::add),
|
|
"__and__" => context.new_rustfunc(PyIntRef::and),
|
|
"__divmod__" => context.new_rustfunc(PyIntRef::divmod),
|
|
"__float__" => context.new_rustfunc(PyIntRef::float),
|
|
"__round__" => context.new_rustfunc(PyIntRef::round),
|
|
"__ceil__" => context.new_rustfunc(PyIntRef::pass_value),
|
|
"__floor__" => context.new_rustfunc(PyIntRef::pass_value),
|
|
"__index__" => context.new_rustfunc(PyIntRef::pass_value),
|
|
"__trunc__" => context.new_rustfunc(PyIntRef::pass_value),
|
|
"__int__" => context.new_rustfunc(PyIntRef::pass_value),
|
|
"__floordiv__" => context.new_rustfunc(PyIntRef::floordiv),
|
|
"__hash__" => context.new_rustfunc(PyIntRef::hash),
|
|
"__lshift__" => context.new_rustfunc(PyIntRef::lshift),
|
|
"__rshift__" => context.new_rustfunc(PyIntRef::rshift),
|
|
"__new__" => context.new_rustfunc(int_new),
|
|
"__mod__" => context.new_rustfunc(PyIntRef::mod_),
|
|
"__mul__" => context.new_rustfunc(PyIntRef::mul),
|
|
"__rmul__" => context.new_rustfunc(PyIntRef::mul),
|
|
"__or__" => context.new_rustfunc(PyIntRef::or),
|
|
"__neg__" => context.new_rustfunc(PyIntRef::neg),
|
|
"__pos__" => context.new_rustfunc(PyIntRef::pass_value),
|
|
"__pow__" => context.new_rustfunc(PyIntRef::pow),
|
|
"__repr__" => context.new_rustfunc(PyIntRef::repr),
|
|
"__sub__" => context.new_rustfunc(PyIntRef::sub),
|
|
"__rsub__" => context.new_rustfunc(PyIntRef::rsub),
|
|
"__format__" => context.new_rustfunc(PyIntRef::format),
|
|
"__truediv__" => context.new_rustfunc(PyIntRef::truediv),
|
|
"__rtruediv__" => context.new_rustfunc(PyIntRef::rtruediv),
|
|
"__xor__" => context.new_rustfunc(PyIntRef::xor),
|
|
"__rxor__" => context.new_rustfunc(PyIntRef::rxor),
|
|
"__bool__" => context.new_rustfunc(PyIntRef::bool),
|
|
"__invert__" => context.new_rustfunc(PyIntRef::invert),
|
|
"bit_length" => context.new_rustfunc(PyIntRef::bit_length),
|
|
"conjugate" => context.new_rustfunc(PyIntRef::pass_value),
|
|
"real" => context.new_property(PyIntRef::pass_value),
|
|
"imag" => context.new_property(PyIntRef::imag)
|
|
});
|
|
}
|