diff --git a/src/any.rs b/src/any.rs index 78fa76d9..16bb8ae7 100644 --- a/src/any.rs +++ b/src/any.rs @@ -1,12 +1,19 @@ //! 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::parser::INT; #[cfg(not(feature = "no_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::{ any::{type_name, Any, TypeId}, boxed::Box, @@ -134,8 +141,11 @@ pub enum Union { Int(INT), #[cfg(not(feature = "no_float"))] Float(FLOAT), + #[cfg(not(feature = "no_index"))] Array(Box), + #[cfg(not(feature = "no_object"))] Map(Box), + #[cfg(not(feature = "no_module"))] Module(Box), Variant(Box>), } @@ -165,8 +175,11 @@ impl Dynamic { Union::Int(_) => TypeId::of::(), #[cfg(not(feature = "no_float"))] Union::Float(_) => TypeId::of::(), + #[cfg(not(feature = "no_index"))] Union::Array(_) => TypeId::of::(), + #[cfg(not(feature = "no_object"))] Union::Map(_) => TypeId::of::(), + #[cfg(not(feature = "no_module"))] Union::Module(_) => TypeId::of::(), Union::Variant(value) => (***value).type_id(), } @@ -182,8 +195,11 @@ impl Dynamic { Union::Int(_) => type_name::(), #[cfg(not(feature = "no_float"))] Union::Float(_) => type_name::(), + #[cfg(not(feature = "no_index"))] Union::Array(_) => "array", + #[cfg(not(feature = "no_object"))] Union::Map(_) => "map", + #[cfg(not(feature = "no_module"))] Union::Module(_) => "sub-scope", #[cfg(not(feature = "no_std"))] @@ -203,8 +219,11 @@ impl fmt::Display for Dynamic { Union::Int(value) => write!(f, "{}", value), #[cfg(not(feature = "no_float"))] Union::Float(value) => write!(f, "{}", value), + #[cfg(not(feature = "no_index"))] Union::Array(value) => write!(f, "{:?}", value), + #[cfg(not(feature = "no_object"))] Union::Map(value) => write!(f, "#{:?}", value), + #[cfg(not(feature = "no_module"))] Union::Module(value) => write!(f, "{:?}", value), #[cfg(not(feature = "no_std"))] @@ -224,8 +243,11 @@ impl fmt::Debug for Dynamic { Union::Int(value) => write!(f, "{:?}", value), #[cfg(not(feature = "no_float"))] Union::Float(value) => write!(f, "{:?}", value), + #[cfg(not(feature = "no_index"))] Union::Array(value) => write!(f, "{:?}", value), + #[cfg(not(feature = "no_object"))] Union::Map(value) => write!(f, "#{:?}", value), + #[cfg(not(feature = "no_module"))] Union::Module(value) => write!(f, "{:?}", value), #[cfg(not(feature = "no_std"))] @@ -245,8 +267,11 @@ impl Clone for Dynamic { Union::Int(value) => Self(Union::Int(value)), #[cfg(not(feature = "no_float"))] Union::Float(value) => Self(Union::Float(value)), + #[cfg(not(feature = "no_index"))] Union::Array(ref value) => Self(Union::Array(value.clone())), + #[cfg(not(feature = "no_object"))] Union::Map(ref value) => Self(Union::Map(value.clone())), + #[cfg(not(feature = "no_module"))] Union::Module(ref value) => Self(Union::Module(value.clone())), Union::Variant(ref value) => (***value).clone_into_dynamic(), } @@ -260,13 +285,13 @@ impl Default for Dynamic { } /// Cast a Boxed type into another type. -fn cast_box(item: Box) -> Result> { +fn cast_box(item: Box) -> Result, Box> { // Only allow casting to the exact same type if TypeId::of::() == TypeId::of::() { // SAFETY: just checked whether we are pointing to the correct type unsafe { let raw: *mut dyn Any = Box::into_raw(item as Box); - Ok(*Box::from_raw(raw as *mut T)) + Ok(Box::from_raw(raw as *mut T)) } } else { // Return the consumed item for chaining. @@ -320,31 +345,33 @@ impl Dynamic { } } - let var = Box::new(value); + let mut var = Box::new(value); - Self( - cast_box::<_, Dynamic>(var) - .map(|x| x.0) - .or_else(|var| { - cast_box::<_, String>(var) - .map(Box::new) - .map(Union::Str) - .or_else(|var| { - cast_box::<_, Array>(var) - .map(Box::new) - .map(Union::Array) - .or_else(|var| { - cast_box::<_, Map>(var) - .map(Box::new) - .map(Union::Map) - .or_else(|var| -> Result { - Ok(Union::Variant(Box::new(var))) - }) - }) - }) - }) - .unwrap(), - ) + var = match cast_box::<_, Dynamic>(var) { + Ok(d) => return *d, + Err(var) => var, + }; + var = match cast_box::<_, String>(var) { + Ok(s) => return Self(Union::Str(s)), + Err(var) => var, + }; + #[cfg(not(feature = "no_index"))] + { + var = match cast_box::<_, Array>(var) { + Ok(array) => return Self(Union::Array(array)), + Err(var) => var, + }; + } + + #[cfg(not(feature = "no_object"))] + { + var = match cast_box::<_, Map>(var) { + Ok(map) => return Self(Union::Map(map)), + Err(var) => var, + } + } + + Self(Union::Variant(Box::new(var))) } /// Get a copy of the `Dynamic` value as a specific type. @@ -363,20 +390,23 @@ impl Dynamic { /// ``` pub fn try_cast(self) -> Option { if TypeId::of::() == TypeId::of::() { - return cast_box::<_, T>(Box::new(self)).ok(); + return cast_box::<_, T>(Box::new(self)).ok().map(|v| *v); } match self.0 { Union::Unit(ref value) => (value as &dyn Any).downcast_ref::().cloned(), Union::Bool(ref value) => (value as &dyn Any).downcast_ref::().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::().cloned(), Union::Int(ref value) => (value as &dyn Any).downcast_ref::().cloned(), #[cfg(not(feature = "no_float"))] Union::Float(ref value) => (value as &dyn Any).downcast_ref::().cloned(), - Union::Array(value) => cast_box::<_, T>(value).ok(), - Union::Map(value) => cast_box::<_, T>(value).ok(), - Union::Module(value) => cast_box::<_, T>(value).ok(), + #[cfg(not(feature = "no_index"))] + Union::Array(value) => cast_box::<_, T>(value).ok().map(|v| *v), + #[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::().cloned(), } } @@ -401,20 +431,23 @@ impl Dynamic { //self.try_cast::().unwrap() if TypeId::of::() == TypeId::of::() { - return cast_box::<_, T>(Box::new(self)).unwrap(); + return *cast_box::<_, T>(Box::new(self)).unwrap(); } match self.0 { Union::Unit(ref value) => (value as &dyn Any).downcast_ref::().unwrap().clone(), Union::Bool(ref value) => (value as &dyn Any).downcast_ref::().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::().unwrap().clone(), Union::Int(ref value) => (value as &dyn Any).downcast_ref::().unwrap().clone(), #[cfg(not(feature = "no_float"))] Union::Float(ref value) => (value as &dyn Any).downcast_ref::().unwrap().clone(), - Union::Array(value) => cast_box::<_, T>(value).unwrap(), - Union::Map(value) => cast_box::<_, T>(value).unwrap(), - Union::Module(value) => cast_box::<_, T>(value).unwrap(), + #[cfg(not(feature = "no_index"))] + Union::Array(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::().unwrap().clone(), } } @@ -435,8 +468,11 @@ impl Dynamic { Union::Int(value) => (value as &dyn Any).downcast_ref::(), #[cfg(not(feature = "no_float"))] Union::Float(value) => (value as &dyn Any).downcast_ref::(), + #[cfg(not(feature = "no_index"))] Union::Array(value) => (value.as_ref() as &dyn Any).downcast_ref::(), + #[cfg(not(feature = "no_object"))] Union::Map(value) => (value.as_ref() as &dyn Any).downcast_ref::(), + #[cfg(not(feature = "no_module"))] Union::Module(value) => (value.as_ref() as &dyn Any).downcast_ref::(), Union::Variant(value) => value.as_ref().as_ref().as_any().downcast_ref::(), } @@ -458,8 +494,11 @@ impl Dynamic { Union::Int(value) => (value as &mut dyn Any).downcast_mut::(), #[cfg(not(feature = "no_float"))] Union::Float(value) => (value as &mut dyn Any).downcast_mut::(), + #[cfg(not(feature = "no_index"))] Union::Array(value) => (value.as_mut() as &mut dyn Any).downcast_mut::(), + #[cfg(not(feature = "no_object"))] Union::Map(value) => (value.as_mut() as &mut dyn Any).downcast_mut::(), + #[cfg(not(feature = "no_module"))] Union::Module(value) => (value.as_mut() as &mut dyn Any).downcast_mut::(), Union::Variant(value) => value.as_mut().as_mut_any().downcast_mut::(), } @@ -542,6 +581,7 @@ impl From for Dynamic { Self(Union::Str(Box::new(value))) } } +#[cfg(not(feature = "no_index"))] impl From> for Dynamic { fn from(value: Vec) -> Self { Self(Union::Array(Box::new( @@ -549,6 +589,7 @@ impl From> for Dynamic { ))) } } +#[cfg(not(feature = "no_object"))] impl From> for Dynamic { fn from(value: HashMap) -> Self { Self(Union::Map(Box::new( diff --git a/src/api.rs b/src/api.rs index abc4d37f..d0193549 100644 --- a/src/api.rs +++ b/src/api.rs @@ -1,7 +1,7 @@ //! Module that defines the extern API of `Engine`. 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::fn_call::FuncArgs; use crate::fn_register::RegisterFn; @@ -11,6 +11,9 @@ use crate::result::EvalAltResult; use crate::scope::Scope; use crate::token::{lex, Position}; +#[cfg(not(feature = "no_object"))] +use crate::engine::Map; + use crate::stdlib::{ any::{type_name, TypeId}, boxed::Box, diff --git a/src/engine.rs b/src/engine.rs index e9e14e1a..b35419cb 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -3,7 +3,6 @@ use crate::any::{Dynamic, Union}; use crate::calc_fn_hash; use crate::error::ParseErrorType; -use crate::module::{resolvers, Module, ModuleResolver}; use crate::optimize::OptimizationLevel; use crate::packages::{CorePackage, Package, PackageLibrary, StandardPackage}; 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::utils::{calc_fn_def, StaticVec}; +#[cfg(not(feature = "no_module"))] +use crate::module::{resolvers, Module, ModuleResolver}; + use crate::stdlib::{ any::TypeId, boxed::Box, @@ -30,11 +32,13 @@ use crate::stdlib::{ /// An dynamic array of `Dynamic` values. /// /// Not available under the `no_index` feature. +#[cfg(not(feature = "no_index"))] pub type Array = Vec; /// An dynamic hash map of `Dynamic` values with `String` keys. /// /// Not available under the `no_object` feature. +#[cfg(not(feature = "no_object"))] pub type Map = HashMap; pub type FnCallArgs<'a> = [&'a mut Dynamic]; @@ -262,6 +266,7 @@ pub struct Engine { pub(crate) type_iterators: HashMap>, /// A module resolution service. + #[cfg(not(feature = "no_module"))] pub(crate) module_resolver: Option>, /// 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_std"))] 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, type_names: Default::default(), @@ -391,38 +397,41 @@ fn search_scope<'a>( index: Option, pos: Position, ) -> Result<(&'a mut Dynamic, ScopeEntryType), Box> { - if let Some(modules) = modules { - let (id, root_pos) = modules.get(0); // First module + #[cfg(not(feature = "no_module"))] + { + if let Some(modules) = modules { + let (id, root_pos) = modules.get(0); // First module - let module = if let Some(index) = index { - scope - .get_mut(scope.len() - index.get()) - .0 - .downcast_mut::() - .unwrap() - } else { - scope - .find_module(id) - .ok_or_else(|| Box::new(EvalAltResult::ErrorModuleNotFound(id.into(), *root_pos)))? - }; + let module = if let Some(index) = index { + scope + .get_mut(scope.len() - index.get()) + .0 + .downcast_mut::() + .unwrap() + } else { + scope.find_module(id).ok_or_else(|| { + Box::new(EvalAltResult::ErrorModuleNotFound(id.into(), *root_pos)) + })? + }; - Ok(( - module.get_qualified_var_mut(name, modules.as_ref(), pos)?, - // Module variables are constant - ScopeEntryType::Constant, - )) - } else { - let index = if let Some(index) = index { - scope.len() - index.get() - } else { - scope - .get_index(name) - .ok_or_else(|| Box::new(EvalAltResult::ErrorVariableNotFound(name.into(), pos)))? - .0 - }; - - Ok(scope.get_mut(index)) + return Ok(( + module.get_qualified_var_mut(name, modules.as_ref(), pos)?, + // Module variables are constant + ScopeEntryType::Constant, + )); + } } + + let index = if let Some(index) = index { + scope.len() - index.get() + } else { + scope + .get_index(name) + .ok_or_else(|| Box::new(EvalAltResult::ErrorVariableNotFound(name.into(), pos)))? + .0 + }; + + Ok(scope.get_mut(index)) } impl Engine { @@ -438,7 +447,10 @@ impl Engine { packages: Default::default(), functions: HashMap::with_capacity(FUNCTIONS_COUNT / 2), type_iterators: Default::default(), + + #[cfg(not(feature = "no_module"))] module_resolver: None, + type_names: Default::default(), print: Box::new(|_| {}), debug: Box::new(|_| {}), @@ -721,7 +733,7 @@ impl Engine { } 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 self.eval_ast_with_scope_raw(scope, &ast) @@ -779,7 +791,7 @@ impl Engine { // xxx.fn_name(arg_expr_list) Expr::FnCall(fn_name, None,_, def_val, pos) => { let mut args: Vec<_> = once(obj) - .chain(idx_val.downcast_mut::().unwrap().iter_mut()) + .chain(idx_val.downcast_mut::>().unwrap().iter_mut()) .collect(); let def_val = def_val.as_deref(); // 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 Expr::FnCall(_,_,_,_,_) => unreachable!(), // {xxx:map}.id = ??? + #[cfg(not(feature = "no_object"))] Expr::Property(id, pos) if obj.is::() && new_val.is_some() => { let mut indexed_val = 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)) } // {xxx:map}.id + #[cfg(not(feature = "no_object"))] Expr::Property(id, pos) if obj.is::() => { let indexed_val = 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]; 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] Expr::Index(dot_lhs, dot_rhs, pos) | // {xxx:map}.dot_lhs.rhs @@ -959,7 +974,10 @@ impl Engine { .map(|arg_expr| self.eval_expr(scope, state, fn_lib, arg_expr, level)) .collect::, _>>()?; - 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::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()); match val { + #[cfg(not(feature = "no_index"))] Dynamic(Union::Array(arr)) => { // val_array[idx] let index = idx @@ -1015,6 +1034,7 @@ impl Engine { } } + #[cfg(not(feature = "no_object"))] Dynamic(Union::Map(map)) => { // val_map[idx] let index = idx @@ -1030,6 +1050,7 @@ impl Engine { }) } + #[cfg(not(feature = "no_index"))] Dynamic(Union::Str(s)) => { // val_string[idx] let index = idx @@ -1088,6 +1109,7 @@ impl Engine { let rhs_value = self.eval_expr(scope, state, fn_lib, rhs, level)?; match rhs_value { + #[cfg(not(feature = "no_index"))] Dynamic(Union::Array(mut rhs_value)) => { let def_value = false.into(); @@ -1108,6 +1130,7 @@ impl Engine { Ok(false.into()) } + #[cfg(not(feature = "no_object"))] Dynamic(Union::Map(rhs_value)) => match lhs_value { // Only allows String or char Dynamic(Union::Str(s)) => Ok(rhs_value.contains_key(s.as_ref()).into()), @@ -1230,7 +1253,8 @@ impl Engine { .collect::, _>>()?, )))), - 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 .iter() .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(); - if let Some(modules) = modules { - // 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 + if fn_name.as_ref() == KEYWORD_EVAL && args.len() == 1 && !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::, _>>()?; + + 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, _) => { self.eval_in_expr(scope, state, fn_lib, lhs.as_ref(), rhs.as_ref(), level) } @@ -1519,25 +1551,28 @@ impl Engine { #[cfg(feature = "no_module")] unreachable!(); - if let Some(path) = self - .eval_expr(scope, state, fn_lib, expr, level)? - .try_cast::() + #[cfg(not(feature = "no_module"))] { - if let Some(resolver) = self.module_resolver.as_ref() { - let module = resolver.resolve(self, &path, expr.position())?; + if let Some(path) = self + .eval_expr(scope, state, fn_lib, expr, level)? + .try_cast::() + { + if let Some(resolver) = self.module_resolver.as_ref() { + let module = resolver.resolve(self, &path, expr.position())?; - // TODO - avoid copying module name in inner block? - let mod_name = name.as_ref().clone(); - scope.push_module(mod_name, module); - Ok(Default::default()) + // TODO - avoid copying module name in inner block? + let mod_name = name.as_ref().clone(); + scope.push_module(mod_name, module); + Ok(Default::default()) + } else { + Err(Box::new(EvalAltResult::ErrorModuleNotFound( + path, + expr.position(), + ))) + } } else { - Err(Box::new(EvalAltResult::ErrorModuleNotFound( - path, - expr.position(), - ))) + Err(Box::new(EvalAltResult::ErrorImportExpr(expr.position()))) } - } else { - Err(Box::new(EvalAltResult::ErrorImportExpr(expr.position()))) } } } diff --git a/src/module.rs b/src/module.rs index 0c329f27..bdb545b1 100644 --- a/src/module.rs +++ b/src/module.rs @@ -1,9 +1,10 @@ //! Module defining external-loaded modules for Rhai. +#![cfg(not(feature = "no_module"))] use crate::any::{Dynamic, Variant}; use crate::calc_fn_hash; use crate::engine::{Engine, FnAny, FnCallArgs, FunctionsLib}; -use crate::parser::FnDef; +use crate::parser::{FnDef, AST}; use crate::result::EvalAltResult; use crate::scope::{Entry as ScopeEntry, EntryType as ScopeEntryType, Scope}; use crate::token::Position; @@ -12,11 +13,12 @@ use crate::utils::StaticVec; use crate::stdlib::{ any::TypeId, + boxed::Box, collections::HashMap, fmt, mem, ops::{Deref, DerefMut}, rc::Rc, - string::String, + string::{String, ToString}, sync::Arc, }; @@ -70,21 +72,61 @@ impl fmt::Debug for Module { impl 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::("answer").unwrap(), 42); + /// ``` pub fn new() -> Self { Default::default() } /// 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 { self.variables.contains_key(name) } /// 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::("answer").unwrap(), 42); + /// ``` pub fn get_var_value(&self, name: &str) -> Option { self.get_var(name).and_then(|v| v.try_cast::()) } - /// 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::(), 42); + /// ``` pub fn get_var(&self, name: &str) -> Option { self.variables.get(name).cloned() } @@ -97,6 +139,16 @@ impl Module { /// Set a variable into the module. /// /// 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::("answer").unwrap(), 42); + /// ``` pub fn set_var, T: Into>(&mut self, name: K, value: T) { self.variables.insert(name.into(), value.into()); } @@ -115,16 +167,49 @@ impl 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 { self.modules.contains_key(name) } /// 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> { self.modules.get(name) } /// 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> { self.modules.get_mut(name) } @@ -132,6 +217,17 @@ impl Module { /// Set a sub-module into the module. /// /// 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>(&mut self, name: K, sub_module: Module) { 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`. /// 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 { 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. /// /// 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>( &mut self, fn_name: &str, @@ -199,6 +315,16 @@ impl Module { /// 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. + /// + /// # 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>( &mut self, fn_name: &str, @@ -217,6 +343,16 @@ impl Module { /// 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. + /// + /// # 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>( &mut self, fn_name: &str, @@ -235,6 +371,18 @@ impl Module { /// 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. + /// + /// # 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>( &mut self, fn_name: &str, @@ -256,7 +404,17 @@ impl Module { /// Set a Rust function taking two parameters (the first one mutable) into the module, /// 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>( &mut self, fn_name: &str, @@ -278,6 +436,18 @@ impl Module { /// 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. + /// + /// # 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< A: Variant + Clone, B: Variant + Clone, @@ -306,6 +476,18 @@ impl Module { /// 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_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< A: Variant + Clone, B: Variant + Clone, @@ -334,6 +516,16 @@ impl Module { /// /// The `u64` hash is calculated by the function `crate::calc_fn_hash`. /// 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> { 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 { &self.fn_lib } @@ -383,10 +584,61 @@ impl Module { .fn_lib .get_function(name, args)) } + + /// Create a new `Module` by evaluating an `AST`. + /// + /// # Examples + /// + /// ``` + /// # fn main() -> Result<(), Box> { + /// use rhai::{Engine, Module}; + /// + /// let engine = Engine::new(); + /// let ast = engine.compile("let answer = 42;")?; + /// let module = Module::eval_ast_as_new(&ast, &engine)?; + /// assert!(module.contains_var("answer")); + /// assert_eq!(module.get_var_value::("answer").unwrap(), 42); + /// # Ok(()) + /// # } + /// ``` + pub fn eval_ast_as_new(ast: &AST, engine: &Engine) -> FuncReturn { + // Use new scope + let mut scope = Scope::new(); + + // Run the script + engine.eval_ast_with_scope_raw(&mut scope, &ast)?; + + // Create new module + let mut module = Module::new(); + + scope.into_iter().for_each( + |ScopeEntry { + name, typ, value, .. + }| { + match typ { + // Variables left in the scope become module variables + ScopeEntryType::Normal | ScopeEntryType::Constant => { + module.variables.insert(name.into_owned(), value); + } + // Modules left in the scope become sub-modules + ScopeEntryType::Module => { + module + .modules + .insert(name.into_owned(), value.cast::()); + } + } + }, + ); + + module.fn_lib = module.fn_lib.merge(ast.fn_lib()); + + Ok(module) + } } /// Re-export module resolvers. pub mod resolvers { + #[cfg(not(feature = "no_std"))] pub use super::file::FileModuleResolver; pub use super::stat::StaticModuleResolver; } @@ -403,6 +655,20 @@ mod file { /// 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, @@ -411,16 +677,66 @@ mod file { 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()) + /// + /// # 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>(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`. - pub fn new_with_path_and_extension(path: PathBuf, extension: String) -> Self { - Self { path, extension } + /// + /// # 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, E: Into>( + 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() } @@ -443,39 +759,8 @@ mod file { .compile_file(file_path) .map_err(|err| EvalAltResult::set_position(err, pos))?; - // Use new scope - let mut scope = Scope::new(); - - // Run the script - engine - .eval_ast_with_scope_raw(&mut scope, &ast) - .map_err(|err| EvalAltResult::set_position(err, pos))?; - - // Create new module - let mut module = Module::new(); - - scope.into_iter().for_each( - |ScopeEntry { - name, typ, value, .. - }| { - match typ { - // Variables left in the scope become module variables - ScopeEntryType::Normal | ScopeEntryType::Constant => { - module.variables.insert(name.into_owned(), value); - } - // Modules left in the scope become sub-modules - ScopeEntryType::Module => { - module - .modules - .insert(name.into_owned(), value.cast::()); - } - } - }, - ); - - module.fn_lib = module.fn_lib.merge(ast.fn_lib()); - - Ok(module) + Module::eval_ast_as_new(&ast, engine) + .map_err(|err| EvalAltResult::set_position(err, pos)) } } } @@ -485,11 +770,41 @@ mod stat { use super::*; /// 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)] pub struct StaticModuleResolver(HashMap); impl 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 { Default::default() } diff --git a/src/optimize.rs b/src/optimize.rs index a2dc0048..33765a56 100644 --- a/src/optimize.rs +++ b/src/optimize.rs @@ -711,11 +711,16 @@ pub fn optimize_into_ast( #[cfg(feature = "no_optimize")] const level: OptimizationLevel = OptimizationLevel::None; + #[cfg(not(feature = "no_function"))] let fn_lib: Vec<_> = functions .iter() .map(|fn_def| (fn_def.name.as_str(), fn_def.params.len())) .collect(); + #[cfg(feature = "no_function")] + const fn_lib: &[(&str, usize)] = &[]; + + #[cfg(not(feature = "no_function"))] let lib = FunctionsLib::from_vec( functions .iter() @@ -745,6 +750,9 @@ pub fn optimize_into_ast( .collect(), ); + #[cfg(feature = "no_function")] + let lib: FunctionsLib = Default::default(); + AST::new( match level { OptimizationLevel::None => statements, diff --git a/src/packages/array_basic.rs b/src/packages/array_basic.rs index ca69f3fc..dfb80422 100644 --- a/src/packages/array_basic.rs +++ b/src/packages/array_basic.rs @@ -1,3 +1,5 @@ +#![cfg(not(feature = "no_index"))] + use super::{reg_binary, reg_binary_mut, reg_trinary_mut, reg_unary_mut}; use crate::any::{Dynamic, Variant}; diff --git a/src/packages/map_basic.rs b/src/packages/map_basic.rs index 40e2cec0..451897bf 100644 --- a/src/packages/map_basic.rs +++ b/src/packages/map_basic.rs @@ -1,3 +1,5 @@ +#![cfg(not(feature = "no_object"))] + use super::{reg_binary, reg_binary_mut, reg_unary_mut}; use crate::any::Dynamic; diff --git a/src/packages/string_basic.rs b/src/packages/string_basic.rs index 87807d70..e58890d1 100644 --- a/src/packages/string_basic.rs +++ b/src/packages/string_basic.rs @@ -1,10 +1,16 @@ use super::{reg_binary, reg_binary_mut, reg_none, reg_unary, reg_unary_mut}; 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::parser::INT; +#[cfg(not(feature = "no_index"))] +use crate::engine::Array; + +#[cfg(not(feature = "no_object"))] +use crate::engine::Map; + use crate::stdlib::{ fmt::{Debug, Display}, format, @@ -18,6 +24,7 @@ fn to_debug(x: &mut T) -> String { fn to_string(x: &mut T) -> String { format!("{}", x) } +#[cfg(not(feature = "no_object"))] fn format_map(x: &mut Map) -> String { format!("#{:?}", x) } diff --git a/src/packages/string_more.rs b/src/packages/string_more.rs index 0144792e..2badac23 100644 --- a/src/packages/string_more.rs +++ b/src/packages/string_more.rs @@ -1,10 +1,12 @@ use super::{reg_binary, reg_binary_mut, reg_trinary_mut, reg_unary_mut}; use crate::def_package; -use crate::engine::Array; use crate::fn_register::map_dynamic as map; use crate::parser::INT; +#[cfg(not(feature = "no_index"))] +use crate::engine::Array; + use crate::stdlib::{ fmt::Display, format, diff --git a/src/parser.rs b/src/parser.rs index b1e91eb0..550feeb0 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -39,6 +39,7 @@ pub type INT = i32; /// The system floating-point type. /// /// Not available under the `no_float` feature. +#[cfg(not(feature = "no_float"))] pub type FLOAT = f64; type PERR = ParseErrorType; @@ -143,13 +144,11 @@ impl AST { (true, true) => vec![], }; - #[cfg(feature = "sync")] - 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()))); + Self::new(ast, functions.merge(other.1.as_ref())) } /// Clear all function definitions in the `AST`. + #[cfg(not(feature = "no_function"))] pub fn clear_functions(&mut self) { #[cfg(feature = "sync")] { @@ -162,6 +161,7 @@ impl AST { } /// Clear all statements in the `AST`, leaving only function definitions. + #[cfg(not(feature = "no_function"))] pub fn retain_functions(&mut self) { self.0 = vec![]; } @@ -351,6 +351,7 @@ pub enum Expr { /// Integer constant. IntegerConstant(INT, Position), /// Floating-point constant. + #[cfg(not(feature = "no_float"))] FloatConstant(FLOAT, Position), /// Character constant. CharConstant(char, Position), @@ -413,6 +414,7 @@ impl Expr { Self::False(_) => false.into(), Self::Unit(_) => ().into(), + #[cfg(not(feature = "no_index"))] Self::Array(items, _) if items.iter().all(Self::is_constant) => { Dynamic(Union::Array(Box::new( items @@ -422,6 +424,7 @@ impl Expr { ))) } + #[cfg(not(feature = "no_object"))] Self::Map(items, _) if items.iter().all(|(_, v, _)| v.is_constant()) => { Dynamic(Union::Map(Box::new( items @@ -442,8 +445,10 @@ impl Expr { /// Panics when the expression is not constant. pub fn get_constant_str(&self) -> String { match self { - Self::IntegerConstant(i, _) => i.to_string(), + #[cfg(not(feature = "no_float"))] Self::FloatConstant(f, _) => f.to_string(), + + Self::IntegerConstant(i, _) => i.to_string(), Self::CharConstant(c, _) => c.to_string(), Self::StringConstant(_, _) => "string".to_string(), Self::True(_) => "true".to_string(), @@ -459,8 +464,10 @@ impl Expr { /// Get the `Position` of the expression. pub fn position(&self) -> Position { match self { + #[cfg(not(feature = "no_float"))] + Self::FloatConstant(_, pos) => *pos, + Self::IntegerConstant(_, pos) - | Self::FloatConstant(_, pos) | Self::CharConstant(_, pos) | Self::StringConstant(_, pos) | Self::Array(_, pos) @@ -485,8 +492,10 @@ impl Expr { /// Get the `Position` of the expression. pub(crate) fn set_position(mut self, new_pos: Position) -> Self { match &mut self { + #[cfg(not(feature = "no_float"))] + Self::FloatConstant(_, pos) => *pos = new_pos, + Self::IntegerConstant(_, pos) - | Self::FloatConstant(_, pos) | Self::CharConstant(_, pos) | Self::StringConstant(_, pos) | Self::Array(_, pos) @@ -531,8 +540,10 @@ impl Expr { /// Is the expression a constant? pub fn is_constant(&self) -> bool { match self { + #[cfg(not(feature = "no_float"))] + Self::FloatConstant(_, _) => true, + Self::IntegerConstant(_, _) - | Self::FloatConstant(_, _) | Self::CharConstant(_, _) | Self::StringConstant(_, _) | Self::True(_) @@ -559,8 +570,10 @@ impl Expr { /// Is a particular token allowed as a postfix operator to this expression? pub fn is_valid_postfix(&self, token: &Token) -> bool { match self { + #[cfg(not(feature = "no_float"))] + Self::FloatConstant(_, _) => false, + Self::IntegerConstant(_, _) - | Self::FloatConstant(_, _) | Self::CharConstant(_, _) | Self::In(_, _, _) | Self::And(_, _, _) @@ -764,8 +777,15 @@ fn parse_index_chain<'a>( .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::And(_, _, pos) | Expr::Or(_, _, pos) @@ -792,8 +812,16 @@ fn parse_index_chain<'a>( ) .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::And(_, _, pos) | Expr::Or(_, _, pos) @@ -811,6 +839,7 @@ fn parse_index_chain<'a>( }, // lhs[float] + #[cfg(not(feature = "no_float"))] Expr::FloatConstant(_, pos) => { return Err(PERR::MalformedIndexExpr( "Array access expects integer index, not a float".into(), @@ -1095,6 +1124,7 @@ fn parse_primary<'a>( } } // Indexing + #[cfg(not(feature = "no_index"))] (expr, Token::LeftBracket) => { 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> { match (&lhs, &rhs) { (_, Expr::IntegerConstant(_, pos)) - | (_, Expr::FloatConstant(_, pos)) | (_, Expr::And(_, _, pos)) | (_, Expr::Or(_, _, pos)) | (_, Expr::In(_, _, pos)) @@ -1294,11 +1323,20 @@ fn make_in_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result { + return Err(PERR::MalformedInExpr( + "'in' expression expects a string, array or object map".into(), + ) + .into_err(*pos)) + } + // "xxx" in "xxxx", 'x' in "xxxx" - OK! (Expr::StringConstant(_, _), Expr::StringConstant(_, _)) | (Expr::CharConstant(_, _), Expr::StringConstant(_, _)) => (), // 123.456 in "xxxx" + #[cfg(not(feature = "no_float"))] (Expr::FloatConstant(_, pos), Expr::StringConstant(_, _)) => { return Err(PERR::MalformedInExpr( "'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 (), // 123.456 in #{...} + #[cfg(not(feature = "no_float"))] (Expr::FloatConstant(_, pos), Expr::Map(_, _)) => { return Err(PERR::MalformedInExpr( "'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::Const => parse_let(input, stack, ScopeEntryType::Constant, allow_stmt_expr), + #[cfg(not(feature = "no_module"))] Token::Import => parse_import(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() { // Collect all the function definitions - if let (Token::Fn, _) = input.peek().unwrap() { - let mut stack = Stack::new(); - let f = parse_fn(input, &mut stack, true)?; - functions.insert(calc_fn_def(&f.name, f.params.len()), f); - continue; + #[cfg(not(feature = "no_function"))] + { + if let (Token::Fn, _) = input.peek().unwrap() { + let mut stack = Stack::new(); + let f = parse_fn(input, &mut stack, true)?; + functions.insert(calc_fn_def(&f.name, f.params.len()), f); + continue; + } } - // Actual statement let stmt = parse_stmt(input, &mut stack, false, true)?; diff --git a/src/scope.rs b/src/scope.rs index 94035dbb..2411dcc1 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -1,10 +1,12 @@ //! Module that defines the `Scope` type representing a function call-stack scope. use crate::any::{Dynamic, Union, Variant}; -use crate::module::Module; use crate::parser::{map_dynamic_to_expr, Expr}; use crate::token::Position; +#[cfg(not(feature = "no_module"))] +use crate::module::Module; + use crate::stdlib::{borrow::Cow, boxed::Box, iter, vec, vec::Vec}; /// Type of an entry in the Scope. @@ -172,6 +174,7 @@ impl<'a> Scope<'a> { /// Add (push) a new module to the Scope. /// /// Modules are used for accessing member variables, functions and plugins under a namespace. + #[cfg(not(feature = "no_module"))] pub fn push_module>>(&mut self, name: K, value: Module) { self.push_dynamic_value( name, @@ -340,6 +343,7 @@ impl<'a> Scope<'a> { } /// 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> { let index = self.get_module_index(name)?; self.get_mut(index).0.downcast_mut::()