diff --git a/src/api.rs b/src/api.rs index 750b6447..71a94b30 100644 --- a/src/api.rs +++ b/src/api.rs @@ -1,7 +1,7 @@ //! Module that defines the extern API of `Engine`. use crate::any::{Dynamic, Variant}; -use crate::engine::{calc_fn_spec, make_getter, make_setter, Engine, FnAny, Map}; +use crate::engine::{make_getter, make_setter, Engine, Map}; use crate::error::ParseError; use crate::fn_call::FuncArgs; use crate::fn_register::RegisterFn; diff --git a/src/engine.rs b/src/engine.rs index c8828aa5..340dd7d9 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -1,6 +1,7 @@ //! Main module defining the script evaluation `Engine`. use crate::any::{Dynamic, Union}; +use crate::calc_fn_hash; use crate::error::ParseErrorType; use crate::optimize::OptimizationLevel; use crate::packages::{CorePackage, Package, PackageLibrary, StandardPackage}; @@ -121,16 +122,12 @@ impl<'a> From<&'a mut Dynamic> for Target<'a> { } } -/// A type that holds a library of script-defined functions. +/// A type that holds a library (`HashMap`) of script-defined functions. /// /// Since script-defined functions have `Dynamic` parameters, functions with the same name /// and number of parameters are considered equivalent. /// -/// Since the key is a combination of the function name (a String) plus the number of parameters, -/// we cannot use a `HashMap` because we don't want to clone the function name string just -/// to search for it. -/// -/// So instead this is implemented as a sorted list and binary searched. +/// The key of the `HashMap` is a `u64` hash calculated by the function `calc_fn_def`. #[derive(Debug, Clone)] pub struct FunctionsLib( #[cfg(feature = "sync")] HashMap>, @@ -224,7 +221,9 @@ impl DerefMut for FunctionsLib { pub struct Engine { /// A collection of all library packages loaded into the engine. pub(crate) packages: Vec, - /// A hashmap containing all compiled functions known to the engine. + /// A `HashMap` containing all compiled functions known to the engine. + /// + /// The key of the `HashMap` is a `u64` hash calculated by the function `crate::calc_fn_hash`. pub(crate) functions: HashMap>, /// A hashmap containing all iterators known to the engine. @@ -335,13 +334,18 @@ fn extract_prop_from_setter(fn_name: &str) -> Option<&str> { } } -pub(crate) fn calc_fn_spec(fn_name: &str, params: impl Iterator) -> u64 { +/// Calculate a `u64` hash key from a function name and parameter types. +/// +/// Parameter types are passed in via `TypeId` values from an iterator +/// which can come from any source. +pub fn calc_fn_spec(fn_name: &str, params: impl Iterator) -> u64 { let mut s = DefaultHasher::new(); fn_name.hash(&mut s); params.for_each(|t| t.hash(&mut s)); s.finish() } +/// Calculate a `u64` hash key from a function name and number of parameters (without regard to types). pub(crate) fn calc_fn_def(fn_name: &str, params: usize) -> u64 { let mut s = DefaultHasher::new(); fn_name.hash(&mut s); @@ -473,7 +477,12 @@ impl Engine { } } + /// Load a new package into the `Engine`. + /// + /// When searching for functions, packages loaded later are preferred. + /// In other words, loaded packages are searched in reverse order. pub fn load_package(&mut self, package: PackageLibrary) { + // Push the package to the top - packages are searched in reverse order self.packages.insert(0, package); } @@ -576,13 +585,13 @@ impl Engine { } // Search built-in's and external functions - let fn_spec = calc_fn_spec(fn_name, args.iter().map(|a| a.type_id())); + let fn_spec = calc_fn_hash(fn_name, args.iter().map(|a| a.type_id())); if let Some(func) = self.functions.get(&fn_spec).or_else(|| { self.packages .iter() - .find(|p| p.0.contains_key(&fn_spec)) - .and_then(|p| p.0.get(&fn_spec)) + .find(|pkg| pkg.functions.contains_key(&fn_spec)) + .and_then(|pkg| pkg.functions.get(&fn_spec)) }) { // Run external function let result = func(args, pos)?; @@ -1343,7 +1352,7 @@ impl Engine { ) -> bool { engine .functions - .contains_key(&calc_fn_spec(name, once(TypeId::of::()))) + .contains_key(&calc_fn_hash(name, once(TypeId::of::()))) || fn_lib.map_or(false, |lib| lib.has_function(name, 1)) } @@ -1555,8 +1564,8 @@ impl Engine { if let Some(iter_fn) = self.type_iterators.get(&tid).or_else(|| { self.packages .iter() - .find(|p| p.1.contains_key(&tid)) - .and_then(|p| p.1.get(&tid)) + .find(|pkg| pkg.type_iterators.contains_key(&tid)) + .and_then(|pkg| pkg.type_iterators.get(&tid)) }) { // Add the loop variable - variable name is copied // TODO - avoid copying variable name diff --git a/src/fn_call.rs b/src/fn_call.rs index 29a9c056..a28522de 100644 --- a/src/fn_call.rs +++ b/src/fn_call.rs @@ -5,15 +5,15 @@ use crate::any::{Dynamic, Variant}; use crate::stdlib::vec::Vec; -/// Trait that represent arguments to a function call. -/// Any data type that can be converted into a `Vec` of `Dynamic` values can be used +/// Trait that represents arguments to a function call. +/// Any data type that can be converted into a `Vec` can be used /// as arguments to a function call. pub trait FuncArgs { - /// Convert to a `Vec` of `Dynamic` arguments. + /// Convert to a `Vec` of the function call arguments. fn into_vec(self) -> Vec; } -// Macro to implement `FuncArgs` for tuples of standard types (each can be +/// Macro to implement `FuncArgs` for tuples of standard types (each can be /// converted into `Dynamic`). macro_rules! impl_args { ($($p:ident),*) => { diff --git a/src/lib.rs b/src/lib.rs index a8468f0d..3729e1f4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -86,7 +86,7 @@ mod stdlib; mod token; pub use any::Dynamic; -pub use engine::Engine; +pub use engine::{calc_fn_spec as calc_fn_hash, Engine}; pub use error::{ParseError, ParseErrorType}; pub use fn_call::FuncArgs; pub use fn_register::{RegisterDynamicFn, RegisterFn, RegisterResultFn}; diff --git a/src/optimize.rs b/src/optimize.rs index 83488630..a1eee6f5 100644 --- a/src/optimize.rs +++ b/src/optimize.rs @@ -1,7 +1,8 @@ use crate::any::Dynamic; +use crate::calc_fn_hash; use crate::engine::{ - calc_fn_spec, Engine, FnAny, FnCallArgs, FunctionsLib, KEYWORD_DEBUG, KEYWORD_EVAL, - KEYWORD_PRINT, KEYWORD_TYPE_OF, + Engine, FnAny, FnCallArgs, FunctionsLib, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_PRINT, + KEYWORD_TYPE_OF, }; use crate::packages::PackageLibrary; use crate::parser::{map_dynamic_to_expr, Expr, FnDef, ReturnType, Stmt, AST}; @@ -118,15 +119,15 @@ fn call_fn( pos: Position, ) -> Result, Box> { // Search built-in's and external functions - let hash = calc_fn_spec(fn_name, args.iter().map(|a| a.type_id())); + let hash = calc_fn_hash(fn_name, args.iter().map(|a| a.type_id())); functions .get(&hash) .or_else(|| { packages .iter() - .find(|p| p.0.contains_key(&hash)) - .and_then(|p| p.0.get(&hash)) + .find(|p| p.functions.contains_key(&hash)) + .and_then(|p| p.functions.get(&hash)) }) .map(|func| func(args, pos)) .transpose() diff --git a/src/packages/arithmetic.rs b/src/packages/arithmetic.rs index acc12f0d..24bf9687 100644 --- a/src/packages/arithmetic.rs +++ b/src/packages/arithmetic.rs @@ -1,7 +1,6 @@ -use super::{ - create_new_package, reg_binary, reg_unary, Package, PackageLibrary, PackageLibraryStore, -}; +use super::{reg_binary, reg_unary}; +use crate::def_package; use crate::fn_register::{map_dynamic as map, map_result as result}; use crate::parser::INT; use crate::result::EvalAltResult; @@ -18,7 +17,7 @@ use num_traits::{ use crate::stdlib::{ fmt::Display, format, - ops::{Add, BitAnd, BitOr, BitXor, Deref, Div, Mul, Neg, Rem, Shl, Shr, Sub}, + ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Neg, Rem, Shl, Shr, Sub}, {i32, i64, u32}, }; @@ -254,16 +253,6 @@ fn pow_f_i_u(x: FLOAT, y: INT) -> FLOAT { x.powi(y as i32) } -pub struct ArithmeticPackage(PackageLibrary); - -impl Deref for ArithmeticPackage { - type Target = PackageLibrary; - - fn deref(&self) -> &PackageLibrary { - &self.0 - } -} - macro_rules! reg_unary_x { ($lib:expr, $op:expr, $func:ident, $($par:ty),*) => { $(reg_unary($lib, $op, $func::<$par>, result);)* }; } @@ -277,166 +266,154 @@ macro_rules! reg_op { ($lib:expr, $op:expr, $func:ident, $($par:ty),*) => { $(reg_binary($lib, $op, $func::<$par>, map);)* }; } -impl Package for ArithmeticPackage { - fn new() -> Self { - let mut pkg = create_new_package(); - Self::init(&mut pkg); - Self(pkg.into()) - } - - fn get(&self) -> PackageLibrary { - self.0.clone() - } - - fn init(lib: &mut PackageLibraryStore) { - // Checked basic arithmetic - #[cfg(not(feature = "unchecked"))] - { - reg_op_x!(lib, "+", add, INT); - reg_op_x!(lib, "-", sub, INT); - reg_op_x!(lib, "*", mul, INT); - reg_op_x!(lib, "/", div, INT); - - #[cfg(not(feature = "only_i32"))] - #[cfg(not(feature = "only_i64"))] - { - reg_op_x!(lib, "+", add, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); - reg_op_x!(lib, "-", sub, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); - reg_op_x!(lib, "*", mul, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); - reg_op_x!(lib, "/", div, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); - } - } - - // Unchecked basic arithmetic - #[cfg(feature = "unchecked")] - { - reg_op!(lib, "+", add_u, INT); - reg_op!(lib, "-", sub_u, INT); - reg_op!(lib, "*", mul_u, INT); - reg_op!(lib, "/", div_u, INT); - - #[cfg(not(feature = "only_i32"))] - #[cfg(not(feature = "only_i64"))] - { - reg_op!(lib, "+", add_u, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); - reg_op!(lib, "-", sub_u, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); - reg_op!(lib, "*", mul_u, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); - reg_op!(lib, "/", div_u, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); - } - } - - // Basic arithmetic for floating-point - no need to check - #[cfg(not(feature = "no_float"))] - { - reg_op!(lib, "+", add_u, f32, f64); - reg_op!(lib, "-", sub_u, f32, f64); - reg_op!(lib, "*", mul_u, f32, f64); - reg_op!(lib, "/", div_u, f32, f64); - } - - // Bit operations - reg_op!(lib, "|", binary_or, INT); - reg_op!(lib, "&", binary_and, INT); - reg_op!(lib, "^", binary_xor, INT); +def_package!(ArithmeticPackage:"Basic arithmetic", lib, { + // Checked basic arithmetic + #[cfg(not(feature = "unchecked"))] + { + reg_op_x!(lib, "+", add, INT); + reg_op_x!(lib, "-", sub, INT); + reg_op_x!(lib, "*", mul, INT); + reg_op_x!(lib, "/", div, INT); #[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i64"))] { - reg_op!(lib, "|", binary_or, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); - reg_op!(lib, "&", binary_and, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); - reg_op!(lib, "^", binary_xor, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); - } - - // Checked bit shifts - #[cfg(not(feature = "unchecked"))] - { - reg_op_x!(lib, "<<", shl, INT); - reg_op_x!(lib, ">>", shr, INT); - reg_op_x!(lib, "%", modulo, INT); - - #[cfg(not(feature = "only_i32"))] - #[cfg(not(feature = "only_i64"))] - { - reg_op_x!(lib, "<<", shl, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); - reg_op_x!(lib, ">>", shr, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); - reg_op_x!(lib, "%", modulo, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); - } - } - - // Unchecked bit shifts - #[cfg(feature = "unchecked")] - { - reg_op!(lib, "<<", shl_u, INT, INT); - reg_op!(lib, ">>", shr_u, INT, INT); - reg_op!(lib, "%", modulo_u, INT); - - #[cfg(not(feature = "only_i32"))] - #[cfg(not(feature = "only_i64"))] - { - reg_op!(lib, "<<", shl_u, i64, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); - reg_op!(lib, ">>", shr_u, i64, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); - reg_op!(lib, "%", modulo_u, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); - } - } - - // Checked power - #[cfg(not(feature = "unchecked"))] - { - reg_binary(lib, "~", pow_i_i, result); - - #[cfg(not(feature = "no_float"))] - reg_binary(lib, "~", pow_f_i, result); - } - - // Unchecked power - #[cfg(feature = "unchecked")] - { - reg_binary(lib, "~", pow_i_i_u, map); - - #[cfg(not(feature = "no_float"))] - reg_binary(lib, "~", pow_f_i_u, map); - } - - // Floating-point modulo and power - #[cfg(not(feature = "no_float"))] - { - reg_op!(lib, "%", modulo_u, f32, f64); - reg_binary(lib, "~", pow_f_f, map); - } - - // Checked unary - #[cfg(not(feature = "unchecked"))] - { - reg_unary_x!(lib, "-", neg, INT); - reg_unary_x!(lib, "abs", abs, INT); - - #[cfg(not(feature = "only_i32"))] - #[cfg(not(feature = "only_i64"))] - { - reg_unary_x!(lib, "-", neg, i8, i16, i32, i64, i128); - reg_unary_x!(lib, "abs", abs, i8, i16, i32, i64, i128); - } - } - - // Unchecked unary - #[cfg(feature = "unchecked")] - { - reg_unary!(lib, "-", neg_u, INT); - reg_unary!(lib, "abs", abs_u, INT); - - #[cfg(not(feature = "only_i32"))] - #[cfg(not(feature = "only_i64"))] - { - reg_unary!(lib, "-", neg_u, i8, i16, i32, i64, i128); - reg_unary!(lib, "abs", abs_u, i8, i16, i32, i64, i128); - } - } - - // Floating-point unary - #[cfg(not(feature = "no_float"))] - { - reg_unary!(lib, "-", neg_u, f32, f64); - reg_unary!(lib, "abs", abs_u, f32, f64); + reg_op_x!(lib, "+", add, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); + reg_op_x!(lib, "-", sub, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); + reg_op_x!(lib, "*", mul, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); + reg_op_x!(lib, "/", div, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); } } -} + + // Unchecked basic arithmetic + #[cfg(feature = "unchecked")] + { + reg_op!(lib, "+", add_u, INT); + reg_op!(lib, "-", sub_u, INT); + reg_op!(lib, "*", mul_u, INT); + reg_op!(lib, "/", div_u, INT); + + #[cfg(not(feature = "only_i32"))] + #[cfg(not(feature = "only_i64"))] + { + reg_op!(lib, "+", add_u, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); + reg_op!(lib, "-", sub_u, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); + reg_op!(lib, "*", mul_u, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); + reg_op!(lib, "/", div_u, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); + } + } + + // Basic arithmetic for floating-point - no need to check + #[cfg(not(feature = "no_float"))] + { + reg_op!(lib, "+", add_u, f32, f64); + reg_op!(lib, "-", sub_u, f32, f64); + reg_op!(lib, "*", mul_u, f32, f64); + reg_op!(lib, "/", div_u, f32, f64); + } + + // Bit operations + reg_op!(lib, "|", binary_or, INT); + reg_op!(lib, "&", binary_and, INT); + reg_op!(lib, "^", binary_xor, INT); + + #[cfg(not(feature = "only_i32"))] + #[cfg(not(feature = "only_i64"))] + { + reg_op!(lib, "|", binary_or, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); + reg_op!(lib, "&", binary_and, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); + reg_op!(lib, "^", binary_xor, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); + } + + // Checked bit shifts + #[cfg(not(feature = "unchecked"))] + { + reg_op_x!(lib, "<<", shl, INT); + reg_op_x!(lib, ">>", shr, INT); + reg_op_x!(lib, "%", modulo, INT); + + #[cfg(not(feature = "only_i32"))] + #[cfg(not(feature = "only_i64"))] + { + reg_op_x!(lib, "<<", shl, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); + reg_op_x!(lib, ">>", shr, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); + reg_op_x!(lib, "%", modulo, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); + } + } + + // Unchecked bit shifts + #[cfg(feature = "unchecked")] + { + reg_op!(lib, "<<", shl_u, INT, INT); + reg_op!(lib, ">>", shr_u, INT, INT); + reg_op!(lib, "%", modulo_u, INT); + + #[cfg(not(feature = "only_i32"))] + #[cfg(not(feature = "only_i64"))] + { + reg_op!(lib, "<<", shl_u, i64, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); + reg_op!(lib, ">>", shr_u, i64, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); + reg_op!(lib, "%", modulo_u, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); + } + } + + // Checked power + #[cfg(not(feature = "unchecked"))] + { + reg_binary(lib, "~", pow_i_i, result); + + #[cfg(not(feature = "no_float"))] + reg_binary(lib, "~", pow_f_i, result); + } + + // Unchecked power + #[cfg(feature = "unchecked")] + { + reg_binary(lib, "~", pow_i_i_u, map); + + #[cfg(not(feature = "no_float"))] + reg_binary(lib, "~", pow_f_i_u, map); + } + + // Floating-point modulo and power + #[cfg(not(feature = "no_float"))] + { + reg_op!(lib, "%", modulo_u, f32, f64); + reg_binary(lib, "~", pow_f_f, map); + } + + // Checked unary + #[cfg(not(feature = "unchecked"))] + { + reg_unary_x!(lib, "-", neg, INT); + reg_unary_x!(lib, "abs", abs, INT); + + #[cfg(not(feature = "only_i32"))] + #[cfg(not(feature = "only_i64"))] + { + reg_unary_x!(lib, "-", neg, i8, i16, i32, i64, i128); + reg_unary_x!(lib, "abs", abs, i8, i16, i32, i64, i128); + } + } + + // Unchecked unary + #[cfg(feature = "unchecked")] + { + reg_unary!(lib, "-", neg_u, INT); + reg_unary!(lib, "abs", abs_u, INT); + + #[cfg(not(feature = "only_i32"))] + #[cfg(not(feature = "only_i64"))] + { + reg_unary!(lib, "-", neg_u, i8, i16, i32, i64, i128); + reg_unary!(lib, "abs", abs_u, i8, i16, i32, i64, i128); + } + } + + // Floating-point unary + #[cfg(not(feature = "no_float"))] + { + reg_unary!(lib, "-", neg_u, f32, f64); + reg_unary!(lib, "abs", abs_u, f32, f64); + } +}); diff --git a/src/packages/array_basic.rs b/src/packages/array_basic.rs index 5dbeecdd..e6b3fd1a 100644 --- a/src/packages/array_basic.rs +++ b/src/packages/array_basic.rs @@ -1,14 +1,12 @@ -use super::{ - create_new_package, reg_binary, reg_binary_mut, reg_trinary_mut, reg_unary_mut, Package, - PackageLibrary, PackageLibraryStore, -}; +use super::{reg_binary, reg_binary_mut, reg_trinary_mut, reg_unary_mut}; +use crate::def_package; use crate::any::{Dynamic, Variant}; use crate::engine::Array; -use crate::fn_register::{map_dynamic as map, map_identity as copy}; +use crate::fn_register::{map_dynamic as map, map_identity as pass}; use crate::parser::INT; -use crate::stdlib::ops::Deref; +use crate::stdlib::any::TypeId; // Register array utility functions fn push(list: &mut Array, item: T) { @@ -31,16 +29,6 @@ fn pad(list: &mut Array, len: INT, item: T) { } } -pub struct BasicArrayPackage(PackageLibrary); - -impl Deref for BasicArrayPackage { - type Target = PackageLibrary; - - fn deref(&self) -> &PackageLibrary { - &self.0 - } -} - macro_rules! reg_op { ($lib:expr, $op:expr, $func:ident, $($par:ty),*) => { $(reg_binary_mut($lib, $op, $func::<$par>, map);)* }; } @@ -48,92 +36,87 @@ macro_rules! reg_tri { ($lib:expr, $op:expr, $func:ident, $($par:ty),*) => { $(reg_trinary_mut($lib, $op, $func::<$par>, map);)* }; } -impl Package for BasicArrayPackage { - fn new() -> Self { - let mut pkg = create_new_package(); - Self::init(&mut pkg); - Self(pkg.into()) +#[cfg(not(feature = "no_index"))] +def_package!(BasicArrayPackage:"Basic array utilities.", lib, { + reg_op!(lib, "push", push, INT, bool, char, String, Array, ()); + reg_tri!(lib, "pad", pad, INT, bool, char, String, Array, ()); + reg_tri!(lib, "insert", ins, INT, bool, char, String, Array, ()); + + reg_binary_mut(lib, "append", |x: &mut Array, y: Array| x.extend(y), map); + reg_binary( + lib, + "+", + |mut x: Array, y: Array| { + x.extend(y); + x + }, + map, + ); + + #[cfg(not(feature = "only_i32"))] + #[cfg(not(feature = "only_i64"))] + { + reg_op!(lib, "push", push, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); + reg_tri!(lib, "pad", pad, i8, u8, i16, u16, i32, u32, i64, u64, i128, u128); + reg_tri!(lib, "insert", ins, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); } - fn get(&self) -> PackageLibrary { - self.0.clone() + #[cfg(not(feature = "no_float"))] + { + reg_op!(lib, "push", push, f32, f64); + reg_tri!(lib, "pad", pad, f32, f64); + reg_tri!(lib, "insert", ins, f32, f64); } - fn init(lib: &mut PackageLibraryStore) { - #[cfg(not(feature = "no_index"))] - { - reg_op!(lib, "push", push, INT, bool, char, String, Array, ()); - reg_tri!(lib, "pad", pad, INT, bool, char, String, Array, ()); - reg_tri!(lib, "insert", ins, INT, bool, char, String, Array, ()); - - reg_binary_mut(lib, "append", |x: &mut Array, y: Array| x.extend(y), map); - reg_binary( - lib, - "+", - |mut x: Array, y: Array| { - x.extend(y); - x - }, - map, - ); - - #[cfg(not(feature = "only_i32"))] - #[cfg(not(feature = "only_i64"))] - { - reg_op!(lib, "push", push, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); - reg_tri!(lib, "pad", pad, i8, u8, i16, u16, i32, u32, i64, u64, i128, u128); - reg_tri!(lib, "insert", ins, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); + reg_unary_mut( + lib, + "pop", + |list: &mut Array| list.pop().unwrap_or_else(|| Dynamic::from_unit()), + pass, + ); + reg_unary_mut( + lib, + "shift", + |list: &mut Array| { + if !list.is_empty() { + Dynamic::from_unit() + } else { + list.remove(0) } - - #[cfg(not(feature = "no_float"))] - { - reg_op!(lib, "push", push, f32, f64); - reg_tri!(lib, "pad", pad, f32, f64); - reg_tri!(lib, "insert", ins, f32, f64); + }, + pass, + ); + reg_binary_mut( + lib, + "remove", + |list: &mut Array, len: INT| { + if len < 0 || (len as usize) >= list.len() { + Dynamic::from_unit() + } else { + list.remove(len as usize) } + }, + pass, + ); + reg_unary_mut(lib, "len", |list: &mut Array| list.len() as INT, map); + reg_unary_mut(lib, "clear", |list: &mut Array| list.clear(), map); + reg_binary_mut( + lib, + "truncate", + |list: &mut Array, len: INT| { + if len >= 0 { + list.truncate(len as usize); + } + }, + map, + ); - reg_unary_mut( - lib, - "pop", - |list: &mut Array| list.pop().unwrap_or_else(|| Dynamic::from_unit()), - copy, - ); - reg_unary_mut( - lib, - "shift", - |list: &mut Array| { - if !list.is_empty() { - Dynamic::from_unit() - } else { - list.remove(0) - } - }, - copy, - ); - reg_binary_mut( - lib, - "remove", - |list: &mut Array, len: INT| { - if len < 0 || (len as usize) >= list.len() { - Dynamic::from_unit() - } else { - list.remove(len as usize) - } - }, - copy, - ); - reg_unary_mut(lib, "len", |list: &mut Array| list.len() as INT, map); - reg_unary_mut(lib, "clear", |list: &mut Array| list.clear(), map); - reg_binary_mut( - lib, - "truncate", - |list: &mut Array, len: INT| { - if len >= 0 { - list.truncate(len as usize); - } - }, - map, - ); - } - } -} + // Register array iterator + lib.type_iterators.insert( + TypeId::of::(), + Box::new(|a: &Dynamic| { + Box::new(a.downcast_ref::().unwrap().clone().into_iter()) + as Box> + }), + ); +}); diff --git a/src/packages/basic.rs b/src/packages/basic.rs deleted file mode 100644 index ecc619e1..00000000 --- a/src/packages/basic.rs +++ /dev/null @@ -1,33 +0,0 @@ -use super::{ - create_new_package, reg_binary, reg_binary_mut, reg_unary, Package, PackageLibrary, - PackageLibraryStore, -}; - -use crate::fn_register::map_dynamic as map; -use crate::parser::INT; - -use crate::stdlib::ops::Deref; - -pub struct BasicPackage(PackageLibrary); - -impl Deref for BasicPackage { - type Target = PackageLibrary; - - fn deref(&self) -> &PackageLibrary { - &self.0 - } -} - -impl Package for BasicPackage { - fn new() -> Self { - let mut pkg = create_new_package(); - Self::init(&mut pkg); - Self(pkg.into()) - } - - fn get(&self) -> PackageLibrary { - self.0.clone() - } - - fn init(lib: &mut PackageLibraryStore) {} -} diff --git a/src/packages/iter_basic.rs b/src/packages/iter_basic.rs index a5ebd896..eefc9e0f 100644 --- a/src/packages/iter_basic.rs +++ b/src/packages/iter_basic.rs @@ -1,8 +1,6 @@ -use super::{ - create_new_package, reg_binary, reg_trinary, reg_unary_mut, Package, PackageLibrary, - PackageLibraryStore, -}; +use super::{reg_binary, reg_trinary, reg_unary_mut, PackageStore}; +use crate::def_package; use crate::any::{Dynamic, Union, Variant}; use crate::engine::{Array, Map}; use crate::fn_register::map_dynamic as map; @@ -10,15 +8,15 @@ use crate::parser::INT; use crate::stdlib::{ any::TypeId, - ops::{Add, Deref, Range}, + ops::{Add, Range}, }; // Register range function -fn reg_range(lib: &mut PackageLibraryStore) +fn reg_range(lib: &mut PackageStore) where Range: Iterator, { - lib.1.insert( + lib.type_iterators.insert( TypeId::of::>(), Box::new(|source: &Dynamic| { Box::new( @@ -57,13 +55,13 @@ where } } -fn reg_step(lib: &mut PackageLibraryStore) +fn reg_step(lib: &mut PackageStore) where for<'a> &'a T: Add<&'a T, Output = T>, T: Variant + Clone + PartialOrd, StepRange: Iterator, { - lib.1.insert( + lib.type_iterators.insert( TypeId::of::>(), Box::new(|source: &Dynamic| { Box::new( @@ -77,97 +75,44 @@ where ); } -pub struct BasicIteratorPackage(PackageLibrary); - -impl Deref for BasicIteratorPackage { - type Target = PackageLibrary; - - fn deref(&self) -> &PackageLibrary { - &self.0 - } -} - -impl Package for BasicIteratorPackage { - fn new() -> Self { - let mut pkg = create_new_package(); - Self::init(&mut pkg); - Self(pkg.into()) +def_package!(BasicIteratorPackage:"Basic range iterators.", lib, { + fn get_range(from: T, to: T) -> Range { + from..to } - fn get(&self) -> PackageLibrary { - self.0.clone() + reg_range::(lib); + reg_binary(lib, "range", get_range::, map); + + #[cfg(not(feature = "only_i32"))] + #[cfg(not(feature = "only_i64"))] + { + macro_rules! reg_range { + ($self:expr, $x:expr, $( $y:ty ),*) => ( + $( + reg_range::<$y>($self); + reg_binary($self, $x, get_range::<$y>, map); + )* + ) + } + + reg_range!(lib, "range", i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); } - fn init(lib: &mut PackageLibraryStore) { - #[cfg(not(feature = "no_index"))] - { - // Register array iterator - lib.1.insert( - TypeId::of::(), - Box::new(|a: &Dynamic| { - Box::new(a.downcast_ref::().unwrap().clone().into_iter()) - as Box> - }), - ); + reg_step::(lib); + reg_trinary(lib, "range", StepRange::, map); + + #[cfg(not(feature = "only_i32"))] + #[cfg(not(feature = "only_i64"))] + { + macro_rules! reg_step { + ($self:expr, $x:expr, $( $y:ty ),*) => ( + $( + reg_step::<$y>($self); + reg_trinary($self, $x, StepRange::<$y>, map); + )* + ) } - // Register map access functions - #[cfg(not(feature = "no_object"))] - { - fn map_get_keys(map: &mut Map) -> Vec { - map.iter() - .map(|(k, _)| Dynamic(Union::Str(Box::new(k.to_string())))) - .collect::>() - } - fn map_get_values(map: &mut Map) -> Vec { - map.iter().map(|(_, v)| v.clone()).collect::>() - } - - #[cfg(not(feature = "no_index"))] - reg_unary_mut(lib, "keys", map_get_keys, map); - - #[cfg(not(feature = "no_index"))] - reg_unary_mut(lib, "values", map_get_values, map); - } - - fn get_range(from: T, to: T) -> Range { - from..to - } - - reg_range::(lib); - reg_binary(lib, "range", get_range::, map); - - #[cfg(not(feature = "only_i32"))] - #[cfg(not(feature = "only_i64"))] - { - macro_rules! reg_range { - ($self:expr, $x:expr, $( $y:ty ),*) => ( - $( - reg_range::<$y>($self); - reg_binary($self, $x, get_range::<$y>, map); - )* - ) - } - - reg_range!(lib, "range", i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); - } - - reg_step::(lib); - reg_trinary(lib, "range", StepRange::, map); - - #[cfg(not(feature = "only_i32"))] - #[cfg(not(feature = "only_i64"))] - { - macro_rules! reg_step { - ($self:expr, $x:expr, $( $y:ty ),*) => ( - $( - reg_step::<$y>($self); - reg_trinary($self, $x, StepRange::<$y>, map); - )* - ) - } - - reg_step!(lib, "range", i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); - } + reg_step!(lib, "range", i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); } -} +}); diff --git a/src/packages/logic.rs b/src/packages/logic.rs index 2244558a..934e4f98 100644 --- a/src/packages/logic.rs +++ b/src/packages/logic.rs @@ -1,13 +1,10 @@ -use super::{ - create_new_package, reg_binary, reg_binary_mut, reg_unary, Package, PackageLibrary, - PackageLibraryStore, -}; +use super::utils::reg_test; +use super::{reg_binary, reg_binary_mut, reg_unary}; +use crate::def_package; use crate::fn_register::map_dynamic as map; use crate::parser::INT; -use crate::stdlib::ops::Deref; - // Comparison operators pub fn lt(x: T, y: T) -> bool { x < y @@ -43,71 +40,50 @@ macro_rules! reg_op { ($lib:expr, $op:expr, $func:ident, $($par:ty),*) => { $(reg_binary($lib, $op, $func::<$par>, map);)* }; } -pub struct LogicPackage(PackageLibrary); +def_package!(LogicPackage:"Logical operators.", lib, { + reg_op!(lib, "<", lt, INT, char); + reg_op!(lib, "<=", lte, INT, char); + reg_op!(lib, ">", gt, INT, char); + reg_op!(lib, ">=", gte, INT, char); + reg_op!(lib, "==", eq, INT, char, bool, ()); + reg_op!(lib, "!=", ne, INT, char, bool, ()); -impl Deref for LogicPackage { - type Target = PackageLibrary; + // Special versions for strings - at least avoid copying the first string + //reg_test(lib, "<", |x: &mut String, y: String| *x < y, |v| v, map); + reg_binary_mut(lib, "<", |x: &mut String, y: String| *x < y, map); + reg_binary_mut(lib, "<=", |x: &mut String, y: String| *x <= y, map); + reg_binary_mut(lib, ">", |x: &mut String, y: String| *x > y, map); + reg_binary_mut(lib, ">=", |x: &mut String, y: String| *x >= y, map); + reg_binary_mut(lib, "==", |x: &mut String, y: String| *x == y, map); + reg_binary_mut(lib, "!=", |x: &mut String, y: String| *x != y, map); - fn deref(&self) -> &PackageLibrary { - &self.0 - } -} - -impl Package for LogicPackage { - fn new() -> Self { - let mut pkg = create_new_package(); - Self::init(&mut pkg); - Self(pkg.into()) + #[cfg(not(feature = "only_i32"))] + #[cfg(not(feature = "only_i64"))] + { + reg_op!(lib, "<", lt, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); + reg_op!(lib, "<=", lte, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); + reg_op!(lib, ">", gt, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); + reg_op!(lib, ">=", gte, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); + reg_op!(lib, "==", eq, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); + reg_op!(lib, "!=", ne, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); } - fn get(&self) -> PackageLibrary { - self.0.clone() + #[cfg(not(feature = "no_float"))] + { + reg_op!(lib, "<", lt, f32, f64); + reg_op!(lib, "<=", lte, f32, f64); + reg_op!(lib, ">", gt, f32, f64); + reg_op!(lib, ">=", gte, f32, f64); + reg_op!(lib, "==", eq, f32, f64); + reg_op!(lib, "!=", ne, f32, f64); } - fn init(lib: &mut PackageLibraryStore) { - reg_op!(lib, "<", lt, INT, char); - reg_op!(lib, "<=", lte, INT, char); - reg_op!(lib, ">", gt, INT, char); - reg_op!(lib, ">=", gte, INT, char); - reg_op!(lib, "==", eq, INT, char, bool, ()); - reg_op!(lib, "!=", ne, INT, char, bool, ()); + // `&&` and `||` are treated specially as they short-circuit. + // They are implemented as special `Expr` instances, not function calls. + //reg_op!(lib, "||", or, bool); + //reg_op!(lib, "&&", and, bool); - // Special versions for strings - at least avoid copying the first string - reg_binary_mut(lib, "<", |x: &mut String, y: String| *x < y, map); - reg_binary_mut(lib, "<=", |x: &mut String, y: String| *x <= y, map); - reg_binary_mut(lib, ">", |x: &mut String, y: String| *x > y, map); - reg_binary_mut(lib, ">=", |x: &mut String, y: String| *x >= y, map); - reg_binary_mut(lib, "==", |x: &mut String, y: String| *x == y, map); - reg_binary_mut(lib, "!=", |x: &mut String, y: String| *x != y, map); - - #[cfg(not(feature = "only_i32"))] - #[cfg(not(feature = "only_i64"))] - { - reg_op!(lib, "<", lt, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); - reg_op!(lib, "<=", lte, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); - reg_op!(lib, ">", gt, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); - reg_op!(lib, ">=", gte, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); - reg_op!(lib, "==", eq, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); - reg_op!(lib, "!=", ne, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); - } - - #[cfg(not(feature = "no_float"))] - { - reg_op!(lib, "<", lt, f32, f64); - reg_op!(lib, "<=", lte, f32, f64); - reg_op!(lib, ">", gt, f32, f64); - reg_op!(lib, ">=", gte, f32, f64); - reg_op!(lib, "==", eq, f32, f64); - reg_op!(lib, "!=", ne, f32, f64); - } - - // `&&` and `||` are treated specially as they short-circuit. - // They are implemented as special `Expr` instances, not function calls. - //reg_op!(lib, "||", or, bool); - //reg_op!(lib, "&&", and, bool); - - reg_binary(lib, "|", or, map); - reg_binary(lib, "&", and, map); - reg_unary(lib, "!", not, map); - } -} + reg_binary(lib, "|", or, map); + reg_binary(lib, "&", and, map); + reg_unary(lib, "!", not, map); +}); diff --git a/src/packages/map_basic.rs b/src/packages/map_basic.rs index 067da61f..b1fb3706 100644 --- a/src/packages/map_basic.rs +++ b/src/packages/map_basic.rs @@ -1,75 +1,62 @@ -use super::{ - create_new_package, reg_binary, reg_binary_mut, reg_unary_mut, Package, PackageLibrary, - PackageLibraryStore, -}; +use super::{reg_binary, reg_binary_mut, reg_unary_mut}; +use crate::def_package; use crate::any::Dynamic; use crate::engine::Map; use crate::fn_register::map_dynamic as map; use crate::parser::INT; -use crate::stdlib::ops::Deref; - -pub struct BasicMapPackage(PackageLibrary); - -impl Deref for BasicMapPackage { - type Target = PackageLibrary; - - fn deref(&self) -> &PackageLibrary { - &self.0 - } +fn map_get_keys(map: &mut Map) -> Vec { + map.iter() + .map(|(k, _)| Dynamic::from_string(k.to_string())) + .collect::>() +} +fn map_get_values(map: &mut Map) -> Vec { + map.iter().map(|(_, v)| v.clone()).collect::>() } -impl Package for BasicMapPackage { - fn new() -> Self { - let mut pkg = create_new_package(); - Self::init(&mut pkg); - Self(pkg.into()) - } +#[cfg(not(feature = "no_object"))] +def_package!(BasicMapPackage:"Basic object map utilities.", lib, { + reg_binary_mut( + lib, + "has", + |map: &mut Map, prop: String| map.contains_key(&prop), + map, + ); + reg_unary_mut(lib, "len", |map: &mut Map| map.len() as INT, map); + reg_unary_mut(lib, "clear", |map: &mut Map| map.clear(), map); + reg_binary_mut( + lib, + "remove", + |x: &mut Map, name: String| x.remove(&name).unwrap_or_else(|| Dynamic::from_unit()), + map, + ); + reg_binary_mut( + lib, + "mixin", + |map1: &mut Map, map2: Map| { + map2.into_iter().for_each(|(key, value)| { + map1.insert(key, value); + }); + }, + map, + ); + reg_binary( + lib, + "+", + |mut map1: Map, map2: Map| { + map2.into_iter().for_each(|(key, value)| { + map1.insert(key, value); + }); + map1 + }, + map, + ); - fn get(&self) -> PackageLibrary { - self.0.clone() - } + // Register map access functions + #[cfg(not(feature = "no_index"))] + reg_unary_mut(lib, "keys", map_get_keys, map); - fn init(lib: &mut PackageLibraryStore) { - // Register map functions - #[cfg(not(feature = "no_object"))] - { - reg_binary_mut( - lib, - "has", - |map: &mut Map, prop: String| map.contains_key(&prop), - map, - ); - reg_unary_mut(lib, "len", |map: &mut Map| map.len() as INT, map); - reg_unary_mut(lib, "clear", |map: &mut Map| map.clear(), map); - reg_binary_mut( - lib, - "remove", - |x: &mut Map, name: String| x.remove(&name).unwrap_or_else(|| Dynamic::from_unit()), - map, - ); - reg_binary_mut( - lib, - "mixin", - |map1: &mut Map, map2: Map| { - map2.into_iter().for_each(|(key, value)| { - map1.insert(key, value); - }); - }, - map, - ); - reg_binary( - lib, - "+", - |mut map1: Map, map2: Map| { - map2.into_iter().for_each(|(key, value)| { - map1.insert(key, value); - }); - map1 - }, - map, - ); - } - } -} + #[cfg(not(feature = "no_index"))] + reg_unary_mut(lib, "values", map_get_values, map); +}); diff --git a/src/packages/math_basic.rs b/src/packages/math_basic.rs index ce96c806..eee708c9 100644 --- a/src/packages/math_basic.rs +++ b/src/packages/math_basic.rs @@ -1,7 +1,6 @@ -use super::{ - create_new_package, reg_binary, reg_unary, Package, PackageLibrary, PackageLibraryStore, -}; +use super::{reg_binary, reg_unary}; +use crate::def_package; use crate::fn_register::{map_dynamic as map, map_result as result}; use crate::parser::INT; use crate::result::EvalAltResult; @@ -10,145 +9,123 @@ use crate::token::Position; #[cfg(not(feature = "no_float"))] use crate::parser::FLOAT; -use crate::stdlib::{i32, i64, ops::Deref}; +use crate::stdlib::{i32, i64}; #[cfg(feature = "only_i32")] pub const MAX_INT: INT = i32::MAX; #[cfg(not(feature = "only_i32"))] pub const MAX_INT: INT = i64::MAX; -pub struct BasicMathPackage(PackageLibrary); +def_package!(BasicMathPackage:"Basic mathematic functions.", lib, { + #[cfg(not(feature = "no_float"))] + { + // Advanced math functions + reg_unary(lib, "sin", |x: FLOAT| x.to_radians().sin(), map); + reg_unary(lib, "cos", |x: FLOAT| x.to_radians().cos(), map); + reg_unary(lib, "tan", |x: FLOAT| x.to_radians().tan(), map); + reg_unary(lib, "sinh", |x: FLOAT| x.to_radians().sinh(), map); + reg_unary(lib, "cosh", |x: FLOAT| x.to_radians().cosh(), map); + reg_unary(lib, "tanh", |x: FLOAT| x.to_radians().tanh(), map); + reg_unary(lib, "asin", |x: FLOAT| x.asin().to_degrees(), map); + reg_unary(lib, "acos", |x: FLOAT| x.acos().to_degrees(), map); + reg_unary(lib, "atan", |x: FLOAT| x.atan().to_degrees(), map); + reg_unary(lib, "asinh", |x: FLOAT| x.asinh().to_degrees(), map); + reg_unary(lib, "acosh", |x: FLOAT| x.acosh().to_degrees(), map); + reg_unary(lib, "atanh", |x: FLOAT| x.atanh().to_degrees(), map); + reg_unary(lib, "sqrt", |x: FLOAT| x.sqrt(), map); + reg_unary(lib, "exp", |x: FLOAT| x.exp(), map); + reg_unary(lib, "ln", |x: FLOAT| x.ln(), map); + reg_binary(lib, "log", |x: FLOAT, base: FLOAT| x.log(base), map); + reg_unary(lib, "log10", |x: FLOAT| x.log10(), map); + reg_unary(lib, "floor", |x: FLOAT| x.floor(), map); + reg_unary(lib, "ceiling", |x: FLOAT| x.ceil(), map); + reg_unary(lib, "round", |x: FLOAT| x.ceil(), map); + reg_unary(lib, "int", |x: FLOAT| x.trunc(), map); + reg_unary(lib, "fraction", |x: FLOAT| x.fract(), map); + reg_unary(lib, "is_nan", |x: FLOAT| x.is_nan(), map); + reg_unary(lib, "is_finite", |x: FLOAT| x.is_finite(), map); + reg_unary(lib, "is_infinite", |x: FLOAT| x.is_infinite(), map); -impl Deref for BasicMathPackage { - type Target = PackageLibrary; - - fn deref(&self) -> &PackageLibrary { - &self.0 - } -} - -impl Package for BasicMathPackage { - fn new() -> Self { - let mut pkg = create_new_package(); - Self::init(&mut pkg); - Self(pkg.into()) - } - - fn get(&self) -> PackageLibrary { - self.0.clone() - } - - fn init(lib: &mut PackageLibraryStore) { - #[cfg(not(feature = "no_float"))] - { - // Advanced math functions - reg_unary(lib, "sin", |x: FLOAT| x.to_radians().sin(), map); - reg_unary(lib, "cos", |x: FLOAT| x.to_radians().cos(), map); - reg_unary(lib, "tan", |x: FLOAT| x.to_radians().tan(), map); - reg_unary(lib, "sinh", |x: FLOAT| x.to_radians().sinh(), map); - reg_unary(lib, "cosh", |x: FLOAT| x.to_radians().cosh(), map); - reg_unary(lib, "tanh", |x: FLOAT| x.to_radians().tanh(), map); - reg_unary(lib, "asin", |x: FLOAT| x.asin().to_degrees(), map); - reg_unary(lib, "acos", |x: FLOAT| x.acos().to_degrees(), map); - reg_unary(lib, "atan", |x: FLOAT| x.atan().to_degrees(), map); - reg_unary(lib, "asinh", |x: FLOAT| x.asinh().to_degrees(), map); - reg_unary(lib, "acosh", |x: FLOAT| x.acosh().to_degrees(), map); - reg_unary(lib, "atanh", |x: FLOAT| x.atanh().to_degrees(), map); - reg_unary(lib, "sqrt", |x: FLOAT| x.sqrt(), map); - reg_unary(lib, "exp", |x: FLOAT| x.exp(), map); - reg_unary(lib, "ln", |x: FLOAT| x.ln(), map); - reg_binary(lib, "log", |x: FLOAT, base: FLOAT| x.log(base), map); - reg_unary(lib, "log10", |x: FLOAT| x.log10(), map); - reg_unary(lib, "floor", |x: FLOAT| x.floor(), map); - reg_unary(lib, "ceiling", |x: FLOAT| x.ceil(), map); - reg_unary(lib, "round", |x: FLOAT| x.ceil(), map); - reg_unary(lib, "int", |x: FLOAT| x.trunc(), map); - reg_unary(lib, "fraction", |x: FLOAT| x.fract(), map); - reg_unary(lib, "is_nan", |x: FLOAT| x.is_nan(), map); - reg_unary(lib, "is_finite", |x: FLOAT| x.is_finite(), map); - reg_unary(lib, "is_infinite", |x: FLOAT| x.is_infinite(), map); - - // Register conversion functions - reg_unary(lib, "to_float", |x: INT| x as FLOAT, map); - reg_unary(lib, "to_float", |x: f32| x as FLOAT, map); - - #[cfg(not(feature = "only_i32"))] - #[cfg(not(feature = "only_i64"))] - { - reg_unary(lib, "to_float", |x: i8| x as FLOAT, map); - reg_unary(lib, "to_float", |x: u8| x as FLOAT, map); - reg_unary(lib, "to_float", |x: i16| x as FLOAT, map); - reg_unary(lib, "to_float", |x: u16| x as FLOAT, map); - reg_unary(lib, "to_float", |x: i32| x as FLOAT, map); - reg_unary(lib, "to_float", |x: u32| x as FLOAT, map); - reg_unary(lib, "to_float", |x: i64| x as FLOAT, map); - reg_unary(lib, "to_float", |x: u64| x as FLOAT, map); - reg_unary(lib, "to_float", |x: i128| x as FLOAT, map); - reg_unary(lib, "to_float", |x: u128| x as FLOAT, map); - } - } - - reg_unary(lib, "to_int", |ch: char| ch as INT, map); + // Register conversion functions + reg_unary(lib, "to_float", |x: INT| x as FLOAT, map); + reg_unary(lib, "to_float", |x: f32| x as FLOAT, map); #[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i64"))] { - reg_unary(lib, "to_int", |x: i8| x as INT, map); - reg_unary(lib, "to_int", |x: u8| x as INT, map); - reg_unary(lib, "to_int", |x: i16| x as INT, map); - reg_unary(lib, "to_int", |x: u16| x as INT, map); - } - - #[cfg(not(feature = "only_i32"))] - { - reg_unary(lib, "to_int", |x: i32| x as INT, map); - reg_unary(lib, "to_int", |x: u64| x as INT, map); - - #[cfg(feature = "only_i64")] - reg_unary(lib, "to_int", |x: u32| x as INT, map); - } - - #[cfg(not(feature = "no_float"))] - { - #[cfg(not(feature = "unchecked"))] - { - reg_unary( - lib, - "to_int", - |x: f32| { - if x > (MAX_INT as f32) { - return Err(EvalAltResult::ErrorArithmetic( - format!("Integer overflow: to_int({})", x), - Position::none(), - )); - } - - Ok(x.trunc() as INT) - }, - result, - ); - reg_unary( - lib, - "to_int", - |x: FLOAT| { - if x > (MAX_INT as FLOAT) { - return Err(EvalAltResult::ErrorArithmetic( - format!("Integer overflow: to_int({})", x), - Position::none(), - )); - } - - Ok(x.trunc() as INT) - }, - result, - ); - } - - #[cfg(feature = "unchecked")] - { - reg_unary(lib, "to_int", |x: f32| x as INT, map); - reg_unary(lib, "to_int", |x: f64| x as INT, map); - } + reg_unary(lib, "to_float", |x: i8| x as FLOAT, map); + reg_unary(lib, "to_float", |x: u8| x as FLOAT, map); + reg_unary(lib, "to_float", |x: i16| x as FLOAT, map); + reg_unary(lib, "to_float", |x: u16| x as FLOAT, map); + reg_unary(lib, "to_float", |x: i32| x as FLOAT, map); + reg_unary(lib, "to_float", |x: u32| x as FLOAT, map); + reg_unary(lib, "to_float", |x: i64| x as FLOAT, map); + reg_unary(lib, "to_float", |x: u64| x as FLOAT, map); + reg_unary(lib, "to_float", |x: i128| x as FLOAT, map); + reg_unary(lib, "to_float", |x: u128| x as FLOAT, map); } } -} + + reg_unary(lib, "to_int", |ch: char| ch as INT, map); + + #[cfg(not(feature = "only_i32"))] + #[cfg(not(feature = "only_i64"))] + { + reg_unary(lib, "to_int", |x: i8| x as INT, map); + reg_unary(lib, "to_int", |x: u8| x as INT, map); + reg_unary(lib, "to_int", |x: i16| x as INT, map); + reg_unary(lib, "to_int", |x: u16| x as INT, map); + } + + #[cfg(not(feature = "only_i32"))] + { + reg_unary(lib, "to_int", |x: i32| x as INT, map); + reg_unary(lib, "to_int", |x: u64| x as INT, map); + + #[cfg(feature = "only_i64")] + reg_unary(lib, "to_int", |x: u32| x as INT, map); + } + + #[cfg(not(feature = "no_float"))] + { + #[cfg(not(feature = "unchecked"))] + { + reg_unary( + lib, + "to_int", + |x: f32| { + if x > (MAX_INT as f32) { + return Err(EvalAltResult::ErrorArithmetic( + format!("Integer overflow: to_int({})", x), + Position::none(), + )); + } + + Ok(x.trunc() as INT) + }, + result, + ); + reg_unary( + lib, + "to_int", + |x: FLOAT| { + if x > (MAX_INT as FLOAT) { + return Err(EvalAltResult::ErrorArithmetic( + format!("Integer overflow: to_int({})", x), + Position::none(), + )); + } + + Ok(x.trunc() as INT) + }, + result, + ); + } + + #[cfg(feature = "unchecked")] + { + reg_unary(lib, "to_int", |x: f32| x as INT, map); + reg_unary(lib, "to_int", |x: f64| x as INT, map); + } + } +}); diff --git a/src/packages/mod.rs b/src/packages/mod.rs index ea5ffad1..25318d23 100644 --- a/src/packages/mod.rs +++ b/src/packages/mod.rs @@ -1,16 +1,8 @@ -use crate::any::{Dynamic, Variant}; -use crate::engine::{calc_fn_spec, FnAny, FnCallArgs, IteratorFn}; -use crate::result::EvalAltResult; -use crate::token::Position; +//! This module contains all built-in _packages_ available to Rhai, plus facilities to define custom packages. -use crate::stdlib::{ - any::{type_name, TypeId}, - boxed::Box, - collections::HashMap, - ops::Deref, - rc::Rc, - sync::Arc, -}; +use crate::engine::{FnAny, IteratorFn}; + +use crate::stdlib::{any::TypeId, boxed::Box, collections::HashMap, rc::Rc, sync::Arc}; mod arithmetic; mod array_basic; @@ -23,6 +15,7 @@ mod pkg_std; mod string_basic; mod string_more; mod time_basic; +mod utils; pub use arithmetic::ArithmeticPackage; pub use array_basic::BasicArrayPackage; @@ -36,272 +29,43 @@ pub use string_basic::BasicStringPackage; pub use string_more::MoreStringPackage; pub use time_basic::BasicTimePackage; -pub trait Package: Deref { +pub use utils::*; + +/// Trait that all packages must implement. +pub trait Package { + /// Create a new instance of a package. fn new() -> Self; - fn init(lib: &mut PackageLibraryStore); + + /// Register all the functions in a package into a store. + fn init(lib: &mut PackageStore); + + /// Retrieve the generic package library from this package. fn get(&self) -> PackageLibrary; } -pub type PackageLibraryStore = (HashMap>, HashMap>); +/// Type to store all functions in the package. +pub struct PackageStore { + /// All functions, keyed by a hash created from the function name and parameter types. + pub functions: HashMap>, -#[cfg(not(feature = "sync"))] -pub type PackageLibrary = Rc; + /// All iterator functions, keyed by the type producing the iterator. + pub type_iterators: HashMap>, +} -#[cfg(feature = "sync")] -pub type PackageLibrary = Arc; - -fn check_num_args( - name: &str, - num_args: usize, - args: &mut FnCallArgs, - pos: Position, -) -> Result<(), Box> { - if args.len() != num_args { - Err(Box::new(EvalAltResult::ErrorFunctionArgsMismatch( - name.to_string(), - num_args, - args.len(), - pos, - ))) - } else { - Ok(()) +impl PackageStore { + /// Create a new `PackageStore`. + pub fn new() -> Self { + Self { + functions: HashMap::new(), + type_iterators: HashMap::new(), + } } } -fn create_new_package() -> PackageLibraryStore { - (HashMap::new(), HashMap::new()) -} +/// Type which `Rc`-wraps a `PackageStore` to facilitate sharing library instances. +#[cfg(not(feature = "sync"))] +pub type PackageLibrary = Rc; -fn reg_none( - lib: &mut PackageLibraryStore, - fn_name: &'static str, - - #[cfg(not(feature = "sync"))] func: impl Fn() -> R + 'static, - #[cfg(feature = "sync")] func: impl Fn() -> R + Send + Sync + 'static, - - #[cfg(not(feature = "sync"))] map_result: impl Fn(R, Position) -> Result> - + 'static, - #[cfg(feature = "sync")] map_result: impl Fn(R, Position) -> Result> - + Send - + Sync - + 'static, -) { - let hash = calc_fn_spec(fn_name, ([] as [TypeId; 0]).iter().cloned()); - - let f = Box::new(move |args: &mut FnCallArgs, pos: Position| { - check_num_args(fn_name, 0, args, pos)?; - - let r = func(); - map_result(r, pos) - }); - - lib.0.insert(hash, f); -} - -fn reg_unary( - lib: &mut PackageLibraryStore, - fn_name: &'static str, - - #[cfg(not(feature = "sync"))] func: impl Fn(T) -> R + 'static, - #[cfg(feature = "sync")] func: impl Fn(T) -> R + Send + Sync + 'static, - - #[cfg(not(feature = "sync"))] map_result: impl Fn(R, Position) -> Result> - + 'static, - #[cfg(feature = "sync")] map_result: impl Fn(R, Position) -> Result> - + Send - + Sync - + 'static, -) { - //println!("register {}({})", fn_name, type_name::()); - - let hash = calc_fn_spec(fn_name, [TypeId::of::()].iter().cloned()); - - let f = Box::new(move |args: &mut FnCallArgs, pos: Position| { - check_num_args(fn_name, 1, args, pos)?; - - let mut drain = args.iter_mut(); - let x: &mut T = drain.next().unwrap().downcast_mut().unwrap(); - - let r = func(x.clone()); - map_result(r, pos) - }); - - lib.0.insert(hash, f); -} - -fn reg_unary_mut( - lib: &mut PackageLibraryStore, - fn_name: &'static str, - - #[cfg(not(feature = "sync"))] func: impl Fn(&mut T) -> R + 'static, - #[cfg(feature = "sync")] func: impl Fn(&mut T) -> R + Send + Sync + 'static, - - #[cfg(not(feature = "sync"))] map_result: impl Fn(R, Position) -> Result> - + 'static, - #[cfg(feature = "sync")] map_result: impl Fn(R, Position) -> Result> - + Send - + Sync - + 'static, -) { - //println!("register {}(&mut {})", fn_name, type_name::()); - - let hash = calc_fn_spec(fn_name, [TypeId::of::()].iter().cloned()); - - let f = Box::new(move |args: &mut FnCallArgs, pos: Position| { - check_num_args(fn_name, 1, args, pos)?; - - let mut drain = args.iter_mut(); - let x: &mut T = drain.next().unwrap().downcast_mut().unwrap(); - - let r = func(x); - map_result(r, pos) - }); - - lib.0.insert(hash, f); -} - -fn reg_binary( - lib: &mut PackageLibraryStore, - fn_name: &'static str, - - #[cfg(not(feature = "sync"))] func: impl Fn(A, B) -> R + 'static, - #[cfg(feature = "sync")] func: impl Fn(A, B) -> R + Send + Sync + 'static, - - #[cfg(not(feature = "sync"))] map_result: impl Fn(R, Position) -> Result> - + 'static, - #[cfg(feature = "sync")] map_result: impl Fn(R, Position) -> Result> - + Send - + Sync - + 'static, -) { - //println!("register {}({}, {})", fn_name, type_name::(), type_name::()); - - let hash = calc_fn_spec( - fn_name, - [TypeId::of::(), TypeId::of::()].iter().cloned(), - ); - - let f = Box::new(move |args: &mut FnCallArgs, pos: Position| { - check_num_args(fn_name, 2, args, pos)?; - - let mut drain = args.iter_mut(); - let x: &mut A = drain.next().unwrap().downcast_mut().unwrap(); - let y: &mut B = drain.next().unwrap().downcast_mut().unwrap(); - - let r = func(x.clone(), y.clone()); - map_result(r, pos) - }); - - lib.0.insert(hash, f); -} - -fn reg_binary_mut( - lib: &mut PackageLibraryStore, - fn_name: &'static str, - - #[cfg(not(feature = "sync"))] func: impl Fn(&mut A, B) -> R + 'static, - #[cfg(feature = "sync")] func: impl Fn(&mut A, B) -> R + Send + Sync + 'static, - - #[cfg(not(feature = "sync"))] map_result: impl Fn(R, Position) -> Result> - + 'static, - #[cfg(feature = "sync")] map_result: impl Fn(R, Position) -> Result> - + Send - + Sync - + 'static, -) { - //println!("register {}(&mut {}, {})", fn_name, type_name::(), type_name::()); - - let hash = calc_fn_spec( - fn_name, - [TypeId::of::(), TypeId::of::()].iter().cloned(), - ); - - let f = Box::new(move |args: &mut FnCallArgs, pos: Position| { - check_num_args(fn_name, 2, args, pos)?; - - let mut drain = args.iter_mut(); - let x: &mut A = drain.next().unwrap().downcast_mut().unwrap(); - let y: &mut B = drain.next().unwrap().downcast_mut().unwrap(); - - let r = func(x, y.clone()); - map_result(r, pos) - }); - - lib.0.insert(hash, f); -} - -fn reg_trinary( - lib: &mut PackageLibraryStore, - fn_name: &'static str, - - #[cfg(not(feature = "sync"))] func: impl Fn(A, B, C) -> R + 'static, - #[cfg(feature = "sync")] func: impl Fn(A, B, C) -> R + Send + Sync + 'static, - - #[cfg(not(feature = "sync"))] map_result: impl Fn(R, Position) -> Result> - + 'static, - #[cfg(feature = "sync")] map_result: impl Fn(R, Position) -> Result> - + Send - + Sync - + 'static, -) { - //println!("register {}({}, {}, {})", fn_name, type_name::(), type_name::(), type_name::()); - - let hash = calc_fn_spec( - fn_name, - [TypeId::of::(), TypeId::of::(), TypeId::of::()] - .iter() - .cloned(), - ); - - let f = Box::new(move |args: &mut FnCallArgs, pos: Position| { - check_num_args(fn_name, 3, args, pos)?; - - let mut drain = args.iter_mut(); - let x: &mut A = drain.next().unwrap().downcast_mut().unwrap(); - let y: &mut B = drain.next().unwrap().downcast_mut().unwrap(); - let z: &mut C = drain.next().unwrap().downcast_mut().unwrap(); - - let r = func(x.clone(), y.clone(), z.clone()); - map_result(r, pos) - }); - - lib.0.insert(hash, f); -} - -fn reg_trinary_mut( - lib: &mut PackageLibraryStore, - fn_name: &'static str, - - #[cfg(not(feature = "sync"))] func: impl Fn(&mut A, B, C) -> R + 'static, - #[cfg(feature = "sync")] func: impl Fn(&mut A, B, C) -> R + Send + Sync + 'static, - - #[cfg(not(feature = "sync"))] map_result: impl Fn(R, Position) -> Result> - + 'static, - #[cfg(feature = "sync")] map_result: impl Fn(R, Position) -> Result> - + Send - + Sync - + 'static, -) { - //println!("register {}(&mut {}, {}, {})", fn_name, type_name::(), type_name::(), type_name::()); - - let hash = calc_fn_spec( - fn_name, - [TypeId::of::(), TypeId::of::(), TypeId::of::()] - .iter() - .cloned(), - ); - - let f = Box::new(move |args: &mut FnCallArgs, pos: Position| { - check_num_args(fn_name, 3, args, pos)?; - - let mut drain = args.iter_mut(); - let x: &mut A = drain.next().unwrap().downcast_mut().unwrap(); - let y: &mut B = drain.next().unwrap().downcast_mut().unwrap(); - let z: &mut C = drain.next().unwrap().downcast_mut().unwrap(); - - let r = func(x, y.clone(), z.clone()); - map_result(r, pos) - }); - - lib.0.insert(hash, f); -} +/// Type which `Arc`-wraps a `PackageStore` to facilitate sharing library instances. +#[cfg(feature = "sync")] +pub type PackageLibrary = Arc; diff --git a/src/packages/pkg_core.rs b/src/packages/pkg_core.rs index 19f3978e..1647cc88 100644 --- a/src/packages/pkg_core.rs +++ b/src/packages/pkg_core.rs @@ -1,37 +1,13 @@ use super::arithmetic::ArithmeticPackage; -use super::create_new_package; use super::iter_basic::BasicIteratorPackage; use super::logic::LogicPackage; use super::string_basic::BasicStringPackage; -use super::{Package, PackageLibrary, PackageLibraryStore}; -use crate::stdlib::ops::Deref; +use crate::def_package; -pub struct CorePackage(PackageLibrary); - -impl Deref for CorePackage { - type Target = PackageLibrary; - - fn deref(&self) -> &PackageLibrary { - &self.0 - } -} - -impl Package for CorePackage { - fn new() -> Self { - let mut pkg = create_new_package(); - Self::init(&mut pkg); - Self(pkg.into()) - } - - fn init(lib: &mut PackageLibraryStore) { - ArithmeticPackage::init(lib); - LogicPackage::init(lib); - BasicStringPackage::init(lib); - BasicIteratorPackage::init(lib); - } - - fn get(&self) -> PackageLibrary { - self.0.clone() - } -} +def_package!(CorePackage:"_Core_ package containing basic facilities.", lib, { + ArithmeticPackage::init(lib); + LogicPackage::init(lib); + BasicStringPackage::init(lib); + BasicIteratorPackage::init(lib); +}); diff --git a/src/packages/pkg_std.rs b/src/packages/pkg_std.rs index f002c60f..f17c275e 100644 --- a/src/packages/pkg_std.rs +++ b/src/packages/pkg_std.rs @@ -4,37 +4,16 @@ use super::math_basic::BasicMathPackage; use super::pkg_core::CorePackage; use super::string_more::MoreStringPackage; use super::time_basic::BasicTimePackage; -use super::{create_new_package, Package, PackageLibrary, PackageLibraryStore}; -use crate::stdlib::ops::Deref; +use crate::def_package; -pub struct StandardPackage(PackageLibrary); - -impl Deref for StandardPackage { - type Target = PackageLibrary; - - fn deref(&self) -> &PackageLibrary { - &self.0 - } -} - -impl Package for StandardPackage { - fn new() -> Self { - let mut pkg = create_new_package(); - Self::init(&mut pkg); - Self(pkg.into()) - } - - fn init(lib: &mut PackageLibraryStore) { - CorePackage::init(lib); - BasicMathPackage::init(lib); - BasicArrayPackage::init(lib); - BasicMapPackage::init(lib); - BasicTimePackage::init(lib); - MoreStringPackage::init(lib); - } - - fn get(&self) -> PackageLibrary { - self.0.clone() - } -} +def_package!(StandardPackage:"_Standard_ package containing all built-in features.", lib, { + CorePackage::init(lib); + BasicMathPackage::init(lib); + #[cfg(not(feature = "no_index"))] + BasicArrayPackage::init(lib); + #[cfg(not(feature = "no_object"))] + BasicMapPackage::init(lib); + BasicTimePackage::init(lib); + MoreStringPackage::init(lib); +}); diff --git a/src/packages/string_basic.rs b/src/packages/string_basic.rs index 20c30493..9c88a18f 100644 --- a/src/packages/string_basic.rs +++ b/src/packages/string_basic.rs @@ -1,8 +1,6 @@ -use super::{ - create_new_package, reg_binary, reg_binary_mut, reg_none, reg_unary, reg_unary_mut, Package, - PackageLibrary, PackageLibraryStore, -}; +use super::{reg_binary, reg_binary_mut, reg_none, reg_unary, reg_unary_mut}; +use crate::def_package; use crate::engine::{Array, Map, FUNC_TO_STRING, KEYWORD_DEBUG, KEYWORD_PRINT}; use crate::fn_register::map_dynamic as map; use crate::parser::INT; @@ -10,7 +8,6 @@ use crate::parser::INT; use crate::stdlib::{ fmt::{Debug, Display}, format, - ops::Deref, }; // Register print and debug @@ -28,97 +25,74 @@ macro_rules! reg_op { ($lib:expr, $op:expr, $func:ident, $($par:ty),*) => { $(reg_unary_mut($lib, $op, $func::<$par>, map);)* }; } -pub struct BasicStringPackage(PackageLibrary); +def_package!(BasicStringPackage:"Basic string utilities, including printing.", lib, { + reg_op!(lib, KEYWORD_PRINT, to_string, INT, bool, char); + reg_op!(lib, FUNC_TO_STRING, to_string, INT, bool, char); -impl Deref for BasicStringPackage { - type Target = PackageLibrary; + reg_none(lib, KEYWORD_PRINT, || "".to_string(), map); + reg_unary(lib, KEYWORD_PRINT, |_: ()| "".to_string(), map); + reg_unary(lib, FUNC_TO_STRING, |_: ()| "".to_string(), map); - fn deref(&self) -> &PackageLibrary { - &self.0 - } -} + reg_unary_mut(lib, KEYWORD_PRINT, |s: &mut String| s.clone(), map); + reg_unary_mut(lib, FUNC_TO_STRING, |s: &mut String| s.clone(), map); -impl Package for BasicStringPackage { - fn new() -> Self { - let mut pkg = create_new_package(); - Self::init(&mut pkg); - Self(pkg.into()) + reg_op!(lib, KEYWORD_DEBUG, to_debug, INT, bool, (), char, String); + + #[cfg(not(feature = "only_i32"))] + #[cfg(not(feature = "only_i64"))] + { + reg_op!(lib, KEYWORD_PRINT, to_string, i8, u8, i16, u16, i32, u32); + reg_op!(lib, FUNC_TO_STRING, to_string, i8, u8, i16, u16, i32, u32); + reg_op!(lib, KEYWORD_DEBUG, to_debug, i8, u8, i16, u16, i32, u32); + reg_op!(lib, KEYWORD_PRINT, to_string, i64, u64, i128, u128); + reg_op!(lib, FUNC_TO_STRING, to_string, i64, u64, i128, u128); + reg_op!(lib, KEYWORD_DEBUG, to_debug, i64, u64, i128, u128); } - fn get(&self) -> PackageLibrary { - self.0.clone() + #[cfg(not(feature = "no_float"))] + { + reg_op!(lib, KEYWORD_PRINT, to_string, f32, f64); + reg_op!(lib, FUNC_TO_STRING, to_string, f32, f64); + reg_op!(lib, KEYWORD_DEBUG, to_debug, f32, f64); } - fn init(lib: &mut PackageLibraryStore) { - reg_op!(lib, KEYWORD_PRINT, to_string, String, INT, bool); - reg_op!(lib, FUNC_TO_STRING, to_string, String, INT, bool); - reg_op!(lib, KEYWORD_PRINT, to_string, String, char, String); - reg_op!(lib, FUNC_TO_STRING, to_string, String, char, String); - reg_none(lib, KEYWORD_PRINT, || "".to_string(), map); - reg_unary(lib, KEYWORD_PRINT, |_: ()| "".to_string(), map); - reg_unary(lib, FUNC_TO_STRING, |_: ()| "".to_string(), map); - reg_op!(lib, KEYWORD_DEBUG, to_debug, String, INT, bool, ()); - reg_op!(lib, KEYWORD_DEBUG, to_debug, String, char, String); - - #[cfg(not(feature = "only_i32"))] - #[cfg(not(feature = "only_i64"))] - { - reg_op!(lib, KEYWORD_PRINT, to_string, String, i8, u8, i16, u16); - reg_op!(lib, FUNC_TO_STRING, to_string, String, i8, u8, i16, u16); - reg_op!(lib, KEYWORD_PRINT, to_string, String, i32, u32, i64, u64); - reg_op!(lib, FUNC_TO_STRING, to_string, String, i32, u32, i64, u64); - reg_op!(lib, KEYWORD_PRINT, to_string, String, i128, u128); - reg_op!(lib, FUNC_TO_STRING, to_string, String, i128, u128); - reg_op!(lib, KEYWORD_DEBUG, to_debug, String, i8, u8, i16, u16); - reg_op!(lib, KEYWORD_DEBUG, to_debug, String, i32, u32, i64, u64); - reg_op!(lib, KEYWORD_DEBUG, to_debug, String, i128, u128); - } - - #[cfg(not(feature = "no_float"))] - { - reg_op!(lib, KEYWORD_PRINT, to_string, String, f32, f64); - reg_op!(lib, FUNC_TO_STRING, to_string, String, f32, f64); - reg_op!(lib, KEYWORD_DEBUG, to_debug, String, f32, f64); - } - - #[cfg(not(feature = "no_index"))] - { - reg_op!(lib, KEYWORD_PRINT, to_debug, String, Array); - reg_op!(lib, FUNC_TO_STRING, to_debug, String, Array); - reg_op!(lib, KEYWORD_DEBUG, to_debug, String, Array); - } - - #[cfg(not(feature = "no_object"))] - { - reg_unary_mut(lib, KEYWORD_PRINT, format_map, map); - reg_unary_mut(lib, FUNC_TO_STRING, format_map, map); - reg_unary_mut(lib, KEYWORD_DEBUG, format_map, map); - } - - reg_binary( - lib, - "+", - |mut s: String, ch: char| { - s.push(ch); - s - }, - map, - ); - reg_binary( - lib, - "+", - |mut s: String, s2: String| { - s.push_str(&s2); - s - }, - map, - ); - reg_binary_mut(lib, "append", |s: &mut String, ch: char| s.push(ch), map); - reg_binary_mut( - lib, - "append", - |s: &mut String, add: String| s.push_str(&add), - map, - ); + #[cfg(not(feature = "no_index"))] + { + reg_op!(lib, KEYWORD_PRINT, to_debug, Array); + reg_op!(lib, FUNC_TO_STRING, to_debug, Array); + reg_op!(lib, KEYWORD_DEBUG, to_debug, Array); } -} + + #[cfg(not(feature = "no_object"))] + { + reg_unary_mut(lib, KEYWORD_PRINT, format_map, map); + reg_unary_mut(lib, FUNC_TO_STRING, format_map, map); + reg_unary_mut(lib, KEYWORD_DEBUG, format_map, map); + } + + reg_binary( + lib, + "+", + |mut s: String, ch: char| { + s.push(ch); + s + }, + map, + ); + reg_binary( + lib, + "+", + |mut s: String, s2: String| { + s.push_str(&s2); + s + }, + map, + ); + reg_binary_mut(lib, "append", |s: &mut String, ch: char| s.push(ch), map); + reg_binary_mut( + lib, + "append", + |s: &mut String, s2: String| s.push_str(&s2), + map, + ); +}); diff --git a/src/packages/string_more.rs b/src/packages/string_more.rs index 15d9bdac..c2a31785 100644 --- a/src/packages/string_more.rs +++ b/src/packages/string_more.rs @@ -1,15 +1,12 @@ -use super::{ - create_new_package, reg_binary, reg_binary_mut, reg_trinary_mut, reg_unary, reg_unary_mut, - Package, PackageLibrary, PackageLibraryStore, -}; +use super::{reg_binary, reg_binary_mut, reg_trinary_mut, reg_unary_mut}; +use crate::def_package; use crate::engine::Array; use crate::fn_register::map_dynamic as map; use crate::parser::INT; -use crate::stdlib::{fmt::Display, ops::Deref}; +use crate::stdlib::fmt::Display; -// Register string concatenate functions fn prepend(x: T, y: String) -> String { format!("{}{}", x, y) } @@ -65,195 +62,173 @@ fn crop_string(s: &mut String, start: INT, len: INT) { .for_each(|&ch| s.push(ch)); } -pub struct MoreStringPackage(PackageLibrary); - -impl Deref for MoreStringPackage { - type Target = PackageLibrary; - - fn deref(&self) -> &PackageLibrary { - &self.0 - } -} - macro_rules! reg_op { ($lib:expr, $op:expr, $func:ident, $($par:ty),*) => { $(reg_binary($lib, $op, $func::<$par>, map);)* }; } -impl Package for MoreStringPackage { - fn new() -> Self { - let mut pkg = create_new_package(); - Self::init(&mut pkg); - Self(pkg.into()) +def_package!(MoreStringPackage:"Additional string utilities, including string building.", lib, { + reg_op!(lib, "+", append, INT, bool, char); + reg_binary_mut(lib, "+", |x: &mut String, _: ()| x.clone(), map); + + reg_op!(lib, "+", prepend, INT, bool, char); + reg_binary(lib, "+", |_: (), y: String| y, map); + + #[cfg(not(feature = "only_i32"))] + #[cfg(not(feature = "only_i64"))] + { + reg_op!(lib, "+", append, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); + reg_op!(lib, "+", prepend, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); } - fn get(&self) -> PackageLibrary { - self.0.clone() + #[cfg(not(feature = "no_float"))] + { + reg_op!(lib, "+", append, f32, f64); + reg_op!(lib, "+", prepend, f32, f64); } - fn init(lib: &mut PackageLibraryStore) { - reg_op!(lib, "+", append, INT, bool, char); - reg_binary_mut(lib, "+", |x: &mut String, _: ()| x.clone(), map); + #[cfg(not(feature = "no_index"))] + { + reg_binary(lib, "+", |x: String, y: Array| format!("{}{:?}", x, y), map); + reg_binary(lib, "+", |x: Array, y: String| format!("{:?}{}", x, y), map); + } - reg_op!(lib, "+", prepend, INT, bool, char); - reg_binary(lib, "+", |_: (), y: String| y, map); + reg_unary_mut(lib, "len", |s: &mut String| s.chars().count() as INT, map); + reg_binary_mut( + lib, + "contains", + |s: &mut String, ch: char| s.contains(ch), + map, + ); + reg_binary_mut( + lib, + "contains", + |s: &mut String, find: String| s.contains(&find), + map, + ); + reg_trinary_mut( + lib, + "index_of", + |s: &mut String, ch: char, start: INT| { + let start = if start < 0 { + 0 + } else if (start as usize) >= s.chars().count() { + return -1 as INT; + } else { + s.chars().take(start as usize).collect::().len() + }; - #[cfg(not(feature = "only_i32"))] - #[cfg(not(feature = "only_i64"))] - { - reg_op!(lib, "+", append, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); - reg_op!(lib, "+", prepend, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); - } + s[start..] + .find(ch) + .map(|index| s[0..start + index].chars().count() as INT) + .unwrap_or(-1 as INT) + }, + map, + ); + reg_binary_mut( + lib, + "index_of", + |s: &mut String, ch: char| { + s.find(ch) + .map(|index| s[0..index].chars().count() as INT) + .unwrap_or(-1 as INT) + }, + map, + ); + reg_trinary_mut( + lib, + "index_of", + |s: &mut String, find: String, start: INT| { + let start = if start < 0 { + 0 + } else if (start as usize) >= s.chars().count() { + return -1 as INT; + } else { + s.chars().take(start as usize).collect::().len() + }; - #[cfg(not(feature = "no_float"))] - { - reg_op!(lib, "+", append, f32, f64); - reg_op!(lib, "+", prepend, f32, f64); - } - - #[cfg(not(feature = "no_index"))] - { - reg_binary(lib, "+", |x: String, y: Array| format!("{}{:?}", x, y), map); - reg_binary(lib, "+", |x: Array, y: String| format!("{:?}{}", x, y), map); - } - - reg_unary_mut(lib, "len", |s: &mut String| s.chars().count() as INT, map); - reg_binary_mut( - lib, - "contains", - |s: &mut String, ch: char| s.contains(ch), - map, - ); - reg_binary_mut( - lib, - "contains", - |s: &mut String, find: String| s.contains(&find), - map, - ); - reg_trinary_mut( - lib, - "index_of", - |s: &mut String, ch: char, start: INT| { - let start = if start < 0 { - 0 - } else if (start as usize) >= s.chars().count() { - return -1 as INT; - } else { - s.chars().take(start as usize).collect::().len() - }; - - s[start..] - .find(ch) - .map(|index| s[0..start + index].chars().count() as INT) - .unwrap_or(-1 as INT) - }, - map, - ); - reg_binary_mut( - lib, - "index_of", - |s: &mut String, ch: char| { - s.find(ch) - .map(|index| s[0..index].chars().count() as INT) - .unwrap_or(-1 as INT) - }, - map, - ); - reg_trinary_mut( - lib, - "index_of", - |s: &mut String, find: String, start: INT| { - let start = if start < 0 { - 0 - } else if (start as usize) >= s.chars().count() { - return -1 as INT; - } else { - s.chars().take(start as usize).collect::().len() - }; - - s[start..] - .find(&find) - .map(|index| s[0..start + index].chars().count() as INT) - .unwrap_or(-1 as INT) - }, - map, - ); - reg_binary_mut( - lib, - "index_of", - |s: &mut String, find: String| { - s.find(&find) - .map(|index| s[0..index].chars().count() as INT) - .unwrap_or(-1 as INT) - }, - map, - ); - reg_unary_mut(lib, "clear", |s: &mut String| s.clear(), map); - reg_binary_mut(lib, "append", |s: &mut String, ch: char| s.push(ch), map); - reg_binary_mut( - lib, - "append", - |s: &mut String, add: String| s.push_str(&add), - map, - ); - reg_trinary_mut(lib, "sub_string", sub_string, map); - reg_binary_mut( - lib, - "sub_string", - |s: &mut String, start: INT| sub_string(s, start, s.len() as INT), - map, - ); - reg_trinary_mut(lib, "crop", crop_string, map); - reg_binary_mut( - lib, - "crop", - |s: &mut String, start: INT| crop_string(s, start, s.len() as INT), - map, - ); - reg_binary_mut( - lib, - "truncate", - |s: &mut String, len: INT| { - if len >= 0 { - let chars: Vec<_> = s.chars().take(len as usize).collect(); - s.clear(); - chars.into_iter().for_each(|ch| s.push(ch)); - } else { - s.clear(); - } - }, - map, - ); - reg_trinary_mut( - lib, - "pad", - |s: &mut String, len: INT, ch: char| { - for _ in 0..s.chars().count() - len as usize { - s.push(ch); - } - }, - map, - ); - reg_trinary_mut( - lib, - "replace", - |s: &mut String, find: String, sub: String| { - let new_str = s.replace(&find, &sub); + s[start..] + .find(&find) + .map(|index| s[0..start + index].chars().count() as INT) + .unwrap_or(-1 as INT) + }, + map, + ); + reg_binary_mut( + lib, + "index_of", + |s: &mut String, find: String| { + s.find(&find) + .map(|index| s[0..index].chars().count() as INT) + .unwrap_or(-1 as INT) + }, + map, + ); + reg_unary_mut(lib, "clear", |s: &mut String| s.clear(), map); + reg_binary_mut(lib, "append", |s: &mut String, ch: char| s.push(ch), map); + reg_binary_mut( + lib, + "append", + |s: &mut String, add: String| s.push_str(&add), + map, + ); + reg_trinary_mut(lib, "sub_string", sub_string, map); + reg_binary_mut( + lib, + "sub_string", + |s: &mut String, start: INT| sub_string(s, start, s.len() as INT), + map, + ); + reg_trinary_mut(lib, "crop", crop_string, map); + reg_binary_mut( + lib, + "crop", + |s: &mut String, start: INT| crop_string(s, start, s.len() as INT), + map, + ); + reg_binary_mut( + lib, + "truncate", + |s: &mut String, len: INT| { + if len >= 0 { + let chars: Vec<_> = s.chars().take(len as usize).collect(); s.clear(); - s.push_str(&new_str); - }, - map, - ); - reg_unary_mut( - lib, - "trim", - |s: &mut String| { - let trimmed = s.trim(); + chars.into_iter().for_each(|ch| s.push(ch)); + } else { + s.clear(); + } + }, + map, + ); + reg_trinary_mut( + lib, + "pad", + |s: &mut String, len: INT, ch: char| { + for _ in 0..s.chars().count() - len as usize { + s.push(ch); + } + }, + map, + ); + reg_trinary_mut( + lib, + "replace", + |s: &mut String, find: String, sub: String| { + let new_str = s.replace(&find, &sub); + s.clear(); + s.push_str(&new_str); + }, + map, + ); + reg_unary_mut( + lib, + "trim", + |s: &mut String| { + let trimmed = s.trim(); - if trimmed.len() < s.len() { - *s = trimmed.to_string(); - } - }, - map, - ); - } -} + if trimmed.len() < s.len() { + *s = trimmed.to_string(); + } + }, + map, + ); +}); diff --git a/src/packages/time_basic.rs b/src/packages/time_basic.rs index 4f7881af..91d5c9bc 100644 --- a/src/packages/time_basic.rs +++ b/src/packages/time_basic.rs @@ -1,129 +1,102 @@ use super::logic::{eq, gt, gte, lt, lte, ne}; use super::math_basic::MAX_INT; -use super::{ - create_new_package, reg_binary, reg_none, reg_unary, Package, PackageLibrary, - PackageLibraryStore, -}; +use super::{reg_binary, reg_none, reg_unary}; +use crate::def_package; use crate::fn_register::{map_dynamic as map, map_result as result}; use crate::parser::INT; use crate::result::EvalAltResult; use crate::token::Position; -use crate::stdlib::{ops::Deref, time::Instant}; +use crate::stdlib::time::Instant; -pub struct BasicTimePackage(PackageLibrary); +def_package!(BasicTimePackage:"Basic timing utilities.", lib, { + #[cfg(not(feature = "no_std"))] + { + // Register date/time functions + reg_none(lib, "timestamp", || Instant::now(), map); -impl Deref for BasicTimePackage { - type Target = PackageLibrary; - - fn deref(&self) -> &PackageLibrary { - &self.0 - } -} - -impl Package for BasicTimePackage { - fn new() -> Self { - let mut pkg = create_new_package(); - Self::init(&mut pkg); - Self(pkg.into()) - } - - fn get(&self) -> PackageLibrary { - self.0.clone() - } - - fn init(lib: &mut PackageLibraryStore) { - #[cfg(not(feature = "no_std"))] - { - // Register date/time functions - reg_none(lib, "timestamp", || Instant::now(), map); - - reg_binary( - lib, - "-", - |ts1: Instant, ts2: Instant| { - if ts2 > ts1 { - #[cfg(not(feature = "no_float"))] - return Ok(-(ts2 - ts1).as_secs_f64()); - - #[cfg(feature = "no_float")] - { - let seconds = (ts2 - ts1).as_secs(); - - #[cfg(not(feature = "unchecked"))] - { - if seconds > (MAX_INT as u64) { - return Err(EvalAltResult::ErrorArithmetic( - format!( - "Integer overflow for timestamp duration: {}", - -(seconds as i64) - ), - Position::none(), - )); - } - } - return Ok(-(seconds as INT)); - } - } else { - #[cfg(not(feature = "no_float"))] - return Ok((ts1 - ts2).as_secs_f64()); - - #[cfg(feature = "no_float")] - { - let seconds = (ts1 - ts2).as_secs(); - - #[cfg(not(feature = "unchecked"))] - { - if seconds > (MAX_INT as u64) { - return Err(EvalAltResult::ErrorArithmetic( - format!( - "Integer overflow for timestamp duration: {}", - seconds - ), - Position::none(), - )); - } - } - return Ok(seconds as INT); - } - } - }, - result, - ); - } - - reg_binary(lib, "<", lt::, map); - reg_binary(lib, "<=", lte::, map); - reg_binary(lib, ">", gt::, map); - reg_binary(lib, ">=", gte::, map); - reg_binary(lib, "==", eq::, map); - reg_binary(lib, "!=", ne::, map); - - reg_unary( + reg_binary( lib, - "elapsed", - |timestamp: Instant| { - #[cfg(not(feature = "no_float"))] - return Ok(timestamp.elapsed().as_secs_f64()); + "-", + |ts1: Instant, ts2: Instant| { + if ts2 > ts1 { + #[cfg(not(feature = "no_float"))] + return Ok(-(ts2 - ts1).as_secs_f64()); - #[cfg(feature = "no_float")] - { - let seconds = timestamp.elapsed().as_secs(); - - #[cfg(not(feature = "unchecked"))] + #[cfg(feature = "no_float")] { - if seconds > (MAX_INT as u64) { - return Err(EvalAltResult::ErrorArithmetic( - format!("Integer overflow for timestamp.elapsed(): {}", seconds), - Position::none(), - )); + let seconds = (ts2 - ts1).as_secs(); + + #[cfg(not(feature = "unchecked"))] + { + if seconds > (MAX_INT as u64) { + return Err(EvalAltResult::ErrorArithmetic( + format!( + "Integer overflow for timestamp duration: {}", + -(seconds as i64) + ), + Position::none(), + )); + } } + return Ok(-(seconds as INT)); + } + } else { + #[cfg(not(feature = "no_float"))] + return Ok((ts1 - ts2).as_secs_f64()); + + #[cfg(feature = "no_float")] + { + let seconds = (ts1 - ts2).as_secs(); + + #[cfg(not(feature = "unchecked"))] + { + if seconds > (MAX_INT as u64) { + return Err(EvalAltResult::ErrorArithmetic( + format!("Integer overflow for timestamp duration: {}", seconds), + Position::none(), + )); + } + } + return Ok(seconds as INT); } - return Ok(seconds as INT); } }, result, ); } -} + + reg_binary(lib, "<", lt::, map); + reg_binary(lib, "<=", lte::, map); + reg_binary(lib, ">", gt::, map); + reg_binary(lib, ">=", gte::, map); + reg_binary(lib, "==", eq::, map); + reg_binary(lib, "!=", ne::, map); + + reg_unary( + lib, + "elapsed", + |timestamp: Instant| { + #[cfg(not(feature = "no_float"))] + return Ok(timestamp.elapsed().as_secs_f64()); + + #[cfg(feature = "no_float")] + { + let seconds = timestamp.elapsed().as_secs(); + + #[cfg(not(feature = "unchecked"))] + { + if seconds > (MAX_INT as u64) { + return Err(EvalAltResult::ErrorArithmetic( + format!("Integer overflow for timestamp.elapsed(): {}", seconds), + Position::none(), + )); + } + } + return Ok(seconds as INT); + } + }, + result, + ); +}); diff --git a/src/packages/utils.rs b/src/packages/utils.rs new file mode 100644 index 00000000..0a0b0106 --- /dev/null +++ b/src/packages/utils.rs @@ -0,0 +1,363 @@ +use super::PackageStore; + +use crate::any::{Dynamic, Variant}; +use crate::calc_fn_hash; +use crate::engine::FnCallArgs; +use crate::result::EvalAltResult; +use crate::token::Position; + +use crate::stdlib::{any::TypeId, boxed::Box}; + +/// This macro makes it easy to define a _package_ and register functions into it. +/// +/// Functions can be added to the package using a number of helper functions under the `packages` module, +/// such as `reg_unary`, `reg_binary_mut`, `reg_trinary_mut` etc. +/// +/// ```,ignore +/// use rhai::def_package; +/// use rhai::packages::reg_binary; +/// +/// fn add(x: i64, y: i64) { x + y } +/// +/// def_package!(MyPackage:"My super-duper package", lib, +/// { +/// reg_binary(lib, "my_add", add, |v| Ok(v.into_dynamic())); +/// // ^^^^^^^^^^^^^^^^^^^^ +/// // map into Result +/// }); +/// ``` +/// +/// The above defines a package named 'MyPackage' with a single function named 'my_add'. +#[macro_export] +macro_rules! def_package { + ($package:ident : $comment:expr , $lib:ident , $block:stmt) => { + #[doc=$comment] + pub struct $package(super::PackageLibrary); + + impl crate::packages::Package for $package { + fn new() -> Self { + let mut pkg = crate::packages::PackageStore::new(); + Self::init(&mut pkg); + Self(pkg.into()) + } + + fn get(&self) -> crate::packages::PackageLibrary { + self.0.clone() + } + + fn init($lib: &mut crate::packages::PackageStore) { + $block + } + } + }; +} + +fn check_num_args( + name: &str, + num_args: usize, + args: &mut FnCallArgs, + pos: Position, +) -> Result<(), Box> { + if args.len() != num_args { + Err(Box::new(EvalAltResult::ErrorFunctionArgsMismatch( + name.to_string(), + num_args, + args.len(), + pos, + ))) + } else { + Ok(()) + } +} + +/// Add a function with no parameters to the package. +/// +/// `map_result` is a function that maps the return type of the function to `Result`. +pub fn reg_none( + lib: &mut PackageStore, + fn_name: &'static str, + + #[cfg(not(feature = "sync"))] func: impl Fn() -> R + 'static, + #[cfg(feature = "sync")] func: impl Fn() -> R + Send + Sync + 'static, + + #[cfg(not(feature = "sync"))] map_result: impl Fn(R, Position) -> Result> + + 'static, + #[cfg(feature = "sync")] map_result: impl Fn(R, Position) -> Result> + + Send + + Sync + + 'static, +) { + let hash = calc_fn_hash(fn_name, ([] as [TypeId; 0]).iter().cloned()); + + let f = Box::new(move |args: &mut FnCallArgs, pos: Position| { + check_num_args(fn_name, 0, args, pos)?; + + let r = func(); + map_result(r, pos) + }); + + lib.functions.insert(hash, f); +} + +/// Add a function with one parameter to the package. +/// +/// `map_result` is a function that maps the return type of the function to `Result`. +pub fn reg_unary( + lib: &mut PackageStore, + fn_name: &'static str, + + #[cfg(not(feature = "sync"))] func: impl Fn(T) -> R + 'static, + #[cfg(feature = "sync")] func: impl Fn(T) -> R + Send + Sync + 'static, + + #[cfg(not(feature = "sync"))] map_result: impl Fn(R, Position) -> Result> + + 'static, + #[cfg(feature = "sync")] map_result: impl Fn(R, Position) -> Result> + + Send + + Sync + + 'static, +) { + //println!("register {}({})", fn_name, crate::std::any::type_name::()); + + let hash = calc_fn_hash(fn_name, [TypeId::of::()].iter().cloned()); + + let f = Box::new(move |args: &mut FnCallArgs, pos: Position| { + check_num_args(fn_name, 1, args, pos)?; + + let mut drain = args.iter_mut(); + let x: &mut T = drain.next().unwrap().downcast_mut().unwrap(); + + let r = func(x.clone()); + map_result(r, pos) + }); + + lib.functions.insert(hash, f); +} + +/// Add a function with one mutable reference parameter to the package. +/// +/// `map_result` is a function that maps the return type of the function to `Result`. +pub fn reg_unary_mut( + lib: &mut PackageStore, + fn_name: &'static str, + + #[cfg(not(feature = "sync"))] func: impl Fn(&mut T) -> R + 'static, + #[cfg(feature = "sync")] func: impl Fn(&mut T) -> R + Send + Sync + 'static, + + #[cfg(not(feature = "sync"))] map_result: impl Fn(R, Position) -> Result> + + 'static, + #[cfg(feature = "sync")] map_result: impl Fn(R, Position) -> Result> + + Send + + Sync + + 'static, +) { + //println!("register {}(&mut {})", fn_name, crate::std::any::type_name::()); + + let hash = calc_fn_hash(fn_name, [TypeId::of::()].iter().cloned()); + + let f = Box::new(move |args: &mut FnCallArgs, pos: Position| { + check_num_args(fn_name, 1, args, pos)?; + + let mut drain = args.iter_mut(); + let x: &mut T = drain.next().unwrap().downcast_mut().unwrap(); + + let r = func(x); + map_result(r, pos) + }); + + lib.functions.insert(hash, f); +} + +pub(crate) fn reg_test<'a, A: Variant + Clone, B: Variant + Clone, X, R>( + lib: &mut PackageStore, + fn_name: &'static str, + + #[cfg(not(feature = "sync"))] func: impl Fn(X, B) -> R + 'static, + #[cfg(feature = "sync")] func: impl Fn(X, B) -> R + Send + Sync + 'static, + + map: impl Fn(&mut A) -> X + 'static, + + #[cfg(not(feature = "sync"))] map_result: impl Fn(R, Position) -> Result> + + 'static, + #[cfg(feature = "sync")] map_result: impl Fn(R, Position) -> Result> + + Send + + Sync + + 'static, +) { + //println!("register {}({}, {})", fn_name, crate::std::any::type_name::(), crate::std::any::type_name::()); + + let hash = calc_fn_hash( + fn_name, + [TypeId::of::(), TypeId::of::()].iter().cloned(), + ); + + let f = Box::new(move |args: &mut FnCallArgs, pos: Position| { + check_num_args(fn_name, 2, args, pos)?; + + let mut drain = args.iter_mut(); + let x: X = map(drain.next().unwrap().downcast_mut::().unwrap()); + let y: B = drain.next().unwrap().downcast_mut::().unwrap().clone(); + + let r = func(x, y); + map_result(r, pos) + }); + + lib.functions.insert(hash, f); +} + +/// Add a function with two parameters to the package. +/// +/// `map_result` is a function that maps the return type of the function to `Result`. +pub fn reg_binary( + lib: &mut PackageStore, + fn_name: &'static str, + + #[cfg(not(feature = "sync"))] func: impl Fn(A, B) -> R + 'static, + #[cfg(feature = "sync")] func: impl Fn(A, B) -> R + Send + Sync + 'static, + + #[cfg(not(feature = "sync"))] map_result: impl Fn(R, Position) -> Result> + + 'static, + #[cfg(feature = "sync")] map_result: impl Fn(R, Position) -> Result> + + Send + + Sync + + 'static, +) { + //println!("register {}({}, {})", fn_name, crate::std::any::type_name::(), crate::std::any::type_name::()); + + let hash = calc_fn_hash( + fn_name, + [TypeId::of::(), TypeId::of::()].iter().cloned(), + ); + + let f = Box::new(move |args: &mut FnCallArgs, pos: Position| { + check_num_args(fn_name, 2, args, pos)?; + + let mut drain = args.iter_mut(); + let x: &mut A = drain.next().unwrap().downcast_mut().unwrap(); + let y: &mut B = drain.next().unwrap().downcast_mut().unwrap(); + + let r = func(x.clone(), y.clone()); + map_result(r, pos) + }); + + lib.functions.insert(hash, f); +} + +/// Add a function with two parameters (the first one being a mutable reference) to the package. +/// +/// `map_result` is a function that maps the return type of the function to `Result`. +pub fn reg_binary_mut( + lib: &mut PackageStore, + fn_name: &'static str, + + #[cfg(not(feature = "sync"))] func: impl Fn(&mut A, B) -> R + 'static, + #[cfg(feature = "sync")] func: impl Fn(&mut A, B) -> R + Send + Sync + 'static, + + #[cfg(not(feature = "sync"))] map_result: impl Fn(R, Position) -> Result> + + 'static, + #[cfg(feature = "sync")] map_result: impl Fn(R, Position) -> Result> + + Send + + Sync + + 'static, +) { + //println!("register {}(&mut {}, {})", fn_name, crate::std::any::type_name::(), crate::std::any::type_name::()); + + let hash = calc_fn_hash( + fn_name, + [TypeId::of::(), TypeId::of::()].iter().cloned(), + ); + + let f = Box::new(move |args: &mut FnCallArgs, pos: Position| { + check_num_args(fn_name, 2, args, pos)?; + + let mut drain = args.iter_mut(); + let x: &mut A = drain.next().unwrap().downcast_mut().unwrap(); + let y: &mut B = drain.next().unwrap().downcast_mut().unwrap(); + + let r = func(x, y.clone()); + map_result(r, pos) + }); + + lib.functions.insert(hash, f); +} + +/// Add a function with three parameters to the package. +/// +/// `map_result` is a function that maps the return type of the function to `Result`. +pub fn reg_trinary( + lib: &mut PackageStore, + fn_name: &'static str, + + #[cfg(not(feature = "sync"))] func: impl Fn(A, B, C) -> R + 'static, + #[cfg(feature = "sync")] func: impl Fn(A, B, C) -> R + Send + Sync + 'static, + + #[cfg(not(feature = "sync"))] map_result: impl Fn(R, Position) -> Result> + + 'static, + #[cfg(feature = "sync")] map_result: impl Fn(R, Position) -> Result> + + Send + + Sync + + 'static, +) { + //println!("register {}({}, {}, {})", fn_name, crate::std::any::type_name::(), crate::std::any::type_name::(), crate::std::any::type_name::()); + + let hash = calc_fn_hash( + fn_name, + [TypeId::of::(), TypeId::of::(), TypeId::of::()] + .iter() + .cloned(), + ); + + let f = Box::new(move |args: &mut FnCallArgs, pos: Position| { + check_num_args(fn_name, 3, args, pos)?; + + let mut drain = args.iter_mut(); + let x: &mut A = drain.next().unwrap().downcast_mut().unwrap(); + let y: &mut B = drain.next().unwrap().downcast_mut().unwrap(); + let z: &mut C = drain.next().unwrap().downcast_mut().unwrap(); + + let r = func(x.clone(), y.clone(), z.clone()); + map_result(r, pos) + }); + + lib.functions.insert(hash, f); +} + +/// Add a function with three parameters (the first one is a mutable reference) to the package. +/// +/// `map_result` is a function that maps the return type of the function to `Result`. +pub fn reg_trinary_mut( + lib: &mut PackageStore, + fn_name: &'static str, + + #[cfg(not(feature = "sync"))] func: impl Fn(&mut A, B, C) -> R + 'static, + #[cfg(feature = "sync")] func: impl Fn(&mut A, B, C) -> R + Send + Sync + 'static, + + #[cfg(not(feature = "sync"))] map_result: impl Fn(R, Position) -> Result> + + 'static, + #[cfg(feature = "sync")] map_result: impl Fn(R, Position) -> Result> + + Send + + Sync + + 'static, +) { + //println!("register {}(&mut {}, {}, {})", fn_name, crate::std::any::type_name::(), crate::std::any::type_name::(), crate::std::any::type_name::()); + + let hash = calc_fn_hash( + fn_name, + [TypeId::of::(), TypeId::of::(), TypeId::of::()] + .iter() + .cloned(), + ); + + let f = Box::new(move |args: &mut FnCallArgs, pos: Position| { + check_num_args(fn_name, 3, args, pos)?; + + let mut drain = args.iter_mut(); + let x: &mut A = drain.next().unwrap().downcast_mut().unwrap(); + let y: &mut B = drain.next().unwrap().downcast_mut().unwrap(); + let z: &mut C = drain.next().unwrap().downcast_mut().unwrap(); + + let r = func(x, y.clone(), z.clone()); + map_result(r, pos) + }); + + lib.functions.insert(hash, f); +}