Move Engine functions and iterators lib to Packages API.

This commit is contained in:
Stephen Chung 2020-05-07 15:25:50 +08:00
parent f3c0609377
commit c607c7c428
5 changed files with 106 additions and 57 deletions

View File

@ -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<T: Variant + Clone, F: IteratorCallback>(&mut self, f: F) {
self.type_iterators.insert(TypeId::of::<T>(), Box::new(f));
self.base_package
.type_iterators
.insert(TypeId::of::<T>(), Box::new(f));
}
/// Register a getter function for a member of a registered type with the `Engine`.

View File

@ -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<FnDef>) -> 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<PackageLibrary>,
/// 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>>,
/// A hashmap containing all iterators known to the engine.
pub(crate) type_iterators: HashMap<TypeId, Box<IteratorFn>>,
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::<String>()));
// 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, ());

View File

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

View File

@ -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<PackageLibrary>,
functions: &HashMap<u64, Box<FnAny>>,
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() {

View File

@ -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<Item = &u64> {
self.functions.keys()
}
/// Get an iterator over the `TypeId` of the type iterators in the `PackageStore`.
pub fn type_iterator_keys(&self) -> impl Iterator<Item = &TypeId> {
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<FnAny>> {
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<IteratorFn>> {
self.type_iterators.get(&id)
}
}
/// Type which `Rc`-wraps a `PackageStore` to facilitate sharing library instances.
@ -70,3 +94,49 @@ pub type PackageLibrary = Rc<PackageStore>;
/// Type which `Arc`-wraps a `PackageStore` to facilitate sharing library instances.
#[cfg(feature = "sync")]
pub type PackageLibrary = Arc<PackageStore>;
#[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<PackageLibrary>,
/// Index of all function keys, pointing to the offset in `packages`.
function_keys: HashMap<u64, usize>,
/// Index of all type iterator `TypeId`'s, pointing to the offset in `packages`.
iterator_types: HashMap<TypeId, usize>,
}
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<FnAny>> {
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<IteratorFn>> {
self.iterator_types
.get(&id)
.and_then(|&index| self.packages[index].type_iterators.get(&id))
}
}