From aac04a5e537941ebc1687122cb6e610f60e65581 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 27 Jun 2020 23:56:24 +0800 Subject: [PATCH] Remove modules from Scope and use separate stack. --- RELEASES.md | 1 + src/any.rs | 11 -- src/api.rs | 17 ++- src/engine.rs | 267 +++++++++++++++++++++++++++------------------ src/fn_native.rs | 11 ++ src/module.rs | 208 +++++++++++++++++++++++++++-------- src/optimize.rs | 3 +- src/parser.rs | 91 ++++++++------- src/scope.rs | 91 ++------------- tests/modules.rs | 58 +++++----- tests/optimizer.rs | 10 +- 11 files changed, 434 insertions(+), 334 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index f42790be..68eb31e4 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -12,6 +12,7 @@ Breaking changes * The trait function `ModuleResolver::resolve` no longer takes a `Scope` as argument. * Functions defined in script now differentiates between using method-call style and normal function-call style. The method-call style will bind the object to the `this` parameter instead of consuming the first parameter. +* Imported modules are no longer stored in the `Scope`. `Scope::push_module` is removed. New features ------------ diff --git a/src/any.rs b/src/any.rs index 0f02f0d5..d99712e1 100644 --- a/src/any.rs +++ b/src/any.rs @@ -1,7 +1,6 @@ //! Helper module which defines the `Any` trait to to allow dynamic value handling. use crate::fn_native::{FnPtr, SendSync}; -use crate::module::Module; use crate::parser::{ImmutableString, INT}; use crate::r#unsafe::{unsafe_cast_box, unsafe_try_cast}; @@ -138,7 +137,6 @@ pub enum Union { Array(Box), #[cfg(not(feature = "no_object"))] Map(Box), - Module(Box), #[cfg(not(feature = "no_function"))] FnPtr(FnPtr), Variant(Box>), @@ -177,7 +175,6 @@ impl Dynamic { Union::Array(_) => TypeId::of::(), #[cfg(not(feature = "no_object"))] Union::Map(_) => TypeId::of::(), - Union::Module(_) => TypeId::of::(), #[cfg(not(feature = "no_function"))] Union::FnPtr(_) => TypeId::of::(), Union::Variant(value) => (***value).type_id(), @@ -198,7 +195,6 @@ impl Dynamic { Union::Array(_) => "array", #[cfg(not(feature = "no_object"))] Union::Map(_) => "map", - Union::Module(_) => "sub-scope", #[cfg(not(feature = "no_function"))] Union::FnPtr(_) => "Fn", @@ -224,7 +220,6 @@ impl fmt::Display for Dynamic { Union::Array(value) => fmt::Debug::fmt(value, f), #[cfg(not(feature = "no_object"))] Union::Map(value) => write!(f, "#{:?}", value), - Union::Module(value) => fmt::Debug::fmt(value, f), #[cfg(not(feature = "no_function"))] Union::FnPtr(value) => fmt::Display::fmt(value, f), @@ -250,7 +245,6 @@ impl fmt::Debug for Dynamic { Union::Array(value) => fmt::Debug::fmt(value, f), #[cfg(not(feature = "no_object"))] Union::Map(value) => write!(f, "#{:?}", value), - Union::Module(value) => fmt::Debug::fmt(value, f), #[cfg(not(feature = "no_function"))] Union::FnPtr(value) => fmt::Display::fmt(value, f), @@ -276,7 +270,6 @@ impl Clone for Dynamic { Union::Array(ref value) => Self(Union::Array(value.clone())), #[cfg(not(feature = "no_object"))] Union::Map(ref value) => Self(Union::Map(value.clone())), - Union::Module(ref value) => Self(Union::Module(value.clone())), #[cfg(not(feature = "no_function"))] Union::FnPtr(ref value) => Self(Union::FnPtr(value.clone())), Union::Variant(ref value) => (***value).clone_into_dynamic(), @@ -407,7 +400,6 @@ impl Dynamic { Union::Array(value) => unsafe_cast_box::<_, T>(value).ok().map(|v| *v), #[cfg(not(feature = "no_object"))] Union::Map(value) => unsafe_cast_box::<_, T>(value).ok().map(|v| *v), - Union::Module(value) => unsafe_cast_box::<_, T>(value).ok().map(|v| *v), #[cfg(not(feature = "no_function"))] Union::FnPtr(value) => unsafe_try_cast(value), Union::Variant(value) => (*value).as_box_any().downcast().map(|x| *x).ok(), @@ -452,7 +444,6 @@ impl Dynamic { Union::Array(value) => *unsafe_cast_box::<_, T>(value).unwrap(), #[cfg(not(feature = "no_object"))] Union::Map(value) => *unsafe_cast_box::<_, T>(value).unwrap(), - Union::Module(value) => *unsafe_cast_box::<_, T>(value).unwrap(), #[cfg(not(feature = "no_function"))] Union::FnPtr(value) => unsafe_try_cast(value).unwrap(), Union::Variant(value) => (*value).as_box_any().downcast().map(|x| *x).unwrap(), @@ -480,7 +471,6 @@ impl Dynamic { Union::Array(value) => ::downcast_ref::(value.as_ref()), #[cfg(not(feature = "no_object"))] Union::Map(value) => ::downcast_ref::(value.as_ref()), - Union::Module(value) => ::downcast_ref::(value.as_ref()), #[cfg(not(feature = "no_function"))] Union::FnPtr(value) => ::downcast_ref::(value), Union::Variant(value) => value.as_ref().as_ref().as_any().downcast_ref::(), @@ -507,7 +497,6 @@ impl Dynamic { Union::Array(value) => ::downcast_mut::(value.as_mut()), #[cfg(not(feature = "no_object"))] Union::Map(value) => ::downcast_mut::(value.as_mut()), - Union::Module(value) => ::downcast_mut::(value.as_mut()), #[cfg(not(feature = "no_function"))] Union::FnPtr(value) => ::downcast_mut::(value), Union::Variant(value) => value.as_mut().as_mut_any().downcast_mut::(), diff --git a/src/api.rs b/src/api.rs index 8ea047f9..66050214 100644 --- a/src/api.rs +++ b/src/api.rs @@ -2,7 +2,7 @@ use crate::any::{Dynamic, Variant}; use crate::engine::{ - get_script_function_by_signature, make_getter, make_setter, Engine, State, FN_IDX_GET, + get_script_function_by_signature, make_getter, make_setter, Engine, Imports, State, FN_IDX_GET, FN_IDX_SET, }; use crate::error::ParseError; @@ -973,7 +973,8 @@ impl Engine { scope: &mut Scope, ast: &AST, ) -> Result> { - let (result, _) = self.eval_ast_with_scope_raw(scope, ast)?; + let mut mods = Imports::new(); + let (result, _) = self.eval_ast_with_scope_raw(scope, &mut mods, ast)?; let return_type = self.map_type_name(result.type_name()); @@ -986,17 +987,18 @@ impl Engine { } /// Evaluate an `AST` with own scope. - pub(crate) fn eval_ast_with_scope_raw( + pub(crate) fn eval_ast_with_scope_raw<'a>( &self, scope: &mut Scope, - ast: &AST, + mods: &mut Imports, + ast: &'a AST, ) -> Result<(Dynamic, u64), Box> { let mut state = State::new(); ast.statements() .iter() .try_fold(().into(), |_, stmt| { - self.eval_stmt(scope, &mut state, ast.lib(), &mut None, stmt, 0) + self.eval_stmt(scope, mods, &mut state, ast.lib(), &mut None, stmt, 0) }) .or_else(|err| match *err { EvalAltResult::Return(out, _) => Ok(out), @@ -1058,11 +1060,12 @@ impl Engine { ast: &AST, ) -> Result<(), Box> { let mut state = State::new(); + let mut mods = Default::default(); ast.statements() .iter() .try_fold(().into(), |_, stmt| { - self.eval_stmt(scope, &mut state, ast.lib(), &mut None, stmt, 0) + self.eval_stmt(scope, &mut mods, &mut state, ast.lib(), &mut None, stmt, 0) }) .map_or_else( |err| match *err { @@ -1201,10 +1204,12 @@ impl Engine { })?; let mut state = State::new(); + let mut mods = Imports::new(); let args = args.as_mut(); self.call_script_fn( scope, + &mut mods, &mut state, ast.lib(), &mut None, diff --git a/src/engine.rs b/src/engine.rs index 5d340e59..49c5650c 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -19,6 +19,7 @@ use crate::parser::FLOAT; use crate::stdlib::{ any::TypeId, + borrow::Cow, boxed::Box, collections::HashMap, format, @@ -40,6 +41,9 @@ pub type Array = Vec; #[cfg(not(feature = "no_object"))] pub type Map = HashMap; +/// A stack of imported modules. +pub type Imports<'a> = Vec<(Cow<'a, str>, Module)>; + #[cfg(not(feature = "unchecked"))] #[cfg(debug_assertions)] pub const MAX_CALL_STACK_DEPTH: usize = 16; @@ -308,7 +312,7 @@ impl Default for Engine { #[cfg(any(feature = "no_module", feature = "no_std", target_arch = "wasm32",))] module_resolver: None, - type_names: HashMap::new(), + type_names: Default::default(), // default print/debug implementations print: Box::new(default_print), @@ -392,6 +396,7 @@ fn default_print(s: &str) { /// Search for a variable within the scope fn search_scope<'s, 'a>( scope: &'s mut Scope, + mods: &'s mut Imports, state: &mut State, this_ptr: &'s mut Option<&mut Dynamic>, expr: &'a Expr, @@ -420,17 +425,16 @@ fn search_scope<'s, 'a>( }; let module = if let Some(index) = index { - scope - .get_mut(scope.len() - index.get()) - .0 - .downcast_mut::() - .unwrap() + let offset = mods.len() - index.get(); + &mut mods.get_mut(offset).unwrap().1 } else { // Find the root module in the scope let (id, root_pos) = modules.get(0); - scope - .find_module_internal(id) + mods.iter_mut() + .rev() + .find(|(n, _)| n == id) + .map(|(_, m)| m) .ok_or_else(|| Box::new(EvalAltResult::ErrorModuleNotFound(id.into(), *root_pos)))? }; @@ -473,7 +477,7 @@ impl Engine { global_module: Default::default(), module_resolver: None, - type_names: HashMap::new(), + type_names: Default::default(), print: Box::new(|_| {}), debug: Box::new(|_| {}), progress: None, @@ -658,6 +662,7 @@ impl Engine { pub(crate) fn call_fn_raw( &self, scope: &mut Scope, + mods: &mut Imports, state: &mut State, lib: &Module, fn_name: &str, @@ -753,6 +758,7 @@ impl Engine { return Ok(( self.call_script_fn( scope, + mods, state, lib, &mut Some(first[0]), @@ -765,7 +771,7 @@ impl Engine { )); } else { let result = self.call_script_fn( - scope, state, lib, &mut None, fn_name, fn_def, args, level, + scope, mods, state, lib, &mut None, fn_name, fn_def, args, level, )?; // Restore the original reference @@ -889,6 +895,7 @@ impl Engine { pub(crate) fn call_script_fn( &self, scope: &mut Scope, + mods: &mut Imports, state: &mut State, lib: &Module, this_ptr: &mut Option<&mut Dynamic>, @@ -900,7 +907,8 @@ impl Engine { let orig_scope_level = state.scope_level; state.scope_level += 1; - let scope_len = scope.len(); + let prev_scope_len = scope.len(); + let prev_mods_len = mods.len(); // Put arguments into scope as variables // Actually consume the arguments instead of cloning them @@ -917,7 +925,7 @@ impl Engine { // Evaluate the function at one higher level of call depth let result = self - .eval_stmt(scope, state, lib, this_ptr, &fn_def.body, level + 1) + .eval_stmt(scope, mods, state, lib, this_ptr, &fn_def.body, level + 1) .or_else(|err| match *err { // Convert return statement to return value EvalAltResult::Return(x, _) => Ok(x), @@ -936,7 +944,8 @@ impl Engine { }); // Remove all local variables - scope.rewind(scope_len); + scope.rewind(prev_scope_len); + mods.truncate(prev_mods_len); state.scope_level = orig_scope_level; result @@ -1009,9 +1018,10 @@ impl Engine { // Normal function call _ => { let mut scope = Scope::new(); + let mut mods = Imports::new(); self.call_fn_raw( - &mut scope, state, lib, fn_name, hashes, args, is_ref, is_method, def_val, - level, + &mut scope, &mut mods, state, lib, fn_name, hashes, args, is_ref, is_method, + def_val, level, ) } } @@ -1022,6 +1032,7 @@ impl Engine { fn eval_script_expr( &self, scope: &mut Scope, + mods: &mut Imports, state: &mut State, lib: &Module, script: &Dynamic, @@ -1047,7 +1058,7 @@ impl Engine { let ast = AST::new(statements, lib.clone()); // Evaluate the AST - let (result, operations) = self.eval_ast_with_scope_raw(scope, &ast)?; + let (result, operations) = self.eval_ast_with_scope_raw(scope, mods, &ast)?; state.operations += operations; self.inc_operations(state)?; @@ -1356,6 +1367,7 @@ impl Engine { fn eval_dot_index_chain( &self, scope: &mut Scope, + mods: &mut Imports, state: &mut State, lib: &Module, this_ptr: &mut Option<&mut Dynamic>, @@ -1371,7 +1383,9 @@ impl Engine { let idx_values = &mut StaticVec::new(); - self.eval_indexed_chain(scope, state, lib, this_ptr, dot_rhs, idx_values, 0, level)?; + self.eval_indexed_chain( + scope, mods, state, lib, this_ptr, dot_rhs, idx_values, 0, level, + )?; match dot_lhs { // id.??? or id[???] @@ -1381,11 +1395,10 @@ impl Engine { self.inc_operations(state) .map_err(|err| EvalAltResult::new_position(err, *var_pos))?; - let (target, _, typ, pos) = search_scope(scope, state, this_ptr, dot_lhs)?; + let (target, _, typ, pos) = search_scope(scope, mods, state, this_ptr, dot_lhs)?; // Constants cannot be modified match typ { - ScopeEntryType::Module => unreachable!(), ScopeEntryType::Constant if new_val.is_some() => { return Err(Box::new(EvalAltResult::ErrorAssignmentToConstant( var_name.to_string(), @@ -1410,7 +1423,7 @@ impl Engine { } // {expr}.??? or {expr}[???] expr => { - let val = self.eval_expr(scope, state, lib, this_ptr, expr, level)?; + let val = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?; let obj_ptr = &mut val.into(); self.eval_dot_index_chain_helper( state, lib, this_ptr, obj_ptr, dot_rhs, idx_values, chain_type, level, new_val, @@ -1429,6 +1442,7 @@ impl Engine { fn eval_indexed_chain( &self, scope: &mut Scope, + mods: &mut Imports, state: &mut State, lib: &Module, this_ptr: &mut Option<&mut Dynamic>, @@ -1442,11 +1456,12 @@ impl Engine { match expr { Expr::FnCall(x) if x.1.is_none() => { - let arg_values = x - .3 - .iter() - .map(|arg_expr| self.eval_expr(scope, state, lib, this_ptr, arg_expr, level)) - .collect::, _>>()?; + let arg_values = + x.3.iter() + .map(|arg_expr| { + self.eval_expr(scope, mods, state, lib, this_ptr, arg_expr, level) + }) + .collect::, _>>()?; idx_values.push(Dynamic::from(arg_values)); } @@ -1458,15 +1473,17 @@ impl Engine { // Evaluate in left-to-right order let lhs_val = match lhs { Expr::Property(_) => Default::default(), // Store a placeholder in case of a property - _ => self.eval_expr(scope, state, lib, this_ptr, lhs, level)?, + _ => self.eval_expr(scope, mods, state, lib, this_ptr, lhs, level)?, }; // Push in reverse order - self.eval_indexed_chain(scope, state, lib, this_ptr, rhs, idx_values, size, level)?; + self.eval_indexed_chain( + scope, mods, state, lib, this_ptr, rhs, idx_values, size, level, + )?; idx_values.push(lhs_val); } - _ => idx_values.push(self.eval_expr(scope, state, lib, this_ptr, expr, level)?), + _ => idx_values.push(self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?), } Ok(()) @@ -1581,6 +1598,7 @@ impl Engine { fn eval_in_expr( &self, scope: &mut Scope, + mods: &mut Imports, state: &mut State, lib: &Module, this_ptr: &mut Option<&mut Dynamic>, @@ -1591,8 +1609,8 @@ impl Engine { self.inc_operations(state) .map_err(|err| EvalAltResult::new_position(err, rhs.position()))?; - let lhs_value = self.eval_expr(scope, state, lib, this_ptr, lhs, level)?; - let rhs_value = self.eval_expr(scope, state, lib, this_ptr, rhs, level)?; + let lhs_value = self.eval_expr(scope, mods, state, lib, this_ptr, lhs, level)?; + let rhs_value = self.eval_expr(scope, mods, state, lib, this_ptr, rhs, level)?; match rhs_value { #[cfg(not(feature = "no_index"))] @@ -1614,8 +1632,8 @@ impl Engine { let (r, _) = self .call_fn_raw( - &mut scope, state, lib, op, hashes, args, false, false, def_value, - level, + &mut scope, mods, state, lib, op, hashes, args, false, false, + def_value, level, ) .map_err(|err| EvalAltResult::new_position(err, rhs.position()))?; if r.as_bool().unwrap_or(false) { @@ -1648,6 +1666,7 @@ impl Engine { fn eval_expr( &self, scope: &mut Scope, + mods: &mut Imports, state: &mut State, lib: &Module, this_ptr: &mut Option<&mut Dynamic>, @@ -1658,7 +1677,7 @@ impl Engine { .map_err(|err| EvalAltResult::new_position(err, expr.position()))?; let result = match expr { - Expr::Expr(x) => self.eval_expr(scope, state, lib, this_ptr, x.as_ref(), level), + Expr::Expr(x) => self.eval_expr(scope, mods, state, lib, this_ptr, x.as_ref(), level), Expr::IntegerConstant(x) => Ok(x.0.into()), #[cfg(not(feature = "no_float"))] @@ -1673,19 +1692,21 @@ impl Engine { } } Expr::Variable(_) => { - let (val, _, _, _) = search_scope(scope, state, this_ptr, expr)?; + let (val, _, _, _) = search_scope(scope, mods, state, this_ptr, expr)?; Ok(val.clone()) } Expr::Property(_) => unreachable!(), // Statement block - Expr::Stmt(x) => self.eval_stmt(scope, state, lib, this_ptr, &x.0, level), + Expr::Stmt(x) => self.eval_stmt(scope, mods, state, lib, this_ptr, &x.0, level), // var op= rhs Expr::Assignment(x) if matches!(x.0, Expr::Variable(_)) => { let (lhs_expr, op, rhs_expr, op_pos) = x.as_ref(); - let mut rhs_val = self.eval_expr(scope, state, lib, this_ptr, rhs_expr, level)?; - let (lhs_ptr, name, typ, pos) = search_scope(scope, state, this_ptr, lhs_expr)?; + let mut rhs_val = + self.eval_expr(scope, mods, state, lib, this_ptr, rhs_expr, level)?; + let (lhs_ptr, name, typ, pos) = + search_scope(scope, mods, state, this_ptr, lhs_expr)?; self.inc_operations(state) .map_err(|err| EvalAltResult::new_position(err, pos))?; @@ -1732,15 +1753,14 @@ impl Engine { } Ok(Default::default()) } - // A module cannot be assigned to - ScopeEntryType::Module => unreachable!(), } } // lhs op= rhs Expr::Assignment(x) => { let (lhs_expr, op, rhs_expr, op_pos) = x.as_ref(); - let mut rhs_val = self.eval_expr(scope, state, lib, this_ptr, rhs_expr, level)?; + let mut rhs_val = + self.eval_expr(scope, mods, state, lib, this_ptr, rhs_expr, level)?; let new_val = Some(if op.is_empty() { // Normal assignment @@ -1750,7 +1770,7 @@ impl Engine { let op = &op[..op.len() - 1]; // extract operator without = let hash = calc_fn_hash(empty(), op, 2, empty()); let args = &mut [ - &mut self.eval_expr(scope, state, lib, this_ptr, lhs_expr, level)?, + &mut self.eval_expr(scope, mods, state, lib, this_ptr, lhs_expr, level)?, &mut rhs_val, ]; self.exec_fn_call(state, lib, op, true, hash, args, false, false, None, level) @@ -1764,12 +1784,12 @@ impl Engine { // idx_lhs[idx_expr] op= rhs #[cfg(not(feature = "no_index"))] Expr::Index(_) => self.eval_dot_index_chain( - scope, state, lib, this_ptr, lhs_expr, level, new_val, + scope, mods, state, lib, this_ptr, lhs_expr, level, new_val, ), // dot_lhs.dot_rhs op= rhs #[cfg(not(feature = "no_object"))] Expr::Dot(_) => self.eval_dot_index_chain( - scope, state, lib, this_ptr, lhs_expr, level, new_val, + scope, mods, state, lib, this_ptr, lhs_expr, level, new_val, ), // Error assignment to constant expr if expr.is_constant() => { @@ -1788,19 +1808,19 @@ impl Engine { // lhs[idx_expr] #[cfg(not(feature = "no_index"))] Expr::Index(_) => { - self.eval_dot_index_chain(scope, state, lib, this_ptr, expr, level, None) + self.eval_dot_index_chain(scope, mods, state, lib, this_ptr, expr, level, None) } // lhs.dot_rhs #[cfg(not(feature = "no_object"))] Expr::Dot(_) => { - self.eval_dot_index_chain(scope, state, lib, this_ptr, expr, level, None) + self.eval_dot_index_chain(scope, mods, state, lib, this_ptr, expr, level, None) } #[cfg(not(feature = "no_index"))] Expr::Array(x) => Ok(Dynamic(Union::Array(Box::new( x.0.iter() - .map(|item| self.eval_expr(scope, state, lib, this_ptr, item, level)) + .map(|item| self.eval_expr(scope, mods, state, lib, this_ptr, item, level)) .collect::, _>>()?, )))), @@ -1808,7 +1828,7 @@ impl Engine { Expr::Map(x) => Ok(Dynamic(Union::Map(Box::new( x.0.iter() .map(|((key, _), expr)| { - self.eval_expr(scope, state, lib, this_ptr, expr, level) + self.eval_expr(scope, mods, state, lib, this_ptr, expr, level) .map(|val| (key.clone(), val)) }) .collect::, _>>()?, @@ -1827,7 +1847,8 @@ impl Engine { if !self.has_override(lib, (hash_fn, *hash)) { // Fn - only in function call style let expr = args_expr.get(0); - let arg_value = self.eval_expr(scope, state, lib, this_ptr, expr, level)?; + let arg_value = + self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?; return arg_value .take_immutable_string() .map(|s| FnPtr::from(s).into()) @@ -1849,9 +1870,10 @@ impl Engine { // eval - only in function call style let prev_len = scope.len(); let expr = args_expr.get(0); - let script = self.eval_expr(scope, state, lib, this_ptr, expr, level)?; + let script = + self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?; let result = self - .eval_script_expr(scope, state, lib, &script) + .eval_script_expr(scope, mods, state, lib, &script) .map_err(|err| EvalAltResult::new_position(err, expr.position())); if scope.len() != prev_len { @@ -1882,16 +1904,12 @@ impl Engine { .iter() .skip(1) .map(|expr| { - self.eval_expr(scope, state, lib, this_ptr, expr, level) + self.eval_expr(scope, mods, state, lib, this_ptr, expr, level) }) .collect::>()?; - let (target, _, typ, pos) = search_scope(scope, state, this_ptr, lhs)?; - - match typ { - ScopeEntryType::Module => unreachable!(), - ScopeEntryType::Constant | ScopeEntryType::Normal => (), - } + let (target, _, _, pos) = + search_scope(scope, mods, state, this_ptr, lhs)?; self.inc_operations(state) .map_err(|err| EvalAltResult::new_position(err, pos))?; @@ -1905,7 +1923,7 @@ impl Engine { arg_values = args_expr .iter() .map(|expr| { - self.eval_expr(scope, state, lib, this_ptr, expr, level) + self.eval_expr(scope, mods, state, lib, this_ptr, expr, level) }) .collect::>()?; @@ -1929,23 +1947,30 @@ impl Engine { let mut arg_values = args_expr .iter() - .map(|expr| self.eval_expr(scope, state, lib, this_ptr, expr, level)) + .map(|expr| self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)) .collect::, _>>()?; let mut args: StaticVec<_> = arg_values.iter_mut().collect(); let (id, root_pos) = modules.get(0); // First module - let module = if let Some(index) = modules.index() { - scope - .get_mut(scope.len() - index.get()) - .0 - .downcast_mut::() - .unwrap() + let index = if state.always_search { + None } else { - scope.find_module_internal(id).ok_or_else(|| { - Box::new(EvalAltResult::ErrorModuleNotFound(id.into(), *root_pos)) - })? + modules.index() + }; + + let module = if let Some(index) = index { + let offset = mods.len() - index.get(); + &mut mods.get_mut(offset).unwrap().1 + } else { + mods.iter_mut() + .rev() + .find(|(n, _)| n == id) + .map(|(_, m)| m) + .ok_or_else(|| { + Box::new(EvalAltResult::ErrorModuleNotFound(id.into(), *root_pos)) + })? }; // First search in script-defined functions (can override built-in) @@ -1975,8 +2000,9 @@ impl Engine { let args = args.as_mut(); let fn_def = f.get_fn_def(); let mut scope = Scope::new(); + let mut mods = Imports::new(); self.call_script_fn( - &mut scope, state, lib, &mut None, name, fn_def, args, level, + &mut scope, &mut mods, state, lib, &mut None, name, fn_def, args, level, ) .map_err(|err| EvalAltResult::new_position(err, *pos)) } @@ -1993,19 +2019,19 @@ impl Engine { } } - Expr::In(x) => self.eval_in_expr(scope, state, lib, this_ptr, &x.0, &x.1, level), + Expr::In(x) => self.eval_in_expr(scope, mods, state, lib, this_ptr, &x.0, &x.1, level), Expr::And(x) => { let (lhs, rhs, _) = x.as_ref(); Ok((self - .eval_expr(scope, state, lib, this_ptr, lhs, level)? + .eval_expr(scope, mods, state, lib, this_ptr, lhs, level)? .as_bool() .map_err(|_| { EvalAltResult::ErrorBooleanArgMismatch("AND".into(), lhs.position()) })? && // Short-circuit using && self - .eval_expr(scope, state, lib, this_ptr, rhs, level)? + .eval_expr(scope, mods, state, lib, this_ptr, rhs, level)? .as_bool() .map_err(|_| { EvalAltResult::ErrorBooleanArgMismatch("AND".into(), rhs.position()) @@ -2016,14 +2042,14 @@ impl Engine { Expr::Or(x) => { let (lhs, rhs, _) = x.as_ref(); Ok((self - .eval_expr(scope, state, lib, this_ptr, lhs, level)? + .eval_expr(scope, mods, state, lib, this_ptr, lhs, level)? .as_bool() .map_err(|_| { EvalAltResult::ErrorBooleanArgMismatch("OR".into(), lhs.position()) })? || // Short-circuit using || self - .eval_expr(scope, state, lib, this_ptr, rhs, level)? + .eval_expr(scope, mods, state, lib, this_ptr, rhs, level)? .as_bool() .map_err(|_| { EvalAltResult::ErrorBooleanArgMismatch("OR".into(), rhs.position()) @@ -2045,6 +2071,7 @@ impl Engine { pub(crate) fn eval_stmt( &self, scope: &mut Scope, + mods: &mut Imports, state: &mut State, lib: &Module, this_ptr: &mut Option<&mut Dynamic>, @@ -2060,7 +2087,7 @@ impl Engine { // Expression as statement Stmt::Expr(expr) => { - let result = self.eval_expr(scope, state, lib, this_ptr, expr, level)?; + let result = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?; Ok(match expr.as_ref() { // If it is a simple assignment, erase the result at the root @@ -2071,14 +2098,16 @@ impl Engine { // Block scope Stmt::Block(x) => { - let prev_len = scope.len(); + let prev_scope_len = scope.len(); + let prev_mods_len = mods.len(); state.scope_level += 1; let result = x.0.iter().try_fold(Default::default(), |_, stmt| { - self.eval_stmt(scope, state, lib, this_ptr, stmt, level) + self.eval_stmt(scope, mods, state, lib, this_ptr, stmt, level) }); - scope.rewind(prev_len); + scope.rewind(prev_scope_len); + mods.truncate(prev_mods_len); state.scope_level -= 1; // The impact of an eval statement goes away at the end of a block @@ -2092,14 +2121,14 @@ impl Engine { Stmt::IfThenElse(x) => { let (expr, if_block, else_block) = x.as_ref(); - self.eval_expr(scope, state, lib, this_ptr, expr, level)? + self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)? .as_bool() .map_err(|_| Box::new(EvalAltResult::ErrorLogicGuard(expr.position()))) .and_then(|guard_val| { if guard_val { - self.eval_stmt(scope, state, lib, this_ptr, if_block, level) + self.eval_stmt(scope, mods, state, lib, this_ptr, if_block, level) } else if let Some(stmt) = else_block { - self.eval_stmt(scope, state, lib, this_ptr, stmt, level) + self.eval_stmt(scope, mods, state, lib, this_ptr, stmt, level) } else { Ok(Default::default()) } @@ -2111,17 +2140,21 @@ impl Engine { let (expr, body) = x.as_ref(); match self - .eval_expr(scope, state, lib, this_ptr, expr, level)? + .eval_expr(scope, mods, state, lib, this_ptr, expr, level)? .as_bool() { - Ok(true) => match self.eval_stmt(scope, state, lib, this_ptr, body, level) { - Ok(_) => (), - Err(err) => match *err { - EvalAltResult::ErrorLoopBreak(false, _) => (), - EvalAltResult::ErrorLoopBreak(true, _) => return Ok(Default::default()), - _ => return Err(err), - }, - }, + Ok(true) => { + match self.eval_stmt(scope, mods, state, lib, this_ptr, body, level) { + Ok(_) => (), + Err(err) => match *err { + EvalAltResult::ErrorLoopBreak(false, _) => (), + EvalAltResult::ErrorLoopBreak(true, _) => { + return Ok(Default::default()) + } + _ => return Err(err), + }, + } + } Ok(false) => return Ok(Default::default()), Err(_) => { return Err(Box::new(EvalAltResult::ErrorLogicGuard(expr.position()))) @@ -2131,7 +2164,7 @@ impl Engine { // Loop statement Stmt::Loop(body) => loop { - match self.eval_stmt(scope, state, lib, this_ptr, body, level) { + match self.eval_stmt(scope, mods, state, lib, this_ptr, body, level) { Ok(_) => (), Err(err) => match *err { EvalAltResult::ErrorLoopBreak(false, _) => (), @@ -2144,7 +2177,7 @@ impl Engine { // For loop Stmt::For(x) => { let (name, expr, stmt) = x.as_ref(); - let iter_type = self.eval_expr(scope, state, lib, this_ptr, expr, level)?; + let iter_type = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?; let tid = iter_type.type_id(); if let Some(func) = self @@ -2163,7 +2196,7 @@ impl Engine { self.inc_operations(state) .map_err(|err| EvalAltResult::new_position(err, stmt.position()))?; - match self.eval_stmt(scope, state, lib, this_ptr, stmt, level) { + match self.eval_stmt(scope, mods, state, lib, this_ptr, stmt, level) { Ok(_) => (), Err(err) => match *err { EvalAltResult::ErrorLoopBreak(false, _) => (), @@ -2190,7 +2223,15 @@ impl Engine { // Return value Stmt::ReturnWithVal(x) if x.1.is_some() && (x.0).0 == ReturnType::Return => { Err(Box::new(EvalAltResult::Return( - self.eval_expr(scope, state, lib, this_ptr, x.1.as_ref().unwrap(), level)?, + self.eval_expr( + scope, + mods, + state, + lib, + this_ptr, + x.1.as_ref().unwrap(), + level, + )?, (x.0).1, ))) } @@ -2202,8 +2243,15 @@ impl Engine { // Throw value Stmt::ReturnWithVal(x) if x.1.is_some() && (x.0).0 == ReturnType::Exception => { - let val = - self.eval_expr(scope, state, lib, this_ptr, x.1.as_ref().unwrap(), level)?; + let val = self.eval_expr( + scope, + mods, + state, + lib, + this_ptr, + x.1.as_ref().unwrap(), + level, + )?; Err(Box::new(EvalAltResult::ErrorRuntime( val.take_string().unwrap_or_else(|_| "".into()), (x.0).1, @@ -2220,8 +2268,15 @@ impl Engine { // Let statement Stmt::Let(x) if x.1.is_some() => { let ((var_name, _), expr) = x.as_ref(); - let val = - self.eval_expr(scope, state, lib, this_ptr, expr.as_ref().unwrap(), level)?; + let val = self.eval_expr( + scope, + mods, + state, + lib, + this_ptr, + expr.as_ref().unwrap(), + level, + )?; let var_name = unsafe_cast_var_name_to_lifetime(var_name, &state); scope.push_dynamic_value(var_name, ScopeEntryType::Normal, val, false); Ok(Default::default()) @@ -2237,7 +2292,7 @@ impl Engine { // Const statement Stmt::Const(x) if x.1.is_constant() => { let ((var_name, _), expr) = x.as_ref(); - let val = self.eval_expr(scope, state, lib, this_ptr, &expr, level)?; + let val = self.eval_expr(scope, mods, state, lib, this_ptr, &expr, level)?; let var_name = unsafe_cast_var_name_to_lifetime(var_name, &state); scope.push_dynamic_value(var_name, ScopeEntryType::Constant, val, true); Ok(Default::default()) @@ -2256,15 +2311,15 @@ impl Engine { } if let Some(path) = self - .eval_expr(scope, state, lib, this_ptr, &expr, level)? + .eval_expr(scope, mods, state, lib, this_ptr, &expr, level)? .try_cast::() { #[cfg(not(feature = "no_module"))] { if let Some(resolver) = &self.module_resolver { - let module = resolver.resolve(self, &path, expr.position())?; - let mod_name = unsafe_cast_var_name_to_lifetime(name, &state); - scope.push_module_internal(mod_name, module); + let mut module = resolver.resolve(self, &path, expr.position())?; + module.index_all_sub_modules(); + mods.push((name.clone().into(), module)); state.modules += 1; @@ -2288,11 +2343,7 @@ impl Engine { Stmt::Export(list) => { for ((id, id_pos), rename) in list.iter() { // Mark scope variables as public - if let Some(index) = scope - .get_index(id) - .map(|(i, _)| i) - .or_else(|| scope.get_module_index(id)) - { + if let Some(index) = scope.get_index(id).map(|(i, _)| i) { let alias = rename .as_ref() .map(|(n, _)| n.clone()) diff --git a/src/fn_native.rs b/src/fn_native.rs index e89cf334..040ff1f4 100644 --- a/src/fn_native.rs +++ b/src/fn_native.rs @@ -124,6 +124,17 @@ impl fmt::Debug for CallableFunction { } } +impl fmt::Display for CallableFunction { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Pure(_) => write!(f, "NativePureFunction"), + Self::Method(_) => write!(f, "NativeMethod"), + Self::Iterator(_) => write!(f, "NativeIterator"), + CallableFunction::Script(s) => fmt::Display::fmt(s, f), + } + } +} + impl CallableFunction { /// Is this a pure native Rust function? pub fn is_pure(&self) -> bool { diff --git a/src/module.rs b/src/module.rs index caf3cb39..37d7c988 100644 --- a/src/module.rs +++ b/src/module.rs @@ -2,7 +2,7 @@ use crate::any::{Dynamic, Variant}; use crate::calc_fn_hash; -use crate::engine::{make_getter, make_setter, Engine, FN_IDX_GET, FN_IDX_SET}; +use crate::engine::{make_getter, make_setter, Engine, Imports, FN_IDX_GET, FN_IDX_SET}; use crate::fn_native::{CallableFunction, FnCallArgs, IteratorFn, SendSync}; use crate::parser::{ FnAccess, @@ -10,13 +10,14 @@ use crate::parser::{ ScriptFnDef, AST, }; use crate::result::EvalAltResult; -use crate::scope::{Entry as ScopeEntry, EntryType as ScopeEntryType, Scope}; +use crate::scope::{Entry as ScopeEntry, Scope}; use crate::token::{Position, Token}; use crate::utils::{StaticVec, StraightHasherBuilder}; use crate::stdlib::{ any::TypeId, boxed::Box, + cell::RefCell, collections::HashMap, fmt, iter::empty, @@ -24,6 +25,7 @@ use crate::stdlib::{ num::NonZeroUsize, ops::{Deref, DerefMut}, string::{String, ToString}, + sync::RwLock, vec, vec::Vec, }; @@ -35,7 +37,7 @@ pub type FuncReturn = Result>; /// external Rust functions, and script-defined functions. /// /// Not available under the `no_module` feature. -#[derive(Clone, Default)] +#[derive(Default)] pub struct Module { /// Sub-modules. modules: HashMap, @@ -59,19 +61,47 @@ pub struct Module { /// Flattened collection of all external Rust functions, native or scripted, /// including those in sub-modules. all_functions: HashMap, + + /// Is the module indexed? + indexed: bool, } impl fmt::Debug for Module { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, - "", - self.variables, - self.functions.len(), + "Module(\n modules: {}\n vars: {}\n functions: {}\n)", + self.modules + .keys() + .map(|k| k.as_str()) + .collect::>() + .join(", "), + self.variables + .iter() + .map(|(k, v)| format!("{}={:?}", k, v)) + .collect::>() + .join(", "), + self.functions + .values() + .map(|(_, _, _, f)| f.to_string()) + .collect::>() + .join(", "), ) } } +impl Clone for Module { + fn clone(&self) -> Self { + // Only clone the index at the top level + Self { + all_variables: self.all_variables.clone(), + all_functions: self.all_functions.clone(), + indexed: self.indexed, + ..self.do_clone(false) + } + } +} + impl Module { /// Create a new module. /// @@ -106,6 +136,24 @@ impl Module { } } + /// Clone the module, optionally skipping the index. + fn do_clone(&self, clone_index: bool) -> Self { + Self { + modules: if clone_index { + self.modules.clone() + } else { + self.modules + .iter() + .map(|(k, m)| (k.clone(), m.do_clone(clone_index))) + .collect() + }, + variables: self.variables.clone(), + functions: self.functions.clone(), + type_iterators: self.type_iterators.clone(), + ..Default::default() + } + } + /// Does a variable exist in the module? /// /// # Examples @@ -166,6 +214,7 @@ impl Module { /// ``` pub fn set_var(&mut self, name: impl Into, value: impl Variant + Clone) { self.variables.insert(name.into(), Dynamic::from(value)); + self.indexed = false; } /// Get a mutable reference to a modules-qualified variable. @@ -197,6 +246,7 @@ impl Module { fn_def.into(), ), ); + self.indexed = false; } /// Does a sub-module exist in the module? @@ -263,6 +313,7 @@ impl Module { /// ``` pub fn set_sub_module(&mut self, name: impl Into, sub_module: Module) { self.modules.insert(name.into(), sub_module.into()); + self.indexed = false; } /// Does the particular Rust function exist in the module? @@ -302,6 +353,8 @@ impl Module { self.functions .insert(hash_fn, (name, access, params, func.into())); + self.indexed = false; + hash_fn } @@ -855,32 +908,27 @@ impl Module { /// ``` #[cfg(not(feature = "no_module"))] pub fn eval_ast_as_new(mut scope: Scope, ast: &AST, engine: &Engine) -> FuncReturn { + let mut mods = Imports::new(); + // Run the script - engine.eval_ast_with_scope_raw(&mut scope, &ast)?; + engine.eval_ast_with_scope_raw(&mut scope, &mut mods, &ast)?; // Create new module let mut module = Module::new(); - scope.into_iter().for_each( - |ScopeEntry { - typ, value, alias, .. - }| { - match typ { - // Variables with an alias left in the scope become module variables - ScopeEntryType::Normal | ScopeEntryType::Constant if alias.is_some() => { - module.variables.insert(*alias.unwrap(), value); - } - // Modules left in the scope become sub-modules - ScopeEntryType::Module if alias.is_some() => { - module - .modules - .insert(*alias.unwrap(), value.cast::()); - } - // Variables and modules with no alias are private and not exported - _ => (), + scope + .into_iter() + .for_each(|ScopeEntry { value, alias, .. }| { + // Variables with an alias left in the scope become module variables + if alias.is_some() { + module.variables.insert(*alias.unwrap(), value); } - }, - ); + }); + + // Modules left in the scope become sub-modules + mods.into_iter().for_each(|(alias, m)| { + module.modules.insert(alias.to_string(), m); + }); module.merge(ast.lib()); @@ -945,6 +993,10 @@ impl Module { } } + if self.indexed { + return; + } + let mut variables = Vec::new(); let mut functions = Vec::new(); @@ -952,6 +1004,7 @@ impl Module { self.all_variables = variables.into_iter().collect(); self.all_functions = functions.into_iter().collect(); + self.indexed = true; } /// Does a type iterator exist in the module? @@ -962,6 +1015,7 @@ impl Module { /// Set a type iterator into the module. pub fn set_iter(&mut self, typ: TypeId, func: IteratorFn) { self.type_iterators.insert(typ, func); + self.indexed = false; } /// Get the specified type iterator. @@ -1055,6 +1109,8 @@ mod file { /// Module resolution service that loads module script files from the file system. /// + /// Script files are cached so they are are not reloaded and recompiled in subsequent requests. + /// /// 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 @@ -1073,10 +1129,16 @@ mod file { /// let mut engine = Engine::new(); /// engine.set_module_resolver(Some(resolver)); /// ``` - #[derive(Debug, Eq, PartialEq, PartialOrd, Ord, Clone, Hash)] + #[derive(Debug)] pub struct FileModuleResolver { path: PathBuf, extension: String, + + #[cfg(not(feature = "sync"))] + cache: RefCell>, + + #[cfg(feature = "sync")] + cache: RwLock>, } impl Default for FileModuleResolver { @@ -1129,6 +1191,7 @@ mod file { Self { path: path.into(), extension: extension.into(), + cache: Default::default(), } } @@ -1173,12 +1236,45 @@ mod file { file_path.push(path); file_path.set_extension(&self.extension); // Force extension - // Compile it - let ast = engine - .compile_file(file_path) - .map_err(|err| err.new_position(pos))?; + let scope = Default::default(); - Module::eval_ast_as_new(Scope::new(), &ast, engine).map_err(|err| err.new_position(pos)) + // See if it is cached + let (module, ast) = { + #[cfg(not(feature = "sync"))] + let c = self.cache.borrow(); + #[cfg(feature = "sync")] + let c = self.cache.read().unwrap(); + + match c.get(&file_path) { + Some(ast) => ( + Module::eval_ast_as_new(scope, ast, engine) + .map_err(|err| err.new_position(pos))?, + None, + ), + None => { + // Load the file and compile it if not found + let ast = engine + .compile_file(file_path.clone()) + .map_err(|err| err.new_position(pos))?; + + ( + Module::eval_ast_as_new(scope, &ast, engine) + .map_err(|err| err.new_position(pos))?, + Some(ast), + ) + } + } + }; + + if let Some(ast) = ast { + // Put it into the cache + #[cfg(not(feature = "sync"))] + self.cache.borrow_mut().insert(file_path, ast); + #[cfg(feature = "sync")] + self.cache.write().unwrap().insert(file_path, ast); + } + + Ok(module) } } } @@ -1222,7 +1318,7 @@ mod stat { /// let mut resolver = StaticModuleResolver::new(); /// /// let module = Module::new(); - /// resolver.insert("hello".to_string(), module); + /// resolver.insert("hello", module); /// /// let mut engine = Engine::new(); /// engine.set_module_resolver(Some(resolver)); @@ -1232,17 +1328,43 @@ mod stat { } } - impl Deref for StaticModuleResolver { - type Target = HashMap; - - fn deref(&self) -> &Self::Target { - &self.0 + impl StaticModuleResolver { + /// Add a module keyed by its path. + pub fn insert>(&mut self, path: S, mut module: Module) { + module.index_all_sub_modules(); + self.0.insert(path.into(), module); } - } - - impl DerefMut for StaticModuleResolver { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 + /// Remove a module given its path. + pub fn remove(&mut self, path: &str) -> Option { + self.0.remove(path) + } + /// Does the path exist? + pub fn contains_path(&self, path: &str) -> bool { + self.0.contains_key(path) + } + /// Get an iterator of all the modules. + pub fn iter(&self) -> impl Iterator { + self.0.iter().map(|(k, v)| (k.as_str(), v)) + } + /// Get a mutable iterator of all the modules. + pub fn iter_mut(&mut self) -> impl Iterator { + self.0.iter_mut().map(|(k, v)| (k.as_str(), v)) + } + /// Get an iterator of all the module paths. + pub fn paths(&self) -> impl Iterator { + self.0.keys().map(String::as_str) + } + /// Get an iterator of all the modules. + pub fn values(&self) -> impl Iterator { + self.0.values() + } + /// Get a mutable iterator of all the modules. + pub fn values_mut(&mut self) -> impl Iterator { + self.0.values_mut() + } + /// Remove all modules. + pub fn clear(&mut self) { + self.0.clear(); } } diff --git a/src/optimize.rs b/src/optimize.rs index 7582aa6d..c7a1ed71 100644 --- a/src/optimize.rs +++ b/src/optimize.rs @@ -1,6 +1,6 @@ use crate::any::Dynamic; use crate::calc_fn_hash; -use crate::engine::{Engine, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_PRINT, KEYWORD_TYPE_OF}; +use crate::engine::{Engine, Imports, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_PRINT, KEYWORD_TYPE_OF}; use crate::module::Module; use crate::parser::{map_dynamic_to_expr, Expr, ReturnType, ScriptFnDef, Stmt, AST}; use crate::scope::{Entry as ScopeEntry, EntryType as ScopeEntryType, Scope}; @@ -122,6 +122,7 @@ fn call_fn_with_constant_arguments( .engine .call_fn_raw( &mut Scope::new(), + &mut Imports::new(), &mut Default::default(), state.lib, fn_name, diff --git a/src/parser.rs b/src/parser.rs index 9a318849..275f0c00 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -15,11 +15,11 @@ use crate::stdlib::{ boxed::Box, char, collections::HashMap, - format, + fmt, format, iter::empty, mem, num::NonZeroUsize, - ops::{Add, Deref, DerefMut}, + ops::Add, string::{String, ToString}, vec, vec::Vec, @@ -202,6 +202,25 @@ pub struct ScriptFnDef { pub pos: Position, } +impl fmt::Display for ScriptFnDef { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{}{}({})", + match self.access { + FnAccess::Public => "", + FnAccess::Private => "private ", + }, + self.name, + self.params + .iter() + .map(|s| s.as_str()) + .collect::>() + .join(",") + ) + } +} + /// `return`/`throw` statement. #[derive(Debug, Eq, PartialEq, Hash, Clone, Copy)] pub enum ReturnType { @@ -214,9 +233,11 @@ pub enum ReturnType { #[derive(Debug, Clone, Eq, PartialEq, Hash, Default)] struct ParseState { /// Encapsulates a local stack with variable names to simulate an actual runtime scope. - stack: Vec<(String, ScopeEntryType)>, + pub stack: Vec<(String, ScopeEntryType)>, + /// Encapsulates a local stack with variable names to simulate an actual runtime scope. + pub modules: Vec, /// Maximum levels of expression nesting. - max_expr_depth: usize, + pub max_expr_depth: usize, /// Maximum length of a string. pub max_string_size: usize, /// Maximum length of an array. @@ -245,15 +266,12 @@ impl ParseState { /// The return value is the offset to be deducted from `Stack::len`, /// i.e. the top element of the `ParseState` is offset 1. /// Return zero when the variable name is not found in the `ParseState`. - pub fn find(&self, name: &str) -> Option { + pub fn find_var(&self, name: &str) -> Option { self.stack .iter() .rev() .enumerate() - .find(|(_, (n, typ))| match typ { - ScopeEntryType::Normal | ScopeEntryType::Constant => *n == name, - ScopeEntryType::Module => false, - }) + .find(|(_, (n, _))| *n == name) .and_then(|(i, _)| NonZeroUsize::new(i + 1)) } /// Find a module by name in the `ParseState`, searching in reverse. @@ -261,32 +279,15 @@ impl ParseState { /// i.e. the top element of the `ParseState` is offset 1. /// Return zero when the variable name is not found in the `ParseState`. pub fn find_module(&self, name: &str) -> Option { - self.stack + self.modules .iter() .rev() .enumerate() - .find(|(_, (n, typ))| match typ { - ScopeEntryType::Module => *n == name, - ScopeEntryType::Normal | ScopeEntryType::Constant => false, - }) + .find(|(_, n)| *n == name) .and_then(|(i, _)| NonZeroUsize::new(i + 1)) } } -impl Deref for ParseState { - type Target = Vec<(String, ScopeEntryType)>; - - fn deref(&self) -> &Self::Target { - &self.stack - } -} - -impl DerefMut for ParseState { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.stack - } -} - #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] /// A type that encapsulates all the settings for a particular parsing function. struct ParseSettings { @@ -1279,7 +1280,7 @@ fn parse_primary( Token::CharConstant(c) => Expr::CharConstant(Box::new((c, settings.pos))), Token::StringConst(s) => Expr::StringConstant(Box::new((s.into(), settings.pos))), Token::Identifier(s) => { - let index = state.find(&s); + let index = state.find_var(&s); Expr::Variable(Box::new(((s, settings.pos), None, 0, index))) } Token::LeftParen => parse_paren_expr(input, state, settings.level_up())?, @@ -1469,7 +1470,7 @@ fn make_assignment_stmt<'a>( // var (indexed) = rhs Expr::Variable(x) => { let ((name, name_pos), _, _, index) = x.as_ref(); - match state.stack[(state.len() - index.unwrap().get())].1 { + match state.stack[(state.stack.len() - index.unwrap().get())].1 { ScopeEntryType::Normal => { Ok(Expr::Assignment(Box::new((lhs, fn_name.into(), rhs, pos)))) } @@ -1477,7 +1478,6 @@ fn make_assignment_stmt<'a>( ScopeEntryType::Constant => { Err(PERR::AssignmentToConstant(name.clone()).into_err(*name_pos)) } - ScopeEntryType::Module => unreachable!(), } } // xxx[???] = rhs, xxx.??? = rhs @@ -1489,7 +1489,7 @@ fn make_assignment_stmt<'a>( // var[???] (indexed) = rhs, var.??? (indexed) = rhs Expr::Variable(x) => { let ((name, name_pos), _, _, index) = x.as_ref(); - match state.stack[(state.len() - index.unwrap().get())].1 { + match state.stack[(state.stack.len() - index.unwrap().get())].1 { ScopeEntryType::Normal => { Ok(Expr::Assignment(Box::new((lhs, fn_name.into(), rhs, pos)))) } @@ -1497,7 +1497,6 @@ fn make_assignment_stmt<'a>( ScopeEntryType::Constant => { Err(PERR::AssignmentToConstant(name.clone()).into_err(*name_pos)) } - ScopeEntryType::Module => unreachable!(), } } // expr[???] = rhs, expr.??? = rhs @@ -2017,13 +2016,13 @@ fn parse_for( ensure_not_statement_expr(input, "a boolean")?; let expr = parse_expr(input, state, settings.level_up())?; - let prev_len = state.len(); - state.push((name.clone(), ScopeEntryType::Normal)); + let prev_len = state.stack.len(); + state.stack.push((name.clone(), ScopeEntryType::Normal)); settings.is_breakable = true; let body = parse_block(input, state, settings.level_up())?; - state.truncate(prev_len); + state.stack.truncate(prev_len); Ok(Stmt::For(Box::new((name, expr, body)))) } @@ -2064,34 +2063,30 @@ fn parse_let( match var_type { // let name = expr ScopeEntryType::Normal => { - state.push((name.clone(), ScopeEntryType::Normal)); + state.stack.push((name.clone(), ScopeEntryType::Normal)); Ok(Stmt::Let(Box::new(((name, pos), Some(init_value))))) } // const name = { expr:constant } ScopeEntryType::Constant if init_value.is_constant() => { - state.push((name.clone(), ScopeEntryType::Constant)); + state.stack.push((name.clone(), ScopeEntryType::Constant)); Ok(Stmt::Const(Box::new(((name, pos), init_value)))) } // const name = expr: error ScopeEntryType::Constant => { Err(PERR::ForbiddenConstantExpr(name).into_err(init_value.position())) } - // Variable cannot be a module - ScopeEntryType::Module => unreachable!(), } } else { // let name match var_type { ScopeEntryType::Normal => { - state.push((name.clone(), ScopeEntryType::Normal)); + state.stack.push((name.clone(), ScopeEntryType::Normal)); Ok(Stmt::Let(Box::new(((name, pos), None)))) } ScopeEntryType::Constant => { - state.push((name.clone(), ScopeEntryType::Constant)); + state.stack.push((name.clone(), ScopeEntryType::Constant)); Ok(Stmt::Const(Box::new(((name, pos), Expr::Unit(pos))))) } - // Variable cannot be a module - ScopeEntryType::Module => unreachable!(), } } } @@ -2127,7 +2122,7 @@ fn parse_import( (_, pos) => return Err(PERR::VariableExpected.into_err(pos)), }; - state.push((name.clone(), ScopeEntryType::Module)); + state.modules.push(name.clone()); Ok(Stmt::Import(Box::new((expr, (name, settings.pos))))) } @@ -2214,7 +2209,7 @@ fn parse_block( settings.ensure_level_within_max_limit(state.max_expr_depth)?; let mut statements = StaticVec::new(); - let prev_len = state.len(); + let prev_len = state.stack.len(); while !match_token(input, Token::RightBrace)? { // Parse statements inside the block @@ -2254,7 +2249,7 @@ fn parse_block( } } - state.truncate(prev_len); + state.stack.truncate(prev_len); Ok(Stmt::Block(Box::new((statements, settings.pos)))) } @@ -2389,7 +2384,7 @@ fn parse_fn( (Token::RightParen, _) => (), _ => match input.next().unwrap() { (Token::Identifier(s), pos) => { - state.push((s.clone(), ScopeEntryType::Normal)); + state.stack.push((s.clone(), ScopeEntryType::Normal)); params.push((s, pos)) } (Token::LexError(err), pos) => return Err(err.into_err(pos)), diff --git a/src/scope.rs b/src/scope.rs index 39827ef5..56b3a8cc 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -1,7 +1,6 @@ //! Module that defines the `Scope` type representing a function call-stack scope. -use crate::any::{Dynamic, Union, Variant}; -use crate::module::Module; +use crate::any::{Dynamic, Variant}; use crate::parser::{map_dynamic_to_expr, Expr}; use crate::token::Position; @@ -14,9 +13,6 @@ pub enum EntryType { Normal, /// Immutable constant value. Constant, - /// Name of a module, allowing member access with the :: operator. - /// This is for internal use only. - Module, } /// An entry in the Scope. @@ -171,32 +167,6 @@ impl<'a> Scope<'a> { self.push_dynamic_value(name, EntryType::Normal, value, false); } - /// 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_module_internal(name, value); - } - - /// Add (push) a new module to the Scope. - /// - /// Modules are used for accessing member variables, functions and plugins under a namespace. - pub(crate) fn push_module_internal>>( - &mut self, - name: K, - mut value: Module, - ) { - value.index_all_sub_modules(); - - self.push_dynamic_value( - name, - EntryType::Module, - Dynamic(Union::Module(Box::new(value))), - false, - ); - } - /// Add (push) a new constant to the Scope. /// /// Constants are immutable and cannot be assigned to. Their values never change. @@ -312,66 +282,26 @@ impl<'a> Scope<'a> { self.0 .iter() .rev() // Always search a Scope in reverse order - .any(|Entry { name: key, typ, .. }| match typ { - EntryType::Normal | EntryType::Constant => name == key, - EntryType::Module => false, - }) + .any(|Entry { name: key, .. }| name == key) } /// Find an entry in the Scope, starting from the last. - /// - /// modules are ignored. pub(crate) fn get_index(&self, name: &str) -> Option<(usize, EntryType)> { self.0 .iter() .enumerate() .rev() // Always search a Scope in reverse order - .find_map(|(index, Entry { name: key, typ, .. })| match typ { - EntryType::Normal | EntryType::Constant => { - if name == key { - Some((index, *typ)) - } else { - None - } + .find_map(|(index, Entry { name: key, typ, .. })| { + if name == key { + Some((index, *typ)) + } else { + None } - EntryType::Module => None, }) } - /// 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::Module => { - if name == key { - Some(index) - } else { - None - } - } - EntryType::Normal | EntryType::Constant => None, - }) - } - - /// 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> { - self.find_module_internal(name) - } - - /// Find a module in the Scope, starting from the last entry. - pub(crate) fn find_module_internal(&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. /// - /// modules are ignored. - /// /// # Examples /// /// ``` @@ -386,10 +316,7 @@ impl<'a> Scope<'a> { self.0 .iter() .rev() - .find(|Entry { name: key, typ, .. }| match typ { - EntryType::Normal | EntryType::Constant => name == key, - EntryType::Module => false, - }) + .find(|Entry { name: key, .. }| name == key) .and_then(|Entry { value, .. }| value.downcast_ref::().cloned()) } @@ -421,8 +348,6 @@ impl<'a> Scope<'a> { Some((index, EntryType::Normal)) => { self.0.get_mut(index).unwrap().value = Dynamic::from(value) } - // modules cannot be modified - Some((_, EntryType::Module)) => unreachable!(), } } diff --git a/tests/modules.rs b/tests/modules.rs index 6f1deb92..d7f6ad7c 100644 --- a/tests/modules.rs +++ b/tests/modules.rs @@ -1,6 +1,7 @@ #![cfg(not(feature = "no_module"))] use rhai::{ - module_resolvers, Engine, EvalAltResult, Module, ParseError, ParseErrorType, Scope, INT, + module_resolvers::StaticModuleResolver, Engine, EvalAltResult, Module, ParseError, + ParseErrorType, Scope, INT, }; #[test] @@ -37,22 +38,25 @@ fn test_module_sub_module() -> Result<(), Box> { assert_eq!(m2.get_var_value::("answer").unwrap(), 41); - let engine = Engine::new(); + let mut resolver = StaticModuleResolver::new(); + resolver.insert("question", module); + + let mut engine = Engine::new(); + engine.set_module_resolver(Some(resolver)); + let mut scope = Scope::new(); - scope.push_module("question", module); - assert_eq!( - engine.eval_expression_with_scope::( + engine.eval_with_scope::( &mut scope, - "question::life::universe::answer + 1" + r#"import "question" as q; q::life::universe::answer + 1"# )?, 42 ); assert_eq!( - engine.eval_expression_with_scope::( + engine.eval_with_scope::( &mut scope, - "question::life::universe::inc(question::life::universe::answer)" + r#"import "question" as q; q::life::universe::inc(q::life::universe::answer)"# )?, 42 ); @@ -62,7 +66,7 @@ fn test_module_sub_module() -> Result<(), Box> { #[test] fn test_module_resolver() -> Result<(), Box> { - let mut resolver = module_resolvers::StaticModuleResolver::new(); + let mut resolver = StaticModuleResolver::new(); let mut module = Module::new(); module.set_var("answer", 42 as INT); @@ -70,7 +74,7 @@ fn test_module_resolver() -> Result<(), Box> { Ok(x + y + z + w) }); - resolver.insert("hello".to_string(), module); + resolver.insert("hello", module); let mut engine = Engine::new(); engine.set_module_resolver(Some(resolver)); @@ -155,12 +159,10 @@ fn test_module_resolver() -> Result<(), Box> { fn test_module_from_ast() -> Result<(), Box> { let mut engine = Engine::new(); - let mut resolver = rhai::module_resolvers::StaticModuleResolver::new(); + let mut resolver1 = StaticModuleResolver::new(); let mut sub_module = Module::new(); sub_module.set_var("foo", true); - resolver.insert("another module".to_string(), sub_module); - - engine.set_module_resolver(Some(resolver)); + resolver1.insert("another module", sub_module); let ast = engine.compile( r#" @@ -190,44 +192,48 @@ fn test_module_from_ast() -> Result<(), Box> { export x as abc, foo, - hello, - extra as foobar; + hello; "#, )?; + engine.set_module_resolver(Some(resolver1)); + let module = Module::eval_ast_as_new(Scope::new(), &ast, &engine)?; + let mut resolver2 = StaticModuleResolver::new(); + resolver2.insert("testing", module); + engine.set_module_resolver(Some(resolver2)); + let mut scope = Scope::new(); - scope.push_module("testing", module); - assert_eq!( - engine.eval_expression_with_scope::(&mut scope, "testing::abc")?, + engine.eval_with_scope::(&mut scope, r#"import "testing" as ttt; ttt::abc"#)?, 123 ); assert_eq!( - engine.eval_expression_with_scope::(&mut scope, "testing::foo")?, + engine.eval_with_scope::(&mut scope, r#"import "testing" as ttt; ttt::foo"#)?, 42 ); - assert!(engine.eval_expression_with_scope::(&mut scope, "testing::foobar::foo")?); + assert!(engine + .eval_with_scope::(&mut scope, r#"import "testing" as ttt; ttt::extra::foo"#)?); assert_eq!( - engine.eval_expression_with_scope::(&mut scope, "testing::hello")?, + engine.eval_with_scope::(&mut scope, r#"import "testing" as ttt; ttt::hello"#)?, "hello, 42 worlds!" ); assert_eq!( - engine.eval_expression_with_scope::(&mut scope, "testing::calc(999)")?, + engine.eval_with_scope::(&mut scope, r#"import "testing" as ttt; ttt::calc(999)"#)?, 1000 ); assert_eq!( - engine.eval_expression_with_scope::( + engine.eval_with_scope::( &mut scope, - "testing::add_len(testing::foo, testing::hello)" + r#"import "testing" as ttt; ttt::add_len(ttt::foo, ttt::hello)"# )?, 59 ); assert!(matches!( *engine - .eval_expression_with_scope::<()>(&mut scope, "testing::hidden()") + .eval_with_scope::<()>(&mut scope, r#"import "testing" as ttt; ttt::hidden()"#) .expect_err("should error"), EvalAltResult::ErrorFunctionNotFound(fn_name, _) if fn_name == "hidden" )); diff --git a/tests/optimizer.rs b/tests/optimizer.rs index 828b0da4..2a75e916 100644 --- a/tests/optimizer.rs +++ b/tests/optimizer.rs @@ -38,19 +38,13 @@ fn test_optimizer_parse() -> Result<(), Box> { let ast = engine.compile("{ const DECISION = false; if DECISION { 42 } }")?; - assert_eq!( - format!("{:?}", ast), - "AST([], )" - ); + assert!(format!("{:?}", ast).starts_with("AST([], Module(")); engine.set_optimization_level(OptimizationLevel::Full); let ast = engine.compile("if 1 == 2 { 42 }")?; - assert_eq!( - format!("{:?}", ast), - "AST([], )" - ); + assert!(format!("{:?}", ast).starts_with("AST([], Module(")); Ok(()) }