From c607c7c4285023e3cfe7783f7afa8a47e19225e8 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Thu, 7 May 2020 15:25:50 +0800 Subject: [PATCH] Move Engine functions and iterators lib to Packages API. --- src/api.rs | 4 ++- src/engine.rs | 62 ++++++++++++++------------------------ src/fn_register.rs | 6 ++-- src/optimize.rs | 19 +++++------- src/packages/mod.rs | 72 ++++++++++++++++++++++++++++++++++++++++++++- 5 files changed, 106 insertions(+), 57 deletions(-) diff --git a/src/api.rs b/src/api.rs index b5865ed6..68be4e59 100644 --- a/src/api.rs +++ b/src/api.rs @@ -168,7 +168,9 @@ impl Engine { /// Register an iterator adapter for a type with the `Engine`. /// This is an advanced feature. pub fn register_iterator(&mut self, f: F) { - self.type_iterators.insert(TypeId::of::(), Box::new(f)); + self.base_package + .type_iterators + .insert(TypeId::of::(), Box::new(f)); } /// Register a getter function for a member of a registered type with the `Engine`. diff --git a/src/engine.rs b/src/engine.rs index 5969ca59..f10d77a2 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -4,7 +4,9 @@ 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}; +use crate::packages::{ + CorePackage, Package, PackageLibrary, PackageStore, PackagesCollection, StandardPackage, +}; use crate::parser::{Expr, FnDef, ModuleRef, ReturnType, Stmt, AST}; use crate::result::EvalAltResult; use crate::scope::{EntryType as ScopeEntryType, Scope}; @@ -60,13 +62,6 @@ pub const MAX_CALL_STACK_DEPTH: usize = 28; #[cfg(not(debug_assertions))] pub const MAX_CALL_STACK_DEPTH: usize = 256; -#[cfg(not(feature = "only_i32"))] -#[cfg(not(feature = "only_i64"))] -const FUNCTIONS_COUNT: usize = 512; - -#[cfg(any(feature = "only_i32", feature = "only_i64"))] -const FUNCTIONS_COUNT: usize = 256; - pub const KEYWORD_PRINT: &str = "print"; pub const KEYWORD_DEBUG: &str = "debug"; pub const KEYWORD_TYPE_OF: &str = "type_of"; @@ -185,10 +180,6 @@ pub struct FunctionsLib( ); impl FunctionsLib { - /// Create a new `FunctionsLib`. - pub fn new() -> Self { - Default::default() - } /// Create a new `FunctionsLib` from a collection of `FnDef`. pub fn from_vec(vec: Vec) -> Self { FunctionsLib( @@ -270,14 +261,9 @@ impl DerefMut for FunctionsLib { /// Currently, `Engine` is neither `Send` nor `Sync`. Turn on the `sync` feature to make it `Send + Sync`. 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. - /// - /// 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. - pub(crate) type_iterators: HashMap>, + pub(crate) packages: PackagesCollection, + /// A collection of all library packages loaded into the engine. + pub(crate) base_package: PackageStore, /// A module resolution service. #[cfg(not(feature = "no_module"))] @@ -314,8 +300,7 @@ impl Default for Engine { // Create the new scripting Engine let mut engine = Self { packages: Default::default(), - functions: HashMap::with_capacity(FUNCTIONS_COUNT), - type_iterators: Default::default(), + base_package: Default::default(), #[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_std"))] @@ -459,8 +444,7 @@ impl Engine { pub fn new_raw() -> Self { Self { packages: Default::default(), - functions: HashMap::with_capacity(FUNCTIONS_COUNT / 2), - type_iterators: Default::default(), + base_package: Default::default(), #[cfg(not(feature = "no_module"))] module_resolver: None, @@ -490,7 +474,7 @@ impl Engine { /// 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); + self.packages.push(package); } /// Control whether and how the `Engine` will optimize an AST after compilation. @@ -545,12 +529,11 @@ impl Engine { // Search built-in's and external functions 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(|pkg| pkg.functions.contains_key(&fn_spec)) - .and_then(|pkg| pkg.functions.get(&fn_spec)) - }) { + if let Some(func) = self + .base_package + .get_function(fn_spec) + .or_else(|| self.packages.get_function(fn_spec)) + { // Run external function let result = func(args, pos)?; @@ -696,9 +679,9 @@ impl Engine { let hash = calc_fn_hash(name, once(TypeId::of::())); // First check registered functions - self.functions.contains_key(&hash) + self.base_package.contains_function(hash) // Then check packages - || self.packages.iter().any(|p| p.functions.contains_key(&hash)) + || self.packages.contains_function(hash) // Then check script-defined functions || state.has_function(name, 1) } @@ -1211,7 +1194,7 @@ impl Engine { let index = if state.always_search { None } else { *index }; match search_scope(scope, name, modules, index, *pos)? { (_, ScopeEntryType::Constant) => Err(Box::new( - EvalAltResult::ErrorAssignmentToConstant(name.to_string(), *op_pos), + EvalAltResult::ErrorAssignmentToConstant(name.to_string(), *pos), )), (value_ptr, ScopeEntryType::Normal) => { *value_ptr = rhs_val; @@ -1482,12 +1465,11 @@ impl Engine { let arr = self.eval_expr(scope, state, expr, level)?; let tid = arr.type_id(); - if let Some(iter_fn) = self.type_iterators.get(&tid).or_else(|| { - self.packages - .iter() - .find(|pkg| pkg.type_iterators.contains_key(&tid)) - .and_then(|pkg| pkg.type_iterators.get(&tid)) - }) { + if let Some(iter_fn) = self + .base_package + .get_iterator(tid) + .or_else(|| self.packages.get_iterator(tid)) + { // Add the loop variable let var_name = name.as_ref().clone(); scope.push(var_name, ()); diff --git a/src/fn_register.rs b/src/fn_register.rs index 9f798af1..2e9a5325 100644 --- a/src/fn_register.rs +++ b/src/fn_register.rs @@ -221,7 +221,7 @@ macro_rules! def_register { let fn_name = name.to_string(); let func = make_func!(fn_name : f : map_dynamic ; $($par => $clone),*); let hash = calc_fn_spec(name, [$(TypeId::of::<$par>()),*].iter().cloned()); - self.functions.insert(hash, Box::new(func)); + self.base_package.functions.insert(hash, Box::new(func)); } } @@ -239,7 +239,7 @@ macro_rules! def_register { let fn_name = name.to_string(); let func = make_func!(fn_name : f : map_identity ; $($par => $clone),*); let hash = calc_fn_spec(name, [$(TypeId::of::<$par>()),*].iter().cloned()); - self.functions.insert(hash, Box::new(func)); + self.base_package.functions.insert(hash, Box::new(func)); } } @@ -258,7 +258,7 @@ macro_rules! def_register { let fn_name = name.to_string(); let func = make_func!(fn_name : f : map_result ; $($par => $clone),*); let hash = calc_fn_spec(name, [$(TypeId::of::<$par>()),*].iter().cloned()); - self.functions.insert(hash, Box::new(func)); + self.base_package.functions.insert(hash, Box::new(func)); } } diff --git a/src/optimize.rs b/src/optimize.rs index 33765a56..018a210e 100644 --- a/src/optimize.rs +++ b/src/optimize.rs @@ -4,7 +4,7 @@ use crate::engine::{ Engine, FnAny, FnCallArgs, FunctionsLib, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_PRINT, KEYWORD_TYPE_OF, }; -use crate::packages::PackageLibrary; +use crate::packages::{PackageStore, PackagesCollection}; use crate::parser::{map_dynamic_to_expr, Expr, FnDef, ReturnType, Stmt, AST}; use crate::result::EvalAltResult; use crate::scope::{Entry as ScopeEntry, EntryType as ScopeEntryType, Scope}; @@ -110,8 +110,8 @@ impl<'a> State<'a> { /// Call a registered function fn call_fn( - packages: &Vec, - functions: &HashMap>, + packages: &PackagesCollection, + base_package: &PackageStore, fn_name: &str, args: &mut FnCallArgs, pos: Position, @@ -119,14 +119,9 @@ fn call_fn( // Search built-in's and external functions let hash = calc_fn_hash(fn_name, args.iter().map(|a| a.type_id())); - functions - .get(&hash) - .or_else(|| { - packages - .iter() - .find(|p| p.functions.contains_key(&hash)) - .and_then(|p| p.functions.get(&hash)) - }) + base_package + .get_function(hash) + .or_else(|| packages.get_function(hash)) .map(|func| func(args, pos)) .transpose() } @@ -577,7 +572,7 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr { "" }; - call_fn(&state.engine.packages, &state.engine.functions, &id, &mut call_args, pos).ok() + call_fn(&state.engine.packages, &state.engine.base_package, &id, &mut call_args, pos).ok() .and_then(|result| result.or_else(|| { if !arg_for_type_of.is_empty() { diff --git a/src/packages/mod.rs b/src/packages/mod.rs index 3734bd25..c9be4487 100644 --- a/src/packages/mod.rs +++ b/src/packages/mod.rs @@ -2,7 +2,7 @@ use crate::engine::{FnAny, IteratorFn}; -use crate::stdlib::{any::TypeId, boxed::Box, collections::HashMap, rc::Rc, sync::Arc}; +use crate::stdlib::{any::TypeId, boxed::Box, collections::HashMap, rc::Rc, sync::Arc, vec::Vec}; mod arithmetic; mod array_basic; @@ -61,6 +61,30 @@ impl PackageStore { pub fn new() -> Self { Default::default() } + /// Get an iterator over the keys of the functions in the `PackageStore`. + pub fn function_keys(&self) -> impl Iterator { + self.functions.keys() + } + /// Get an iterator over the `TypeId` of the type iterators in the `PackageStore`. + pub fn type_iterator_keys(&self) -> impl Iterator { + self.type_iterators.keys() + } + /// Does the specified function hash key exist in the `PackageStore`? + pub fn contains_function(&self, hash: u64) -> bool { + self.functions.contains_key(&hash) + } + /// Get specified function via its hash key. + pub fn get_function(&self, hash: u64) -> Option<&Box> { + self.functions.get(&hash) + } + /// Does the specified TypeId iterator exist in the `PackageStore`? + pub fn contains_iterator(&self, id: TypeId) -> bool { + self.type_iterators.contains_key(&id) + } + /// Get the specified TypeId iterator. + pub fn get_iterator(&self, id: TypeId) -> Option<&Box> { + self.type_iterators.get(&id) + } } /// Type which `Rc`-wraps a `PackageStore` to facilitate sharing library instances. @@ -70,3 +94,49 @@ pub type PackageLibrary = Rc; /// Type which `Arc`-wraps a `PackageStore` to facilitate sharing library instances. #[cfg(feature = "sync")] pub type PackageLibrary = Arc; + +#[derive(Default)] +/// Type containing a collection of `PackageLibrary` instances. +/// All function and type iterator keys in the loaded packages are indexed for fast access. +pub(crate) struct PackagesCollection { + /// Collection of `PackageLibrary` instances. + packages: Vec, + /// Index of all function keys, pointing to the offset in `packages`. + function_keys: HashMap, + /// Index of all type iterator `TypeId`'s, pointing to the offset in `packages`. + iterator_types: HashMap, +} + +impl PackagesCollection { + /// Add a `PackageLibrary` into the `PackagesCollection`. + pub fn push(&mut self, package: PackageLibrary) { + let index = self.packages.len(); + package.function_keys().for_each(|&hash| { + self.function_keys.insert(hash, index); + }); + package.type_iterator_keys().for_each(|&id| { + self.iterator_types.insert(id, index); + }); + self.packages.push(package); + } + /// Does the specified function hash key exist in the `PackagesCollection`? + pub fn contains_function(&self, hash: u64) -> bool { + self.function_keys.contains_key(&hash) + } + /// Get specified function via its hash key. + pub fn get_function(&self, hash: u64) -> Option<&Box> { + self.function_keys + .get(&hash) + .and_then(|&index| self.packages[index].functions.get(&hash)) + } + /// Does the specified TypeId iterator exist in the `PackagesCollection`? + pub fn contains_iterator(&self, id: TypeId) -> bool { + self.iterator_types.contains_key(&id) + } + /// Get the specified TypeId iterator. + pub fn get_iterator(&self, id: TypeId) -> Option<&Box> { + self.iterator_types + .get(&id) + .and_then(|&index| self.packages[index].type_iterators.get(&id)) + } +}