Refine packages plumbing.

This commit is contained in:
Stephen Chung 2020-04-21 23:01:10 +08:00
parent e394824bf3
commit 0a75479637
19 changed files with 1232 additions and 1406 deletions

View File

@ -1,7 +1,7 @@
//! Module that defines the extern API of `Engine`. //! Module that defines the extern API of `Engine`.
use crate::any::{Dynamic, Variant}; 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::error::ParseError;
use crate::fn_call::FuncArgs; use crate::fn_call::FuncArgs;
use crate::fn_register::RegisterFn; use crate::fn_register::RegisterFn;

View File

@ -1,6 +1,7 @@
//! Main module defining the script evaluation `Engine`. //! Main module defining the script evaluation `Engine`.
use crate::any::{Dynamic, Union}; use crate::any::{Dynamic, Union};
use crate::calc_fn_hash;
use crate::error::ParseErrorType; use crate::error::ParseErrorType;
use crate::optimize::OptimizationLevel; use crate::optimize::OptimizationLevel;
use crate::packages::{CorePackage, Package, PackageLibrary, StandardPackage}; 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 /// Since script-defined functions have `Dynamic` parameters, functions with the same name
/// and number of parameters are considered equivalent. /// and number of parameters are considered equivalent.
/// ///
/// Since the key is a combination of the function name (a String) plus the number of parameters, /// The key of the `HashMap` is a `u64` hash calculated by the function `calc_fn_def`.
/// 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.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct FunctionsLib( pub struct FunctionsLib(
#[cfg(feature = "sync")] HashMap<u64, Arc<FnDef>>, #[cfg(feature = "sync")] HashMap<u64, Arc<FnDef>>,
@ -224,7 +221,9 @@ impl DerefMut for FunctionsLib {
pub struct Engine { pub struct Engine {
/// A collection of all library packages loaded into the engine. /// A collection of all library packages loaded into the engine.
pub(crate) packages: Vec<PackageLibrary>, pub(crate) packages: Vec<PackageLibrary>,
/// 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<u64, Box<FnAny>>, pub(crate) functions: HashMap<u64, Box<FnAny>>,
/// A hashmap containing all iterators known to the engine. /// 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<Item = TypeId>) -> 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<Item = TypeId>) -> u64 {
let mut s = DefaultHasher::new(); let mut s = DefaultHasher::new();
fn_name.hash(&mut s); fn_name.hash(&mut s);
params.for_each(|t| t.hash(&mut s)); params.for_each(|t| t.hash(&mut s));
s.finish() 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 { pub(crate) fn calc_fn_def(fn_name: &str, params: usize) -> u64 {
let mut s = DefaultHasher::new(); let mut s = DefaultHasher::new();
fn_name.hash(&mut s); 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) { 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); self.packages.insert(0, package);
} }
@ -576,13 +585,13 @@ impl Engine {
} }
// Search built-in's and external functions // 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(|| { if let Some(func) = self.functions.get(&fn_spec).or_else(|| {
self.packages self.packages
.iter() .iter()
.find(|p| p.0.contains_key(&fn_spec)) .find(|pkg| pkg.functions.contains_key(&fn_spec))
.and_then(|p| p.0.get(&fn_spec)) .and_then(|pkg| pkg.functions.get(&fn_spec))
}) { }) {
// Run external function // Run external function
let result = func(args, pos)?; let result = func(args, pos)?;
@ -1343,7 +1352,7 @@ impl Engine {
) -> bool { ) -> bool {
engine engine
.functions .functions
.contains_key(&calc_fn_spec(name, once(TypeId::of::<String>()))) .contains_key(&calc_fn_hash(name, once(TypeId::of::<String>())))
|| fn_lib.map_or(false, |lib| lib.has_function(name, 1)) || 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(|| { if let Some(iter_fn) = self.type_iterators.get(&tid).or_else(|| {
self.packages self.packages
.iter() .iter()
.find(|p| p.1.contains_key(&tid)) .find(|pkg| pkg.type_iterators.contains_key(&tid))
.and_then(|p| p.1.get(&tid)) .and_then(|pkg| pkg.type_iterators.get(&tid))
}) { }) {
// Add the loop variable - variable name is copied // Add the loop variable - variable name is copied
// TODO - avoid copying variable name // TODO - avoid copying variable name

View File

@ -5,15 +5,15 @@
use crate::any::{Dynamic, Variant}; use crate::any::{Dynamic, Variant};
use crate::stdlib::vec::Vec; use crate::stdlib::vec::Vec;
/// Trait that represent arguments to a function call. /// Trait that represents arguments to a function call.
/// Any data type that can be converted into a `Vec` of `Dynamic` values can be used /// Any data type that can be converted into a `Vec<Dynamic>` can be used
/// as arguments to a function call. /// as arguments to a function call.
pub trait FuncArgs { pub trait FuncArgs {
/// Convert to a `Vec` of `Dynamic` arguments. /// Convert to a `Vec<Dynamic>` of the function call arguments.
fn into_vec(self) -> Vec<Dynamic>; fn into_vec(self) -> Vec<Dynamic>;
} }
// 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`). /// converted into `Dynamic`).
macro_rules! impl_args { macro_rules! impl_args {
($($p:ident),*) => { ($($p:ident),*) => {

View File

@ -86,7 +86,7 @@ mod stdlib;
mod token; mod token;
pub use any::Dynamic; 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 error::{ParseError, ParseErrorType};
pub use fn_call::FuncArgs; pub use fn_call::FuncArgs;
pub use fn_register::{RegisterDynamicFn, RegisterFn, RegisterResultFn}; pub use fn_register::{RegisterDynamicFn, RegisterFn, RegisterResultFn};

View File

@ -1,7 +1,8 @@
use crate::any::Dynamic; use crate::any::Dynamic;
use crate::calc_fn_hash;
use crate::engine::{ use crate::engine::{
calc_fn_spec, Engine, FnAny, FnCallArgs, FunctionsLib, KEYWORD_DEBUG, KEYWORD_EVAL, Engine, FnAny, FnCallArgs, FunctionsLib, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_PRINT,
KEYWORD_PRINT, KEYWORD_TYPE_OF, KEYWORD_TYPE_OF,
}; };
use crate::packages::PackageLibrary; use crate::packages::PackageLibrary;
use crate::parser::{map_dynamic_to_expr, Expr, FnDef, ReturnType, Stmt, AST}; use crate::parser::{map_dynamic_to_expr, Expr, FnDef, ReturnType, Stmt, AST};
@ -118,15 +119,15 @@ fn call_fn(
pos: Position, pos: Position,
) -> Result<Option<Dynamic>, Box<EvalAltResult>> { ) -> Result<Option<Dynamic>, Box<EvalAltResult>> {
// Search built-in's and external functions // 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 functions
.get(&hash) .get(&hash)
.or_else(|| { .or_else(|| {
packages packages
.iter() .iter()
.find(|p| p.0.contains_key(&hash)) .find(|p| p.functions.contains_key(&hash))
.and_then(|p| p.0.get(&hash)) .and_then(|p| p.functions.get(&hash))
}) })
.map(|func| func(args, pos)) .map(|func| func(args, pos))
.transpose() .transpose()

View File

@ -1,7 +1,6 @@
use super::{ use super::{reg_binary, reg_unary};
create_new_package, reg_binary, reg_unary, Package, PackageLibrary, PackageLibraryStore,
};
use crate::def_package;
use crate::fn_register::{map_dynamic as map, map_result as result}; use crate::fn_register::{map_dynamic as map, map_result as result};
use crate::parser::INT; use crate::parser::INT;
use crate::result::EvalAltResult; use crate::result::EvalAltResult;
@ -18,7 +17,7 @@ use num_traits::{
use crate::stdlib::{ use crate::stdlib::{
fmt::Display, fmt::Display,
format, 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}, {i32, i64, u32},
}; };
@ -254,16 +253,6 @@ fn pow_f_i_u(x: FLOAT, y: INT) -> FLOAT {
x.powi(y as i32) 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),*) => { macro_rules! reg_unary_x { ($lib:expr, $op:expr, $func:ident, $($par:ty),*) => {
$(reg_unary($lib, $op, $func::<$par>, result);)* }; $(reg_unary($lib, $op, $func::<$par>, result);)* };
} }
@ -277,18 +266,7 @@ macro_rules! reg_op { ($lib:expr, $op:expr, $func:ident, $($par:ty),*) => {
$(reg_binary($lib, $op, $func::<$par>, map);)* }; $(reg_binary($lib, $op, $func::<$par>, map);)* };
} }
impl Package for ArithmeticPackage { def_package!(ArithmeticPackage:"Basic arithmetic", lib, {
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 // Checked basic arithmetic
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
{ {
@ -438,5 +416,4 @@ impl Package for ArithmeticPackage {
reg_unary!(lib, "-", neg_u, f32, f64); reg_unary!(lib, "-", neg_u, f32, f64);
reg_unary!(lib, "abs", abs_u, f32, f64); reg_unary!(lib, "abs", abs_u, f32, f64);
} }
} });
}

View File

@ -1,14 +1,12 @@
use super::{ use super::{reg_binary, reg_binary_mut, reg_trinary_mut, reg_unary_mut};
create_new_package, reg_binary, reg_binary_mut, reg_trinary_mut, reg_unary_mut, Package,
PackageLibrary, PackageLibraryStore,
};
use crate::def_package;
use crate::any::{Dynamic, Variant}; use crate::any::{Dynamic, Variant};
use crate::engine::Array; 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::parser::INT;
use crate::stdlib::ops::Deref; use crate::stdlib::any::TypeId;
// Register array utility functions // Register array utility functions
fn push<T: Variant + Clone>(list: &mut Array, item: T) { fn push<T: Variant + Clone>(list: &mut Array, item: T) {
@ -31,16 +29,6 @@ fn pad<T: Variant + Clone>(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),*) => { macro_rules! reg_op { ($lib:expr, $op:expr, $func:ident, $($par:ty),*) => {
$(reg_binary_mut($lib, $op, $func::<$par>, map);)* }; $(reg_binary_mut($lib, $op, $func::<$par>, map);)* };
} }
@ -48,20 +36,8 @@ macro_rules! reg_tri { ($lib:expr, $op:expr, $func:ident, $($par:ty),*) => {
$(reg_trinary_mut($lib, $op, $func::<$par>, map);)* }; $(reg_trinary_mut($lib, $op, $func::<$par>, map);)* };
} }
impl Package for BasicArrayPackage { #[cfg(not(feature = "no_index"))]
fn new() -> Self { def_package!(BasicArrayPackage:"Basic array utilities.", lib, {
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_index"))]
{
reg_op!(lib, "push", push, INT, bool, char, String, Array, ()); reg_op!(lib, "push", push, INT, bool, char, String, Array, ());
reg_tri!(lib, "pad", pad, 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_tri!(lib, "insert", ins, INT, bool, char, String, Array, ());
@ -96,7 +72,7 @@ impl Package for BasicArrayPackage {
lib, lib,
"pop", "pop",
|list: &mut Array| list.pop().unwrap_or_else(|| Dynamic::from_unit()), |list: &mut Array| list.pop().unwrap_or_else(|| Dynamic::from_unit()),
copy, pass,
); );
reg_unary_mut( reg_unary_mut(
lib, lib,
@ -108,7 +84,7 @@ impl Package for BasicArrayPackage {
list.remove(0) list.remove(0)
} }
}, },
copy, pass,
); );
reg_binary_mut( reg_binary_mut(
lib, lib,
@ -120,7 +96,7 @@ impl Package for BasicArrayPackage {
list.remove(len as usize) list.remove(len as usize)
} }
}, },
copy, pass,
); );
reg_unary_mut(lib, "len", |list: &mut Array| list.len() as INT, map); 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_unary_mut(lib, "clear", |list: &mut Array| list.clear(), map);
@ -134,6 +110,13 @@ impl Package for BasicArrayPackage {
}, },
map, map,
); );
}
} // Register array iterator
} lib.type_iterators.insert(
TypeId::of::<Array>(),
Box::new(|a: &Dynamic| {
Box::new(a.downcast_ref::<Array>().unwrap().clone().into_iter())
as Box<dyn Iterator<Item = Dynamic>>
}),
);
});

View File

@ -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) {}
}

View File

@ -1,8 +1,6 @@
use super::{ use super::{reg_binary, reg_trinary, reg_unary_mut, PackageStore};
create_new_package, reg_binary, reg_trinary, reg_unary_mut, Package, PackageLibrary,
PackageLibraryStore,
};
use crate::def_package;
use crate::any::{Dynamic, Union, Variant}; use crate::any::{Dynamic, Union, Variant};
use crate::engine::{Array, Map}; use crate::engine::{Array, Map};
use crate::fn_register::map_dynamic as map; use crate::fn_register::map_dynamic as map;
@ -10,15 +8,15 @@ use crate::parser::INT;
use crate::stdlib::{ use crate::stdlib::{
any::TypeId, any::TypeId,
ops::{Add, Deref, Range}, ops::{Add, Range},
}; };
// Register range function // Register range function
fn reg_range<T: Variant + Clone>(lib: &mut PackageLibraryStore) fn reg_range<T: Variant + Clone>(lib: &mut PackageStore)
where where
Range<T>: Iterator<Item = T>, Range<T>: Iterator<Item = T>,
{ {
lib.1.insert( lib.type_iterators.insert(
TypeId::of::<Range<T>>(), TypeId::of::<Range<T>>(),
Box::new(|source: &Dynamic| { Box::new(|source: &Dynamic| {
Box::new( Box::new(
@ -57,13 +55,13 @@ where
} }
} }
fn reg_step<T>(lib: &mut PackageLibraryStore) fn reg_step<T>(lib: &mut PackageStore)
where where
for<'a> &'a T: Add<&'a T, Output = T>, for<'a> &'a T: Add<&'a T, Output = T>,
T: Variant + Clone + PartialOrd, T: Variant + Clone + PartialOrd,
StepRange<T>: Iterator<Item = T>, StepRange<T>: Iterator<Item = T>,
{ {
lib.1.insert( lib.type_iterators.insert(
TypeId::of::<StepRange<T>>(), TypeId::of::<StepRange<T>>(),
Box::new(|source: &Dynamic| { Box::new(|source: &Dynamic| {
Box::new( Box::new(
@ -77,59 +75,7 @@ where
); );
} }
pub struct BasicIteratorPackage(PackageLibrary); def_package!(BasicIteratorPackage:"Basic range iterators.", lib, {
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())
}
fn get(&self) -> PackageLibrary {
self.0.clone()
}
fn init(lib: &mut PackageLibraryStore) {
#[cfg(not(feature = "no_index"))]
{
// Register array iterator
lib.1.insert(
TypeId::of::<Array>(),
Box::new(|a: &Dynamic| {
Box::new(a.downcast_ref::<Array>().unwrap().clone().into_iter())
as Box<dyn Iterator<Item = Dynamic>>
}),
);
}
// Register map access functions
#[cfg(not(feature = "no_object"))]
{
fn map_get_keys(map: &mut Map) -> Vec<Dynamic> {
map.iter()
.map(|(k, _)| Dynamic(Union::Str(Box::new(k.to_string()))))
.collect::<Vec<_>>()
}
fn map_get_values(map: &mut Map) -> Vec<Dynamic> {
map.iter().map(|(_, v)| v.clone()).collect::<Vec<_>>()
}
#[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<T>(from: T, to: T) -> Range<T> { fn get_range<T>(from: T, to: T) -> Range<T> {
from..to from..to
} }
@ -169,5 +115,4 @@ impl Package for BasicIteratorPackage {
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);
} }
} });
}

View File

@ -1,13 +1,10 @@
use super::{ use super::utils::reg_test;
create_new_package, reg_binary, reg_binary_mut, reg_unary, Package, PackageLibrary, use super::{reg_binary, reg_binary_mut, reg_unary};
PackageLibraryStore,
};
use crate::def_package;
use crate::fn_register::map_dynamic as map; use crate::fn_register::map_dynamic as map;
use crate::parser::INT; use crate::parser::INT;
use crate::stdlib::ops::Deref;
// Comparison operators // Comparison operators
pub fn lt<T: PartialOrd>(x: T, y: T) -> bool { pub fn lt<T: PartialOrd>(x: T, y: T) -> bool {
x < y x < y
@ -43,28 +40,7 @@ macro_rules! reg_op { ($lib:expr, $op:expr, $func:ident, $($par:ty),*) => {
$(reg_binary($lib, $op, $func::<$par>, map);)* }; $(reg_binary($lib, $op, $func::<$par>, map);)* };
} }
pub struct LogicPackage(PackageLibrary); def_package!(LogicPackage:"Logical operators.", lib, {
impl Deref for LogicPackage {
type Target = PackageLibrary;
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())
}
fn get(&self) -> PackageLibrary {
self.0.clone()
}
fn init(lib: &mut PackageLibraryStore) {
reg_op!(lib, "<", lt, INT, char); reg_op!(lib, "<", lt, INT, char);
reg_op!(lib, "<=", lte, INT, char); reg_op!(lib, "<=", lte, INT, char);
reg_op!(lib, ">", gt, INT, char); reg_op!(lib, ">", gt, INT, char);
@ -73,6 +49,7 @@ impl Package for LogicPackage {
reg_op!(lib, "!=", ne, INT, char, bool, ()); reg_op!(lib, "!=", ne, INT, char, bool, ());
// Special versions for strings - at least avoid copying the first string // 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);
@ -109,5 +86,4 @@ impl Package for LogicPackage {
reg_binary(lib, "|", or, map); reg_binary(lib, "|", or, map);
reg_binary(lib, "&", and, map); reg_binary(lib, "&", and, map);
reg_unary(lib, "!", not, map); reg_unary(lib, "!", not, map);
} });
}

View File

@ -1,40 +1,22 @@
use super::{ use super::{reg_binary, reg_binary_mut, reg_unary_mut};
create_new_package, reg_binary, reg_binary_mut, reg_unary_mut, Package, PackageLibrary,
PackageLibraryStore,
};
use crate::def_package;
use crate::any::Dynamic; use crate::any::Dynamic;
use crate::engine::Map; use crate::engine::Map;
use crate::fn_register::map_dynamic as map; use crate::fn_register::map_dynamic as map;
use crate::parser::INT; use crate::parser::INT;
use crate::stdlib::ops::Deref; fn map_get_keys(map: &mut Map) -> Vec<Dynamic> {
map.iter()
pub struct BasicMapPackage(PackageLibrary); .map(|(k, _)| Dynamic::from_string(k.to_string()))
.collect::<Vec<_>>()
impl Deref for BasicMapPackage { }
type Target = PackageLibrary; fn map_get_values(map: &mut Map) -> Vec<Dynamic> {
map.iter().map(|(_, v)| v.clone()).collect::<Vec<_>>()
fn deref(&self) -> &PackageLibrary {
&self.0
}
} }
impl Package for BasicMapPackage { #[cfg(not(feature = "no_object"))]
fn new() -> Self { def_package!(BasicMapPackage:"Basic object map utilities.", lib, {
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) {
// Register map functions
#[cfg(not(feature = "no_object"))]
{
reg_binary_mut( reg_binary_mut(
lib, lib,
"has", "has",
@ -70,6 +52,11 @@ impl Package for BasicMapPackage {
}, },
map, map,
); );
}
} // Register map access functions
} #[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);
});

View File

@ -1,7 +1,6 @@
use super::{ use super::{reg_binary, reg_unary};
create_new_package, reg_binary, reg_unary, Package, PackageLibrary, PackageLibraryStore,
};
use crate::def_package;
use crate::fn_register::{map_dynamic as map, map_result as result}; use crate::fn_register::{map_dynamic as map, map_result as result};
use crate::parser::INT; use crate::parser::INT;
use crate::result::EvalAltResult; use crate::result::EvalAltResult;
@ -10,35 +9,14 @@ use crate::token::Position;
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
use crate::parser::FLOAT; use crate::parser::FLOAT;
use crate::stdlib::{i32, i64, ops::Deref}; use crate::stdlib::{i32, i64};
#[cfg(feature = "only_i32")] #[cfg(feature = "only_i32")]
pub const MAX_INT: INT = i32::MAX; pub const MAX_INT: INT = i32::MAX;
#[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i32"))]
pub const MAX_INT: INT = i64::MAX; pub const MAX_INT: INT = i64::MAX;
pub struct BasicMathPackage(PackageLibrary); def_package!(BasicMathPackage:"Basic mathematic functions.", lib, {
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"))] #[cfg(not(feature = "no_float"))]
{ {
// Advanced math functions // Advanced math functions
@ -150,5 +128,4 @@ impl Package for BasicMathPackage {
reg_unary(lib, "to_int", |x: f64| x as INT, map); reg_unary(lib, "to_int", |x: f64| x as INT, map);
} }
} }
} });
}

View File

@ -1,16 +1,8 @@
use crate::any::{Dynamic, Variant}; //! This module contains all built-in _packages_ available to Rhai, plus facilities to define custom packages.
use crate::engine::{calc_fn_spec, FnAny, FnCallArgs, IteratorFn};
use crate::result::EvalAltResult;
use crate::token::Position;
use crate::stdlib::{ use crate::engine::{FnAny, IteratorFn};
any::{type_name, TypeId},
boxed::Box, use crate::stdlib::{any::TypeId, boxed::Box, collections::HashMap, rc::Rc, sync::Arc};
collections::HashMap,
ops::Deref,
rc::Rc,
sync::Arc,
};
mod arithmetic; mod arithmetic;
mod array_basic; mod array_basic;
@ -23,6 +15,7 @@ mod pkg_std;
mod string_basic; mod string_basic;
mod string_more; mod string_more;
mod time_basic; mod time_basic;
mod utils;
pub use arithmetic::ArithmeticPackage; pub use arithmetic::ArithmeticPackage;
pub use array_basic::BasicArrayPackage; pub use array_basic::BasicArrayPackage;
@ -36,272 +29,43 @@ pub use string_basic::BasicStringPackage;
pub use string_more::MoreStringPackage; pub use string_more::MoreStringPackage;
pub use time_basic::BasicTimePackage; 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 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; fn get(&self) -> PackageLibrary;
} }
pub type PackageLibraryStore = (HashMap<u64, Box<FnAny>>, HashMap<TypeId, Box<IteratorFn>>); /// 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<u64, Box<FnAny>>,
#[cfg(not(feature = "sync"))] /// All iterator functions, keyed by the type producing the iterator.
pub type PackageLibrary = Rc<PackageLibraryStore>; pub type_iterators: HashMap<TypeId, Box<IteratorFn>>,
}
#[cfg(feature = "sync")] impl PackageStore {
pub type PackageLibrary = Arc<PackageLibraryStore>; /// Create a new `PackageStore`.
pub fn new() -> Self {
fn check_num_args( Self {
name: &str, functions: HashMap::new(),
num_args: usize, type_iterators: HashMap::new(),
args: &mut FnCallArgs, }
pos: Position,
) -> Result<(), Box<EvalAltResult>> {
if args.len() != num_args {
Err(Box::new(EvalAltResult::ErrorFunctionArgsMismatch(
name.to_string(),
num_args,
args.len(),
pos,
)))
} else {
Ok(())
} }
} }
fn create_new_package() -> PackageLibraryStore { /// Type which `Rc`-wraps a `PackageStore` to facilitate sharing library instances.
(HashMap::new(), HashMap::new()) #[cfg(not(feature = "sync"))]
} pub type PackageLibrary = Rc<PackageStore>;
fn reg_none<R>( /// Type which `Arc`-wraps a `PackageStore` to facilitate sharing library instances.
lib: &mut PackageLibraryStore, #[cfg(feature = "sync")]
fn_name: &'static str, pub type PackageLibrary = Arc<PackageStore>;
#[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<Dynamic, Box<EvalAltResult>>
+ 'static,
#[cfg(feature = "sync")] map_result: impl Fn(R, Position) -> Result<Dynamic, Box<EvalAltResult>>
+ 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<T: Variant + Clone, R>(
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<Dynamic, Box<EvalAltResult>>
+ 'static,
#[cfg(feature = "sync")] map_result: impl Fn(R, Position) -> Result<Dynamic, Box<EvalAltResult>>
+ Send
+ Sync
+ 'static,
) {
//println!("register {}({})", fn_name, type_name::<T>());
let hash = calc_fn_spec(fn_name, [TypeId::of::<T>()].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<T: Variant + Clone, R>(
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<Dynamic, Box<EvalAltResult>>
+ 'static,
#[cfg(feature = "sync")] map_result: impl Fn(R, Position) -> Result<Dynamic, Box<EvalAltResult>>
+ Send
+ Sync
+ 'static,
) {
//println!("register {}(&mut {})", fn_name, type_name::<T>());
let hash = calc_fn_spec(fn_name, [TypeId::of::<T>()].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<A: Variant + Clone, B: Variant + Clone, R>(
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<Dynamic, Box<EvalAltResult>>
+ 'static,
#[cfg(feature = "sync")] map_result: impl Fn(R, Position) -> Result<Dynamic, Box<EvalAltResult>>
+ Send
+ Sync
+ 'static,
) {
//println!("register {}({}, {})", fn_name, type_name::<A>(), type_name::<B>());
let hash = calc_fn_spec(
fn_name,
[TypeId::of::<A>(), TypeId::of::<B>()].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<A: Variant + Clone, B: Variant + Clone, R>(
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<Dynamic, Box<EvalAltResult>>
+ 'static,
#[cfg(feature = "sync")] map_result: impl Fn(R, Position) -> Result<Dynamic, Box<EvalAltResult>>
+ Send
+ Sync
+ 'static,
) {
//println!("register {}(&mut {}, {})", fn_name, type_name::<A>(), type_name::<B>());
let hash = calc_fn_spec(
fn_name,
[TypeId::of::<A>(), TypeId::of::<B>()].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<A: Variant + Clone, B: Variant + Clone, C: Variant + Clone, R>(
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<Dynamic, Box<EvalAltResult>>
+ 'static,
#[cfg(feature = "sync")] map_result: impl Fn(R, Position) -> Result<Dynamic, Box<EvalAltResult>>
+ Send
+ Sync
+ 'static,
) {
//println!("register {}({}, {}, {})", fn_name, type_name::<A>(), type_name::<B>(), type_name::<C>());
let hash = calc_fn_spec(
fn_name,
[TypeId::of::<A>(), TypeId::of::<B>(), TypeId::of::<C>()]
.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<A: Variant + Clone, B: Variant + Clone, C: Variant + Clone, R>(
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<Dynamic, Box<EvalAltResult>>
+ 'static,
#[cfg(feature = "sync")] map_result: impl Fn(R, Position) -> Result<Dynamic, Box<EvalAltResult>>
+ Send
+ Sync
+ 'static,
) {
//println!("register {}(&mut {}, {}, {})", fn_name, type_name::<A>(), type_name::<B>(), type_name::<C>());
let hash = calc_fn_spec(
fn_name,
[TypeId::of::<A>(), TypeId::of::<B>(), TypeId::of::<C>()]
.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);
}

View File

@ -1,37 +1,13 @@
use super::arithmetic::ArithmeticPackage; use super::arithmetic::ArithmeticPackage;
use super::create_new_package;
use super::iter_basic::BasicIteratorPackage; use super::iter_basic::BasicIteratorPackage;
use super::logic::LogicPackage; use super::logic::LogicPackage;
use super::string_basic::BasicStringPackage; use super::string_basic::BasicStringPackage;
use super::{Package, PackageLibrary, PackageLibraryStore};
use crate::stdlib::ops::Deref; use crate::def_package;
pub struct CorePackage(PackageLibrary); def_package!(CorePackage:"_Core_ package containing basic facilities.", lib, {
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); ArithmeticPackage::init(lib);
LogicPackage::init(lib); LogicPackage::init(lib);
BasicStringPackage::init(lib); BasicStringPackage::init(lib);
BasicIteratorPackage::init(lib); BasicIteratorPackage::init(lib);
} });
fn get(&self) -> PackageLibrary {
self.0.clone()
}
}

View File

@ -4,37 +4,16 @@ use super::math_basic::BasicMathPackage;
use super::pkg_core::CorePackage; use super::pkg_core::CorePackage;
use super::string_more::MoreStringPackage; use super::string_more::MoreStringPackage;
use super::time_basic::BasicTimePackage; 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); def_package!(StandardPackage:"_Standard_ package containing all built-in features.", lib, {
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); CorePackage::init(lib);
BasicMathPackage::init(lib); BasicMathPackage::init(lib);
#[cfg(not(feature = "no_index"))]
BasicArrayPackage::init(lib); BasicArrayPackage::init(lib);
#[cfg(not(feature = "no_object"))]
BasicMapPackage::init(lib); BasicMapPackage::init(lib);
BasicTimePackage::init(lib); BasicTimePackage::init(lib);
MoreStringPackage::init(lib); MoreStringPackage::init(lib);
} });
fn get(&self) -> PackageLibrary {
self.0.clone()
}
}

View File

@ -1,8 +1,6 @@
use super::{ use super::{reg_binary, reg_binary_mut, reg_none, reg_unary, reg_unary_mut};
create_new_package, reg_binary, reg_binary_mut, reg_none, reg_unary, reg_unary_mut, Package,
PackageLibrary, PackageLibraryStore,
};
use crate::def_package;
use crate::engine::{Array, Map, FUNC_TO_STRING, KEYWORD_DEBUG, KEYWORD_PRINT}; use crate::engine::{Array, Map, FUNC_TO_STRING, KEYWORD_DEBUG, KEYWORD_PRINT};
use crate::fn_register::map_dynamic as map; use crate::fn_register::map_dynamic as map;
use crate::parser::INT; use crate::parser::INT;
@ -10,7 +8,6 @@ use crate::parser::INT;
use crate::stdlib::{ use crate::stdlib::{
fmt::{Debug, Display}, fmt::{Debug, Display},
format, format,
ops::Deref,
}; };
// Register print and debug // Register print and debug
@ -28,64 +25,42 @@ macro_rules! reg_op { ($lib:expr, $op:expr, $func:ident, $($par:ty),*) => {
$(reg_unary_mut($lib, $op, $func::<$par>, map);)* }; $(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;
fn deref(&self) -> &PackageLibrary {
&self.0
}
}
impl Package for BasicStringPackage {
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) {
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_none(lib, KEYWORD_PRINT, || "".to_string(), map);
reg_unary(lib, KEYWORD_PRINT, |_: ()| "".to_string(), map); reg_unary(lib, KEYWORD_PRINT, |_: ()| "".to_string(), map);
reg_unary(lib, FUNC_TO_STRING, |_: ()| "".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); 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);
reg_op!(lib, KEYWORD_DEBUG, to_debug, INT, bool, (), char, String);
#[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i32"))]
#[cfg(not(feature = "only_i64"))] #[cfg(not(feature = "only_i64"))]
{ {
reg_op!(lib, KEYWORD_PRINT, to_string, String, i8, u8, i16, u16); reg_op!(lib, KEYWORD_PRINT, to_string, i8, u8, i16, u16, i32, u32);
reg_op!(lib, FUNC_TO_STRING, to_string, String, i8, u8, i16, u16); reg_op!(lib, FUNC_TO_STRING, to_string, i8, u8, i16, u16, i32, u32);
reg_op!(lib, KEYWORD_PRINT, to_string, String, i32, u32, i64, u64); reg_op!(lib, KEYWORD_DEBUG, to_debug, i8, u8, i16, u16, i32, u32);
reg_op!(lib, FUNC_TO_STRING, to_string, String, i32, u32, i64, u64); reg_op!(lib, KEYWORD_PRINT, to_string, i64, u64, i128, u128);
reg_op!(lib, KEYWORD_PRINT, to_string, String, i128, u128); reg_op!(lib, FUNC_TO_STRING, to_string, i64, u64, i128, u128);
reg_op!(lib, FUNC_TO_STRING, to_string, String, i128, u128); reg_op!(lib, KEYWORD_DEBUG, to_debug, i64, u64, 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"))] #[cfg(not(feature = "no_float"))]
{ {
reg_op!(lib, KEYWORD_PRINT, to_string, String, f32, f64); reg_op!(lib, KEYWORD_PRINT, to_string, f32, f64);
reg_op!(lib, FUNC_TO_STRING, to_string, String, f32, f64); reg_op!(lib, FUNC_TO_STRING, to_string, f32, f64);
reg_op!(lib, KEYWORD_DEBUG, to_debug, String, f32, f64); reg_op!(lib, KEYWORD_DEBUG, to_debug, f32, f64);
} }
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
{ {
reg_op!(lib, KEYWORD_PRINT, to_debug, String, Array); reg_op!(lib, KEYWORD_PRINT, to_debug, Array);
reg_op!(lib, FUNC_TO_STRING, to_debug, String, Array); reg_op!(lib, FUNC_TO_STRING, to_debug, Array);
reg_op!(lib, KEYWORD_DEBUG, to_debug, String, Array); reg_op!(lib, KEYWORD_DEBUG, to_debug, Array);
} }
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
@ -117,8 +92,7 @@ impl Package for BasicStringPackage {
reg_binary_mut( reg_binary_mut(
lib, lib,
"append", "append",
|s: &mut String, add: String| s.push_str(&add), |s: &mut String, s2: String| s.push_str(&s2),
map, map,
); );
} });
}

View File

@ -1,15 +1,12 @@
use super::{ use super::{reg_binary, reg_binary_mut, reg_trinary_mut, reg_unary_mut};
create_new_package, reg_binary, reg_binary_mut, reg_trinary_mut, reg_unary, reg_unary_mut,
Package, PackageLibrary, PackageLibraryStore,
};
use crate::def_package;
use crate::engine::Array; use crate::engine::Array;
use crate::fn_register::map_dynamic as map; use crate::fn_register::map_dynamic as map;
use crate::parser::INT; use crate::parser::INT;
use crate::stdlib::{fmt::Display, ops::Deref}; use crate::stdlib::fmt::Display;
// Register string concatenate functions
fn prepend<T: Display>(x: T, y: String) -> String { fn prepend<T: Display>(x: T, y: String) -> String {
format!("{}{}", x, y) format!("{}{}", x, y)
} }
@ -65,32 +62,11 @@ fn crop_string(s: &mut String, start: INT, len: INT) {
.for_each(|&ch| s.push(ch)); .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),*) => { macro_rules! reg_op { ($lib:expr, $op:expr, $func:ident, $($par:ty),*) => {
$(reg_binary($lib, $op, $func::<$par>, map);)* }; $(reg_binary($lib, $op, $func::<$par>, map);)* };
} }
impl Package for MoreStringPackage { def_package!(MoreStringPackage:"Additional string utilities, including string building.", lib, {
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) {
reg_op!(lib, "+", append, INT, bool, char); reg_op!(lib, "+", append, INT, bool, char);
reg_binary_mut(lib, "+", |x: &mut String, _: ()| x.clone(), map); reg_binary_mut(lib, "+", |x: &mut String, _: ()| x.clone(), map);
@ -255,5 +231,4 @@ impl Package for MoreStringPackage {
}, },
map, map,
); );
} });
}

View File

@ -1,39 +1,16 @@
use super::logic::{eq, gt, gte, lt, lte, ne}; use super::logic::{eq, gt, gte, lt, lte, ne};
use super::math_basic::MAX_INT; use super::math_basic::MAX_INT;
use super::{ use super::{reg_binary, reg_none, reg_unary};
create_new_package, reg_binary, reg_none, reg_unary, Package, PackageLibrary,
PackageLibraryStore,
};
use crate::def_package;
use crate::fn_register::{map_dynamic as map, map_result as result}; use crate::fn_register::{map_dynamic as map, map_result as result};
use crate::parser::INT; use crate::parser::INT;
use crate::result::EvalAltResult; use crate::result::EvalAltResult;
use crate::token::Position; 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, {
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"))] #[cfg(not(feature = "no_std"))]
{ {
// Register date/time functions // Register date/time functions
@ -77,10 +54,7 @@ impl Package for BasicTimePackage {
{ {
if seconds > (MAX_INT as u64) { if seconds > (MAX_INT as u64) {
return Err(EvalAltResult::ErrorArithmetic( return Err(EvalAltResult::ErrorArithmetic(
format!( format!("Integer overflow for timestamp duration: {}", seconds),
"Integer overflow for timestamp duration: {}",
seconds
),
Position::none(), Position::none(),
)); ));
} }
@ -125,5 +99,4 @@ impl Package for BasicTimePackage {
}, },
result, result,
); );
} });
}

363
src/packages/utils.rs Normal file
View File

@ -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<Dynamic, EvalAltResult>
/// });
/// ```
///
/// 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<EvalAltResult>> {
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<Dynamic, EvalAltResult>`.
pub fn reg_none<R>(
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<Dynamic, Box<EvalAltResult>>
+ 'static,
#[cfg(feature = "sync")] map_result: impl Fn(R, Position) -> Result<Dynamic, Box<EvalAltResult>>
+ 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<Dynamic, EvalAltResult>`.
pub fn reg_unary<T: Variant + Clone, R>(
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<Dynamic, Box<EvalAltResult>>
+ 'static,
#[cfg(feature = "sync")] map_result: impl Fn(R, Position) -> Result<Dynamic, Box<EvalAltResult>>
+ Send
+ Sync
+ 'static,
) {
//println!("register {}({})", fn_name, crate::std::any::type_name::<T>());
let hash = calc_fn_hash(fn_name, [TypeId::of::<T>()].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<Dynamic, EvalAltResult>`.
pub fn reg_unary_mut<T: Variant + Clone, R>(
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<Dynamic, Box<EvalAltResult>>
+ 'static,
#[cfg(feature = "sync")] map_result: impl Fn(R, Position) -> Result<Dynamic, Box<EvalAltResult>>
+ Send
+ Sync
+ 'static,
) {
//println!("register {}(&mut {})", fn_name, crate::std::any::type_name::<T>());
let hash = calc_fn_hash(fn_name, [TypeId::of::<T>()].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<Dynamic, Box<EvalAltResult>>
+ 'static,
#[cfg(feature = "sync")] map_result: impl Fn(R, Position) -> Result<Dynamic, Box<EvalAltResult>>
+ Send
+ Sync
+ 'static,
) {
//println!("register {}({}, {})", fn_name, crate::std::any::type_name::<A>(), crate::std::any::type_name::<B>());
let hash = calc_fn_hash(
fn_name,
[TypeId::of::<A>(), TypeId::of::<B>()].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::<A>().unwrap());
let y: B = drain.next().unwrap().downcast_mut::<B>().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<Dynamic, EvalAltResult>`.
pub fn reg_binary<A: Variant + Clone, B: Variant + Clone, R>(
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<Dynamic, Box<EvalAltResult>>
+ 'static,
#[cfg(feature = "sync")] map_result: impl Fn(R, Position) -> Result<Dynamic, Box<EvalAltResult>>
+ Send
+ Sync
+ 'static,
) {
//println!("register {}({}, {})", fn_name, crate::std::any::type_name::<A>(), crate::std::any::type_name::<B>());
let hash = calc_fn_hash(
fn_name,
[TypeId::of::<A>(), TypeId::of::<B>()].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<Dynamic, EvalAltResult>`.
pub fn reg_binary_mut<A: Variant + Clone, B: Variant + Clone, R>(
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<Dynamic, Box<EvalAltResult>>
+ 'static,
#[cfg(feature = "sync")] map_result: impl Fn(R, Position) -> Result<Dynamic, Box<EvalAltResult>>
+ Send
+ Sync
+ 'static,
) {
//println!("register {}(&mut {}, {})", fn_name, crate::std::any::type_name::<A>(), crate::std::any::type_name::<B>());
let hash = calc_fn_hash(
fn_name,
[TypeId::of::<A>(), TypeId::of::<B>()].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<Dynamic, EvalAltResult>`.
pub fn reg_trinary<A: Variant + Clone, B: Variant + Clone, C: Variant + Clone, R>(
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<Dynamic, Box<EvalAltResult>>
+ 'static,
#[cfg(feature = "sync")] map_result: impl Fn(R, Position) -> Result<Dynamic, Box<EvalAltResult>>
+ Send
+ Sync
+ 'static,
) {
//println!("register {}({}, {}, {})", fn_name, crate::std::any::type_name::<A>(), crate::std::any::type_name::<B>(), crate::std::any::type_name::<C>());
let hash = calc_fn_hash(
fn_name,
[TypeId::of::<A>(), TypeId::of::<B>(), TypeId::of::<C>()]
.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<Dynamic, EvalAltResult>`.
pub fn reg_trinary_mut<A: Variant + Clone, B: Variant + Clone, C: Variant + Clone, R>(
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<Dynamic, Box<EvalAltResult>>
+ 'static,
#[cfg(feature = "sync")] map_result: impl Fn(R, Position) -> Result<Dynamic, Box<EvalAltResult>>
+ Send
+ Sync
+ 'static,
) {
//println!("register {}(&mut {}, {}, {})", fn_name, crate::std::any::type_name::<A>(), crate::std::any::type_name::<B>(), crate::std::any::type_name::<C>());
let hash = calc_fn_hash(
fn_name,
[TypeId::of::<A>(), TypeId::of::<B>(), TypeId::of::<C>()]
.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);
}