2020-05-05 06:24:13 +02:00
|
|
|
//! Module defining external-loaded modules for Rhai.
|
|
|
|
|
2020-05-05 09:00:10 +02:00
|
|
|
use crate::any::{Dynamic, Variant};
|
2020-05-05 11:51:40 +02:00
|
|
|
use crate::calc_fn_hash;
|
2020-05-11 07:36:50 +02:00
|
|
|
use crate::engine::{Engine, FunctionsLib};
|
2020-05-11 12:55:58 +02:00
|
|
|
use crate::fn_native::{
|
2020-05-13 13:21:42 +02:00
|
|
|
FnAny, FnCallArgs, IteratorFn, NativeCallable, NativeFunction, NativeFunctionABI,
|
|
|
|
NativeFunctionABI::*, SharedIteratorFunction, SharedNativeFunction,
|
2020-05-11 12:55:58 +02:00
|
|
|
};
|
2020-05-11 07:36:50 +02:00
|
|
|
use crate::parser::{FnAccess, FnDef, SharedFnDef, AST};
|
2020-05-05 09:00:10 +02:00
|
|
|
use crate::result::EvalAltResult;
|
2020-05-06 10:09:44 +02:00
|
|
|
use crate::scope::{Entry as ScopeEntry, EntryType as ScopeEntryType, Scope};
|
2020-05-07 18:19:08 +02:00
|
|
|
use crate::token::{Position, Token};
|
2020-05-08 05:34:56 +02:00
|
|
|
use crate::utils::{StaticVec, EMPTY_TYPE_ID};
|
2020-05-05 06:24:13 +02:00
|
|
|
|
2020-05-05 17:57:25 +02:00
|
|
|
use crate::stdlib::{
|
2020-05-06 10:09:44 +02:00
|
|
|
any::TypeId,
|
2020-05-06 13:45:17 +02:00
|
|
|
boxed::Box,
|
2020-05-06 10:09:44 +02:00
|
|
|
collections::HashMap,
|
2020-05-07 18:19:08 +02:00
|
|
|
fmt,
|
2020-05-08 05:34:56 +02:00
|
|
|
iter::{empty, repeat},
|
2020-05-07 18:19:08 +02:00
|
|
|
mem,
|
2020-05-08 16:38:56 +02:00
|
|
|
num::NonZeroUsize,
|
2020-05-06 10:09:44 +02:00
|
|
|
ops::{Deref, DerefMut},
|
|
|
|
rc::Rc,
|
2020-05-06 13:45:17 +02:00
|
|
|
string::{String, ToString},
|
2020-05-06 10:09:44 +02:00
|
|
|
sync::Arc,
|
2020-05-07 18:19:08 +02:00
|
|
|
vec,
|
|
|
|
vec::Vec,
|
2020-05-05 17:57:25 +02:00
|
|
|
};
|
|
|
|
|
2020-05-11 12:55:58 +02:00
|
|
|
/// Default function access mode.
|
|
|
|
const DEF_ACCESS: FnAccess = FnAccess::Public;
|
|
|
|
|
2020-05-06 10:09:44 +02:00
|
|
|
/// Return type of module-level Rust function.
|
2020-05-13 13:21:42 +02:00
|
|
|
pub type FuncReturn<T> = Result<T, Box<EvalAltResult>>;
|
2020-05-06 10:09:44 +02:00
|
|
|
|
2020-05-05 09:00:10 +02:00
|
|
|
/// An imported module, which may contain variables, sub-modules,
|
|
|
|
/// external Rust functions, and script-defined functions.
|
2020-05-05 06:24:13 +02:00
|
|
|
///
|
|
|
|
/// Not available under the `no_module` feature.
|
2020-05-13 13:21:42 +02:00
|
|
|
#[derive(Clone, Default)]
|
2020-05-05 09:00:10 +02:00
|
|
|
pub struct Module {
|
|
|
|
/// Sub-modules.
|
|
|
|
modules: HashMap<String, Module>,
|
2020-05-07 18:19:08 +02:00
|
|
|
|
|
|
|
/// Module variables.
|
2020-05-05 09:00:10 +02:00
|
|
|
variables: HashMap<String, Dynamic>,
|
2020-05-05 17:57:25 +02:00
|
|
|
|
2020-05-07 18:19:08 +02:00
|
|
|
/// Flattened collection of all module variables, including those in sub-modules.
|
|
|
|
all_variables: HashMap<u64, Dynamic>,
|
|
|
|
|
2020-05-05 09:00:10 +02:00
|
|
|
/// External Rust functions.
|
2020-05-11 07:36:50 +02:00
|
|
|
functions: HashMap<u64, (String, FnAccess, Vec<TypeId>, SharedNativeFunction)>,
|
2020-05-07 18:19:08 +02:00
|
|
|
|
|
|
|
/// Flattened collection of all external Rust functions, including those in sub-modules.
|
2020-05-11 07:36:50 +02:00
|
|
|
all_functions: HashMap<u64, SharedNativeFunction>,
|
2020-05-05 17:57:25 +02:00
|
|
|
|
2020-05-05 09:00:10 +02:00
|
|
|
/// Script-defined functions.
|
2020-05-05 17:57:25 +02:00
|
|
|
fn_lib: FunctionsLib,
|
2020-05-08 05:34:56 +02:00
|
|
|
|
|
|
|
/// Flattened collection of all script-defined functions, including those in sub-modules.
|
|
|
|
all_fn_lib: FunctionsLib,
|
2020-05-13 13:21:42 +02:00
|
|
|
|
|
|
|
/// Iterator functions, keyed by the type producing the iterator.
|
|
|
|
type_iterators: HashMap<TypeId, SharedIteratorFunction>,
|
2020-05-05 09:00:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
impl fmt::Debug for Module {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
|
|
write!(
|
|
|
|
f,
|
|
|
|
"<module {:?}, functions={}, lib={}>",
|
|
|
|
self.variables,
|
|
|
|
self.functions.len(),
|
2020-05-05 17:57:25 +02:00
|
|
|
self.fn_lib.len()
|
2020-05-05 09:00:10 +02:00
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-05 06:24:13 +02:00
|
|
|
impl Module {
|
|
|
|
/// Create a new module.
|
2020-05-06 13:45:17 +02:00
|
|
|
///
|
|
|
|
/// # Examples
|
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
/// use rhai::Module;
|
|
|
|
///
|
|
|
|
/// let mut module = Module::new();
|
|
|
|
/// module.set_var("answer", 42_i64);
|
|
|
|
/// assert_eq!(module.get_var_value::<i64>("answer").unwrap(), 42);
|
|
|
|
/// ```
|
2020-05-05 06:24:13 +02:00
|
|
|
pub fn new() -> Self {
|
2020-05-05 09:00:10 +02:00
|
|
|
Default::default()
|
2020-05-05 06:24:13 +02:00
|
|
|
}
|
|
|
|
|
2020-05-13 13:39:34 +02:00
|
|
|
/// Create a new module with a specified capacity for native Rust functions.
|
|
|
|
///
|
|
|
|
/// # Examples
|
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
/// use rhai::Module;
|
|
|
|
///
|
|
|
|
/// let mut module = Module::new();
|
|
|
|
/// module.set_var("answer", 42_i64);
|
|
|
|
/// assert_eq!(module.get_var_value::<i64>("answer").unwrap(), 42);
|
|
|
|
/// ```
|
|
|
|
pub fn new_with_capacity(capacity: usize) -> Self {
|
|
|
|
Self {
|
|
|
|
functions: HashMap::with_capacity(capacity),
|
|
|
|
..Default::default()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-05 09:00:10 +02:00
|
|
|
/// Does a variable exist in the module?
|
2020-05-06 13:45:17 +02:00
|
|
|
///
|
|
|
|
/// # Examples
|
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
/// use rhai::Module;
|
|
|
|
///
|
|
|
|
/// let mut module = Module::new();
|
|
|
|
/// module.set_var("answer", 42_i64);
|
|
|
|
/// assert!(module.contains_var("answer"));
|
|
|
|
/// ```
|
2020-05-05 11:51:40 +02:00
|
|
|
pub fn contains_var(&self, name: &str) -> bool {
|
2020-05-05 09:00:10 +02:00
|
|
|
self.variables.contains_key(name)
|
2020-05-05 06:24:13 +02:00
|
|
|
}
|
|
|
|
|
2020-05-05 09:00:10 +02:00
|
|
|
/// Get the value of a module variable.
|
2020-05-06 13:45:17 +02:00
|
|
|
///
|
|
|
|
/// # Examples
|
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
/// use rhai::Module;
|
|
|
|
///
|
|
|
|
/// let mut module = Module::new();
|
|
|
|
/// module.set_var("answer", 42_i64);
|
|
|
|
/// assert_eq!(module.get_var_value::<i64>("answer").unwrap(), 42);
|
|
|
|
/// ```
|
2020-05-05 11:51:40 +02:00
|
|
|
pub fn get_var_value<T: Variant + Clone>(&self, name: &str) -> Option<T> {
|
2020-05-07 18:19:08 +02:00
|
|
|
self.get_var(name).and_then(Dynamic::try_cast::<T>)
|
2020-05-05 09:00:10 +02:00
|
|
|
}
|
|
|
|
|
2020-05-06 13:45:17 +02:00
|
|
|
/// Get a module variable as a `Dynamic`.
|
|
|
|
///
|
|
|
|
/// # Examples
|
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
/// use rhai::Module;
|
|
|
|
///
|
|
|
|
/// let mut module = Module::new();
|
|
|
|
/// module.set_var("answer", 42_i64);
|
|
|
|
/// assert_eq!(module.get_var("answer").unwrap().cast::<i64>(), 42);
|
|
|
|
/// ```
|
2020-05-05 11:51:40 +02:00
|
|
|
pub fn get_var(&self, name: &str) -> Option<Dynamic> {
|
2020-05-05 09:00:10 +02:00
|
|
|
self.variables.get(name).cloned()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Set a variable into the module.
|
|
|
|
///
|
|
|
|
/// If there is an existing variable of the same name, it is replaced.
|
2020-05-06 13:45:17 +02:00
|
|
|
///
|
|
|
|
/// # Examples
|
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
/// use rhai::Module;
|
|
|
|
///
|
|
|
|
/// let mut module = Module::new();
|
|
|
|
/// module.set_var("answer", 42_i64);
|
|
|
|
/// assert_eq!(module.get_var_value::<i64>("answer").unwrap(), 42);
|
|
|
|
/// ```
|
2020-05-13 13:21:42 +02:00
|
|
|
pub fn set_var<K: Into<String>, T: Variant + Clone>(&mut self, name: K, value: T) {
|
|
|
|
self.variables.insert(name.into(), Dynamic::from(value));
|
2020-05-05 09:00:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Get a mutable reference to a modules-qualified variable.
|
2020-05-07 18:19:08 +02:00
|
|
|
///
|
|
|
|
/// The `u64` hash is calculated by the function `crate::calc_fn_hash`.
|
2020-05-05 11:51:40 +02:00
|
|
|
pub(crate) fn get_qualified_var_mut(
|
2020-05-05 09:00:10 +02:00
|
|
|
&mut self,
|
|
|
|
name: &str,
|
2020-05-11 17:48:50 +02:00
|
|
|
hash_var: u64,
|
2020-05-05 09:00:10 +02:00
|
|
|
pos: Position,
|
|
|
|
) -> Result<&mut Dynamic, Box<EvalAltResult>> {
|
2020-05-07 18:19:08 +02:00
|
|
|
self.all_variables
|
2020-05-11 17:48:50 +02:00
|
|
|
.get_mut(&hash_var)
|
2020-05-07 18:19:08 +02:00
|
|
|
.ok_or_else(|| Box::new(EvalAltResult::ErrorVariableNotFound(name.to_string(), pos)))
|
2020-05-05 09:00:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Does a sub-module exist in the module?
|
2020-05-06 13:45:17 +02:00
|
|
|
///
|
|
|
|
/// # Examples
|
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
/// use rhai::Module;
|
|
|
|
///
|
|
|
|
/// let mut module = Module::new();
|
|
|
|
/// let sub_module = Module::new();
|
|
|
|
/// module.set_sub_module("question", sub_module);
|
|
|
|
/// assert!(module.contains_sub_module("question"));
|
|
|
|
/// ```
|
2020-05-05 09:00:10 +02:00
|
|
|
pub fn contains_sub_module(&self, name: &str) -> bool {
|
|
|
|
self.modules.contains_key(name)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Get a sub-module.
|
2020-05-06 13:45:17 +02:00
|
|
|
///
|
|
|
|
/// # Examples
|
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
/// use rhai::Module;
|
|
|
|
///
|
|
|
|
/// let mut module = Module::new();
|
|
|
|
/// let sub_module = Module::new();
|
|
|
|
/// module.set_sub_module("question", sub_module);
|
|
|
|
/// assert!(module.get_sub_module("question").is_some());
|
|
|
|
/// ```
|
2020-05-05 09:00:10 +02:00
|
|
|
pub fn get_sub_module(&self, name: &str) -> Option<&Module> {
|
|
|
|
self.modules.get(name)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Get a mutable reference to a sub-module.
|
2020-05-06 13:45:17 +02:00
|
|
|
///
|
|
|
|
/// # Examples
|
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
/// use rhai::Module;
|
|
|
|
///
|
|
|
|
/// let mut module = Module::new();
|
|
|
|
/// let sub_module = Module::new();
|
|
|
|
/// module.set_sub_module("question", sub_module);
|
|
|
|
/// assert!(module.get_sub_module_mut("question").is_some());
|
|
|
|
/// ```
|
2020-05-05 09:00:10 +02:00
|
|
|
pub fn get_sub_module_mut(&mut self, name: &str) -> Option<&mut Module> {
|
|
|
|
self.modules.get_mut(name)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Set a sub-module into the module.
|
|
|
|
///
|
|
|
|
/// If there is an existing sub-module of the same name, it is replaced.
|
2020-05-06 13:45:17 +02:00
|
|
|
///
|
|
|
|
/// # Examples
|
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
/// use rhai::Module;
|
|
|
|
///
|
|
|
|
/// let mut module = Module::new();
|
|
|
|
/// let sub_module = Module::new();
|
|
|
|
/// module.set_sub_module("question", sub_module);
|
|
|
|
/// assert!(module.get_sub_module("question").is_some());
|
|
|
|
/// ```
|
2020-05-05 09:00:10 +02:00
|
|
|
pub fn set_sub_module<K: Into<String>>(&mut self, name: K, sub_module: Module) {
|
|
|
|
self.modules.insert(name.into(), sub_module.into());
|
|
|
|
}
|
|
|
|
|
2020-05-05 11:51:40 +02:00
|
|
|
/// Does the particular Rust function exist in the module?
|
|
|
|
///
|
|
|
|
/// The `u64` hash is calculated by the function `crate::calc_fn_hash`.
|
|
|
|
/// It is also returned by the `set_fn_XXX` calls.
|
2020-05-06 13:45:17 +02:00
|
|
|
///
|
|
|
|
/// # Examples
|
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
/// use rhai::Module;
|
|
|
|
///
|
|
|
|
/// let mut module = Module::new();
|
2020-05-09 18:19:13 +02:00
|
|
|
/// let hash = module.set_fn_0("calc", || Ok(42_i64));
|
2020-05-06 13:45:17 +02:00
|
|
|
/// assert!(module.contains_fn(hash));
|
|
|
|
/// ```
|
2020-05-11 17:48:50 +02:00
|
|
|
pub fn contains_fn(&self, hash_fn: u64) -> bool {
|
|
|
|
self.functions.contains_key(&hash_fn)
|
2020-05-05 11:51:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/// 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.
|
2020-05-09 05:29:30 +02:00
|
|
|
pub fn set_fn(
|
|
|
|
&mut self,
|
2020-05-11 12:55:58 +02:00
|
|
|
name: String,
|
|
|
|
abi: NativeFunctionABI,
|
2020-05-09 05:29:30 +02:00
|
|
|
access: FnAccess,
|
2020-05-13 13:21:42 +02:00
|
|
|
params: &[TypeId],
|
2020-05-09 05:29:30 +02:00
|
|
|
func: Box<FnAny>,
|
|
|
|
) -> u64 {
|
2020-05-11 17:48:50 +02:00
|
|
|
let hash_fn = calc_fn_hash(empty(), &name, params.iter().cloned());
|
2020-05-05 17:57:25 +02:00
|
|
|
|
2020-05-11 12:55:58 +02:00
|
|
|
let f = Box::new(NativeFunction::from((func, abi))) as Box<dyn NativeCallable>;
|
2020-05-11 07:36:50 +02:00
|
|
|
|
2020-05-05 17:57:25 +02:00
|
|
|
#[cfg(not(feature = "sync"))]
|
2020-05-11 07:36:50 +02:00
|
|
|
let func = Rc::new(f);
|
2020-05-05 17:57:25 +02:00
|
|
|
#[cfg(feature = "sync")]
|
2020-05-11 07:36:50 +02:00
|
|
|
let func = Arc::new(f);
|
|
|
|
|
2020-05-13 13:21:42 +02:00
|
|
|
self.functions
|
|
|
|
.insert(hash_fn, (name, access, params.to_vec(), func));
|
2020-05-05 17:57:25 +02:00
|
|
|
|
2020-05-11 17:48:50 +02:00
|
|
|
hash_fn
|
2020-05-05 11:51:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Set a Rust function taking no parameters into the module, returning a hash key.
|
|
|
|
///
|
|
|
|
/// If there is a similar existing Rust function, it is replaced.
|
2020-05-06 13:45:17 +02:00
|
|
|
///
|
|
|
|
/// # Examples
|
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
/// use rhai::Module;
|
|
|
|
///
|
|
|
|
/// let mut module = Module::new();
|
2020-05-09 18:19:13 +02:00
|
|
|
/// let hash = module.set_fn_0("calc", || Ok(42_i64));
|
2020-05-06 13:45:17 +02:00
|
|
|
/// assert!(module.get_fn(hash).is_some());
|
|
|
|
/// ```
|
2020-05-13 13:21:42 +02:00
|
|
|
pub fn set_fn_0<K: Into<String>, T: Variant + Clone>(
|
2020-05-05 11:51:40 +02:00
|
|
|
&mut self,
|
2020-05-11 12:55:58 +02:00
|
|
|
name: K,
|
2020-05-06 10:09:44 +02:00
|
|
|
#[cfg(not(feature = "sync"))] func: impl Fn() -> FuncReturn<T> + 'static,
|
|
|
|
#[cfg(feature = "sync")] func: impl Fn() -> FuncReturn<T> + Send + Sync + 'static,
|
2020-05-05 11:51:40 +02:00
|
|
|
) -> u64 {
|
2020-05-13 15:58:38 +02:00
|
|
|
let f = move |_: &mut FnCallArgs| func().map(Dynamic::from);
|
2020-05-13 13:21:42 +02:00
|
|
|
let arg_types = [];
|
|
|
|
self.set_fn(name.into(), Pure, DEF_ACCESS, &arg_types, Box::new(f))
|
2020-05-05 11:51:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Set a Rust function taking one parameter into the module, returning a hash key.
|
|
|
|
///
|
|
|
|
/// If there is a similar existing Rust function, it is replaced.
|
2020-05-06 13:45:17 +02:00
|
|
|
///
|
|
|
|
/// # Examples
|
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
/// use rhai::Module;
|
|
|
|
///
|
|
|
|
/// let mut module = Module::new();
|
2020-05-09 18:19:13 +02:00
|
|
|
/// let hash = module.set_fn_1("calc", |x: i64| Ok(x + 1));
|
2020-05-06 13:45:17 +02:00
|
|
|
/// assert!(module.get_fn(hash).is_some());
|
|
|
|
/// ```
|
2020-05-13 13:21:42 +02:00
|
|
|
pub fn set_fn_1<K: Into<String>, A: Variant + Clone, T: Variant + Clone>(
|
2020-05-05 11:51:40 +02:00
|
|
|
&mut self,
|
2020-05-11 12:55:58 +02:00
|
|
|
name: K,
|
2020-05-06 10:09:44 +02:00
|
|
|
#[cfg(not(feature = "sync"))] func: impl Fn(A) -> FuncReturn<T> + 'static,
|
|
|
|
#[cfg(feature = "sync")] func: impl Fn(A) -> FuncReturn<T> + Send + Sync + 'static,
|
2020-05-05 11:51:40 +02:00
|
|
|
) -> u64 {
|
2020-05-13 15:58:38 +02:00
|
|
|
let f =
|
|
|
|
move |args: &mut FnCallArgs| func(mem::take(args[0]).cast::<A>()).map(Dynamic::from);
|
2020-05-13 13:21:42 +02:00
|
|
|
let arg_types = [TypeId::of::<A>()];
|
|
|
|
self.set_fn(name.into(), Pure, DEF_ACCESS, &arg_types, Box::new(f))
|
2020-05-05 11:51:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Set a Rust function taking one mutable parameter into the module, returning a hash key.
|
|
|
|
///
|
|
|
|
/// If there is a similar existing Rust function, it is replaced.
|
2020-05-06 13:45:17 +02:00
|
|
|
///
|
|
|
|
/// # Examples
|
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
/// use rhai::Module;
|
|
|
|
///
|
|
|
|
/// let mut module = Module::new();
|
2020-05-09 18:19:13 +02:00
|
|
|
/// let hash = module.set_fn_1_mut("calc", |x: &mut i64| { *x += 1; Ok(*x) });
|
2020-05-06 13:45:17 +02:00
|
|
|
/// assert!(module.get_fn(hash).is_some());
|
|
|
|
/// ```
|
2020-05-13 13:21:42 +02:00
|
|
|
pub fn set_fn_1_mut<K: Into<String>, A: Variant + Clone, T: Variant + Clone>(
|
2020-05-05 11:51:40 +02:00
|
|
|
&mut self,
|
2020-05-11 12:55:58 +02:00
|
|
|
name: K,
|
2020-05-06 10:09:44 +02:00
|
|
|
#[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,
|
2020-05-05 11:51:40 +02:00
|
|
|
) -> u64 {
|
2020-05-13 15:58:38 +02:00
|
|
|
let f = move |args: &mut FnCallArgs| {
|
|
|
|
func(args[0].downcast_mut::<A>().unwrap()).map(Dynamic::from)
|
2020-05-05 11:51:40 +02:00
|
|
|
};
|
2020-05-13 13:21:42 +02:00
|
|
|
let arg_types = [TypeId::of::<A>()];
|
|
|
|
self.set_fn(name.into(), Method, DEF_ACCESS, &arg_types, Box::new(f))
|
2020-05-05 11:51:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Set a Rust function taking two parameters into the module, returning a hash key.
|
|
|
|
///
|
|
|
|
/// If there is a similar existing Rust function, it is replaced.
|
2020-05-06 13:45:17 +02:00
|
|
|
///
|
|
|
|
/// # Examples
|
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
/// use rhai::Module;
|
|
|
|
///
|
|
|
|
/// let mut module = Module::new();
|
|
|
|
/// let hash = module.set_fn_2("calc", |x: i64, y: String| {
|
|
|
|
/// Ok(x + y.len() as i64)
|
2020-05-09 18:19:13 +02:00
|
|
|
/// });
|
2020-05-06 13:45:17 +02:00
|
|
|
/// assert!(module.get_fn(hash).is_some());
|
|
|
|
/// ```
|
2020-05-13 13:21:42 +02:00
|
|
|
pub fn set_fn_2<K: Into<String>, A: Variant + Clone, B: Variant + Clone, T: Variant + Clone>(
|
2020-05-05 11:51:40 +02:00
|
|
|
&mut self,
|
2020-05-11 12:55:58 +02:00
|
|
|
name: K,
|
2020-05-06 10:09:44 +02:00
|
|
|
#[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,
|
2020-05-05 11:51:40 +02:00
|
|
|
) -> u64 {
|
2020-05-13 15:58:38 +02:00
|
|
|
let f = move |args: &mut FnCallArgs| {
|
2020-05-05 11:51:40 +02:00
|
|
|
let a = mem::take(args[0]).cast::<A>();
|
|
|
|
let b = mem::take(args[1]).cast::<B>();
|
|
|
|
|
2020-05-13 15:58:38 +02:00
|
|
|
func(a, b).map(Dynamic::from)
|
2020-05-05 11:51:40 +02:00
|
|
|
};
|
2020-05-13 13:21:42 +02:00
|
|
|
let arg_types = [TypeId::of::<A>(), TypeId::of::<B>()];
|
|
|
|
self.set_fn(name.into(), Pure, DEF_ACCESS, &arg_types, Box::new(f))
|
2020-05-05 11:51:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Set a Rust function taking two parameters (the first one mutable) into the module,
|
|
|
|
/// returning a hash key.
|
|
|
|
///
|
2020-05-06 13:45:17 +02:00
|
|
|
/// # Examples
|
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
/// use rhai::Module;
|
|
|
|
///
|
|
|
|
/// let mut module = Module::new();
|
|
|
|
/// let hash = module.set_fn_2_mut("calc", |x: &mut i64, y: String| {
|
|
|
|
/// *x += y.len() as i64; Ok(*x)
|
2020-05-09 18:19:13 +02:00
|
|
|
/// });
|
2020-05-06 13:45:17 +02:00
|
|
|
/// assert!(module.get_fn(hash).is_some());
|
|
|
|
/// ```
|
2020-05-07 18:19:08 +02:00
|
|
|
pub fn set_fn_2_mut<
|
|
|
|
K: Into<String>,
|
|
|
|
A: Variant + Clone,
|
|
|
|
B: Variant + Clone,
|
2020-05-13 13:21:42 +02:00
|
|
|
T: Variant + Clone,
|
2020-05-07 18:19:08 +02:00
|
|
|
>(
|
2020-05-05 11:51:40 +02:00
|
|
|
&mut self,
|
2020-05-11 12:55:58 +02:00
|
|
|
name: K,
|
2020-05-06 10:09:44 +02:00
|
|
|
#[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,
|
2020-05-05 11:51:40 +02:00
|
|
|
) -> u64 {
|
2020-05-13 15:58:38 +02:00
|
|
|
let f = move |args: &mut FnCallArgs| {
|
2020-05-05 11:51:40 +02:00
|
|
|
let b = mem::take(args[1]).cast::<B>();
|
|
|
|
let a = args[0].downcast_mut::<A>().unwrap();
|
|
|
|
|
2020-05-13 15:58:38 +02:00
|
|
|
func(a, b).map(Dynamic::from)
|
2020-05-05 11:51:40 +02:00
|
|
|
};
|
2020-05-13 13:21:42 +02:00
|
|
|
let arg_types = [TypeId::of::<A>(), TypeId::of::<B>()];
|
|
|
|
self.set_fn(name.into(), Method, DEF_ACCESS, &arg_types, Box::new(f))
|
2020-05-05 11:51:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Set a Rust function taking three parameters into the module, returning a hash key.
|
|
|
|
///
|
|
|
|
/// If there is a similar existing Rust function, it is replaced.
|
2020-05-06 13:45:17 +02:00
|
|
|
///
|
|
|
|
/// # Examples
|
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
/// use rhai::Module;
|
|
|
|
///
|
|
|
|
/// let mut module = Module::new();
|
|
|
|
/// let hash = module.set_fn_3("calc", |x: i64, y: String, z: i64| {
|
|
|
|
/// Ok(x + y.len() as i64 + z)
|
2020-05-09 18:19:13 +02:00
|
|
|
/// });
|
2020-05-06 13:45:17 +02:00
|
|
|
/// assert!(module.get_fn(hash).is_some());
|
|
|
|
/// ```
|
2020-05-05 11:51:40 +02:00
|
|
|
pub fn set_fn_3<
|
2020-05-07 18:19:08 +02:00
|
|
|
K: Into<String>,
|
2020-05-05 11:51:40 +02:00
|
|
|
A: Variant + Clone,
|
|
|
|
B: Variant + Clone,
|
|
|
|
C: Variant + Clone,
|
2020-05-13 13:21:42 +02:00
|
|
|
T: Variant + Clone,
|
2020-05-05 11:51:40 +02:00
|
|
|
>(
|
|
|
|
&mut self,
|
2020-05-11 12:55:58 +02:00
|
|
|
name: K,
|
2020-05-06 10:09:44 +02:00
|
|
|
#[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,
|
2020-05-05 11:51:40 +02:00
|
|
|
) -> u64 {
|
2020-05-13 15:58:38 +02:00
|
|
|
let f = move |args: &mut FnCallArgs| {
|
2020-05-05 11:51:40 +02:00
|
|
|
let a = mem::take(args[0]).cast::<A>();
|
|
|
|
let b = mem::take(args[1]).cast::<B>();
|
|
|
|
let c = mem::take(args[2]).cast::<C>();
|
|
|
|
|
2020-05-13 15:58:38 +02:00
|
|
|
func(a, b, c).map(Dynamic::from)
|
2020-05-05 11:51:40 +02:00
|
|
|
};
|
2020-05-13 13:21:42 +02:00
|
|
|
let arg_types = [TypeId::of::<A>(), TypeId::of::<B>(), TypeId::of::<C>()];
|
|
|
|
self.set_fn(name.into(), Pure, DEF_ACCESS, &arg_types, Box::new(f))
|
2020-05-05 11:51:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Set a Rust function taking three parameters (the first one mutable) into the module,
|
|
|
|
/// returning a hash key.
|
|
|
|
///
|
|
|
|
/// If there is a similar existing Rust function, it is replaced.
|
2020-05-06 13:45:17 +02:00
|
|
|
///
|
|
|
|
/// # Examples
|
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
/// use rhai::Module;
|
|
|
|
///
|
|
|
|
/// let mut module = Module::new();
|
|
|
|
/// let hash = module.set_fn_3_mut("calc", |x: &mut i64, y: String, z: i64| {
|
|
|
|
/// *x += y.len() as i64 + z; Ok(*x)
|
2020-05-09 18:19:13 +02:00
|
|
|
/// });
|
2020-05-06 13:45:17 +02:00
|
|
|
/// assert!(module.get_fn(hash).is_some());
|
|
|
|
/// ```
|
2020-05-05 11:51:40 +02:00
|
|
|
pub fn set_fn_3_mut<
|
2020-05-07 18:19:08 +02:00
|
|
|
K: Into<String>,
|
2020-05-05 11:51:40 +02:00
|
|
|
A: Variant + Clone,
|
|
|
|
B: Variant + Clone,
|
|
|
|
C: Variant + Clone,
|
2020-05-13 13:21:42 +02:00
|
|
|
T: Variant + Clone,
|
2020-05-05 11:51:40 +02:00
|
|
|
>(
|
|
|
|
&mut self,
|
2020-05-11 12:55:58 +02:00
|
|
|
name: K,
|
2020-05-06 10:09:44 +02:00
|
|
|
#[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,
|
2020-05-05 11:51:40 +02:00
|
|
|
) -> u64 {
|
2020-05-13 15:58:38 +02:00
|
|
|
let f = move |args: &mut FnCallArgs| {
|
2020-05-05 11:51:40 +02:00
|
|
|
let b = mem::take(args[1]).cast::<B>();
|
|
|
|
let c = mem::take(args[2]).cast::<C>();
|
|
|
|
let a = args[0].downcast_mut::<A>().unwrap();
|
|
|
|
|
2020-05-13 15:58:38 +02:00
|
|
|
func(a, b, c).map(Dynamic::from)
|
2020-05-05 11:51:40 +02:00
|
|
|
};
|
2020-05-13 13:21:42 +02:00
|
|
|
let arg_types = [TypeId::of::<A>(), TypeId::of::<B>(), TypeId::of::<C>()];
|
|
|
|
self.set_fn(name.into(), Method, DEF_ACCESS, &arg_types, Box::new(f))
|
2020-05-05 11:51:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Get a Rust function.
|
|
|
|
///
|
|
|
|
/// The `u64` hash is calculated by the function `crate::calc_fn_hash`.
|
|
|
|
/// It is also returned by the `set_fn_XXX` calls.
|
2020-05-06 13:45:17 +02:00
|
|
|
///
|
|
|
|
/// # Examples
|
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
/// use rhai::Module;
|
|
|
|
///
|
|
|
|
/// let mut module = Module::new();
|
2020-05-09 18:19:13 +02:00
|
|
|
/// let hash = module.set_fn_1("calc", |x: i64| Ok(x + 1));
|
2020-05-06 13:45:17 +02:00
|
|
|
/// assert!(module.get_fn(hash).is_some());
|
|
|
|
/// ```
|
2020-05-11 17:48:50 +02:00
|
|
|
pub fn get_fn(&self, hash_fn: u64) -> Option<&Box<dyn NativeCallable>> {
|
|
|
|
self.functions.get(&hash_fn).map(|(_, _, _, v)| v.as_ref())
|
2020-05-05 11:51:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Get a modules-qualified function.
|
|
|
|
///
|
|
|
|
/// The `u64` hash is calculated by the function `crate::calc_fn_hash`.
|
|
|
|
/// It is also returned by the `set_fn_XXX` calls.
|
|
|
|
pub(crate) fn get_qualified_fn(
|
|
|
|
&mut self,
|
|
|
|
name: &str,
|
2020-05-11 17:48:50 +02:00
|
|
|
hash_fn_native: u64,
|
2020-05-11 07:36:50 +02:00
|
|
|
) -> Result<&Box<dyn NativeCallable>, Box<EvalAltResult>> {
|
2020-05-07 18:19:08 +02:00
|
|
|
self.all_functions
|
2020-05-11 17:48:50 +02:00
|
|
|
.get(&hash_fn_native)
|
2020-05-07 18:19:08 +02:00
|
|
|
.map(|f| f.as_ref())
|
2020-05-13 15:58:38 +02:00
|
|
|
.ok_or_else(|| {
|
|
|
|
Box::new(EvalAltResult::ErrorFunctionNotFound(
|
|
|
|
name.to_string(),
|
|
|
|
Position::none(),
|
|
|
|
))
|
|
|
|
})
|
2020-05-05 11:51:40 +02:00
|
|
|
}
|
2020-05-05 17:57:25 +02:00
|
|
|
|
2020-05-08 05:34:56 +02:00
|
|
|
/// Get a modules-qualified script-defined functions.
|
|
|
|
///
|
|
|
|
/// The `u64` hash is calculated by the function `crate::calc_fn_hash`.
|
2020-05-11 17:48:50 +02:00
|
|
|
pub(crate) fn get_qualified_scripted_fn(&mut self, hash_fn_def: u64) -> Option<&FnDef> {
|
|
|
|
self.all_fn_lib.get_function(hash_fn_def)
|
2020-05-05 17:57:25 +02:00
|
|
|
}
|
2020-05-06 13:45:17 +02:00
|
|
|
|
|
|
|
/// Create a new `Module` by evaluating an `AST`.
|
|
|
|
///
|
|
|
|
/// # Examples
|
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
2020-05-08 16:38:56 +02:00
|
|
|
/// use rhai::{Engine, Module, Scope};
|
2020-05-06 13:45:17 +02:00
|
|
|
///
|
|
|
|
/// let engine = Engine::new();
|
2020-05-08 16:38:56 +02:00
|
|
|
/// let ast = engine.compile("let answer = 42; export answer;")?;
|
|
|
|
/// let module = Module::eval_ast_as_new(Scope::new(), &ast, &engine)?;
|
2020-05-06 13:45:17 +02:00
|
|
|
/// assert!(module.contains_var("answer"));
|
|
|
|
/// assert_eq!(module.get_var_value::<i64>("answer").unwrap(), 42);
|
|
|
|
/// # Ok(())
|
|
|
|
/// # }
|
|
|
|
/// ```
|
2020-05-13 13:21:42 +02:00
|
|
|
#[cfg(not(feature = "no_module"))]
|
2020-05-08 10:49:24 +02:00
|
|
|
pub fn eval_ast_as_new(mut scope: Scope, ast: &AST, engine: &Engine) -> FuncReturn<Self> {
|
2020-05-06 13:45:17 +02:00
|
|
|
// Run the script
|
|
|
|
engine.eval_ast_with_scope_raw(&mut scope, &ast)?;
|
|
|
|
|
|
|
|
// Create new module
|
|
|
|
let mut module = Module::new();
|
|
|
|
|
|
|
|
scope.into_iter().for_each(
|
|
|
|
|ScopeEntry {
|
2020-05-09 05:29:30 +02:00
|
|
|
typ, value, alias, ..
|
2020-05-06 13:45:17 +02:00
|
|
|
}| {
|
|
|
|
match typ {
|
2020-05-08 10:49:24 +02:00
|
|
|
// Variables with an alias left in the scope become module variables
|
|
|
|
ScopeEntryType::Normal | ScopeEntryType::Constant if alias.is_some() => {
|
|
|
|
module.variables.insert(*alias.unwrap(), value);
|
2020-05-06 13:45:17 +02:00
|
|
|
}
|
|
|
|
// Modules left in the scope become sub-modules
|
2020-05-08 16:38:56 +02:00
|
|
|
ScopeEntryType::Module if alias.is_some() => {
|
2020-05-06 13:45:17 +02:00
|
|
|
module
|
|
|
|
.modules
|
2020-05-08 16:38:56 +02:00
|
|
|
.insert(*alias.unwrap(), value.cast::<Module>());
|
2020-05-06 13:45:17 +02:00
|
|
|
}
|
2020-05-08 16:38:56 +02:00
|
|
|
// Variables and modules with no alias are private and not exported
|
|
|
|
_ => (),
|
2020-05-06 13:45:17 +02:00
|
|
|
}
|
|
|
|
},
|
|
|
|
);
|
|
|
|
|
|
|
|
module.fn_lib = module.fn_lib.merge(ast.fn_lib());
|
|
|
|
|
|
|
|
Ok(module)
|
|
|
|
}
|
2020-05-07 18:19:08 +02:00
|
|
|
|
|
|
|
/// Scan through all the sub-modules in the `Module` build an index of all
|
|
|
|
/// variables and external Rust functions via hashing.
|
2020-05-09 04:00:59 +02:00
|
|
|
pub(crate) fn index_all_sub_modules(&mut self) {
|
2020-05-07 18:19:08 +02:00
|
|
|
// Collect a particular module.
|
2020-05-09 04:00:59 +02:00
|
|
|
fn index_module<'a>(
|
2020-05-07 18:19:08 +02:00
|
|
|
module: &'a mut Module,
|
2020-05-09 04:00:59 +02:00
|
|
|
qualifiers: &mut Vec<&'a str>,
|
2020-05-07 18:19:08 +02:00
|
|
|
variables: &mut Vec<(u64, Dynamic)>,
|
2020-05-11 07:36:50 +02:00
|
|
|
functions: &mut Vec<(u64, SharedNativeFunction)>,
|
|
|
|
fn_lib: &mut Vec<(u64, SharedFnDef)>,
|
2020-05-07 18:19:08 +02:00
|
|
|
) {
|
2020-05-09 04:00:59 +02:00
|
|
|
for (name, m) in module.modules.iter_mut() {
|
|
|
|
// Index all the sub-modules first.
|
|
|
|
qualifiers.push(name);
|
|
|
|
index_module(m, qualifiers, variables, functions, fn_lib);
|
|
|
|
qualifiers.pop();
|
2020-05-07 18:19:08 +02:00
|
|
|
}
|
|
|
|
|
2020-05-09 04:00:59 +02:00
|
|
|
// Index all variables
|
2020-05-07 18:19:08 +02:00
|
|
|
for (var_name, value) in module.variables.iter() {
|
2020-05-09 04:00:59 +02:00
|
|
|
// Qualifiers + variable name
|
2020-05-11 17:48:50 +02:00
|
|
|
let hash_var = calc_fn_hash(qualifiers.iter().map(|&v| v), var_name, empty());
|
|
|
|
variables.push((hash_var, value.clone()));
|
2020-05-07 18:19:08 +02:00
|
|
|
}
|
2020-05-09 04:00:59 +02:00
|
|
|
// Index all Rust functions
|
2020-05-11 12:55:58 +02:00
|
|
|
for (name, access, params, func) in module.functions.values() {
|
2020-05-09 05:29:30 +02:00
|
|
|
match access {
|
|
|
|
// Private functions are not exported
|
|
|
|
FnAccess::Private => continue,
|
|
|
|
FnAccess::Public => (),
|
|
|
|
}
|
2020-05-09 04:00:59 +02:00
|
|
|
// Rust functions are indexed in two steps:
|
|
|
|
// 1) Calculate a hash in a similar manner to script-defined functions,
|
|
|
|
// i.e. qualifiers + function name + dummy parameter types (one for each parameter).
|
2020-05-09 18:19:13 +02:00
|
|
|
let hash_fn_def = calc_fn_hash(
|
2020-05-11 04:29:33 +02:00
|
|
|
qualifiers.iter().map(|&v| v),
|
2020-05-11 12:55:58 +02:00
|
|
|
name,
|
2020-05-08 05:34:56 +02:00
|
|
|
repeat(EMPTY_TYPE_ID()).take(params.len()),
|
|
|
|
);
|
2020-05-09 04:00:59 +02:00
|
|
|
// 2) Calculate a second hash with no qualifiers, empty function name, and
|
|
|
|
// the actual list of parameter `TypeId`'.s
|
2020-05-09 18:19:13 +02:00
|
|
|
let hash_fn_args = calc_fn_hash(empty(), "", params.iter().cloned());
|
2020-05-09 04:00:59 +02:00
|
|
|
// 3) The final hash is the XOR of the two hashes.
|
2020-05-11 17:48:50 +02:00
|
|
|
let hash_fn_native = hash_fn_def ^ hash_fn_args;
|
2020-05-09 04:00:59 +02:00
|
|
|
|
2020-05-11 17:48:50 +02:00
|
|
|
functions.push((hash_fn_native, func.clone()));
|
2020-05-08 05:34:56 +02:00
|
|
|
}
|
2020-05-09 04:00:59 +02:00
|
|
|
// Index all script-defined functions
|
2020-05-08 05:34:56 +02:00
|
|
|
for fn_def in module.fn_lib.values() {
|
2020-05-09 05:29:30 +02:00
|
|
|
match fn_def.access {
|
|
|
|
// Private functions are not exported
|
|
|
|
FnAccess::Private => continue,
|
2020-05-11 12:55:58 +02:00
|
|
|
DEF_ACCESS => (),
|
2020-05-09 05:29:30 +02:00
|
|
|
}
|
2020-05-09 04:00:59 +02:00
|
|
|
// Qualifiers + function name + placeholders (one for each parameter)
|
2020-05-11 17:48:50 +02:00
|
|
|
let hash_fn_def = calc_fn_hash(
|
2020-05-11 04:29:33 +02:00
|
|
|
qualifiers.iter().map(|&v| v),
|
2020-05-08 05:34:56 +02:00
|
|
|
&fn_def.name,
|
|
|
|
repeat(EMPTY_TYPE_ID()).take(fn_def.params.len()),
|
|
|
|
);
|
2020-05-11 17:48:50 +02:00
|
|
|
fn_lib.push((hash_fn_def, fn_def.clone()));
|
2020-05-07 18:19:08 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut variables = Vec::new();
|
|
|
|
let mut functions = Vec::new();
|
2020-05-08 05:34:56 +02:00
|
|
|
let mut fn_lib = Vec::new();
|
|
|
|
|
2020-05-09 04:00:59 +02:00
|
|
|
index_module(
|
2020-05-08 05:34:56 +02:00
|
|
|
self,
|
|
|
|
&mut vec!["root"],
|
|
|
|
&mut variables,
|
|
|
|
&mut functions,
|
|
|
|
&mut fn_lib,
|
|
|
|
);
|
2020-05-07 18:19:08 +02:00
|
|
|
|
2020-05-08 05:34:56 +02:00
|
|
|
self.all_variables = variables.into_iter().collect();
|
|
|
|
self.all_functions = functions.into_iter().collect();
|
|
|
|
self.all_fn_lib = fn_lib.into();
|
2020-05-07 18:19:08 +02:00
|
|
|
}
|
2020-05-13 13:21:42 +02:00
|
|
|
|
|
|
|
/// Does a type iterator exist in the module?
|
2020-05-13 14:22:05 +02:00
|
|
|
pub fn contains_iter(&self, id: TypeId) -> bool {
|
2020-05-13 13:21:42 +02:00
|
|
|
self.type_iterators.contains_key(&id)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Set a type iterator into the module.
|
2020-05-13 14:22:05 +02:00
|
|
|
pub fn set_iter(&mut self, typ: TypeId, func: Box<IteratorFn>) {
|
2020-05-13 13:21:42 +02:00
|
|
|
#[cfg(not(feature = "sync"))]
|
|
|
|
self.type_iterators.insert(typ, Rc::new(func));
|
|
|
|
#[cfg(feature = "sync")]
|
|
|
|
self.type_iterators.insert(typ, Arc::new(func));
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Get the specified type iterator.
|
2020-05-13 14:22:05 +02:00
|
|
|
pub fn get_iter(&self, id: TypeId) -> Option<&SharedIteratorFunction> {
|
2020-05-13 13:21:42 +02:00
|
|
|
self.type_iterators.get(&id)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// 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, Eq, PartialEq, Hash, Default)]
|
|
|
|
pub struct ModuleRef(StaticVec<(String, Position)>, Option<NonZeroUsize>);
|
|
|
|
|
|
|
|
impl fmt::Debug for ModuleRef {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
|
|
fmt::Debug::fmt(&self.0, f)?;
|
|
|
|
|
|
|
|
if let Some(index) = self.1 {
|
|
|
|
write!(f, " -> {}", index)
|
|
|
|
} 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, None)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ModuleRef {
|
|
|
|
pub(crate) fn index(&self) -> Option<NonZeroUsize> {
|
|
|
|
self.1
|
|
|
|
}
|
|
|
|
pub(crate) fn set_index(&mut self, index: Option<NonZeroUsize>) {
|
|
|
|
self.1 = index
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-15 15:40:54 +02:00
|
|
|
/// Trait that encapsulates a module resolution service.
|
2020-05-13 13:21:42 +02:00
|
|
|
#[cfg(not(feature = "no_module"))]
|
|
|
|
#[cfg(not(feature = "sync"))]
|
|
|
|
pub trait ModuleResolver {
|
|
|
|
/// Resolve a module based on a path string.
|
|
|
|
fn resolve(
|
|
|
|
&self,
|
|
|
|
engine: &Engine,
|
|
|
|
scope: Scope,
|
|
|
|
path: &str,
|
|
|
|
pos: Position,
|
|
|
|
) -> Result<Module, Box<EvalAltResult>>;
|
|
|
|
}
|
|
|
|
|
2020-05-15 15:40:54 +02:00
|
|
|
/// Trait that encapsulates a module resolution service.
|
2020-05-13 13:21:42 +02:00
|
|
|
#[cfg(not(feature = "no_module"))]
|
|
|
|
#[cfg(feature = "sync")]
|
|
|
|
pub trait ModuleResolver: Send + Sync {
|
|
|
|
/// Resolve a module based on a path string.
|
|
|
|
fn resolve(
|
|
|
|
&self,
|
|
|
|
engine: &Engine,
|
|
|
|
scope: Scope,
|
|
|
|
path: &str,
|
|
|
|
pos: Position,
|
|
|
|
) -> Result<Module, Box<EvalAltResult>>;
|
2020-05-05 17:57:25 +02:00
|
|
|
}
|
|
|
|
|
2020-05-06 10:09:44 +02:00
|
|
|
/// Re-export module resolvers.
|
2020-05-13 13:21:42 +02:00
|
|
|
#[cfg(not(feature = "no_module"))]
|
2020-05-05 17:57:25 +02:00
|
|
|
pub mod resolvers {
|
2020-05-06 13:45:17 +02:00
|
|
|
#[cfg(not(feature = "no_std"))]
|
2020-05-06 10:09:44 +02:00
|
|
|
pub use super::file::FileModuleResolver;
|
|
|
|
pub use super::stat::StaticModuleResolver;
|
|
|
|
}
|
2020-05-05 17:57:25 +02:00
|
|
|
|
2020-05-06 10:09:44 +02:00
|
|
|
/// Script file-based module resolver.
|
2020-05-13 13:21:42 +02:00
|
|
|
#[cfg(not(feature = "no_module"))]
|
2020-05-06 10:09:44 +02:00
|
|
|
#[cfg(not(feature = "no_std"))]
|
|
|
|
mod file {
|
|
|
|
use super::*;
|
2020-05-05 17:57:25 +02:00
|
|
|
use crate::stdlib::path::PathBuf;
|
|
|
|
|
2020-05-15 15:40:54 +02:00
|
|
|
/// Module resolution service that loads module script files from the file system.
|
2020-05-06 10:09:44 +02:00
|
|
|
///
|
|
|
|
/// The `new_with_path` and `new_with_path_and_extension` constructor functions
|
|
|
|
/// allow specification of a base directory with module path used as a relative path offset
|
|
|
|
/// to the base directory. The script file is then forced to be in a specified extension
|
|
|
|
/// (default `.rhai`).
|
2020-05-06 13:45:17 +02:00
|
|
|
///
|
|
|
|
/// # Examples
|
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
/// use rhai::Engine;
|
|
|
|
/// use rhai::module_resolvers::FileModuleResolver;
|
|
|
|
///
|
|
|
|
/// // Create a new 'FileModuleResolver' loading scripts from the 'scripts' subdirectory
|
|
|
|
/// // with file extension '.x'.
|
|
|
|
/// let resolver = FileModuleResolver::new_with_path_and_extension("./scripts", "x");
|
|
|
|
///
|
|
|
|
/// let mut engine = Engine::new();
|
|
|
|
/// engine.set_module_resolver(Some(resolver));
|
|
|
|
/// ```
|
2020-05-12 12:48:25 +02:00
|
|
|
#[derive(Debug, Eq, PartialEq, PartialOrd, Ord, Clone, Hash)]
|
2020-05-06 10:09:44 +02:00
|
|
|
pub struct FileModuleResolver {
|
|
|
|
path: PathBuf,
|
|
|
|
extension: String,
|
|
|
|
}
|
2020-05-05 17:57:25 +02:00
|
|
|
|
2020-05-07 18:19:08 +02:00
|
|
|
impl Default for FileModuleResolver {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self::new_with_path(PathBuf::default())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-05 17:57:25 +02:00
|
|
|
impl FileModuleResolver {
|
|
|
|
/// Create a new `FileModuleResolver` with a specific base path.
|
2020-05-06 13:45:17 +02:00
|
|
|
///
|
|
|
|
/// # Examples
|
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
/// use rhai::Engine;
|
|
|
|
/// use rhai::module_resolvers::FileModuleResolver;
|
|
|
|
///
|
|
|
|
/// // Create a new 'FileModuleResolver' loading scripts from the 'scripts' subdirectory
|
|
|
|
/// // with file extension '.rhai' (the default).
|
|
|
|
/// let resolver = FileModuleResolver::new_with_path("./scripts");
|
|
|
|
///
|
|
|
|
/// let mut engine = Engine::new();
|
|
|
|
/// engine.set_module_resolver(Some(resolver));
|
|
|
|
/// ```
|
|
|
|
pub fn new_with_path<P: Into<PathBuf>>(path: P) -> Self {
|
|
|
|
Self::new_with_path_and_extension(path, "rhai")
|
2020-05-06 10:09:44 +02:00
|
|
|
}
|
2020-05-06 13:45:17 +02:00
|
|
|
|
2020-05-06 10:09:44 +02:00
|
|
|
/// Create a new `FileModuleResolver` with a specific base path and file extension.
|
|
|
|
///
|
|
|
|
/// The default extension is `.rhai`.
|
2020-05-06 13:45:17 +02:00
|
|
|
///
|
|
|
|
/// # Examples
|
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
/// use rhai::Engine;
|
|
|
|
/// use rhai::module_resolvers::FileModuleResolver;
|
|
|
|
///
|
|
|
|
/// // Create a new 'FileModuleResolver' loading scripts from the 'scripts' subdirectory
|
|
|
|
/// // with file extension '.x'.
|
|
|
|
/// let resolver = FileModuleResolver::new_with_path_and_extension("./scripts", "x");
|
|
|
|
///
|
|
|
|
/// let mut engine = Engine::new();
|
|
|
|
/// engine.set_module_resolver(Some(resolver));
|
|
|
|
/// ```
|
|
|
|
pub fn new_with_path_and_extension<P: Into<PathBuf>, E: Into<String>>(
|
|
|
|
path: P,
|
|
|
|
extension: E,
|
|
|
|
) -> Self {
|
|
|
|
Self {
|
|
|
|
path: path.into(),
|
|
|
|
extension: extension.into(),
|
|
|
|
}
|
2020-05-05 17:57:25 +02:00
|
|
|
}
|
2020-05-06 13:45:17 +02:00
|
|
|
|
2020-05-05 17:57:25 +02:00
|
|
|
/// Create a new `FileModuleResolver` with the current directory as base path.
|
2020-05-06 13:45:17 +02:00
|
|
|
///
|
|
|
|
/// # Examples
|
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
/// use rhai::Engine;
|
|
|
|
/// use rhai::module_resolvers::FileModuleResolver;
|
|
|
|
///
|
|
|
|
/// // Create a new 'FileModuleResolver' loading scripts from the current directory
|
|
|
|
/// // with file extension '.rhai' (the default).
|
|
|
|
/// let resolver = FileModuleResolver::new();
|
|
|
|
///
|
|
|
|
/// let mut engine = Engine::new();
|
|
|
|
/// engine.set_module_resolver(Some(resolver));
|
|
|
|
/// ```
|
2020-05-05 17:57:25 +02:00
|
|
|
pub fn new() -> Self {
|
|
|
|
Default::default()
|
|
|
|
}
|
2020-05-06 16:26:52 +02:00
|
|
|
|
|
|
|
/// Create a `Module` from a file path.
|
|
|
|
pub fn create_module<P: Into<PathBuf>>(
|
|
|
|
&self,
|
|
|
|
engine: &Engine,
|
2020-05-08 10:49:24 +02:00
|
|
|
scope: Scope,
|
2020-05-06 16:26:52 +02:00
|
|
|
path: &str,
|
|
|
|
) -> Result<Module, Box<EvalAltResult>> {
|
2020-05-08 10:49:24 +02:00
|
|
|
self.resolve(engine, scope, path, Default::default())
|
2020-05-06 16:26:52 +02:00
|
|
|
}
|
2020-05-05 17:57:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
impl ModuleResolver for FileModuleResolver {
|
2020-05-06 10:09:44 +02:00
|
|
|
fn resolve(
|
|
|
|
&self,
|
|
|
|
engine: &Engine,
|
2020-05-08 10:49:24 +02:00
|
|
|
scope: Scope,
|
2020-05-06 10:09:44 +02:00
|
|
|
path: &str,
|
|
|
|
pos: Position,
|
|
|
|
) -> Result<Module, Box<EvalAltResult>> {
|
|
|
|
// Construct the script file path
|
|
|
|
let mut file_path = self.path.clone();
|
2020-05-05 17:57:25 +02:00
|
|
|
file_path.push(path);
|
2020-05-06 10:09:44 +02:00
|
|
|
file_path.set_extension(&self.extension); // Force extension
|
2020-05-05 17:57:25 +02:00
|
|
|
|
|
|
|
// Compile it
|
2020-05-06 10:09:44 +02:00
|
|
|
let ast = engine
|
|
|
|
.compile_file(file_path)
|
2020-05-13 15:58:38 +02:00
|
|
|
.map_err(|err| err.new_position(pos))?;
|
2020-05-05 17:57:25 +02:00
|
|
|
|
2020-05-13 15:58:38 +02:00
|
|
|
Module::eval_ast_as_new(scope, &ast, engine).map_err(|err| err.new_position(pos))
|
2020-05-05 17:57:25 +02:00
|
|
|
}
|
|
|
|
}
|
2020-05-06 10:09:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Static module resolver.
|
2020-05-13 13:21:42 +02:00
|
|
|
#[cfg(not(feature = "no_module"))]
|
2020-05-06 10:09:44 +02:00
|
|
|
mod stat {
|
|
|
|
use super::*;
|
2020-05-05 17:57:25 +02:00
|
|
|
|
2020-05-15 15:40:54 +02:00
|
|
|
/// Module resolution service that serves modules added into it.
|
2020-05-06 13:45:17 +02:00
|
|
|
///
|
|
|
|
/// # Examples
|
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
/// use rhai::{Engine, Module};
|
|
|
|
/// use rhai::module_resolvers::StaticModuleResolver;
|
|
|
|
///
|
|
|
|
/// let mut resolver = StaticModuleResolver::new();
|
|
|
|
///
|
|
|
|
/// let module = Module::new();
|
|
|
|
/// resolver.insert("hello".to_string(), module);
|
|
|
|
///
|
|
|
|
/// let mut engine = Engine::new();
|
|
|
|
/// engine.set_module_resolver(Some(resolver));
|
|
|
|
/// ```
|
2020-05-05 17:57:25 +02:00
|
|
|
#[derive(Debug, Clone, Default)]
|
|
|
|
pub struct StaticModuleResolver(HashMap<String, Module>);
|
|
|
|
|
|
|
|
impl StaticModuleResolver {
|
|
|
|
/// Create a new `StaticModuleResolver`.
|
2020-05-06 13:45:17 +02:00
|
|
|
///
|
|
|
|
/// # Examples
|
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
/// use rhai::{Engine, Module};
|
|
|
|
/// use rhai::module_resolvers::StaticModuleResolver;
|
|
|
|
///
|
|
|
|
/// let mut resolver = StaticModuleResolver::new();
|
|
|
|
///
|
|
|
|
/// let module = Module::new();
|
|
|
|
/// resolver.insert("hello".to_string(), module);
|
|
|
|
///
|
|
|
|
/// let mut engine = Engine::new();
|
|
|
|
/// engine.set_module_resolver(Some(resolver));
|
|
|
|
/// ```
|
2020-05-05 17:57:25 +02:00
|
|
|
pub fn new() -> Self {
|
|
|
|
Default::default()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-06 10:09:44 +02:00
|
|
|
impl Deref for StaticModuleResolver {
|
|
|
|
type Target = HashMap<String, Module>;
|
|
|
|
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
|
|
&self.0
|
2020-05-05 17:57:25 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-06 10:09:44 +02:00
|
|
|
impl DerefMut for StaticModuleResolver {
|
|
|
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
|
|
&mut self.0
|
2020-05-05 17:57:25 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-06 10:09:44 +02:00
|
|
|
impl ModuleResolver for StaticModuleResolver {
|
|
|
|
fn resolve(
|
|
|
|
&self,
|
|
|
|
_: &Engine,
|
2020-05-08 10:49:24 +02:00
|
|
|
_: Scope,
|
2020-05-06 10:09:44 +02:00
|
|
|
path: &str,
|
|
|
|
pos: Position,
|
|
|
|
) -> Result<Module, Box<EvalAltResult>> {
|
|
|
|
self.0
|
|
|
|
.get(path)
|
|
|
|
.cloned()
|
2020-05-11 17:48:50 +02:00
|
|
|
.ok_or_else(|| Box::new(EvalAltResult::ErrorModuleNotFound(path.into(), pos)))
|
2020-05-05 17:57:25 +02:00
|
|
|
}
|
|
|
|
}
|
2020-05-05 06:24:13 +02:00
|
|
|
}
|