diff --git a/Cargo.toml b/Cargo.toml index 3148eb3e..2afe38b0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,7 +28,7 @@ no_float = [] # no floating-point no_function = [] # no script-defined functions no_object = [] # no custom objects no_optimize = [] # no script optimizer -no_import = [] # no modules +no_module = [] # no modules only_i32 = [] # set INT=i32 (useful for 32-bit systems) only_i64 = [] # set INT=i64 (default) and disable support for all other integer types sync = [] # restrict to only types that implement Send + Sync diff --git a/README.md b/README.md index 7122feb6..bde4bbcd 100644 --- a/README.md +++ b/README.md @@ -70,7 +70,7 @@ Optional features | `no_object` | Disable support for custom types and objects. | | `no_float` | Disable floating-point numbers and math if not needed. | | `no_optimize` | Disable the script optimizer. | -| `no_import` | Disable modules. | +| `no_module` | Disable modules. | | `only_i32` | Set the system integer type to `i32` and disable all other integer types. `INT` is set to `i32`. | | `only_i64` | Set the system integer type to `i64` and disable all other integer types. `INT` is set to `i64`. | | `no_std` | Build for `no-std`. Notice that additional dependencies will be pulled in to replace `std` features. | @@ -86,7 +86,7 @@ Excluding unneeded functionalities can result in smaller, faster builds as well [`no_function`]: #optional-features [`no_object`]: #optional-features [`no_optimize`]: #optional-features -[`no_import`]: #optional-features +[`no_module`]: #optional-features [`only_i32`]: #optional-features [`only_i64`]: #optional-features [`no_std`]: #optional-features diff --git a/src/any.rs b/src/any.rs index e07d584e..aee3da60 100644 --- a/src/any.rs +++ b/src/any.rs @@ -1,6 +1,6 @@ //! Helper module which defines the `Any` trait to to allow dynamic value handling. -use crate::engine::{Array, Map, SubScope}; +use crate::engine::{Array, Map, Module}; use crate::parser::INT; #[cfg(not(feature = "no_float"))] @@ -135,7 +135,7 @@ pub enum Union { Float(FLOAT), Array(Box), Map(Box), - SubScope(Box), + Module(Box), Variant(Box>), } @@ -166,7 +166,7 @@ impl Dynamic { Union::Float(_) => TypeId::of::(), Union::Array(_) => TypeId::of::(), Union::Map(_) => TypeId::of::(), - Union::SubScope(_) => TypeId::of::(), + Union::Module(_) => TypeId::of::(), Union::Variant(value) => (***value).type_id(), } } @@ -183,7 +183,7 @@ impl Dynamic { Union::Float(_) => type_name::(), Union::Array(_) => "array", Union::Map(_) => "map", - Union::SubScope(_) => "sub-scope", + Union::Module(_) => "sub-scope", #[cfg(not(feature = "no_std"))] Union::Variant(value) if value.is::() => "timestamp", @@ -204,7 +204,7 @@ impl fmt::Display for Dynamic { Union::Float(value) => write!(f, "{}", value), Union::Array(value) => write!(f, "{:?}", value), Union::Map(value) => write!(f, "#{:?}", value), - Union::SubScope(value) => write!(f, "#{:?}", value), + Union::Module(value) => write!(f, "#{:?}", value), Union::Variant(_) => write!(f, "?"), } } @@ -222,7 +222,7 @@ impl fmt::Debug for Dynamic { Union::Float(value) => write!(f, "{:?}", value), Union::Array(value) => write!(f, "{:?}", value), Union::Map(value) => write!(f, "#{:?}", value), - Union::SubScope(value) => write!(f, "#{:?}", value), + Union::Module(value) => write!(f, "#{:?}", value), Union::Variant(_) => write!(f, ""), } } @@ -240,7 +240,7 @@ impl Clone for Dynamic { Union::Float(value) => Self(Union::Float(value)), Union::Array(ref value) => Self(Union::Array(value.clone())), Union::Map(ref value) => Self(Union::Map(value.clone())), - Union::SubScope(ref value) => Self(Union::SubScope(value.clone())), + Union::Module(ref value) => Self(Union::Module(value.clone())), Union::Variant(ref value) => (***value).clone_into_dynamic(), } } @@ -369,7 +369,7 @@ impl Dynamic { 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::SubScope(value) => cast_box::<_, T>(value).ok(), + Union::Module(value) => cast_box::<_, T>(value).ok(), Union::Variant(value) => value.as_any().downcast_ref::().cloned(), } } @@ -407,7 +407,7 @@ impl Dynamic { 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::SubScope(value) => cast_box::<_, T>(value).unwrap(), + Union::Module(value) => cast_box::<_, T>(value).unwrap(), Union::Variant(value) => value.as_any().downcast_ref::().unwrap().clone(), } } @@ -430,7 +430,7 @@ impl Dynamic { Union::Float(value) => (value as &dyn Any).downcast_ref::(), Union::Array(value) => (value.as_ref() as &dyn Any).downcast_ref::(), Union::Map(value) => (value.as_ref() as &dyn Any).downcast_ref::(), - Union::SubScope(value) => (value.as_ref() as &dyn Any).downcast_ref::(), + Union::Module(value) => (value.as_ref() as &dyn Any).downcast_ref::(), Union::Variant(value) => value.as_ref().as_ref().as_any().downcast_ref::(), } } @@ -453,7 +453,7 @@ impl Dynamic { Union::Float(value) => (value as &mut dyn Any).downcast_mut::(), Union::Array(value) => (value.as_mut() as &mut dyn Any).downcast_mut::(), Union::Map(value) => (value.as_mut() as &mut dyn Any).downcast_mut::(), - Union::SubScope(value) => (value.as_mut() as &mut dyn Any).downcast_mut::(), + Union::Module(value) => (value.as_mut() as &mut dyn Any).downcast_mut::(), Union::Variant(value) => value.as_mut().as_mut_any().downcast_mut::(), } } diff --git a/src/engine.rs b/src/engine.rs index 884e757b..cdcdbc72 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -5,7 +5,7 @@ use crate::calc_fn_hash; use crate::error::ParseErrorType; use crate::optimize::OptimizationLevel; use crate::packages::{CorePackage, Package, PackageLibrary, StandardPackage}; -use crate::parser::{Expr, FnDef, ReturnType, Stmt}; +use crate::parser::{Expr, FnDef, ModuleRef, ReturnType, Stmt}; use crate::result::EvalAltResult; use crate::scope::{EntryType as ScopeEntryType, Scope}; use crate::token::Position; @@ -42,27 +42,27 @@ pub type Array = Vec; /// Not available under the `no_object` feature. pub type Map = HashMap; -/// A sub-scope - basically an imported module namespace. +/// An imported module. /// -/// Not available under the `no_import` feature. +/// Not available under the `no_module` feature. #[derive(Debug, Clone)] -pub struct SubScope(HashMap); +pub struct Module(HashMap); -impl SubScope { - /// Create a new sub-scope. +impl Module { + /// Create a new module. pub fn new() -> Self { Self(HashMap::new()) } } -impl Deref for SubScope { +impl Deref for Module { type Target = HashMap; fn deref(&self) -> &Self::Target { &self.0 } } -impl DerefMut for SubScope { +impl DerefMut for Module { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } @@ -500,67 +500,61 @@ fn default_print(s: &str) { } /// Search for a variable within the scope -fn search_scope_variables<'a>( +fn search_scope<'a>( scope: &'a mut Scope, name: &str, + modules: &Option>>, index: Option, pos: Position, ) -> Result<(&'a mut Dynamic, ScopeEntryType), Box> { - let index = if let Some(index) = index { - scope.len() - index.get() + if let Some(modules) = modules { + let (id, root_pos) = modules.get(0); // First module + + let mut 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)))? + }; + + for x in 1..modules.len() { + let (id, id_pos) = modules.get(x); + + module = module + .get_mut(id) + .and_then(|v| v.downcast_mut::()) + .ok_or_else(|| Box::new(EvalAltResult::ErrorModuleNotFound(id.into(), *id_pos)))?; + } + + let result = module + .get_mut(name) + .map(|v| (v, ScopeEntryType::Constant)) + .ok_or_else(|| Box::new(EvalAltResult::ErrorVariableNotFound(name.into(), pos)))?; + + if result.0.is::() { + Err(Box::new(EvalAltResult::ErrorVariableNotFound( + name.into(), + pos, + ))) + } else { + Ok(result) + } } else { - scope - .get_index(name) - .ok_or_else(|| Box::new(EvalAltResult::ErrorVariableNotFound(name.into(), pos)))? - .0 - }; + 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)) -} - -/// Search for a sub-scope within the scope -fn search_scope_modules<'a>( - scope: &'a mut Scope, - name: &str, - modules: &Box>, - index: Option, - pos: Position, -) -> Result<(&'a mut Dynamic, ScopeEntryType), Box> { - let (id, root_pos) = modules.get(0); // First module - - let mut sub_scope = if let Some(index) = index { - scope - .get_mut(scope.len() - index.get()) - .0 - .downcast_mut::() - .unwrap() - } else { - scope - .find_sub_scope(id) - .ok_or_else(|| Box::new(EvalAltResult::ErrorModuleNotFound(id.into(), *root_pos)))? - }; - - for x in 1..modules.len() { - let (id, id_pos) = modules.get(x); - - sub_scope = sub_scope - .get_mut(id) - .and_then(|v| v.downcast_mut::()) - .ok_or_else(|| Box::new(EvalAltResult::ErrorModuleNotFound(id.into(), *id_pos)))?; - } - - let result = sub_scope - .get_mut(name) - .map(|v| (v, ScopeEntryType::Constant)) - .ok_or_else(|| Box::new(EvalAltResult::ErrorVariableNotFound(name.into(), pos)))?; - - if result.0.is::() { - Err(Box::new(EvalAltResult::ErrorVariableNotFound( - name.into(), - pos, - ))) - } else { - Ok(result) + Ok(scope.get_mut(index)) } } @@ -620,11 +614,13 @@ impl Engine { } /// Universal method for calling functions either registered with the `Engine` or written in Rhai + // TODO - handle moduled function call pub(crate) fn call_fn_raw( &self, scope: Option<&mut Scope>, fn_lib: &FunctionsLib, fn_name: &str, + modules: &ModuleRef, args: &mut FnCallArgs, def_val: Option<&Dynamic>, pos: Position, @@ -794,6 +790,7 @@ impl Engine { &self, fn_lib: &FunctionsLib, fn_name: &str, + modules: &ModuleRef, args: &mut FnCallArgs, def_val: Option<&Dynamic>, pos: Position, @@ -801,12 +798,20 @@ impl Engine { ) -> Result> { match fn_name { // type_of - KEYWORD_TYPE_OF if args.len() == 1 && !self.has_override(fn_lib, KEYWORD_TYPE_OF) => { + KEYWORD_TYPE_OF + if modules.is_none() + && args.len() == 1 + && !self.has_override(fn_lib, KEYWORD_TYPE_OF) => + { Ok(self.map_type_name(args[0].type_name()).to_string().into()) } // eval - reaching this point it must be a method-style call - KEYWORD_EVAL if args.len() == 1 && !self.has_override(fn_lib, KEYWORD_EVAL) => { + KEYWORD_EVAL + if modules.is_none() + && args.len() == 1 + && !self.has_override(fn_lib, KEYWORD_EVAL) => + { Err(Box::new(EvalAltResult::ErrorRuntime( "'eval' should not be called in method style. Try eval(...);".into(), pos, @@ -814,7 +819,7 @@ impl Engine { } // Normal method call - _ => self.call_fn_raw(None, fn_lib, fn_name, args, def_val, pos, level), + _ => self.call_fn_raw(None, fn_lib, fn_name, modules, args, def_val, pos, level), } } @@ -915,7 +920,7 @@ impl Engine { let def_val = def_val.as_deref(); // A function call is assumed to have side effects, so the value is changed // TODO - Remove assumption of side effects by checking whether the first parameter is &mut - self.exec_fn_call(fn_lib, fn_name, &mut args, def_val, *pos, 0).map(|v| (v, true)) + self.exec_fn_call(fn_lib, fn_name, &None, &mut args, def_val, *pos, 0).map(|v| (v, true)) } // xxx.module::fn_name(...) - syntax error Expr::FnCall(_,_,_,_,_) => unreachable!(), @@ -936,13 +941,13 @@ impl Engine { Expr::Property(id, pos) if new_val.is_some() => { let fn_name = make_setter(id); let mut args = [obj, new_val.as_mut().unwrap()]; - self.exec_fn_call(fn_lib, &fn_name, &mut args, None, *pos, 0).map(|v| (v, true)) + self.exec_fn_call(fn_lib, &fn_name, &None, &mut args, None, *pos, 0).map(|v| (v, true)) } // xxx.id Expr::Property(id, pos) => { let fn_name = make_getter(id); 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, &None, &mut args, None, *pos, 0).map(|v| (v, false)) } // {xxx:map}.idx_lhs[idx_expr] Expr::Index(dot_lhs, dot_rhs, pos) | @@ -972,7 +977,7 @@ impl Engine { let indexed_val = &mut (if let Expr::Property(id, pos) = dot_lhs.as_ref() { let fn_name = make_getter(id); - self.exec_fn_call(fn_lib, &fn_name, &mut args[..1], None, *pos, 0)? + self.exec_fn_call(fn_lib, &fn_name, &None, &mut args[..1], None, *pos, 0)? } else { // Syntax error return Err(Box::new(EvalAltResult::ErrorDotExpr( @@ -990,7 +995,7 @@ impl Engine { let fn_name = make_setter(id); // Re-use args because the first &mut parameter will not be consumed args[1] = indexed_val; - self.exec_fn_call(fn_lib, &fn_name, &mut args, None, *pos, 0).or_else(|err| match *err { + self.exec_fn_call(fn_lib, &fn_name, &None, &mut args, None, *pos, 0).or_else(|err| match *err { // If there is no setter, no need to feed it back because the property is read-only EvalAltResult::ErrorDotExpr(_,_) => Ok(Default::default()), err => Err(Box::new(err)) @@ -1030,16 +1035,11 @@ impl Engine { // id.??? or id[???] Expr::Variable(id, modules, index, pos) => { let index = if state.always_search { None } else { *index }; - - let (target, typ) = if let Some(modules) = modules { - search_scope_modules(scope, id, modules, index, *pos)? - } else { - search_scope_variables(scope, id, index, *pos)? - }; + let (target, typ) = search_scope(scope, id, modules, index, *pos)?; // Constants cannot be modified match typ { - ScopeEntryType::SubScope => unreachable!(), + ScopeEntryType::Module => unreachable!(), ScopeEntryType::Constant if new_val.is_some() => { return Err(Box::new(EvalAltResult::ErrorAssignmentToConstant( id.to_string(), @@ -1223,9 +1223,10 @@ impl Engine { for value in rhs_value.iter_mut() { let args = &mut [&mut lhs_value, value]; let def_value = Some(&def_value); + let pos = rhs.position(); if self - .call_fn_raw(None, fn_lib, "==", args, def_value, rhs.position(), level)? + .call_fn_raw(None, fn_lib, "==", &None, args, def_value, pos, level)? .as_bool() .unwrap_or(false) { @@ -1268,13 +1269,7 @@ impl Engine { Expr::CharConstant(c, _) => Ok((*c).into()), Expr::Variable(id, modules, index, pos) => { let index = if state.always_search { None } else { *index }; - - let val = if let Some(modules) = modules { - search_scope_modules(scope, id, modules, index, *pos)? - } else { - search_scope_variables(scope, id, index, *pos)? - }; - + let val = search_scope(scope, id, modules, index, *pos)?; Ok(val.0.clone()) } Expr::Property(_, _) => unreachable!(), @@ -1290,13 +1285,7 @@ impl Engine { // name = rhs Expr::Variable(name, modules, index, pos) => { let index = if state.always_search { None } else { *index }; - let val = if let Some(modules) = modules { - search_scope_modules(scope, name, modules, index, *pos)? - } else { - search_scope_variables(scope, name, index, *pos)? - }; - - match val { + match search_scope(scope, name, modules, index, *pos)? { (_, ScopeEntryType::Constant) => Err(Box::new( EvalAltResult::ErrorAssignmentToConstant(name.to_string(), *op_pos), )), @@ -1304,8 +1293,8 @@ impl Engine { *value_ptr = rhs_val; Ok(Default::default()) } - // End variable cannot be a sub-scope - (_, ScopeEntryType::SubScope) => unreachable!(), + // End variable cannot be a module + (_, ScopeEntryType::Module) => unreachable!(), } } // idx_lhs[idx_expr] = rhs @@ -1369,7 +1358,6 @@ impl Engine { .collect::, _>>()?, )))), - // TODO - handle moduled function call Expr::FnCall(fn_name, modules, arg_exprs, def_val, pos) => { let mut arg_values = arg_exprs .iter() @@ -1380,6 +1368,7 @@ impl Engine { // eval - only in function call style if fn_name.as_ref() == KEYWORD_EVAL + && modules.is_none() && args.len() == 1 && !self.has_override(fn_lib, KEYWORD_EVAL) { @@ -1399,7 +1388,8 @@ impl Engine { } // Normal function call - except for eval (handled above) - self.exec_fn_call(fn_lib, fn_name, &mut args, def_val.as_deref(), *pos, level) + let def_value = def_val.as_deref(); + self.exec_fn_call(fn_lib, fn_name, modules, &mut args, def_value, *pos, level) } Expr::In(lhs, rhs, _) => { @@ -1634,13 +1624,13 @@ impl Engine { .eval_expr(scope, state, fn_lib, expr, level)? .try_cast::() { - let mut module = SubScope::new(); + let mut module = Module::new(); module.insert("kitty".to_string(), "foo".to_string().into()); module.insert("path".to_string(), path.into()); // TODO - avoid copying module name in inner block? let mod_name = name.as_ref().clone(); - scope.push_sub_scope(mod_name, module); + scope.push_module(mod_name, module); Ok(Default::default()) } else { Err(Box::new(EvalAltResult::ErrorImportExpr(expr.position()))) diff --git a/src/lib.rs b/src/lib.rs index beeafb46..e13eaf0f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -103,9 +103,6 @@ pub use engine::Array; #[cfg(not(feature = "no_object"))] pub use engine::Map; -#[cfg(not(feature = "no_import"))] -pub use engine::SubScope; - #[cfg(not(feature = "no_float"))] pub use parser::FLOAT; diff --git a/src/optimize.rs b/src/optimize.rs index 57425c9a..29d2f7fe 100644 --- a/src/optimize.rs +++ b/src/optimize.rs @@ -231,6 +231,8 @@ fn optimize_stmt<'a>(stmt: Stmt, state: &mut State<'a>, preserve_result: bool) - } // let id; Stmt::Let(_, None, _) => stmt, + // import expr as id; + Stmt::Import(expr, id, pos) => Stmt::Import(Box::new(optimize_expr(*expr, state)), id, pos), // { block } Stmt::Block(block, pos) => { let orig_len = block.len(); // Original number of statements in the block, for change detection @@ -260,7 +262,7 @@ fn optimize_stmt<'a>(stmt: Stmt, state: &mut State<'a>, preserve_result: bool) - result.push(stmt); } - // Remove all let statements at the end of a block - the new variables will go away anyway. + // Remove all let/import statements at the end of a block - the new variables will go away anyway. // But be careful only remove ones that have no initial values or have values that are pure expressions, // otherwise there may be side effects. let mut removed = false; @@ -268,7 +270,8 @@ fn optimize_stmt<'a>(stmt: Stmt, state: &mut State<'a>, preserve_result: bool) - while let Some(expr) = result.pop() { match expr { Stmt::Let(_, None, _) => removed = true, - Stmt::Let(_, Some(val_expr), _) if val_expr.is_pure() => removed = true, + Stmt::Let(_, Some(val_expr), _) => removed = val_expr.is_pure(), + Stmt::Import(expr, _, _) => removed = expr.is_pure(), _ => { result.push(expr); break; @@ -323,6 +326,8 @@ fn optimize_stmt<'a>(stmt: Stmt, state: &mut State<'a>, preserve_result: bool) - state.set_dirty(); Stmt::Noop(pos) } + // Only one let/import statement - leave it alone + [Stmt::Let(_, _, _)] | [Stmt::Import(_, _, _)] => Stmt::Block(result, pos), // Only one statement - promote [_] => { state.set_dirty(); @@ -666,7 +671,10 @@ fn optimize<'a>( _ => { // Keep all variable declarations at this level // and always keep the last return value - let keep = matches!(stmt, Stmt::Let(_, _, _)) || i == num_statements - 1; + let keep = match stmt { + Stmt::Let(_, _, _) | Stmt::Import(_, _, _) => true, + _ => i == num_statements - 1, + }; optimize_stmt(stmt, &mut state, keep) } } diff --git a/src/parser.rs b/src/parser.rs index 8f242e0f..ef31c3f9 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -42,6 +42,8 @@ pub type FLOAT = f64; type PERR = ParseErrorType; +pub type ModuleRef = Option>>; + /// Compiled AST (abstract syntax tree) of a Rhai script. /// /// Currently, `AST` is neither `Send` nor `Sync`. Turn on the `sync` feature to make it `Send + Sync`. @@ -204,7 +206,7 @@ impl Stack { .enumerate() .find(|(_, (n, typ))| match typ { ScopeEntryType::Normal | ScopeEntryType::Constant => *n == name, - ScopeEntryType::SubScope => false, + ScopeEntryType::Module => false, }) .and_then(|(i, _)| NonZeroUsize::new(i + 1)) } @@ -218,7 +220,7 @@ impl Stack { .rev() .enumerate() .find(|(_, (n, typ))| match typ { - ScopeEntryType::SubScope => *n == name, + ScopeEntryType::Module => *n == name, ScopeEntryType::Normal | ScopeEntryType::Constant => false, }) .and_then(|(i, _)| NonZeroUsize::new(i + 1)) @@ -344,12 +346,7 @@ pub enum Expr { /// String constant. StringConstant(String, Position), /// Variable access - (variable name, optional modules, optional index, position) - Variable( - Box, - Option>>, - Option, - Position, - ), + Variable(Box, ModuleRef, Option, Position), /// Property access. Property(String, Position), /// { stmt } @@ -359,7 +356,7 @@ pub enum Expr { /// and the function names are predictable, so no need to allocate a new `String`. FnCall( Box>, - Option>>, + ModuleRef, Box>, Option>, Position, @@ -575,12 +572,12 @@ impl Expr { Self::Variable(_, None, _, _) => match token { Token::LeftBracket | Token::LeftParen => true, - #[cfg(not(feature = "no_import"))] + #[cfg(not(feature = "no_module"))] Token::DoubleColon => true, _ => false, }, Self::Variable(_, _, _, _) => match token { - #[cfg(not(feature = "no_import"))] + #[cfg(not(feature = "no_module"))] Token::DoubleColon => true, _ => false, }, @@ -659,7 +656,7 @@ fn parse_call_expr<'a>( input: &mut Peekable>, stack: &mut Stack, id: String, - modules: Option>>, + modules: ModuleRef, begin: Position, allow_stmt_expr: bool, ) -> Result> { @@ -1071,7 +1068,7 @@ fn parse_primary<'a>( parse_call_expr(input, stack, id, None, pos, allow_stmt_expr)? } // module access - #[cfg(not(feature = "no_import"))] + #[cfg(not(feature = "no_module"))] (Expr::Variable(id, mut modules, mut index, pos), Token::DoubleColon) => { match input.next().unwrap() { (Token::Identifier(id2), pos2) => { @@ -1790,7 +1787,7 @@ fn parse_let<'a>( Err(PERR::ForbiddenConstantExpr(name).into_err(init_value.position())) } // Variable cannot be a sub-scope - ScopeEntryType::SubScope => unreachable!(), + ScopeEntryType::Module => unreachable!(), } } else { // let name @@ -1799,7 +1796,7 @@ fn parse_let<'a>( } /// Parse an import statement. -#[cfg(not(feature = "no_import"))] +#[cfg(not(feature = "no_module"))] fn parse_import<'a>( input: &mut Peekable>, stack: &mut Stack, @@ -1829,7 +1826,7 @@ fn parse_import<'a>( (_, pos) => return Err(PERR::VariableExpected.into_err(pos)), }; - stack.push((name.clone(), ScopeEntryType::SubScope)); + stack.push((name.clone(), ScopeEntryType::Module)); Ok(Stmt::Import(Box::new(expr), Box::new(name), pos)) } diff --git a/src/scope.rs b/src/scope.rs index a965930f..394997bf 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -1,7 +1,7 @@ //! Module that defines the `Scope` type representing a function call-stack scope. use crate::any::{Dynamic, Union, Variant}; -use crate::engine::SubScope; +use crate::engine::Module; use crate::parser::{map_dynamic_to_expr, Expr}; use crate::token::Position; @@ -14,8 +14,9 @@ pub enum EntryType { Normal, /// Immutable constant value. Constant, - /// Name of a sub-scope, allowing member access with the :: operator. - SubScope, + /// Name of a module, allowing member access with the :: operator. + /// This is for internal use only. + Module, } /// An entry in the Scope. @@ -168,30 +169,14 @@ impl<'a> Scope<'a> { self.push_dynamic_value(name, EntryType::Normal, value, false); } - /// Add (push) a new sub-scope to the Scope. + /// Add (push) a new module to the Scope. /// - /// Sub-scopes are used for accessing members in modules and plugins under a namespace. - /// - /// # Examples - /// - /// ``` - /// use rhai::{Scope, SubScope}; - /// - /// let mut my_scope = Scope::new(); - /// - /// let mut sub_scope = SubScope::new(); - /// sub_scope.insert("x".to_string(), 42_i64.into()); - /// - /// my_scope.push_sub_scope("my_plugin", sub_scope); - /// - /// let s = my_scope.find_sub_scope("my_plugin").unwrap(); - /// assert_eq!(*s.get("x").unwrap().downcast_ref::().unwrap(), 42); - /// ``` - pub fn push_sub_scope>>(&mut self, name: K, value: SubScope) { + /// Modules are used for accessing member variables, functions and plugins under a namespace. + pub(crate) fn push_module>>(&mut self, name: K, value: Module) { self.push_dynamic_value( name, - EntryType::SubScope, - Dynamic(Union::SubScope(Box::new(value))), + EntryType::Module, + Dynamic(Union::Module(Box::new(value))), true, ); } @@ -295,8 +280,6 @@ impl<'a> Scope<'a> { /// Does the scope contain the entry? /// - /// Sub-scopes are ignored. - /// /// # Examples /// /// ``` @@ -314,13 +297,13 @@ impl<'a> Scope<'a> { .rev() // Always search a Scope in reverse order .any(|Entry { name: key, typ, .. }| match typ { EntryType::Normal | EntryType::Constant => name == key, - EntryType::SubScope => false, + EntryType::Module => false, }) } /// Find an entry in the Scope, starting from the last. /// - /// Sub-scopes are ignored. + /// modules are ignored. pub(crate) fn get_index(&self, name: &str) -> Option<(usize, EntryType)> { self.0 .iter() @@ -334,18 +317,18 @@ impl<'a> Scope<'a> { None } } - EntryType::SubScope => None, + EntryType::Module => None, }) } - /// Find a sub-scope in the Scope, starting from the last. - pub(crate) fn get_sub_scope_index(&self, name: &str) -> Option { + /// Find a module in the Scope, starting from the last. + pub(crate) fn get_module_index(&self, name: &str) -> Option { self.0 .iter() .enumerate() .rev() // Always search a Scope in reverse order .find_map(|(index, Entry { name: key, typ, .. })| match typ { - EntryType::SubScope => { + EntryType::Module => { if name == key { Some(index) } else { @@ -356,15 +339,15 @@ impl<'a> Scope<'a> { }) } - /// Find a sub-scope in the Scope, starting from the last entry. - pub fn find_sub_scope(&mut self, name: &str) -> Option<&mut SubScope> { - let index = self.get_sub_scope_index(name)?; - self.get_mut(index).0.downcast_mut::() + /// Find a module in the Scope, starting from the last entry. + 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::() } /// Get the value of an entry in the Scope, starting from the last. /// - /// Sub-scopes are ignored. + /// modules are ignored. /// /// # Examples /// @@ -382,7 +365,7 @@ impl<'a> Scope<'a> { .rev() .find(|Entry { name: key, typ, .. }| match typ { EntryType::Normal | EntryType::Constant => name == key, - EntryType::SubScope => false, + EntryType::Module => false, }) .and_then(|Entry { value, .. }| value.downcast_ref::().cloned()) } @@ -415,8 +398,8 @@ impl<'a> Scope<'a> { Some((index, EntryType::Normal)) => { self.0.get_mut(index).unwrap().value = Dynamic::from(value) } - // Sub-scopes cannot be modified - Some((_, EntryType::SubScope)) => unreachable!(), + // modules cannot be modified + Some((_, EntryType::Module)) => unreachable!(), } } diff --git a/src/token.rs b/src/token.rs index 86a3b820..1a303810 100644 --- a/src/token.rs +++ b/src/token.rs @@ -153,7 +153,7 @@ pub enum Token { RightShift, SemiColon, Colon, - #[cfg(not(feature = "no_import"))] + #[cfg(not(feature = "no_module"))] DoubleColon, Comma, Period, @@ -199,11 +199,11 @@ pub enum Token { XOrAssign, ModuloAssign, PowerOfAssign, - #[cfg(not(feature = "no_import"))] + #[cfg(not(feature = "no_module"))] Import, - #[cfg(not(feature = "no_import"))] + #[cfg(not(feature = "no_module"))] Export, - #[cfg(not(feature = "no_import"))] + #[cfg(not(feature = "no_module"))] As, LexError(Box), EOF, @@ -238,7 +238,7 @@ impl Token { Divide => "/", SemiColon => ";", Colon => ":", - #[cfg(not(feature = "no_import"))] + #[cfg(not(feature = "no_module"))] DoubleColon => "::", Comma => ",", Period => ".", @@ -288,11 +288,11 @@ impl Token { ModuloAssign => "%=", PowerOf => "~", PowerOfAssign => "~=", - #[cfg(not(feature = "no_import"))] + #[cfg(not(feature = "no_module"))] Import => "import", - #[cfg(not(feature = "no_import"))] + #[cfg(not(feature = "no_module"))] Export => "export", - #[cfg(not(feature = "no_import"))] + #[cfg(not(feature = "no_module"))] As => "as", EOF => "{EOF}", _ => panic!("operator should be match in outer scope"), @@ -763,11 +763,11 @@ impl<'a> TokenIterator<'a> { "for" => Token::For, "in" => Token::In, - #[cfg(not(feature = "no_import"))] + #[cfg(not(feature = "no_module"))] "import" => Token::Import, - #[cfg(not(feature = "no_import"))] + #[cfg(not(feature = "no_module"))] "export" => Token::Export, - #[cfg(not(feature = "no_import"))] + #[cfg(not(feature = "no_module"))] "as" => Token::As, #[cfg(not(feature = "no_function"))] @@ -924,7 +924,7 @@ impl<'a> TokenIterator<'a> { } ('=', _) => return Some((Token::Equals, pos)), - #[cfg(not(feature = "no_import"))] + #[cfg(not(feature = "no_module"))] (':', ':') => { self.eat_next(); return Some((Token::DoubleColon, pos)); diff --git a/tests/modules.rs b/tests/modules.rs index 9052edb3..5046791d 100644 --- a/tests/modules.rs +++ b/tests/modules.rs @@ -1,15 +1 @@ -use rhai::{EvalAltResult, Scope, SubScope, INT}; - -#[test] -#[cfg(not(feature = "no_import"))] -fn test_sub_scope() { - let mut my_scope = Scope::new(); - - let mut sub_scope = SubScope::new(); - sub_scope.insert("x".to_string(), (42 as INT).into()); - - my_scope.push_sub_scope("my_plugin", sub_scope); - - let s = my_scope.find_sub_scope("my_plugin").unwrap(); - assert_eq!(*s.get("x").unwrap().downcast_ref::().unwrap(), 42); -} +use rhai::{EvalAltResult, Scope, INT};