Code enhancements.

This commit is contained in:
Stephen Chung
2020-12-26 13:05:57 +08:00
parent e1ac6cc90e
commit dc4e52e795
31 changed files with 621 additions and 391 deletions

View File

@@ -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 {

View File

@@ -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.

View File

@@ -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")) {

View File

@@ -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.

View File

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

View File

@@ -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(())

View File

@@ -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 {

View File

@@ -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

View File

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

View 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()
}
}

View File

@@ -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 {

View File

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

View File

@@ -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() {

View File

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

View File

@@ -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),

View File

@@ -1325,6 +1325,7 @@ fn parse_unary(
}
}
/// Make an assignment statement.
fn make_assignment_stmt<'a>(
fn_name: Cow<'static, str>,
state: &mut ParseState,

View File

@@ -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.

View File

@@ -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),

View File

@@ -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`].

View File

@@ -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,