Add Decimal number type.
This commit is contained in:
@@ -197,6 +197,10 @@ def_package!(crate:ArithmeticPackage:"Basic arithmetic", lib, {
|
||||
combine_with_exported_module!(lib, "f32", f32_functions);
|
||||
combine_with_exported_module!(lib, "f64", f64_functions);
|
||||
}
|
||||
|
||||
// Decimal functions
|
||||
#[cfg(feature = "decimal")]
|
||||
combine_with_exported_module!(lib, "decimal", decimal_functions);
|
||||
});
|
||||
|
||||
gen_arithmetic_functions!(arith_basic => INT);
|
||||
@@ -257,7 +261,7 @@ mod f32_functions {
|
||||
}
|
||||
#[rhai_fn(name = "+")]
|
||||
pub fn plus(x: f32) -> f32 {
|
||||
-x
|
||||
x
|
||||
}
|
||||
pub fn abs(x: f32) -> f32 {
|
||||
x.abs()
|
||||
@@ -320,7 +324,7 @@ mod f64_functions {
|
||||
}
|
||||
#[rhai_fn(name = "+")]
|
||||
pub fn plus(x: f64) -> f64 {
|
||||
-x
|
||||
x
|
||||
}
|
||||
pub fn abs(x: f64) -> f64 {
|
||||
x.abs()
|
||||
@@ -346,3 +350,130 @@ mod f64_functions {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "decimal")]
|
||||
#[export_module]
|
||||
mod decimal_functions {
|
||||
use rust_decimal::{prelude::Zero, Decimal};
|
||||
|
||||
#[rhai_fn(name = "+", return_raw)]
|
||||
pub fn add_dd(x: Decimal, y: Decimal) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
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 add_id(x: INT, y: Decimal) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
add_dd(x.into(), y)
|
||||
}
|
||||
#[rhai_fn(name = "+", return_raw)]
|
||||
pub fn add_di(x: Decimal, y: INT) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
add_dd(x, y.into())
|
||||
}
|
||||
#[rhai_fn(name = "-", return_raw)]
|
||||
pub fn subtract_dd(x: Decimal, y: Decimal) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
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 subtract_id(x: INT, y: Decimal) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
subtract_dd(x.into(), y)
|
||||
}
|
||||
#[rhai_fn(name = "-", return_raw)]
|
||||
pub fn subtract_di(x: Decimal, y: INT) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
subtract_dd(x, y.into())
|
||||
}
|
||||
#[rhai_fn(name = "*", return_raw)]
|
||||
pub fn multiply_dd(x: Decimal, y: Decimal) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
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 multiply_id(x: INT, y: Decimal) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
multiply_dd(x.into(), y)
|
||||
}
|
||||
#[rhai_fn(name = "*", return_raw)]
|
||||
pub fn multiply_di(x: Decimal, y: INT) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
multiply_dd(x, y.into())
|
||||
}
|
||||
#[rhai_fn(name = "/", return_raw)]
|
||||
pub fn divide_dd(x: Decimal, y: Decimal) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
if cfg!(not(feature = "unchecked")) {
|
||||
// Detect division by zero
|
||||
if y == Decimal::zero() {
|
||||
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 divide_id(x: INT, y: Decimal) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
divide_dd(x.into(), y)
|
||||
}
|
||||
#[rhai_fn(name = "/", return_raw)]
|
||||
pub fn divide_di(x: Decimal, y: INT) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
divide_dd(x, y.into())
|
||||
}
|
||||
#[rhai_fn(name = "%", return_raw)]
|
||||
pub fn modulo_dd(x: Decimal, y: Decimal) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
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 modulo_id(x: INT, y: Decimal) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
modulo_dd(x.into(), y)
|
||||
}
|
||||
#[rhai_fn(name = "%", return_raw)]
|
||||
pub fn modulo_di(x: Decimal, y: INT) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
modulo_dd(x, y.into())
|
||||
}
|
||||
#[rhai_fn(name = "-")]
|
||||
pub fn neg(x: Decimal) -> Decimal {
|
||||
-x
|
||||
}
|
||||
#[rhai_fn(name = "+")]
|
||||
pub fn plus(x: Decimal) -> Decimal {
|
||||
x
|
||||
}
|
||||
pub fn abs(x: Decimal) -> Decimal {
|
||||
x.abs()
|
||||
}
|
||||
pub fn sign(x: Decimal) -> INT {
|
||||
if x == Decimal::zero() {
|
||||
0
|
||||
} else if x.is_sign_negative() {
|
||||
-1
|
||||
} else {
|
||||
1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -16,6 +16,9 @@ use num_traits::float::Float;
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
use crate::stdlib::format;
|
||||
|
||||
#[cfg(feature = "decimal")]
|
||||
use rust_decimal::Decimal;
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[cfg(feature = "only_i32")]
|
||||
pub const MAX_INT: INT = i32::MAX;
|
||||
@@ -23,7 +26,7 @@ pub const MAX_INT: INT = i32::MAX;
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
pub const MAX_INT: INT = i64::MAX;
|
||||
|
||||
macro_rules! gen_conversion_functions {
|
||||
macro_rules! gen_conversion_as_functions {
|
||||
($root:ident => $func_name:ident ( $($arg_type:ident),+ ) -> $result_type:ty) => {
|
||||
pub mod $root { $(pub mod $arg_type {
|
||||
use super::super::*;
|
||||
@@ -36,6 +39,19 @@ macro_rules! gen_conversion_functions {
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! gen_conversion_into_functions {
|
||||
($root:ident => $func_name:ident ( $($arg_type:ident),+ ) -> $result_type:ty) => {
|
||||
pub mod $root { $(pub mod $arg_type {
|
||||
use super::super::*;
|
||||
|
||||
#[export_fn]
|
||||
pub fn $func_name(x: $arg_type) -> $result_type {
|
||||
x.into()
|
||||
}
|
||||
})* }
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! reg_functions {
|
||||
($mod_name:ident += $root:ident :: $func_name:ident ( $($arg_type:ident),+ ) ) => { $(
|
||||
set_exported_fn!($mod_name, stringify!($func_name), $root::$arg_type::$func_name);
|
||||
@@ -76,6 +92,18 @@ def_package!(crate:BasicMathPackage:"Basic mathematic functions.", lib, {
|
||||
reg_functions!(lib += num_128_to_float::to_float(i128, u128));
|
||||
}
|
||||
}
|
||||
|
||||
// Decimal functions
|
||||
#[cfg(feature = "decimal")]
|
||||
{
|
||||
combine_with_exported_module!(lib, "decimal", decimal_functions);
|
||||
|
||||
reg_functions!(lib += basic_to_decimal::to_decimal(INT));
|
||||
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
#[cfg(not(feature = "only_i64"))]
|
||||
reg_functions!(lib += numbers_to_decimal::to_decimal(i8, u8, i16, u16, i32, u32, i64, u64));
|
||||
}
|
||||
});
|
||||
|
||||
#[export_module]
|
||||
@@ -267,27 +295,75 @@ mod float_functions {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "decimal")]
|
||||
#[export_module]
|
||||
mod decimal_functions {
|
||||
use rust_decimal::Decimal;
|
||||
|
||||
#[rhai_fn(name = "floor", get = "floor")]
|
||||
pub fn floor(x: Decimal) -> Decimal {
|
||||
x.floor()
|
||||
}
|
||||
#[rhai_fn(name = "ceiling", get = "ceiling")]
|
||||
pub fn ceiling(x: Decimal) -> Decimal {
|
||||
x.ceil()
|
||||
}
|
||||
#[rhai_fn(name = "round", get = "round")]
|
||||
pub fn round(x: Decimal) -> Decimal {
|
||||
x.ceil()
|
||||
}
|
||||
#[rhai_fn(name = "int", get = "int")]
|
||||
pub fn int(x: Decimal) -> Decimal {
|
||||
x.trunc()
|
||||
}
|
||||
#[rhai_fn(name = "fraction", get = "fraction")]
|
||||
pub fn fraction(x: Decimal) -> Decimal {
|
||||
x.fract()
|
||||
}
|
||||
#[rhai_fn(return_raw)]
|
||||
pub fn parse_decimal(s: &str) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
s.trim()
|
||||
.parse::<Decimal>()
|
||||
.map(Into::<Dynamic>::into)
|
||||
.map_err(|err| {
|
||||
EvalAltResult::ErrorArithmetic(
|
||||
format!("Error parsing decimal number '{}': {}", s, err),
|
||||
Position::NONE,
|
||||
)
|
||||
.into()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
gen_conversion_functions!(basic_to_float => to_float (INT) -> FLOAT);
|
||||
gen_conversion_as_functions!(basic_to_float => to_float (INT) -> FLOAT);
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
#[cfg(not(feature = "only_i64"))]
|
||||
gen_conversion_functions!(numbers_to_float => to_float (i8, u8, i16, u16, i32, u32, i64, u64) -> FLOAT);
|
||||
gen_conversion_as_functions!(numbers_to_float => to_float (i8, u8, i16, u16, i32, u32, i64, u64) -> FLOAT);
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
#[cfg(not(feature = "only_i64"))]
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
gen_conversion_functions!(num_128_to_float => to_float (i128, u128) -> FLOAT);
|
||||
gen_conversion_as_functions!(num_128_to_float => to_float (i128, u128) -> FLOAT);
|
||||
|
||||
gen_conversion_functions!(basic_to_int => to_int (char) -> INT);
|
||||
gen_conversion_as_functions!(basic_to_int => to_int (char) -> INT);
|
||||
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
#[cfg(not(feature = "only_i64"))]
|
||||
gen_conversion_functions!(numbers_to_int => to_int (i8, u8, i16, u16, i32, u32, i64, u64) -> INT);
|
||||
gen_conversion_as_functions!(numbers_to_int => to_int (i8, u8, i16, u16, i32, u32, i64, u64) -> INT);
|
||||
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
#[cfg(not(feature = "only_i64"))]
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
gen_conversion_functions!(num_128_to_int => to_int (i128, u128) -> INT);
|
||||
gen_conversion_as_functions!(num_128_to_int => to_int (i128, u128) -> INT);
|
||||
|
||||
#[cfg(feature = "decimal")]
|
||||
gen_conversion_into_functions!(basic_to_decimal => to_decimal (INT) -> Decimal);
|
||||
|
||||
#[cfg(feature = "decimal")]
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
#[cfg(not(feature = "only_i64"))]
|
||||
gen_conversion_into_functions!(numbers_to_decimal => to_decimal (i8, u8, i16, u16, i32, u32, i64, u64) -> Decimal);
|
||||
|
@@ -15,6 +15,9 @@ use crate::Array;
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
use crate::Map;
|
||||
|
||||
#[cfg(feature = "decimal")]
|
||||
use rust_decimal::Decimal;
|
||||
|
||||
const FUNC_TO_STRING: &'static str = "to_string";
|
||||
const FUNC_TO_DEBUG: &'static str = "to_debug";
|
||||
|
||||
@@ -73,6 +76,12 @@ def_package!(crate:BasicStringPackage:"Basic string utilities, including printin
|
||||
reg_print_functions!(lib += print_float_32; f32);
|
||||
reg_debug_functions!(lib += print_float_32; f32);
|
||||
}
|
||||
|
||||
#[cfg(feature = "decimal")]
|
||||
{
|
||||
reg_print_functions!(lib += print_decimal; Decimal);
|
||||
reg_debug_functions!(lib += debug_decimal; Decimal);
|
||||
}
|
||||
});
|
||||
|
||||
fn to_string<T: Display>(x: &mut T) -> ImmutableString {
|
||||
@@ -133,6 +142,12 @@ gen_functions!(print_float_64 => print_f64(f64));
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
gen_functions!(print_float_32 => print_f32(f32));
|
||||
|
||||
#[cfg(feature = "decimal")]
|
||||
gen_functions!(print_decimal => to_string(Decimal));
|
||||
|
||||
#[cfg(feature = "decimal")]
|
||||
gen_functions!(debug_decimal => to_debug(Decimal));
|
||||
|
||||
// Register print and debug
|
||||
|
||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||
|
Reference in New Issue
Block a user