diff --git a/vm/src/protocol/mod.rs b/vm/src/protocol/mod.rs index 4170c20df..f355b61ea 100644 --- a/vm/src/protocol/mod.rs +++ b/vm/src/protocol/mod.rs @@ -10,5 +10,5 @@ pub use buffer::{BufferDescriptor, BufferMethods, BufferResizeGuard, PyBuffer, V pub use callable::PyCallable; pub use iter::{PyIter, PyIterIter, PyIterReturn}; pub use mapping::{PyMapping, PyMappingMethods}; -pub use number::{PyNumber, PyNumberMethods}; +pub use number::{PyNumber, PyNumberMethods, PyNumberMethodsOffset}; pub use sequence::{PySequence, PySequenceMethods}; diff --git a/vm/src/protocol/number.rs b/vm/src/protocol/number.rs index bbf88de6a..8abc54e1b 100644 --- a/vm/src/protocol/number.rs +++ b/vm/src/protocol/number.rs @@ -8,6 +8,7 @@ use crate::{ VirtualMachine, }; use crossbeam_utils::atomic::AtomicCell; +use std::ptr; type UnaryFunc = AtomicCell PyResult>>; type BinaryFunc = @@ -109,6 +110,7 @@ impl PyObject { } #[derive(Default)] +// #[repr(C)] pub struct PyNumberMethods { /* Number implementations must check *both* arguments for proper type and implement the necessary conversions @@ -199,6 +201,98 @@ impl PyNumberMethods { }; } +pub enum PyNumberMethodsOffset { + Add, + Subtract, + Multiply, + Remainder, + Divmod, + Power, + Negative, + Positive, + Absolute, + Boolean, + Invert, + Lshift, + Rshift, + And, + Xor, + Or, + Int, + Float, + InplaceAdd, + InplaceSubtract, + InplaceMultiply, + InplaceRemainder, + InplaceDivmod, + InplacePower, + InplaceLshift, + InplaceRshift, + InplaceAnd, + InplaceXor, + InplaceOr, + FloorDivide, + TrueDivide, + InplaceFloorDivide, + InplaceTrueDivide, + Index, + MatrixMultiply, + InplaceMatrixMultiply, +} + +impl PyNumberMethodsOffset { + pub fn method(&self, methods: &PyNumberMethods, vm: &VirtualMachine) -> PyResult<&BinaryFunc> { + use PyNumberMethodsOffset::*; + unsafe { + match self { + // BinaryFunc + Add => ptr::addr_of!(methods.add), + Subtract => ptr::addr_of!(methods.subtract), + Multiply => ptr::addr_of!(methods.multiply), + Remainder => ptr::addr_of!(methods.remainder), + Divmod => ptr::addr_of!(methods.divmod), + Power => ptr::addr_of!(methods.power), + Lshift => ptr::addr_of!(methods.lshift), + Rshift => ptr::addr_of!(methods.rshift), + And => ptr::addr_of!(methods.and), + Xor => ptr::addr_of!(methods.xor), + Or => ptr::addr_of!(methods.or), + InplaceAdd => ptr::addr_of!(methods.inplace_add), + InplaceSubtract => ptr::addr_of!(methods.inplace_subtract), + InplaceMultiply => ptr::addr_of!(methods.inplace_multiply), + InplaceRemainder => ptr::addr_of!(methods.inplace_remainder), + InplaceDivmod => ptr::addr_of!(methods.inplace_divmod), + InplacePower => ptr::addr_of!(methods.inplace_power), + InplaceLshift => ptr::addr_of!(methods.inplace_lshift), + InplaceRshift => ptr::addr_of!(methods.inplace_rshift), + InplaceAnd => ptr::addr_of!(methods.inplace_and), + InplaceXor => ptr::addr_of!(methods.inplace_xor), + InplaceOr => ptr::addr_of!(methods.inplace_or), + FloorDivide => ptr::addr_of!(methods.floor_divide), + TrueDivide => ptr::addr_of!(methods.true_divide), + InplaceFloorDivide => ptr::addr_of!(methods.inplace_floor_divide), + InplaceTrueDivide => ptr::addr_of!(methods.inplace_true_divide), + MatrixMultiply => ptr::addr_of!(methods.matrix_multiply), + InplaceMatrixMultiply => ptr::addr_of!(methods.inplace_matrix_multiply), + // UnaryFunc + Negative => ptr::null(), + Positive => ptr::null(), + Absolute => ptr::null(), + Boolean => ptr::null(), + Invert => ptr::null(), + Int => ptr::null(), + Float => ptr::null(), + Index => ptr::null(), + } + .as_ref() + .ok_or_else(|| { + vm.new_value_error("No unaryop supported for PyNumberMethodsOffset".to_owned()) + }) + } + } +} + +#[derive(Copy, Clone)] pub struct PyNumber<'a> { pub obj: &'a PyObject, methods: &'a PyNumberMethods, @@ -220,8 +314,12 @@ impl PyNumber<'_> { obj.class().mro_find_map(|x| x.slots.as_number.load()) } - pub fn methods(&self) -> &PyNumberMethods { - self.methods + pub fn methods<'a>( + &'a self, + op_slot: &'a PyNumberMethodsOffset, + vm: &VirtualMachine, + ) -> PyResult<&BinaryFunc> { + op_slot.method(self.methods, vm) } // PyNumber_Check @@ -238,12 +336,12 @@ impl PyNumber<'_> { // PyIndex_Check pub fn is_index(&self) -> bool { - self.methods().index.load().is_some() + self.methods.index.load().is_some() } #[inline] pub fn int(self, vm: &VirtualMachine) -> Option> { - self.methods().int.load().map(|f| { + self.methods.int.load().map(|f| { let ret = f(self, vm)?; let value = if !ret.class().is(PyInt::class(vm)) { warnings::warn( @@ -267,7 +365,7 @@ impl PyNumber<'_> { #[inline] pub fn index(self, vm: &VirtualMachine) -> Option> { - self.methods().index.load().map(|f| { + self.methods.index.load().map(|f| { let ret = f(self, vm)?; let value = if !ret.class().is(PyInt::class(vm)) { warnings::warn( @@ -291,7 +389,7 @@ impl PyNumber<'_> { #[inline] pub fn float(self, vm: &VirtualMachine) -> Option>> { - self.methods().float.load().map(|f| { + self.methods.float.load().map(|f| { let ret = f(self, vm)?; let value = if !ret.class().is(PyFloat::class(vm)) { warnings::warn( diff --git a/vm/src/stdlib/builtins.rs b/vm/src/stdlib/builtins.rs index fa2d91768..4ebdbd4f0 100644 --- a/vm/src/stdlib/builtins.rs +++ b/vm/src/stdlib/builtins.rs @@ -21,7 +21,7 @@ mod builtins { ArgBytesLike, ArgCallable, ArgIntoBool, ArgIterable, ArgMapping, ArgStrOrBytesLike, Either, FuncArgs, KwArgs, OptionalArg, OptionalOption, PosArgs, PyArithmeticValue, }, - protocol::{PyIter, PyIterReturn}, + protocol::{PyIter, PyIterReturn, PyNumberMethodsOffset}, py_io, readline::{Readline, ReadlineResult}, stdlib::sys, @@ -605,13 +605,9 @@ mod builtins { modulus, } = args; match modulus { - None => vm.call_or_reflection( - &x, - &y, - identifier!(vm, __pow__), - identifier!(vm, __rpow__), - |vm, x, y| Err(vm.new_unsupported_binop_error(x, y, "pow")), - ), + None => vm.binary_op(&x, &y, PyNumberMethodsOffset::Power, "pow", |vm, _, _| { + Ok(vm.ctx.not_implemented()) + }), Some(z) => { let try_pow_value = |obj: &PyObject, args: (PyObjectRef, PyObjectRef, PyObjectRef)| diff --git a/vm/src/vm/vm_ops.rs b/vm/src/vm/vm_ops.rs index c0530a691..c7a281803 100644 --- a/vm/src/vm/vm_ops.rs +++ b/vm/src/vm/vm_ops.rs @@ -3,9 +3,10 @@ use crate::{ builtins::{PyInt, PyIntRef, PyStr, PyStrInterned, PyStrRef}, function::PyArithmeticValue, object::{AsObject, PyObject, PyObjectRef, PyResult}, - protocol::PyIterReturn, + protocol::{PyIterReturn, PyNumberMethodsOffset, PySequence}, types::PyComparisonOp, }; +use num_traits::ToPrimitive; /// Collection of operators impl VirtualMachine { @@ -103,6 +104,7 @@ impl VirtualMachine { } } + // TODO: Should be deleted after transplanting complete number protocol /// Calls a method on `obj` passing `arg`, if the method exists. /// /// Otherwise, or if the result is the special `NotImplemented` built-in constant, @@ -128,6 +130,7 @@ impl VirtualMachine { unsupported(self, obj, arg) } + // TODO: Should be deleted after transplanting complete number protocol /// Calls a method, falling back to its reflection with the operands /// reversed, and then to the value provided by `unsupported`. /// @@ -174,160 +177,348 @@ impl VirtualMachine { }) } - pub fn _sub(&self, a: &PyObject, b: &PyObject) -> PyResult { - self.call_or_reflection( - a, - b, - identifier!(self, __sub__), - identifier!(self, __rsub__), - |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "-")), - ) + fn binary_op1( + &self, + a: &PyObject, + b: &PyObject, + op_slot: PyNumberMethodsOffset, + unsupported: F, + ) -> PyResult + where + F: Fn(&VirtualMachine, &PyObject, &PyObject) -> PyResult, + { + let num_a = a.to_number(); + let num_b = b.to_number(); + + let slot_a = num_a.methods(&op_slot, self)?.load(); + let slot_b = num_b.methods(&op_slot, self)?.load(); + + if let Some(slot_a) = slot_a { + if let Some(slot_b) = slot_b { + // Check if `a` is subclass of `b` + if b.fast_isinstance(a.class()) { + let ret = slot_b(num_a, b, self)?; + if ret.rich_compare_bool( + self.ctx.not_implemented.as_object(), + PyComparisonOp::Ne, + self, + )? { + return Ok(ret); + } + } + } + + let ret = slot_a(num_a, b, self)?; + if ret.rich_compare_bool( + self.ctx.not_implemented.as_object(), + PyComparisonOp::Ne, + self, + )? { + return Ok(ret); + } + } + + // No slot_a or Not implemented + if let Some(slot_b) = slot_b { + let ret = slot_b(num_a, b, self)?; + if ret.rich_compare_bool( + self.ctx.not_implemented.as_object(), + PyComparisonOp::Ne, + self, + )? { + return Ok(ret); + } + } + + // Both slot_a & slot_b don't exist or are not implemented. + unsupported(self, a, b) } - pub fn _isub(&self, a: &PyObject, b: &PyObject) -> PyResult { - self.call_or_unsupported(a, b, identifier!(self, __isub__), |vm, a, b| { - vm.call_or_reflection( - a, - b, - identifier!(self, __sub__), - identifier!(self, __rsub__), - |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "-=")), - ) - }) + /// `binary_op()` can work only with [`PyNumberMethods::BinaryFunc`]. + pub fn binary_op( + &self, + a: &PyObject, + b: &PyObject, + op_slot: PyNumberMethodsOffset, + op: &str, + unsupported: F, + ) -> PyResult + where + F: Fn(&VirtualMachine, &PyObject, &PyObject) -> PyResult, + { + let result = self.binary_op1(a, b, op_slot, unsupported)?; + + if result.rich_compare_bool( + self.ctx.not_implemented.as_object(), + PyComparisonOp::Eq, + self, + )? { + Err(self.new_unsupported_binop_error(a, b, op)) + } else { + Ok(result) + } + } + + /// ### Binary in-place operators + /// + /// The in-place operators are defined to fall back to the 'normal', + /// non in-place operations, if the in-place methods are not in place. + /// + /// - If the left hand object has the appropriate struct members, and + /// they are filled, call the appropriate function and return the + /// result. No coercion is done on the arguments; the left-hand object + /// is the one the operation is performed on, and it's up to the + /// function to deal with the right-hand object. + /// + /// - Otherwise, in-place modification is not supported. Handle it exactly as + /// a non in-place operation of the same kind. + fn binary_iop1( + &self, + a: &PyObject, + b: &PyObject, + iop_slot: PyNumberMethodsOffset, + op_slot: PyNumberMethodsOffset, + unsupported: F, + ) -> PyResult + where + F: Fn(&VirtualMachine, &PyObject, &PyObject) -> PyResult, + { + let num_a = a.to_number(); + let slot_a = num_a.methods(&iop_slot, self)?.load(); + + if let Some(slot_a) = slot_a { + let ret = slot_a(num_a, b, self)?; + if ret.rich_compare_bool( + self.ctx.not_implemented.as_object(), + PyComparisonOp::Ne, + self, + )? { + return Ok(ret); + } + } + + self.binary_op1(a, b, op_slot, unsupported) + } + + /// `binary_iop()` can work only with [`PyNumberMethods::BinaryFunc`]. + fn binary_iop( + &self, + a: &PyObject, + b: &PyObject, + iop_slot: PyNumberMethodsOffset, + op_slot: PyNumberMethodsOffset, + op: &str, + unsupported: F, + ) -> PyResult + where + F: Fn(&VirtualMachine, &PyObject, &PyObject) -> PyResult, + { + let result = self.binary_iop1(a, b, iop_slot, op_slot, unsupported)?; + + if result.rich_compare_bool( + self.ctx.not_implemented.as_object(), + PyComparisonOp::Eq, + self, + )? { + Err(self.new_unsupported_binop_error(a, b, op)) + } else { + Ok(result) + } } pub fn _add(&self, a: &PyObject, b: &PyObject) -> PyResult { - self.call_or_reflection( - a, - b, - identifier!(self, __add__), - identifier!(self, __radd__), - |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "+")), - ) + self.binary_op(a, b, PyNumberMethodsOffset::Add, "+", |vm, a, b| { + let seq_a = PySequence::try_protocol(a, vm); + + if let Ok(seq_a) = seq_a { + let ret = seq_a.concat(b, vm)?; + if ret.rich_compare_bool( + vm.ctx.not_implemented.as_object(), + PyComparisonOp::Ne, + vm, + )? { + return Ok(ret); + } + } + + Ok(vm.ctx.not_implemented()) + }) } pub fn _iadd(&self, a: &PyObject, b: &PyObject) -> PyResult { - self.call_or_unsupported(a, b, identifier!(self, __iadd__), |vm, a, b| { - vm.call_or_reflection( - a, - b, - identifier!(self, __add__), - identifier!(self, __radd__), - |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "+=")), - ) - }) - } - - pub fn _mul(&self, a: &PyObject, b: &PyObject) -> PyResult { - self.call_or_reflection( + self.binary_iop( a, b, - identifier!(self, __mul__), - identifier!(self, __rmul__), - |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "*")), + PyNumberMethodsOffset::InplaceAdd, + PyNumberMethodsOffset::Add, + "+=", + |vm, a, b| { + let seq_a = PySequence::try_protocol(a, vm); + + if let Ok(seq_a) = seq_a { + return seq_a.inplace_concat(b, vm); + } + + Ok(vm.ctx.not_implemented()) + }, ) } - pub fn _imul(&self, a: &PyObject, b: &PyObject) -> PyResult { - self.call_or_unsupported(a, b, identifier!(self, __imul__), |vm, a, b| { - vm.call_or_reflection( - a, - b, - identifier!(self, __mul__), - identifier!(self, __rmul__), - |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "*=")), - ) + pub fn _sub(&self, a: &PyObject, b: &PyObject) -> PyResult { + self.binary_op(a, b, PyNumberMethodsOffset::Subtract, "-", |vm, _, _| { + Ok(vm.ctx.not_implemented()) }) } - pub fn _matmul(&self, a: &PyObject, b: &PyObject) -> PyResult { - self.call_or_reflection( + pub fn _isub(&self, a: &PyObject, b: &PyObject) -> PyResult { + self.binary_iop( a, b, - identifier!(self, __matmul__), - identifier!(self, __rmatmul__), - |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "@")), + PyNumberMethodsOffset::InplaceSubtract, + PyNumberMethodsOffset::Subtract, + "-=", + |vm, _, _| Ok(vm.ctx.not_implemented()), + ) + } + + pub fn _mul(&self, a: &PyObject, b: &PyObject) -> PyResult { + self.binary_op(a, b, PyNumberMethodsOffset::Multiply, "*", |vm, a, b| { + // TODO: check if PySequence::with_methods can replace try_protocol + let seq_a = PySequence::try_protocol(a, vm); + let seq_b = PySequence::try_protocol(b, vm); + + // TODO: I think converting to isize process should be handled in repeat function. + // TODO: This can be helpful to unify the sequence protocol's closure. + + if let Ok(seq_a) = seq_a { + let n = b.try_int(vm)?.as_bigint().to_isize().ok_or_else(|| { + vm.new_overflow_error("repeated bytes are too long".to_owned()) + })?; + + return seq_a.repeat(n, vm); + } else if let Ok(seq_b) = seq_b { + let n = a.try_int(vm)?.as_bigint().to_isize().ok_or_else(|| { + vm.new_overflow_error("repeated bytes are too long".to_owned()) + })?; + + return seq_b.repeat(n, vm); + } + + Ok(vm.ctx.not_implemented()) + }) + } + + pub fn _imul(&self, a: &PyObject, b: &PyObject) -> PyResult { + self.binary_iop( + a, + b, + PyNumberMethodsOffset::InplaceMultiply, + PyNumberMethodsOffset::Multiply, + "*=", + |vm, a, b| { + // TODO: check if PySequence::with_methods can replace try_protocol + let seq_a = PySequence::try_protocol(a, vm); + let seq_b = PySequence::try_protocol(b, vm); + + if let Ok(seq_a) = seq_a { + let n = b.try_int(vm)?.as_bigint().to_isize().ok_or_else(|| { + vm.new_overflow_error("repeated bytes are too long".to_owned()) + })?; + + return seq_a.inplace_repeat(n, vm); + } else if let Ok(seq_b) = seq_b { + let n = a.try_int(vm)?.as_bigint().to_isize().ok_or_else(|| { + vm.new_overflow_error("repeated bytes are too long".to_owned()) + })?; + + /* Note that the right hand operand should not be + * mutated in this case so sq_inplace_repeat is not + * used. */ + return seq_b.repeat(n, vm); + } + + Ok(vm.ctx.not_implemented()) + }, + ) + } + + pub fn _matmul(&self, a: &PyObject, b: &PyObject) -> PyResult { + self.binary_op( + a, + b, + PyNumberMethodsOffset::MatrixMultiply, + "@", + |vm, _, _| Ok(vm.ctx.not_implemented()), ) } pub fn _imatmul(&self, a: &PyObject, b: &PyObject) -> PyResult { - self.call_or_unsupported(a, b, identifier!(self, __imatmul__), |vm, a, b| { - vm.call_or_reflection( - a, - b, - identifier!(self, __matmul__), - identifier!(self, __rmatmul__), - |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "@=")), - ) - }) - } - - pub fn _truediv(&self, a: &PyObject, b: &PyObject) -> PyResult { - self.call_or_reflection( + self.binary_iop( a, b, - identifier!(self, __truediv__), - identifier!(self, __rtruediv__), - |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "/")), + PyNumberMethodsOffset::InplaceMatrixMultiply, + PyNumberMethodsOffset::MatrixMultiply, + "@=", + |vm, _, _| Ok(vm.ctx.not_implemented()), ) } - pub fn _itruediv(&self, a: &PyObject, b: &PyObject) -> PyResult { - self.call_or_unsupported(a, b, identifier!(self, __itruediv__), |vm, a, b| { - vm.call_or_reflection( - a, - b, - identifier!(self, __truediv__), - identifier!(self, __rtruediv__), - |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "/=")), - ) + pub fn _truediv(&self, a: &PyObject, b: &PyObject) -> PyResult { + self.binary_op(a, b, PyNumberMethodsOffset::TrueDivide, "/", |vm, _, _| { + Ok(vm.ctx.not_implemented()) }) } - pub fn _floordiv(&self, a: &PyObject, b: &PyObject) -> PyResult { - self.call_or_reflection( + pub fn _itruediv(&self, a: &PyObject, b: &PyObject) -> PyResult { + self.binary_iop( a, b, - identifier!(self, __floordiv__), - identifier!(self, __rfloordiv__), - |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "//")), + PyNumberMethodsOffset::InplaceTrueDivide, + PyNumberMethodsOffset::TrueDivide, + "/=", + |vm, _, _| Ok(vm.ctx.not_implemented()), + ) + } + + pub fn _floordiv(&self, a: &PyObject, b: &PyObject) -> PyResult { + self.binary_op( + a, + b, + PyNumberMethodsOffset::FloorDivide, + "//", + |vm, _, _| Ok(vm.ctx.not_implemented()), ) } pub fn _ifloordiv(&self, a: &PyObject, b: &PyObject) -> PyResult { - self.call_or_unsupported(a, b, identifier!(self, __ifloordiv__), |vm, a, b| { - vm.call_or_reflection( - a, - b, - identifier!(self, __floordiv__), - identifier!(self, __rfloordiv__), - |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "//=")), - ) - }) - } - - pub fn _pow(&self, a: &PyObject, b: &PyObject) -> PyResult { - self.call_or_reflection( + self.binary_iop( a, b, - identifier!(self, __pow__), - identifier!(self, __rpow__), - |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "**")), + PyNumberMethodsOffset::InplaceFloorDivide, + PyNumberMethodsOffset::FloorDivide, + "//=", + |vm, _, _| Ok(vm.ctx.not_implemented()), ) } - pub fn _ipow(&self, a: &PyObject, b: &PyObject) -> PyResult { - self.call_or_unsupported(a, b, identifier!(self, __ipow__), |vm, a, b| { - vm.call_or_reflection( - a, - b, - identifier!(self, __pow__), - identifier!(self, __rpow__), - |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "**=")), - ) + pub fn _pow(&self, a: &PyObject, b: &PyObject) -> PyResult { + self.binary_op(a, b, PyNumberMethodsOffset::Power, "**", |vm, _, _| { + Ok(vm.ctx.not_implemented()) }) } + pub fn _ipow(&self, a: &PyObject, b: &PyObject) -> PyResult { + self.binary_iop( + a, + b, + PyNumberMethodsOffset::InplacePower, + PyNumberMethodsOffset::Power, + "**=", + |vm, _, _| Ok(vm.ctx.not_implemented()), + ) + } + + // TODO: `str` modular opertation(mod, imod) is not supported now. Should implement it. pub fn _mod(&self, a: &PyObject, b: &PyObject) -> PyResult { self.call_or_reflection( a, @@ -336,6 +527,10 @@ impl VirtualMachine { identifier!(self, __rmod__), |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "%")), ) + + // self.binary_op(a, b, PyNumberMethodsOffset::Remainder, "%", |vm, _, _| { + // Ok(vm.ctx.not_implemented()) + // }) } pub fn _imod(&self, a: &PyObject, b: &PyObject) -> PyResult { @@ -348,84 +543,76 @@ impl VirtualMachine { |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "%=")), ) }) + + // self.binary_iop( + // a, + // b, + // PyNumberMethodsOffset::InplaceRemainder, + // PyNumberMethodsOffset::Remainder, + // "%=", + // |vm, _, _| Ok(vm.ctx.not_implemented()), + // ) } pub fn _divmod(&self, a: &PyObject, b: &PyObject) -> PyResult { - self.call_or_reflection( - a, - b, - identifier!(self, __divmod__), - identifier!(self, __rdivmod__), - |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "divmod")), - ) + self.binary_op(a, b, PyNumberMethodsOffset::Divmod, "divmod", |vm, _, _| { + Ok(vm.ctx.not_implemented()) + }) } pub fn _lshift(&self, a: &PyObject, b: &PyObject) -> PyResult { - self.call_or_reflection( - a, - b, - identifier!(self, __lshift__), - identifier!(self, __rlshift__), - |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "<<")), - ) + self.binary_op(a, b, PyNumberMethodsOffset::Lshift, "<<", |vm, _, _| { + Ok(vm.ctx.not_implemented()) + }) } pub fn _ilshift(&self, a: &PyObject, b: &PyObject) -> PyResult { - self.call_or_unsupported(a, b, identifier!(self, __ilshift__), |vm, a, b| { - vm.call_or_reflection( - a, - b, - identifier!(self, __lshift__), - identifier!(self, __rlshift__), - |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "<<=")), - ) - }) + self.binary_iop( + a, + b, + PyNumberMethodsOffset::InplaceLshift, + PyNumberMethodsOffset::Lshift, + "<<=", + |vm, _, _| Ok(vm.ctx.not_implemented()), + ) } pub fn _rshift(&self, a: &PyObject, b: &PyObject) -> PyResult { - self.call_or_reflection( - a, - b, - identifier!(self, __rshift__), - identifier!(self, __rrshift__), - |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, ">>")), - ) + self.binary_op(a, b, PyNumberMethodsOffset::Rshift, ">>", |vm, _, _| { + Ok(vm.ctx.not_implemented()) + }) } pub fn _irshift(&self, a: &PyObject, b: &PyObject) -> PyResult { - self.call_or_unsupported(a, b, identifier!(self, __irshift__), |vm, a, b| { - vm.call_or_reflection( - a, - b, - identifier!(self, __rshift__), - identifier!(self, __rrshift__), - |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, ">>=")), - ) - }) - } - - pub fn _xor(&self, a: &PyObject, b: &PyObject) -> PyResult { - self.call_or_reflection( + self.binary_iop( a, b, - identifier!(self, __xor__), - identifier!(self, __rxor__), - |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "^")), + PyNumberMethodsOffset::InplaceRshift, + PyNumberMethodsOffset::Rshift, + ">>=", + |vm, _, _| Ok(vm.ctx.not_implemented()), ) } - pub fn _ixor(&self, a: &PyObject, b: &PyObject) -> PyResult { - self.call_or_unsupported(a, b, identifier!(self, __ixor__), |vm, a, b| { - vm.call_or_reflection( - a, - b, - identifier!(self, __xor__), - identifier!(self, __rxor__), - |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "^=")), - ) + pub fn _xor(&self, a: &PyObject, b: &PyObject) -> PyResult { + self.binary_op(a, b, PyNumberMethodsOffset::Xor, "^", |vm, _, _| { + Ok(vm.ctx.not_implemented()) }) } + pub fn _ixor(&self, a: &PyObject, b: &PyObject) -> PyResult { + self.binary_iop( + a, + b, + PyNumberMethodsOffset::InplaceXor, + PyNumberMethodsOffset::Xor, + "^=", + |vm, _, _| Ok(vm.ctx.not_implemented()), + ) + } + + // TODO: `or` method doesn't work because of structure of `type_::or_()`. + // TODO: It should be changed by adjusting with AsNumber. pub fn _or(&self, a: &PyObject, b: &PyObject) -> PyResult { self.call_or_reflection( a, @@ -434,6 +621,10 @@ impl VirtualMachine { identifier!(self, __ror__), |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "|")), ) + + // self.binary_op(a, b, PyNumberMethodsOffset::Or, "|", |vm, _, _| { + // Ok(vm.ctx.not_implemented()) + // }) } pub fn _ior(&self, a: &PyObject, b: &PyObject) -> PyResult { @@ -446,8 +637,19 @@ impl VirtualMachine { |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "|=")), ) }) + + // self.binary_iop( + // a, + // b, + // PyNumberMethodsOffset::InplaceOr, + // PyNumberMethodsOffset::Or, + // "|=", + // |vm, _, _| Ok(vm.ctx.not_implemented()), + // ) } + // TODO: `and` method doesn't work because of structure of `set`. + // TODO: It should be changed by adjusting with AsNumber. pub fn _and(&self, a: &PyObject, b: &PyObject) -> PyResult { self.call_or_reflection( a, @@ -456,6 +658,10 @@ impl VirtualMachine { identifier!(self, __rand__), |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "&")), ) + + // self.binary_op(a, b, PyNumberMethodsOffset::And, "&", |vm, _, _| { + // Ok(vm.ctx.not_implemented()) + // }) } pub fn _iand(&self, a: &PyObject, b: &PyObject) -> PyResult { @@ -468,6 +674,15 @@ impl VirtualMachine { |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "&=")), ) }) + + // self.binary_iop( + // a, + // b, + // PyNumberMethodsOffset::InplaceAnd, + // PyNumberMethodsOffset::And, + // "&=", + // |vm, _, _| Ok(vm.ctx.not_implemented()), + // ) } pub fn _abs(&self, a: &PyObject) -> PyResult {