Make sure all features compile correctly.

This commit is contained in:
Stephen Chung 2020-05-06 19:45:17 +08:00
parent 88fec57394
commit d75a8bc6cd
11 changed files with 640 additions and 179 deletions

View File

@ -1,12 +1,19 @@
//! Helper module which defines the `Any` trait to to allow dynamic value handling. //! Helper module which defines the `Any` trait to to allow dynamic value handling.
use crate::engine::{Array, Map}; #[cfg(not(feature = "no_module"))]
use crate::module::Module; use crate::module::Module;
use crate::parser::INT; use crate::parser::INT;
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
use crate::parser::FLOAT; use crate::parser::FLOAT;
#[cfg(not(feature = "no_index"))]
use crate::engine::Array;
#[cfg(not(feature = "no_object"))]
use crate::engine::Map;
use crate::stdlib::{ use crate::stdlib::{
any::{type_name, Any, TypeId}, any::{type_name, Any, TypeId},
boxed::Box, boxed::Box,
@ -134,8 +141,11 @@ pub enum Union {
Int(INT), Int(INT),
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
Float(FLOAT), Float(FLOAT),
#[cfg(not(feature = "no_index"))]
Array(Box<Array>), Array(Box<Array>),
#[cfg(not(feature = "no_object"))]
Map(Box<Map>), Map(Box<Map>),
#[cfg(not(feature = "no_module"))]
Module(Box<Module>), Module(Box<Module>),
Variant(Box<Box<dyn Variant>>), Variant(Box<Box<dyn Variant>>),
} }
@ -165,8 +175,11 @@ impl Dynamic {
Union::Int(_) => TypeId::of::<INT>(), Union::Int(_) => TypeId::of::<INT>(),
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
Union::Float(_) => TypeId::of::<FLOAT>(), Union::Float(_) => TypeId::of::<FLOAT>(),
#[cfg(not(feature = "no_index"))]
Union::Array(_) => TypeId::of::<Array>(), Union::Array(_) => TypeId::of::<Array>(),
#[cfg(not(feature = "no_object"))]
Union::Map(_) => TypeId::of::<Map>(), Union::Map(_) => TypeId::of::<Map>(),
#[cfg(not(feature = "no_module"))]
Union::Module(_) => TypeId::of::<Module>(), Union::Module(_) => TypeId::of::<Module>(),
Union::Variant(value) => (***value).type_id(), Union::Variant(value) => (***value).type_id(),
} }
@ -182,8 +195,11 @@ impl Dynamic {
Union::Int(_) => type_name::<INT>(), Union::Int(_) => type_name::<INT>(),
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
Union::Float(_) => type_name::<FLOAT>(), Union::Float(_) => type_name::<FLOAT>(),
#[cfg(not(feature = "no_index"))]
Union::Array(_) => "array", Union::Array(_) => "array",
#[cfg(not(feature = "no_object"))]
Union::Map(_) => "map", Union::Map(_) => "map",
#[cfg(not(feature = "no_module"))]
Union::Module(_) => "sub-scope", Union::Module(_) => "sub-scope",
#[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_std"))]
@ -203,8 +219,11 @@ impl fmt::Display for Dynamic {
Union::Int(value) => write!(f, "{}", value), Union::Int(value) => write!(f, "{}", value),
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
Union::Float(value) => write!(f, "{}", value), Union::Float(value) => write!(f, "{}", value),
#[cfg(not(feature = "no_index"))]
Union::Array(value) => write!(f, "{:?}", value), Union::Array(value) => write!(f, "{:?}", value),
#[cfg(not(feature = "no_object"))]
Union::Map(value) => write!(f, "#{:?}", value), Union::Map(value) => write!(f, "#{:?}", value),
#[cfg(not(feature = "no_module"))]
Union::Module(value) => write!(f, "{:?}", value), Union::Module(value) => write!(f, "{:?}", value),
#[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_std"))]
@ -224,8 +243,11 @@ impl fmt::Debug for Dynamic {
Union::Int(value) => write!(f, "{:?}", value), Union::Int(value) => write!(f, "{:?}", value),
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
Union::Float(value) => write!(f, "{:?}", value), Union::Float(value) => write!(f, "{:?}", value),
#[cfg(not(feature = "no_index"))]
Union::Array(value) => write!(f, "{:?}", value), Union::Array(value) => write!(f, "{:?}", value),
#[cfg(not(feature = "no_object"))]
Union::Map(value) => write!(f, "#{:?}", value), Union::Map(value) => write!(f, "#{:?}", value),
#[cfg(not(feature = "no_module"))]
Union::Module(value) => write!(f, "{:?}", value), Union::Module(value) => write!(f, "{:?}", value),
#[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_std"))]
@ -245,8 +267,11 @@ impl Clone for Dynamic {
Union::Int(value) => Self(Union::Int(value)), Union::Int(value) => Self(Union::Int(value)),
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
Union::Float(value) => Self(Union::Float(value)), Union::Float(value) => Self(Union::Float(value)),
#[cfg(not(feature = "no_index"))]
Union::Array(ref value) => Self(Union::Array(value.clone())), Union::Array(ref value) => Self(Union::Array(value.clone())),
#[cfg(not(feature = "no_object"))]
Union::Map(ref value) => Self(Union::Map(value.clone())), Union::Map(ref value) => Self(Union::Map(value.clone())),
#[cfg(not(feature = "no_module"))]
Union::Module(ref value) => Self(Union::Module(value.clone())), Union::Module(ref value) => Self(Union::Module(value.clone())),
Union::Variant(ref value) => (***value).clone_into_dynamic(), Union::Variant(ref value) => (***value).clone_into_dynamic(),
} }
@ -260,13 +285,13 @@ impl Default for Dynamic {
} }
/// Cast a Boxed type into another type. /// Cast a Boxed type into another type.
fn cast_box<X: Variant, T: Variant>(item: Box<X>) -> Result<T, Box<X>> { fn cast_box<X: Variant, T: Variant>(item: Box<X>) -> Result<Box<T>, Box<X>> {
// Only allow casting to the exact same type // Only allow casting to the exact same type
if TypeId::of::<X>() == TypeId::of::<T>() { if TypeId::of::<X>() == TypeId::of::<T>() {
// SAFETY: just checked whether we are pointing to the correct type // SAFETY: just checked whether we are pointing to the correct type
unsafe { unsafe {
let raw: *mut dyn Any = Box::into_raw(item as Box<dyn Any>); let raw: *mut dyn Any = Box::into_raw(item as Box<dyn Any>);
Ok(*Box::from_raw(raw as *mut T)) Ok(Box::from_raw(raw as *mut T))
} }
} else { } else {
// Return the consumed item for chaining. // Return the consumed item for chaining.
@ -320,31 +345,33 @@ impl Dynamic {
} }
} }
let var = Box::new(value); let mut var = Box::new(value);
Self( var = match cast_box::<_, Dynamic>(var) {
cast_box::<_, Dynamic>(var) Ok(d) => return *d,
.map(|x| x.0) Err(var) => var,
.or_else(|var| { };
cast_box::<_, String>(var) var = match cast_box::<_, String>(var) {
.map(Box::new) Ok(s) => return Self(Union::Str(s)),
.map(Union::Str) Err(var) => var,
.or_else(|var| { };
cast_box::<_, Array>(var) #[cfg(not(feature = "no_index"))]
.map(Box::new) {
.map(Union::Array) var = match cast_box::<_, Array>(var) {
.or_else(|var| { Ok(array) => return Self(Union::Array(array)),
cast_box::<_, Map>(var) Err(var) => var,
.map(Box::new) };
.map(Union::Map) }
.or_else(|var| -> Result<Union, ()> {
Ok(Union::Variant(Box::new(var))) #[cfg(not(feature = "no_object"))]
}) {
}) var = match cast_box::<_, Map>(var) {
}) Ok(map) => return Self(Union::Map(map)),
}) Err(var) => var,
.unwrap(), }
) }
Self(Union::Variant(Box::new(var)))
} }
/// Get a copy of the `Dynamic` value as a specific type. /// Get a copy of the `Dynamic` value as a specific type.
@ -363,20 +390,23 @@ impl Dynamic {
/// ``` /// ```
pub fn try_cast<T: Variant + Clone>(self) -> Option<T> { pub fn try_cast<T: Variant + Clone>(self) -> Option<T> {
if TypeId::of::<T>() == TypeId::of::<Dynamic>() { if TypeId::of::<T>() == TypeId::of::<Dynamic>() {
return cast_box::<_, T>(Box::new(self)).ok(); return cast_box::<_, T>(Box::new(self)).ok().map(|v| *v);
} }
match self.0 { match self.0 {
Union::Unit(ref value) => (value as &dyn Any).downcast_ref::<T>().cloned(), Union::Unit(ref value) => (value as &dyn Any).downcast_ref::<T>().cloned(),
Union::Bool(ref value) => (value as &dyn Any).downcast_ref::<T>().cloned(), Union::Bool(ref value) => (value as &dyn Any).downcast_ref::<T>().cloned(),
Union::Str(value) => cast_box::<_, T>(value).ok(), Union::Str(value) => cast_box::<_, T>(value).ok().map(|v| *v),
Union::Char(ref value) => (value as &dyn Any).downcast_ref::<T>().cloned(), Union::Char(ref value) => (value as &dyn Any).downcast_ref::<T>().cloned(),
Union::Int(ref value) => (value as &dyn Any).downcast_ref::<T>().cloned(), Union::Int(ref value) => (value as &dyn Any).downcast_ref::<T>().cloned(),
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
Union::Float(ref value) => (value as &dyn Any).downcast_ref::<T>().cloned(), Union::Float(ref value) => (value as &dyn Any).downcast_ref::<T>().cloned(),
Union::Array(value) => cast_box::<_, T>(value).ok(), #[cfg(not(feature = "no_index"))]
Union::Map(value) => cast_box::<_, T>(value).ok(), Union::Array(value) => cast_box::<_, T>(value).ok().map(|v| *v),
Union::Module(value) => cast_box::<_, T>(value).ok(), #[cfg(not(feature = "no_object"))]
Union::Map(value) => cast_box::<_, T>(value).ok().map(|v| *v),
#[cfg(not(feature = "no_module"))]
Union::Module(value) => cast_box::<_, T>(value).ok().map(|v| *v),
Union::Variant(value) => value.as_any().downcast_ref::<T>().cloned(), Union::Variant(value) => value.as_any().downcast_ref::<T>().cloned(),
} }
} }
@ -401,20 +431,23 @@ impl Dynamic {
//self.try_cast::<T>().unwrap() //self.try_cast::<T>().unwrap()
if TypeId::of::<T>() == TypeId::of::<Dynamic>() { if TypeId::of::<T>() == TypeId::of::<Dynamic>() {
return cast_box::<_, T>(Box::new(self)).unwrap(); return *cast_box::<_, T>(Box::new(self)).unwrap();
} }
match self.0 { match self.0 {
Union::Unit(ref value) => (value as &dyn Any).downcast_ref::<T>().unwrap().clone(), Union::Unit(ref value) => (value as &dyn Any).downcast_ref::<T>().unwrap().clone(),
Union::Bool(ref value) => (value as &dyn Any).downcast_ref::<T>().unwrap().clone(), Union::Bool(ref value) => (value as &dyn Any).downcast_ref::<T>().unwrap().clone(),
Union::Str(value) => cast_box::<_, T>(value).unwrap(), Union::Str(value) => *cast_box::<_, T>(value).unwrap(),
Union::Char(ref value) => (value as &dyn Any).downcast_ref::<T>().unwrap().clone(), Union::Char(ref value) => (value as &dyn Any).downcast_ref::<T>().unwrap().clone(),
Union::Int(ref value) => (value as &dyn Any).downcast_ref::<T>().unwrap().clone(), Union::Int(ref value) => (value as &dyn Any).downcast_ref::<T>().unwrap().clone(),
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
Union::Float(ref value) => (value as &dyn Any).downcast_ref::<T>().unwrap().clone(), Union::Float(ref value) => (value as &dyn Any).downcast_ref::<T>().unwrap().clone(),
Union::Array(value) => cast_box::<_, T>(value).unwrap(), #[cfg(not(feature = "no_index"))]
Union::Map(value) => cast_box::<_, T>(value).unwrap(), Union::Array(value) => *cast_box::<_, T>(value).unwrap(),
Union::Module(value) => cast_box::<_, T>(value).unwrap(), #[cfg(not(feature = "no_object"))]
Union::Map(value) => *cast_box::<_, T>(value).unwrap(),
#[cfg(not(feature = "no_module"))]
Union::Module(value) => *cast_box::<_, T>(value).unwrap(),
Union::Variant(value) => value.as_any().downcast_ref::<T>().unwrap().clone(), Union::Variant(value) => value.as_any().downcast_ref::<T>().unwrap().clone(),
} }
} }
@ -435,8 +468,11 @@ impl Dynamic {
Union::Int(value) => (value as &dyn Any).downcast_ref::<T>(), Union::Int(value) => (value as &dyn Any).downcast_ref::<T>(),
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
Union::Float(value) => (value as &dyn Any).downcast_ref::<T>(), Union::Float(value) => (value as &dyn Any).downcast_ref::<T>(),
#[cfg(not(feature = "no_index"))]
Union::Array(value) => (value.as_ref() as &dyn Any).downcast_ref::<T>(), Union::Array(value) => (value.as_ref() as &dyn Any).downcast_ref::<T>(),
#[cfg(not(feature = "no_object"))]
Union::Map(value) => (value.as_ref() as &dyn Any).downcast_ref::<T>(), Union::Map(value) => (value.as_ref() as &dyn Any).downcast_ref::<T>(),
#[cfg(not(feature = "no_module"))]
Union::Module(value) => (value.as_ref() as &dyn Any).downcast_ref::<T>(), Union::Module(value) => (value.as_ref() as &dyn Any).downcast_ref::<T>(),
Union::Variant(value) => value.as_ref().as_ref().as_any().downcast_ref::<T>(), Union::Variant(value) => value.as_ref().as_ref().as_any().downcast_ref::<T>(),
} }
@ -458,8 +494,11 @@ impl Dynamic {
Union::Int(value) => (value as &mut dyn Any).downcast_mut::<T>(), Union::Int(value) => (value as &mut dyn Any).downcast_mut::<T>(),
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
Union::Float(value) => (value as &mut dyn Any).downcast_mut::<T>(), Union::Float(value) => (value as &mut dyn Any).downcast_mut::<T>(),
#[cfg(not(feature = "no_index"))]
Union::Array(value) => (value.as_mut() as &mut dyn Any).downcast_mut::<T>(), Union::Array(value) => (value.as_mut() as &mut dyn Any).downcast_mut::<T>(),
#[cfg(not(feature = "no_object"))]
Union::Map(value) => (value.as_mut() as &mut dyn Any).downcast_mut::<T>(), Union::Map(value) => (value.as_mut() as &mut dyn Any).downcast_mut::<T>(),
#[cfg(not(feature = "no_module"))]
Union::Module(value) => (value.as_mut() as &mut dyn Any).downcast_mut::<T>(), Union::Module(value) => (value.as_mut() as &mut dyn Any).downcast_mut::<T>(),
Union::Variant(value) => value.as_mut().as_mut_any().downcast_mut::<T>(), Union::Variant(value) => value.as_mut().as_mut_any().downcast_mut::<T>(),
} }
@ -542,6 +581,7 @@ impl From<String> for Dynamic {
Self(Union::Str(Box::new(value))) Self(Union::Str(Box::new(value)))
} }
} }
#[cfg(not(feature = "no_index"))]
impl<T: Variant + Clone> From<Vec<T>> for Dynamic { impl<T: Variant + Clone> From<Vec<T>> for Dynamic {
fn from(value: Vec<T>) -> Self { fn from(value: Vec<T>) -> Self {
Self(Union::Array(Box::new( Self(Union::Array(Box::new(
@ -549,6 +589,7 @@ impl<T: Variant + Clone> From<Vec<T>> for Dynamic {
))) )))
} }
} }
#[cfg(not(feature = "no_object"))]
impl<T: Variant + Clone> From<HashMap<String, T>> for Dynamic { impl<T: Variant + Clone> From<HashMap<String, T>> for Dynamic {
fn from(value: HashMap<String, T>) -> Self { fn from(value: HashMap<String, T>) -> Self {
Self(Union::Map(Box::new( Self(Union::Map(Box::new(

View File

@ -1,7 +1,7 @@
//! Module that defines the extern API of `Engine`. //! Module that defines the extern API of `Engine`.
use crate::any::{Dynamic, Variant}; use crate::any::{Dynamic, Variant};
use crate::engine::{make_getter, make_setter, Engine, Map, State, FUNC_INDEXER}; use crate::engine::{make_getter, make_setter, Engine, State, FUNC_INDEXER};
use crate::error::ParseError; use crate::error::ParseError;
use crate::fn_call::FuncArgs; use crate::fn_call::FuncArgs;
use crate::fn_register::RegisterFn; use crate::fn_register::RegisterFn;
@ -11,6 +11,9 @@ use crate::result::EvalAltResult;
use crate::scope::Scope; use crate::scope::Scope;
use crate::token::{lex, Position}; use crate::token::{lex, Position};
#[cfg(not(feature = "no_object"))]
use crate::engine::Map;
use crate::stdlib::{ use crate::stdlib::{
any::{type_name, TypeId}, any::{type_name, TypeId},
boxed::Box, boxed::Box,

View File

@ -3,7 +3,6 @@
use crate::any::{Dynamic, Union}; use crate::any::{Dynamic, Union};
use crate::calc_fn_hash; use crate::calc_fn_hash;
use crate::error::ParseErrorType; use crate::error::ParseErrorType;
use crate::module::{resolvers, Module, ModuleResolver};
use crate::optimize::OptimizationLevel; use crate::optimize::OptimizationLevel;
use crate::packages::{CorePackage, Package, PackageLibrary, StandardPackage}; use crate::packages::{CorePackage, Package, PackageLibrary, StandardPackage};
use crate::parser::{Expr, FnDef, ModuleRef, ReturnType, Stmt, AST}; use crate::parser::{Expr, FnDef, ModuleRef, ReturnType, Stmt, AST};
@ -12,6 +11,9 @@ use crate::scope::{EntryType as ScopeEntryType, Scope};
use crate::token::Position; use crate::token::Position;
use crate::utils::{calc_fn_def, StaticVec}; use crate::utils::{calc_fn_def, StaticVec};
#[cfg(not(feature = "no_module"))]
use crate::module::{resolvers, Module, ModuleResolver};
use crate::stdlib::{ use crate::stdlib::{
any::TypeId, any::TypeId,
boxed::Box, boxed::Box,
@ -30,11 +32,13 @@ use crate::stdlib::{
/// An dynamic array of `Dynamic` values. /// An dynamic array of `Dynamic` values.
/// ///
/// Not available under the `no_index` feature. /// Not available under the `no_index` feature.
#[cfg(not(feature = "no_index"))]
pub type Array = Vec<Dynamic>; pub type Array = Vec<Dynamic>;
/// An dynamic hash map of `Dynamic` values with `String` keys. /// An dynamic hash map of `Dynamic` values with `String` keys.
/// ///
/// Not available under the `no_object` feature. /// Not available under the `no_object` feature.
#[cfg(not(feature = "no_object"))]
pub type Map = HashMap<String, Dynamic>; pub type Map = HashMap<String, Dynamic>;
pub type FnCallArgs<'a> = [&'a mut Dynamic]; pub type FnCallArgs<'a> = [&'a mut Dynamic];
@ -262,6 +266,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.
#[cfg(not(feature = "no_module"))]
pub(crate) module_resolver: Option<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.
@ -301,7 +306,8 @@ 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: Some(Box::new(resolvers::FileModuleResolver::new())), module_resolver: Some(Box::new(resolvers::FileModuleResolver::new())),
#[cfg(any(feature = "no_std", feature = "no_module"))] #[cfg(not(feature = "no_module"))]
#[cfg(feature = "no_std")]
module_resolver: None, module_resolver: None,
type_names: Default::default(), type_names: Default::default(),
@ -391,6 +397,8 @@ fn search_scope<'a>(
index: Option<NonZeroUsize>, index: Option<NonZeroUsize>,
pos: Position, pos: Position,
) -> Result<(&'a mut Dynamic, ScopeEntryType), Box<EvalAltResult>> { ) -> Result<(&'a mut Dynamic, ScopeEntryType), Box<EvalAltResult>> {
#[cfg(not(feature = "no_module"))]
{
if let Some(modules) = modules { if let Some(modules) = modules {
let (id, root_pos) = modules.get(0); // First module let (id, root_pos) = modules.get(0); // First module
@ -401,17 +409,19 @@ fn search_scope<'a>(
.downcast_mut::<Module>() .downcast_mut::<Module>()
.unwrap() .unwrap()
} else { } else {
scope scope.find_module(id).ok_or_else(|| {
.find_module(id) Box::new(EvalAltResult::ErrorModuleNotFound(id.into(), *root_pos))
.ok_or_else(|| Box::new(EvalAltResult::ErrorModuleNotFound(id.into(), *root_pos)))? })?
}; };
Ok(( return Ok((
module.get_qualified_var_mut(name, modules.as_ref(), pos)?, module.get_qualified_var_mut(name, modules.as_ref(), pos)?,
// Module variables are constant // Module variables are constant
ScopeEntryType::Constant, ScopeEntryType::Constant,
)) ));
} else { }
}
let index = if let Some(index) = index { let index = if let Some(index) = index {
scope.len() - index.get() scope.len() - index.get()
} else { } else {
@ -422,7 +432,6 @@ fn search_scope<'a>(
}; };
Ok(scope.get_mut(index)) Ok(scope.get_mut(index))
}
} }
impl Engine { impl Engine {
@ -438,7 +447,10 @@ 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(),
#[cfg(not(feature = "no_module"))]
module_resolver: None, module_resolver: None,
type_names: Default::default(), type_names: Default::default(),
print: Box::new(|_| {}), print: Box::new(|_| {}),
debug: Box::new(|_| {}), debug: Box::new(|_| {}),
@ -721,7 +733,7 @@ impl Engine {
} }
let statements = mem::take(ast.statements_mut()); let statements = mem::take(ast.statements_mut());
ast = AST::new(statements, fn_lib.clone()); let ast = AST::new(statements, fn_lib.clone());
// Evaluate the AST // Evaluate the AST
self.eval_ast_with_scope_raw(scope, &ast) self.eval_ast_with_scope_raw(scope, &ast)
@ -779,7 +791,7 @@ impl Engine {
// xxx.fn_name(arg_expr_list) // xxx.fn_name(arg_expr_list)
Expr::FnCall(fn_name, None,_, def_val, pos) => { Expr::FnCall(fn_name, None,_, def_val, pos) => {
let mut args: Vec<_> = once(obj) let mut args: Vec<_> = once(obj)
.chain(idx_val.downcast_mut::<Array>().unwrap().iter_mut()) .chain(idx_val.downcast_mut::<Vec<Dynamic>>().unwrap().iter_mut())
.collect(); .collect();
let def_val = def_val.as_deref(); let def_val = def_val.as_deref();
// A function call is assumed to have side effects, so the value is changed // A function call is assumed to have side effects, so the value is changed
@ -789,6 +801,7 @@ impl Engine {
// xxx.module::fn_name(...) - syntax error // xxx.module::fn_name(...) - syntax error
Expr::FnCall(_,_,_,_,_) => unreachable!(), Expr::FnCall(_,_,_,_,_) => unreachable!(),
// {xxx:map}.id = ??? // {xxx:map}.id = ???
#[cfg(not(feature = "no_object"))]
Expr::Property(id, pos) if obj.is::<Map>() && new_val.is_some() => { Expr::Property(id, pos) if obj.is::<Map>() && new_val.is_some() => {
let mut indexed_val = let mut indexed_val =
self.get_indexed_mut(fn_lib, obj, id.to_string().into(), *pos, op_pos, true)?; self.get_indexed_mut(fn_lib, obj, id.to_string().into(), *pos, op_pos, true)?;
@ -796,6 +809,7 @@ impl Engine {
Ok((Default::default(), true)) Ok((Default::default(), true))
} }
// {xxx:map}.id // {xxx:map}.id
#[cfg(not(feature = "no_object"))]
Expr::Property(id, pos) if obj.is::<Map>() => { Expr::Property(id, pos) if obj.is::<Map>() => {
let indexed_val = let indexed_val =
self.get_indexed_mut(fn_lib, obj, id.to_string().into(), *pos, op_pos, false)?; self.get_indexed_mut(fn_lib, obj, id.to_string().into(), *pos, op_pos, false)?;
@ -813,6 +827,7 @@ impl Engine {
let mut args = [obj]; let mut args = [obj];
self.exec_fn_call(fn_lib, &fn_name, &mut args, None, *pos, 0).map(|v| (v, false)) self.exec_fn_call(fn_lib, &fn_name, &mut args, None, *pos, 0).map(|v| (v, false))
} }
#[cfg(not(feature = "no_object"))]
// {xxx:map}.idx_lhs[idx_expr] // {xxx:map}.idx_lhs[idx_expr]
Expr::Index(dot_lhs, dot_rhs, pos) | Expr::Index(dot_lhs, dot_rhs, pos) |
// {xxx:map}.dot_lhs.rhs // {xxx:map}.dot_lhs.rhs
@ -959,7 +974,10 @@ impl Engine {
.map(|arg_expr| self.eval_expr(scope, state, fn_lib, arg_expr, level)) .map(|arg_expr| self.eval_expr(scope, state, fn_lib, arg_expr, level))
.collect::<Result<Vec<_>, _>>()?; .collect::<Result<Vec<_>, _>>()?;
idx_values.push(arg_values) #[cfg(not(feature = "no_index"))]
idx_values.push(arg_values);
#[cfg(feature = "no_index")]
idx_values.push(Dynamic::from(arg_values));
} }
Expr::FnCall(_, _, _, _, _) => unreachable!(), Expr::FnCall(_, _, _, _, _) => unreachable!(),
Expr::Property(_, _) => idx_values.push(()), // Store a placeholder - no need to copy the property name Expr::Property(_, _) => idx_values.push(()), // Store a placeholder - no need to copy the property name
@ -994,6 +1012,7 @@ impl Engine {
let type_name = self.map_type_name(val.type_name()); let type_name = self.map_type_name(val.type_name());
match val { match val {
#[cfg(not(feature = "no_index"))]
Dynamic(Union::Array(arr)) => { Dynamic(Union::Array(arr)) => {
// val_array[idx] // val_array[idx]
let index = idx let index = idx
@ -1015,6 +1034,7 @@ impl Engine {
} }
} }
#[cfg(not(feature = "no_object"))]
Dynamic(Union::Map(map)) => { Dynamic(Union::Map(map)) => {
// val_map[idx] // val_map[idx]
let index = idx let index = idx
@ -1030,6 +1050,7 @@ impl Engine {
}) })
} }
#[cfg(not(feature = "no_index"))]
Dynamic(Union::Str(s)) => { Dynamic(Union::Str(s)) => {
// val_string[idx] // val_string[idx]
let index = idx let index = idx
@ -1088,6 +1109,7 @@ impl Engine {
let rhs_value = self.eval_expr(scope, state, fn_lib, rhs, level)?; let rhs_value = self.eval_expr(scope, state, fn_lib, rhs, level)?;
match rhs_value { match rhs_value {
#[cfg(not(feature = "no_index"))]
Dynamic(Union::Array(mut rhs_value)) => { Dynamic(Union::Array(mut rhs_value)) => {
let def_value = false.into(); let def_value = false.into();
@ -1108,6 +1130,7 @@ impl Engine {
Ok(false.into()) Ok(false.into())
} }
#[cfg(not(feature = "no_object"))]
Dynamic(Union::Map(rhs_value)) => match lhs_value { Dynamic(Union::Map(rhs_value)) => match lhs_value {
// Only allows String or char // Only allows String or char
Dynamic(Union::Str(s)) => Ok(rhs_value.contains_key(s.as_ref()).into()), Dynamic(Union::Str(s)) => Ok(rhs_value.contains_key(s.as_ref()).into()),
@ -1230,7 +1253,8 @@ impl Engine {
.collect::<Result<HashMap<_, _>, _>>()?, .collect::<Result<HashMap<_, _>, _>>()?,
)))), )))),
Expr::FnCall(fn_name, modules, arg_exprs, def_val, pos) => { // Normal function call
Expr::FnCall(fn_name, None, arg_exprs, def_val, pos) => {
let mut arg_values = arg_exprs let mut arg_values = arg_exprs
.iter() .iter()
.map(|expr| self.eval_expr(scope, state, fn_lib, expr, level)) .map(|expr| self.eval_expr(scope, state, fn_lib, expr, level))
@ -1238,32 +1262,7 @@ impl Engine {
let mut args: Vec<_> = arg_values.iter_mut().collect(); let mut args: Vec<_> = arg_values.iter_mut().collect();
if let Some(modules) = modules { if fn_name.as_ref() == KEYWORD_EVAL
// Module-qualified function call
let modules = modules.as_ref();
let (id, root_pos) = modules.get(0); // First module
let module = scope.find_module(id).ok_or_else(|| {
Box::new(EvalAltResult::ErrorModuleNotFound(id.into(), *root_pos))
})?;
// First search in script-defined functions (can override built-in)
if let Some(fn_def) =
module.get_qualified_fn_lib(fn_name, args.len(), modules)?
{
self.call_fn_from_lib(None, fn_lib, fn_def, &mut args, *pos, level)
} else {
// Then search in Rust functions
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),
Err(_) if def_val.is_some() => Ok(def_val.as_deref().unwrap().clone()),
Err(err) => Err(err),
}
}
} else if fn_name.as_ref() == KEYWORD_EVAL
&& args.len() == 1 && args.len() == 1
&& !self.has_override(fn_lib, KEYWORD_EVAL) && !self.has_override(fn_lib, KEYWORD_EVAL)
{ {
@ -1288,6 +1287,39 @@ impl Engine {
} }
} }
// Module-qualified function call
#[cfg(not(feature = "no_module"))]
Expr::FnCall(fn_name, Some(modules), arg_exprs, def_val, pos) => {
let modules = modules.as_ref();
let mut arg_values = arg_exprs
.iter()
.map(|expr| self.eval_expr(scope, state, fn_lib, expr, level))
.collect::<Result<Vec<_>, _>>()?;
let mut args: Vec<_> = arg_values.iter_mut().collect();
let (id, root_pos) = modules.get(0); // First module
let module = scope.find_module(id).ok_or_else(|| {
Box::new(EvalAltResult::ErrorModuleNotFound(id.into(), *root_pos))
})?;
// First search in script-defined functions (can override built-in)
if let Some(fn_def) = module.get_qualified_fn_lib(fn_name, args.len(), modules)? {
self.call_fn_from_lib(None, fn_lib, fn_def, &mut args, *pos, level)
} else {
// Then search in Rust functions
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),
Err(_) if def_val.is_some() => Ok(def_val.as_deref().unwrap().clone()),
Err(err) => Err(err),
}
}
}
Expr::In(lhs, rhs, _) => { Expr::In(lhs, rhs, _) => {
self.eval_in_expr(scope, state, fn_lib, lhs.as_ref(), rhs.as_ref(), level) self.eval_in_expr(scope, state, fn_lib, lhs.as_ref(), rhs.as_ref(), level)
} }
@ -1519,6 +1551,8 @@ impl Engine {
#[cfg(feature = "no_module")] #[cfg(feature = "no_module")]
unreachable!(); unreachable!();
#[cfg(not(feature = "no_module"))]
{
if let Some(path) = self if let Some(path) = self
.eval_expr(scope, state, fn_lib, expr, level)? .eval_expr(scope, state, fn_lib, expr, level)?
.try_cast::<String>() .try_cast::<String>()
@ -1542,6 +1576,7 @@ impl Engine {
} }
} }
} }
}
/// Map a type_name into a pretty-print name /// Map a type_name into a pretty-print name
pub(crate) fn map_type_name<'a>(&'a self, name: &'a str) -> &'a str { pub(crate) fn map_type_name<'a>(&'a self, name: &'a str) -> &'a str {

View File

@ -1,9 +1,10 @@
//! Module defining external-loaded modules for Rhai. //! Module defining external-loaded modules for Rhai.
#![cfg(not(feature = "no_module"))]
use crate::any::{Dynamic, Variant}; use crate::any::{Dynamic, Variant};
use crate::calc_fn_hash; 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, AST};
use crate::result::EvalAltResult; use crate::result::EvalAltResult;
use crate::scope::{Entry as ScopeEntry, EntryType as ScopeEntryType, Scope}; use crate::scope::{Entry as ScopeEntry, EntryType as ScopeEntryType, Scope};
use crate::token::Position; use crate::token::Position;
@ -12,11 +13,12 @@ use crate::utils::StaticVec;
use crate::stdlib::{ use crate::stdlib::{
any::TypeId, any::TypeId,
boxed::Box,
collections::HashMap, collections::HashMap,
fmt, mem, fmt, mem,
ops::{Deref, DerefMut}, ops::{Deref, DerefMut},
rc::Rc, rc::Rc,
string::String, string::{String, ToString},
sync::Arc, sync::Arc,
}; };
@ -70,21 +72,61 @@ impl fmt::Debug for Module {
impl Module { impl Module {
/// Create a new module. /// Create a new module.
///
/// # Examples
///
/// ```
/// use rhai::Module;
///
/// let mut module = Module::new();
/// module.set_var("answer", 42_i64);
/// assert_eq!(module.get_var_value::<i64>("answer").unwrap(), 42);
/// ```
pub fn new() -> Self { pub fn new() -> Self {
Default::default() Default::default()
} }
/// Does a variable exist in the module? /// Does a variable exist in the module?
///
/// # Examples
///
/// ```
/// use rhai::Module;
///
/// let mut module = Module::new();
/// module.set_var("answer", 42_i64);
/// assert!(module.contains_var("answer"));
/// ```
pub fn contains_var(&self, name: &str) -> bool { pub fn contains_var(&self, name: &str) -> bool {
self.variables.contains_key(name) self.variables.contains_key(name)
} }
/// Get the value of a module variable. /// Get the value of a module variable.
///
/// # Examples
///
/// ```
/// use rhai::Module;
///
/// let mut module = Module::new();
/// module.set_var("answer", 42_i64);
/// assert_eq!(module.get_var_value::<i64>("answer").unwrap(), 42);
/// ```
pub fn get_var_value<T: Variant + Clone>(&self, name: &str) -> Option<T> { pub fn get_var_value<T: Variant + Clone>(&self, name: &str) -> Option<T> {
self.get_var(name).and_then(|v| v.try_cast::<T>()) self.get_var(name).and_then(|v| v.try_cast::<T>())
} }
/// Get a module variable. /// Get a module variable as a `Dynamic`.
///
/// # Examples
///
/// ```
/// use rhai::Module;
///
/// let mut module = Module::new();
/// module.set_var("answer", 42_i64);
/// assert_eq!(module.get_var("answer").unwrap().cast::<i64>(), 42);
/// ```
pub fn get_var(&self, name: &str) -> Option<Dynamic> { pub fn get_var(&self, name: &str) -> Option<Dynamic> {
self.variables.get(name).cloned() self.variables.get(name).cloned()
} }
@ -97,6 +139,16 @@ impl Module {
/// Set a variable into the module. /// Set a variable into the module.
/// ///
/// If there is an existing variable of the same name, it is replaced. /// If there is an existing variable of the same name, it is replaced.
///
/// # Examples
///
/// ```
/// use rhai::Module;
///
/// let mut module = Module::new();
/// module.set_var("answer", 42_i64);
/// assert_eq!(module.get_var_value::<i64>("answer").unwrap(), 42);
/// ```
pub fn set_var<K: Into<String>, T: Into<Dynamic>>(&mut self, name: K, value: T) { pub fn set_var<K: Into<String>, T: Into<Dynamic>>(&mut self, name: K, value: T) {
self.variables.insert(name.into(), value.into()); self.variables.insert(name.into(), value.into());
} }
@ -115,16 +167,49 @@ impl Module {
} }
/// Does a sub-module exist in the module? /// Does a sub-module exist in the module?
///
/// # Examples
///
/// ```
/// use rhai::Module;
///
/// let mut module = Module::new();
/// let sub_module = Module::new();
/// module.set_sub_module("question", sub_module);
/// assert!(module.contains_sub_module("question"));
/// ```
pub fn contains_sub_module(&self, name: &str) -> bool { pub fn contains_sub_module(&self, name: &str) -> bool {
self.modules.contains_key(name) self.modules.contains_key(name)
} }
/// Get a sub-module. /// Get a sub-module.
///
/// # Examples
///
/// ```
/// use rhai::Module;
///
/// let mut module = Module::new();
/// let sub_module = Module::new();
/// module.set_sub_module("question", sub_module);
/// assert!(module.get_sub_module("question").is_some());
/// ```
pub fn get_sub_module(&self, name: &str) -> Option<&Module> { pub fn get_sub_module(&self, name: &str) -> Option<&Module> {
self.modules.get(name) self.modules.get(name)
} }
/// Get a mutable reference to a sub-module. /// Get a mutable reference to a sub-module.
///
/// # Examples
///
/// ```
/// use rhai::Module;
///
/// let mut module = Module::new();
/// let sub_module = Module::new();
/// module.set_sub_module("question", sub_module);
/// assert!(module.get_sub_module_mut("question").is_some());
/// ```
pub fn get_sub_module_mut(&mut self, name: &str) -> Option<&mut Module> { pub fn get_sub_module_mut(&mut self, name: &str) -> Option<&mut Module> {
self.modules.get_mut(name) self.modules.get_mut(name)
} }
@ -132,6 +217,17 @@ impl Module {
/// Set a sub-module into the module. /// Set a sub-module into the module.
/// ///
/// If there is an existing sub-module of the same name, it is replaced. /// If there is an existing sub-module of the same name, it is replaced.
///
/// # Examples
///
/// ```
/// use rhai::Module;
///
/// let mut module = Module::new();
/// let sub_module = Module::new();
/// module.set_sub_module("question", sub_module);
/// assert!(module.get_sub_module("question").is_some());
/// ```
pub fn set_sub_module<K: Into<String>>(&mut self, name: K, sub_module: Module) { pub fn set_sub_module<K: Into<String>>(&mut self, name: K, sub_module: Module) {
self.modules.insert(name.into(), sub_module.into()); self.modules.insert(name.into(), sub_module.into());
} }
@ -160,6 +256,16 @@ impl Module {
/// ///
/// The `u64` hash is calculated by the function `crate::calc_fn_hash`. /// The `u64` hash is calculated by the function `crate::calc_fn_hash`.
/// It is also returned by the `set_fn_XXX` calls. /// It is also returned by the `set_fn_XXX` calls.
///
/// # Examples
///
/// ```
/// use rhai::Module;
///
/// let mut module = Module::new();
/// let hash = module.set_fn_0("calc", || Ok(42_i64));
/// assert!(module.contains_fn(hash));
/// ```
pub fn contains_fn(&self, hash: u64) -> bool { pub fn contains_fn(&self, hash: u64) -> bool {
self.functions.contains_key(&hash) self.functions.contains_key(&hash)
} }
@ -181,6 +287,16 @@ impl Module {
/// Set a Rust function taking no parameters into the module, returning a hash key. /// Set a Rust function taking no parameters into the module, returning a hash key.
/// ///
/// If there is a similar existing Rust function, it is replaced. /// If there is a similar existing Rust function, it is replaced.
///
/// # Examples
///
/// ```
/// use rhai::Module;
///
/// let mut module = Module::new();
/// let hash = module.set_fn_0("calc", || Ok(42_i64));
/// assert!(module.get_fn(hash).is_some());
/// ```
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,
@ -199,6 +315,16 @@ impl Module {
/// 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.
/// ///
/// If there is a similar existing Rust function, it is replaced. /// If there is a similar existing Rust function, it is replaced.
///
/// # Examples
///
/// ```
/// use rhai::Module;
///
/// let mut module = Module::new();
/// let hash = module.set_fn_1("calc", |x: i64| Ok(x + 1));
/// assert!(module.get_fn(hash).is_some());
/// ```
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,
@ -217,6 +343,16 @@ impl Module {
/// 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.
/// ///
/// If there is a similar existing Rust function, it is replaced. /// If there is a similar existing Rust function, it is replaced.
///
/// # Examples
///
/// ```
/// use rhai::Module;
///
/// let mut module = Module::new();
/// let hash = module.set_fn_1_mut("calc", |x: &mut i64| { *x += 1; Ok(*x) });
/// assert!(module.get_fn(hash).is_some());
/// ```
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,
@ -235,6 +371,18 @@ impl Module {
/// 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.
/// ///
/// If there is a similar existing Rust function, it is replaced. /// If there is a similar existing Rust function, it is replaced.
///
/// # Examples
///
/// ```
/// use rhai::Module;
///
/// let mut module = Module::new();
/// let hash = module.set_fn_2("calc", |x: i64, y: String| {
/// Ok(x + y.len() as i64)
/// });
/// assert!(module.get_fn(hash).is_some());
/// ```
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,
@ -256,7 +404,17 @@ impl Module {
/// 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,
/// returning a hash key. /// returning a hash key.
/// ///
/// If there is a similar existing Rust function, it is replaced. /// # Examples
///
/// ```
/// use rhai::Module;
///
/// let mut module = Module::new();
/// let hash = module.set_fn_2_mut("calc", |x: &mut i64, y: String| {
/// *x += y.len() as i64; Ok(*x)
/// });
/// assert!(module.get_fn(hash).is_some());
/// ```
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,
@ -278,6 +436,18 @@ impl Module {
/// 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.
/// ///
/// If there is a similar existing Rust function, it is replaced. /// If there is a similar existing Rust function, it is replaced.
///
/// # Examples
///
/// ```
/// use rhai::Module;
///
/// let mut module = Module::new();
/// let hash = module.set_fn_3("calc", |x: i64, y: String, z: i64| {
/// Ok(x + y.len() as i64 + z)
/// });
/// assert!(module.get_fn(hash).is_some());
/// ```
pub fn set_fn_3< pub fn set_fn_3<
A: Variant + Clone, A: Variant + Clone,
B: Variant + Clone, B: Variant + Clone,
@ -306,6 +476,18 @@ impl Module {
/// returning a hash key. /// returning a hash key.
/// ///
/// If there is a similar existing Rust function, it is replaced. /// If there is a similar existing Rust function, it is replaced.
///
/// # Examples
///
/// ```
/// use rhai::Module;
///
/// let mut module = Module::new();
/// let hash = module.set_fn_3_mut("calc", |x: &mut i64, y: String, z: i64| {
/// *x += y.len() as i64 + z; Ok(*x)
/// });
/// assert!(module.get_fn(hash).is_some());
/// ```
pub fn set_fn_3_mut< pub fn set_fn_3_mut<
A: Variant + Clone, A: Variant + Clone,
B: Variant + Clone, B: Variant + Clone,
@ -334,6 +516,16 @@ impl Module {
/// ///
/// The `u64` hash is calculated by the function `crate::calc_fn_hash`. /// The `u64` hash is calculated by the function `crate::calc_fn_hash`.
/// It is also returned by the `set_fn_XXX` calls. /// It is also returned by the `set_fn_XXX` calls.
///
/// # Examples
///
/// ```
/// use rhai::Module;
///
/// let mut module = Module::new();
/// let hash = module.set_fn_1("calc", |x: i64| Ok(x + 1));
/// assert!(module.get_fn(hash).is_some());
/// ```
pub fn get_fn(&self, hash: u64) -> Option<&Box<FnAny>> { pub fn get_fn(&self, hash: u64) -> Option<&Box<FnAny>> {
self.functions.get(&hash).map(|v| v.as_ref()) self.functions.get(&hash).map(|v| v.as_ref())
} }
@ -366,7 +558,16 @@ impl Module {
})?) })?)
} }
/// Get a script-defined function. /// Get the script-defined functions.
///
/// # Examples
///
/// ```
/// use rhai::Module;
///
/// let mut module = Module::new();
/// assert_eq!(module.get_fn_lib().len(), 0);
/// ```
pub fn get_fn_lib(&self) -> &FunctionsLib { pub fn get_fn_lib(&self) -> &FunctionsLib {
&self.fn_lib &self.fn_lib
} }
@ -383,73 +584,29 @@ impl Module {
.fn_lib .fn_lib
.get_function(name, args)) .get_function(name, args))
} }
}
/// Re-export module resolvers. /// Create a new `Module` by evaluating an `AST`.
pub mod resolvers {
pub use super::file::FileModuleResolver;
pub use super::stat::StaticModuleResolver;
}
/// 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 from the file system.
/// ///
/// The `new_with_path` and `new_with_path_and_extension` constructor functions /// # Examples
/// 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,
}
impl FileModuleResolver {
/// Create a new `FileModuleResolver` with a specific base path.
pub fn new_with_path(path: PathBuf) -> Self {
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 { /// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
Self { path, extension } /// use rhai::{Engine, Module};
} ///
/// Create a new `FileModuleResolver` with the current directory as base path. /// let engine = Engine::new();
pub fn new() -> Self { /// let ast = engine.compile("let answer = 42;")?;
Default::default() /// let module = Module::eval_ast_as_new(&ast, &engine)?;
} /// assert!(module.contains_var("answer"));
} /// assert_eq!(module.get_var_value::<i64>("answer").unwrap(), 42);
/// # Ok(())
impl ModuleResolver for FileModuleResolver { /// # }
fn resolve( /// ```
&self, pub fn eval_ast_as_new(ast: &AST, engine: &Engine) -> FuncReturn<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(&self.extension); // Force extension
// Compile it
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 engine.eval_ast_with_scope_raw(&mut scope, &ast)?;
.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();
@ -477,6 +634,134 @@ mod file {
Ok(module) Ok(module)
} }
}
/// Re-export module resolvers.
pub mod resolvers {
#[cfg(not(feature = "no_std"))]
pub use super::file::FileModuleResolver;
pub use super::stat::StaticModuleResolver;
}
/// 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 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`).
///
/// # Examples
///
/// ```
/// use rhai::Engine;
/// use rhai::module_resolvers::FileModuleResolver;
///
/// // Create a new 'FileModuleResolver' loading scripts from the 'scripts' subdirectory
/// // with file extension '.x'.
/// let resolver = FileModuleResolver::new_with_path_and_extension("./scripts", "x");
///
/// let mut engine = Engine::new();
/// engine.set_module_resolver(Some(resolver));
/// ```
#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Default)]
pub struct FileModuleResolver {
path: PathBuf,
extension: String,
}
impl FileModuleResolver {
/// Create a new `FileModuleResolver` with a specific base path.
///
/// # Examples
///
/// ```
/// use rhai::Engine;
/// use rhai::module_resolvers::FileModuleResolver;
///
/// // Create a new 'FileModuleResolver' loading scripts from the 'scripts' subdirectory
/// // with file extension '.rhai' (the default).
/// let resolver = FileModuleResolver::new_with_path("./scripts");
///
/// let mut engine = Engine::new();
/// engine.set_module_resolver(Some(resolver));
/// ```
pub fn new_with_path<P: Into<PathBuf>>(path: P) -> Self {
Self::new_with_path_and_extension(path, "rhai")
}
/// Create a new `FileModuleResolver` with a specific base path and file extension.
///
/// The default extension is `.rhai`.
///
/// # Examples
///
/// ```
/// use rhai::Engine;
/// use rhai::module_resolvers::FileModuleResolver;
///
/// // Create a new 'FileModuleResolver' loading scripts from the 'scripts' subdirectory
/// // with file extension '.x'.
/// let resolver = FileModuleResolver::new_with_path_and_extension("./scripts", "x");
///
/// let mut engine = Engine::new();
/// engine.set_module_resolver(Some(resolver));
/// ```
pub fn new_with_path_and_extension<P: Into<PathBuf>, E: Into<String>>(
path: P,
extension: E,
) -> Self {
Self {
path: path.into(),
extension: extension.into(),
}
}
/// Create a new `FileModuleResolver` with the current directory as base path.
///
/// # Examples
///
/// ```
/// use rhai::Engine;
/// use rhai::module_resolvers::FileModuleResolver;
///
/// // Create a new 'FileModuleResolver' loading scripts from the current directory
/// // with file extension '.rhai' (the default).
/// let resolver = FileModuleResolver::new();
///
/// let mut engine = Engine::new();
/// engine.set_module_resolver(Some(resolver));
/// ```
pub fn new() -> Self {
Default::default()
}
}
impl ModuleResolver for FileModuleResolver {
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(&self.extension); // Force extension
// Compile it
let ast = engine
.compile_file(file_path)
.map_err(|err| EvalAltResult::set_position(err, pos))?;
Module::eval_ast_as_new(&ast, engine)
.map_err(|err| EvalAltResult::set_position(err, pos))
}
} }
} }
@ -485,11 +770,41 @@ mod stat {
use super::*; use super::*;
/// A module resolution service that serves modules added into it. /// A module resolution service that serves modules added into it.
///
/// # Examples
///
/// ```
/// use rhai::{Engine, Module};
/// use rhai::module_resolvers::StaticModuleResolver;
///
/// let mut resolver = StaticModuleResolver::new();
///
/// let module = Module::new();
/// resolver.insert("hello".to_string(), module);
///
/// let mut engine = Engine::new();
/// engine.set_module_resolver(Some(resolver));
/// ```
#[derive(Debug, Clone, Default)] #[derive(Debug, Clone, Default)]
pub struct StaticModuleResolver(HashMap<String, Module>); pub struct StaticModuleResolver(HashMap<String, Module>);
impl StaticModuleResolver { impl StaticModuleResolver {
/// Create a new `StaticModuleResolver`. /// Create a new `StaticModuleResolver`.
///
/// # Examples
///
/// ```
/// use rhai::{Engine, Module};
/// use rhai::module_resolvers::StaticModuleResolver;
///
/// let mut resolver = StaticModuleResolver::new();
///
/// let module = Module::new();
/// resolver.insert("hello".to_string(), module);
///
/// let mut engine = Engine::new();
/// engine.set_module_resolver(Some(resolver));
/// ```
pub fn new() -> Self { pub fn new() -> Self {
Default::default() Default::default()
} }

View File

@ -711,11 +711,16 @@ pub fn optimize_into_ast(
#[cfg(feature = "no_optimize")] #[cfg(feature = "no_optimize")]
const level: OptimizationLevel = OptimizationLevel::None; const level: OptimizationLevel = OptimizationLevel::None;
#[cfg(not(feature = "no_function"))]
let fn_lib: Vec<_> = functions let fn_lib: Vec<_> = functions
.iter() .iter()
.map(|fn_def| (fn_def.name.as_str(), fn_def.params.len())) .map(|fn_def| (fn_def.name.as_str(), fn_def.params.len()))
.collect(); .collect();
#[cfg(feature = "no_function")]
const fn_lib: &[(&str, usize)] = &[];
#[cfg(not(feature = "no_function"))]
let lib = FunctionsLib::from_vec( let lib = FunctionsLib::from_vec(
functions functions
.iter() .iter()
@ -745,6 +750,9 @@ pub fn optimize_into_ast(
.collect(), .collect(),
); );
#[cfg(feature = "no_function")]
let lib: FunctionsLib = Default::default();
AST::new( AST::new(
match level { match level {
OptimizationLevel::None => statements, OptimizationLevel::None => statements,

View File

@ -1,3 +1,5 @@
#![cfg(not(feature = "no_index"))]
use super::{reg_binary, reg_binary_mut, reg_trinary_mut, reg_unary_mut}; use super::{reg_binary, reg_binary_mut, reg_trinary_mut, reg_unary_mut};
use crate::any::{Dynamic, Variant}; use crate::any::{Dynamic, Variant};

View File

@ -1,3 +1,5 @@
#![cfg(not(feature = "no_object"))]
use super::{reg_binary, reg_binary_mut, reg_unary_mut}; use super::{reg_binary, reg_binary_mut, reg_unary_mut};
use crate::any::Dynamic; use crate::any::Dynamic;

View File

@ -1,10 +1,16 @@
use super::{reg_binary, reg_binary_mut, reg_none, reg_unary, reg_unary_mut}; use super::{reg_binary, reg_binary_mut, reg_none, reg_unary, reg_unary_mut};
use crate::def_package; use crate::def_package;
use crate::engine::{Array, Map, FUNC_TO_STRING, KEYWORD_DEBUG, KEYWORD_PRINT}; use crate::engine::{FUNC_TO_STRING, KEYWORD_DEBUG, KEYWORD_PRINT};
use crate::fn_register::map_dynamic as map; use crate::fn_register::map_dynamic as map;
use crate::parser::INT; use crate::parser::INT;
#[cfg(not(feature = "no_index"))]
use crate::engine::Array;
#[cfg(not(feature = "no_object"))]
use crate::engine::Map;
use crate::stdlib::{ use crate::stdlib::{
fmt::{Debug, Display}, fmt::{Debug, Display},
format, format,
@ -18,6 +24,7 @@ fn to_debug<T: Debug>(x: &mut T) -> String {
fn to_string<T: Display>(x: &mut T) -> String { fn to_string<T: Display>(x: &mut T) -> String {
format!("{}", x) format!("{}", x)
} }
#[cfg(not(feature = "no_object"))]
fn format_map(x: &mut Map) -> String { fn format_map(x: &mut Map) -> String {
format!("#{:?}", x) format!("#{:?}", x)
} }

View File

@ -1,10 +1,12 @@
use super::{reg_binary, reg_binary_mut, reg_trinary_mut, reg_unary_mut}; use super::{reg_binary, reg_binary_mut, reg_trinary_mut, reg_unary_mut};
use crate::def_package; use crate::def_package;
use crate::engine::Array;
use crate::fn_register::map_dynamic as map; use crate::fn_register::map_dynamic as map;
use crate::parser::INT; use crate::parser::INT;
#[cfg(not(feature = "no_index"))]
use crate::engine::Array;
use crate::stdlib::{ use crate::stdlib::{
fmt::Display, fmt::Display,
format, format,

View File

@ -39,6 +39,7 @@ pub type INT = i32;
/// The system floating-point type. /// The system floating-point type.
/// ///
/// Not available under the `no_float` feature. /// Not available under the `no_float` feature.
#[cfg(not(feature = "no_float"))]
pub type FLOAT = f64; pub type FLOAT = f64;
type PERR = ParseErrorType; type PERR = ParseErrorType;
@ -143,13 +144,11 @@ impl AST {
(true, true) => vec![], (true, true) => vec![],
}; };
#[cfg(feature = "sync")] Self::new(ast, functions.merge(other.1.as_ref()))
return Self(ast, Arc::new(functions.merge(other.1.as_ref())));
#[cfg(not(feature = "sync"))]
return Self(ast, Rc::new(functions.merge(other.1.as_ref())));
} }
/// Clear all function definitions in the `AST`. /// Clear all function definitions in the `AST`.
#[cfg(not(feature = "no_function"))]
pub fn clear_functions(&mut self) { pub fn clear_functions(&mut self) {
#[cfg(feature = "sync")] #[cfg(feature = "sync")]
{ {
@ -162,6 +161,7 @@ impl AST {
} }
/// Clear all statements in the `AST`, leaving only function definitions. /// Clear all statements in the `AST`, leaving only function definitions.
#[cfg(not(feature = "no_function"))]
pub fn retain_functions(&mut self) { pub fn retain_functions(&mut self) {
self.0 = vec![]; self.0 = vec![];
} }
@ -351,6 +351,7 @@ pub enum Expr {
/// Integer constant. /// Integer constant.
IntegerConstant(INT, Position), IntegerConstant(INT, Position),
/// Floating-point constant. /// Floating-point constant.
#[cfg(not(feature = "no_float"))]
FloatConstant(FLOAT, Position), FloatConstant(FLOAT, Position),
/// Character constant. /// Character constant.
CharConstant(char, Position), CharConstant(char, Position),
@ -413,6 +414,7 @@ impl Expr {
Self::False(_) => false.into(), Self::False(_) => false.into(),
Self::Unit(_) => ().into(), Self::Unit(_) => ().into(),
#[cfg(not(feature = "no_index"))]
Self::Array(items, _) if items.iter().all(Self::is_constant) => { Self::Array(items, _) if items.iter().all(Self::is_constant) => {
Dynamic(Union::Array(Box::new( Dynamic(Union::Array(Box::new(
items items
@ -422,6 +424,7 @@ impl Expr {
))) )))
} }
#[cfg(not(feature = "no_object"))]
Self::Map(items, _) if items.iter().all(|(_, v, _)| v.is_constant()) => { Self::Map(items, _) if items.iter().all(|(_, v, _)| v.is_constant()) => {
Dynamic(Union::Map(Box::new( Dynamic(Union::Map(Box::new(
items items
@ -442,8 +445,10 @@ impl Expr {
/// Panics when the expression is not constant. /// Panics when the expression is not constant.
pub fn get_constant_str(&self) -> String { pub fn get_constant_str(&self) -> String {
match self { match self {
Self::IntegerConstant(i, _) => i.to_string(), #[cfg(not(feature = "no_float"))]
Self::FloatConstant(f, _) => f.to_string(), Self::FloatConstant(f, _) => f.to_string(),
Self::IntegerConstant(i, _) => i.to_string(),
Self::CharConstant(c, _) => c.to_string(), Self::CharConstant(c, _) => c.to_string(),
Self::StringConstant(_, _) => "string".to_string(), Self::StringConstant(_, _) => "string".to_string(),
Self::True(_) => "true".to_string(), Self::True(_) => "true".to_string(),
@ -459,8 +464,10 @@ impl Expr {
/// Get the `Position` of the expression. /// Get the `Position` of the expression.
pub fn position(&self) -> Position { pub fn position(&self) -> Position {
match self { match self {
#[cfg(not(feature = "no_float"))]
Self::FloatConstant(_, pos) => *pos,
Self::IntegerConstant(_, pos) Self::IntegerConstant(_, pos)
| Self::FloatConstant(_, pos)
| Self::CharConstant(_, pos) | Self::CharConstant(_, pos)
| Self::StringConstant(_, pos) | Self::StringConstant(_, pos)
| Self::Array(_, pos) | Self::Array(_, pos)
@ -485,8 +492,10 @@ impl Expr {
/// Get the `Position` of the expression. /// Get the `Position` of the expression.
pub(crate) fn set_position(mut self, new_pos: Position) -> Self { pub(crate) fn set_position(mut self, new_pos: Position) -> Self {
match &mut self { match &mut self {
#[cfg(not(feature = "no_float"))]
Self::FloatConstant(_, pos) => *pos = new_pos,
Self::IntegerConstant(_, pos) Self::IntegerConstant(_, pos)
| Self::FloatConstant(_, pos)
| Self::CharConstant(_, pos) | Self::CharConstant(_, pos)
| Self::StringConstant(_, pos) | Self::StringConstant(_, pos)
| Self::Array(_, pos) | Self::Array(_, pos)
@ -531,8 +540,10 @@ impl Expr {
/// Is the expression a constant? /// Is the expression a constant?
pub fn is_constant(&self) -> bool { pub fn is_constant(&self) -> bool {
match self { match self {
#[cfg(not(feature = "no_float"))]
Self::FloatConstant(_, _) => true,
Self::IntegerConstant(_, _) Self::IntegerConstant(_, _)
| Self::FloatConstant(_, _)
| Self::CharConstant(_, _) | Self::CharConstant(_, _)
| Self::StringConstant(_, _) | Self::StringConstant(_, _)
| Self::True(_) | Self::True(_)
@ -559,8 +570,10 @@ impl Expr {
/// Is a particular token allowed as a postfix operator to this expression? /// Is a particular token allowed as a postfix operator to this expression?
pub fn is_valid_postfix(&self, token: &Token) -> bool { pub fn is_valid_postfix(&self, token: &Token) -> bool {
match self { match self {
#[cfg(not(feature = "no_float"))]
Self::FloatConstant(_, _) => false,
Self::IntegerConstant(_, _) Self::IntegerConstant(_, _)
| Self::FloatConstant(_, _)
| Self::CharConstant(_, _) | Self::CharConstant(_, _)
| Self::In(_, _, _) | Self::In(_, _, _)
| Self::And(_, _, _) | Self::And(_, _, _)
@ -764,8 +777,15 @@ fn parse_index_chain<'a>(
.into_err(*pos)) .into_err(*pos))
} }
Expr::FloatConstant(_, pos) #[cfg(not(feature = "no_float"))]
| Expr::CharConstant(_, pos) Expr::FloatConstant(_, pos) => {
return Err(PERR::MalformedIndexExpr(
"Only arrays, object maps and strings can be indexed".into(),
)
.into_err(pos))
}
Expr::CharConstant(_, pos)
| Expr::Assignment(_, _, pos) | Expr::Assignment(_, _, pos)
| Expr::And(_, _, pos) | Expr::And(_, _, pos)
| Expr::Or(_, _, pos) | Expr::Or(_, _, pos)
@ -792,8 +812,16 @@ fn parse_index_chain<'a>(
) )
.into_err(*pos)) .into_err(*pos))
} }
Expr::FloatConstant(_, pos)
| Expr::CharConstant(_, pos) #[cfg(not(feature = "no_float"))]
Expr::FloatConstant(_, pos) => {
return Err(PERR::MalformedIndexExpr(
"Only arrays, object maps and strings can be indexed".into(),
)
.into_err(pos))
}
Expr::CharConstant(_, pos)
| Expr::Assignment(_, _, pos) | Expr::Assignment(_, _, pos)
| Expr::And(_, _, pos) | Expr::And(_, _, pos)
| Expr::Or(_, _, pos) | Expr::Or(_, _, pos)
@ -811,6 +839,7 @@ fn parse_index_chain<'a>(
}, },
// lhs[float] // lhs[float]
#[cfg(not(feature = "no_float"))]
Expr::FloatConstant(_, pos) => { Expr::FloatConstant(_, pos) => {
return Err(PERR::MalformedIndexExpr( return Err(PERR::MalformedIndexExpr(
"Array access expects integer index, not a float".into(), "Array access expects integer index, not a float".into(),
@ -1095,6 +1124,7 @@ fn parse_primary<'a>(
} }
} }
// Indexing // Indexing
#[cfg(not(feature = "no_index"))]
(expr, Token::LeftBracket) => { (expr, Token::LeftBracket) => {
parse_index_chain(input, stack, expr, token_pos, allow_stmt_expr)? parse_index_chain(input, stack, expr, token_pos, allow_stmt_expr)?
} }
@ -1280,7 +1310,6 @@ fn make_dot_expr(
fn make_in_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result<Expr, Box<ParseError>> { fn make_in_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result<Expr, Box<ParseError>> {
match (&lhs, &rhs) { match (&lhs, &rhs) {
(_, Expr::IntegerConstant(_, pos)) (_, Expr::IntegerConstant(_, pos))
| (_, Expr::FloatConstant(_, pos))
| (_, Expr::And(_, _, pos)) | (_, Expr::And(_, _, pos))
| (_, Expr::Or(_, _, pos)) | (_, Expr::Or(_, _, pos))
| (_, Expr::In(_, _, pos)) | (_, Expr::In(_, _, pos))
@ -1294,11 +1323,20 @@ fn make_in_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result<Expr, Box<Pars
.into_err(*pos)) .into_err(*pos))
} }
#[cfg(not(feature = "no_float"))]
(_, Expr::FloatConstant(_, pos)) => {
return Err(PERR::MalformedInExpr(
"'in' expression expects a string, array or object map".into(),
)
.into_err(*pos))
}
// "xxx" in "xxxx", 'x' in "xxxx" - OK! // "xxx" in "xxxx", 'x' in "xxxx" - OK!
(Expr::StringConstant(_, _), Expr::StringConstant(_, _)) (Expr::StringConstant(_, _), Expr::StringConstant(_, _))
| (Expr::CharConstant(_, _), Expr::StringConstant(_, _)) => (), | (Expr::CharConstant(_, _), Expr::StringConstant(_, _)) => (),
// 123.456 in "xxxx" // 123.456 in "xxxx"
#[cfg(not(feature = "no_float"))]
(Expr::FloatConstant(_, pos), Expr::StringConstant(_, _)) => { (Expr::FloatConstant(_, pos), Expr::StringConstant(_, _)) => {
return Err(PERR::MalformedInExpr( return Err(PERR::MalformedInExpr(
"'in' expression for a string expects a string, not a float".into(), "'in' expression for a string expects a string, not a float".into(),
@ -1352,6 +1390,7 @@ fn make_in_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result<Expr, Box<Pars
| (Expr::CharConstant(_, _), Expr::Map(_, _)) => (), | (Expr::CharConstant(_, _), Expr::Map(_, _)) => (),
// 123.456 in #{...} // 123.456 in #{...}
#[cfg(not(feature = "no_float"))]
(Expr::FloatConstant(_, pos), Expr::Map(_, _)) => { (Expr::FloatConstant(_, pos), Expr::Map(_, _)) => {
return Err(PERR::MalformedInExpr( return Err(PERR::MalformedInExpr(
"'in' expression for an object map expects a string, not a float".into(), "'in' expression for an object map expects a string, not a float".into(),
@ -1975,6 +2014,7 @@ fn parse_stmt<'a>(
Token::Let => parse_let(input, stack, ScopeEntryType::Normal, allow_stmt_expr), Token::Let => parse_let(input, stack, ScopeEntryType::Normal, allow_stmt_expr),
Token::Const => parse_let(input, stack, ScopeEntryType::Constant, allow_stmt_expr), Token::Const => parse_let(input, stack, ScopeEntryType::Constant, allow_stmt_expr),
#[cfg(not(feature = "no_module"))]
Token::Import => parse_import(input, stack, allow_stmt_expr), Token::Import => parse_import(input, stack, allow_stmt_expr),
_ => parse_expr_stmt(input, stack, allow_stmt_expr), _ => parse_expr_stmt(input, stack, allow_stmt_expr),
@ -2105,13 +2145,15 @@ 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)?;

View File

@ -1,10 +1,12 @@
//! Module that defines the `Scope` type representing a function call-stack scope. //! Module that defines the `Scope` type representing a function call-stack scope.
use crate::any::{Dynamic, Union, Variant}; use crate::any::{Dynamic, Union, Variant};
use crate::module::Module;
use crate::parser::{map_dynamic_to_expr, Expr}; use crate::parser::{map_dynamic_to_expr, Expr};
use crate::token::Position; use crate::token::Position;
#[cfg(not(feature = "no_module"))]
use crate::module::Module;
use crate::stdlib::{borrow::Cow, boxed::Box, iter, vec, vec::Vec}; use crate::stdlib::{borrow::Cow, boxed::Box, iter, vec, vec::Vec};
/// Type of an entry in the Scope. /// Type of an entry in the Scope.
@ -172,6 +174,7 @@ impl<'a> Scope<'a> {
/// Add (push) a new module to the Scope. /// Add (push) a new module to the Scope.
/// ///
/// Modules are used for accessing member variables, functions and plugins under a namespace. /// Modules are used for accessing member variables, functions and plugins under a namespace.
#[cfg(not(feature = "no_module"))]
pub fn push_module<K: Into<Cow<'a, str>>>(&mut self, name: K, value: Module) { pub fn push_module<K: Into<Cow<'a, str>>>(&mut self, name: K, value: Module) {
self.push_dynamic_value( self.push_dynamic_value(
name, name,
@ -340,6 +343,7 @@ impl<'a> Scope<'a> {
} }
/// Find a module in the Scope, starting from the last entry. /// Find a module in the Scope, starting from the last entry.
#[cfg(not(feature = "no_module"))]
pub fn find_module(&mut self, name: &str) -> Option<&mut Module> { pub fn find_module(&mut self, name: &str) -> Option<&mut Module> {
let index = self.get_module_index(name)?; let index = self.get_module_index(name)?;
self.get_mut(index).0.downcast_mut::<Module>() self.get_mut(index).0.downcast_mut::<Module>()