From 388b62aae20f378248c74057efa4490867df8133 Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Sun, 28 Apr 2019 13:44:06 +0900 Subject: [PATCH 01/17] objint::to_int to call __int__ --- tests/snippets/ints.py | 34 ++++++++++++++++++++++++++++++++++ vm/src/obj/objint.rs | 26 +++++++++++++++++--------- 2 files changed, 51 insertions(+), 9 deletions(-) diff --git a/tests/snippets/ints.py b/tests/snippets/ints.py index 1fbbc2bef..a072cc542 100644 --- a/tests/snippets/ints.py +++ b/tests/snippets/ints.py @@ -74,3 +74,37 @@ with assertRaises(TypeError): with assertRaises(TypeError): # check that first parameter is truly positional only int(val_options=1) + +class A(object): + def __int__(self): + return 10 + +assert int(A()) == 10 + +class B(object): + pass + +b = B() +b.__int__ = lambda: 20 + +with assertRaises(TypeError): + assert int(b) == 20 + +class C(object): + def __int__(self): + return 'str' + +with assertRaises(TypeError): + int(C()) + +class I(int): + def __int__(self): + return 3 + +assert int(I(1)) == 3 + +class F(float): + def __int__(self): + return 3 + +assert int(F(1.2)) == 3 diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index c9a5c9a40..3687ed2b0 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -1,12 +1,12 @@ use std::fmt; use std::hash::{Hash, Hasher}; -use num_bigint::{BigInt, ToBigInt}; +use num_bigint::BigInt; use num_integer::Integer; use num_traits::{Pow, Signed, ToPrimitive, Zero}; use crate::format::FormatSpec; -use crate::function::OptionalArg; +use crate::function::{OptionalArg, PyFuncArgs}; use crate::pyobject::{ IntoPyObject, PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue, TryFromObject, TypeProtocol, @@ -510,11 +510,8 @@ fn int_new(cls: PyClassRef, options: IntOptions, vm: &VirtualMachine) -> PyResul } // Casting function: -// TODO: this should just call `__int__` on the object pub fn to_int(vm: &VirtualMachine, obj: &PyObjectRef, base: u32) -> PyResult { match_class!(obj.clone(), - i @ PyInt => Ok(i.as_bigint().clone()), - f @ PyFloat => Ok(f.to_f64().to_bigint().unwrap()), s @ PyString => { i32::from_str_radix(s.as_str(), base) .map(BigInt::from) @@ -523,10 +520,21 @@ pub fn to_int(vm: &VirtualMachine, obj: &PyObjectRef, base: u32) -> PyResult Err(vm.new_type_error(format!( - "int() argument must be a string or a number, not '{}'", - obj.class().name - ))) + obj => { + if let Ok(f) = vm.get_method(obj.clone(), "__int__") { + let int_res = vm.invoke(f, PyFuncArgs::default())?; + match int_res.payload::() { + Some(i) => Ok(i.as_bigint().clone()), + None => Err(vm.new_type_error(format!( + "TypeError: __int__ returned non-int (type '{}')", int_res.class().name))), + } + } else { + Err(vm.new_type_error(format!( + "int() argument must be a string or a number, not '{}'", + obj.class().name + ))) + } + } ) } From f8373aa38a4da14d1cadebd5e5b068f8340357b6 Mon Sep 17 00:00:00 2001 From: Nicolas Trinquier Date: Sun, 28 Apr 2019 22:57:34 +0200 Subject: [PATCH 02/17] Add mul method for the complex type --- tests/snippets/builtin_complex.py | 5 +++++ vm/src/obj/objcomplex.rs | 12 ++++++++++++ 2 files changed, 17 insertions(+) diff --git a/tests/snippets/builtin_complex.py b/tests/snippets/builtin_complex.py index 2df8e4df7..9c13e2d85 100644 --- a/tests/snippets/builtin_complex.py +++ b/tests/snippets/builtin_complex.py @@ -24,6 +24,11 @@ assert complex(1, 2) != complex(1, 1) assert complex(1, 2) != 'foo' assert complex(1, 2).__eq__('foo') == NotImplemented +# __mul__ + +assert complex(2, -3) * complex(-5, 7) == complex(-21, 29) +assert complex(2, -3) * 5 == complex(10, -15) + # __neg__ assert -complex(1, -1) == complex(-1, 1) diff --git a/vm/src/obj/objcomplex.rs b/vm/src/obj/objcomplex.rs index 93a0559aa..3dd48c3f9 100644 --- a/vm/src/obj/objcomplex.rs +++ b/vm/src/obj/objcomplex.rs @@ -161,6 +161,18 @@ impl PyComplex { vm.ctx.new_bool(result) } + #[pymethod(name = "__mul__")] + fn mul(self, other: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef { + match to_complex(other, vm) { + Ok(Some(other)) => Ok(vm.ctx.new_complex(Complex64::new( + self.value.re * other.re - self.value.im * other.im, + self.value.re * other.im + self.value.re * other.im, + ))), + Ok(None) => Ok(vm.ctx.not_implemented()), + Err(err) => Err(err), + } + } + #[pymethod(name = "__neg__")] fn neg(&self, _vm: &VirtualMachine) -> PyComplex { PyComplex::from(-self.value) From eebdbfe679237a3efb0ec1605d9e41e875dbd5c1 Mon Sep 17 00:00:00 2001 From: Nicolas Trinquier Date: Sun, 28 Apr 2019 22:57:56 +0200 Subject: [PATCH 03/17] Add int and float methods for the complex type --- tests/snippets/builtin_complex.py | 2 +- vm/src/obj/objcomplex.rs | 14 ++++++++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/tests/snippets/builtin_complex.py b/tests/snippets/builtin_complex.py index 9c13e2d85..4d0fa1202 100644 --- a/tests/snippets/builtin_complex.py +++ b/tests/snippets/builtin_complex.py @@ -26,7 +26,7 @@ assert complex(1, 2).__eq__('foo') == NotImplemented # __mul__ -assert complex(2, -3) * complex(-5, 7) == complex(-21, 29) +assert complex(2, -3) * complex(-5, 7) == complex(11, 29) assert complex(2, -3) * 5 == complex(10, -15) # __neg__ diff --git a/vm/src/obj/objcomplex.rs b/vm/src/obj/objcomplex.rs index 3dd48c3f9..cd70c61c7 100644 --- a/vm/src/obj/objcomplex.rs +++ b/vm/src/obj/objcomplex.rs @@ -161,12 +161,22 @@ impl PyComplex { vm.ctx.new_bool(result) } + #[pymethod(name = "__float__")] + fn float(&self, vm: &VirtualMachine) -> PyResult { + return Err(vm.new_type_error(String::from("Can't convert complex to float"))); + } + + #[pymethod(name = "__int__")] + fn int(&self, vm: &VirtualMachine) -> PyResult { + return Err(vm.new_type_error(String::from("Can't convert complex to int"))); + } + #[pymethod(name = "__mul__")] - fn mul(self, other: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef { + fn mul(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { match to_complex(other, vm) { Ok(Some(other)) => Ok(vm.ctx.new_complex(Complex64::new( self.value.re * other.re - self.value.im * other.im, - self.value.re * other.im + self.value.re * other.im, + self.value.re * other.im + self.value.im * other.re, ))), Ok(None) => Ok(vm.ctx.not_implemented()), Err(err) => Err(err), From 68e15a2bd88b4fc55f92647ef3cdeff455a71f9d Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Mon, 29 Apr 2019 14:46:04 +0900 Subject: [PATCH 04/17] Add basic float.__round__ without ndigits support --- tests/snippets/math_basics.py | 7 +++++++ vm/src/obj/objfloat.rs | 34 +++++++++++++++++++++++++++++++++- 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/tests/snippets/math_basics.py b/tests/snippets/math_basics.py index 16d6e011c..a9092d58d 100644 --- a/tests/snippets/math_basics.py +++ b/tests/snippets/math_basics.py @@ -18,3 +18,10 @@ assert 2 == a / 2 assert a - 3 == 1 assert -a == -4 assert +a == 4 + +assert round(1.2) == 1 +assert round(1.8) == 2 +assert round(0.5) == 0 +assert round(1.5) == 2 +assert round(-0.5) == 0 +assert round(-1.5) == -2 diff --git a/vm/src/obj/objfloat.rs b/vm/src/obj/objfloat.rs index a588eee6a..f40c403a8 100644 --- a/vm/src/obj/objfloat.rs +++ b/vm/src/obj/objfloat.rs @@ -2,9 +2,11 @@ use super::objbytes; use super::objint; use super::objstr; use super::objtype; +use crate::function::OptionalArg; use crate::obj::objtype::PyClassRef; use crate::pyobject::{ - IntoPyObject, PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol, + IdProtocol, IntoPyObject, PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue, + TypeProtocol, }; use crate::vm::VirtualMachine; use num_bigint::{BigInt, ToBigInt}; @@ -357,6 +359,36 @@ impl PyFloat { self.value.to_bigint().unwrap() } + #[pymethod(name = "__round__")] + fn round(&self, ndigits: OptionalArg, vm: &VirtualMachine) -> PyObjectRef { + let ndigits = match ndigits { + OptionalArg::Missing => None, + OptionalArg::Present(ref value) => { + if !vm.get_none().is(value) { + // retrive int to implement it + Some(vm.ctx.not_implemented()) + } else { + None + } + } + }; + if ndigits.is_none() { + let fract = self.value.fract(); + let value = if fract.abs() == 0.5 { + if self.value.trunc() % 2.0 == 0.0 { + self.value - fract + } else { + self.value + fract + } + } else { + self.value.round() + }; + vm.ctx.new_int(value.to_bigint().unwrap()) + } else { + vm.ctx.not_implemented() + } + } + #[pymethod(name = "__int__")] fn int(&self, vm: &VirtualMachine) -> BigInt { self.trunc(vm) From 490db0a84f8b6207392617adeadf7e42869a008e Mon Sep 17 00:00:00 2001 From: Nicolas Trinquier Date: Mon, 29 Apr 2019 20:13:01 +0200 Subject: [PATCH 05/17] Add support for complex type for the to_complex function --- vm/src/obj/objcomplex.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/vm/src/obj/objcomplex.rs b/vm/src/obj/objcomplex.rs index cd70c61c7..f9ff9d9b0 100644 --- a/vm/src/obj/objcomplex.rs +++ b/vm/src/obj/objcomplex.rs @@ -67,7 +67,9 @@ impl PyComplexRef { } fn to_complex(value: PyObjectRef, vm: &VirtualMachine) -> PyResult> { - if objtype::isinstance(&value, &vm.ctx.int_type()) { + if objtype::isinstance(&value, &vm.ctx.complex_type()) { + Ok(Some(get_value(&value))) + } else if objtype::isinstance(&value, &vm.ctx.int_type()) { match objint::get_value(&value).to_f64() { Some(v) => Ok(Some(Complex64::new(v, 0.0))), None => Err(vm.new_overflow_error("int too large to convert to float".to_string())), From 4c8f655d4ee6d0aef5ae65334a9cdc2666d9a04e Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Tue, 30 Apr 2019 01:37:47 +0900 Subject: [PATCH 06/17] float.__round__ inf/nan support --- tests/snippets/math_basics.py | 15 ++++++++++++++- vm/src/obj/objfloat.rs | 29 ++++++++++++++++++++++++++--- 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/tests/snippets/math_basics.py b/tests/snippets/math_basics.py index a9092d58d..51ea5c726 100644 --- a/tests/snippets/math_basics.py +++ b/tests/snippets/math_basics.py @@ -1,4 +1,4 @@ -from testutils import assertRaises +from testutils import assert_raises a = 4 @@ -25,3 +25,16 @@ assert round(0.5) == 0 assert round(1.5) == 2 assert round(-0.5) == 0 assert round(-1.5) == -2 + +assert_raises( + ValueError, + lambda: round(float('nan')), + 'ValueError: cannot convert float NaN to integer') +assert_raises( + OverflowError, + lambda: round(float('inf')), + 'OverflowError: cannot convert float NaN to integer') +assert_raises( + OverflowError, + lambda: round(-float('inf')), + 'OverflowError: cannot convert float NaN to integer') diff --git a/vm/src/obj/objfloat.rs b/vm/src/obj/objfloat.rs index f40c403a8..bb7a65975 100644 --- a/vm/src/obj/objfloat.rs +++ b/vm/src/obj/objfloat.rs @@ -51,6 +51,28 @@ fn mod_(v1: f64, v2: f64, vm: &VirtualMachine) -> PyResult { } } +fn try_to_bigint(value: f64, vm: &VirtualMachine) -> PyResult { + match value.to_bigint() { + Some(int) => Ok(int), + None => { + if value.is_infinite() { + Err(vm.new_overflow_error( + "OverflowError: cannot convert float NaN to integer".to_string(), + )) + } else if value.is_nan() { + Err(vm + .new_value_error("ValueError: cannot convert float NaN to integer".to_string())) + } else { + // unreachable unless BigInt has a bug + unreachable!( + "A finite float value failed to be converted to bigint: {}", + value + ) + } + } + } +} + #[pyimpl] impl PyFloat { #[pymethod(name = "__eq__")] @@ -360,7 +382,7 @@ impl PyFloat { } #[pymethod(name = "__round__")] - fn round(&self, ndigits: OptionalArg, vm: &VirtualMachine) -> PyObjectRef { + fn round(&self, ndigits: OptionalArg, vm: &VirtualMachine) -> PyResult { let ndigits = match ndigits { OptionalArg::Missing => None, OptionalArg::Present(ref value) => { @@ -383,9 +405,10 @@ impl PyFloat { } else { self.value.round() }; - vm.ctx.new_int(value.to_bigint().unwrap()) + let int = try_to_bigint(value, vm)?; + Ok(vm.ctx.new_int(int)) } else { - vm.ctx.not_implemented() + Ok(vm.ctx.not_implemented()) } } From b7c7591639617e845d0d7e8c141887a1e2bf91e2 Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Tue, 30 Apr 2019 01:48:03 +0900 Subject: [PATCH 07/17] Fix float.__trunc__ to handle abnormal values --- tests/snippets/floats.py | 4 +++- vm/src/obj/objfloat.rs | 6 +++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/tests/snippets/floats.py b/tests/snippets/floats.py index c4f7bdb64..8820813f1 100644 --- a/tests/snippets/floats.py +++ b/tests/snippets/floats.py @@ -111,7 +111,9 @@ assert 1.2.__float__() == 1.2 assert 1.2.__trunc__() == 1 assert int(1.2) == 1 assert float(1.2) == 1.2 -# assert math.trunc(1.2) == 1 +assert math.trunc(1.2) == 1 +assert_raises(OverflowError, float('inf').__trunc__) +assert_raises(ValueError, float('nan').__trunc__) assert (1.7).real == 1.7 assert (1.3).is_integer() == False diff --git a/vm/src/obj/objfloat.rs b/vm/src/obj/objfloat.rs index bb7a65975..80c325a33 100644 --- a/vm/src/obj/objfloat.rs +++ b/vm/src/obj/objfloat.rs @@ -377,8 +377,8 @@ impl PyFloat { } #[pymethod(name = "__trunc__")] - fn trunc(&self, _vm: &VirtualMachine) -> BigInt { - self.value.to_bigint().unwrap() + fn trunc(&self, vm: &VirtualMachine) -> PyResult { + try_to_bigint(self.value, vm) } #[pymethod(name = "__round__")] @@ -413,7 +413,7 @@ impl PyFloat { } #[pymethod(name = "__int__")] - fn int(&self, vm: &VirtualMachine) -> BigInt { + fn int(&self, vm: &VirtualMachine) -> PyResult { self.trunc(vm) } From c951cb3cfd693536968dee2c675862b01761210e Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Tue, 30 Apr 2019 03:27:06 +0900 Subject: [PATCH 08/17] float.__round__ for ndigits == 0 --- tests/snippets/floats.py | 8 ++++++++ vm/src/obj/objfloat.rs | 17 ++++++++++++++--- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/tests/snippets/floats.py b/tests/snippets/floats.py index 8820813f1..176ce927e 100644 --- a/tests/snippets/floats.py +++ b/tests/snippets/floats.py @@ -114,6 +114,14 @@ assert float(1.2) == 1.2 assert math.trunc(1.2) == 1 assert_raises(OverflowError, float('inf').__trunc__) assert_raises(ValueError, float('nan').__trunc__) +assert 0.5.__round__() == 0.0 +assert 1.5.__round__() == 2.0 +assert 0.5.__round__(0) == 0.0 +assert 1.5.__round__(0) == 2.0 +assert 0.5.__round__(None) == 0.0 +assert 1.5.__round__(None) == 2.0 +assert_raises(OverflowError, float('inf').__round__) +assert_raises(ValueError, float('nan').__round__) assert (1.7).real == 1.7 assert (1.3).is_integer() == False diff --git a/vm/src/obj/objfloat.rs b/vm/src/obj/objfloat.rs index 80c325a33..42810da8e 100644 --- a/vm/src/obj/objfloat.rs +++ b/vm/src/obj/objfloat.rs @@ -11,7 +11,7 @@ use crate::pyobject::{ use crate::vm::VirtualMachine; use num_bigint::{BigInt, ToBigInt}; use num_rational::Ratio; -use num_traits::ToPrimitive; +use num_traits::{ToPrimitive, Zero}; #[pyclass(name = "float")] #[derive(Debug, Copy, Clone, PartialEq)] @@ -387,8 +387,19 @@ impl PyFloat { OptionalArg::Missing => None, OptionalArg::Present(ref value) => { if !vm.get_none().is(value) { - // retrive int to implement it - Some(vm.ctx.not_implemented()) + let ndigits = if objtype::isinstance(value, &vm.ctx.int_type()) { + objint::get_value(value) + } else { + return Err(vm.new_type_error(format!( + "TypeError: '{}' object cannot be interpreted as an integer", + value.class().name + ))); + }; + if ndigits.is_zero() { + None + } else { + Some(ndigits) + } } else { None } From a318e4e09e062affcf97f12540a87724b849a1b4 Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Wed, 1 May 2019 01:19:53 +0900 Subject: [PATCH 09/17] objfloat.rs try_float for common operand handling --- vm/src/obj/objfloat.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/vm/src/obj/objfloat.rs b/vm/src/obj/objfloat.rs index a588eee6a..87ac3199d 100644 --- a/vm/src/obj/objfloat.rs +++ b/vm/src/obj/objfloat.rs @@ -41,6 +41,16 @@ impl From for PyFloat { } } +fn try_float(value: &PyObjectRef, vm: &VirtualMachine) -> PyResult> { + Ok(if objtype::isinstance(&value, &vm.ctx.float_type()) { + Some(get_value(&value)) + } else if objtype::isinstance(&value, &vm.ctx.int_type()) { + Some(objint::get_float_value(&value, vm)?) + } else { + None + }) +} + fn mod_(v1: f64, v2: f64, vm: &VirtualMachine) -> PyResult { if v2 != 0.0 { Ok(vm.ctx.new_float(v1 % v2)) From a0ad4368c618cd2f49a4425897b1b7a4181261f7 Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Tue, 30 Apr 2019 01:59:38 +0900 Subject: [PATCH 10/17] Add float.pow OverflowError handling --- tests/snippets/floats.py | 3 +++ vm/src/obj/objfloat.rs | 15 +++++---------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/tests/snippets/floats.py b/tests/snippets/floats.py index c4f7bdb64..58855acc9 100644 --- a/tests/snippets/floats.py +++ b/tests/snippets/floats.py @@ -113,6 +113,9 @@ assert int(1.2) == 1 assert float(1.2) == 1.2 # assert math.trunc(1.2) == 1 +assert 1.2 ** 2 == 1.44 +assert_raises(OverflowError, lambda: 1.2 ** (10 ** 1000)) + assert (1.7).real == 1.7 assert (1.3).is_integer() == False assert (1.0).is_integer() == True diff --git a/vm/src/obj/objfloat.rs b/vm/src/obj/objfloat.rs index 87ac3199d..23636cef8 100644 --- a/vm/src/obj/objfloat.rs +++ b/vm/src/obj/objfloat.rs @@ -262,16 +262,11 @@ impl PyFloat { } #[pymethod(name = "__pow__")] - fn pow(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef { - let v1 = self.value; - if objtype::isinstance(&other, &vm.ctx.float_type()) { - vm.ctx.new_float(v1.powf(get_value(&other))) - } else if objtype::isinstance(&other, &vm.ctx.int_type()) { - let result = v1.powf(objint::get_value(&other).to_f64().unwrap()); - vm.ctx.new_float(result) - } else { - vm.ctx.not_implemented() - } + fn pow(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { + try_float(&other, vm)?.map_or_else( + || Ok(vm.ctx.not_implemented()), + |other| self.value.powf(other).into_pyobject(vm), + ) } #[pymethod(name = "__sub__")] From 813307f8f94b89cd78165b34a469a2f247ea3417 Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Tue, 30 Apr 2019 02:58:50 +0900 Subject: [PATCH 11/17] Refactor PyFloat using try_float() --- tests/snippets/floats.py | 15 +++ vm/src/obj/objfloat.rs | 191 +++++++++++++++------------------------ 2 files changed, 89 insertions(+), 117 deletions(-) diff --git a/tests/snippets/floats.py b/tests/snippets/floats.py index 58855acc9..580c5920f 100644 --- a/tests/snippets/floats.py +++ b/tests/snippets/floats.py @@ -8,6 +8,7 @@ a = 1.2 b = 1.3 c = 1.2 z = 2 +ov = 10 ** 1000 assert -a == -1.2 @@ -37,6 +38,20 @@ assert a / z == 0.6 assert 6 / a == 5.0 assert 2.0 % z == 0.0 assert z % 2.0 == 0.0 +assert_raises(OverflowError, lambda: a + ov) +assert_raises(OverflowError, lambda: a - ov) +assert_raises(OverflowError, lambda: a * ov) +assert_raises(OverflowError, lambda: a / ov) +assert_raises(OverflowError, lambda: a // ov) +assert_raises(OverflowError, lambda: a % ov) +assert_raises(OverflowError, lambda: a ** ov) +assert_raises(OverflowError, lambda: ov + a) +assert_raises(OverflowError, lambda: ov - a) +assert_raises(OverflowError, lambda: ov * a) +assert_raises(OverflowError, lambda: ov / a) +assert_raises(OverflowError, lambda: ov // a) +assert_raises(OverflowError, lambda: ov % a) +# assert_raises(OverflowError, lambda: ov ** a) assert a < 5 assert a <= 5 diff --git a/vm/src/obj/objfloat.rs b/vm/src/obj/objfloat.rs index 23636cef8..426483f4c 100644 --- a/vm/src/obj/objfloat.rs +++ b/vm/src/obj/objfloat.rs @@ -51,14 +51,30 @@ fn try_float(value: &PyObjectRef, vm: &VirtualMachine) -> PyResult> }) } -fn mod_(v1: f64, v2: f64, vm: &VirtualMachine) -> PyResult { +fn inner_div(v1: f64, v2: f64, vm: &VirtualMachine) -> PyResult { if v2 != 0.0 { - Ok(vm.ctx.new_float(v1 % v2)) + Ok(v1 / v2) + } else { + Err(vm.new_zero_division_error("float division by zero".to_string())) + } +} + +fn inner_mod(v1: f64, v2: f64, vm: &VirtualMachine) -> PyResult { + if v2 != 0.0 { + Ok(v1 % v2) } else { Err(vm.new_zero_division_error("float mod by zero".to_string())) } } +fn inner_floordiv(v1: f64, v2: f64, vm: &VirtualMachine) -> PyResult { + if v2 != 0.0 { + Ok((v1 / v2).floor()) + } else { + Err(vm.new_zero_division_error("float floordiv by zero".to_string())) + } +} + #[pyimpl] impl PyFloat { #[pymethod(name = "__eq__")] @@ -139,20 +155,15 @@ impl PyFloat { } #[pymethod(name = "__add__")] - fn add(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef { - let v1 = self.value; - if objtype::isinstance(&other, &vm.ctx.float_type()) { - vm.ctx.new_float(v1 + get_value(&other)) - } else if objtype::isinstance(&other, &vm.ctx.int_type()) { - vm.ctx - .new_float(v1 + objint::get_value(&other).to_f64().unwrap()) - } else { - vm.ctx.not_implemented() - } + fn add(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { + try_float(&other, vm)?.map_or_else( + || Ok(vm.ctx.not_implemented()), + |other| (self.value + other).into_pyobject(vm), + ) } #[pymethod(name = "__radd__")] - fn radd(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef { + fn radd(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { self.add(other, vm) } @@ -163,45 +174,39 @@ impl PyFloat { #[pymethod(name = "__divmod__")] fn divmod(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { - if objtype::isinstance(&other, &vm.ctx.float_type()) - || objtype::isinstance(&other, &vm.ctx.int_type()) - { - let r1 = self.floordiv(other.clone(), vm)?; - let r2 = self.mod_(other, vm)?; - Ok(vm.ctx.new_tuple(vec![r1, r2])) - } else { - Ok(vm.ctx.not_implemented()) - } + try_float(&other, vm)?.map_or_else( + || Ok(vm.ctx.not_implemented()), + |other| { + let r1 = inner_floordiv(self.value, other, vm)?; + let r2 = inner_mod(self.value, other, vm)?; + Ok(vm + .ctx + .new_tuple(vec![vm.ctx.new_float(r1), vm.ctx.new_float(r2)])) + }, + ) } #[pymethod(name = "__floordiv__")] fn floordiv(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { - let v1 = self.value; - let v2 = if objtype::isinstance(&other, &vm.ctx.float_type) { - get_value(&other) - } else if objtype::isinstance(&other, &vm.ctx.int_type) { - objint::get_float_value(&other, vm)? - } else { - return Ok(vm.ctx.not_implemented()); - }; + try_float(&other, vm)?.map_or_else( + || Ok(vm.ctx.not_implemented()), + |other| inner_floordiv(self.value, other, vm)?.into_pyobject(vm), + ) + } - if v2 != 0.0 { - Ok(vm.ctx.new_float((v1 / v2).floor())) - } else { - Err(vm.new_zero_division_error("float floordiv by zero".to_string())) - } + #[pymethod(name = "__rfloordiv__")] + fn rfloordiv(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { + try_float(&other, vm)?.map_or_else( + || Ok(vm.ctx.not_implemented()), + |other| inner_floordiv(other, self.value, vm)?.into_pyobject(vm), + ) } fn new_float(cls: PyClassRef, arg: PyObjectRef, vm: &VirtualMachine) -> PyResult { let value = if objtype::isinstance(&arg, &vm.ctx.float_type()) { get_value(&arg) } else if objtype::isinstance(&arg, &vm.ctx.int_type()) { - match objint::get_float_value(&arg, vm) { - Ok(f) => f, - Err(e) => { - return Err(e); - } - } + objint::get_float_value(&arg, vm)? } else if objtype::isinstance(&arg, &vm.ctx.str_type()) { match lexical::try_parse(objstr::get_value(&arg)) { Ok(f) => f, @@ -232,28 +237,18 @@ impl PyFloat { #[pymethod(name = "__mod__")] fn mod_(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { - let v1 = self.value; - let v2 = if objtype::isinstance(&other, &vm.ctx.float_type) { - get_value(&other) - } else if objtype::isinstance(&other, &vm.ctx.int_type) { - objint::get_float_value(&other, vm)? - } else { - return Ok(vm.ctx.not_implemented()); - }; - - mod_(v1, v2, vm) + try_float(&other, vm)?.map_or_else( + || Ok(vm.ctx.not_implemented()), + |other| inner_mod(self.value, other, vm)?.into_pyobject(vm), + ) } #[pymethod(name = "__rmod__")] fn rmod(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { - let v2 = self.value; - let v1 = if objtype::isinstance(&other, &vm.ctx.int_type) { - objint::get_float_value(&other, vm)? - } else { - return Ok(vm.ctx.not_implemented()); - }; - - mod_(v1, v2, vm) + try_float(&other, vm)?.map_or_else( + || Ok(vm.ctx.not_implemented()), + |other| inner_mod(other, self.value, vm)?.into_pyobject(vm), + ) } #[pymethod(name = "__neg__")] @@ -271,30 +266,18 @@ impl PyFloat { #[pymethod(name = "__sub__")] fn sub(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { - let v1 = self.value; - if objtype::isinstance(&other, &vm.ctx.float_type()) { - Ok(vm.ctx.new_float(v1 - get_value(&other))) - } else if objtype::isinstance(&other, &vm.ctx.int_type()) { - Ok(vm - .ctx - .new_float(v1 - objint::get_value(&other).to_f64().unwrap())) - } else { - Ok(vm.ctx.not_implemented()) - } + try_float(&other, vm)?.map_or_else( + || Ok(vm.ctx.not_implemented()), + |other| (self.value - other).into_pyobject(vm), + ) } #[pymethod(name = "__rsub__")] fn rsub(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { - let v1 = self.value; - if objtype::isinstance(&other, &vm.ctx.float_type()) { - Ok(vm.ctx.new_float(get_value(&other) - v1)) - } else if objtype::isinstance(&other, &vm.ctx.int_type()) { - Ok(vm - .ctx - .new_float(objint::get_value(&other).to_f64().unwrap() - v1)) - } else { - Ok(vm.ctx.not_implemented()) - } + try_float(&other, vm)?.map_or_else( + || Ok(vm.ctx.not_implemented()), + |other| (other - self.value).into_pyobject(vm), + ) } #[pymethod(name = "__repr__")] @@ -304,52 +287,26 @@ impl PyFloat { #[pymethod(name = "__truediv__")] fn truediv(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { - let v1 = self.value; - let v2 = if objtype::isinstance(&other, &vm.ctx.float_type) { - get_value(&other) - } else if objtype::isinstance(&other, &vm.ctx.int_type) { - objint::get_float_value(&other, vm)? - } else { - return Ok(vm.ctx.not_implemented()); - }; - - if v2 != 0.0 { - Ok(vm.ctx.new_float(v1 / v2)) - } else { - Err(vm.new_zero_division_error("float division by zero".to_string())) - } + try_float(&other, vm)?.map_or_else( + || Ok(vm.ctx.not_implemented()), + |other| inner_div(self.value, other, vm)?.into_pyobject(vm), + ) } #[pymethod(name = "__rtruediv__")] fn rtruediv(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { - let v1 = self.value; - let v2 = if objtype::isinstance(&other, &vm.ctx.float_type) { - get_value(&other) - } else if objtype::isinstance(&other, &vm.ctx.int_type) { - objint::get_float_value(&other, vm)? - } else { - return Ok(vm.ctx.not_implemented()); - }; - - if v1 != 0.0 { - Ok(vm.ctx.new_float(v2 / v1)) - } else { - Err(vm.new_zero_division_error("float division by zero".to_string())) - } + try_float(&other, vm)?.map_or_else( + || Ok(vm.ctx.not_implemented()), + |other| inner_div(other, self.value, vm)?.into_pyobject(vm), + ) } #[pymethod(name = "__mul__")] fn mul(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { - let v1 = self.value; - if objtype::isinstance(&other, &vm.ctx.float_type) { - Ok(vm.ctx.new_float(v1 * get_value(&other))) - } else if objtype::isinstance(&other, &vm.ctx.int_type) { - Ok(vm - .ctx - .new_float(v1 * objint::get_value(&other).to_f64().unwrap())) - } else { - Ok(vm.ctx.not_implemented()) - } + try_float(&other, vm)?.map_or_else( + || Ok(vm.ctx.not_implemented()), + |other| (self.value * other).into_pyobject(vm), + ) } #[pymethod(name = "__rmul__")] From 09710b52226a9f8cfc5ee3b70cc88fb2ada06723 Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Wed, 1 May 2019 03:15:28 +0900 Subject: [PATCH 12/17] Add float.__rdivmod__ and div/mod tests --- tests/snippets/floats.py | 7 +++++++ vm/src/obj/objfloat.rs | 24 ++++++++++++++++++++++-- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/tests/snippets/floats.py b/tests/snippets/floats.py index 580c5920f..45f25c100 100644 --- a/tests/snippets/floats.py +++ b/tests/snippets/floats.py @@ -106,6 +106,8 @@ assert 2.0.__sub__(1.0) == 1.0 assert 2.0.__rmul__(1.0) == 2.0 assert 1.0.__truediv__(2.0) == 0.5 assert 1.0.__rtruediv__(2.0) == 2.0 +assert 2.5.__divmod__(2.0) == (1.0, 0.5) +assert 2.0.__rdivmod__(2.5) == (1.0, 0.5) assert 1.0.__add__(1) == 2.0 assert 1.0.__radd__(1) == 2.0 @@ -120,6 +122,11 @@ assert 2.0.__rmod__(2) == 0.0 assert_raises(ZeroDivisionError, lambda: 2.0 / 0) assert_raises(ZeroDivisionError, lambda: 2.0 // 0) assert_raises(ZeroDivisionError, lambda: 2.0 % 0) +assert_raises(ZeroDivisionError, lambda: divmod(2.0, 0)) +assert_raises(ZeroDivisionError, lambda: 2 / 0.0) +assert_raises(ZeroDivisionError, lambda: 2 // 0.0) +assert_raises(ZeroDivisionError, lambda: 2 % 0.0) +# assert_raises(ZeroDivisionError, lambda: divmod(2, 0.0)) assert 1.2.__int__() == 1 assert 1.2.__float__() == 1.2 diff --git a/vm/src/obj/objfloat.rs b/vm/src/obj/objfloat.rs index 426483f4c..2845e5ef4 100644 --- a/vm/src/obj/objfloat.rs +++ b/vm/src/obj/objfloat.rs @@ -75,6 +75,14 @@ fn inner_floordiv(v1: f64, v2: f64, vm: &VirtualMachine) -> PyResult { } } +fn inner_divmod(v1: f64, v2: f64, vm: &VirtualMachine) -> PyResult<(f64, f64)> { + if v2 != 0.0 { + Ok(((v1 / v2).floor(), v1 % v2)) + } else { + Err(vm.new_zero_division_error("float divmod()".to_string())) + } +} + #[pyimpl] impl PyFloat { #[pymethod(name = "__eq__")] @@ -177,8 +185,20 @@ impl PyFloat { try_float(&other, vm)?.map_or_else( || Ok(vm.ctx.not_implemented()), |other| { - let r1 = inner_floordiv(self.value, other, vm)?; - let r2 = inner_mod(self.value, other, vm)?; + let (r1, r2) = inner_divmod(self.value, other, vm)?; + Ok(vm + .ctx + .new_tuple(vec![vm.ctx.new_float(r1), vm.ctx.new_float(r2)])) + }, + ) + } + + #[pymethod(name = "__rdivmod__")] + fn rdivmod(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { + try_float(&other, vm)?.map_or_else( + || Ok(vm.ctx.not_implemented()), + |other| { + let (r1, r2) = inner_divmod(other, self.value, vm)?; Ok(vm .ctx .new_tuple(vec![vm.ctx.new_float(r1), vm.ctx.new_float(r2)])) From 1c814ff2b5b9a53ebef4d09f7f0d96b63e544d60 Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Wed, 1 May 2019 04:02:34 +0900 Subject: [PATCH 13/17] Add float.__rpow__ --- tests/snippets/floats.py | 1 + vm/src/obj/objfloat.rs | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/tests/snippets/floats.py b/tests/snippets/floats.py index 45f25c100..d409463ee 100644 --- a/tests/snippets/floats.py +++ b/tests/snippets/floats.py @@ -137,6 +137,7 @@ assert float(1.2) == 1.2 assert 1.2 ** 2 == 1.44 assert_raises(OverflowError, lambda: 1.2 ** (10 ** 1000)) +assert 3 ** 2.0 == 9.0 assert (1.7).real == 1.7 assert (1.3).is_integer() == False diff --git a/vm/src/obj/objfloat.rs b/vm/src/obj/objfloat.rs index 2845e5ef4..baf043d6c 100644 --- a/vm/src/obj/objfloat.rs +++ b/vm/src/obj/objfloat.rs @@ -284,6 +284,14 @@ impl PyFloat { ) } + #[pymethod(name = "__rpow__")] + fn rpow(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { + try_float(&other, vm)?.map_or_else( + || Ok(vm.ctx.not_implemented()), + |other| other.powf(self.value).into_pyobject(vm), + ) + } + #[pymethod(name = "__sub__")] fn sub(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { try_float(&other, vm)?.map_or_else( From 14dd85a596f3b5e5e002f4899f5fd737230b363d Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Wed, 1 May 2019 03:00:54 +0900 Subject: [PATCH 14/17] Fix __builtins__.divmod to reflect __rdivmod__ --- vm/src/builtins.rs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index 41082707e..300d11b31 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -123,11 +123,14 @@ fn builtin_dir(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { } fn builtin_divmod(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(x, None), (y, None)]); - match vm.get_method(x.clone(), "__divmod__") { - Ok(attrib) => vm.invoke(attrib, vec![y.clone()]), - Err(..) => Err(vm.new_type_error("unsupported operand type(s) for divmod".to_string())), - } + arg_check!(vm, args, required = [(a, None), (b, None)]); + vm.call_or_reflection( + a.clone(), + b.clone(), + "__divmod__", + "__rdivmod__", + |vm, a, b| Err(vm.new_unsupported_operand_error(a, b, "divmod")), + ) } /// Implements `eval`. From 0d891cba2c90b56cb11ee1b20ef3458acf86686c Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Wed, 1 May 2019 02:59:43 +0900 Subject: [PATCH 15/17] Temporary fix to assertRaises and assert_raises prints errors --- tests/snippets/testutils.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/snippets/testutils.py b/tests/snippets/testutils.py index 7dda57b17..49b7fedc1 100644 --- a/tests/snippets/testutils.py +++ b/tests/snippets/testutils.py @@ -14,9 +14,9 @@ def assert_raises(exc_type, expr, msg=None): except exc_type: pass else: - failmsg = '{!s} was not raised'.format(exc_type.__name__) + failmsg = '{} was not raised'.format(exc_type.__name__) if msg is not None: - failmsg += ': {!s}'.format(msg) + failmsg += ': {}'.format(msg) assert False, failmsg @@ -29,8 +29,8 @@ class assertRaises: def __exit__(self, exc_type, exc_val, exc_tb): if exc_type is None: - failmsg = '{!s} was not raised'.format(self.expected.__name__) - assert False, failmsg + failmsg = '{} was not raised'.format(self.expected.__name__) + assert False, failmsg if not issubclass(exc_type, self.expected): return False return True From 4ef65d8ac303a3574f5be78482afc0cfa942ae4d Mon Sep 17 00:00:00 2001 From: Tzu-Yin Hong Date: Wed, 1 May 2019 15:53:28 +0800 Subject: [PATCH 16/17] Add bytearray.append --- tests/snippets/bytearray.py | 6 ++++++ vm/src/obj/objbytearray.rs | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/tests/snippets/bytearray.py b/tests/snippets/bytearray.py index 563da2c53..4286f8171 100644 --- a/tests/snippets/bytearray.py +++ b/tests/snippets/bytearray.py @@ -65,3 +65,9 @@ except IndexError: pass else: assert False + +a = bytearray(b'appen') +assert len(a) == 5 +a.append(100) +assert len(a) == 6 +assert a.pop() == 100 diff --git a/vm/src/obj/objbytearray.rs b/vm/src/obj/objbytearray.rs index f6244399b..16758a438 100644 --- a/vm/src/obj/objbytearray.rs +++ b/vm/src/obj/objbytearray.rs @@ -79,6 +79,7 @@ pub fn init(context: &PyContext) { "istitle" =>context.new_rustfunc(PyByteArrayRef::istitle), "isupper" => context.new_rustfunc(PyByteArrayRef::isupper), "lower" => context.new_rustfunc(PyByteArrayRef::lower), + "append" => context.new_rustfunc(PyByteArrayRef::append), "pop" => context.new_rustfunc(PyByteArrayRef::pop), "upper" => context.new_rustfunc(PyByteArrayRef::upper) }); @@ -213,6 +214,10 @@ impl PyByteArrayRef { self.value.borrow_mut().clear(); } + fn append(self, x: u8, _vm: &VirtualMachine) { + self.value.borrow_mut().push(x); + } + fn pop(self, vm: &VirtualMachine) -> PyResult { let mut bytes = self.value.borrow_mut(); bytes From c7fd54e809d542320c97977ee7734ef36f153068 Mon Sep 17 00:00:00 2001 From: Adrian Wielgosik Date: Sun, 28 Apr 2019 13:25:57 +0200 Subject: [PATCH 17/17] Convert iterators to pyclass macros --- vm/src/obj/objbytearray.rs | 22 ++++++++++------------ vm/src/obj/objbytes.rs | 20 +++++++++----------- vm/src/obj/objenumerate.rs | 20 +++++++++++--------- vm/src/obj/objfilter.rs | 31 +++++++++++++++---------------- vm/src/obj/objiter.rs | 25 ++++++++++++------------- vm/src/obj/objlist.rs | 23 +++++++++++------------ vm/src/obj/objmap.rs | 30 +++++++++++++++--------------- vm/src/obj/objrange.rs | 22 ++++++++++------------ vm/src/obj/objtuple.rs | 22 ++++++++++------------ vm/src/obj/objzip.rs | 20 +++++++++++--------- vm/src/stdlib/os.rs | 22 +++++++++++----------- 11 files changed, 125 insertions(+), 132 deletions(-) diff --git a/vm/src/obj/objbytearray.rs b/vm/src/obj/objbytearray.rs index 16758a438..6f78ecf48 100644 --- a/vm/src/obj/objbytearray.rs +++ b/vm/src/obj/objbytearray.rs @@ -7,7 +7,7 @@ use std::ops::{Deref, DerefMut}; use num_traits::ToPrimitive; use crate::function::OptionalArg; -use crate::pyobject::{PyContext, PyObjectRef, PyRef, PyResult, PyValue}; +use crate::pyobject::{PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue}; use crate::vm::VirtualMachine; use super::objint; @@ -84,11 +84,7 @@ pub fn init(context: &PyContext) { "upper" => context.new_rustfunc(PyByteArrayRef::upper) }); - let bytearrayiterator_type = &context.bytearrayiterator_type; - extend_class!(context, bytearrayiterator_type, { - "__next__" => context.new_rustfunc(PyByteArrayIteratorRef::next), - "__iter__" => context.new_rustfunc(PyByteArrayIteratorRef::iter), - }); + PyByteArrayIterator::extend_class(context, &context.bytearrayiterator_type); } fn bytearray_new( @@ -287,6 +283,7 @@ mod tests { } } +#[pyclass] #[derive(Debug)] pub struct PyByteArrayIterator { position: Cell, @@ -299,10 +296,10 @@ impl PyValue for PyByteArrayIterator { } } -type PyByteArrayIteratorRef = PyRef; - -impl PyByteArrayIteratorRef { - fn next(self, vm: &VirtualMachine) -> PyResult { +#[pyimpl] +impl PyByteArrayIterator { + #[pymethod(name = "__next__")] + fn next(&self, vm: &VirtualMachine) -> PyResult { if self.position.get() < self.bytearray.value.borrow().len() { let ret = self.bytearray.value.borrow()[self.position.get()]; self.position.set(self.position.get() + 1); @@ -312,7 +309,8 @@ impl PyByteArrayIteratorRef { } } - fn iter(self, _vm: &VirtualMachine) -> Self { - self + #[pymethod(name = "__iter__")] + fn iter(zelf: PyRef, _vm: &VirtualMachine) -> PyRef { + zelf } } diff --git a/vm/src/obj/objbytes.rs b/vm/src/obj/objbytes.rs index 0556de053..df9795902 100644 --- a/vm/src/obj/objbytes.rs +++ b/vm/src/obj/objbytes.rs @@ -64,11 +64,7 @@ pub fn init(context: &PyContext) { extend_class!(context, bytes_type, { "fromhex" => context.new_rustfunc(PyBytesRef::fromhex), }); - let bytesiterator_type = &context.bytesiterator_type; - extend_class!(context, bytesiterator_type, { - "__next__" => context.new_rustfunc(PyBytesIteratorRef::next), - "__iter__" => context.new_rustfunc(PyBytesIteratorRef::iter), - }); + PyBytesIterator::extend_class(context, &context.bytesiterator_type); } #[pyimpl] @@ -271,6 +267,7 @@ impl PyBytesRef { } } +#[pyclass] #[derive(Debug)] pub struct PyBytesIterator { position: Cell, @@ -283,10 +280,10 @@ impl PyValue for PyBytesIterator { } } -type PyBytesIteratorRef = PyRef; - -impl PyBytesIteratorRef { - fn next(self, vm: &VirtualMachine) -> PyResult { +#[pyimpl] +impl PyBytesIterator { + #[pymethod(name = "__next__")] + fn next(&self, vm: &VirtualMachine) -> PyResult { if self.position.get() < self.bytes.inner.len() { let ret = self.bytes[self.position.get()]; self.position.set(self.position.get() + 1); @@ -296,7 +293,8 @@ impl PyBytesIteratorRef { } } - fn iter(self, _vm: &VirtualMachine) -> Self { - self + #[pymethod(name = "__iter__")] + fn iter(zelf: PyRef, _vm: &VirtualMachine) -> PyRef { + zelf } } diff --git a/vm/src/obj/objenumerate.rs b/vm/src/obj/objenumerate.rs index c94aeabbd..56d232272 100644 --- a/vm/src/obj/objenumerate.rs +++ b/vm/src/obj/objenumerate.rs @@ -5,13 +5,14 @@ use num_bigint::BigInt; use num_traits::Zero; use crate::function::OptionalArg; -use crate::pyobject::{PyContext, PyObjectRef, PyRef, PyResult, PyValue}; +use crate::pyobject::{PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue}; use crate::vm::VirtualMachine; use super::objint::PyIntRef; use super::objiter; use super::objtype::PyClassRef; +#[pyclass] #[derive(Debug)] pub struct PyEnumerate { counter: RefCell, @@ -44,8 +45,10 @@ fn enumerate_new( .into_ref_with_type(vm, cls) } -impl PyEnumerateRef { - fn next(self, vm: &VirtualMachine) -> PyResult { +#[pyimpl] +impl PyEnumerate { + #[pymethod(name = "__next__")] + fn next(&self, vm: &VirtualMachine) -> PyResult { let iterator = &self.iterator; let counter = &self.counter; let next_obj = objiter::call_next(vm, iterator)?; @@ -58,16 +61,15 @@ impl PyEnumerateRef { Ok(result) } - fn iter(self, _vm: &VirtualMachine) -> Self { - self + #[pymethod(name = "__iter__")] + fn iter(zelf: PyRef, _vm: &VirtualMachine) -> PyRef { + zelf } } pub fn init(context: &PyContext) { - let enumerate_type = &context.enumerate_type; - extend_class!(context, enumerate_type, { + PyEnumerate::extend_class(context, &context.enumerate_type); + extend_class!(context, &context.enumerate_type, { "__new__" => context.new_rustfunc(enumerate_new), - "__next__" => context.new_rustfunc(PyEnumerateRef::next), - "__iter__" => context.new_rustfunc(PyEnumerateRef::iter), }); } diff --git a/vm/src/obj/objfilter.rs b/vm/src/obj/objfilter.rs index 858a0531a..24800d6aa 100644 --- a/vm/src/obj/objfilter.rs +++ b/vm/src/obj/objfilter.rs @@ -1,4 +1,4 @@ -use crate::pyobject::{IdProtocol, PyContext, PyObjectRef, PyRef, PyResult, PyValue}; +use crate::pyobject::{IdProtocol, PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue}; use crate::vm::VirtualMachine; // Required for arg_check! to use isinstance use super::objbool; @@ -7,6 +7,11 @@ use crate::obj::objtype::PyClassRef; pub type PyFilterRef = PyRef; +/// filter(function or None, iterable) --> filter object +/// +/// Return an iterator yielding those items of iterable for which function(item) +/// is true. If function is None, return the items that are true. +#[pyclass] #[derive(Debug)] pub struct PyFilter { predicate: PyObjectRef, @@ -34,8 +39,10 @@ fn filter_new( .into_ref_with_type(vm, cls) } -impl PyFilterRef { - fn next(self, vm: &VirtualMachine) -> PyResult { +#[pyimpl] +impl PyFilter { + #[pymethod(name = "__next__")] + fn next(&self, vm: &VirtualMachine) -> PyResult { let predicate = &self.predicate; let iterator = &self.iterator; loop { @@ -53,23 +60,15 @@ impl PyFilterRef { } } - fn iter(self, _vm: &VirtualMachine) -> Self { - self + #[pymethod(name = "__iter__")] + fn iter(zelf: PyRef, _vm: &VirtualMachine) -> PyRef { + zelf } } pub fn init(context: &PyContext) { - let filter_type = &context.filter_type; - - let filter_doc = - "filter(function or None, iterable) --> filter object\n\n\ - Return an iterator yielding those items of iterable for which function(item)\n\ - is true. If function is None, return the items that are true."; - - extend_class!(context, filter_type, { + PyFilter::extend_class(context, &context.filter_type); + extend_class!(context, &context.filter_type, { "__new__" => context.new_rustfunc(filter_new), - "__doc__" => context.new_str(filter_doc.to_string()), - "__next__" => context.new_rustfunc(PyFilterRef::next), - "__iter__" => context.new_rustfunc(PyFilterRef::iter), }); } diff --git a/vm/src/obj/objiter.rs b/vm/src/obj/objiter.rs index 6e1dc1d06..efe672d62 100644 --- a/vm/src/obj/objiter.rs +++ b/vm/src/obj/objiter.rs @@ -4,7 +4,9 @@ use std::cell::Cell; -use crate::pyobject::{PyContext, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol}; +use crate::pyobject::{ + PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol, +}; use crate::vm::VirtualMachine; use super::objtype; @@ -75,6 +77,7 @@ pub fn new_stop_iteration(vm: &VirtualMachine) -> PyObjectRef { vm.new_exception(stop_iteration_type, "End of iterator".to_string()) } +#[pyclass] #[derive(Debug)] pub struct PySequenceIterator { pub position: Cell, @@ -87,10 +90,10 @@ impl PyValue for PySequenceIterator { } } -type PySequenceIteratorRef = PyRef; - -impl PySequenceIteratorRef { - fn next(self, vm: &VirtualMachine) -> PyResult { +#[pyimpl] +impl PySequenceIterator { + #[pymethod(name = "__next__")] + fn next(&self, vm: &VirtualMachine) -> PyResult { let number = vm.ctx.new_int(self.position.get()); match vm.call_method(&self.obj, "__getitem__", vec![number]) { Ok(val) => { @@ -105,16 +108,12 @@ impl PySequenceIteratorRef { } } - fn iter(self, _vm: &VirtualMachine) -> Self { - self + #[pymethod(name = "__iter__")] + fn iter(zelf: PyRef, _vm: &VirtualMachine) -> PyRef { + zelf } } pub fn init(context: &PyContext) { - let iter_type = &context.iter_type; - - extend_class!(context, iter_type, { - "__next__" => context.new_rustfunc(PySequenceIteratorRef::next), - "__iter__" => context.new_rustfunc(PySequenceIteratorRef::iter), - }); + PySequenceIterator::extend_class(context, &context.iter_type); } diff --git a/vm/src/obj/objlist.rs b/vm/src/obj/objlist.rs index 5c1bad5af..6b00b6828 100644 --- a/vm/src/obj/objlist.rs +++ b/vm/src/obj/objlist.rs @@ -8,7 +8,8 @@ use num_traits::{One, Signed, ToPrimitive, Zero}; use crate::function::{OptionalArg, PyFuncArgs}; use crate::pyobject::{ - IdProtocol, PyContext, PyIterable, PyObjectRef, PyRef, PyResult, PyValue, TryFromObject, + IdProtocol, PyClassImpl, PyContext, PyIterable, PyObjectRef, PyRef, PyResult, PyValue, + TryFromObject, }; use crate::vm::{ReprGuard, VirtualMachine}; @@ -776,6 +777,7 @@ fn list_sort(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.get_none()) } +#[pyclass] #[derive(Debug)] pub struct PyListIterator { pub position: Cell, @@ -788,10 +790,10 @@ impl PyValue for PyListIterator { } } -type PyListIteratorRef = PyRef; - -impl PyListIteratorRef { - fn next(self, vm: &VirtualMachine) -> PyResult { +#[pyimpl] +impl PyListIterator { + #[pymethod(name = "__next__")] + fn next(&self, vm: &VirtualMachine) -> PyResult { if self.position.get() < self.list.elements.borrow().len() { let ret = self.list.elements.borrow()[self.position.get()].clone(); self.position.set(self.position.get() + 1); @@ -801,8 +803,9 @@ impl PyListIteratorRef { } } - fn iter(self, _vm: &VirtualMachine) -> Self { - self + #[pymethod(name = "__iter__")] + fn iter(zelf: PyRef, _vm: &VirtualMachine) -> PyRef { + zelf } } @@ -848,9 +851,5 @@ pub fn init(context: &PyContext) { "remove" => context.new_rustfunc(PyListRef::remove) }); - let listiterator_type = &context.listiterator_type; - extend_class!(context, listiterator_type, { - "__next__" => context.new_rustfunc(PyListIteratorRef::next), - "__iter__" => context.new_rustfunc(PyListIteratorRef::iter), - }); + PyListIterator::extend_class(context, &context.listiterator_type); } diff --git a/vm/src/obj/objmap.rs b/vm/src/obj/objmap.rs index 061f4179f..61fe67c0d 100644 --- a/vm/src/obj/objmap.rs +++ b/vm/src/obj/objmap.rs @@ -1,10 +1,15 @@ use crate::function::Args; -use crate::pyobject::{PyContext, PyObjectRef, PyRef, PyResult, PyValue}; +use crate::pyobject::{PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue}; use crate::vm::VirtualMachine; use super::objiter; use super::objtype::PyClassRef; +/// map(func, *iterables) --> map object +/// +/// Make an iterator that computes the function using arguments from +/// each of the iterables. Stops when the shortest iterable is exhausted. +#[pyclass] #[derive(Debug)] pub struct PyMap { mapper: PyObjectRef, @@ -35,8 +40,10 @@ fn map_new( .into_ref_with_type(vm, cls.clone()) } -impl PyMapRef { - fn next(self, vm: &VirtualMachine) -> PyResult { +#[pyimpl] +impl PyMap { + #[pymethod(name = "__next__")] + fn next(&self, vm: &VirtualMachine) -> PyResult { let next_objs = self .iterators .iter() @@ -47,22 +54,15 @@ impl PyMapRef { vm.invoke(self.mapper.clone(), next_objs) } - fn iter(self, _vm: &VirtualMachine) -> Self { - self + #[pymethod(name = "__iter__")] + fn iter(zelf: PyRef, _vm: &VirtualMachine) -> PyRef { + zelf } } pub fn init(context: &PyContext) { - let map_type = &context.map_type; - - let map_doc = "map(func, *iterables) --> map object\n\n\ - Make an iterator that computes the function using arguments from\n\ - each of the iterables. Stops when the shortest iterable is exhausted."; - - extend_class!(context, map_type, { + PyMap::extend_class(context, &context.map_type); + extend_class!(context, &context.map_type, { "__new__" => context.new_rustfunc(map_new), - "__next__" => context.new_rustfunc(PyMapRef::next), - "__iter__" => context.new_rustfunc(PyMapRef::iter), - "__doc__" => context.new_str(map_doc.to_string()) }); } diff --git a/vm/src/obj/objrange.rs b/vm/src/obj/objrange.rs index d98e7491f..2a43579c5 100644 --- a/vm/src/obj/objrange.rs +++ b/vm/src/obj/objrange.rs @@ -6,7 +6,7 @@ use num_traits::{One, Signed, Zero}; use crate::function::{OptionalArg, PyFuncArgs}; use crate::pyobject::{ - PyContext, PyObjectRef, PyRef, PyResult, PyValue, TryFromObject, TypeProtocol, + PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue, TryFromObject, TypeProtocol, }; use crate::vm::VirtualMachine; @@ -124,11 +124,7 @@ pub fn init(context: &PyContext) { "step" => context.new_property(PyRange::step), }); - let rangeiterator_type = &context.rangeiterator_type; - extend_class!(context, rangeiterator_type, { - "__next__" => context.new_rustfunc(PyRangeIteratorRef::next), - "__iter__" => context.new_rustfunc(PyRangeIteratorRef::iter), - }); + PyRangeIterator::extend_class(context, &context.rangeiterator_type); } type PyRangeRef = PyRef; @@ -341,6 +337,7 @@ fn range_new(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(range.into_object()) } +#[pyclass] #[derive(Debug)] pub struct PyRangeIterator { position: Cell, @@ -353,10 +350,10 @@ impl PyValue for PyRangeIterator { } } -type PyRangeIteratorRef = PyRef; - -impl PyRangeIteratorRef { - fn next(self, vm: &VirtualMachine) -> PyResult { +#[pyimpl] +impl PyRangeIterator { + #[pymethod(name = "__next__")] + fn next(&self, vm: &VirtualMachine) -> PyResult { let position = BigInt::from(self.position.get()); if let Some(int) = self.range.get(&position) { self.position.set(self.position.get() + 1); @@ -366,8 +363,9 @@ impl PyRangeIteratorRef { } } - fn iter(self, _vm: &VirtualMachine) -> Self { - self + #[pymethod(name = "__iter__")] + fn iter(zelf: PyRef, _vm: &VirtualMachine) -> PyRef { + zelf } } diff --git a/vm/src/obj/objtuple.rs b/vm/src/obj/objtuple.rs index 216945284..42999189f 100644 --- a/vm/src/obj/objtuple.rs +++ b/vm/src/obj/objtuple.rs @@ -3,7 +3,7 @@ use std::fmt; use std::hash::{Hash, Hasher}; use crate::function::OptionalArg; -use crate::pyobject::{IdProtocol, PyContext, PyObjectRef, PyRef, PyResult, PyValue}; +use crate::pyobject::{IdProtocol, PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue}; use crate::vm::{ReprGuard, VirtualMachine}; use super::objbool; @@ -224,6 +224,7 @@ fn tuple_new( PyTuple::from(elements).into_ref_with_type(vm, cls) } +#[pyclass] #[derive(Debug)] pub struct PyTupleIterator { position: Cell, @@ -236,10 +237,10 @@ impl PyValue for PyTupleIterator { } } -type PyTupleIteratorRef = PyRef; - -impl PyTupleIteratorRef { - fn next(self, vm: &VirtualMachine) -> PyResult { +#[pyimpl] +impl PyTupleIterator { + #[pymethod(name = "__next__")] + fn next(&self, vm: &VirtualMachine) -> PyResult { if self.position.get() < self.tuple.elements.borrow().len() { let ret = self.tuple.elements.borrow()[self.position.get()].clone(); self.position.set(self.position.get() + 1); @@ -249,8 +250,9 @@ impl PyTupleIteratorRef { } } - fn iter(self, _vm: &VirtualMachine) -> Self { - self + #[pymethod(name = "__iter__")] + fn iter(zelf: PyRef, _vm: &VirtualMachine) -> PyRef { + zelf } } @@ -282,9 +284,5 @@ If the argument is a tuple, the return value is the same object."; "index" => context.new_rustfunc(PyTupleRef::index) }); - let tupleiterator_type = &context.tupleiterator_type; - extend_class!(context, tupleiterator_type, { - "__next__" => context.new_rustfunc(PyTupleIteratorRef::next), - "__iter__" => context.new_rustfunc(PyTupleIteratorRef::iter), - }); + PyTupleIterator::extend_class(context, &context.tupleiterator_type); } diff --git a/vm/src/obj/objzip.rs b/vm/src/obj/objzip.rs index 81640a724..d9c577aa0 100644 --- a/vm/src/obj/objzip.rs +++ b/vm/src/obj/objzip.rs @@ -1,5 +1,5 @@ use crate::function::Args; -use crate::pyobject::{PyContext, PyObjectRef, PyRef, PyResult, PyValue}; +use crate::pyobject::{PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue}; use crate::vm::VirtualMachine; use super::objiter; @@ -7,6 +7,7 @@ use crate::obj::objtype::PyClassRef; pub type PyZipRef = PyRef; +#[pyclass] #[derive(Debug)] pub struct PyZip { iterators: Vec, @@ -26,8 +27,10 @@ fn zip_new(cls: PyClassRef, iterables: Args, vm: &VirtualMachine) -> PyResult PyResult { +#[pyimpl] +impl PyZip { + #[pymethod(name = "__next__")] + fn next(&self, vm: &VirtualMachine) -> PyResult { if self.iterators.is_empty() { Err(objiter::new_stop_iteration(vm)) } else { @@ -41,16 +44,15 @@ impl PyZipRef { } } - fn iter(self, _vm: &VirtualMachine) -> Self { - self + #[pymethod(name = "__iter__")] + fn iter(zelf: PyRef, _vm: &VirtualMachine) -> PyRef { + zelf } } pub fn init(context: &PyContext) { - let zip_type = &context.zip_type; - extend_class!(context, zip_type, { + PyZip::extend_class(context, &context.zip_type); + extend_class!(context, &context.zip_type, { "__new__" => context.new_rustfunc(zip_new), - "__next__" => context.new_rustfunc(PyZipRef::next), - "__iter__" => context.new_rustfunc(PyZipRef::iter), }); } diff --git a/vm/src/stdlib/os.rs b/vm/src/stdlib/os.rs index 6d81ca9c1..2b6c859eb 100644 --- a/vm/src/stdlib/os.rs +++ b/vm/src/stdlib/os.rs @@ -15,7 +15,7 @@ use crate::obj::objiter; use crate::obj::objstr; use crate::obj::objstr::PyStringRef; use crate::obj::objtype::PyClassRef; -use crate::pyobject::{ItemProtocol, PyObjectRef, PyRef, PyResult, PyValue}; +use crate::pyobject::{ItemProtocol, PyClassImpl, PyObjectRef, PyRef, PyResult, PyValue}; use crate::vm::VirtualMachine; #[cfg(unix)] @@ -232,6 +232,7 @@ impl DirEntryRef { } } +#[pyclass] #[derive(Debug)] struct ScandirIterator { entries: RefCell, @@ -243,10 +244,10 @@ impl PyValue for ScandirIterator { } } -type ScandirIteratorRef = PyRef; - -impl ScandirIteratorRef { - fn next(self, vm: &VirtualMachine) -> PyResult { +#[pyimpl] +impl ScandirIterator { + #[pymethod(name = "__next__")] + fn next(&self, vm: &VirtualMachine) -> PyResult { match self.entries.borrow_mut().next() { Some(entry) => match entry { Ok(entry) => Ok(DirEntry { entry }.into_ref(vm).into_object()), @@ -256,8 +257,9 @@ impl ScandirIteratorRef { } } - fn iter(self, _vm: &VirtualMachine) -> Self { - self + #[pymethod(name = "__iter__")] + fn iter(zelf: PyRef, _vm: &VirtualMachine) -> PyRef { + zelf } } @@ -418,10 +420,8 @@ pub fn make_module(vm: &VirtualMachine) -> PyObjectRef { let environ = _os_environ(vm); - let scandir_iter = py_class!(ctx, "ScandirIter", ctx.object(), { - "__iter__" => ctx.new_rustfunc(ScandirIteratorRef::iter), - "__next__" => ctx.new_rustfunc(ScandirIteratorRef::next), - }); + let scandir_iter = ctx.new_class("ScandirIter", ctx.object()); + ScandirIterator::extend_class(ctx, &scandir_iter); let dir_entry = py_class!(ctx, "DirEntry", ctx.object(), { "name" => ctx.new_property(DirEntryRef::name),