Make module_resolver optional and remove NullModuleResolver.
This commit is contained in:
parent
c9571d375a
commit
88fec57394
@ -20,7 +20,7 @@ categories = [ "no-std", "embedded", "parser-implementations" ]
|
|||||||
num-traits = { version = "0.2.11", default-features = false }
|
num-traits = { version = "0.2.11", default-features = false }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
#default = ["no_stdlib", "no_function", "no_index", "no_object", "no_float", "only_i32", "unchecked", "no_optimize", "sync"]
|
#default = ["no_stdlib", "no_function", "no_index", "no_object", "no_module", "no_float", "only_i32", "unchecked", "no_optimize", "sync"]
|
||||||
default = []
|
default = []
|
||||||
unchecked = [] # unchecked arithmetic
|
unchecked = [] # unchecked arithmetic
|
||||||
no_index = [] # no arrays and indexing
|
no_index = [] # no arrays and indexing
|
||||||
|
@ -2118,13 +2118,16 @@ Built-in module resolvers are grouped under the `rhai::module_resolvers` module
|
|||||||
| ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
| ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| `FileModuleResolver` | The default module resolution service, not available under the [`no_std`] feature. Loads a script file (based off the current directory) with `.rhai` extension.<br/>The base directory can be changed via the `FileModuleResolver::new_with_path()` constructor function. |
|
| `FileModuleResolver` | The default module resolution service, not available under the [`no_std`] feature. Loads a script file (based off the current directory) with `.rhai` extension.<br/>The base directory can be changed via the `FileModuleResolver::new_with_path()` constructor function. |
|
||||||
| `StaticModuleResolver` | Loads modules that are statically added. This can be used when the [`no_std`] feature is turned on. |
|
| `StaticModuleResolver` | Loads modules that are statically added. This can be used when the [`no_std`] feature is turned on. |
|
||||||
| `NullModuleResolver` | The default module resolution service under the [`no_std`] feature. Always returns an `EvalAltResult::ErrorModuleNotFound` error. |
|
|
||||||
|
|
||||||
An [`Engine`]'s module resolver is set via a call to `set_module_resolver`:
|
An [`Engine`]'s module resolver is set via a call to `set_module_resolver`:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
// Use the 'NullModuleResolver'
|
// Use the 'StaticModuleResolver'
|
||||||
engine.set_module_resolver(rhai::module_resolvers::NullModuleResolver::new());
|
let resolver = rhai::module_resolvers::StaticModuleResolver::new();
|
||||||
|
engine.set_module_resolver(Some(resolver));
|
||||||
|
|
||||||
|
// Effectively disable 'import' statements by setting module resolver to 'None'
|
||||||
|
engine.set_module_resolver(None);
|
||||||
```
|
```
|
||||||
|
|
||||||
Script optimization
|
Script optimization
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use rhai::{Dynamic, Engine, EvalAltResult, Map, Scope, AST, INT};
|
use rhai::{Dynamic, Engine, EvalAltResult, Scope, AST, INT};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_optimize"))]
|
#[cfg(not(feature = "no_optimize"))]
|
||||||
use rhai::OptimizationLevel;
|
use rhai::OptimizationLevel;
|
||||||
@ -163,6 +163,7 @@ fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Throw away all the statements, leaving only the functions
|
// Throw away all the statements, leaving only the functions
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
main_ast.retain_functions();
|
main_ast.retain_functions();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -262,7 +262,7 @@ pub struct Engine {
|
|||||||
pub(crate) type_iterators: HashMap<TypeId, Box<IteratorFn>>,
|
pub(crate) type_iterators: HashMap<TypeId, Box<IteratorFn>>,
|
||||||
|
|
||||||
/// A module resolution service.
|
/// A module resolution service.
|
||||||
pub(crate) module_resolver: Box<dyn ModuleResolver>,
|
pub(crate) module_resolver: Option<Box<dyn ModuleResolver>>,
|
||||||
|
|
||||||
/// A hashmap mapping type names to pretty-print names.
|
/// A hashmap mapping type names to pretty-print names.
|
||||||
pub(crate) type_names: HashMap<String, String>,
|
pub(crate) type_names: HashMap<String, String>,
|
||||||
@ -300,9 +300,9 @@ impl Default for Engine {
|
|||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
#[cfg(not(feature = "no_std"))]
|
#[cfg(not(feature = "no_std"))]
|
||||||
module_resolver: Box::new(resolvers::FileModuleResolver::new()),
|
module_resolver: Some(Box::new(resolvers::FileModuleResolver::new())),
|
||||||
#[cfg(any(feature = "no_std", feature = "no_module"))]
|
#[cfg(any(feature = "no_std", feature = "no_module"))]
|
||||||
module_resolver: Box::new(resolvers::NullModuleResolver::new()),
|
module_resolver: None,
|
||||||
|
|
||||||
type_names: Default::default(),
|
type_names: Default::default(),
|
||||||
|
|
||||||
@ -438,13 +438,7 @@ impl Engine {
|
|||||||
packages: Default::default(),
|
packages: Default::default(),
|
||||||
functions: HashMap::with_capacity(FUNCTIONS_COUNT / 2),
|
functions: HashMap::with_capacity(FUNCTIONS_COUNT / 2),
|
||||||
type_iterators: Default::default(),
|
type_iterators: Default::default(),
|
||||||
|
module_resolver: None,
|
||||||
#[cfg(not(feature = "no_module"))]
|
|
||||||
#[cfg(not(feature = "no_std"))]
|
|
||||||
module_resolver: Box::new(resolvers::FileModuleResolver::new()),
|
|
||||||
#[cfg(any(feature = "no_std", feature = "no_module"))]
|
|
||||||
module_resolver: Box::new(resolvers::NullModuleResolver::new()),
|
|
||||||
|
|
||||||
type_names: Default::default(),
|
type_names: Default::default(),
|
||||||
print: Box::new(|_| {}),
|
print: Box::new(|_| {}),
|
||||||
debug: Box::new(|_| {}),
|
debug: Box::new(|_| {}),
|
||||||
@ -491,8 +485,8 @@ impl Engine {
|
|||||||
///
|
///
|
||||||
/// Not available under the `no_module` feature.
|
/// Not available under the `no_module` feature.
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
pub fn set_module_resolver(&mut self, resolver: impl ModuleResolver + 'static) {
|
pub fn set_module_resolver(&mut self, resolver: Option<impl ModuleResolver + 'static>) {
|
||||||
self.module_resolver = Box::new(resolver);
|
self.module_resolver = resolver.map(|f| Box::new(f) as Box<dyn ModuleResolver>);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Universal method for calling functions either registered with the `Engine` or written in Rhai.
|
/// Universal method for calling functions either registered with the `Engine` or written in Rhai.
|
||||||
@ -1264,8 +1258,7 @@ impl Engine {
|
|||||||
let hash = calc_fn_hash(fn_name, args.iter().map(|a| a.type_id()));
|
let hash = calc_fn_hash(fn_name, args.iter().map(|a| a.type_id()));
|
||||||
|
|
||||||
match module.get_qualified_fn(fn_name, hash, modules, *pos) {
|
match module.get_qualified_fn(fn_name, hash, modules, *pos) {
|
||||||
Ok(func) => func(&mut args, *pos)
|
Ok(func) => func(&mut args, *pos),
|
||||||
.map_err(|err| EvalAltResult::set_position(err, *pos)),
|
|
||||||
Err(_) if def_val.is_some() => Ok(def_val.as_deref().unwrap().clone()),
|
Err(_) if def_val.is_some() => Ok(def_val.as_deref().unwrap().clone()),
|
||||||
Err(err) => Err(err),
|
Err(err) => Err(err),
|
||||||
}
|
}
|
||||||
@ -1530,15 +1523,19 @@ impl Engine {
|
|||||||
.eval_expr(scope, state, fn_lib, expr, level)?
|
.eval_expr(scope, state, fn_lib, expr, level)?
|
||||||
.try_cast::<String>()
|
.try_cast::<String>()
|
||||||
{
|
{
|
||||||
let module = self
|
if let Some(resolver) = self.module_resolver.as_ref() {
|
||||||
.module_resolver
|
let module = resolver.resolve(self, &path, expr.position())?;
|
||||||
.resolve(self, &path)
|
|
||||||
.map_err(|err| EvalAltResult::set_position(err, expr.position()))?;
|
|
||||||
|
|
||||||
// TODO - avoid copying module name in inner block?
|
// TODO - avoid copying module name in inner block?
|
||||||
let mod_name = name.as_ref().clone();
|
let mod_name = name.as_ref().clone();
|
||||||
scope.push_module(mod_name, module);
|
scope.push_module(mod_name, module);
|
||||||
Ok(Default::default())
|
Ok(Default::default())
|
||||||
|
} else {
|
||||||
|
Err(Box::new(EvalAltResult::ErrorModuleNotFound(
|
||||||
|
path,
|
||||||
|
expr.position(),
|
||||||
|
)))
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Err(Box::new(EvalAltResult::ErrorImportExpr(expr.position())))
|
Err(Box::new(EvalAltResult::ErrorImportExpr(expr.position())))
|
||||||
}
|
}
|
||||||
|
291
src/module.rs
291
src/module.rs
@ -5,21 +5,35 @@ use crate::calc_fn_hash;
|
|||||||
use crate::engine::{Engine, FnAny, FnCallArgs, FunctionsLib};
|
use crate::engine::{Engine, FnAny, FnCallArgs, FunctionsLib};
|
||||||
use crate::parser::FnDef;
|
use crate::parser::FnDef;
|
||||||
use crate::result::EvalAltResult;
|
use crate::result::EvalAltResult;
|
||||||
use crate::scope::{EntryType as ScopeEntryType, Scope};
|
use crate::scope::{Entry as ScopeEntry, EntryType as ScopeEntryType, Scope};
|
||||||
use crate::token::Position;
|
use crate::token::Position;
|
||||||
use crate::token::Token;
|
use crate::token::Token;
|
||||||
use crate::utils::StaticVec;
|
use crate::utils::StaticVec;
|
||||||
|
|
||||||
use crate::stdlib::{
|
use crate::stdlib::{
|
||||||
any::TypeId, collections::HashMap, fmt, iter::empty, mem, rc::Rc, string::String, sync::Arc,
|
any::TypeId,
|
||||||
|
collections::HashMap,
|
||||||
|
fmt, mem,
|
||||||
|
ops::{Deref, DerefMut},
|
||||||
|
rc::Rc,
|
||||||
|
string::String,
|
||||||
|
sync::Arc,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A trait that encapsulates a module resolution service.
|
/// A trait that encapsulates a module resolution service.
|
||||||
pub trait ModuleResolver {
|
pub trait ModuleResolver {
|
||||||
/// Resolve a module based on a path string.
|
/// Resolve a module based on a path string.
|
||||||
fn resolve(&self, engine: &Engine, path: &str) -> Result<Module, Box<EvalAltResult>>;
|
fn resolve(
|
||||||
|
&self,
|
||||||
|
engine: &Engine,
|
||||||
|
path: &str,
|
||||||
|
pos: Position,
|
||||||
|
) -> Result<Module, Box<EvalAltResult>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return type of module-level Rust function.
|
||||||
|
type FuncReturn<T> = Result<T, Box<EvalAltResult>>;
|
||||||
|
|
||||||
/// An imported module, which may contain variables, sub-modules,
|
/// An imported module, which may contain variables, sub-modules,
|
||||||
/// external Rust functions, and script-defined functions.
|
/// external Rust functions, and script-defined functions.
|
||||||
///
|
///
|
||||||
@ -158,7 +172,6 @@ impl Module {
|
|||||||
|
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
self.functions.insert(hash, Rc::new(func));
|
self.functions.insert(hash, Rc::new(func));
|
||||||
|
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
self.functions.insert(hash, Arc::new(func));
|
self.functions.insert(hash, Arc::new(func));
|
||||||
|
|
||||||
@ -171,14 +184,16 @@ impl Module {
|
|||||||
pub fn set_fn_0<T: Into<Dynamic>>(
|
pub fn set_fn_0<T: Into<Dynamic>>(
|
||||||
&mut self,
|
&mut self,
|
||||||
fn_name: &str,
|
fn_name: &str,
|
||||||
#[cfg(not(feature = "sync"))] func: impl Fn() -> Result<T, Box<EvalAltResult>> + 'static,
|
#[cfg(not(feature = "sync"))] func: impl Fn() -> FuncReturn<T> + 'static,
|
||||||
#[cfg(feature = "sync")] func: impl Fn() -> Result<T, Box<EvalAltResult>>
|
#[cfg(feature = "sync")] func: impl Fn() -> FuncReturn<T> + Send + Sync + 'static,
|
||||||
+ Send
|
|
||||||
+ Sync
|
|
||||||
+ 'static,
|
|
||||||
) -> u64 {
|
) -> u64 {
|
||||||
let f = move |_: &mut FnCallArgs, _: Position| func().map(|v| v.into());
|
let f = move |_: &mut FnCallArgs, pos| {
|
||||||
self.set_fn(fn_name, &[], Box::new(f))
|
func()
|
||||||
|
.map(|v| v.into())
|
||||||
|
.map_err(|err| EvalAltResult::set_position(err, pos))
|
||||||
|
};
|
||||||
|
let arg_types = &[];
|
||||||
|
self.set_fn(fn_name, arg_types, Box::new(f))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set a Rust function taking one parameter into the module, returning a hash key.
|
/// Set a Rust function taking one parameter into the module, returning a hash key.
|
||||||
@ -187,16 +202,16 @@ impl Module {
|
|||||||
pub fn set_fn_1<A: Variant + Clone, T: Into<Dynamic>>(
|
pub fn set_fn_1<A: Variant + Clone, T: Into<Dynamic>>(
|
||||||
&mut self,
|
&mut self,
|
||||||
fn_name: &str,
|
fn_name: &str,
|
||||||
#[cfg(not(feature = "sync"))] func: impl Fn(A) -> Result<T, Box<EvalAltResult>> + 'static,
|
#[cfg(not(feature = "sync"))] func: impl Fn(A) -> FuncReturn<T> + 'static,
|
||||||
#[cfg(feature = "sync")] func: impl Fn(A) -> Result<T, Box<EvalAltResult>>
|
#[cfg(feature = "sync")] func: impl Fn(A) -> FuncReturn<T> + Send + Sync + 'static,
|
||||||
+ Send
|
|
||||||
+ Sync
|
|
||||||
+ 'static,
|
|
||||||
) -> u64 {
|
) -> u64 {
|
||||||
let f = move |args: &mut FnCallArgs, _: Position| {
|
let f = move |args: &mut FnCallArgs, pos| {
|
||||||
func(mem::take(args[0]).cast::<A>()).map(|v| v.into())
|
func(mem::take(args[0]).cast::<A>())
|
||||||
|
.map(|v| v.into())
|
||||||
|
.map_err(|err| EvalAltResult::set_position(err, pos))
|
||||||
};
|
};
|
||||||
self.set_fn(fn_name, &[TypeId::of::<A>()], Box::new(f))
|
let arg_types = &[TypeId::of::<A>()];
|
||||||
|
self.set_fn(fn_name, arg_types, Box::new(f))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set a Rust function taking one mutable parameter into the module, returning a hash key.
|
/// Set a Rust function taking one mutable parameter into the module, returning a hash key.
|
||||||
@ -205,16 +220,16 @@ impl Module {
|
|||||||
pub fn set_fn_1_mut<A: Variant + Clone, T: Into<Dynamic>>(
|
pub fn set_fn_1_mut<A: Variant + Clone, T: Into<Dynamic>>(
|
||||||
&mut self,
|
&mut self,
|
||||||
fn_name: &str,
|
fn_name: &str,
|
||||||
#[cfg(not(feature = "sync"))] func: impl Fn(&mut A) -> Result<T, Box<EvalAltResult>> + 'static,
|
#[cfg(not(feature = "sync"))] func: impl Fn(&mut A) -> FuncReturn<T> + 'static,
|
||||||
#[cfg(feature = "sync")] func: impl Fn(&mut A) -> Result<T, Box<EvalAltResult>>
|
#[cfg(feature = "sync")] func: impl Fn(&mut A) -> FuncReturn<T> + Send + Sync + 'static,
|
||||||
+ Send
|
|
||||||
+ Sync
|
|
||||||
+ 'static,
|
|
||||||
) -> u64 {
|
) -> u64 {
|
||||||
let f = move |args: &mut FnCallArgs, _: Position| {
|
let f = move |args: &mut FnCallArgs, pos| {
|
||||||
func(args[0].downcast_mut::<A>().unwrap()).map(|v| v.into())
|
func(args[0].downcast_mut::<A>().unwrap())
|
||||||
|
.map(|v| v.into())
|
||||||
|
.map_err(|err| EvalAltResult::set_position(err, pos))
|
||||||
};
|
};
|
||||||
self.set_fn(fn_name, &[TypeId::of::<A>()], Box::new(f))
|
let arg_types = &[TypeId::of::<A>()];
|
||||||
|
self.set_fn(fn_name, arg_types, Box::new(f))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set a Rust function taking two parameters into the module, returning a hash key.
|
/// Set a Rust function taking two parameters into the module, returning a hash key.
|
||||||
@ -223,23 +238,19 @@ impl Module {
|
|||||||
pub fn set_fn_2<A: Variant + Clone, B: Variant + Clone, T: Into<Dynamic>>(
|
pub fn set_fn_2<A: Variant + Clone, B: Variant + Clone, T: Into<Dynamic>>(
|
||||||
&mut self,
|
&mut self,
|
||||||
fn_name: &str,
|
fn_name: &str,
|
||||||
#[cfg(not(feature = "sync"))] func: impl Fn(A, B) -> Result<T, Box<EvalAltResult>> + 'static,
|
#[cfg(not(feature = "sync"))] func: impl Fn(A, B) -> FuncReturn<T> + 'static,
|
||||||
#[cfg(feature = "sync")] func: impl Fn(A, B) -> Result<T, Box<EvalAltResult>>
|
#[cfg(feature = "sync")] func: impl Fn(A, B) -> FuncReturn<T> + Send + Sync + 'static,
|
||||||
+ Send
|
|
||||||
+ Sync
|
|
||||||
+ 'static,
|
|
||||||
) -> u64 {
|
) -> u64 {
|
||||||
let f = move |args: &mut FnCallArgs, _: Position| {
|
let f = move |args: &mut FnCallArgs, pos| {
|
||||||
let a = mem::take(args[0]).cast::<A>();
|
let a = mem::take(args[0]).cast::<A>();
|
||||||
let b = mem::take(args[1]).cast::<B>();
|
let b = mem::take(args[1]).cast::<B>();
|
||||||
|
|
||||||
func(a, b).map(|v| v.into())
|
func(a, b)
|
||||||
|
.map(|v| v.into())
|
||||||
|
.map_err(|err| EvalAltResult::set_position(err, pos))
|
||||||
};
|
};
|
||||||
self.set_fn(
|
let arg_types = &[TypeId::of::<A>(), TypeId::of::<B>()];
|
||||||
fn_name,
|
self.set_fn(fn_name, arg_types, Box::new(f))
|
||||||
&[TypeId::of::<A>(), TypeId::of::<B>()],
|
|
||||||
Box::new(f),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set a Rust function taking two parameters (the first one mutable) into the module,
|
/// Set a Rust function taking two parameters (the first one mutable) into the module,
|
||||||
@ -249,24 +260,19 @@ impl Module {
|
|||||||
pub fn set_fn_2_mut<A: Variant + Clone, B: Variant + Clone, T: Into<Dynamic>>(
|
pub fn set_fn_2_mut<A: Variant + Clone, B: Variant + Clone, T: Into<Dynamic>>(
|
||||||
&mut self,
|
&mut self,
|
||||||
fn_name: &str,
|
fn_name: &str,
|
||||||
#[cfg(not(feature = "sync"))] func: impl Fn(&mut A, B) -> Result<T, Box<EvalAltResult>>
|
#[cfg(not(feature = "sync"))] func: impl Fn(&mut A, B) -> FuncReturn<T> + 'static,
|
||||||
+ 'static,
|
#[cfg(feature = "sync")] func: impl Fn(&mut A, B) -> FuncReturn<T> + Send + Sync + 'static,
|
||||||
#[cfg(feature = "sync")] func: impl Fn(&mut A, B) -> Result<T, Box<EvalAltResult>>
|
|
||||||
+ Send
|
|
||||||
+ Sync
|
|
||||||
+ 'static,
|
|
||||||
) -> u64 {
|
) -> u64 {
|
||||||
let f = move |args: &mut FnCallArgs, _: Position| {
|
let f = move |args: &mut FnCallArgs, pos| {
|
||||||
let b = mem::take(args[1]).cast::<B>();
|
let b = mem::take(args[1]).cast::<B>();
|
||||||
let a = args[0].downcast_mut::<A>().unwrap();
|
let a = args[0].downcast_mut::<A>().unwrap();
|
||||||
|
|
||||||
func(a, b).map(|v| v.into())
|
func(a, b)
|
||||||
|
.map(|v| v.into())
|
||||||
|
.map_err(|err| EvalAltResult::set_position(err, pos))
|
||||||
};
|
};
|
||||||
self.set_fn(
|
let arg_types = &[TypeId::of::<A>(), TypeId::of::<B>()];
|
||||||
fn_name,
|
self.set_fn(fn_name, arg_types, Box::new(f))
|
||||||
&[TypeId::of::<A>(), TypeId::of::<B>()],
|
|
||||||
Box::new(f),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set a Rust function taking three parameters into the module, returning a hash key.
|
/// Set a Rust function taking three parameters into the module, returning a hash key.
|
||||||
@ -280,24 +286,20 @@ impl Module {
|
|||||||
>(
|
>(
|
||||||
&mut self,
|
&mut self,
|
||||||
fn_name: &str,
|
fn_name: &str,
|
||||||
#[cfg(not(feature = "sync"))] func: impl Fn(A, B, C) -> Result<T, Box<EvalAltResult>> + 'static,
|
#[cfg(not(feature = "sync"))] func: impl Fn(A, B, C) -> FuncReturn<T> + 'static,
|
||||||
#[cfg(feature = "sync")] func: impl Fn(A, B, C) -> Result<T, Box<EvalAltResult>>
|
#[cfg(feature = "sync")] func: impl Fn(A, B, C) -> FuncReturn<T> + Send + Sync + 'static,
|
||||||
+ Send
|
|
||||||
+ Sync
|
|
||||||
+ 'static,
|
|
||||||
) -> u64 {
|
) -> u64 {
|
||||||
let f = move |args: &mut FnCallArgs, _: Position| {
|
let f = move |args: &mut FnCallArgs, pos| {
|
||||||
let a = mem::take(args[0]).cast::<A>();
|
let a = mem::take(args[0]).cast::<A>();
|
||||||
let b = mem::take(args[1]).cast::<B>();
|
let b = mem::take(args[1]).cast::<B>();
|
||||||
let c = mem::take(args[2]).cast::<C>();
|
let c = mem::take(args[2]).cast::<C>();
|
||||||
|
|
||||||
func(a, b, c).map(|v| v.into())
|
func(a, b, c)
|
||||||
|
.map(|v| v.into())
|
||||||
|
.map_err(|err| EvalAltResult::set_position(err, pos))
|
||||||
};
|
};
|
||||||
self.set_fn(
|
let arg_types = &[TypeId::of::<A>(), TypeId::of::<B>(), TypeId::of::<C>()];
|
||||||
fn_name,
|
self.set_fn(fn_name, arg_types, Box::new(f))
|
||||||
&[TypeId::of::<A>(), TypeId::of::<B>(), TypeId::of::<C>()],
|
|
||||||
Box::new(f),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set a Rust function taking three parameters (the first one mutable) into the module,
|
/// Set a Rust function taking three parameters (the first one mutable) into the module,
|
||||||
@ -312,25 +314,20 @@ impl Module {
|
|||||||
>(
|
>(
|
||||||
&mut self,
|
&mut self,
|
||||||
fn_name: &str,
|
fn_name: &str,
|
||||||
#[cfg(not(feature = "sync"))] func: impl Fn(&mut A, B, C) -> Result<T, Box<EvalAltResult>>
|
#[cfg(not(feature = "sync"))] func: impl Fn(&mut A, B, C) -> FuncReturn<T> + 'static,
|
||||||
+ 'static,
|
#[cfg(feature = "sync")] func: impl Fn(&mut A, B, C) -> FuncReturn<T> + Send + Sync + 'static,
|
||||||
#[cfg(feature = "sync")] func: impl Fn(&mut A, B, C) -> Result<T, Box<EvalAltResult>>
|
|
||||||
+ Send
|
|
||||||
+ Sync
|
|
||||||
+ 'static,
|
|
||||||
) -> u64 {
|
) -> u64 {
|
||||||
let f = move |args: &mut FnCallArgs, _: Position| {
|
let f = move |args: &mut FnCallArgs, pos| {
|
||||||
let b = mem::take(args[1]).cast::<B>();
|
let b = mem::take(args[1]).cast::<B>();
|
||||||
let c = mem::take(args[2]).cast::<C>();
|
let c = mem::take(args[2]).cast::<C>();
|
||||||
let a = args[0].downcast_mut::<A>().unwrap();
|
let a = args[0].downcast_mut::<A>().unwrap();
|
||||||
|
|
||||||
func(a, b, c).map(|v| v.into())
|
func(a, b, c)
|
||||||
|
.map(|v| v.into())
|
||||||
|
.map_err(|err| EvalAltResult::set_position(err, pos))
|
||||||
};
|
};
|
||||||
self.set_fn(
|
let arg_types = &[TypeId::of::<A>(), TypeId::of::<B>(), TypeId::of::<C>()];
|
||||||
fn_name,
|
self.set_fn(fn_name, arg_types, Box::new(f))
|
||||||
&[TypeId::of::<A>(), TypeId::of::<B>(), TypeId::of::<C>()],
|
|
||||||
Box::new(f),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a Rust function.
|
/// Get a Rust function.
|
||||||
@ -388,22 +385,40 @@ impl Module {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Re-export module resolvers.
|
||||||
pub mod resolvers {
|
pub mod resolvers {
|
||||||
use super::*;
|
pub use super::file::FileModuleResolver;
|
||||||
|
pub use super::stat::StaticModuleResolver;
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_std"))]
|
/// Script file-based module resolver.
|
||||||
|
#[cfg(not(feature = "no_std"))]
|
||||||
|
mod file {
|
||||||
|
use super::*;
|
||||||
use crate::stdlib::path::PathBuf;
|
use crate::stdlib::path::PathBuf;
|
||||||
|
|
||||||
/// A module resolution service that loads module script files (assumed `.rhai` extension).
|
/// A module resolution service that loads module script files from the file system.
|
||||||
#[cfg(not(feature = "no_std"))]
|
///
|
||||||
#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
/// The `new_with_path` and `new_with_path_and_extension` constructor functions
|
||||||
pub struct FileModuleResolver(PathBuf);
|
/// 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`).
|
||||||
|
#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Default)]
|
||||||
|
pub struct FileModuleResolver {
|
||||||
|
path: PathBuf,
|
||||||
|
extension: String,
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_std"))]
|
|
||||||
impl FileModuleResolver {
|
impl FileModuleResolver {
|
||||||
/// Create a new `FileModuleResolver` with a specific base path.
|
/// Create a new `FileModuleResolver` with a specific base path.
|
||||||
pub fn new_with_path(path: PathBuf) -> Self {
|
pub fn new_with_path(path: PathBuf) -> Self {
|
||||||
Self(path)
|
Self::new_with_path_and_extension(path, "rhai".to_string())
|
||||||
|
}
|
||||||
|
/// Create a new `FileModuleResolver` with a specific base path and file extension.
|
||||||
|
///
|
||||||
|
/// The default extension is `.rhai`.
|
||||||
|
pub fn new_with_path_and_extension(path: PathBuf, extension: String) -> Self {
|
||||||
|
Self { path, extension }
|
||||||
}
|
}
|
||||||
/// Create a new `FileModuleResolver` with the current directory as base path.
|
/// Create a new `FileModuleResolver` with the current directory as base path.
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
@ -411,54 +426,63 @@ pub mod resolvers {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_std"))]
|
|
||||||
impl Default for FileModuleResolver {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::new_with_path(".".into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_std"))]
|
|
||||||
impl ModuleResolver for FileModuleResolver {
|
impl ModuleResolver for FileModuleResolver {
|
||||||
fn resolve(&self, engine: &Engine, path: &str) -> Result<Module, Box<EvalAltResult>> {
|
fn resolve(
|
||||||
// Load the script file (attaching `.rhai`)
|
&self,
|
||||||
let mut file_path = self.0.clone();
|
engine: &Engine,
|
||||||
|
path: &str,
|
||||||
|
pos: Position,
|
||||||
|
) -> Result<Module, Box<EvalAltResult>> {
|
||||||
|
// Construct the script file path
|
||||||
|
let mut file_path = self.path.clone();
|
||||||
file_path.push(path);
|
file_path.push(path);
|
||||||
file_path.set_extension("rhai");
|
file_path.set_extension(&self.extension); // Force extension
|
||||||
|
|
||||||
// Compile it
|
// Compile it
|
||||||
let ast = engine.compile_file(file_path)?;
|
let ast = engine
|
||||||
|
.compile_file(file_path)
|
||||||
|
.map_err(|err| EvalAltResult::set_position(err, pos))?;
|
||||||
|
|
||||||
// Use new scope
|
// Use new scope
|
||||||
let mut scope = Scope::new();
|
let mut scope = Scope::new();
|
||||||
|
|
||||||
// Run the script
|
// Run the script
|
||||||
engine.eval_ast_with_scope_raw(&mut scope, &ast)?;
|
engine
|
||||||
|
.eval_ast_with_scope_raw(&mut scope, &ast)
|
||||||
|
.map_err(|err| EvalAltResult::set_position(err, pos))?;
|
||||||
|
|
||||||
// Create new module
|
// Create new module
|
||||||
let mut module = Module::new();
|
let mut module = Module::new();
|
||||||
|
|
||||||
|
scope.into_iter().for_each(
|
||||||
|
|ScopeEntry {
|
||||||
|
name, typ, value, ..
|
||||||
|
}| {
|
||||||
|
match typ {
|
||||||
// Variables left in the scope become module variables
|
// Variables left in the scope become module variables
|
||||||
for entry in scope.into_iter() {
|
|
||||||
match entry.typ {
|
|
||||||
ScopeEntryType::Normal | ScopeEntryType::Constant => {
|
ScopeEntryType::Normal | ScopeEntryType::Constant => {
|
||||||
module
|
module.variables.insert(name.into_owned(), value);
|
||||||
.variables
|
|
||||||
.insert(entry.name.into_owned(), entry.value);
|
|
||||||
}
|
}
|
||||||
|
// Modules left in the scope become sub-modules
|
||||||
ScopeEntryType::Module => {
|
ScopeEntryType::Module => {
|
||||||
module
|
module
|
||||||
.modules
|
.modules
|
||||||
.insert(entry.name.into_owned(), entry.value.cast::<Module>());
|
.insert(name.into_owned(), value.cast::<Module>());
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
module.fn_lib = FunctionsLib::new().merge(ast.fn_lib());
|
module.fn_lib = module.fn_lib.merge(ast.fn_lib());
|
||||||
|
|
||||||
Ok(module)
|
Ok(module)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Static module resolver.
|
||||||
|
mod stat {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
/// A module resolution service that serves modules added into it.
|
/// A module resolution service that serves modules added into it.
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
@ -469,40 +493,33 @@ pub mod resolvers {
|
|||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Default::default()
|
Default::default()
|
||||||
}
|
}
|
||||||
/// Add a named module.
|
}
|
||||||
pub fn add_module(&mut self, name: &str, module: Module) {
|
|
||||||
self.0.insert(name.to_string(), module);
|
impl Deref for StaticModuleResolver {
|
||||||
|
type Target = HashMap<String, Module>;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DerefMut for StaticModuleResolver {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
&mut self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ModuleResolver for StaticModuleResolver {
|
impl ModuleResolver for StaticModuleResolver {
|
||||||
fn resolve(&self, _: &Engine, path: &str) -> Result<Module, Box<EvalAltResult>> {
|
fn resolve(
|
||||||
self.0.get(path).cloned().ok_or_else(|| {
|
&self,
|
||||||
Box::new(EvalAltResult::ErrorModuleNotFound(
|
_: &Engine,
|
||||||
path.to_string(),
|
path: &str,
|
||||||
Position::none(),
|
pos: Position,
|
||||||
))
|
) -> Result<Module, Box<EvalAltResult>> {
|
||||||
})
|
self.0
|
||||||
}
|
.get(path)
|
||||||
}
|
.cloned()
|
||||||
|
.ok_or_else(|| Box::new(EvalAltResult::ErrorModuleNotFound(path.to_string(), pos)))
|
||||||
/// A module resolution service that always returns a not-found error.
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Copy, Default)]
|
|
||||||
pub struct NullModuleResolver;
|
|
||||||
|
|
||||||
impl NullModuleResolver {
|
|
||||||
/// Create a new `NullModuleResolver`.
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Default::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ModuleResolver for NullModuleResolver {
|
|
||||||
fn resolve(&self, _: &Engine, path: &str) -> Result<Module, Box<EvalAltResult>> {
|
|
||||||
Err(Box::new(EvalAltResult::ErrorModuleNotFound(
|
|
||||||
path.to_string(),
|
|
||||||
Position::none(),
|
|
||||||
)))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -67,13 +67,9 @@ impl AST {
|
|||||||
/// Create a new `AST`.
|
/// Create a new `AST`.
|
||||||
pub fn new(statements: Vec<Stmt>, fn_lib: FunctionsLib) -> Self {
|
pub fn new(statements: Vec<Stmt>, fn_lib: FunctionsLib) -> Self {
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
{
|
return Self(statements, Arc::new(fn_lib));
|
||||||
Self(statements, Arc::new(fn_lib))
|
|
||||||
}
|
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
{
|
return Self(statements, Rc::new(fn_lib));
|
||||||
Self(statements, Rc::new(fn_lib))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the statements.
|
/// Get the statements.
|
||||||
@ -148,13 +144,9 @@ impl AST {
|
|||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
{
|
return Self(ast, Arc::new(functions.merge(other.1.as_ref())));
|
||||||
Self(ast, Arc::new(functions.merge(other.1.as_ref())))
|
|
||||||
}
|
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
{
|
return Self(ast, Rc::new(functions.merge(other.1.as_ref())));
|
||||||
Self(ast, Rc::new(functions.merge(other.1.as_ref())))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Clear all function definitions in the `AST`.
|
/// Clear all function definitions in the `AST`.
|
||||||
@ -1810,7 +1802,6 @@ fn parse_let<'a>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Parse an import statement.
|
/// Parse an import statement.
|
||||||
#[cfg(not(feature = "no_module"))]
|
|
||||||
fn parse_import<'a>(
|
fn parse_import<'a>(
|
||||||
input: &mut Peekable<TokenIterator<'a>>,
|
input: &mut Peekable<TokenIterator<'a>>,
|
||||||
stack: &mut Stack,
|
stack: &mut Stack,
|
||||||
@ -1941,7 +1932,6 @@ fn parse_stmt<'a>(
|
|||||||
Token::LeftBrace => parse_block(input, stack, breakable, allow_stmt_expr),
|
Token::LeftBrace => parse_block(input, stack, breakable, allow_stmt_expr),
|
||||||
|
|
||||||
// fn ...
|
// fn ...
|
||||||
#[cfg(not(feature = "no_function"))]
|
|
||||||
Token::Fn => Err(PERR::WrongFnDefinition.into_err(*pos)),
|
Token::Fn => Err(PERR::WrongFnDefinition.into_err(*pos)),
|
||||||
|
|
||||||
Token::If => parse_if(input, stack, breakable, allow_stmt_expr),
|
Token::If => parse_if(input, stack, breakable, allow_stmt_expr),
|
||||||
@ -2115,15 +2105,12 @@ fn parse_global_level<'a>(
|
|||||||
|
|
||||||
while !input.peek().unwrap().0.is_eof() {
|
while !input.peek().unwrap().0.is_eof() {
|
||||||
// Collect all the function definitions
|
// Collect all the function definitions
|
||||||
#[cfg(not(feature = "no_function"))]
|
|
||||||
{
|
|
||||||
if let (Token::Fn, _) = input.peek().unwrap() {
|
if let (Token::Fn, _) = input.peek().unwrap() {
|
||||||
let mut stack = Stack::new();
|
let mut stack = Stack::new();
|
||||||
let f = parse_fn(input, &mut stack, true)?;
|
let f = parse_fn(input, &mut stack, true)?;
|
||||||
functions.insert(calc_fn_def(&f.name, f.params.len()), f);
|
functions.insert(calc_fn_def(&f.name, f.params.len()), f);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Actual statement
|
// Actual statement
|
||||||
let stmt = parse_stmt(input, &mut stack, false, true)?;
|
let stmt = parse_stmt(input, &mut stack, false, true)?;
|
||||||
|
12
src/token.rs
12
src/token.rs
@ -153,11 +153,9 @@ pub enum Token {
|
|||||||
RightShift,
|
RightShift,
|
||||||
SemiColon,
|
SemiColon,
|
||||||
Colon,
|
Colon,
|
||||||
#[cfg(not(feature = "no_module"))]
|
|
||||||
DoubleColon,
|
DoubleColon,
|
||||||
Comma,
|
Comma,
|
||||||
Period,
|
Period,
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
MapStart,
|
MapStart,
|
||||||
Equals,
|
Equals,
|
||||||
True,
|
True,
|
||||||
@ -182,7 +180,6 @@ pub enum Token {
|
|||||||
XOr,
|
XOr,
|
||||||
Ampersand,
|
Ampersand,
|
||||||
And,
|
And,
|
||||||
#[cfg(not(feature = "no_function"))]
|
|
||||||
Fn,
|
Fn,
|
||||||
Continue,
|
Continue,
|
||||||
Break,
|
Break,
|
||||||
@ -199,11 +196,8 @@ pub enum Token {
|
|||||||
XOrAssign,
|
XOrAssign,
|
||||||
ModuloAssign,
|
ModuloAssign,
|
||||||
PowerOfAssign,
|
PowerOfAssign,
|
||||||
#[cfg(not(feature = "no_module"))]
|
|
||||||
Import,
|
Import,
|
||||||
#[cfg(not(feature = "no_module"))]
|
|
||||||
Export,
|
Export,
|
||||||
#[cfg(not(feature = "no_module"))]
|
|
||||||
As,
|
As,
|
||||||
LexError(Box<LexError>),
|
LexError(Box<LexError>),
|
||||||
EOF,
|
EOF,
|
||||||
@ -238,11 +232,9 @@ impl Token {
|
|||||||
Divide => "/",
|
Divide => "/",
|
||||||
SemiColon => ";",
|
SemiColon => ";",
|
||||||
Colon => ":",
|
Colon => ":",
|
||||||
#[cfg(not(feature = "no_module"))]
|
|
||||||
DoubleColon => "::",
|
DoubleColon => "::",
|
||||||
Comma => ",",
|
Comma => ",",
|
||||||
Period => ".",
|
Period => ".",
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
MapStart => "#{",
|
MapStart => "#{",
|
||||||
Equals => "=",
|
Equals => "=",
|
||||||
True => "true",
|
True => "true",
|
||||||
@ -266,7 +258,6 @@ impl Token {
|
|||||||
Or => "||",
|
Or => "||",
|
||||||
Ampersand => "&",
|
Ampersand => "&",
|
||||||
And => "&&",
|
And => "&&",
|
||||||
#[cfg(not(feature = "no_function"))]
|
|
||||||
Fn => "fn",
|
Fn => "fn",
|
||||||
Continue => "continue",
|
Continue => "continue",
|
||||||
Break => "break",
|
Break => "break",
|
||||||
@ -288,11 +279,8 @@ impl Token {
|
|||||||
ModuloAssign => "%=",
|
ModuloAssign => "%=",
|
||||||
PowerOf => "~",
|
PowerOf => "~",
|
||||||
PowerOfAssign => "~=",
|
PowerOfAssign => "~=",
|
||||||
#[cfg(not(feature = "no_module"))]
|
|
||||||
Import => "import",
|
Import => "import",
|
||||||
#[cfg(not(feature = "no_module"))]
|
|
||||||
Export => "export",
|
Export => "export",
|
||||||
#[cfg(not(feature = "no_module"))]
|
|
||||||
As => "as",
|
As => "as",
|
||||||
EOF => "{EOF}",
|
EOF => "{EOF}",
|
||||||
_ => panic!("operator should be match in outer scope"),
|
_ => panic!("operator should be match in outer scope"),
|
||||||
|
@ -64,10 +64,10 @@ fn test_module_resolver() -> Result<(), Box<EvalAltResult>> {
|
|||||||
let mut module = Module::new();
|
let mut module = Module::new();
|
||||||
module.set_var("answer", 42 as INT);
|
module.set_var("answer", 42 as INT);
|
||||||
|
|
||||||
resolver.add_module("hello", module);
|
resolver.insert("hello".to_string(), module);
|
||||||
|
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
engine.set_module_resolver(resolver);
|
engine.set_module_resolver(Some(resolver));
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<INT>(
|
engine.eval::<INT>(
|
||||||
|
Loading…
Reference in New Issue
Block a user