#![allow(non_snake_case)] use crate::def_package; use crate::plugin::*; use crate::INT; use crate::{result::EvalAltResult, token::NO_POS}; #[cfg(not(feature = "no_float"))] use crate::FLOAT; #[cfg(feature = "no_std")] #[cfg(not(feature = "no_float"))] use num_traits::float::Float; use crate::stdlib::{format, string::String}; #[inline(always)] pub fn make_err(msg: impl Into) -> Box { EvalAltResult::ErrorArithmetic(msg.into(), NO_POS).into() } macro_rules! gen_arithmetic_functions { ($root:ident => $($arg_type:ident),+) => { pub mod $root { $(pub mod $arg_type { use super::super::*; #[export_module] pub mod functions { #[rhai_fn(name = "+", return_raw)] pub fn add(x: $arg_type, y: $arg_type) -> Result> { if cfg!(not(feature = "unchecked")) { x.checked_add(y).ok_or_else(|| make_err(format!("Addition overflow: {} + {}", x, y))).map(Dynamic::from) } else { Ok(Dynamic::from(x + y)) } } #[rhai_fn(name = "-", return_raw)] pub fn subtract(x: $arg_type, y: $arg_type) -> Result> { if cfg!(not(feature = "unchecked")) { x.checked_sub(y).ok_or_else(|| make_err(format!("Subtraction overflow: {} - {}", x, y))).map(Dynamic::from) } else { Ok(Dynamic::from(x - y)) } } #[rhai_fn(name = "*", return_raw)] pub fn multiply(x: $arg_type, y: $arg_type) -> Result> { if cfg!(not(feature = "unchecked")) { x.checked_mul(y).ok_or_else(|| make_err(format!("Multiplication overflow: {} * {}", x, y))).map(Dynamic::from) } else { Ok(Dynamic::from(x * y)) } } #[rhai_fn(name = "/", return_raw)] pub fn divide(x: $arg_type, y: $arg_type) -> Result> { if cfg!(not(feature = "unchecked")) { // Detect division by zero if y == 0 { Err(make_err(format!("Division by zero: {} / {}", x, y))) } else { x.checked_div(y).ok_or_else(|| make_err(format!("Division overflow: {} / {}", x, y))).map(Dynamic::from) } } else { Ok(Dynamic::from(x / y)) } } #[rhai_fn(name = "%", return_raw)] pub fn modulo(x: $arg_type, y: $arg_type) -> Result> { if cfg!(not(feature = "unchecked")) { x.checked_rem(y).ok_or_else(|| make_err(format!("Modulo division by zero or overflow: {} % {}", x, y))).map(Dynamic::from) } else { Ok(Dynamic::from(x % y)) } } #[rhai_fn(name = "~", return_raw)] pub fn power(x: INT, y: INT) -> Result> { if cfg!(not(feature = "unchecked")) { if cfg!(not(feature = "only_i32")) && y > (u32::MAX as INT) { Err(make_err(format!("Integer raised to too large an index: {} ~ {}", x, y))) } else if y < 0 { Err(make_err(format!("Integer raised to a negative index: {} ~ {}", x, y))) } else { x.checked_pow(y as u32).ok_or_else(|| make_err(format!("Power overflow: {} ~ {}", x, y))).map(Dynamic::from) } } else { Ok(Dynamic::from(x.pow(y as u32))) } } #[rhai_fn(name = "<<", return_raw)] pub fn shift_left(x: $arg_type, y: INT) -> Result> { if cfg!(not(feature = "unchecked")) { if cfg!(not(feature = "only_i32")) && y > (u32::MAX as INT) { Err(make_err(format!("Left-shift by too many bits: {} << {}", x, y))) } else if y < 0 { Err(make_err(format!("Left-shift by a negative number: {} << {}", x, y))) } else { x.checked_shl(y as u32).ok_or_else(|| make_err(format!("Left-shift by too many bits: {} << {}", x, y))).map(Dynamic::from) } } else { Ok(Dynamic::from(x << y)) } } #[rhai_fn(name = ">>", return_raw)] pub fn shift_right(x: $arg_type, y: INT) -> Result> { if cfg!(not(feature = "unchecked")) { if cfg!(not(feature = "only_i32")) && y > (u32::MAX as INT) { Err(make_err(format!("Right-shift by too many bits: {} >> {}", x, y))) } else if y < 0 { Err(make_err(format!("Right-shift by a negative number: {} >> {}", x, y))) } else { x.checked_shr(y as u32).ok_or_else(|| make_err(format!("Right-shift by too many bits: {} >> {}", x, y))).map(Dynamic::from) } } else { Ok(Dynamic::from(x >> y)) } } #[rhai_fn(name = "&")] pub fn binary_and(x: $arg_type, y: $arg_type) -> $arg_type { x & y } #[rhai_fn(name = "|")] pub fn binary_or(x: $arg_type, y: $arg_type) -> $arg_type { x | y } #[rhai_fn(name = "^")] pub fn binary_xor(x: $arg_type, y: $arg_type) -> $arg_type { x ^ y } } })* } } } macro_rules! gen_signed_functions { ($root:ident => $($arg_type:ident),+) => { pub mod $root { $(pub mod $arg_type { use super::super::*; #[export_module] pub mod functions { #[rhai_fn(name = "-", return_raw)] pub fn neg(x: $arg_type) -> Result> { if cfg!(not(feature = "unchecked")) { x.checked_neg().ok_or_else(|| make_err(format!("Negation overflow: -{}", x))).map(Dynamic::from) } else { Ok(Dynamic::from(-x)) } } #[rhai_fn(return_raw)] pub fn abs(x: $arg_type) -> Result> { if cfg!(not(feature = "unchecked")) { x.checked_abs().ok_or_else(|| make_err(format!("Negation overflow: -{}", x))).map(Dynamic::from) } else { Ok(Dynamic::from(x.abs())) } } pub fn sign(x: $arg_type) -> INT { if x == 0 { 0 } else if x < 0 { -1 } else { 1 } } } })* } } } macro_rules! reg_functions { ($mod_name:ident += $root:ident ; $($arg_type:ident),+ ) => { $( combine_with_exported_module!($mod_name, "arithmetic", $root::$arg_type::functions); )* } } def_package!(crate:ArithmeticPackage:"Basic arithmetic", lib, { reg_functions!(lib += signed_basic; INT); #[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i64"))] { reg_functions!(lib += arith_numbers; i8, u8, i16, u16, i32, u32, u64); reg_functions!(lib += signed_numbers; i8, i16, i32); #[cfg(not(target_arch = "wasm32"))] { reg_functions!(lib += arith_num_128; i128, u128); reg_functions!(lib += signed_num_128; i128); } } // Basic arithmetic for floating-point #[cfg(not(feature = "no_float"))] { combine_with_exported_module!(lib, "f32", f32_functions); combine_with_exported_module!(lib, "f64", f64_functions); } }); gen_arithmetic_functions!(arith_basic => INT); #[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i64"))] gen_arithmetic_functions!(arith_numbers => i8, u8, i16, u16, i32, u32, u64); #[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i64"))] #[cfg(not(target_arch = "wasm32"))] gen_arithmetic_functions!(arith_num_128 => i128, u128); gen_signed_functions!(signed_basic => INT); #[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i64"))] gen_signed_functions!(signed_numbers => i8, i16, i32); #[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i64"))] #[cfg(not(target_arch = "wasm32"))] gen_signed_functions!(signed_num_128 => i128); #[cfg(not(feature = "no_float"))] #[export_module] mod f32_functions { #[cfg(not(feature = "f32_float"))] pub mod basic_arithmetic { #[rhai_fn(name = "+")] pub fn add(x: f32, y: f32) -> f32 { x + y } #[rhai_fn(name = "-")] pub fn subtract(x: f32, y: f32) -> f32 { x - y } #[rhai_fn(name = "*")] pub fn multiply(x: f32, y: f32) -> f32 { x * y } #[rhai_fn(name = "/")] pub fn divide(x: f32, y: f32) -> f32 { x / y } #[rhai_fn(name = "%")] pub fn modulo(x: f32, y: f32) -> f32 { x % y } #[rhai_fn(name = "~", return_raw)] pub fn pow_f_f(x: f32, y: f32) -> Result> { Ok(Dynamic::from(x.powf(y))) } } #[rhai_fn(name = "-")] pub fn neg(x: f32) -> f32 { -x } pub fn abs(x: f32) -> f32 { x.abs() } pub fn sign(x: f32) -> INT { if x == 0.0 { 0 } else if x < 0.0 { -1 } else { 1 } } #[rhai_fn(name = "~", return_raw)] pub fn pow_f_i(x: f32, y: INT) -> Result> { if cfg!(not(feature = "unchecked")) && y > (i32::MAX as INT) { Err(make_err(format!( "Number raised to too large an index: {} ~ {}", x, y ))) } else { Ok(Dynamic::from(x.powi(y as i32))) } } } #[cfg(not(feature = "no_float"))] #[export_module] mod f64_functions { #[cfg(feature = "f32_float")] pub mod basic_arithmetic { #[rhai_fn(name = "+")] pub fn add(x: f64, y: f64) -> f64 { x + y } #[rhai_fn(name = "-")] pub fn subtract(x: f64, y: f64) -> f64 { x - y } #[rhai_fn(name = "*")] pub fn multiply(x: f64, y: f64) -> f64 { x * y } #[rhai_fn(name = "/")] pub fn divide(x: f64, y: f64) -> f64 { x / y } #[rhai_fn(name = "%")] pub fn modulo(x: f64, y: f64) -> f64 { x % y } #[rhai_fn(name = "~", return_raw)] pub fn pow_f_f(x: f64, y: f64) -> Result> { Ok(Dynamic::from(x.powf(y))) } } #[rhai_fn(name = "-")] pub fn neg(x: f64) -> f64 { -x } pub fn abs(x: f64) -> f64 { x.abs() } pub fn sign(x: f64) -> INT { if x == 0.0 { 0 } else if x < 0.0 { -1 } else { 1 } } #[rhai_fn(name = "~", return_raw)] pub fn pow_f_i(x: FLOAT, y: INT) -> Result> { if cfg!(not(feature = "unchecked")) && y > (i32::MAX as INT) { Err(make_err(format!( "Number raised to too large an index: {} ~ {}", x, y ))) } else { Ok(x.powi(y as i32).into()) } } }