Files
not-python-rust/src/obj/int.rs
Alek Ratzloff 43183d6553 Most object types get their own file now
This is hopefully going to make navigating the source tree easier.
Hopefully.

The only types that don't get their own files are:

* function types (UserFunction, BuiltinFunction, Method), which all live
  in obj/function.rs
* Nil, which lives in obj.rs
* Obj, which lives in obj.rs

Type definitions and init_types now live in obj/ty.rs.

New obj::prelude module for common imports.

Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
2024-09-30 15:15:41 -07:00

187 lines
6.3 KiB
Rust

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::<Int>() {
self.int_value == other.int_value
} else if let Some(other) = other.as_any().downcast_ref::<Float>() {
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>() {
Int::create(lhs_value $op int_inst.int_value())
} else if let Some(float_inst) = rhs.borrow().as_any().downcast_ref::<Float>() {
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::<Int>() {
Bool::create(lhs_value $op int_inst.int_value())
} else if let Some(float_inst) = rhs.borrow().as_any().downcast_ref::<Float>() {
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>() {
Int::create(lhs_value * int_inst.int_value())
} else if let Some(float_inst) = rhs.borrow().as_any().downcast_ref::<Float>() {
Float::create(lhs_value as f64 * float_inst.float_value())
} else if let Some(str_inst) = rhs.borrow().as_any().downcast_ref::<Str>() {
// 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()
}
}