use std::fmt::{self, Debug, Display}; use gc::{Finalize, Trace}; use crate::obj::macros::*; use crate::obj::prelude::*; use crate::obj::BaseObj; use crate::vm::Vm; #[derive(Trace, Finalize)] pub struct Int { base: BaseObj, pub(crate) int_value: i64, } impl Int { pub fn new(int_value: i64) -> Self { Self { int_value, base: Default::default(), } } impl_create!(int_value: i64); pub fn int_value(&self) -> i64 { self.int_value } } impl Display for Int { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { write!(fmt, "{}", self.int_value) } } impl Debug for Int { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { write!(fmt, "{}", self.int_value) } } impl Object for Int { fn is_truthy(&self) -> bool { self.int_value != 0 } fn equals(&self, other: &dyn Object) -> bool { if let Some(other) = other.as_any().downcast_ref::() { self.int_value == other.int_value } else if let Some(other) = other.as_any().downcast_ref::() { self.int_value as f64 == other.float_value() } else { false } } impl_base_obj!(Int); } //////////////////////////////////////////////////////////////////////////////// // Int implementations //////////////////////////////////////////////////////////////////////////////// macro_rules! int_bin_op_math { ($function:ident, $op:tt) => { pub(crate) fn $function(vm: &mut Vm, _state: FunctionState) -> FunctionResult { let lhs = vm.frame_stack()[0].clone(); let rhs = vm.frame_stack()[1].clone(); let lhs_value = with_obj_downcast(lhs, Int::int_value); let result = if let Some(int_inst) = rhs.borrow().as_any().downcast_ref::() { Int::create(lhs_value $op int_inst.int_value()) } else if let Some(float_inst) = rhs.borrow().as_any().downcast_ref::() { Float::create(lhs_value as f64 $op float_inst.float_value()) } else { // TODO Int arithmetic operator - throw an exception when RHS is not Int, Float // BLOCKED-ON: exceptions todo!( concat!("cannot use '", stringify!($op), "' operator with Int and {}"), rhs.borrow().ty_name() ) }; result.into() } } } macro_rules! int_bin_op_logical { ($function:ident, $op:tt) => { pub(crate) fn $function(vm: &mut Vm, _state: FunctionState) -> FunctionResult { let lhs = vm.frame_stack()[0].clone(); let rhs = vm.frame_stack()[1].clone(); let lhs_value = with_obj_downcast(lhs, Int::int_value); let result = if let Some(int_inst) = rhs.borrow().as_any().downcast_ref::() { Bool::create(lhs_value $op int_inst.int_value()) } else if let Some(float_inst) = rhs.borrow().as_any().downcast_ref::() { Bool::create((lhs_value as f64) $op float_inst.float_value()) } else { // TODO Int logical operator - throw an exception when RHS is not Int, Float // BLOCKED-ON: exceptions todo!( concat!("cannot use '", stringify!($op), "' operator with Int and {}"), rhs.borrow().ty_name() ) }; result.into() } } } impl Int { pub(crate) fn to_int(_vm: &mut Vm, _state: FunctionState) -> FunctionResult { FunctionResult::Return } pub(crate) fn to_float(vm: &mut Vm, _state: FunctionState) -> FunctionResult { let int_value = with_obj_downcast(vm.frame_stack()[0].clone(), Int::int_value); Float::create(int_value as f64).into() } impl_do_call!(to_int); pub(crate) fn init(_vm: &mut Vm, _state: FunctionState) -> FunctionResult { // This is a no-op. We don't want the user-exposed `__init__` function to do anything, // instantiation is done in the `__call__` function. FunctionResult::ReturnPush(Nil::create()) } int_bin_op_math!(add, +); int_bin_op_math!(sub, -); pub(crate) fn mul(vm: &mut Vm, _state: FunctionState) -> FunctionResult { // can't bin_op_math this one because it needs the string case let lhs = vm.frame_stack()[0].clone(); let rhs = vm.frame_stack()[1].clone(); let lhs_value = with_obj_downcast(lhs, Int::int_value); let result = if let Some(int_inst) = rhs.borrow().as_any().downcast_ref::() { Int::create(lhs_value * int_inst.int_value()) } else if let Some(float_inst) = rhs.borrow().as_any().downcast_ref::() { Float::create(lhs_value as f64 * float_inst.float_value()) } else if let Some(str_inst) = rhs.borrow().as_any().downcast_ref::() { // TODO Int::mul - maybe convert this to just call Str.mul with arguments reversed? // Just so we have the same logic here Str::create(str_inst.str_value().repeat(lhs_value as usize)) } else { // TODO Int::mul - throw an exception when RHS is not Int, Float, Str // BLOCKED-ON: exceptions todo!( "cannot use '*' operator with Int and {}", rhs.borrow().ty_name() ) }; result.into() } // TODO Int::div - handle divide by zero // BLOCKED-ON: exceptions // NOTE - we will probably need to get rid of the macro here to handle that :( int_bin_op_math!(div, /); // __eq__ will use the default .equals implementation //int_bin_op_logical!(eq, ==); // __ne__ will call __eq__ and negate it int_bin_op_logical!(gt, >); int_bin_op_logical!(ge, >=); int_bin_op_logical!(lt, <); int_bin_op_logical!(le, <=); pub(crate) fn pos(vm: &mut Vm, _state: FunctionState) -> FunctionResult { let lhs = vm.frame_stack()[0].clone(); let value = with_obj_downcast(lhs, Int::int_value); Int::create(value.abs()).into() } pub(crate) fn neg(vm: &mut Vm, _state: FunctionState) -> FunctionResult { let lhs = vm.frame_stack()[0].clone(); let value = with_obj_downcast(lhs, Int::int_value); Int::create(-value).into() } }