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 }
|
||||
|
||||
[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 = []
|
||||
unchecked = [] # unchecked arithmetic
|
||||
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. |
|
||||
| `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`:
|
||||
|
||||
```rust
|
||||
// Use the 'NullModuleResolver'
|
||||
engine.set_module_resolver(rhai::module_resolvers::NullModuleResolver::new());
|
||||
// Use the 'StaticModuleResolver'
|
||||
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
|
||||
|
@ -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"))]
|
||||
use rhai::OptimizationLevel;
|
||||
@ -163,6 +163,7 @@ fn main() {
|
||||
}
|
||||
|
||||
// Throw away all the statements, leaving only the functions
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
main_ast.retain_functions();
|
||||
}
|
||||
}
|
||||
|
@ -262,7 +262,7 @@ pub struct Engine {
|
||||
pub(crate) type_iterators: HashMap<TypeId, Box<IteratorFn>>,
|
||||
|
||||
/// 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.
|
||||
pub(crate) type_names: HashMap<String, String>,
|
||||
@ -300,9 +300,9 @@ impl Default for Engine {
|
||||
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
#[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"))]
|
||||
module_resolver: Box::new(resolvers::NullModuleResolver::new()),
|
||||
module_resolver: None,
|
||||
|
||||
type_names: Default::default(),
|
||||
|
||||
@ -438,13 +438,7 @@ impl Engine {
|
||||
packages: Default::default(),
|
||||
functions: HashMap::with_capacity(FUNCTIONS_COUNT / 2),
|
||||
type_iterators: Default::default(),
|
||||
|
||||
#[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()),
|
||||
|
||||
module_resolver: None,
|
||||
type_names: Default::default(),
|
||||
print: Box::new(|_| {}),
|
||||
debug: Box::new(|_| {}),
|
||||
@ -491,8 +485,8 @@ impl Engine {
|
||||
///
|
||||
/// Not available under the `no_module` feature.
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
pub fn set_module_resolver(&mut self, resolver: impl ModuleResolver + 'static) {
|
||||
self.module_resolver = Box::new(resolver);
|
||||
pub fn set_module_resolver(&mut self, resolver: Option<impl ModuleResolver + 'static>) {
|
||||
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.
|
||||
@ -1264,8 +1258,7 @@ impl Engine {
|
||||
let hash = calc_fn_hash(fn_name, args.iter().map(|a| a.type_id()));
|
||||
|
||||
match module.get_qualified_fn(fn_name, hash, modules, *pos) {
|
||||
Ok(func) => func(&mut args, *pos)
|
||||
.map_err(|err| EvalAltResult::set_position(err, *pos)),
|
||||
Ok(func) => func(&mut args, *pos),
|
||||
Err(_) if def_val.is_some() => Ok(def_val.as_deref().unwrap().clone()),
|
||||
Err(err) => Err(err),
|
||||
}
|
||||
@ -1530,15 +1523,19 @@ impl Engine {
|
||||
.eval_expr(scope, state, fn_lib, expr, level)?
|
||||
.try_cast::<String>()
|
||||
{
|
||||
let module = self
|
||||
.module_resolver
|
||||
.resolve(self, &path)
|
||||
.map_err(|err| EvalAltResult::set_position(err, expr.position()))?;
|
||||
if let Some(resolver) = self.module_resolver.as_ref() {
|
||||
let module = resolver.resolve(self, &path, expr.position())?;
|
||||
|
||||
// TODO - avoid copying module name in inner block?
|
||||
let mod_name = name.as_ref().clone();
|
||||
scope.push_module(mod_name, module);
|
||||
Ok(Default::default())
|
||||
// TODO - avoid copying module name in inner block?
|
||||
let mod_name = name.as_ref().clone();
|
||||
scope.push_module(mod_name, module);
|
||||
Ok(Default::default())
|
||||
} else {
|
||||
Err(Box::new(EvalAltResult::ErrorModuleNotFound(
|
||||
path,
|
||||
expr.position(),
|
||||
)))
|
||||
}
|
||||
} else {
|
||||
Err(Box::new(EvalAltResult::ErrorImportExpr(expr.position())))
|
||||
}
|
||||
|
305
src/module.rs
305
src/module.rs
@ -5,21 +5,35 @@ use crate::calc_fn_hash;
|
||||
use crate::engine::{Engine, FnAny, FnCallArgs, FunctionsLib};
|
||||
use crate::parser::FnDef;
|
||||
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::Token;
|
||||
use crate::utils::StaticVec;
|
||||
|
||||
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.
|
||||
pub trait ModuleResolver {
|
||||
/// 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,
|
||||
/// external Rust functions, and script-defined functions.
|
||||
///
|
||||
@ -158,7 +172,6 @@ impl Module {
|
||||
|
||||
#[cfg(not(feature = "sync"))]
|
||||
self.functions.insert(hash, Rc::new(func));
|
||||
|
||||
#[cfg(feature = "sync")]
|
||||
self.functions.insert(hash, Arc::new(func));
|
||||
|
||||
@ -171,14 +184,16 @@ impl Module {
|
||||
pub fn set_fn_0<T: Into<Dynamic>>(
|
||||
&mut self,
|
||||
fn_name: &str,
|
||||
#[cfg(not(feature = "sync"))] func: impl Fn() -> Result<T, Box<EvalAltResult>> + 'static,
|
||||
#[cfg(feature = "sync")] func: impl Fn() -> Result<T, Box<EvalAltResult>>
|
||||
+ Send
|
||||
+ Sync
|
||||
+ 'static,
|
||||
#[cfg(not(feature = "sync"))] func: impl Fn() -> FuncReturn<T> + 'static,
|
||||
#[cfg(feature = "sync")] func: impl Fn() -> FuncReturn<T> + Send + Sync + 'static,
|
||||
) -> u64 {
|
||||
let f = move |_: &mut FnCallArgs, _: Position| func().map(|v| v.into());
|
||||
self.set_fn(fn_name, &[], Box::new(f))
|
||||
let f = move |_: &mut FnCallArgs, pos| {
|
||||
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.
|
||||
@ -187,16 +202,16 @@ impl Module {
|
||||
pub fn set_fn_1<A: Variant + Clone, T: Into<Dynamic>>(
|
||||
&mut self,
|
||||
fn_name: &str,
|
||||
#[cfg(not(feature = "sync"))] func: impl Fn(A) -> Result<T, Box<EvalAltResult>> + 'static,
|
||||
#[cfg(feature = "sync")] func: impl Fn(A) -> Result<T, Box<EvalAltResult>>
|
||||
+ Send
|
||||
+ Sync
|
||||
+ 'static,
|
||||
#[cfg(not(feature = "sync"))] func: impl Fn(A) -> FuncReturn<T> + 'static,
|
||||
#[cfg(feature = "sync")] func: impl Fn(A) -> FuncReturn<T> + Send + Sync + 'static,
|
||||
) -> u64 {
|
||||
let f = move |args: &mut FnCallArgs, _: Position| {
|
||||
func(mem::take(args[0]).cast::<A>()).map(|v| v.into())
|
||||
let f = move |args: &mut FnCallArgs, pos| {
|
||||
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.
|
||||
@ -205,16 +220,16 @@ impl Module {
|
||||
pub fn set_fn_1_mut<A: Variant + Clone, T: Into<Dynamic>>(
|
||||
&mut self,
|
||||
fn_name: &str,
|
||||
#[cfg(not(feature = "sync"))] func: impl Fn(&mut A) -> Result<T, Box<EvalAltResult>> + 'static,
|
||||
#[cfg(feature = "sync")] func: impl Fn(&mut A) -> Result<T, Box<EvalAltResult>>
|
||||
+ Send
|
||||
+ Sync
|
||||
+ 'static,
|
||||
#[cfg(not(feature = "sync"))] func: impl Fn(&mut A) -> FuncReturn<T> + 'static,
|
||||
#[cfg(feature = "sync")] func: impl Fn(&mut A) -> FuncReturn<T> + Send + Sync + 'static,
|
||||
) -> u64 {
|
||||
let f = move |args: &mut FnCallArgs, _: Position| {
|
||||
func(args[0].downcast_mut::<A>().unwrap()).map(|v| v.into())
|
||||
let f = move |args: &mut FnCallArgs, pos| {
|
||||
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.
|
||||
@ -223,23 +238,19 @@ impl Module {
|
||||
pub fn set_fn_2<A: Variant + Clone, B: Variant + Clone, T: Into<Dynamic>>(
|
||||
&mut self,
|
||||
fn_name: &str,
|
||||
#[cfg(not(feature = "sync"))] func: impl Fn(A, B) -> Result<T, Box<EvalAltResult>> + 'static,
|
||||
#[cfg(feature = "sync")] func: impl Fn(A, B) -> Result<T, Box<EvalAltResult>>
|
||||
+ Send
|
||||
+ Sync
|
||||
+ 'static,
|
||||
#[cfg(not(feature = "sync"))] func: impl Fn(A, B) -> FuncReturn<T> + 'static,
|
||||
#[cfg(feature = "sync")] func: impl Fn(A, B) -> FuncReturn<T> + Send + Sync + 'static,
|
||||
) -> u64 {
|
||||
let f = move |args: &mut FnCallArgs, _: Position| {
|
||||
let f = move |args: &mut FnCallArgs, pos| {
|
||||
let a = mem::take(args[0]).cast::<A>();
|
||||
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(
|
||||
fn_name,
|
||||
&[TypeId::of::<A>(), TypeId::of::<B>()],
|
||||
Box::new(f),
|
||||
)
|
||||
let arg_types = &[TypeId::of::<A>(), TypeId::of::<B>()];
|
||||
self.set_fn(fn_name, arg_types, Box::new(f))
|
||||
}
|
||||
|
||||
/// 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>>(
|
||||
&mut self,
|
||||
fn_name: &str,
|
||||
#[cfg(not(feature = "sync"))] func: impl Fn(&mut A, B) -> Result<T, Box<EvalAltResult>>
|
||||
+ 'static,
|
||||
#[cfg(feature = "sync")] func: impl Fn(&mut A, B) -> Result<T, Box<EvalAltResult>>
|
||||
+ Send
|
||||
+ Sync
|
||||
+ 'static,
|
||||
#[cfg(not(feature = "sync"))] func: impl Fn(&mut A, B) -> FuncReturn<T> + 'static,
|
||||
#[cfg(feature = "sync")] func: impl Fn(&mut A, B) -> FuncReturn<T> + Send + Sync + 'static,
|
||||
) -> u64 {
|
||||
let f = move |args: &mut FnCallArgs, _: Position| {
|
||||
let f = move |args: &mut FnCallArgs, pos| {
|
||||
let b = mem::take(args[1]).cast::<B>();
|
||||
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(
|
||||
fn_name,
|
||||
&[TypeId::of::<A>(), TypeId::of::<B>()],
|
||||
Box::new(f),
|
||||
)
|
||||
let arg_types = &[TypeId::of::<A>(), TypeId::of::<B>()];
|
||||
self.set_fn(fn_name, arg_types, Box::new(f))
|
||||
}
|
||||
|
||||
/// Set a Rust function taking three parameters into the module, returning a hash key.
|
||||
@ -280,24 +286,20 @@ impl Module {
|
||||
>(
|
||||
&mut self,
|
||||
fn_name: &str,
|
||||
#[cfg(not(feature = "sync"))] func: impl Fn(A, B, C) -> Result<T, Box<EvalAltResult>> + 'static,
|
||||
#[cfg(feature = "sync")] func: impl Fn(A, B, C) -> Result<T, Box<EvalAltResult>>
|
||||
+ Send
|
||||
+ Sync
|
||||
+ 'static,
|
||||
#[cfg(not(feature = "sync"))] func: impl Fn(A, B, C) -> FuncReturn<T> + 'static,
|
||||
#[cfg(feature = "sync")] func: impl Fn(A, B, C) -> FuncReturn<T> + Send + Sync + 'static,
|
||||
) -> u64 {
|
||||
let f = move |args: &mut FnCallArgs, _: Position| {
|
||||
let f = move |args: &mut FnCallArgs, pos| {
|
||||
let a = mem::take(args[0]).cast::<A>();
|
||||
let b = mem::take(args[1]).cast::<B>();
|
||||
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(
|
||||
fn_name,
|
||||
&[TypeId::of::<A>(), TypeId::of::<B>(), TypeId::of::<C>()],
|
||||
Box::new(f),
|
||||
)
|
||||
let arg_types = &[TypeId::of::<A>(), TypeId::of::<B>(), TypeId::of::<C>()];
|
||||
self.set_fn(fn_name, arg_types, Box::new(f))
|
||||
}
|
||||
|
||||
/// Set a Rust function taking three parameters (the first one mutable) into the module,
|
||||
@ -312,25 +314,20 @@ impl Module {
|
||||
>(
|
||||
&mut self,
|
||||
fn_name: &str,
|
||||
#[cfg(not(feature = "sync"))] func: impl Fn(&mut A, B, C) -> Result<T, Box<EvalAltResult>>
|
||||
+ 'static,
|
||||
#[cfg(feature = "sync")] func: impl Fn(&mut A, B, C) -> Result<T, Box<EvalAltResult>>
|
||||
+ Send
|
||||
+ Sync
|
||||
+ 'static,
|
||||
#[cfg(not(feature = "sync"))] func: impl Fn(&mut A, B, C) -> FuncReturn<T> + 'static,
|
||||
#[cfg(feature = "sync")] func: impl Fn(&mut A, B, C) -> FuncReturn<T> + Send + Sync + 'static,
|
||||
) -> u64 {
|
||||
let f = move |args: &mut FnCallArgs, _: Position| {
|
||||
let f = move |args: &mut FnCallArgs, pos| {
|
||||
let b = mem::take(args[1]).cast::<B>();
|
||||
let c = mem::take(args[2]).cast::<C>();
|
||||
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(
|
||||
fn_name,
|
||||
&[TypeId::of::<A>(), TypeId::of::<B>(), TypeId::of::<C>()],
|
||||
Box::new(f),
|
||||
)
|
||||
let arg_types = &[TypeId::of::<A>(), TypeId::of::<B>(), TypeId::of::<C>()];
|
||||
self.set_fn(fn_name, arg_types, Box::new(f))
|
||||
}
|
||||
|
||||
/// Get a Rust function.
|
||||
@ -388,22 +385,40 @@ impl Module {
|
||||
}
|
||||
}
|
||||
|
||||
/// Re-export module 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;
|
||||
|
||||
/// A module resolution service that loads module script files (assumed `.rhai` extension).
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct FileModuleResolver(PathBuf);
|
||||
/// A module resolution service that loads module script files from the file system.
|
||||
///
|
||||
/// The `new_with_path` and `new_with_path_and_extension` constructor functions
|
||||
/// allow specification of a base directory with module path used as a relative path offset
|
||||
/// to the base directory. The script file is then forced to be in a specified extension
|
||||
/// (default `.rhai`).
|
||||
#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Default)]
|
||||
pub struct FileModuleResolver {
|
||||
path: PathBuf,
|
||||
extension: String,
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
impl FileModuleResolver {
|
||||
/// Create a new `FileModuleResolver` with a specific base path.
|
||||
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.
|
||||
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 {
|
||||
fn resolve(&self, engine: &Engine, path: &str) -> Result<Module, Box<EvalAltResult>> {
|
||||
// Load the script file (attaching `.rhai`)
|
||||
let mut file_path = self.0.clone();
|
||||
fn resolve(
|
||||
&self,
|
||||
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.set_extension("rhai");
|
||||
file_path.set_extension(&self.extension); // Force extension
|
||||
|
||||
// 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
|
||||
let mut scope = Scope::new();
|
||||
|
||||
// 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
|
||||
let mut module = Module::new();
|
||||
|
||||
// Variables left in the scope become module variables
|
||||
for entry in scope.into_iter() {
|
||||
match entry.typ {
|
||||
ScopeEntryType::Normal | ScopeEntryType::Constant => {
|
||||
module
|
||||
.variables
|
||||
.insert(entry.name.into_owned(), entry.value);
|
||||
scope.into_iter().for_each(
|
||||
|ScopeEntry {
|
||||
name, typ, value, ..
|
||||
}| {
|
||||
match typ {
|
||||
// Variables left in the scope become module variables
|
||||
ScopeEntryType::Normal | ScopeEntryType::Constant => {
|
||||
module.variables.insert(name.into_owned(), value);
|
||||
}
|
||||
// Modules left in the scope become sub-modules
|
||||
ScopeEntryType::Module => {
|
||||
module
|
||||
.modules
|
||||
.insert(name.into_owned(), value.cast::<Module>());
|
||||
}
|
||||
}
|
||||
ScopeEntryType::Module => {
|
||||
module
|
||||
.modules
|
||||
.insert(entry.name.into_owned(), entry.value.cast::<Module>());
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
module.fn_lib = FunctionsLib::new().merge(ast.fn_lib());
|
||||
module.fn_lib = module.fn_lib.merge(ast.fn_lib());
|
||||
|
||||
Ok(module)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Static module resolver.
|
||||
mod stat {
|
||||
use super::*;
|
||||
|
||||
/// A module resolution service that serves modules added into it.
|
||||
#[derive(Debug, Clone, Default)]
|
||||
@ -469,40 +493,33 @@ pub mod resolvers {
|
||||
pub fn new() -> Self {
|
||||
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 {
|
||||
fn resolve(&self, _: &Engine, path: &str) -> Result<Module, Box<EvalAltResult>> {
|
||||
self.0.get(path).cloned().ok_or_else(|| {
|
||||
Box::new(EvalAltResult::ErrorModuleNotFound(
|
||||
path.to_string(),
|
||||
Position::none(),
|
||||
))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// 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(),
|
||||
)))
|
||||
fn resolve(
|
||||
&self,
|
||||
_: &Engine,
|
||||
path: &str,
|
||||
pos: Position,
|
||||
) -> Result<Module, Box<EvalAltResult>> {
|
||||
self.0
|
||||
.get(path)
|
||||
.cloned()
|
||||
.ok_or_else(|| Box::new(EvalAltResult::ErrorModuleNotFound(path.to_string(), pos)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -67,13 +67,9 @@ impl AST {
|
||||
/// Create a new `AST`.
|
||||
pub fn new(statements: Vec<Stmt>, fn_lib: FunctionsLib) -> Self {
|
||||
#[cfg(feature = "sync")]
|
||||
{
|
||||
Self(statements, Arc::new(fn_lib))
|
||||
}
|
||||
return Self(statements, Arc::new(fn_lib));
|
||||
#[cfg(not(feature = "sync"))]
|
||||
{
|
||||
Self(statements, Rc::new(fn_lib))
|
||||
}
|
||||
return Self(statements, Rc::new(fn_lib));
|
||||
}
|
||||
|
||||
/// Get the statements.
|
||||
@ -148,13 +144,9 @@ impl AST {
|
||||
};
|
||||
|
||||
#[cfg(feature = "sync")]
|
||||
{
|
||||
Self(ast, Arc::new(functions.merge(other.1.as_ref())))
|
||||
}
|
||||
return Self(ast, Arc::new(functions.merge(other.1.as_ref())));
|
||||
#[cfg(not(feature = "sync"))]
|
||||
{
|
||||
Self(ast, Rc::new(functions.merge(other.1.as_ref())))
|
||||
}
|
||||
return Self(ast, Rc::new(functions.merge(other.1.as_ref())));
|
||||
}
|
||||
|
||||
/// Clear all function definitions in the `AST`.
|
||||
@ -1810,7 +1802,6 @@ fn parse_let<'a>(
|
||||
}
|
||||
|
||||
/// Parse an import statement.
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
fn parse_import<'a>(
|
||||
input: &mut Peekable<TokenIterator<'a>>,
|
||||
stack: &mut Stack,
|
||||
@ -1941,7 +1932,6 @@ fn parse_stmt<'a>(
|
||||
Token::LeftBrace => parse_block(input, stack, breakable, allow_stmt_expr),
|
||||
|
||||
// fn ...
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
Token::Fn => Err(PERR::WrongFnDefinition.into_err(*pos)),
|
||||
|
||||
Token::If => parse_if(input, stack, breakable, allow_stmt_expr),
|
||||
@ -2115,14 +2105,11 @@ fn parse_global_level<'a>(
|
||||
|
||||
while !input.peek().unwrap().0.is_eof() {
|
||||
// Collect all the function definitions
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
{
|
||||
if let (Token::Fn, _) = input.peek().unwrap() {
|
||||
let mut stack = Stack::new();
|
||||
let f = parse_fn(input, &mut stack, true)?;
|
||||
functions.insert(calc_fn_def(&f.name, f.params.len()), f);
|
||||
continue;
|
||||
}
|
||||
if let (Token::Fn, _) = input.peek().unwrap() {
|
||||
let mut stack = Stack::new();
|
||||
let f = parse_fn(input, &mut stack, true)?;
|
||||
functions.insert(calc_fn_def(&f.name, f.params.len()), f);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Actual statement
|
||||
|
12
src/token.rs
12
src/token.rs
@ -153,11 +153,9 @@ pub enum Token {
|
||||
RightShift,
|
||||
SemiColon,
|
||||
Colon,
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
DoubleColon,
|
||||
Comma,
|
||||
Period,
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
MapStart,
|
||||
Equals,
|
||||
True,
|
||||
@ -182,7 +180,6 @@ pub enum Token {
|
||||
XOr,
|
||||
Ampersand,
|
||||
And,
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
Fn,
|
||||
Continue,
|
||||
Break,
|
||||
@ -199,11 +196,8 @@ pub enum Token {
|
||||
XOrAssign,
|
||||
ModuloAssign,
|
||||
PowerOfAssign,
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
Import,
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
Export,
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
As,
|
||||
LexError(Box<LexError>),
|
||||
EOF,
|
||||
@ -238,11 +232,9 @@ impl Token {
|
||||
Divide => "/",
|
||||
SemiColon => ";",
|
||||
Colon => ":",
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
DoubleColon => "::",
|
||||
Comma => ",",
|
||||
Period => ".",
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
MapStart => "#{",
|
||||
Equals => "=",
|
||||
True => "true",
|
||||
@ -266,7 +258,6 @@ impl Token {
|
||||
Or => "||",
|
||||
Ampersand => "&",
|
||||
And => "&&",
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
Fn => "fn",
|
||||
Continue => "continue",
|
||||
Break => "break",
|
||||
@ -288,11 +279,8 @@ impl Token {
|
||||
ModuloAssign => "%=",
|
||||
PowerOf => "~",
|
||||
PowerOfAssign => "~=",
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
Import => "import",
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
Export => "export",
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
As => "as",
|
||||
EOF => "{EOF}",
|
||||
_ => panic!("operator should be match in outer scope"),
|
||||
|
@ -64,10 +64,10 @@ fn test_module_resolver() -> Result<(), Box<EvalAltResult>> {
|
||||
let mut module = Module::new();
|
||||
module.set_var("answer", 42 as INT);
|
||||
|
||||
resolver.add_module("hello", module);
|
||||
resolver.insert("hello".to_string(), module);
|
||||
|
||||
let mut engine = Engine::new();
|
||||
engine.set_module_resolver(resolver);
|
||||
engine.set_module_resolver(Some(resolver));
|
||||
|
||||
assert_eq!(
|
||||
engine.eval::<INT>(
|
||||
|
Loading…
Reference in New Issue
Block a user