Make module_resolver optional and remove NullModuleResolver.

This commit is contained in:
Stephen Chung 2020-05-06 16:09:44 +08:00
parent c9571d375a
commit 88fec57394
8 changed files with 200 additions and 207 deletions

View File

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

View File

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

View File

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

View File

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

View File

@ -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();
// Variables left in the scope become module variables scope.into_iter().for_each(
for entry in scope.into_iter() { |ScopeEntry {
match entry.typ { name, typ, value, ..
ScopeEntryType::Normal | ScopeEntryType::Constant => { }| {
module match typ {
.variables // Variables left in the scope become module variables
.insert(entry.name.into_owned(), entry.value); 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) 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(),
)))
} }
} }
} }

View File

@ -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,14 +2105,11 @@ 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() {
{ let mut stack = Stack::new();
if let (Token::Fn, _) = input.peek().unwrap() { let f = parse_fn(input, &mut stack, true)?;
let mut stack = Stack::new(); functions.insert(calc_fn_def(&f.name, f.params.len()), f);
let f = parse_fn(input, &mut stack, true)?; continue;
functions.insert(calc_fn_def(&f.name, f.params.len()), f);
continue;
}
} }
// Actual statement // Actual statement

View File

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

View File

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