Use hashed lookup for module-qualified functions and variables.
This commit is contained in:
parent
7f6ce29447
commit
5f12391ec6
@ -3,14 +3,15 @@
|
||||
use crate::any::{Dynamic, Union};
|
||||
use crate::calc_fn_hash;
|
||||
use crate::error::ParseErrorType;
|
||||
use crate::module::ModuleRef;
|
||||
use crate::optimize::OptimizationLevel;
|
||||
use crate::packages::{
|
||||
CorePackage, Package, PackageLibrary, PackageStore, PackagesCollection, StandardPackage,
|
||||
};
|
||||
use crate::parser::{Expr, FnDef, ModuleRef, ReturnType, Stmt, AST};
|
||||
use crate::parser::{Expr, FnDef, ReturnType, Stmt, AST};
|
||||
use crate::result::EvalAltResult;
|
||||
use crate::scope::{EntryType as ScopeEntryType, Scope};
|
||||
use crate::token::Position;
|
||||
use crate::token::{Position, Token};
|
||||
use crate::utils::{calc_fn_def, StaticVec};
|
||||
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
@ -21,7 +22,7 @@ use crate::stdlib::{
|
||||
boxed::Box,
|
||||
collections::HashMap,
|
||||
format,
|
||||
iter::once,
|
||||
iter::{empty, once},
|
||||
mem,
|
||||
num::NonZeroUsize,
|
||||
ops::{Deref, DerefMut},
|
||||
@ -392,14 +393,14 @@ fn default_print(s: &str) {
|
||||
fn search_scope<'a>(
|
||||
scope: &'a mut Scope,
|
||||
name: &str,
|
||||
modules: &ModuleRef,
|
||||
modules: &Option<Box<ModuleRef>>,
|
||||
index: Option<NonZeroUsize>,
|
||||
pos: Position,
|
||||
) -> Result<(&'a mut Dynamic, ScopeEntryType), Box<EvalAltResult>> {
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
{
|
||||
if let Some(modules) = modules {
|
||||
let (id, root_pos) = modules.get(0); // First module
|
||||
let (id, root_pos) = modules.get(0);
|
||||
|
||||
let module = if let Some(index) = index {
|
||||
scope
|
||||
@ -409,12 +410,15 @@ fn search_scope<'a>(
|
||||
.unwrap()
|
||||
} else {
|
||||
scope.find_module(id).ok_or_else(|| {
|
||||
Box::new(EvalAltResult::ErrorModuleNotFound(id.into(), *root_pos))
|
||||
Box::new(EvalAltResult::ErrorModuleNotFound(
|
||||
id.to_string(),
|
||||
*root_pos,
|
||||
))
|
||||
})?
|
||||
};
|
||||
|
||||
return Ok((
|
||||
module.get_qualified_var_mut(name, modules.as_ref(), pos)?,
|
||||
module.get_qualified_var_mut(name, modules.key(), pos)?,
|
||||
// Module variables are constant
|
||||
ScopeEntryType::Constant,
|
||||
));
|
||||
@ -527,7 +531,7 @@ impl Engine {
|
||||
}
|
||||
|
||||
// Search built-in's and external functions
|
||||
let fn_spec = calc_fn_hash(fn_name, args.iter().map(|a| a.type_id()));
|
||||
let fn_spec = calc_fn_hash(empty(), fn_name, args.iter().map(|a| a.type_id()));
|
||||
|
||||
if let Some(func) = self
|
||||
.base_package
|
||||
@ -676,7 +680,7 @@ impl Engine {
|
||||
|
||||
// Has a system function an override?
|
||||
fn has_override(&self, state: &State, name: &str) -> bool {
|
||||
let hash = calc_fn_hash(name, once(TypeId::of::<String>()));
|
||||
let hash = calc_fn_hash(empty(), name, once(TypeId::of::<String>()));
|
||||
|
||||
// First check registered functions
|
||||
self.base_package.contains_function(hash)
|
||||
@ -1176,8 +1180,8 @@ impl Engine {
|
||||
Expr::CharConstant(c, _) => Ok((*c).into()),
|
||||
Expr::Variable(id, modules, index, pos) => {
|
||||
let index = if state.always_search { None } else { *index };
|
||||
let val = search_scope(scope, id, modules, index, *pos)?;
|
||||
Ok(val.0.clone())
|
||||
let (val, _) = search_scope(scope, id, modules, index, *pos)?;
|
||||
Ok(val.clone())
|
||||
}
|
||||
Expr::Property(_, _) => unreachable!(),
|
||||
|
||||
@ -1190,18 +1194,19 @@ impl Engine {
|
||||
|
||||
match lhs.as_ref() {
|
||||
// name = rhs
|
||||
Expr::Variable(name, modules, index, pos) => {
|
||||
Expr::Variable(id, modules, index, pos) => {
|
||||
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(), *pos),
|
||||
let (value_ptr, typ) = search_scope(scope, id, modules, index, *pos)?;
|
||||
match typ {
|
||||
ScopeEntryType::Constant => Err(Box::new(
|
||||
EvalAltResult::ErrorAssignmentToConstant(id.to_string(), *pos),
|
||||
)),
|
||||
(value_ptr, ScopeEntryType::Normal) => {
|
||||
ScopeEntryType::Normal => {
|
||||
*value_ptr = rhs_val;
|
||||
Ok(Default::default())
|
||||
}
|
||||
// End variable cannot be a module
|
||||
(_, ScopeEntryType::Module) => unreachable!(),
|
||||
ScopeEntryType::Module => unreachable!(),
|
||||
}
|
||||
}
|
||||
// idx_lhs[idx_expr] = rhs
|
||||
@ -1322,9 +1327,13 @@ impl Engine {
|
||||
self.call_script_fn(None, state, fn_def, &mut args, *pos, level)
|
||||
} else {
|
||||
// Then search in Rust functions
|
||||
let hash = calc_fn_hash(fn_name, args.iter().map(|a| a.type_id()));
|
||||
let hash = calc_fn_hash(
|
||||
modules.iter().map(|(m, _)| m.as_str()),
|
||||
fn_name,
|
||||
args.iter().map(|a| a.type_id()),
|
||||
);
|
||||
|
||||
match module.get_qualified_fn(fn_name, hash, modules, *pos) {
|
||||
match module.get_qualified_fn(fn_name, hash, *pos) {
|
||||
Ok(func) => func(&mut args, *pos),
|
||||
Err(_) if def_val.is_some() => Ok(def_val.as_deref().unwrap().clone()),
|
||||
Err(err) => Err(err),
|
||||
|
@ -8,7 +8,7 @@ use crate::result::EvalAltResult;
|
||||
use crate::token::Position;
|
||||
use crate::utils::calc_fn_spec;
|
||||
|
||||
use crate::stdlib::{any::TypeId, boxed::Box, mem, string::ToString};
|
||||
use crate::stdlib::{any::TypeId, boxed::Box, iter::empty, mem, string::ToString};
|
||||
|
||||
/// A trait to register custom functions with the `Engine`.
|
||||
pub trait RegisterFn<FN, ARGS, RET> {
|
||||
@ -220,7 +220,7 @@ macro_rules! def_register {
|
||||
fn register_fn(&mut self, name: &str, f: FN) {
|
||||
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());
|
||||
let hash = calc_fn_spec(empty(), name, [$(TypeId::of::<$par>()),*].iter().cloned());
|
||||
self.base_package.functions.insert(hash, Box::new(func));
|
||||
}
|
||||
}
|
||||
@ -238,7 +238,7 @@ macro_rules! def_register {
|
||||
fn register_dynamic_fn(&mut self, name: &str, f: FN) {
|
||||
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());
|
||||
let hash = calc_fn_spec(empty(), name, [$(TypeId::of::<$par>()),*].iter().cloned());
|
||||
self.base_package.functions.insert(hash, Box::new(func));
|
||||
}
|
||||
}
|
||||
@ -257,7 +257,7 @@ macro_rules! def_register {
|
||||
fn register_result_fn(&mut self, name: &str, f: FN) {
|
||||
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());
|
||||
let hash = calc_fn_spec(empty(), name, [$(TypeId::of::<$par>()),*].iter().cloned());
|
||||
self.base_package.functions.insert(hash, Box::new(func));
|
||||
}
|
||||
}
|
||||
|
250
src/module.rs
250
src/module.rs
@ -7,21 +7,29 @@ use crate::engine::{Engine, FnAny, FnCallArgs, FunctionsLib};
|
||||
use crate::parser::{FnDef, AST};
|
||||
use crate::result::EvalAltResult;
|
||||
use crate::scope::{Entry as ScopeEntry, EntryType as ScopeEntryType, Scope};
|
||||
use crate::token::Position;
|
||||
use crate::token::Token;
|
||||
use crate::token::{Position, Token};
|
||||
use crate::utils::StaticVec;
|
||||
|
||||
use crate::stdlib::{
|
||||
any::TypeId,
|
||||
boxed::Box,
|
||||
collections::HashMap,
|
||||
fmt, mem,
|
||||
fmt,
|
||||
iter::{empty, once},
|
||||
mem,
|
||||
ops::{Deref, DerefMut},
|
||||
rc::Rc,
|
||||
string::{String, ToString},
|
||||
sync::Arc,
|
||||
vec,
|
||||
vec::Vec,
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "sync"))]
|
||||
type NativeFunction = Rc<Box<FnAny>>;
|
||||
#[cfg(feature = "sync")]
|
||||
type NativeFunction = Arc<Box<FnAny>>;
|
||||
|
||||
/// A trait that encapsulates a module resolution service.
|
||||
pub trait ModuleResolver {
|
||||
/// Resolve a module based on a path string.
|
||||
@ -44,15 +52,18 @@ type FuncReturn<T> = Result<T, Box<EvalAltResult>>;
|
||||
pub struct Module {
|
||||
/// Sub-modules.
|
||||
modules: HashMap<String, Module>,
|
||||
/// Module variables, including sub-modules.
|
||||
|
||||
/// Module variables.
|
||||
variables: HashMap<String, Dynamic>,
|
||||
|
||||
/// Flattened collection of all module variables, including those in sub-modules.
|
||||
all_variables: HashMap<u64, Dynamic>,
|
||||
|
||||
/// External Rust functions.
|
||||
#[cfg(not(feature = "sync"))]
|
||||
functions: HashMap<u64, Rc<Box<FnAny>>>,
|
||||
/// External Rust functions.
|
||||
#[cfg(feature = "sync")]
|
||||
functions: HashMap<u64, Arc<Box<FnAny>>>,
|
||||
functions: HashMap<u64, (String, Vec<TypeId>, NativeFunction)>,
|
||||
|
||||
/// Flattened collection of all external Rust functions, including those in sub-modules.
|
||||
all_functions: HashMap<u64, NativeFunction>,
|
||||
|
||||
/// Script-defined functions.
|
||||
fn_lib: FunctionsLib,
|
||||
@ -113,7 +124,7 @@ impl Module {
|
||||
/// assert_eq!(module.get_var_value::<i64>("answer").unwrap(), 42);
|
||||
/// ```
|
||||
pub fn get_var_value<T: Variant + Clone>(&self, name: &str) -> Option<T> {
|
||||
self.get_var(name).and_then(|v| v.try_cast::<T>())
|
||||
self.get_var(name).and_then(Dynamic::try_cast::<T>)
|
||||
}
|
||||
|
||||
/// Get a module variable as a `Dynamic`.
|
||||
@ -131,11 +142,6 @@ impl Module {
|
||||
self.variables.get(name).cloned()
|
||||
}
|
||||
|
||||
/// Get a mutable reference to a module variable.
|
||||
pub fn get_var_mut(&mut self, name: &str) -> Option<&mut Dynamic> {
|
||||
self.variables.get_mut(name)
|
||||
}
|
||||
|
||||
/// Set a variable into the module.
|
||||
///
|
||||
/// If there is an existing variable of the same name, it is replaced.
|
||||
@ -154,16 +160,17 @@ impl Module {
|
||||
}
|
||||
|
||||
/// Get a mutable reference to a modules-qualified variable.
|
||||
///
|
||||
/// The `u64` hash is calculated by the function `crate::calc_fn_hash`.
|
||||
pub(crate) fn get_qualified_var_mut(
|
||||
&mut self,
|
||||
name: &str,
|
||||
modules: &StaticVec<(String, Position)>,
|
||||
hash: u64,
|
||||
pos: Position,
|
||||
) -> Result<&mut Dynamic, Box<EvalAltResult>> {
|
||||
Ok(self
|
||||
.get_qualified_module_mut(modules)?
|
||||
.get_var_mut(name)
|
||||
.ok_or_else(|| Box::new(EvalAltResult::ErrorVariableNotFound(name.into(), pos)))?)
|
||||
self.all_variables
|
||||
.get_mut(&hash)
|
||||
.ok_or_else(|| Box::new(EvalAltResult::ErrorVariableNotFound(name.to_string(), pos)))
|
||||
}
|
||||
|
||||
/// Does a sub-module exist in the module?
|
||||
@ -273,13 +280,15 @@ impl Module {
|
||||
/// Set a Rust function into the module, returning a hash key.
|
||||
///
|
||||
/// If there is an existing Rust function of the same hash, it is replaced.
|
||||
pub fn set_fn(&mut self, fn_name: &str, params: &[TypeId], func: Box<FnAny>) -> u64 {
|
||||
let hash = calc_fn_hash(fn_name, params.iter().cloned());
|
||||
pub fn set_fn(&mut self, fn_name: String, params: Vec<TypeId>, func: Box<FnAny>) -> u64 {
|
||||
let hash = calc_fn_hash(empty(), &fn_name, params.iter().cloned());
|
||||
|
||||
#[cfg(not(feature = "sync"))]
|
||||
self.functions.insert(hash, Rc::new(func));
|
||||
self.functions
|
||||
.insert(hash, (fn_name, params, Rc::new(func)));
|
||||
#[cfg(feature = "sync")]
|
||||
self.functions.insert(hash, Arc::new(func));
|
||||
self.functions
|
||||
.insert(hash, (fn_name, params, Arc::new(func)));
|
||||
|
||||
hash
|
||||
}
|
||||
@ -297,9 +306,9 @@ impl Module {
|
||||
/// let hash = module.set_fn_0("calc", || Ok(42_i64));
|
||||
/// assert!(module.get_fn(hash).is_some());
|
||||
/// ```
|
||||
pub fn set_fn_0<T: Into<Dynamic>>(
|
||||
pub fn set_fn_0<K: Into<String>, T: Into<Dynamic>>(
|
||||
&mut self,
|
||||
fn_name: &str,
|
||||
fn_name: K,
|
||||
#[cfg(not(feature = "sync"))] func: impl Fn() -> FuncReturn<T> + 'static,
|
||||
#[cfg(feature = "sync")] func: impl Fn() -> FuncReturn<T> + Send + Sync + 'static,
|
||||
) -> u64 {
|
||||
@ -308,8 +317,8 @@ impl Module {
|
||||
.map(|v| v.into())
|
||||
.map_err(|err| EvalAltResult::set_position(err, pos))
|
||||
};
|
||||
let arg_types = &[];
|
||||
self.set_fn(fn_name, arg_types, Box::new(f))
|
||||
let arg_types = vec![];
|
||||
self.set_fn(fn_name.into(), arg_types, Box::new(f))
|
||||
}
|
||||
|
||||
/// Set a Rust function taking one parameter into the module, returning a hash key.
|
||||
@ -325,9 +334,9 @@ impl Module {
|
||||
/// let hash = module.set_fn_1("calc", |x: i64| Ok(x + 1));
|
||||
/// assert!(module.get_fn(hash).is_some());
|
||||
/// ```
|
||||
pub fn set_fn_1<A: Variant + Clone, T: Into<Dynamic>>(
|
||||
pub fn set_fn_1<K: Into<String>, A: Variant + Clone, T: Into<Dynamic>>(
|
||||
&mut self,
|
||||
fn_name: &str,
|
||||
fn_name: K,
|
||||
#[cfg(not(feature = "sync"))] func: impl Fn(A) -> FuncReturn<T> + 'static,
|
||||
#[cfg(feature = "sync")] func: impl Fn(A) -> FuncReturn<T> + Send + Sync + 'static,
|
||||
) -> u64 {
|
||||
@ -336,8 +345,8 @@ impl Module {
|
||||
.map(|v| v.into())
|
||||
.map_err(|err| EvalAltResult::set_position(err, pos))
|
||||
};
|
||||
let arg_types = &[TypeId::of::<A>()];
|
||||
self.set_fn(fn_name, arg_types, Box::new(f))
|
||||
let arg_types = vec![TypeId::of::<A>()];
|
||||
self.set_fn(fn_name.into(), arg_types, Box::new(f))
|
||||
}
|
||||
|
||||
/// Set a Rust function taking one mutable parameter into the module, returning a hash key.
|
||||
@ -353,9 +362,9 @@ impl Module {
|
||||
/// let hash = module.set_fn_1_mut("calc", |x: &mut i64| { *x += 1; Ok(*x) });
|
||||
/// assert!(module.get_fn(hash).is_some());
|
||||
/// ```
|
||||
pub fn set_fn_1_mut<A: Variant + Clone, T: Into<Dynamic>>(
|
||||
pub fn set_fn_1_mut<K: Into<String>, A: Variant + Clone, T: Into<Dynamic>>(
|
||||
&mut self,
|
||||
fn_name: &str,
|
||||
fn_name: K,
|
||||
#[cfg(not(feature = "sync"))] func: impl Fn(&mut A) -> FuncReturn<T> + 'static,
|
||||
#[cfg(feature = "sync")] func: impl Fn(&mut A) -> FuncReturn<T> + Send + Sync + 'static,
|
||||
) -> u64 {
|
||||
@ -364,8 +373,8 @@ impl Module {
|
||||
.map(|v| v.into())
|
||||
.map_err(|err| EvalAltResult::set_position(err, pos))
|
||||
};
|
||||
let arg_types = &[TypeId::of::<A>()];
|
||||
self.set_fn(fn_name, arg_types, Box::new(f))
|
||||
let arg_types = vec![TypeId::of::<A>()];
|
||||
self.set_fn(fn_name.into(), arg_types, Box::new(f))
|
||||
}
|
||||
|
||||
/// Set a Rust function taking two parameters into the module, returning a hash key.
|
||||
@ -383,9 +392,9 @@ impl Module {
|
||||
/// });
|
||||
/// assert!(module.get_fn(hash).is_some());
|
||||
/// ```
|
||||
pub fn set_fn_2<A: Variant + Clone, B: Variant + Clone, T: Into<Dynamic>>(
|
||||
pub fn set_fn_2<K: Into<String>, A: Variant + Clone, B: Variant + Clone, T: Into<Dynamic>>(
|
||||
&mut self,
|
||||
fn_name: &str,
|
||||
fn_name: K,
|
||||
#[cfg(not(feature = "sync"))] func: impl Fn(A, B) -> FuncReturn<T> + 'static,
|
||||
#[cfg(feature = "sync")] func: impl Fn(A, B) -> FuncReturn<T> + Send + Sync + 'static,
|
||||
) -> u64 {
|
||||
@ -397,8 +406,8 @@ impl Module {
|
||||
.map(|v| v.into())
|
||||
.map_err(|err| EvalAltResult::set_position(err, pos))
|
||||
};
|
||||
let arg_types = &[TypeId::of::<A>(), TypeId::of::<B>()];
|
||||
self.set_fn(fn_name, arg_types, Box::new(f))
|
||||
let arg_types = vec![TypeId::of::<A>(), TypeId::of::<B>()];
|
||||
self.set_fn(fn_name.into(), arg_types, Box::new(f))
|
||||
}
|
||||
|
||||
/// Set a Rust function taking two parameters (the first one mutable) into the module,
|
||||
@ -415,9 +424,14 @@ impl Module {
|
||||
/// });
|
||||
/// assert!(module.get_fn(hash).is_some());
|
||||
/// ```
|
||||
pub fn set_fn_2_mut<A: Variant + Clone, B: Variant + Clone, T: Into<Dynamic>>(
|
||||
pub fn set_fn_2_mut<
|
||||
K: Into<String>,
|
||||
A: Variant + Clone,
|
||||
B: Variant + Clone,
|
||||
T: Into<Dynamic>,
|
||||
>(
|
||||
&mut self,
|
||||
fn_name: &str,
|
||||
fn_name: K,
|
||||
#[cfg(not(feature = "sync"))] func: impl Fn(&mut A, B) -> FuncReturn<T> + 'static,
|
||||
#[cfg(feature = "sync")] func: impl Fn(&mut A, B) -> FuncReturn<T> + Send + Sync + 'static,
|
||||
) -> u64 {
|
||||
@ -429,8 +443,8 @@ impl Module {
|
||||
.map(|v| v.into())
|
||||
.map_err(|err| EvalAltResult::set_position(err, pos))
|
||||
};
|
||||
let arg_types = &[TypeId::of::<A>(), TypeId::of::<B>()];
|
||||
self.set_fn(fn_name, arg_types, Box::new(f))
|
||||
let arg_types = vec![TypeId::of::<A>(), TypeId::of::<B>()];
|
||||
self.set_fn(fn_name.into(), arg_types, Box::new(f))
|
||||
}
|
||||
|
||||
/// Set a Rust function taking three parameters into the module, returning a hash key.
|
||||
@ -449,13 +463,14 @@ impl Module {
|
||||
/// assert!(module.get_fn(hash).is_some());
|
||||
/// ```
|
||||
pub fn set_fn_3<
|
||||
K: Into<String>,
|
||||
A: Variant + Clone,
|
||||
B: Variant + Clone,
|
||||
C: Variant + Clone,
|
||||
T: Into<Dynamic>,
|
||||
>(
|
||||
&mut self,
|
||||
fn_name: &str,
|
||||
fn_name: K,
|
||||
#[cfg(not(feature = "sync"))] func: impl Fn(A, B, C) -> FuncReturn<T> + 'static,
|
||||
#[cfg(feature = "sync")] func: impl Fn(A, B, C) -> FuncReturn<T> + Send + Sync + 'static,
|
||||
) -> u64 {
|
||||
@ -468,8 +483,8 @@ impl Module {
|
||||
.map(|v| v.into())
|
||||
.map_err(|err| EvalAltResult::set_position(err, pos))
|
||||
};
|
||||
let arg_types = &[TypeId::of::<A>(), TypeId::of::<B>(), TypeId::of::<C>()];
|
||||
self.set_fn(fn_name, arg_types, Box::new(f))
|
||||
let arg_types = vec![TypeId::of::<A>(), TypeId::of::<B>(), TypeId::of::<C>()];
|
||||
self.set_fn(fn_name.into(), arg_types, Box::new(f))
|
||||
}
|
||||
|
||||
/// Set a Rust function taking three parameters (the first one mutable) into the module,
|
||||
@ -489,13 +504,14 @@ impl Module {
|
||||
/// assert!(module.get_fn(hash).is_some());
|
||||
/// ```
|
||||
pub fn set_fn_3_mut<
|
||||
K: Into<String>,
|
||||
A: Variant + Clone,
|
||||
B: Variant + Clone,
|
||||
C: Variant + Clone,
|
||||
T: Into<Dynamic>,
|
||||
>(
|
||||
&mut self,
|
||||
fn_name: &str,
|
||||
fn_name: K,
|
||||
#[cfg(not(feature = "sync"))] func: impl Fn(&mut A, B, C) -> FuncReturn<T> + 'static,
|
||||
#[cfg(feature = "sync")] func: impl Fn(&mut A, B, C) -> FuncReturn<T> + Send + Sync + 'static,
|
||||
) -> u64 {
|
||||
@ -508,8 +524,8 @@ impl Module {
|
||||
.map(|v| v.into())
|
||||
.map_err(|err| EvalAltResult::set_position(err, pos))
|
||||
};
|
||||
let arg_types = &[TypeId::of::<A>(), TypeId::of::<B>(), TypeId::of::<C>()];
|
||||
self.set_fn(fn_name, arg_types, Box::new(f))
|
||||
let arg_types = vec![TypeId::of::<A>(), TypeId::of::<B>(), TypeId::of::<C>()];
|
||||
self.set_fn(fn_name.into(), arg_types, Box::new(f))
|
||||
}
|
||||
|
||||
/// Get a Rust function.
|
||||
@ -527,7 +543,7 @@ impl Module {
|
||||
/// assert!(module.get_fn(hash).is_some());
|
||||
/// ```
|
||||
pub fn get_fn(&self, hash: u64) -> Option<&Box<FnAny>> {
|
||||
self.functions.get(&hash).map(|v| v.as_ref())
|
||||
self.functions.get(&hash).map(|(_, _, v)| v.as_ref())
|
||||
}
|
||||
|
||||
/// Get a modules-qualified function.
|
||||
@ -538,24 +554,12 @@ impl Module {
|
||||
&mut self,
|
||||
name: &str,
|
||||
hash: u64,
|
||||
modules: &StaticVec<(String, Position)>,
|
||||
pos: Position,
|
||||
) -> Result<&Box<FnAny>, Box<EvalAltResult>> {
|
||||
Ok(self
|
||||
.get_qualified_module_mut(modules)?
|
||||
.get_fn(hash)
|
||||
.ok_or_else(|| {
|
||||
let mut fn_name: String = Default::default();
|
||||
|
||||
modules.iter().for_each(|(n, _)| {
|
||||
fn_name.push_str(n);
|
||||
fn_name.push_str(Token::DoubleColon.syntax().as_ref());
|
||||
});
|
||||
|
||||
fn_name.push_str(name);
|
||||
|
||||
Box::new(EvalAltResult::ErrorFunctionNotFound(fn_name, pos))
|
||||
})?)
|
||||
self.all_functions
|
||||
.get(&hash)
|
||||
.map(|f| f.as_ref())
|
||||
.ok_or_else(|| Box::new(EvalAltResult::ErrorFunctionNotFound(name.to_string(), pos)))
|
||||
}
|
||||
|
||||
/// Get the script-defined functions.
|
||||
@ -634,6 +638,52 @@ impl Module {
|
||||
|
||||
Ok(module)
|
||||
}
|
||||
|
||||
/// Scan through all the sub-modules in the `Module` build an index of all
|
||||
/// variables and external Rust functions via hashing.
|
||||
pub(crate) fn collect_all_sub_modules(&mut self) {
|
||||
// Collect a particular module.
|
||||
fn collect<'a>(
|
||||
module: &'a mut Module,
|
||||
names: &mut Vec<&'a str>,
|
||||
variables: &mut Vec<(u64, Dynamic)>,
|
||||
functions: &mut Vec<(u64, NativeFunction)>,
|
||||
) {
|
||||
for (n, m) in module.modules.iter_mut() {
|
||||
// Collect all the sub-modules first.
|
||||
names.push(n);
|
||||
collect(m, names, variables, functions);
|
||||
names.pop();
|
||||
}
|
||||
|
||||
// Collect all variables
|
||||
for (var_name, value) in module.variables.iter() {
|
||||
let hash = calc_fn_hash(names.iter().map(|v| *v), var_name, empty());
|
||||
variables.push((hash, value.clone()));
|
||||
}
|
||||
// Collect all functions
|
||||
for (fn_name, params, func) in module.functions.values() {
|
||||
let hash = calc_fn_hash(names.iter().map(|v| *v), fn_name, params.iter().cloned());
|
||||
functions.push((hash, func.clone()));
|
||||
}
|
||||
}
|
||||
|
||||
let mut variables = Vec::new();
|
||||
let mut functions = Vec::new();
|
||||
|
||||
collect(self, &mut vec!["root"], &mut variables, &mut functions);
|
||||
|
||||
self.all_variables.clear();
|
||||
self.all_functions.clear();
|
||||
|
||||
for (key, value) in variables {
|
||||
self.all_variables.insert(key, value);
|
||||
}
|
||||
|
||||
for (key, value) in functions {
|
||||
self.all_functions.insert(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Re-export module resolvers.
|
||||
@ -669,12 +719,18 @@ mod file {
|
||||
/// let mut engine = Engine::new();
|
||||
/// engine.set_module_resolver(Some(resolver));
|
||||
/// ```
|
||||
#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Default)]
|
||||
#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct FileModuleResolver {
|
||||
path: PathBuf,
|
||||
extension: String,
|
||||
}
|
||||
|
||||
impl Default for FileModuleResolver {
|
||||
fn default() -> Self {
|
||||
Self::new_with_path(PathBuf::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl FileModuleResolver {
|
||||
/// Create a new `FileModuleResolver` with a specific base path.
|
||||
///
|
||||
@ -774,6 +830,64 @@ mod file {
|
||||
}
|
||||
}
|
||||
|
||||
/// A chain of module names to qualify a variable or function call.
|
||||
/// A `u64` hash key is kept for quick search purposes.
|
||||
///
|
||||
/// A `StaticVec` is used because most module-level access contains only one level,
|
||||
/// and it is wasteful to always allocate a `Vec` with one element.
|
||||
#[derive(Clone, Hash, Default)]
|
||||
pub struct ModuleRef(StaticVec<(String, Position)>, u64);
|
||||
|
||||
impl fmt::Debug for ModuleRef {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Debug::fmt(&self.0, f)?;
|
||||
|
||||
if self.1 > 0 {
|
||||
write!(f, " -> {}", self.1)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for ModuleRef {
|
||||
type Target = StaticVec<(String, Position)>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for ModuleRef {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ModuleRef {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
for (m, _) in self.0.iter() {
|
||||
write!(f, "{}{}", m, Token::DoubleColon.syntax())?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<StaticVec<(String, Position)>> for ModuleRef {
|
||||
fn from(modules: StaticVec<(String, Position)>) -> Self {
|
||||
Self(modules, 0)
|
||||
}
|
||||
}
|
||||
|
||||
impl ModuleRef {
|
||||
pub fn key(&self) -> u64 {
|
||||
self.1
|
||||
}
|
||||
pub fn set_key(&mut self, key: u64) {
|
||||
self.1 = key
|
||||
}
|
||||
}
|
||||
|
||||
/// Static module resolver.
|
||||
mod stat {
|
||||
use super::*;
|
||||
|
@ -13,6 +13,7 @@ use crate::token::Position;
|
||||
use crate::stdlib::{
|
||||
boxed::Box,
|
||||
collections::HashMap,
|
||||
iter::empty,
|
||||
string::{String, ToString},
|
||||
vec,
|
||||
vec::Vec,
|
||||
@ -117,7 +118,7 @@ fn call_fn(
|
||||
pos: Position,
|
||||
) -> Result<Option<Dynamic>, Box<EvalAltResult>> {
|
||||
// Search built-in's and external functions
|
||||
let hash = calc_fn_hash(fn_name, args.iter().map(|a| a.type_id()));
|
||||
let hash = calc_fn_hash(empty(), fn_name, args.iter().map(|a| a.type_id()));
|
||||
|
||||
base_package
|
||||
.get_function(hash)
|
||||
@ -376,11 +377,7 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr {
|
||||
// id1 = id2 = expr2
|
||||
(id1, id2) => Expr::Assignment(
|
||||
Box::new(id1),
|
||||
Box::new(Expr::Assignment(
|
||||
Box::new(id2),
|
||||
Box::new(optimize_expr(*expr2, state)),
|
||||
pos2,
|
||||
)),
|
||||
Box::new(Expr::Assignment(Box::new(id2), Box::new(optimize_expr(*expr2, state)), pos2)),
|
||||
pos,
|
||||
),
|
||||
},
|
||||
|
@ -9,6 +9,7 @@ use crate::token::Position;
|
||||
use crate::stdlib::{
|
||||
any::TypeId,
|
||||
boxed::Box,
|
||||
iter::empty,
|
||||
mem,
|
||||
string::{String, ToString},
|
||||
};
|
||||
@ -115,7 +116,7 @@ pub fn reg_none<R>(
|
||||
+ Sync
|
||||
+ 'static,
|
||||
) {
|
||||
let hash = calc_fn_hash(fn_name, ([] as [TypeId; 0]).iter().cloned());
|
||||
let hash = calc_fn_hash(empty(), 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)?;
|
||||
@ -165,7 +166,7 @@ pub fn reg_unary<T: Variant + Clone, R>(
|
||||
) {
|
||||
//println!("register {}({})", fn_name, crate::std::any::type_name::<T>());
|
||||
|
||||
let hash = calc_fn_hash(fn_name, [TypeId::of::<T>()].iter().cloned());
|
||||
let hash = calc_fn_hash(empty(), 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)?;
|
||||
@ -225,7 +226,7 @@ pub fn reg_unary_mut<T: Variant + Clone, R>(
|
||||
) {
|
||||
//println!("register {}(&mut {})", fn_name, crate::std::any::type_name::<T>());
|
||||
|
||||
let hash = calc_fn_hash(fn_name, [TypeId::of::<T>()].iter().cloned());
|
||||
let hash = calc_fn_hash(empty(), 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)?;
|
||||
@ -279,6 +280,7 @@ pub fn reg_binary<A: Variant + Clone, B: Variant + Clone, R>(
|
||||
//println!("register {}({}, {})", fn_name, crate::std::any::type_name::<A>(), crate::std::any::type_name::<B>());
|
||||
|
||||
let hash = calc_fn_hash(
|
||||
empty(),
|
||||
fn_name,
|
||||
[TypeId::of::<A>(), TypeId::of::<B>()].iter().cloned(),
|
||||
);
|
||||
@ -343,6 +345,7 @@ pub fn reg_binary_mut<A: Variant + Clone, B: Variant + Clone, R>(
|
||||
//println!("register {}(&mut {}, {})", fn_name, crate::std::any::type_name::<A>(), crate::std::any::type_name::<B>());
|
||||
|
||||
let hash = calc_fn_hash(
|
||||
empty(),
|
||||
fn_name,
|
||||
[TypeId::of::<A>(), TypeId::of::<B>()].iter().cloned(),
|
||||
);
|
||||
@ -381,6 +384,7 @@ pub fn reg_trinary<A: Variant + Clone, B: Variant + Clone, C: Variant + Clone, R
|
||||
//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(
|
||||
empty(),
|
||||
fn_name,
|
||||
[TypeId::of::<A>(), TypeId::of::<B>(), TypeId::of::<C>()]
|
||||
.iter()
|
||||
@ -422,6 +426,7 @@ pub fn reg_trinary_mut<A: Variant + Clone, B: Variant + Clone, C: Variant + Clon
|
||||
//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(
|
||||
empty(),
|
||||
fn_name,
|
||||
[TypeId::of::<A>(), TypeId::of::<B>(), TypeId::of::<C>()]
|
||||
.iter()
|
||||
|
@ -1,8 +1,10 @@
|
||||
//! Main module defining the lexer and parser.
|
||||
|
||||
use crate::any::{Dynamic, Union};
|
||||
use crate::calc_fn_hash;
|
||||
use crate::engine::{Engine, FunctionsLib};
|
||||
use crate::error::{LexError, ParseError, ParseErrorType};
|
||||
use crate::module::ModuleRef;
|
||||
use crate::optimize::{optimize_into_ast, OptimizationLevel};
|
||||
use crate::scope::{EntryType as ScopeEntryType, Scope};
|
||||
use crate::token::{Position, Token, TokenIterator};
|
||||
@ -14,7 +16,7 @@ use crate::stdlib::{
|
||||
char,
|
||||
collections::HashMap,
|
||||
format,
|
||||
iter::Peekable,
|
||||
iter::{empty, Peekable},
|
||||
num::NonZeroUsize,
|
||||
ops::{Add, Deref, DerefMut},
|
||||
rc::Rc,
|
||||
@ -44,11 +46,6 @@ pub type FLOAT = f64;
|
||||
|
||||
type PERR = ParseErrorType;
|
||||
|
||||
/// A chain of module names to qualify a variable or function call.
|
||||
/// A `StaticVec` is used because most module-level access contains only one level,
|
||||
/// and it is wasteful to always allocate a `Vec` with one element.
|
||||
pub type ModuleRef = Option<Box<StaticVec<(String, Position)>>>;
|
||||
|
||||
/// Compiled AST (abstract syntax tree) of a Rhai script.
|
||||
///
|
||||
/// Currently, `AST` is neither `Send` nor `Sync`. Turn on the `sync` feature to make it `Send + Sync`.
|
||||
@ -357,8 +354,13 @@ pub enum Expr {
|
||||
CharConstant(char, Position),
|
||||
/// String constant.
|
||||
StringConstant(String, Position),
|
||||
/// Variable access - (variable name, optional modules, optional index, position)
|
||||
Variable(Box<String>, ModuleRef, Option<NonZeroUsize>, Position),
|
||||
/// Variable access - (variable name, optional modules, hash, optional index, position)
|
||||
Variable(
|
||||
Box<String>,
|
||||
Option<Box<ModuleRef>>,
|
||||
Option<NonZeroUsize>,
|
||||
Position,
|
||||
),
|
||||
/// Property access.
|
||||
Property(String, Position),
|
||||
/// { stmt }
|
||||
@ -368,7 +370,7 @@ pub enum Expr {
|
||||
/// and the function names are predictable, so no need to allocate a new `String`.
|
||||
FnCall(
|
||||
Box<Cow<'static, str>>,
|
||||
ModuleRef,
|
||||
Option<Box<ModuleRef>>,
|
||||
Box<Vec<Expr>>,
|
||||
Option<Box<Dynamic>>,
|
||||
Position,
|
||||
@ -675,7 +677,7 @@ fn parse_call_expr<'a>(
|
||||
input: &mut Peekable<TokenIterator<'a>>,
|
||||
stack: &mut Stack,
|
||||
id: String,
|
||||
modules: ModuleRef,
|
||||
modules: Option<Box<ModuleRef>>,
|
||||
begin: Position,
|
||||
allow_stmt_expr: bool,
|
||||
) -> Result<Expr, Box<ParseError>> {
|
||||
@ -1110,12 +1112,11 @@ fn parse_primary<'a>(
|
||||
if let Some(ref mut modules) = modules {
|
||||
modules.push((*id, pos));
|
||||
} else {
|
||||
index = stack.find_module(id.as_ref());
|
||||
|
||||
let mut vec = StaticVec::new();
|
||||
vec.push((*id, pos));
|
||||
modules = Some(Box::new(vec));
|
||||
|
||||
let root = modules.as_ref().unwrap().iter().next().unwrap();
|
||||
index = stack.find_module(&root.0);
|
||||
modules = Some(Box::new(vec.into()));
|
||||
}
|
||||
|
||||
Expr::Variable(Box::new(id2), modules, index, pos2)
|
||||
@ -1133,6 +1134,15 @@ fn parse_primary<'a>(
|
||||
}
|
||||
}
|
||||
|
||||
match &mut root_expr {
|
||||
// Calculate hash key for module-qualified variables
|
||||
Expr::Variable(id, Some(modules), _, _) => {
|
||||
let hash = calc_fn_hash(modules.iter().map(|(v, _)| v.as_str()), id, empty());
|
||||
modules.set_key(hash);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
Ok(root_expr)
|
||||
}
|
||||
|
||||
@ -1315,7 +1325,7 @@ fn make_dot_expr(
|
||||
}
|
||||
// lhs.module::id - syntax error
|
||||
(_, Expr::Variable(_, Some(modules), _, _)) => {
|
||||
return Err(PERR::PropertyExpected.into_err(modules.iter().next().unwrap().1))
|
||||
return Err(PERR::PropertyExpected.into_err(modules.get(0).1))
|
||||
}
|
||||
// lhs.dot_lhs.dot_rhs
|
||||
(lhs, Expr::Dot(dot_lhs, dot_rhs, dot_pos)) => Expr::Dot(
|
||||
|
@ -175,7 +175,9 @@ impl<'a> Scope<'a> {
|
||||
///
|
||||
/// Modules are used for accessing member variables, functions and plugins under a namespace.
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
pub fn push_module<K: Into<Cow<'a, str>>>(&mut self, name: K, value: Module) {
|
||||
pub fn push_module<K: Into<Cow<'a, str>>>(&mut self, name: K, mut value: Module) {
|
||||
value.collect_all_sub_modules();
|
||||
|
||||
self.push_dynamic_value(
|
||||
name,
|
||||
EntryType::Module,
|
||||
|
20
src/utils.rs
20
src/utils.rs
@ -14,16 +14,26 @@ use crate::stdlib::collections::hash_map::DefaultHasher;
|
||||
#[cfg(feature = "no_std")]
|
||||
use ahash::AHasher;
|
||||
|
||||
/// Calculate a `u64` hash key from a function name and parameter types.
|
||||
/// Calculate a `u64` hash key from a module-qualified 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 {
|
||||
/// Module names are passed in via `&str` references from an iterator.
|
||||
/// Parameter types are passed in via `TypeId` values from an iterator.
|
||||
///
|
||||
/// ### Note
|
||||
///
|
||||
/// The first module name is skipped. Hashing starts from the _second_ module in the chain.
|
||||
pub fn calc_fn_spec<'a>(
|
||||
modules: impl Iterator<Item = &'a str>,
|
||||
fn_name: &str,
|
||||
params: impl Iterator<Item = TypeId>,
|
||||
) -> u64 {
|
||||
#[cfg(feature = "no_std")]
|
||||
let mut s: AHasher = Default::default();
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
let mut s = DefaultHasher::new();
|
||||
|
||||
// We always skip the first module
|
||||
modules.skip(1).for_each(|m| m.hash(&mut s));
|
||||
s.write(fn_name.as_bytes());
|
||||
params.for_each(|t| t.hash(&mut s));
|
||||
s.finish()
|
||||
@ -45,7 +55,7 @@ pub(crate) fn calc_fn_def(fn_name: &str, num_params: usize) -> u64 {
|
||||
///
|
||||
/// This is essentially a knock-off of the [`staticvec`](https://crates.io/crates/staticvec) crate.
|
||||
/// This simplified implementation here is to avoid pulling in another crate.
|
||||
#[derive(Clone, Default)]
|
||||
#[derive(Clone, Hash, Default)]
|
||||
pub struct StaticVec<T: Default + Clone> {
|
||||
/// Total number of values held.
|
||||
len: usize,
|
||||
|
Loading…
Reference in New Issue
Block a user