From 08977e2a62f3aa3717160faa69c0677967ce8e01 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 21 Aug 2020 21:48:45 +0800 Subject: [PATCH 1/6] Use combine_flatten for plugin modules. --- RELEASES.md | 9 +++++++++ src/module.rs | 33 +++++++++++++++++++++++++++++++-- src/packages/array_basic.rs | 2 +- src/packages/fn_basic.rs | 2 +- src/packages/logic.rs | 2 +- src/packages/map_basic.rs | 4 ++-- src/packages/math_basic.rs | 4 ++-- src/packages/string_more.rs | 4 ++-- src/packages/time_basic.rs | 2 +- src/parser.rs | 4 ++-- src/settings.rs | 1 + tests/plugins.rs | 16 +++++++++++----- 12 files changed, 64 insertions(+), 19 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index d42f41e2..fb624187 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -4,6 +4,15 @@ Rhai Release Notes Version 0.19.0 ============== +New features +------------ + +* Adds `Module::combine_flatten` to combine two modules while flattening to the root level. + + +Version 0.18.2 +============== + Bug fixes --------- diff --git a/src/module.rs b/src/module.rs index 28c64ff0..c6e161b6 100644 --- a/src/module.rs +++ b/src/module.rs @@ -931,6 +931,24 @@ impl Module { /// Combine another module into this module. /// The other module is consumed to merge into this module. pub fn combine(&mut self, other: Self) -> &mut Self { + self.modules.extend(other.modules.into_iter()); + self.variables.extend(other.variables.into_iter()); + self.functions.extend(other.functions.into_iter()); + self.type_iterators.extend(other.type_iterators.into_iter()); + self.all_functions.clear(); + self.all_variables.clear(); + self.indexed = false; + self + } + + /// Combine another module into this module. + /// The other module is consumed to merge into this module. + /// Sub-modules are flattened onto the root module, with higher level overriding lower level. + pub fn combine_flatten(&mut self, other: Self) -> &mut Self { + other.modules.into_iter().for_each(|(_, m)| { + self.combine_flatten(m); + }); + self.variables.extend(other.variables.into_iter()); self.functions.extend(other.functions.into_iter()); self.type_iterators.extend(other.type_iterators.into_iter()); @@ -942,15 +960,26 @@ impl Module { /// Merge another module into this module. pub fn merge(&mut self, other: &Self) -> &mut Self { - self.merge_filtered(other, |_, _, _| true) + self.merge_filtered(other, &|_, _, _| true) } /// Merge another module into this module, with only selected script-defined functions based on a filter predicate. pub(crate) fn merge_filtered( &mut self, other: &Self, - _filter: impl Fn(FnAccess, &str, usize) -> bool, + _filter: &impl Fn(FnAccess, &str, usize) -> bool, ) -> &mut Self { + #[cfg(not(feature = "no_function"))] + for (k, v) in &other.modules { + let mut m = Self::new(); + m.merge_filtered(v, _filter); + self.modules.insert(k.clone(), m); + } + + #[cfg(feature = "no_function")] + self.modules + .extend(other.modules.iter().map(|(k, v)| (k.clone(), v.clone()))); + self.variables .extend(other.variables.iter().map(|(k, v)| (k.clone(), v.clone()))); diff --git a/src/packages/array_basic.rs b/src/packages/array_basic.rs index 433f815f..5b117731 100644 --- a/src/packages/array_basic.rs +++ b/src/packages/array_basic.rs @@ -59,7 +59,7 @@ macro_rules! reg_pad { } def_package!(crate:BasicArrayPackage:"Basic array utilities.", lib, { - lib.combine(exported_module!(array_functions)); + lib.combine_flatten(exported_module!(array_functions)); reg_functions!(lib += basic; INT, bool, char, ImmutableString, FnPtr, Array, Unit); reg_pad!(lib, INT, bool, char, ImmutableString, FnPtr, Array, Unit); diff --git a/src/packages/fn_basic.rs b/src/packages/fn_basic.rs index a115485a..274b85e1 100644 --- a/src/packages/fn_basic.rs +++ b/src/packages/fn_basic.rs @@ -3,7 +3,7 @@ use crate::fn_native::FnPtr; use crate::plugin::*; def_package!(crate:BasicFnPackage:"Basic Fn functions.", lib, { - lib.combine(exported_module!(fn_ptr_functions)); + lib.combine_flatten(exported_module!(fn_ptr_functions)); }); #[export_module] diff --git a/src/packages/logic.rs b/src/packages/logic.rs index 60d7d75c..0c994dca 100644 --- a/src/packages/logic.rs +++ b/src/packages/logic.rs @@ -47,7 +47,7 @@ macro_rules! gen_cmp_functions { macro_rules! reg_functions { ($mod_name:ident += $root:ident ; $($arg_type:ident),+) => { - $($mod_name.combine(exported_module!($root::$arg_type::functions));)* + $($mod_name.combine_flatten(exported_module!($root::$arg_type::functions));)* } } diff --git a/src/packages/map_basic.rs b/src/packages/map_basic.rs index 5834e66b..97e4a9a9 100644 --- a/src/packages/map_basic.rs +++ b/src/packages/map_basic.rs @@ -9,11 +9,11 @@ use crate::plugin::*; use crate::stdlib::vec::Vec; def_package!(crate:BasicMapPackage:"Basic object map utilities.", lib, { - lib.combine(exported_module!(map_functions)); + lib.combine_flatten(exported_module!(map_functions)); // Register map access functions #[cfg(not(feature = "no_index"))] - lib.combine(exported_module!(index_functions)); + lib.combine_flatten(exported_module!(index_functions)); }); #[export_module] diff --git a/src/packages/math_basic.rs b/src/packages/math_basic.rs index f8679766..d5904998 100644 --- a/src/packages/math_basic.rs +++ b/src/packages/math_basic.rs @@ -26,10 +26,10 @@ def_package!(crate:BasicMathPackage:"Basic mathematic functions.", lib, { #[cfg(not(feature = "no_float"))] { // Floating point functions - lib.combine(exported_module!(float_functions)); + lib.combine_flatten(exported_module!(float_functions)); // Trig functions - lib.combine(exported_module!(trig_functions)); + lib.combine_flatten(exported_module!(trig_functions)); // Register conversion functions lib.set_fn_1("to_float", |x: INT| Ok(x as FLOAT)); diff --git a/src/packages/string_more.rs b/src/packages/string_more.rs index fbcb2acd..a387ac40 100644 --- a/src/packages/string_more.rs +++ b/src/packages/string_more.rs @@ -60,9 +60,9 @@ def_package!(crate:MoreStringPackage:"Additional string utilities, including str reg_functions!(lib += float; f32, f64); #[cfg(not(feature = "no_index"))] - lib.combine(exported_module!(index_functions)); + lib.combine_flatten(exported_module!(index_functions)); - lib.combine(exported_module!(string_functions)); + lib.combine_flatten(exported_module!(string_functions)); lib.set_raw_fn( "pad", diff --git a/src/packages/time_basic.rs b/src/packages/time_basic.rs index 6aff8096..d1401288 100644 --- a/src/packages/time_basic.rs +++ b/src/packages/time_basic.rs @@ -26,7 +26,7 @@ use instant::Instant; def_package!(crate:BasicTimePackage:"Basic timing utilities.", lib, { // Register date/time functions - lib.combine(exported_module!(time_functions)); + lib.combine_flatten(exported_module!(time_functions)); }); #[export_module] diff --git a/src/parser.rs b/src/parser.rs index b8bb9cd8..da184a16 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -133,7 +133,7 @@ impl AST { filter: impl Fn(FnAccess, &str, usize) -> bool, ) -> Self { let mut functions: Module = Default::default(); - functions.merge_filtered(&self.1, filter); + functions.merge_filtered(&self.1, &filter); Self(Default::default(), functions) } @@ -266,7 +266,7 @@ impl AST { }; let mut functions = functions.clone(); - functions.merge_filtered(&other.1, filter); + functions.merge_filtered(&other.1, &filter); Self::new(ast, functions) } diff --git a/src/settings.rs b/src/settings.rs index 6e9e020d..4f0f7ccf 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -17,6 +17,7 @@ use crate::stdlib::boxed::Box; impl Engine { /// Load a new package into the `Engine`. + /// Anything that can be converted into a `PackageLibrary` is accepted, including a simple `Module`. /// /// When searching for functions, packages loaded later are preferred. /// In other words, loaded packages are searched in reverse order. diff --git a/tests/plugins.rs b/tests/plugins.rs index 9d40107e..46a52ede 100644 --- a/tests/plugins.rs +++ b/tests/plugins.rs @@ -10,11 +10,16 @@ mod test { pub mod special_array_package { use rhai::{Array, INT}; - #[rhai_fn(get = "foo", return_raw)] - #[inline(always)] - pub fn foo(array: &mut Array) -> Result> { - Ok(array[0].clone()) + #[cfg(not(feature = "no_object"))] + #[rhai_mod()] + pub mod feature { + #[rhai_fn(get = "foo", return_raw)] + #[inline(always)] + pub fn foo(array: &mut Array) -> Result> { + Ok(array[0].clone()) + } } + #[rhai_fn(name = "test")] #[inline(always)] pub fn len(array: &mut Array, mul: INT) -> INT { @@ -59,7 +64,8 @@ gen_unary_functions!(greet = make_greeting(INT, bool, char) -> String); fn test_plugins_package() -> Result<(), Box> { let mut engine = Engine::new(); - let m = exported_module!(test::special_array_package); + let mut m = Module::new(); + m.combine_flatten(exported_module!(test::special_array_package)); engine.load_package(m); reg_functions!(engine += greet::single(INT, bool, char)); From 5b3467631f856a0a98b0d6ef7809c7ad7b28bd4e Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 22 Aug 2020 10:44:51 +0800 Subject: [PATCH 2/6] No need for rhai_mod if not renaming. --- tests/plugins.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/plugins.rs b/tests/plugins.rs index 46a52ede..dd56ae61 100644 --- a/tests/plugins.rs +++ b/tests/plugins.rs @@ -11,7 +11,6 @@ mod test { use rhai::{Array, INT}; #[cfg(not(feature = "no_object"))] - #[rhai_mod()] pub mod feature { #[rhai_fn(get = "foo", return_raw)] #[inline(always)] From 211ce549737532e7111a07dd1bba445183eb7be9 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 22 Aug 2020 22:26:49 +0800 Subject: [PATCH 3/6] Move most packages to modules. --- Cargo.toml | 3 +- src/fn_call.rs | 52 +-- src/packages/arithmetic.rs | 765 ++++++++++++++++++------------------ src/packages/map_basic.rs | 21 +- src/packages/math_basic.rs | 150 ++++--- src/packages/string_more.rs | 30 +- 6 files changed, 511 insertions(+), 510 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9ed6f6c6..ce2f3350 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,6 @@ keywords = [ "scripting" ] categories = [ "no-std", "embedded", "wasm", "parser-implementations" ] [dependencies] -num-traits = { version = "0.2.11", default-features = false } smallvec = { version = "1.4.1", default-features = false } rhai_codegen = { version = "0.1", path = "codegen" } @@ -39,7 +38,7 @@ internals = [] # expose internal data structures unicode-xid-ident = ["unicode-xid"] # allow Unicode Standard Annex #31 for identifiers. # compiling for no-std -no_std = [ "no_closure", "num-traits/libm", "hashbrown", "core-error", "libm", "ahash" ] +no_std = [ "no_closure", "hashbrown", "core-error", "libm", "ahash" ] [profile.release] lto = "fat" diff --git a/src/fn_call.rs b/src/fn_call.rs index 792ccd4c..4ac5a50d 100644 --- a/src/fn_call.rs +++ b/src/fn_call.rs @@ -1120,7 +1120,7 @@ pub fn run_builtin_binary_op( x: &Dynamic, y: &Dynamic, ) -> Result, Box> { - use crate::packages::arithmetic::*; + use crate::packages::arithmetic::arith_basic::INT::functions::*; let args_type = x.type_id(); @@ -1134,14 +1134,14 @@ pub fn run_builtin_binary_op( if cfg!(not(feature = "unchecked")) { match op { - "+" => return add(x, y).map(Into::into).map(Some), - "-" => return sub(x, y).map(Into::into).map(Some), - "*" => return mul(x, y).map(Into::into).map(Some), - "/" => return div(x, y).map(Into::into).map(Some), - "%" => return modulo(x, y).map(Into::into).map(Some), - "~" => return pow_i_i(x, y).map(Into::into).map(Some), - ">>" => return shr(x, y).map(Into::into).map(Some), - "<<" => return shl(x, y).map(Into::into).map(Some), + "+" => return add(x, y).map(Some), + "-" => return subtract(x, y).map(Some), + "*" => return multiply(x, y).map(Some), + "/" => return divide(x, y).map(Some), + "%" => return modulo(x, y).map(Some), + "~" => return power(x, y).map(Some), + ">>" => return shift_right(x, y).map(Some), + "<<" => return shift_left(x, y).map(Some), _ => (), } } else { @@ -1151,9 +1151,9 @@ pub fn run_builtin_binary_op( "*" => return Ok(Some((x * y).into())), "/" => return Ok(Some((x / y).into())), "%" => return Ok(Some((x % y).into())), - "~" => return pow_i_i_u(x, y).map(Into::into).map(Some), - ">>" => return shr_u(x, y).map(Into::into).map(Some), - "<<" => return shl_u(x, y).map(Into::into).map(Some), + "~" => return Ok(Some(x.pow(y as u32).into())), + ">>" => return Ok(Some((x >> y).into())), + "<<" => return Ok(Some((x << y).into())), _ => (), } } @@ -1228,7 +1228,7 @@ pub fn run_builtin_binary_op( "*" => return Ok(Some((x * y).into())), "/" => return Ok(Some((x / y).into())), "%" => return Ok(Some((x % y).into())), - "~" => return pow_f_f(x, y).map(Into::into).map(Some), + "~" => return Ok(Some(x.powf(y).into())), "==" => return Ok(Some((x == y).into())), "!=" => return Ok(Some((x != y).into())), ">" => return Ok(Some((x > y).into())), @@ -1248,7 +1248,7 @@ pub fn run_builtin_op_assignment( x: &mut Dynamic, y: &Dynamic, ) -> Result, Box> { - use crate::packages::arithmetic::*; + use crate::packages::arithmetic::arith_basic::INT::functions::*; let args_type = x.type_id(); @@ -1262,14 +1262,14 @@ pub fn run_builtin_op_assignment( if cfg!(not(feature = "unchecked")) { match op { - "+=" => return Ok(Some(*x = add(*x, y)?)), - "-=" => return Ok(Some(*x = sub(*x, y)?)), - "*=" => return Ok(Some(*x = mul(*x, y)?)), - "/=" => return Ok(Some(*x = div(*x, y)?)), - "%=" => return Ok(Some(*x = modulo(*x, y)?)), - "~=" => return Ok(Some(*x = pow_i_i(*x, y)?)), - ">>=" => return Ok(Some(*x = shr(*x, y)?)), - "<<=" => return Ok(Some(*x = shl(*x, y)?)), + "+=" => return Ok(Some(*x = add(*x, y)?.as_int().unwrap())), + "-=" => return Ok(Some(*x = subtract(*x, y)?.as_int().unwrap())), + "*=" => return Ok(Some(*x = multiply(*x, y)?.as_int().unwrap())), + "/=" => return Ok(Some(*x = divide(*x, y)?.as_int().unwrap())), + "%=" => return Ok(Some(*x = modulo(*x, y)?.as_int().unwrap())), + "~=" => return Ok(Some(*x = power(*x, y)?.as_int().unwrap())), + ">>=" => return Ok(Some(*x = shift_right(*x, y)?.as_int().unwrap())), + "<<=" => return Ok(Some(*x = shift_left(*x, y)?.as_int().unwrap())), _ => (), } } else { @@ -1279,9 +1279,9 @@ pub fn run_builtin_op_assignment( "*=" => return Ok(Some(*x *= y)), "/=" => return Ok(Some(*x /= y)), "%=" => return Ok(Some(*x %= y)), - "~=" => return Ok(Some(*x = pow_i_i_u(*x, y)?)), - ">>=" => return Ok(Some(*x = shr_u(*x, y)?)), - "<<=" => return Ok(Some(*x = shl_u(*x, y)?)), + "~=" => return Ok(Some(*x = x.pow(y as u32))), + ">>=" => return Ok(Some(*x = *x << y)), + "<<=" => return Ok(Some(*x = *x << y)), _ => (), } } @@ -1322,7 +1322,7 @@ pub fn run_builtin_op_assignment( "*=" => return Ok(Some(*x *= y)), "/=" => return Ok(Some(*x /= y)), "%=" => return Ok(Some(*x %= y)), - "~=" => return Ok(Some(*x = pow_f_f(*x, y)?)), + "~=" => return Ok(Some(*x = x.powf(y))), _ => (), } } diff --git a/src/packages/arithmetic.rs b/src/packages/arithmetic.rs index f40f1d11..10d33f8e 100644 --- a/src/packages/arithmetic.rs +++ b/src/packages/arithmetic.rs @@ -1,419 +1,404 @@ +#![allow(non_snake_case)] + use crate::def_package; -use crate::module::FuncReturn; use crate::parser::INT; +use crate::plugin::*; use crate::{result::EvalAltResult, token::Position}; #[cfg(not(feature = "no_float"))] use crate::parser::FLOAT; -use num_traits::{ - identities::Zero, CheckedAdd, CheckedDiv, CheckedMul, CheckedNeg, CheckedRem, CheckedShl, - CheckedShr, CheckedSub, -}; - #[cfg(feature = "no_std")] #[cfg(not(feature = "no_float"))] use num_traits::float::Float; -use crate::stdlib::{ - fmt::Display, - format, - ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Neg, Rem, Shl, Shr, Sub}, -}; +use crate::stdlib::format; -// Checked add -pub fn add(x: T, y: T) -> FuncReturn { - x.checked_add(&y).ok_or_else(|| { - EvalAltResult::ErrorArithmetic( - format!("Addition overflow: {} + {}", x, y), - Position::none(), - ) - .into() - }) -} -// Checked subtract -pub fn sub(x: T, y: T) -> FuncReturn { - x.checked_sub(&y).ok_or_else(|| { - EvalAltResult::ErrorArithmetic( - format!("Subtraction underflow: {} - {}", x, y), - Position::none(), - ) - .into() - }) -} -// Checked multiply -pub fn mul(x: T, y: T) -> FuncReturn { - x.checked_mul(&y).ok_or_else(|| { - EvalAltResult::ErrorArithmetic( - format!("Multiplication overflow: {} * {}", x, y), - Position::none(), - ) - .into() - }) -} -// Checked divide -pub fn div(x: T, y: T) -> FuncReturn -where - T: Display + CheckedDiv + PartialEq + Zero, -{ - // Detect division by zero - if y == T::zero() { - return EvalAltResult::ErrorArithmetic( - format!("Division by zero: {} / {}", x, y), - Position::none(), - ) - .into(); - } +macro_rules! gen_arithmetic_functions { + ($root:ident => $($arg_type:ident),+) => { + pub mod $root { $( + pub mod $arg_type { + use super::super::*; - x.checked_div(&y).ok_or_else(|| { - EvalAltResult::ErrorArithmetic( - format!("Division overflow: {} / {}", x, y), - Position::none(), - ) - .into() - }) -} -// Checked negative - e.g. -(i32::MIN) will overflow i32::MAX -pub fn neg(x: T) -> FuncReturn { - x.checked_neg().ok_or_else(|| { - EvalAltResult::ErrorArithmetic(format!("Negation overflow: -{}", x), Position::none()) - .into() - }) -} -// Checked absolute -pub fn abs(x: T) -> FuncReturn { - // FIX - We don't use Signed::abs() here because, contrary to documentation, it panics - // when the number is ::MIN instead of returning ::MIN itself. - if x >= ::zero() { - Ok(x) - } else { - x.checked_neg().ok_or_else(|| { - EvalAltResult::ErrorArithmetic(format!("Negation overflow: -{}", x), Position::none()) - .into() - }) - } -} -// Unchecked add - may panic on overflow -fn add_u(x: T, y: T) -> FuncReturn<::Output> { - Ok(x + y) -} -// Unchecked subtract - may panic on underflow -fn sub_u(x: T, y: T) -> FuncReturn<::Output> { - Ok(x - y) -} -// Unchecked multiply - may panic on overflow -fn mul_u(x: T, y: T) -> FuncReturn<::Output> { - Ok(x * y) -} -// Unchecked divide - may panic when dividing by zero -fn div_u(x: T, y: T) -> FuncReturn<::Output> { - Ok(x / y) -} -// Unchecked negative - may panic on overflow -fn neg_u(x: T) -> FuncReturn<::Output> { - Ok(-x) -} -// Unchecked absolute - may panic on overflow -fn abs_u(x: T) -> FuncReturn<::Output> -where - T: Neg + PartialOrd + Default + Into<::Output>, -{ - // Numbers should default to zero - if x < Default::default() { - Ok(-x) - } else { - Ok(x.into()) - } -} -// Bit operators -fn binary_and(x: T, y: T) -> FuncReturn<::Output> { - Ok(x & y) -} -fn binary_or(x: T, y: T) -> FuncReturn<::Output> { - Ok(x | y) -} -fn binary_xor(x: T, y: T) -> FuncReturn<::Output> { - Ok(x ^ y) -} -// Checked left-shift -pub fn shl(x: T, y: INT) -> FuncReturn { - // Cannot shift by a negative number of bits - if y < 0 { - return EvalAltResult::ErrorArithmetic( - format!("Left-shift by a negative number: {} << {}", x, y), - Position::none(), - ) - .into(); - } + #[export_module] + pub mod functions { + #[rhai_fn(name = "+", return_raw)] + #[inline] + pub fn add(x: $arg_type, y: $arg_type) -> Result> { + if cfg!(not(feature = "unchecked")) { + x.checked_add(y).ok_or_else(|| { + EvalAltResult::ErrorArithmetic( + format!("Addition overflow: {} + {}", x, y), + Position::none(), + ) + .into() + }).map(Dynamic::from) + } else { + Ok(Dynamic::from(x + y)) + } + } + #[rhai_fn(name = "-", return_raw)] + #[inline] + pub fn subtract(x: $arg_type, y: $arg_type) -> Result> { + if cfg!(not(feature = "unchecked")) { + x.checked_sub(y).ok_or_else(|| { + EvalAltResult::ErrorArithmetic( + format!("Subtraction overflow: {} - {}", x, y), + Position::none(), + ) + .into() + }).map(Dynamic::from) + } else { + Ok(Dynamic::from(x - y)) + } + } + #[rhai_fn(name = "*", return_raw)] + #[inline] + pub fn multiply(x: $arg_type, y: $arg_type) -> Result> { + if cfg!(not(feature = "unchecked")) { + x.checked_mul(y).ok_or_else(|| { + EvalAltResult::ErrorArithmetic( + format!("Multiplication overflow: {} * {}", x, y), + Position::none(), + ) + .into() + }).map(Dynamic::from) + } else { + Ok(Dynamic::from(x * y)) + } + } + #[rhai_fn(name = "/", return_raw)] + #[inline] + pub fn divide(x: $arg_type, y: $arg_type) -> Result> { + if cfg!(not(feature = "unchecked")) { + // Detect division by zero + if y == 0 { + EvalAltResult::ErrorArithmetic( + format!("Division by zero: {} / {}", x, y), + Position::none(), + ) + .into() + } else { + x.checked_div(y).ok_or_else(|| { + EvalAltResult::ErrorArithmetic( + format!("Division overflow: {} / {}", x, y), + Position::none(), + ) + .into() + }).map(Dynamic::from) + } + } else { + Ok(Dynamic::from(x / y)) + } + } + #[rhai_fn(name = "%", return_raw)] + #[inline] + pub fn modulo(x: $arg_type, y: $arg_type) -> Result> { + if cfg!(not(feature = "unchecked")) { + x.checked_rem(y).ok_or_else(|| { + EvalAltResult::ErrorArithmetic( + format!("Modulo division by zero or overflow: {} % {}", x, y), + Position::none(), + ) + .into() + }).map(Dynamic::from) + } else { + Ok(Dynamic::from(x % y)) + } + } + #[rhai_fn(name = "~", return_raw)] + #[inline] + pub fn power(x: INT, y: INT) -> Result> { + if cfg!(not(feature = "unchecked")) { + if cfg!(not(feature = "only_i32")) && y > (u32::MAX as INT) { + EvalAltResult::ErrorArithmetic( + format!("Integer raised to too large an index: {} ~ {}", x, y), + Position::none(), + ) + .into() + } else if y < 0 { + EvalAltResult::ErrorArithmetic( + format!("Integer raised to a negative index: {} ~ {}", x, y), + Position::none(), + ) + .into() + } else { + x.checked_pow(y as u32).ok_or_else(|| { + EvalAltResult::ErrorArithmetic( + format!("Power overflow: {} ~ {}", x, y), + Position::none(), + ) + .into() + }).map(Dynamic::from) + } + } else { + Ok(Dynamic::from(x.pow(y as u32))) + } + } - CheckedShl::checked_shl(&x, y as u32).ok_or_else(|| { - EvalAltResult::ErrorArithmetic( - format!("Left-shift by too many bits: {} << {}", x, y), - Position::none(), - ) - .into() - }) -} -// Checked right-shift -pub fn shr(x: T, y: INT) -> FuncReturn { - // Cannot shift by a negative number of bits - if y < 0 { - return EvalAltResult::ErrorArithmetic( - format!("Right-shift by a negative number: {} >> {}", x, y), - Position::none(), - ) - .into(); + #[rhai_fn(name = "<<", return_raw)] + #[inline] + 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) { + EvalAltResult::ErrorArithmetic( + format!("Left-shift by too many bits: {} << {}", x, y), + Position::none(), + ) + .into() + } else if y < 0 { + EvalAltResult::ErrorArithmetic( + format!("Left-shift by a negative number: {} << {}", x, y), + Position::none(), + ) + .into() + } else { + x.checked_shl(y as u32).ok_or_else(|| { + EvalAltResult::ErrorArithmetic( + format!("Left-shift by too many bits: {} << {}", x, y), + Position::none(), + ) + .into() + }).map(Dynamic::from) + } + } else { + Ok(Dynamic::from(x << y)) + } + } + #[rhai_fn(name = ">>", return_raw)] + #[inline] + 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) { + EvalAltResult::ErrorArithmetic( + format!("Right-shift by too many bits: {} >> {}", x, y), + Position::none(), + ) + .into() + } else if y < 0 { + EvalAltResult::ErrorArithmetic( + format!("Right-shift by a negative number: {} >> {}", x, y), + Position::none(), + ) + .into() + } else { + x.checked_shr(y as u32).ok_or_else(|| { + EvalAltResult::ErrorArithmetic( + format!("Right-shift by too many bits: {} >> {}", x, y), + Position::none(), + ) + .into() + }).map(Dynamic::from) + } + } else { + Ok(Dynamic::from(x >> y)) + } + } + #[rhai_fn(name = "&")] + #[inline(always)] + fn binary_and(x: $arg_type, y: $arg_type) -> $arg_type { + x & y + } + #[rhai_fn(name = "|")] + #[inline(always)] + fn binary_or(x: $arg_type, y: $arg_type) -> $arg_type { + x | y + } + #[rhai_fn(name = "^")] + #[inline(always)] + fn binary_xor(x: $arg_type, y: $arg_type) -> $arg_type { + x ^ y + } + } + } + )* } } - - CheckedShr::checked_shr(&x, y as u32).ok_or_else(|| { - EvalAltResult::ErrorArithmetic( - format!("Right-shift by too many bits: {} % {}", x, y), - Position::none(), - ) - .into() - }) -} -// Unchecked left-shift - may panic if shifting by a negative number of bits -pub fn shl_u>(x: T, y: T) -> FuncReturn<>::Output> { - Ok(x.shl(y)) -} -// Unchecked right-shift - may panic if shifting by a negative number of bits -pub fn shr_u>(x: T, y: T) -> FuncReturn<>::Output> { - Ok(x.shr(y)) -} -// Checked modulo -pub fn modulo(x: T, y: T) -> FuncReturn { - x.checked_rem(&y).ok_or_else(|| { - EvalAltResult::ErrorArithmetic( - format!("Modulo division by zero or overflow: {} % {}", x, y), - Position::none(), - ) - .into() - }) -} -// Unchecked modulo - may panic if dividing by zero -fn modulo_u(x: T, y: T) -> FuncReturn<::Output> { - Ok(x % y) -} -// Checked power -pub fn pow_i_i(x: INT, y: INT) -> FuncReturn { - if cfg!(not(feature = "only_i32")) { - if y > (u32::MAX as INT) { - EvalAltResult::ErrorArithmetic( - format!("Integer raised to too large an index: {} ~ {}", x, y), - Position::none(), - ) - .into() - } else if y < 0 { - EvalAltResult::ErrorArithmetic( - format!("Integer raised to a negative index: {} ~ {}", x, y), - Position::none(), - ) - .into() - } else { - x.checked_pow(y as u32).ok_or_else(|| { - EvalAltResult::ErrorArithmetic( - format!("Power overflow: {} ~ {}", x, y), - Position::none(), - ) - .into() - }) - } - } else { - if y < 0 { - EvalAltResult::ErrorArithmetic( - format!("Integer raised to a negative index: {} ~ {}", x, y), - Position::none(), - ) - .into() - } else { - x.checked_pow(y as u32).ok_or_else(|| { - EvalAltResult::ErrorArithmetic( - format!("Power overflow: {} ~ {}", x, y), - Position::none(), - ) - .into() - }) - } - } -} -// Unchecked integer power - may panic on overflow or if the power index is too high (> u32::MAX) -pub fn pow_i_i_u(x: INT, y: INT) -> FuncReturn { - Ok(x.pow(y as u32)) -} -// Floating-point power - always well-defined -#[cfg(not(feature = "no_float"))] -pub fn pow_f_f(x: FLOAT, y: FLOAT) -> FuncReturn { - Ok(x.powf(y)) -} -// Checked power -#[cfg(not(feature = "no_float"))] -pub fn pow_f_i(x: FLOAT, y: INT) -> FuncReturn { - // Raise to power that is larger than an i32 - if y > (i32::MAX as INT) { - return EvalAltResult::ErrorArithmetic( - format!("Number raised to too large an index: {} ~ {}", x, y), - Position::none(), - ) - .into(); - } - - Ok(x.powi(y as i32)) -} -// Unchecked power - may be incorrect if the power index is too high (> i32::MAX) -#[cfg(not(feature = "no_float"))] -pub fn pow_f_i_u(x: FLOAT, y: INT) -> FuncReturn { - Ok(x.powi(y as i32)) } -macro_rules! reg_unary { - ($lib:expr, $op:expr, $func:ident, $($par:ty),*) => { - $( $lib.set_fn_1($op, $func::<$par>); )* - }; +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)] + #[inline] + pub fn neg(x: $arg_type) -> Result> { + if cfg!(not(feature = "unchecked")) { + x.checked_neg().ok_or_else(|| { + EvalAltResult::ErrorArithmetic(format!("Negation overflow: -{}", x), Position::none()) + .into() + }).map(Dynamic::from) + } else { + Ok(Dynamic::from(-x)) + } + } + #[rhai_fn(return_raw)] + #[inline] + pub fn abs(x: $arg_type) -> Result> { + if cfg!(not(feature = "unchecked")) { + x.checked_abs().ok_or_else(|| { + EvalAltResult::ErrorArithmetic(format!("Negation overflow: -{}", x), Position::none()) + .into() + }).map(Dynamic::from) + } else { + Ok(Dynamic::from(x.abs())) + } + } + #[inline] + pub fn sign(x: $arg_type) -> INT { + if x == 0 { + 0 + } else if x < 0 { + -1 + } else { + 1 + } + } + } + } + )* } + } } -macro_rules! reg_op { - ($lib:expr, $op:expr, $func:ident, $($par:ty),*) => { - $( $lib.set_fn_2($op, $func::<$par>); )* - }; -} -macro_rules! reg_sign { - ($lib:expr, $op:expr, $ret:ty, $($par:ty),*) => { - $( $lib.set_fn_1($op, |value: $par| -> Result<$ret, _> { - Ok(if value == (0 as $par) { - (0 as $ret) - } else if value < (0 as $par) { - (-1 as $ret) - } else { - (1 as $ret) - }) - }); )* - }; + +macro_rules! reg_functions { + ($mod_name:ident += $root:ident ; $($arg_type:ident),+ ) => { + $($mod_name.combine_flatten(exported_module!($root::$arg_type::functions));)* + } } def_package!(crate:ArithmeticPackage:"Basic arithmetic", lib, { - if cfg!(not(feature = "only_i32")) && cfg!(not(feature = "only_i64")) { - if cfg!(not(feature = "unchecked")) { - // Checked basic arithmetic - reg_op!(lib, "+", add, i8, u8, i16, u16, i32, u32, u64); - reg_op!(lib, "-", sub, i8, u8, i16, u16, i32, u32, u64); - reg_op!(lib, "*", mul, i8, u8, i16, u16, i32, u32, u64); - reg_op!(lib, "/", div, i8, u8, i16, u16, i32, u32, u64); - // Checked bit shifts - reg_op!(lib, "<<", shl, i8, u8, i16, u16, i32, u32, u64); - reg_op!(lib, ">>", shr, i8, u8, i16, u16, i32, u32, u64); - reg_op!(lib, "%", modulo, i8, u8, i16, u16, i32, u32, u64); + reg_functions!(lib += signed_basic; INT); - if cfg!(not(target_arch = "wasm32")) { - reg_op!(lib, "+", add, i128, u128); - reg_op!(lib, "-", sub, i128, u128); - reg_op!(lib, "*", mul, i128, u128); - reg_op!(lib, "/", div, i128, u128); - // Checked bit shifts - reg_op!(lib, "<<", shl, i128, u128); - reg_op!(lib, ">>", shr, i128, u128); - reg_op!(lib, "%", modulo, i128, u128); - } - } - - if cfg!(feature = "unchecked") { - // Unchecked basic arithmetic - reg_op!(lib, "+", add_u, i8, u8, i16, u16, i32, u32, u64); - reg_op!(lib, "-", sub_u, i8, u8, i16, u16, i32, u32, u64); - reg_op!(lib, "*", mul_u, i8, u8, i16, u16, i32, u32, u64); - reg_op!(lib, "/", div_u, i8, u8, i16, u16, i32, u32, u64); - // Unchecked bit shifts - reg_op!(lib, "<<", shl_u, i64, i8, u8, i16, u16, i32, u32, u64); - reg_op!(lib, ">>", shr_u, i64, i8, u8, i16, u16, i32, u32, u64); - reg_op!(lib, "%", modulo_u, i8, u8, i16, u16, i32, u32, u64); - - if cfg!(not(target_arch = "wasm32")) { - reg_op!(lib, "+", add_u, i128, u128); - reg_op!(lib, "-", sub_u, i128, u128); - reg_op!(lib, "*", mul_u, i128, u128); - reg_op!(lib, "/", div_u, i128, u128); - // Unchecked bit shifts - reg_op!(lib, "<<", shl_u, i128, u128); - reg_op!(lib, ">>", shr_u, i128, u128); - reg_op!(lib, "%", modulo_u, i128, u128); - } - } - - reg_sign!(lib, "sign", INT, i8, i16, i32, i64); - - if cfg!(not(target_arch = "wasm32")) { - reg_sign!(lib, "sign", INT, i128); - } - } - - // Basic arithmetic for floating-point - no need to check - if cfg!(not(feature = "no_float")) { - reg_op!(lib, "+", add_u, f32); - reg_op!(lib, "-", sub_u, f32); - reg_op!(lib, "*", mul_u, f32); - reg_op!(lib, "/", div_u, f32); - reg_sign!(lib, "sign", f32, f32); - reg_sign!(lib, "sign", f64, f64); - } - - if cfg!(not(feature = "only_i32")) && cfg!(not(feature = "only_i64")) { - reg_op!(lib, "|", binary_or, i8, u8, i16, u16, i32, u32, u64); - reg_op!(lib, "&", binary_and, i8, u8, i16, u16, i32, u32, u64); - reg_op!(lib, "^", binary_xor, i8, u8, i16, u16, i32, u32, u64); - - if cfg!(not(target_arch = "wasm32")) { - reg_op!(lib, "|", binary_or, i128, u128); - reg_op!(lib, "&", binary_and, i128, u128); - reg_op!(lib, "^", binary_xor, i128, u128); - } - } - - #[cfg(not(feature = "no_float"))] + #[cfg(not(feature = "only_i32"))] + #[cfg(not(feature = "only_i64"))] { - // Checked power - if cfg!(not(feature = "unchecked")) { - lib.set_fn_2("~", pow_f_i); - } else { - lib.set_fn_2("~", pow_f_i_u); - } + reg_functions!(lib += arith_numbers; i8, u8, i16, u16, i32, u32, u64); + reg_functions!(lib += signed_numbers; i8, i16, i32); - // Floating-point modulo and power - reg_op!(lib, "%", modulo_u, f32); - - // Floating-point unary - reg_unary!(lib, "-", neg_u, f32, f64); - reg_unary!(lib, "abs", abs_u, f32, f64); - } - - // Checked unary - if cfg!(not(feature = "unchecked")) { - reg_unary!(lib, "-", neg, INT); - reg_unary!(lib, "abs", abs, INT); - - if cfg!(not(feature = "only_i32")) && cfg!(not(feature = "only_i64")) { - reg_unary!(lib, "-", neg, i8, i16, i32, i64); - reg_unary!(lib, "abs", abs, i8, i16, i32, i64); - - if cfg!(not(target_arch = "wasm32")) { - reg_unary!(lib, "-", neg, i128); - reg_unary!(lib, "abs", abs, i128); - } + #[cfg(not(target_arch = "wasm32"))] + { + reg_functions!(lib += arith_num_128; i128, u128); + reg_functions!(lib += signed_num_128; i128); } } - // Unchecked unary - if cfg!(feature = "unchecked") { - reg_unary!(lib, "-", neg_u, INT); - reg_unary!(lib, "abs", abs_u, INT); - - if cfg!(not(feature = "only_i32")) && cfg!(not(feature = "only_i64")) { - reg_unary!(lib, "-", neg_u, i8, i16, i32, i64); - reg_unary!(lib, "abs", abs_u, i8, i16, i32, i64); - - if cfg!(not(target_arch = "wasm32")) { - reg_unary!(lib, "-", neg_u, i128); - reg_unary!(lib, "abs", abs_u, i128); - } - } - } + // Basic arithmetic for floating-point + #[cfg(not(feature = "no_float"))] + lib.combine_flatten(exported_module!(float_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 float_functions { + #[rhai_fn(name = "+")] + #[inline(always)] + pub fn add(x: f32, y: f32) -> f32 { + x + y + } + #[rhai_fn(name = "-")] + #[inline(always)] + pub fn subtract(x: f32, y: f32) -> f32 { + x - y + } + #[rhai_fn(name = "*")] + #[inline(always)] + pub fn multiply(x: f32, y: f32) -> f32 { + x * y + } + #[rhai_fn(name = "/")] + #[inline(always)] + pub fn divide(x: f32, y: f32) -> f32 { + x / y + } + #[rhai_fn(name = "%")] + #[inline(always)] + pub fn modulo(x: f32, y: f32) -> f32 { + x % y + } + #[rhai_fn(name = "-")] + #[inline(always)] + pub fn neg_f32(x: f32) -> f32 { + -x + } + #[rhai_fn(name = "-")] + #[inline(always)] + pub fn neg_f64(x: f64) -> f64 { + -x + } + #[rhai_fn(name = "abs")] + #[inline(always)] + pub fn abs_f32(x: f32) -> f32 { + x.abs() + } + #[rhai_fn(name = "abs")] + #[inline(always)] + pub fn abs_f64(x: f64) -> f64 { + x.abs() + } + #[rhai_fn(name = "sign")] + #[inline] + pub fn sign_f32(x: f32) -> INT { + if x == 0.0 { + 0 + } else if x < 0.0 { + -1 + } else { + 1 + } + } + #[rhai_fn(name = "sign")] + #[inline] + pub fn sign_f64(x: f64) -> INT { + if x == 0.0 { + 0 + } else if x < 0.0 { + -1 + } else { + 1 + } + } + #[rhai_fn(name = "~", return_raw)] + #[inline(always)] + pub fn pow_f_f(x: FLOAT, y: FLOAT) -> Result> { + Ok(x.powf(y).into()) + } + #[rhai_fn(name = "~", return_raw)] + #[inline] + pub fn pow_f_i(x: FLOAT, y: INT) -> Result> { + if cfg!(not(feature = "unchecked")) && y > (i32::MAX as INT) { + EvalAltResult::ErrorArithmetic( + format!("Number raised to too large an index: {} ~ {}", x, y), + Position::none(), + ) + .into() + } else { + Ok(x.powi(y as i32).into()) + } + } +} diff --git a/src/packages/map_basic.rs b/src/packages/map_basic.rs index 97e4a9a9..d479fcfd 100644 --- a/src/packages/map_basic.rs +++ b/src/packages/map_basic.rs @@ -10,10 +10,6 @@ use crate::stdlib::vec::Vec; def_package!(crate:BasicMapPackage:"Basic object map utilities.", lib, { lib.combine_flatten(exported_module!(map_functions)); - - // Register map access functions - #[cfg(not(feature = "no_index"))] - lib.combine_flatten(exported_module!(index_functions)); }); #[export_module] @@ -63,15 +59,14 @@ mod map_functions { } }); } -} -#[cfg(not(feature = "no_index"))] -#[export_module] -mod index_functions { - pub fn keys(map: &mut Map) -> Vec { - map.iter().map(|(k, _)| k.clone().into()).collect() - } - pub fn values(map: &mut Map) -> Vec { - map.iter().map(|(_, v)| v.clone()).collect() + #[cfg(not(feature = "no_index"))] + pub mod indexing { + pub fn keys(map: &mut Map) -> Vec { + map.iter().map(|(k, _)| k.clone().into()).collect() + } + pub fn values(map: &mut Map) -> Vec { + map.iter().map(|(_, v)| v.clone()).collect() + } } } diff --git a/src/packages/math_basic.rs b/src/packages/math_basic.rs index d5904998..3f557124 100644 --- a/src/packages/math_basic.rs +++ b/src/packages/math_basic.rs @@ -1,3 +1,5 @@ +#![allow(non_snake_case)] + use crate::def_package; use crate::parser::INT; use crate::plugin::*; @@ -22,6 +24,28 @@ pub const MAX_INT: INT = i32::MAX; #[cfg(not(feature = "only_i32"))] pub const MAX_INT: INT = i64::MAX; +macro_rules! gen_conversion_functions { + ($root:ident => $func_name:ident ( $($arg_type:ident),+ ) -> $result_type:ty) => { + pub mod $root { $( + pub mod $arg_type { + use super::super::*; + + #[export_fn] + #[inline(always)] + pub fn $func_name(x: $arg_type) -> $result_type { + x as $result_type + } + } + )* } + } +} + +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);)* + } +} + def_package!(crate:BasicMathPackage:"Basic mathematic functions.", lib, { #[cfg(not(feature = "no_float"))] { @@ -31,76 +55,27 @@ def_package!(crate:BasicMathPackage:"Basic mathematic functions.", lib, { // Trig functions lib.combine_flatten(exported_module!(trig_functions)); - // Register conversion functions - lib.set_fn_1("to_float", |x: INT| Ok(x as FLOAT)); - lib.set_fn_1("to_float", |x: f32| Ok(x as FLOAT)); + reg_functions!(lib += basic_to_float::to_float(INT)); - if cfg!(not(feature = "only_i32")) && cfg!(not(feature = "only_i64")) { - lib.set_fn_1("to_float", |x: i8| Ok(x as FLOAT)); - lib.set_fn_1("to_float", |x: u8| Ok(x as FLOAT)); - lib.set_fn_1("to_float", |x: i16| Ok(x as FLOAT)); - lib.set_fn_1("to_float", |x: u16| Ok(x as FLOAT)); - lib.set_fn_1("to_float", |x: i32| Ok(x as FLOAT)); - lib.set_fn_1("to_float", |x: u32| Ok(x as FLOAT)); - lib.set_fn_1("to_float", |x: i64| Ok(x as FLOAT)); - lib.set_fn_1("to_float", |x: u64| Ok(x as FLOAT)); + #[cfg(not(feature = "only_i32"))] + #[cfg(not(feature = "only_i64"))] + { + reg_functions!(lib += numbers_to_float::to_float(i8, u8, i16, u16, i32, u32, i64, u32)); - if cfg!(not(target_arch = "wasm32")) { - lib.set_fn_1("to_float", |x: i128| Ok(x as FLOAT)); - lib.set_fn_1("to_float", |x: u128| Ok(x as FLOAT)); - } + #[cfg(not(target_arch = "wasm32"))] + reg_functions!(lib += num_128_to_float::to_float(i128, u128)); } } - lib.set_fn_1("to_int", |ch: char| Ok(ch as INT)); + reg_functions!(lib += basic_to_int::to_int(char)); - if cfg!(not(feature = "only_i32")) && cfg!(not(feature = "only_i64")) { - lib.set_fn_1("to_int", |x: i8| Ok(x as INT)); - lib.set_fn_1("to_int", |x: u8| Ok(x as INT)); - lib.set_fn_1("to_int", |x: i16| Ok(x as INT)); - lib.set_fn_1("to_int", |x: u16| Ok(x as INT)); - } - - if cfg!(not(feature = "only_i32")) { - lib.set_fn_1("to_int", |x: i32| Ok(x as INT)); - lib.set_fn_1("to_int", |x: u64| Ok(x as INT)); - - if cfg!(feature = "only_i64") { - lib.set_fn_1("to_int", |x: u32| Ok(x as INT)); - } - } - - #[cfg(not(feature = "no_float"))] + #[cfg(not(feature = "only_i32"))] + #[cfg(not(feature = "only_i64"))] { - if cfg!(not(feature = "unchecked")) { - lib.set_fn_1("to_int", |x: f32| { - if x > (MAX_INT as f32) { - return EvalAltResult::ErrorArithmetic( - format!("Integer overflow: to_int({})", x), - Position::none(), - ) - .into(); - } + reg_functions!(lib += numbers_to_int::to_int(i8, u8, i16, u16, i32, u32, i64, u64)); - Ok(x.trunc() as INT) - }); - lib.set_fn_1("to_int", |x: FLOAT| { - if x > (MAX_INT as FLOAT) { - return EvalAltResult::ErrorArithmetic( - format!("Integer overflow: to_int({})", x), - Position::none(), - ) - .into(); - } - - Ok(x.trunc() as INT) - }); - } - - if cfg!(feature = "unchecked") { - lib.set_fn_1("to_int", |x: f32| Ok(x as INT)); - lib.set_fn_1("to_int", |x: f64| Ok(x as INT)); - } + #[cfg(not(target_arch = "wasm32"))] + reg_functions!(lib += num_128_to_int::to_int(i128, u128)); } }); @@ -256,4 +231,55 @@ mod float_functions { pub fn is_infinite_prop(x: FLOAT) -> bool { is_infinite(x) } + #[rhai_fn(name = "to_int", return_raw)] + #[inline] + pub fn f32_to_int(x: f32) -> Result> { + if cfg!(not(feature = "unchecked")) && x > (MAX_INT as f32) { + EvalAltResult::ErrorArithmetic( + format!("Integer overflow: to_int({})", x), + Position::none(), + ) + .into() + } else { + Ok((x.trunc() as INT).into()) + } + } + #[rhai_fn(name = "to_int", return_raw)] + #[inline] + pub fn f64_to_int(x: f64) -> Result> { + if cfg!(not(feature = "unchecked")) && x > (MAX_INT as f64) { + EvalAltResult::ErrorArithmetic( + format!("Integer overflow: to_int({})", x), + Position::none(), + ) + .into() + } else { + Ok((x.trunc() as INT).into()) + } + } } + +#[cfg(not(feature = "no_float"))] +gen_conversion_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); + +#[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_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); + +#[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); diff --git a/src/packages/string_more.rs b/src/packages/string_more.rs index a387ac40..c0e937fa 100644 --- a/src/packages/string_more.rs +++ b/src/packages/string_more.rs @@ -59,9 +59,6 @@ def_package!(crate:MoreStringPackage:"Additional string utilities, including str #[cfg(not(feature = "no_float"))] reg_functions!(lib += float; f32, f64); - #[cfg(not(feature = "no_index"))] - lib.combine_flatten(exported_module!(index_functions)); - lib.combine_flatten(exported_module!(string_functions)); lib.set_raw_fn( @@ -343,21 +340,20 @@ mod string_functions { pub fn replace_char(s: &mut ImmutableString, find: char, sub: char) { *s = s.replace(&find.to_string(), &sub.to_string()).into(); } -} -#[cfg(not(feature = "no_index"))] -#[export_module] -mod index_functions { - use crate::engine::Array; + #[cfg(not(feature = "no_index"))] + pub mod arrays { + use crate::engine::Array; - #[rhai_fn(name = "+")] - #[inline] - pub fn append(x: &mut ImmutableString, y: Array) -> String { - format!("{}{:?}", x, y) - } - #[rhai_fn(name = "+")] - #[inline] - pub fn prepend(x: &mut Array, y: ImmutableString) -> String { - format!("{:?}{}", x, y) + #[rhai_fn(name = "+")] + #[inline] + pub fn append(x: &mut ImmutableString, y: Array) -> String { + format!("{}{:?}", x, y) + } + #[rhai_fn(name = "+")] + #[inline] + pub fn prepend(x: &mut Array, y: ImmutableString) -> String { + format!("{:?}{}", x, y) + } } } From c6a3397b29999f8599c42b50640862c3def036b9 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sun, 23 Aug 2020 11:16:08 +0800 Subject: [PATCH 4/6] Remove dependency on num-traits. --- doc/src/about/features.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/src/about/features.md b/doc/src/about/features.md index d0624c2b..b88599ff 100644 --- a/doc/src/about/features.md +++ b/doc/src/about/features.md @@ -14,7 +14,7 @@ Easy * Easily [call a script-defined function]({{rootUrl}}/engine/call-fn.md) from Rust. -* Very few additional dependencies (right now only [`num-traits`](https://crates.io/crates/num-traits/) to do checked arithmetic operations, and [`smallvec`](https://crates.io/crates/smallvec/)); +* Very few additional dependencies (right now only [`smallvec`](https://crates.io/crates/smallvec/)); for [`no-std`] builds, a number of additional dependencies are pulled in to provide for functionalities that used to be in `std`. Fast From 3902e49a7da5321c3c4d042bd5a26186989d3b65 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sun, 23 Aug 2020 18:04:19 +0800 Subject: [PATCH 5/6] Fix bug in right-shifts. --- src/fn_call.rs | 2 +- tests/compound_equality.rs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/fn_call.rs b/src/fn_call.rs index 4ac5a50d..f7836107 100644 --- a/src/fn_call.rs +++ b/src/fn_call.rs @@ -1280,7 +1280,7 @@ pub fn run_builtin_op_assignment( "/=" => return Ok(Some(*x /= y)), "%=" => return Ok(Some(*x %= y)), "~=" => return Ok(Some(*x = x.pow(y as u32))), - ">>=" => return Ok(Some(*x = *x << y)), + ">>=" => return Ok(Some(*x = *x >> y)), "<<=" => return Ok(Some(*x = *x << y)), _ => (), } diff --git a/tests/compound_equality.rs b/tests/compound_equality.rs index b09115a0..af5c16b6 100644 --- a/tests/compound_equality.rs +++ b/tests/compound_equality.rs @@ -45,16 +45,16 @@ fn test_divide_equals() -> Result<(), Box> { } #[test] -fn test_left_shift_equals() -> Result<(), Box> { +fn test_right_shift_equals() -> Result<(), Box> { let engine = Engine::new(); assert_eq!(engine.eval::("let x = 9; x >>=1; x")?, 4); Ok(()) } #[test] -fn test_right_shift_equals() -> Result<(), Box> { +fn test_left_shift_equals() -> Result<(), Box> { let engine = Engine::new(); - assert_eq!(engine.eval::("let x = 4; x<<= 2; x")?, 16); + assert_eq!(engine.eval::("let x = 4; x <<= 2; x")?, 16); Ok(()) } From 1dacf21a84d9d28ae6c0c7957ce671e66f5e32af Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sun, 23 Aug 2020 18:28:55 +0800 Subject: [PATCH 6/6] Fix no_std builds by pulling in num-traits. --- Cargo.toml | 7 ++++++- src/fn_call.rs | 4 ++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index aa50392c..83a2d021 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,7 +38,7 @@ internals = [] # expose internal data structures unicode-xid-ident = ["unicode-xid"] # allow Unicode Standard Annex #31 for identifiers. # compiling for no-std -no_std = [ "no_closure", "hashbrown", "core-error", "libm", "ahash" ] +no_std = [ "no_closure", "num-traits/libm", "hashbrown", "core-error", "libm", "ahash" ] [profile.release] lto = "fat" @@ -51,6 +51,11 @@ version = "0.2.1" default_features = false optional = true +[dependencies.num-traits] +version = "0.2.11" +default-features = false +optional = true + [dependencies.core-error] version = "0.0.0" default_features = false diff --git a/src/fn_call.rs b/src/fn_call.rs index f7836107..e7c5fdc7 100644 --- a/src/fn_call.rs +++ b/src/fn_call.rs @@ -52,6 +52,10 @@ use crate::stdlib::{ #[cfg(not(feature = "no_function"))] use crate::stdlib::{collections::HashSet, string::String}; +#[cfg(feature = "no_std")] +#[cfg(not(feature = "no_float"))] +use num_traits::float::Float; + /// Extract the property name from a getter function name. #[inline(always)] fn extract_prop_from_getter(_fn_name: &str) -> Option<&str> {