Code enhancements.
This commit is contained in:
@@ -748,13 +748,16 @@ impl Dynamic {
|
||||
|
||||
Self(Union::Variant(Box::new(boxed), AccessMode::ReadWrite))
|
||||
}
|
||||
/// Turn the [`Dynamic`] value into a shared [`Dynamic`] value backed by an [`Rc`][std::rc::Rc]`<`[`RefCell`][std::cell::RefCell]`<`[`Dynamic`]`>>`
|
||||
/// or [`Arc`][std::sync::Arc]`<`[`RwLock`][std::sync::RwLock]`<`[`Dynamic`]`>>` depending on the `sync` feature.
|
||||
/// Turn the [`Dynamic`] value into a shared [`Dynamic`] value backed by an
|
||||
/// [`Rc`][std::rc::Rc]`<`[`RefCell`][std::cell::RefCell]`<`[`Dynamic`]`>>` or
|
||||
/// [`Arc`][std::sync::Arc]`<`[`RwLock`][std::sync::RwLock]`<`[`Dynamic`]`>>`
|
||||
/// depending on the `sync` feature.
|
||||
///
|
||||
/// Shared [`Dynamic`] values are relatively cheap to clone as they simply increment the
|
||||
/// reference counts.
|
||||
///
|
||||
/// Shared [`Dynamic`] values can be converted seamlessly to and from ordinary [`Dynamic`] values.
|
||||
/// Shared [`Dynamic`] values can be converted seamlessly to and from ordinary [`Dynamic`]
|
||||
/// values.
|
||||
///
|
||||
/// If the [`Dynamic`] value is already shared, this method returns itself.
|
||||
///
|
||||
@@ -970,8 +973,8 @@ impl Dynamic {
|
||||
///
|
||||
/// If the [`Dynamic`] is not a shared value, it returns itself.
|
||||
///
|
||||
/// If the [`Dynamic`] is a shared value, it returns the shared value if there are
|
||||
/// no outstanding references, or a cloned copy.
|
||||
/// If the [`Dynamic`] is a shared value, it returns the shared value if there are no
|
||||
/// outstanding references, or a cloned copy.
|
||||
#[inline(always)]
|
||||
pub fn flatten(self) -> Self {
|
||||
match self.0 {
|
||||
|
105
src/engine.rs
105
src/engine.rs
@@ -18,7 +18,7 @@ use crate::stdlib::{
|
||||
collections::{HashMap, HashSet},
|
||||
fmt, format,
|
||||
hash::{Hash, Hasher},
|
||||
iter::{empty, once},
|
||||
iter::{empty, once, FromIterator},
|
||||
num::NonZeroU64,
|
||||
num::NonZeroUsize,
|
||||
ops::DerefMut,
|
||||
@@ -137,6 +137,22 @@ impl Imports {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: IntoIterator<Item = (&'a ImmutableString, &'a Shared<Module>)>> From<T> for Imports {
|
||||
fn from(value: T) -> Self {
|
||||
Self(
|
||||
value
|
||||
.into_iter()
|
||||
.map(|(k, v)| (k.clone(), v.clone()))
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
}
|
||||
impl FromIterator<(ImmutableString, Shared<Module>)> for Imports {
|
||||
fn from_iter<T: IntoIterator<Item = (ImmutableString, Shared<Module>)>>(iter: T) -> Self {
|
||||
Self(iter.into_iter().collect())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
#[cfg(debug_assertions)]
|
||||
pub const MAX_CALL_STACK_DEPTH: usize = 8;
|
||||
@@ -609,11 +625,11 @@ pub struct Engine {
|
||||
/// A collection of all modules loaded into the global namespace of the Engine.
|
||||
pub(crate) global_modules: StaticVec<Shared<Module>>,
|
||||
/// A collection of all sub-modules directly loaded into the Engine.
|
||||
pub(crate) global_sub_modules: Imports,
|
||||
pub(crate) global_sub_modules: HashMap<ImmutableString, Shared<Module>>,
|
||||
|
||||
/// A module resolution service.
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
pub(crate) module_resolver: Option<Box<dyn crate::ModuleResolver>>,
|
||||
pub(crate) module_resolver: Box<dyn crate::ModuleResolver>,
|
||||
|
||||
/// A hashmap mapping type names to pretty-print names.
|
||||
pub(crate) type_names: HashMap<String, String>,
|
||||
@@ -745,7 +761,7 @@ impl Engine {
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
module_resolver: Some(Box::new(crate::module::resolvers::FileModuleResolver::new())),
|
||||
module_resolver: Box::new(crate::module::resolvers::FileModuleResolver::new()),
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
#[cfg(any(feature = "no_std", target_arch = "wasm32",))]
|
||||
module_resolver: None,
|
||||
@@ -808,7 +824,7 @@ impl Engine {
|
||||
global_sub_modules: Default::default(),
|
||||
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
module_resolver: None,
|
||||
module_resolver: Box::new(crate::module::resolvers::DummyModuleResolver::new()),
|
||||
|
||||
type_names: Default::default(),
|
||||
disabled_symbols: Default::default(),
|
||||
@@ -849,15 +865,15 @@ impl Engine {
|
||||
|
||||
/// Search for a variable within the scope or within imports,
|
||||
/// depending on whether the variable name is namespace-qualified.
|
||||
pub(crate) fn search_namespace<'s, 'a>(
|
||||
pub(crate) fn search_namespace<'s>(
|
||||
&self,
|
||||
scope: &'s mut Scope,
|
||||
mods: &mut Imports,
|
||||
state: &mut State,
|
||||
lib: &[&Module],
|
||||
this_ptr: &'s mut Option<&mut Dynamic>,
|
||||
expr: &'a Expr,
|
||||
) -> Result<(Target<'s>, &'a str, Position), Box<EvalAltResult>> {
|
||||
expr: &Expr,
|
||||
) -> Result<(Target<'s>, Position), Box<EvalAltResult>> {
|
||||
match expr {
|
||||
Expr::Variable(v) => match v.as_ref() {
|
||||
// Qualified variable
|
||||
@@ -876,7 +892,7 @@ impl Engine {
|
||||
// Module variables are constant
|
||||
let mut target = target.clone();
|
||||
target.set_access_mode(AccessMode::ReadOnly);
|
||||
Ok((target.into(), name, *pos))
|
||||
Ok((target.into(), *pos))
|
||||
}
|
||||
// Normal variable access
|
||||
_ => self.search_scope_only(scope, mods, state, lib, this_ptr, expr),
|
||||
@@ -886,15 +902,15 @@ impl Engine {
|
||||
}
|
||||
|
||||
/// Search for a variable within the scope
|
||||
pub(crate) fn search_scope_only<'s, 'a>(
|
||||
pub(crate) fn search_scope_only<'s>(
|
||||
&self,
|
||||
scope: &'s mut Scope,
|
||||
mods: &mut Imports,
|
||||
state: &mut State,
|
||||
lib: &[&Module],
|
||||
this_ptr: &'s mut Option<&mut Dynamic>,
|
||||
expr: &'a Expr,
|
||||
) -> Result<(Target<'s>, &'a str, Position), Box<EvalAltResult>> {
|
||||
expr: &Expr,
|
||||
) -> Result<(Target<'s>, Position), Box<EvalAltResult>> {
|
||||
let (index, _, Ident { name, pos }) = match expr {
|
||||
Expr::Variable(v) => v.as_ref(),
|
||||
_ => unreachable!(),
|
||||
@@ -903,7 +919,7 @@ impl Engine {
|
||||
// Check if the variable is `this`
|
||||
if name.as_str() == KEYWORD_THIS {
|
||||
if let Some(val) = this_ptr {
|
||||
return Ok(((*val).into(), KEYWORD_THIS, *pos));
|
||||
return Ok(((*val).into(), *pos));
|
||||
} else {
|
||||
return EvalAltResult::ErrorUnboundThis(*pos).into();
|
||||
}
|
||||
@@ -931,7 +947,7 @@ impl Engine {
|
||||
resolve_var(name, index, &context).map_err(|err| err.fill_position(*pos))?
|
||||
{
|
||||
result.set_access_mode(AccessMode::ReadOnly);
|
||||
return Ok((result.into(), name, *pos));
|
||||
return Ok((result.into(), *pos));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -945,7 +961,7 @@ impl Engine {
|
||||
.0
|
||||
};
|
||||
|
||||
let val = scope.get_mut(index);
|
||||
let val = scope.get_mut_by_index(index);
|
||||
|
||||
// Check for data race - probably not necessary because the only place it should conflict is in a method call
|
||||
// when the object variable is also used as a parameter.
|
||||
@@ -953,7 +969,7 @@ impl Engine {
|
||||
// return EvalAltResult::ErrorDataRace(name.into(), *pos).into();
|
||||
// }
|
||||
|
||||
Ok((val.into(), name, *pos))
|
||||
Ok((val.into(), *pos))
|
||||
}
|
||||
|
||||
/// Chain-evaluate a dot/index chain.
|
||||
@@ -1300,7 +1316,7 @@ impl Engine {
|
||||
|
||||
self.inc_operations(state, *var_pos)?;
|
||||
|
||||
let (target, _, pos) =
|
||||
let (target, pos) =
|
||||
self.search_namespace(scope, mods, state, lib, this_ptr, lhs)?;
|
||||
|
||||
// Constants cannot be modified
|
||||
@@ -1594,7 +1610,7 @@ impl Engine {
|
||||
match expr {
|
||||
// var - point directly to the value
|
||||
Expr::Variable(_) => {
|
||||
let (mut target, _, pos) =
|
||||
let (mut target, pos) =
|
||||
self.search_namespace(scope, mods, state, lib, this_ptr, expr)?;
|
||||
|
||||
// If necessary, constants are cloned
|
||||
@@ -1720,7 +1736,7 @@ impl Engine {
|
||||
}
|
||||
}
|
||||
Expr::Variable(_) => {
|
||||
let (val, _, _) = self.search_namespace(scope, mods, state, lib, this_ptr, expr)?;
|
||||
let (val, _) = self.search_namespace(scope, mods, state, lib, this_ptr, expr)?;
|
||||
Ok(val.take_or_clone())
|
||||
}
|
||||
Expr::Property(_) => unreachable!(),
|
||||
@@ -1926,11 +1942,15 @@ impl Engine {
|
||||
let mut rhs_val = self
|
||||
.eval_expr(scope, mods, state, lib, this_ptr, rhs_expr, level)?
|
||||
.flatten();
|
||||
let (mut lhs_ptr, name, pos) =
|
||||
let (mut lhs_ptr, pos) =
|
||||
self.search_namespace(scope, mods, state, lib, this_ptr, lhs_expr)?;
|
||||
|
||||
if !lhs_ptr.is_ref() {
|
||||
return EvalAltResult::ErrorAssignmentToConstant(name.to_string(), pos).into();
|
||||
return EvalAltResult::ErrorAssignmentToConstant(
|
||||
lhs_expr.get_variable_access(false).unwrap().to_string(),
|
||||
pos,
|
||||
)
|
||||
.into();
|
||||
}
|
||||
|
||||
self.inc_operations(state, pos)?;
|
||||
@@ -1938,7 +1958,7 @@ impl Engine {
|
||||
if lhs_ptr.as_ref().is_read_only() {
|
||||
// Assignment to constant variable
|
||||
Err(Box::new(EvalAltResult::ErrorAssignmentToConstant(
|
||||
name.to_string(),
|
||||
lhs_expr.get_variable_access(false).unwrap().to_string(),
|
||||
pos,
|
||||
)))
|
||||
} else if op.is_empty() {
|
||||
@@ -2194,7 +2214,7 @@ impl Engine {
|
||||
state.scope_level += 1;
|
||||
|
||||
for iter_value in func(iter_obj) {
|
||||
let loop_var = scope.get_mut(index);
|
||||
let loop_var = scope.get_mut_by_index(index);
|
||||
|
||||
let value = iter_value.flatten();
|
||||
if cfg!(not(feature = "no_closure")) && loop_var.is_shared() {
|
||||
@@ -2360,31 +2380,24 @@ impl Engine {
|
||||
.eval_expr(scope, mods, state, lib, this_ptr, &expr, level)?
|
||||
.try_cast::<ImmutableString>()
|
||||
{
|
||||
if let Some(resolver) = &self.module_resolver {
|
||||
let module = resolver.resolve(self, &path, expr.position())?;
|
||||
let module = self.module_resolver.resolve(self, &path, expr.position())?;
|
||||
|
||||
if let Some(name_def) = alias {
|
||||
if !module.is_indexed() {
|
||||
// Index the module (making a clone copy if necessary) if it is not indexed
|
||||
let mut module = crate::fn_native::shared_take_or_clone(module);
|
||||
module.build_index();
|
||||
mods.push(name_def.name.clone(), module);
|
||||
} else {
|
||||
mods.push(name_def.name.clone(), module);
|
||||
}
|
||||
// When imports list is modified, clear the functions lookup cache
|
||||
state.functions_cache.clear();
|
||||
if let Some(name_def) = alias {
|
||||
if !module.is_indexed() {
|
||||
// Index the module (making a clone copy if necessary) if it is not indexed
|
||||
let mut module = crate::fn_native::shared_take_or_clone(module);
|
||||
module.build_index();
|
||||
mods.push(name_def.name.clone(), module);
|
||||
} else {
|
||||
mods.push(name_def.name.clone(), module);
|
||||
}
|
||||
|
||||
state.modules += 1;
|
||||
|
||||
Ok(Dynamic::UNIT)
|
||||
} else {
|
||||
Err(
|
||||
EvalAltResult::ErrorModuleNotFound(path.to_string(), expr.position())
|
||||
.into(),
|
||||
)
|
||||
// When imports list is modified, clear the functions lookup cache
|
||||
state.functions_cache.clear();
|
||||
}
|
||||
|
||||
state.modules += 1;
|
||||
|
||||
Ok(Dynamic::UNIT)
|
||||
} else {
|
||||
Err(self.make_type_mismatch_err::<ImmutableString>("", expr.position()))
|
||||
}
|
||||
@@ -2410,7 +2423,7 @@ impl Engine {
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
Stmt::Share(x) => {
|
||||
if let Some((index, _)) = scope.get_index(&x.name) {
|
||||
let val = scope.get_mut(index);
|
||||
let val = scope.get_mut_by_index(index);
|
||||
|
||||
if !val.is_shared() {
|
||||
// Replace the variable with a shared value.
|
||||
|
@@ -209,9 +209,10 @@ impl Engine {
|
||||
pub fn register_get<T: Variant + Clone, U: Variant + Clone>(
|
||||
&mut self,
|
||||
name: &str,
|
||||
callback: impl Fn(&mut T) -> U + SendSync + 'static,
|
||||
get_fn: impl Fn(&mut T) -> U + SendSync + 'static,
|
||||
) -> &mut Self {
|
||||
crate::RegisterFn::register_fn(self, &crate::engine::make_getter(name), callback)
|
||||
use crate::{engine::make_getter, RegisterFn};
|
||||
self.register_fn(&make_getter(name), get_fn)
|
||||
}
|
||||
/// Register a getter function for a member of a registered type with the [`Engine`].
|
||||
///
|
||||
@@ -255,13 +256,10 @@ impl Engine {
|
||||
pub fn register_get_result<T: Variant + Clone>(
|
||||
&mut self,
|
||||
name: &str,
|
||||
callback: impl Fn(&mut T) -> Result<Dynamic, Box<EvalAltResult>> + SendSync + 'static,
|
||||
get_fn: impl Fn(&mut T) -> Result<Dynamic, Box<EvalAltResult>> + SendSync + 'static,
|
||||
) -> &mut Self {
|
||||
crate::RegisterResultFn::register_result_fn(
|
||||
self,
|
||||
&crate::engine::make_getter(name),
|
||||
callback,
|
||||
)
|
||||
use crate::{engine::make_getter, RegisterResultFn};
|
||||
self.register_result_fn(&make_getter(name), get_fn)
|
||||
}
|
||||
/// Register a setter function for a member of a registered type with the [`Engine`].
|
||||
///
|
||||
@@ -304,9 +302,10 @@ impl Engine {
|
||||
pub fn register_set<T: Variant + Clone, U: Variant + Clone>(
|
||||
&mut self,
|
||||
name: &str,
|
||||
callback: impl Fn(&mut T, U) + SendSync + 'static,
|
||||
set_fn: impl Fn(&mut T, U) + SendSync + 'static,
|
||||
) -> &mut Self {
|
||||
crate::RegisterFn::register_fn(self, &crate::engine::make_setter(name), callback)
|
||||
use crate::{engine::make_setter, RegisterFn};
|
||||
self.register_fn(&make_setter(name), set_fn)
|
||||
}
|
||||
/// Register a setter function for a member of a registered type with the [`Engine`].
|
||||
///
|
||||
@@ -352,13 +351,12 @@ impl Engine {
|
||||
pub fn register_set_result<T: Variant + Clone, U: Variant + Clone>(
|
||||
&mut self,
|
||||
name: &str,
|
||||
callback: impl Fn(&mut T, U) -> Result<(), Box<EvalAltResult>> + SendSync + 'static,
|
||||
set_fn: impl Fn(&mut T, U) -> Result<(), Box<EvalAltResult>> + SendSync + 'static,
|
||||
) -> &mut Self {
|
||||
crate::RegisterResultFn::register_result_fn(
|
||||
self,
|
||||
&crate::engine::make_setter(name),
|
||||
move |obj: &mut T, value: U| callback(obj, value).map(Into::into),
|
||||
)
|
||||
use crate::{engine::make_setter, RegisterResultFn};
|
||||
self.register_result_fn(&make_setter(name), move |obj: &mut T, value: U| {
|
||||
set_fn(obj, value).map(Into::into)
|
||||
})
|
||||
}
|
||||
/// Short-hand for registering both getter and setter functions
|
||||
/// of a registered type with the [`Engine`].
|
||||
@@ -453,7 +451,7 @@ impl Engine {
|
||||
#[inline(always)]
|
||||
pub fn register_indexer_get<T: Variant + Clone, X: Variant + Clone, U: Variant + Clone>(
|
||||
&mut self,
|
||||
callback: impl Fn(&mut T, X) -> U + SendSync + 'static,
|
||||
get_fn: impl Fn(&mut T, X) -> U + SendSync + 'static,
|
||||
) -> &mut Self {
|
||||
if TypeId::of::<T>() == TypeId::of::<Array>() {
|
||||
panic!("Cannot register indexer for arrays.");
|
||||
@@ -469,7 +467,8 @@ impl Engine {
|
||||
panic!("Cannot register indexer for strings.");
|
||||
}
|
||||
|
||||
crate::RegisterFn::register_fn(self, crate::engine::FN_IDX_GET, callback)
|
||||
use crate::{engine::FN_IDX_GET, RegisterFn};
|
||||
self.register_fn(FN_IDX_GET, get_fn)
|
||||
}
|
||||
/// Register an index getter for a custom type with the [`Engine`].
|
||||
///
|
||||
@@ -518,7 +517,7 @@ impl Engine {
|
||||
#[inline(always)]
|
||||
pub fn register_indexer_get_result<T: Variant + Clone, X: Variant + Clone>(
|
||||
&mut self,
|
||||
callback: impl Fn(&mut T, X) -> Result<Dynamic, Box<EvalAltResult>> + SendSync + 'static,
|
||||
get_fn: impl Fn(&mut T, X) -> Result<Dynamic, Box<EvalAltResult>> + SendSync + 'static,
|
||||
) -> &mut Self {
|
||||
if TypeId::of::<T>() == TypeId::of::<Array>() {
|
||||
panic!("Cannot register indexer for arrays.");
|
||||
@@ -534,7 +533,8 @@ impl Engine {
|
||||
panic!("Cannot register indexer for strings.");
|
||||
}
|
||||
|
||||
crate::RegisterResultFn::register_result_fn(self, crate::engine::FN_IDX_GET, callback)
|
||||
use crate::{engine::FN_IDX_GET, RegisterResultFn};
|
||||
self.register_result_fn(FN_IDX_GET, get_fn)
|
||||
}
|
||||
/// Register an index setter for a custom type with the [`Engine`].
|
||||
///
|
||||
@@ -581,7 +581,7 @@ impl Engine {
|
||||
#[inline(always)]
|
||||
pub fn register_indexer_set<T: Variant + Clone, X: Variant + Clone, U: Variant + Clone>(
|
||||
&mut self,
|
||||
callback: impl Fn(&mut T, X, U) + SendSync + 'static,
|
||||
set_fn: impl Fn(&mut T, X, U) + SendSync + 'static,
|
||||
) -> &mut Self {
|
||||
if TypeId::of::<T>() == TypeId::of::<Array>() {
|
||||
panic!("Cannot register indexer for arrays.");
|
||||
@@ -597,7 +597,8 @@ impl Engine {
|
||||
panic!("Cannot register indexer for strings.");
|
||||
}
|
||||
|
||||
crate::RegisterFn::register_fn(self, crate::engine::FN_IDX_SET, callback)
|
||||
use crate::{engine::FN_IDX_SET, RegisterFn};
|
||||
self.register_fn(FN_IDX_SET, set_fn)
|
||||
}
|
||||
/// Register an index setter for a custom type with the [`Engine`].
|
||||
///
|
||||
@@ -651,7 +652,7 @@ impl Engine {
|
||||
U: Variant + Clone,
|
||||
>(
|
||||
&mut self,
|
||||
callback: impl Fn(&mut T, X, U) -> Result<(), Box<EvalAltResult>> + SendSync + 'static,
|
||||
set_fn: impl Fn(&mut T, X, U) -> Result<(), Box<EvalAltResult>> + SendSync + 'static,
|
||||
) -> &mut Self {
|
||||
if TypeId::of::<T>() == TypeId::of::<Array>() {
|
||||
panic!("Cannot register indexer for arrays.");
|
||||
@@ -667,11 +668,10 @@ impl Engine {
|
||||
panic!("Cannot register indexer for strings.");
|
||||
}
|
||||
|
||||
crate::RegisterResultFn::register_result_fn(
|
||||
self,
|
||||
crate::engine::FN_IDX_SET,
|
||||
move |obj: &mut T, index: X, value: U| callback(obj, index, value).map(Into::into),
|
||||
)
|
||||
use crate::{engine::FN_IDX_SET, RegisterResultFn};
|
||||
self.register_result_fn(FN_IDX_SET, move |obj: &mut T, index: X, value: U| {
|
||||
set_fn(obj, index, value).map(Into::into)
|
||||
})
|
||||
}
|
||||
/// Short-hand for register both index getter and setter functions for a custom type with the [`Engine`].
|
||||
///
|
||||
@@ -755,7 +755,7 @@ impl Engine {
|
||||
///
|
||||
/// ```
|
||||
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
||||
/// use rhai::{Engine, Module};
|
||||
/// use rhai::{Engine, Shared, Module};
|
||||
///
|
||||
/// let mut engine = Engine::new();
|
||||
///
|
||||
@@ -763,9 +763,18 @@ impl Engine {
|
||||
/// let mut module = Module::new();
|
||||
/// module.set_fn_1("calc", |x: i64| Ok(x + 1));
|
||||
///
|
||||
/// // Register the module as a fixed sub-module
|
||||
/// engine.register_static_module("CalcService", module.into());
|
||||
/// let module: Shared<Module> = module.into();
|
||||
///
|
||||
/// // Register the module as a fixed sub-module
|
||||
/// engine.register_static_module("foo::bar::baz", module.clone());
|
||||
///
|
||||
/// // Multiple registrations to the same partial path is also OK!
|
||||
/// engine.register_static_module("foo::bar::hello", module.clone());
|
||||
///
|
||||
/// engine.register_static_module("CalcService", module);
|
||||
///
|
||||
/// assert_eq!(engine.eval::<i64>("foo::bar::baz::calc(41)")?, 42);
|
||||
/// assert_eq!(engine.eval::<i64>("foo::bar::hello::calc(41)")?, 42);
|
||||
/// assert_eq!(engine.eval::<i64>("CalcService::calc(41)")?, 42);
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
@@ -773,19 +782,49 @@ impl Engine {
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
pub fn register_static_module(
|
||||
&mut self,
|
||||
name: impl Into<crate::ImmutableString>,
|
||||
name: impl AsRef<str>,
|
||||
module: Shared<Module>,
|
||||
) -> &mut Self {
|
||||
if !module.is_indexed() {
|
||||
// Index the module (making a clone copy if necessary) if it is not indexed
|
||||
let mut module = crate::fn_native::shared_take_or_clone(module);
|
||||
module.build_index();
|
||||
self.global_sub_modules.push(name, module);
|
||||
} else {
|
||||
self.global_sub_modules.push(name, module);
|
||||
fn register_static_module_raw(
|
||||
root: &mut crate::stdlib::collections::HashMap<crate::ImmutableString, Shared<Module>>,
|
||||
name: impl AsRef<str>,
|
||||
module: Shared<Module>,
|
||||
) {
|
||||
let separator = crate::token::Token::DoubleColon.syntax();
|
||||
|
||||
if !name.as_ref().contains(separator.as_ref()) {
|
||||
if !module.is_indexed() {
|
||||
// Index the module (making a clone copy if necessary) if it is not indexed
|
||||
let mut module = crate::fn_native::shared_take_or_clone(module);
|
||||
module.build_index();
|
||||
root.insert(name.as_ref().trim().into(), module.into());
|
||||
} else {
|
||||
root.insert(name.as_ref().trim().into(), module);
|
||||
}
|
||||
} else {
|
||||
let mut iter = name.as_ref().splitn(2, separator.as_ref());
|
||||
let sub_module = iter.next().unwrap().trim();
|
||||
let remainder = iter.next().unwrap().trim();
|
||||
|
||||
if !root.contains_key(sub_module) {
|
||||
let mut m: Module = Default::default();
|
||||
register_static_module_raw(m.sub_modules_mut(), remainder, module);
|
||||
m.build_index();
|
||||
root.insert(sub_module.into(), m.into());
|
||||
} else {
|
||||
let m = root.remove(sub_module).unwrap();
|
||||
let mut m = crate::fn_native::shared_take_or_clone(m);
|
||||
register_static_module_raw(m.sub_modules_mut(), remainder, module);
|
||||
m.build_index();
|
||||
root.insert(sub_module.into(), m.into());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
register_static_module_raw(&mut self.global_sub_modules, name.as_ref(), module);
|
||||
self
|
||||
}
|
||||
|
||||
/// Register a shared [`Module`][crate::Module] as a static module namespace with the [`Engine`].
|
||||
///
|
||||
/// ## Deprecated
|
||||
@@ -796,7 +835,7 @@ impl Engine {
|
||||
#[deprecated = "use `register_static_module` instead"]
|
||||
pub fn register_module(
|
||||
&mut self,
|
||||
name: impl Into<crate::ImmutableString>,
|
||||
name: impl AsRef<str>,
|
||||
module: impl Into<Shared<Module>>,
|
||||
) -> &mut Self {
|
||||
self.register_static_module(name, module.into())
|
||||
@@ -1407,7 +1446,7 @@ impl Engine {
|
||||
scope: &mut Scope,
|
||||
ast: &AST,
|
||||
) -> Result<T, Box<EvalAltResult>> {
|
||||
let mods = &mut self.global_sub_modules.clone();
|
||||
let mods = &mut (&self.global_sub_modules).into();
|
||||
|
||||
let result = self.eval_ast_with_scope_raw(scope, mods, ast)?;
|
||||
|
||||
@@ -1493,7 +1532,7 @@ impl Engine {
|
||||
scope: &mut Scope,
|
||||
ast: &AST,
|
||||
) -> Result<(), Box<EvalAltResult>> {
|
||||
let mods = &mut self.global_sub_modules.clone();
|
||||
let mods = &mut (&self.global_sub_modules).into();
|
||||
let state = &mut State {
|
||||
source: ast.clone_source(),
|
||||
..Default::default()
|
||||
@@ -1539,12 +1578,12 @@ impl Engine {
|
||||
/// ```
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
#[inline]
|
||||
pub fn call_fn<A: crate::fn_args::FuncArgs, T: Variant + Clone>(
|
||||
pub fn call_fn<T: Variant + Clone>(
|
||||
&self,
|
||||
scope: &mut Scope,
|
||||
ast: &AST,
|
||||
name: &str,
|
||||
args: A,
|
||||
args: impl crate::fn_args::FuncArgs,
|
||||
) -> Result<T, Box<EvalAltResult>> {
|
||||
let mut arg_values = args.into_vec();
|
||||
let mut args: crate::StaticVec<_> = arg_values.as_mut().iter_mut().collect();
|
||||
@@ -1650,7 +1689,7 @@ impl Engine {
|
||||
.ok_or_else(|| EvalAltResult::ErrorFunctionNotFound(name.into(), Position::NONE))?;
|
||||
|
||||
let mut state = Default::default();
|
||||
let mut mods = self.global_sub_modules.clone();
|
||||
let mut mods = (&self.global_sub_modules).into();
|
||||
|
||||
// Check for data race.
|
||||
if cfg!(not(feature = "no_closure")) {
|
||||
|
@@ -168,9 +168,9 @@ impl Engine {
|
||||
#[inline(always)]
|
||||
pub fn set_module_resolver(
|
||||
&mut self,
|
||||
resolver: Option<impl crate::ModuleResolver + 'static>,
|
||||
resolver: impl crate::ModuleResolver + 'static,
|
||||
) -> &mut Self {
|
||||
self.module_resolver = resolver.map(|f| Box::new(f) as Box<dyn crate::ModuleResolver>);
|
||||
self.module_resolver = Box::new(resolver);
|
||||
self
|
||||
}
|
||||
/// Disable a particular keyword or operator in the language.
|
||||
|
@@ -1041,7 +1041,7 @@ impl Engine {
|
||||
.map(|expr| self.eval_expr(scope, mods, state, lib, this_ptr, expr, level))
|
||||
.collect::<Result<_, _>>()?;
|
||||
|
||||
let (mut target, _, pos) =
|
||||
let (mut target, pos) =
|
||||
self.search_namespace(scope, mods, state, lib, this_ptr, &args_expr[0])?;
|
||||
|
||||
if target.as_ref().is_read_only() {
|
||||
@@ -1137,7 +1137,7 @@ impl Engine {
|
||||
.collect::<Result<_, _>>()?;
|
||||
|
||||
// Get target reference to first argument
|
||||
let (target, _, pos) =
|
||||
let (target, pos) =
|
||||
self.search_scope_only(scope, mods, state, lib, this_ptr, &args_expr[0])?;
|
||||
|
||||
self.inc_operations(state, pos)?;
|
||||
|
@@ -27,15 +27,15 @@ pub trait Func<ARGS, RET> {
|
||||
/// // Func takes two type parameters:
|
||||
/// // 1) a tuple made up of the types of the script function's parameters
|
||||
/// // 2) the return type of the script function
|
||||
/// //
|
||||
///
|
||||
/// // 'func' will have type Box<dyn Fn(i64, String) -> Result<bool, Box<EvalAltResult>>> and is callable!
|
||||
/// let func = Func::<(i64, String), bool>::create_from_ast(
|
||||
/// // ^^^^^^^^^^^^^ function parameter types in tuple
|
||||
///
|
||||
/// engine, // the 'Engine' is consumed into the closure
|
||||
/// ast, // the 'AST'
|
||||
/// "calc" // the entry-point function name
|
||||
/// );
|
||||
/// engine, // the 'Engine' is consumed into the closure
|
||||
/// ast, // the 'AST'
|
||||
/// "calc" // the entry-point function name
|
||||
/// );
|
||||
///
|
||||
/// func(123, "hello".to_string())? == false; // call the anonymous function
|
||||
/// # Ok(())
|
||||
@@ -58,15 +58,15 @@ pub trait Func<ARGS, RET> {
|
||||
/// // Func takes two type parameters:
|
||||
/// // 1) a tuple made up of the types of the script function's parameters
|
||||
/// // 2) the return type of the script function
|
||||
/// //
|
||||
///
|
||||
/// // 'func' will have type Box<dyn Fn(i64, String) -> Result<bool, Box<EvalAltResult>>> and is callable!
|
||||
/// let func = Func::<(i64, String), bool>::create_from_script(
|
||||
/// // ^^^^^^^^^^^^^ function parameter types in tuple
|
||||
///
|
||||
/// engine, // the 'Engine' is consumed into the closure
|
||||
/// script, // the script, notice number of parameters must match
|
||||
/// "calc" // the entry-point function name
|
||||
/// )?;
|
||||
/// engine, // the 'Engine' is consumed into the closure
|
||||
/// script, // the script, notice number of parameters must match
|
||||
/// "calc" // the entry-point function name
|
||||
/// )?;
|
||||
///
|
||||
/// func(123, "hello".to_string())? == false; // call the anonymous function
|
||||
/// # Ok(())
|
||||
|
@@ -3,7 +3,14 @@
|
||||
use crate::ast::{FnAccess, ScriptFnDef};
|
||||
use crate::engine::Imports;
|
||||
use crate::plugin::PluginFunction;
|
||||
use crate::stdlib::{boxed::Box, convert::TryFrom, fmt, iter::empty, mem, string::String};
|
||||
use crate::stdlib::{
|
||||
boxed::Box,
|
||||
convert::{TryFrom, TryInto},
|
||||
fmt,
|
||||
iter::empty,
|
||||
mem,
|
||||
string::String,
|
||||
};
|
||||
use crate::token::is_valid_identifier;
|
||||
use crate::{
|
||||
calc_script_fn_hash, Dynamic, Engine, EvalAltResult, EvalContext, ImmutableString, Module,
|
||||
@@ -153,25 +160,13 @@ impl<'e, 's, 'a, 'm, 'pm> NativeCallContext<'e, 's, 'a, 'm, 'pm> {
|
||||
args: &mut [&mut Dynamic],
|
||||
def_value: Option<&Dynamic>,
|
||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
let mut mods = self.mods.cloned().unwrap_or_default();
|
||||
|
||||
let hash_script = calc_script_fn_hash(
|
||||
empty(),
|
||||
fn_name,
|
||||
if is_method {
|
||||
args.len() - 1
|
||||
} else {
|
||||
args.len()
|
||||
},
|
||||
);
|
||||
|
||||
self.engine()
|
||||
.exec_fn_call(
|
||||
&mut mods,
|
||||
&mut self.mods.cloned().unwrap_or_default(),
|
||||
&mut Default::default(),
|
||||
self.lib,
|
||||
fn_name,
|
||||
hash_script,
|
||||
calc_script_fn_hash(empty(), fn_name, args.len() - if is_method { 1 } else { 0 }),
|
||||
args,
|
||||
is_method,
|
||||
is_method,
|
||||
@@ -225,11 +220,15 @@ pub type FnCallArgs<'a> = [&'a mut Dynamic];
|
||||
|
||||
/// A general function pointer, which may carry additional (i.e. curried) argument values
|
||||
/// to be passed onto a function during a call.
|
||||
#[derive(Debug, Clone, Default)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FnPtr(ImmutableString, StaticVec<Dynamic>);
|
||||
|
||||
impl FnPtr {
|
||||
/// Create a new function pointer.
|
||||
pub fn new(name: impl Into<ImmutableString>) -> Result<Self, Box<EvalAltResult>> {
|
||||
name.into().try_into()
|
||||
}
|
||||
/// Create a new function pointer without checking its parameters.
|
||||
#[inline(always)]
|
||||
pub(crate) fn new_unchecked(
|
||||
name: impl Into<ImmutableString>,
|
||||
@@ -257,7 +256,24 @@ impl FnPtr {
|
||||
pub fn curry(&self) -> &[Dynamic] {
|
||||
self.1.as_ref()
|
||||
}
|
||||
/// Does this function pointer refer to an anonymous function?
|
||||
/// Add a new curried argument.
|
||||
#[inline(always)]
|
||||
pub fn add_curry(&mut self, value: Dynamic) -> &mut Self {
|
||||
self.1.push(value);
|
||||
self
|
||||
}
|
||||
/// Set curried arguments to the function pointer.
|
||||
#[inline(always)]
|
||||
pub fn set_curry(&mut self, values: impl IntoIterator<Item = Dynamic>) -> &mut Self {
|
||||
self.1 = values.into_iter().collect();
|
||||
self
|
||||
}
|
||||
/// Is the function pointer curried?
|
||||
#[inline(always)]
|
||||
pub fn is_curried(&self) -> bool {
|
||||
!self.1.is_empty()
|
||||
}
|
||||
/// Does the function pointer refer to an anonymous function?
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
#[inline(always)]
|
||||
pub fn is_anonymous(&self) -> bool {
|
||||
|
@@ -35,9 +35,9 @@ use crate::Map;
|
||||
/// A type representing the namespace of a function.
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
|
||||
pub enum FnNamespace {
|
||||
/// Global namespace.
|
||||
/// Expose to global namespace.
|
||||
Global,
|
||||
/// Internal only.
|
||||
/// Module namespace only.
|
||||
Internal,
|
||||
}
|
||||
|
||||
@@ -465,6 +465,16 @@ impl Module {
|
||||
.map(|FuncInfo { func, .. }| func.get_fn_def())
|
||||
}
|
||||
|
||||
/// Get a mutable reference to the underlying [`HashMap`] of sub-modules.
|
||||
#[inline(always)]
|
||||
pub(crate) fn sub_modules_mut(&mut self) -> &mut HashMap<ImmutableString, Shared<Module>> {
|
||||
// We must assume that the user has changed the sub-modules
|
||||
// (otherwise why take a mutable reference?)
|
||||
self.indexed = false;
|
||||
|
||||
&mut self.modules
|
||||
}
|
||||
|
||||
/// Does a sub-module exist in the module?
|
||||
///
|
||||
/// # Example
|
||||
@@ -1699,7 +1709,7 @@ impl Module {
|
||||
ast: &crate::AST,
|
||||
engine: &crate::Engine,
|
||||
) -> Result<Self, Box<EvalAltResult>> {
|
||||
let mut mods = engine.global_sub_modules.clone();
|
||||
let mut mods: crate::engine::Imports = (&engine.global_sub_modules).into();
|
||||
let orig_mods_len = mods.len();
|
||||
|
||||
// Run the script
|
||||
|
@@ -16,7 +16,7 @@ use crate::{Engine, EvalAltResult, Module, ModuleResolver, Position, Shared};
|
||||
/// collection.push(resolver);
|
||||
///
|
||||
/// let mut engine = Engine::new();
|
||||
/// engine.set_module_resolver(Some(collection));
|
||||
/// engine.set_module_resolver(collection);
|
||||
/// ```
|
||||
#[derive(Default)]
|
||||
pub struct ModuleResolversCollection(Vec<Box<dyn ModuleResolver>>);
|
||||
@@ -36,16 +36,41 @@ impl ModuleResolversCollection {
|
||||
/// collection.push(resolver);
|
||||
///
|
||||
/// let mut engine = Engine::new();
|
||||
/// engine.set_module_resolver(Some(collection));
|
||||
/// engine.set_module_resolver(collection);
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn new() -> Self {
|
||||
Default::default()
|
||||
}
|
||||
/// Add a module keyed by its path.
|
||||
/// Append a module resolver to the end.
|
||||
#[inline(always)]
|
||||
pub fn push(&mut self, resolver: impl ModuleResolver + 'static) {
|
||||
pub fn push(&mut self, resolver: impl ModuleResolver + 'static) -> &mut Self {
|
||||
self.0.push(Box::new(resolver));
|
||||
self
|
||||
}
|
||||
/// Insert a module resolver to an offset index.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the index is out of bounds.
|
||||
#[inline(always)]
|
||||
pub fn insert(&mut self, index: usize, resolver: impl ModuleResolver + 'static) -> &mut Self {
|
||||
self.0.insert(index, Box::new(resolver));
|
||||
self
|
||||
}
|
||||
/// Remove the last module resolver from the end, if any.
|
||||
#[inline(always)]
|
||||
pub fn pop(&mut self) -> Option<Box<dyn ModuleResolver>> {
|
||||
self.0.pop()
|
||||
}
|
||||
/// Remove a module resolver at an offset index.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the index is out of bounds.
|
||||
#[inline(always)]
|
||||
pub fn remove(&mut self, index: usize) -> Box<dyn ModuleResolver> {
|
||||
self.0.remove(index)
|
||||
}
|
||||
/// Get an iterator of all the module resolvers.
|
||||
#[inline(always)]
|
||||
@@ -59,8 +84,9 @@ impl ModuleResolversCollection {
|
||||
}
|
||||
/// Remove all module resolvers.
|
||||
#[inline(always)]
|
||||
pub fn clear(&mut self) {
|
||||
pub fn clear(&mut self) -> &mut Self {
|
||||
self.0.clear();
|
||||
self
|
||||
}
|
||||
/// Is this [`ModuleResolversCollection`] empty?
|
||||
#[inline(always)]
|
||||
@@ -75,8 +101,9 @@ impl ModuleResolversCollection {
|
||||
/// Add another [`ModuleResolversCollection`] to the end of this collection.
|
||||
/// The other [`ModuleResolversCollection`] is consumed.
|
||||
#[inline(always)]
|
||||
pub fn append(&mut self, other: Self) {
|
||||
pub fn append(&mut self, other: Self) -> &mut Self {
|
||||
self.0.extend(other.0.into_iter());
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
|
48
src/module/resolvers/dummy.rs
Normal file
48
src/module/resolvers/dummy.rs
Normal file
@@ -0,0 +1,48 @@
|
||||
use crate::stdlib::boxed::Box;
|
||||
use crate::{Engine, EvalAltResult, Module, ModuleResolver, Position, Shared};
|
||||
|
||||
/// Empty/disabled module resolution service that acts as a dummy.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use rhai::{Engine, Module};
|
||||
/// use rhai::module_resolvers::DummyModuleResolver;
|
||||
///
|
||||
/// let resolver = DummyModuleResolver::new();
|
||||
/// let mut engine = Engine::new();
|
||||
/// engine.set_module_resolver(resolver);
|
||||
/// ```
|
||||
#[derive(Debug, Copy, Eq, PartialEq, Clone, Default, Hash)]
|
||||
pub struct DummyModuleResolver;
|
||||
|
||||
impl DummyModuleResolver {
|
||||
/// Create a new [`DummyModuleResolver`].
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use rhai::{Engine, Module};
|
||||
/// use rhai::module_resolvers::DummyModuleResolver;
|
||||
///
|
||||
/// let resolver = DummyModuleResolver::new();
|
||||
/// let mut engine = Engine::new();
|
||||
/// engine.set_module_resolver(resolver);
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn new() -> Self {
|
||||
Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
impl ModuleResolver for DummyModuleResolver {
|
||||
#[inline(always)]
|
||||
fn resolve(
|
||||
&self,
|
||||
_: &Engine,
|
||||
path: &str,
|
||||
pos: Position,
|
||||
) -> Result<Shared<Module>, Box<EvalAltResult>> {
|
||||
EvalAltResult::ErrorModuleNotFound(path.into(), pos).into()
|
||||
}
|
||||
}
|
@@ -1,5 +1,9 @@
|
||||
use crate::stdlib::{
|
||||
boxed::Box, collections::HashMap, io::Error as IoError, path::PathBuf, string::String,
|
||||
boxed::Box,
|
||||
collections::HashMap,
|
||||
io::Error as IoError,
|
||||
path::{Path, PathBuf},
|
||||
string::String,
|
||||
};
|
||||
use crate::{Engine, EvalAltResult, Module, ModuleResolver, Position, Shared};
|
||||
|
||||
@@ -31,7 +35,7 @@ use crate::{Engine, EvalAltResult, Module, ModuleResolver, Position, Shared};
|
||||
///
|
||||
/// let mut engine = Engine::new();
|
||||
///
|
||||
/// engine.set_module_resolver(Some(resolver));
|
||||
/// engine.set_module_resolver(resolver);
|
||||
/// ```
|
||||
#[derive(Debug)]
|
||||
pub struct FileModuleResolver {
|
||||
@@ -65,10 +69,10 @@ impl FileModuleResolver {
|
||||
/// let resolver = FileModuleResolver::new_with_path("./scripts");
|
||||
///
|
||||
/// let mut engine = Engine::new();
|
||||
/// engine.set_module_resolver(Some(resolver));
|
||||
/// engine.set_module_resolver(resolver);
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn new_with_path<P: Into<PathBuf>>(path: P) -> Self {
|
||||
pub fn new_with_path(path: impl Into<PathBuf>) -> Self {
|
||||
Self::new_with_path_and_extension(path, "rhai")
|
||||
}
|
||||
|
||||
@@ -87,12 +91,12 @@ impl FileModuleResolver {
|
||||
/// let resolver = FileModuleResolver::new_with_path_and_extension("./scripts", "x");
|
||||
///
|
||||
/// let mut engine = Engine::new();
|
||||
/// engine.set_module_resolver(Some(resolver));
|
||||
/// engine.set_module_resolver(resolver);
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn new_with_path_and_extension<P: Into<PathBuf>, E: Into<String>>(
|
||||
path: P,
|
||||
extension: E,
|
||||
pub fn new_with_path_and_extension(
|
||||
path: impl Into<PathBuf>,
|
||||
extension: impl Into<String>,
|
||||
) -> Self {
|
||||
Self {
|
||||
path: path.into(),
|
||||
@@ -114,12 +118,64 @@ impl FileModuleResolver {
|
||||
/// let resolver = FileModuleResolver::new();
|
||||
///
|
||||
/// let mut engine = Engine::new();
|
||||
/// engine.set_module_resolver(Some(resolver));
|
||||
/// engine.set_module_resolver(resolver);
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn new() -> Self {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
/// Get the base path for script files.
|
||||
#[inline(always)]
|
||||
pub fn path(&self) -> &Path {
|
||||
self.path.as_ref()
|
||||
}
|
||||
/// Set the base path for script files.
|
||||
#[inline(always)]
|
||||
pub fn set_path(&mut self, path: impl Into<PathBuf>) -> &mut Self {
|
||||
self.path = path.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// Get the script file extension.
|
||||
#[inline(always)]
|
||||
pub fn extension(&self) -> &str {
|
||||
&self.extension
|
||||
}
|
||||
|
||||
/// Set the script file extension.
|
||||
#[inline(always)]
|
||||
pub fn set_extension(&mut self, extension: impl Into<String>) -> &mut Self {
|
||||
self.extension = extension.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// Empty the internal cache.
|
||||
#[inline(always)]
|
||||
pub fn clear_cache(&mut self) {
|
||||
#[cfg(not(feature = "sync"))]
|
||||
self.cache.borrow_mut().clear();
|
||||
#[cfg(feature = "sync")]
|
||||
self.cache.write().unwrap().clear();
|
||||
}
|
||||
|
||||
/// Empty the internal cache.
|
||||
#[inline(always)]
|
||||
pub fn clear_cache_for_path(&mut self, path: impl AsRef<Path>) -> Option<Shared<Module>> {
|
||||
#[cfg(not(feature = "sync"))]
|
||||
return self
|
||||
.cache
|
||||
.borrow_mut()
|
||||
.remove_entry(path.as_ref())
|
||||
.map(|(_, v)| v);
|
||||
#[cfg(feature = "sync")]
|
||||
return self
|
||||
.cache
|
||||
.write()
|
||||
.unwrap()
|
||||
.remove_entry(path.as_ref())
|
||||
.map(|(_, v)| v);
|
||||
}
|
||||
}
|
||||
|
||||
impl ModuleResolver for FileModuleResolver {
|
||||
|
@@ -2,6 +2,9 @@ use crate::fn_native::SendSync;
|
||||
use crate::stdlib::boxed::Box;
|
||||
use crate::{Engine, EvalAltResult, Module, Position, Shared};
|
||||
|
||||
mod dummy;
|
||||
pub use dummy::DummyModuleResolver;
|
||||
|
||||
mod collection;
|
||||
pub use collection::ModuleResolversCollection;
|
||||
|
||||
|
@@ -16,7 +16,7 @@ use crate::{Engine, EvalAltResult, Module, ModuleResolver, Position, Shared};
|
||||
///
|
||||
/// let mut engine = Engine::new();
|
||||
///
|
||||
/// engine.set_module_resolver(Some(resolver));
|
||||
/// engine.set_module_resolver(resolver);
|
||||
/// ```
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct StaticModuleResolver(HashMap<String, Shared<Module>>);
|
||||
@@ -36,7 +36,7 @@ impl StaticModuleResolver {
|
||||
/// resolver.insert("hello", module);
|
||||
///
|
||||
/// let mut engine = Engine::new();
|
||||
/// engine.set_module_resolver(Some(resolver));
|
||||
/// engine.set_module_resolver(resolver);
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn new() -> Self {
|
||||
@@ -60,8 +60,13 @@ impl StaticModuleResolver {
|
||||
}
|
||||
/// Get an iterator of all the modules.
|
||||
#[inline(always)]
|
||||
pub fn iter(&self) -> impl Iterator<Item = (&str, Shared<Module>)> {
|
||||
self.0.iter().map(|(k, v)| (k.as_str(), v.clone()))
|
||||
pub fn iter(&self) -> impl Iterator<Item = (&str, &Shared<Module>)> {
|
||||
self.0.iter().map(|(k, v)| (k.as_str(), v))
|
||||
}
|
||||
/// Get a mutable iterator of all the modules.
|
||||
#[inline(always)]
|
||||
pub fn iter_mut(&mut self) -> impl Iterator<Item = (&str, &mut Shared<Module>)> {
|
||||
self.0.iter_mut().map(|(k, v)| (k.as_str(), v))
|
||||
}
|
||||
/// Get a mutable iterator of all the modules.
|
||||
#[inline(always)]
|
||||
@@ -75,8 +80,8 @@ impl StaticModuleResolver {
|
||||
}
|
||||
/// Get an iterator of all the modules.
|
||||
#[inline(always)]
|
||||
pub fn values<'a>(&'a self) -> impl Iterator<Item = Shared<Module>> + 'a {
|
||||
self.0.values().map(|m| m.clone())
|
||||
pub fn values(&self) -> impl Iterator<Item = &Shared<Module>> {
|
||||
self.0.values().map(|m| m)
|
||||
}
|
||||
/// Remove all modules.
|
||||
#[inline(always)]
|
||||
@@ -95,6 +100,8 @@ impl StaticModuleResolver {
|
||||
}
|
||||
/// Merge another [`StaticModuleResolver`] into this.
|
||||
/// The other [`StaticModuleResolver`] is consumed.
|
||||
///
|
||||
/// Existing modules of the same path name are overwritten.
|
||||
#[inline(always)]
|
||||
pub fn merge(&mut self, other: Self) {
|
||||
if !other.is_empty() {
|
||||
|
@@ -2,7 +2,7 @@
|
||||
|
||||
use crate::ast::{Expr, ScriptFnDef, Stmt};
|
||||
use crate::dynamic::AccessMode;
|
||||
use crate::engine::{KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_PRINT, KEYWORD_TYPE_OF};
|
||||
use crate::engine::{Imports, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_PRINT, KEYWORD_TYPE_OF};
|
||||
use crate::fn_call::run_builtin_binary_op;
|
||||
use crate::parser::map_dynamic_to_expr;
|
||||
use crate::stdlib::{
|
||||
@@ -62,6 +62,8 @@ struct State<'a> {
|
||||
variables: Vec<(String, AccessMode, Expr)>,
|
||||
/// An [`Engine`] instance for eager function evaluation.
|
||||
engine: &'a Engine,
|
||||
/// Collection of sub-modules.
|
||||
mods: Imports,
|
||||
/// [Module] containing script-defined functions.
|
||||
lib: &'a [&'a Module],
|
||||
/// Optimization level.
|
||||
@@ -76,6 +78,7 @@ impl<'a> State<'a> {
|
||||
changed: false,
|
||||
variables: vec![],
|
||||
engine,
|
||||
mods: (&engine.global_sub_modules).into(),
|
||||
lib,
|
||||
optimization_level: level,
|
||||
}
|
||||
@@ -655,7 +658,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) {
|
||||
let arg_types: StaticVec<_> = arg_values.iter().map(Dynamic::type_id).collect();
|
||||
|
||||
// Search for overloaded operators (can override built-in).
|
||||
if !state.engine.has_override_by_name_and_arguments(Some(&state.engine.global_sub_modules), state.lib, x.name.as_ref(), arg_types.as_ref(), false) {
|
||||
if !state.engine.has_override_by_name_and_arguments(Some(&state.mods), state.lib, x.name.as_ref(), arg_types.as_ref(), false) {
|
||||
if let Some(result) = run_builtin_binary_op(x.name.as_ref(), &arg_values[0], &arg_values[1])
|
||||
.ok().flatten()
|
||||
.and_then(|result| map_dynamic_to_expr(result, *pos))
|
||||
|
@@ -91,13 +91,16 @@ pub enum ParseErrorType {
|
||||
UnknownOperator(String),
|
||||
/// Expecting a particular token but not finding one. Wrapped values are the token and description.
|
||||
MissingToken(String, String),
|
||||
/// An expression in function call arguments `()` has syntax error. Wrapped value is the error description (if any).
|
||||
/// An expression in function call arguments `()` has syntax error. Wrapped value is the error
|
||||
/// description (if any).
|
||||
MalformedCallExpr(String),
|
||||
/// An expression in indexing brackets `[]` has syntax error. Wrapped value is the error description (if any).
|
||||
/// An expression in indexing brackets `[]` has syntax error. Wrapped value is the error
|
||||
/// description (if any).
|
||||
///
|
||||
/// Never appears under the `no_index` feature.
|
||||
MalformedIndexExpr(String),
|
||||
/// An expression in an `in` expression has syntax error. Wrapped value is the error description (if any).
|
||||
/// An expression in an `in` expression has syntax error. Wrapped value is the error description
|
||||
/// (if any).
|
||||
///
|
||||
/// Never appears under the `no_object` and `no_index` features combination.
|
||||
MalformedInExpr(String),
|
||||
@@ -137,7 +140,8 @@ pub enum ParseErrorType {
|
||||
///
|
||||
/// Never appears under the `no_function` feature.
|
||||
FnMissingParams(String),
|
||||
/// A function definition has duplicated parameters. Wrapped values are the function name and parameter name.
|
||||
/// A function definition has duplicated parameters. Wrapped values are the function name and
|
||||
/// parameter name.
|
||||
///
|
||||
/// Never appears under the `no_function` feature.
|
||||
FnDuplicatedParam(String, String),
|
||||
|
@@ -1325,6 +1325,7 @@ fn parse_unary(
|
||||
}
|
||||
}
|
||||
|
||||
/// Make an assignment statement.
|
||||
fn make_assignment_stmt<'a>(
|
||||
fn_name: Cow<'static, str>,
|
||||
state: &mut ParseState,
|
||||
|
@@ -13,8 +13,8 @@ pub use rhai_codegen::*;
|
||||
pub use rhai_codegen::{export_fn, register_exported_fn};
|
||||
|
||||
/// Trait implemented by a _plugin function_.
|
||||
/// This trait should not be used directly.
|
||||
///
|
||||
/// This trait should not be used directly.
|
||||
/// Use the `#[export_module]` and `#[export_fn]` procedural attributes instead.
|
||||
pub trait PluginFunction {
|
||||
/// Call the plugin function with the arguments provided.
|
||||
|
@@ -55,8 +55,8 @@ pub enum EvalAltResult {
|
||||
/// String indexing out-of-bounds.
|
||||
/// Wrapped values are the current number of characters in the string and the index number.
|
||||
ErrorStringBounds(usize, INT, Position),
|
||||
/// Trying to index into a type that is not an array, an object map, or a string, and has no indexer function defined.
|
||||
/// Wrapped value is the type name.
|
||||
/// Trying to index into a type that is not an array, an object map, or a string, and has no
|
||||
/// indexer function defined. Wrapped value is the type name.
|
||||
ErrorIndexingType(String, Position),
|
||||
/// Invalid arguments for `in` operator.
|
||||
ErrorInExpr(Position),
|
||||
|
33
src/scope.rs
33
src/scope.rs
@@ -41,9 +41,11 @@ use crate::{Dynamic, ImmutableString, StaticVec};
|
||||
// [`Scope`] is implemented as two [`Vec`]'s of exactly the same length. Variables data (name, type, etc.)
|
||||
// is manually split into two equal-length arrays. That's because variable names take up the most space,
|
||||
// with [`Cow<str>`][Cow] being four words long, but in the vast majority of cases the name is NOT used to
|
||||
/// look up a variable. Variable lookup is usually via direct indexing, by-passing the name altogether.
|
||||
// look up a variable. Variable lookup is usually via direct indexing, by-passing the name altogether.
|
||||
//
|
||||
// Since [`Dynamic`] is reasonably small, packing it tightly improves cache locality when variables are accessed.
|
||||
//
|
||||
// The alias is `Box`'ed because it occurs infrequently.
|
||||
#[derive(Debug, Clone, Hash)]
|
||||
pub struct Scope<'a> {
|
||||
/// Current value of the entry.
|
||||
@@ -356,8 +358,35 @@ impl<'a> Scope<'a> {
|
||||
self
|
||||
}
|
||||
/// Get a mutable reference to an entry in the [`Scope`].
|
||||
///
|
||||
/// If the entry by the specified name is not found, of if it is read-only,
|
||||
/// [`None`] is returned.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use rhai::Scope;
|
||||
///
|
||||
/// let mut my_scope = Scope::new();
|
||||
///
|
||||
/// my_scope.push("x", 42_i64);
|
||||
/// assert_eq!(my_scope.get_value::<i64>("x").unwrap(), 42);
|
||||
///
|
||||
/// let ptr = my_scope.get_mut("x").unwrap();
|
||||
/// *ptr = 123_i64.into();
|
||||
///
|
||||
/// assert_eq!(my_scope.get_value::<i64>("x").unwrap(), 123);
|
||||
/// ```
|
||||
pub fn get_mut(&mut self, name: &str) -> Option<&mut Dynamic> {
|
||||
self.get_index(name)
|
||||
.and_then(move |(index, access)| match access {
|
||||
AccessMode::ReadWrite => Some(self.get_mut_by_index(index)),
|
||||
AccessMode::ReadOnly => None,
|
||||
})
|
||||
}
|
||||
/// Get a mutable reference to an entry in the [`Scope`] based on the index.
|
||||
#[inline(always)]
|
||||
pub(crate) fn get_mut(&mut self, index: usize) -> &mut Dynamic {
|
||||
pub(crate) fn get_mut_by_index(&mut self, index: usize) -> &mut Dynamic {
|
||||
self.values.get_mut(index).expect("invalid index in Scope")
|
||||
}
|
||||
/// Update the access type of an entry in the [`Scope`].
|
||||
|
@@ -63,7 +63,8 @@ pub fn get_hasher() -> impl Hasher {
|
||||
s
|
||||
}
|
||||
|
||||
/// _(INTERNALS)_ Calculate a [`NonZeroU64`] hash key from a namespace-qualified function name and parameter types.
|
||||
/// _(INTERNALS)_ Calculate a [`NonZeroU64`] hash key from a namespace-qualified function name and
|
||||
/// parameter types.
|
||||
/// Exported under the `internals` feature only.
|
||||
///
|
||||
/// Module names are passed in via `&str` references from an iterator.
|
||||
@@ -108,6 +109,7 @@ pub fn calc_script_fn_hash<'a>(
|
||||
/// # Note
|
||||
///
|
||||
/// The first module name is skipped. Hashing starts from the _second_ module in the chain.
|
||||
#[inline(always)]
|
||||
fn calc_fn_hash<'a>(
|
||||
mut modules: impl Iterator<Item = &'a str>,
|
||||
fn_name: &str,
|
||||
|
Reference in New Issue
Block a user