diff --git a/src/ast.rs b/src/ast.rs new file mode 100644 index 00000000..7b282c24 --- /dev/null +++ b/src/ast.rs @@ -0,0 +1,427 @@ +//! Module defining the AST (abstract syntax tree). + +use crate::fn_native::Shared; +use crate::module::Module; +use crate::parser::{FnAccess, ScriptFnDef, Stmt}; + +use crate::stdlib::{ + ops::{Add, AddAssign}, + vec, + vec::Vec, +}; + +/// Compiled AST (abstract syntax tree) of a Rhai script. +/// +/// # Thread Safety +/// +/// Currently, `AST` is neither `Send` nor `Sync`. Turn on the `sync` feature to make it `Send + Sync`. +#[derive(Debug, Clone, Default)] +pub struct AST( + /// Global statements. + Vec, + /// Script-defined functions. + Module, +); + +impl AST { + /// Create a new `AST`. + #[inline(always)] + pub fn new(statements: Vec, lib: Module) -> Self { + Self(statements, lib) + } + + /// Get the statements. + #[cfg(not(feature = "internals"))] + #[inline(always)] + pub(crate) fn statements(&self) -> &[Stmt] { + &self.0 + } + + /// _[INTERNALS]_ Get the statements. + /// Exported under the `internals` feature only. + #[cfg(feature = "internals")] + #[deprecated(note = "this method is volatile and may change")] + #[inline(always)] + pub fn statements(&self) -> &[Stmt] { + &self.0 + } + + /// Get a mutable reference to the statements. + #[cfg(not(feature = "no_optimize"))] + #[inline(always)] + pub(crate) fn statements_mut(&mut self) -> &mut Vec { + &mut self.0 + } + + /// Get the internal `Module` containing all script-defined functions. + #[cfg(not(feature = "internals"))] + #[inline(always)] + pub(crate) fn lib(&self) -> &Module { + &self.1 + } + + /// _[INTERNALS]_ Get the internal `Module` containing all script-defined functions. + /// Exported under the `internals` feature only. + #[cfg(feature = "internals")] + #[deprecated(note = "this method is volatile and may change")] + #[inline(always)] + pub fn lib(&self) -> &Module { + &self.1 + } + + /// Clone the `AST`'s functions into a new `AST`. + /// No statements are cloned. + /// + /// This operation is cheap because functions are shared. + #[inline(always)] + pub fn clone_functions_only(&self) -> Self { + self.clone_functions_only_filtered(|_, _, _| true) + } + + /// Clone the `AST`'s functions into a new `AST` based on a filter predicate. + /// No statements are cloned. + /// + /// This operation is cheap because functions are shared. + #[inline(always)] + pub fn clone_functions_only_filtered( + &self, + mut filter: impl FnMut(FnAccess, &str, usize) -> bool, + ) -> Self { + let mut functions: Module = Default::default(); + functions.merge_filtered(&self.1, &mut filter); + Self(Default::default(), functions) + } + + /// Clone the `AST`'s script statements into a new `AST`. + /// No functions are cloned. + #[inline(always)] + pub fn clone_statements_only(&self) -> Self { + Self(self.0.clone(), Default::default()) + } + + /// Merge two `AST` into one. Both `AST`'s are untouched and a new, merged, version + /// is returned. + /// + /// Statements in the second `AST` are simply appended to the end of the first _without any processing_. + /// Thus, the return value of the first `AST` (if using expression-statement syntax) is buried. + /// Of course, if the first `AST` uses a `return` statement at the end, then + /// the second `AST` will essentially be dead code. + /// + /// All script-defined functions in the second `AST` overwrite similarly-named functions + /// in the first `AST` with the same number of parameters. + /// + /// # Example + /// + /// ``` + /// # fn main() -> Result<(), Box> { + /// # #[cfg(not(feature = "no_function"))] + /// # { + /// use rhai::Engine; + /// + /// let engine = Engine::new(); + /// + /// let ast1 = engine.compile(r#" + /// fn foo(x) { 42 + x } + /// foo(1) + /// "#)?; + /// + /// let ast2 = engine.compile(r#" + /// fn foo(n) { "hello" + n } + /// foo("!") + /// "#)?; + /// + /// let ast = ast1.merge(&ast2); // Merge 'ast2' into 'ast1' + /// + /// // Notice that using the '+' operator also works: + /// // let ast = &ast1 + &ast2; + /// + /// // 'ast' is essentially: + /// // + /// // fn foo(n) { "hello" + n } // <- definition of first 'foo' is overwritten + /// // foo(1) // <- notice this will be "hello1" instead of 43, + /// // // but it is no longer the return value + /// // foo("!") // returns "hello!" + /// + /// // Evaluate it + /// assert_eq!(engine.eval_ast::(&ast)?, "hello!"); + /// # } + /// # Ok(()) + /// # } + /// ``` + #[inline(always)] + pub fn merge(&self, other: &Self) -> Self { + self.merge_filtered(other, |_, _, _| true) + } + + /// Combine one `AST` with another. The second `AST` is consumed. + /// + /// Statements in the second `AST` are simply appended to the end of the first _without any processing_. + /// Thus, the return value of the first `AST` (if using expression-statement syntax) is buried. + /// Of course, if the first `AST` uses a `return` statement at the end, then + /// the second `AST` will essentially be dead code. + /// + /// All script-defined functions in the second `AST` overwrite similarly-named functions + /// in the first `AST` with the same number of parameters. + /// + /// # Example + /// + /// ``` + /// # fn main() -> Result<(), Box> { + /// # #[cfg(not(feature = "no_function"))] + /// # { + /// use rhai::Engine; + /// + /// let engine = Engine::new(); + /// + /// let mut ast1 = engine.compile(r#" + /// fn foo(x) { 42 + x } + /// foo(1) + /// "#)?; + /// + /// let ast2 = engine.compile(r#" + /// fn foo(n) { "hello" + n } + /// foo("!") + /// "#)?; + /// + /// ast1.combine(ast2); // Combine 'ast2' into 'ast1' + /// + /// // Notice that using the '+=' operator also works: + /// // ast1 += ast2; + /// + /// // 'ast1' is essentially: + /// // + /// // fn foo(n) { "hello" + n } // <- definition of first 'foo' is overwritten + /// // foo(1) // <- notice this will be "hello1" instead of 43, + /// // // but it is no longer the return value + /// // foo("!") // returns "hello!" + /// + /// // Evaluate it + /// assert_eq!(engine.eval_ast::(&ast1)?, "hello!"); + /// # } + /// # Ok(()) + /// # } + /// ``` + #[inline(always)] + pub fn combine(&mut self, other: Self) -> &mut Self { + self.combine_filtered(other, |_, _, _| true) + } + + /// Merge two `AST` into one. Both `AST`'s are untouched and a new, merged, version + /// is returned. + /// + /// Statements in the second `AST` are simply appended to the end of the first _without any processing_. + /// Thus, the return value of the first `AST` (if using expression-statement syntax) is buried. + /// Of course, if the first `AST` uses a `return` statement at the end, then + /// the second `AST` will essentially be dead code. + /// + /// All script-defined functions in the second `AST` are first selected based on a filter + /// predicate, then overwrite similarly-named functions in the first `AST` with the + /// same number of parameters. + /// + /// # Example + /// + /// ``` + /// # fn main() -> Result<(), Box> { + /// # #[cfg(not(feature = "no_function"))] + /// # { + /// use rhai::Engine; + /// + /// let engine = Engine::new(); + /// + /// let ast1 = engine.compile(r#" + /// fn foo(x) { 42 + x } + /// foo(1) + /// "#)?; + /// + /// let ast2 = engine.compile(r#" + /// fn foo(n) { "hello" + n } + /// fn error() { 0 } + /// foo("!") + /// "#)?; + /// + /// // Merge 'ast2', picking only 'error()' but not 'foo(_)', into 'ast1' + /// let ast = ast1.merge_filtered(&ast2, |_, name, params| name == "error" && params == 0); + /// + /// // 'ast' is essentially: + /// // + /// // fn foo(n) { 42 + n } // <- definition of 'ast1::foo' is not overwritten + /// // // because 'ast2::foo' is filtered away + /// // foo(1) // <- notice this will be 43 instead of "hello1", + /// // // but it is no longer the return value + /// // fn error() { 0 } // <- this function passes the filter and is merged + /// // foo("!") // <- returns "42!" + /// + /// // Evaluate it + /// assert_eq!(engine.eval_ast::(&ast)?, "42!"); + /// # } + /// # Ok(()) + /// # } + /// ``` + #[inline] + pub fn merge_filtered( + &self, + other: &Self, + mut filter: impl FnMut(FnAccess, &str, usize) -> bool, + ) -> Self { + let Self(statements, functions) = self; + + let ast = match (statements.is_empty(), other.0.is_empty()) { + (false, false) => { + let mut statements = statements.clone(); + statements.extend(other.0.iter().cloned()); + statements + } + (false, true) => statements.clone(), + (true, false) => other.0.clone(), + (true, true) => vec![], + }; + + let mut functions = functions.clone(); + functions.merge_filtered(&other.1, &mut filter); + + Self::new(ast, functions) + } + + /// Combine one `AST` with another. The second `AST` is consumed. + /// + /// Statements in the second `AST` are simply appended to the end of the first _without any processing_. + /// Thus, the return value of the first `AST` (if using expression-statement syntax) is buried. + /// Of course, if the first `AST` uses a `return` statement at the end, then + /// the second `AST` will essentially be dead code. + /// + /// All script-defined functions in the second `AST` are first selected based on a filter + /// predicate, then overwrite similarly-named functions in the first `AST` with the + /// same number of parameters. + /// + /// # Example + /// + /// ``` + /// # fn main() -> Result<(), Box> { + /// # #[cfg(not(feature = "no_function"))] + /// # { + /// use rhai::Engine; + /// + /// let engine = Engine::new(); + /// + /// let mut ast1 = engine.compile(r#" + /// fn foo(x) { 42 + x } + /// foo(1) + /// "#)?; + /// + /// let ast2 = engine.compile(r#" + /// fn foo(n) { "hello" + n } + /// fn error() { 0 } + /// foo("!") + /// "#)?; + /// + /// // Combine 'ast2', picking only 'error()' but not 'foo(_)', into 'ast1' + /// ast1.combine_filtered(ast2, |_, name, params| name == "error" && params == 0); + /// + /// // 'ast1' is essentially: + /// // + /// // fn foo(n) { 42 + n } // <- definition of 'ast1::foo' is not overwritten + /// // // because 'ast2::foo' is filtered away + /// // foo(1) // <- notice this will be 43 instead of "hello1", + /// // // but it is no longer the return value + /// // fn error() { 0 } // <- this function passes the filter and is merged + /// // foo("!") // <- returns "42!" + /// + /// // Evaluate it + /// assert_eq!(engine.eval_ast::(&ast1)?, "42!"); + /// # } + /// # Ok(()) + /// # } + /// ``` + #[inline(always)] + pub fn combine_filtered( + &mut self, + other: Self, + mut filter: impl FnMut(FnAccess, &str, usize) -> bool, + ) -> &mut Self { + let Self(ref mut statements, ref mut functions) = self; + statements.extend(other.0.into_iter()); + functions.merge_filtered(&other.1, &mut filter); + self + } + + /// Filter out the functions, retaining only some based on a filter predicate. + /// + /// # Example + /// + /// ``` + /// # fn main() -> Result<(), Box> { + /// # #[cfg(not(feature = "no_function"))] + /// # { + /// use rhai::Engine; + /// + /// let engine = Engine::new(); + /// + /// let mut ast = engine.compile(r#" + /// fn foo(n) { n + 1 } + /// fn bar() { print("hello"); } + /// "#)?; + /// + /// // Remove all functions except 'foo(_)' + /// ast.retain_functions(|_, name, params| name == "foo" && params == 1); + /// # } + /// # Ok(()) + /// # } + /// ``` + #[cfg(not(feature = "no_function"))] + #[inline(always)] + pub fn retain_functions(&mut self, filter: impl FnMut(FnAccess, &str, usize) -> bool) { + self.1.retain_functions(filter); + } + + /// Iterate through all functions + #[cfg(not(feature = "no_function"))] + #[inline(always)] + pub fn iter_functions<'a>( + &'a self, + ) -> impl Iterator)> + 'a { + self.1.iter_script_fn() + } + + /// Clear all function definitions in the `AST`. + #[cfg(not(feature = "no_function"))] + #[inline(always)] + pub fn clear_functions(&mut self) { + self.1 = Default::default(); + } + + /// Clear all statements in the `AST`, leaving only function definitions. + #[inline(always)] + pub fn clear_statements(&mut self) { + self.0 = vec![]; + } +} + +impl> Add for &AST { + type Output = AST; + + #[inline(always)] + fn add(self, rhs: A) -> Self::Output { + self.merge(rhs.as_ref()) + } +} + +impl> AddAssign for AST { + #[inline(always)] + fn add_assign(&mut self, rhs: A) { + self.combine(rhs.into()); + } +} + +impl AsRef<[Stmt]> for AST { + #[inline(always)] + fn as_ref(&self) -> &[Stmt] { + self.statements() + } +} + +impl AsRef for AST { + #[inline(always)] + fn as_ref(&self) -> &Module { + self.lib() + } +} diff --git a/src/any.rs b/src/dynamic.rs similarity index 100% rename from src/any.rs rename to src/dynamic.rs diff --git a/src/engine.rs b/src/engine.rs index dcd786e4..18a5f582 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -1,6 +1,6 @@ //! Main module defining the script evaluation `Engine`. -use crate::any::{map_std_type_name, Dynamic, Union, Variant}; +use crate::dynamic::{map_std_type_name, Dynamic, Union, Variant}; use crate::fn_call::run_builtin_op_assignment; use crate::fn_native::{Callback, FnPtr, OnVarCallback}; use crate::module::{Module, ModuleRef}; @@ -12,7 +12,7 @@ use crate::result::EvalAltResult; use crate::scope::{EntryType as ScopeEntryType, Scope}; use crate::syntax::CustomSyntax; use crate::token::Position; -use crate::{calc_fn_hash, StaticVec}; +use crate::{calc_native_fn_hash, StaticVec}; #[cfg(not(feature = "no_index"))] use crate::parser::INT; @@ -29,7 +29,7 @@ use crate::utils::ImmutableString; #[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_object"))] -use crate::any::DynamicWriteLock; +use crate::dynamic::DynamicWriteLock; use crate::stdlib::{ any::type_name, @@ -462,7 +462,7 @@ impl<'e, 'x, 'px, 'a, 's, 'm, 'pm, 't, 'pt> EvalContext<'e, 'x, 'px, 'a, 's, 'm, #[cfg(not(feature = "no_module"))] #[inline(always)] pub fn imports(&self) -> &'a Imports { - self.mods + self.mods.as_ref() } /// Get an iterator over the namespaces containing definition of all script-defined functions. #[inline(always)] @@ -1447,8 +1447,7 @@ impl Engine { let args = &mut [&mut lhs_value.clone(), value]; // Qualifiers (none) + function name + number of arguments + argument `TypeId`'s. - let hash = - calc_fn_hash(empty(), op, args.len(), args.iter().map(|a| a.type_id())); + let hash = calc_native_fn_hash(empty(), op, args.iter().map(|a| a.type_id())); if self .call_native_fn(state, lib, op, hash, args, false, false, &def_value) @@ -1698,7 +1697,7 @@ impl Engine { // Qualifiers (none) + function name + number of arguments + argument `TypeId`'s. let arg_types = once(lhs_ptr.as_mut().type_id()).chain(once(rhs_val.type_id())); - let hash_fn = calc_fn_hash(empty(), op, 2, arg_types); + let hash_fn = calc_native_fn_hash(empty(), op, arg_types); match self .global_module diff --git a/src/api.rs b/src/engine_api.rs similarity index 99% rename from src/api.rs rename to src/engine_api.rs index bfa0abfa..729a7105 100644 --- a/src/api.rs +++ b/src/engine_api.rs @@ -1,11 +1,11 @@ //! Module that defines the extern API of `Engine`. -use crate::any::{Dynamic, Variant}; +use crate::ast::AST; +use crate::dynamic::{Dynamic, Variant}; use crate::engine::{Engine, EvalContext, Imports, State}; -use crate::error::ParseError; use crate::fn_native::{FnCallArgs, NativeCallContext, SendSync}; use crate::optimize::OptimizationLevel; -use crate::parser::AST; +use crate::parse_error::ParseError; use crate::result::EvalAltResult; use crate::scope::Scope; use crate::token::Position; @@ -19,7 +19,7 @@ use crate::{ #[cfg(not(feature = "no_object"))] use crate::{ engine::{make_getter, make_setter, Map}, - error::ParseErrorType, + parse_error::ParseErrorType, token::Token, }; diff --git a/src/settings.rs b/src/engine_settings.rs similarity index 100% rename from src/settings.rs rename to src/engine_settings.rs diff --git a/src/fn_args.rs b/src/fn_args.rs index 07405c0f..db0f3cd4 100644 --- a/src/fn_args.rs +++ b/src/fn_args.rs @@ -2,7 +2,7 @@ #![allow(non_snake_case)] -use crate::any::{Dynamic, Variant}; +use crate::dynamic::{Dynamic, Variant}; use crate::StaticVec; /// Trait that represents arguments to a function call. diff --git a/src/fn_call.rs b/src/fn_call.rs index 531a9284..5cb9d7d5 100644 --- a/src/fn_call.rs +++ b/src/fn_call.rs @@ -1,21 +1,21 @@ //! Implement function-calling mechanism for `Engine`. -use crate::any::Dynamic; +use crate::dynamic::Dynamic; use crate::engine::{ search_imports, Engine, Imports, State, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_FN_PTR_CALL, KEYWORD_FN_PTR_CURRY, KEYWORD_IS_DEF_FN, KEYWORD_IS_DEF_VAR, KEYWORD_PRINT, KEYWORD_TYPE_OF, }; -use crate::error::ParseErrorType; use crate::fn_native::{FnCallArgs, FnPtr}; use crate::module::{Module, ModuleRef}; use crate::optimize::OptimizationLevel; +use crate::parse_error::ParseErrorType; use crate::parser::{Expr, ImmutableString, Stmt, INT}; use crate::result::EvalAltResult; use crate::scope::Scope; use crate::stdlib::ops::Deref; use crate::token::Position; -use crate::{calc_fn_hash, StaticVec}; +use crate::{calc_native_fn_hash, calc_script_fn_hash, StaticVec}; #[cfg(not(feature = "no_function"))] use crate::{ @@ -439,15 +439,8 @@ impl Engine { pub_only: bool, ) -> bool { let arg_types = arg_types.as_ref(); - - let arg_len = if arg_types.is_empty() { - usize::MAX - } else { - arg_types.len() - }; - - let hash_fn = calc_fn_hash(empty(), name, arg_len, arg_types.iter().cloned()); - let hash_script = calc_fn_hash(empty(), name, arg_types.len(), empty()); + let hash_fn = calc_native_fn_hash(empty(), name, arg_types.iter().cloned()); + let hash_script = calc_script_fn_hash(empty(), name, arg_types.len()); self.has_override(lib, hash_fn, hash_script, pub_only) } @@ -503,17 +496,7 @@ impl Engine { // Qualifiers (none) + function name + number of arguments + argument `TypeId`'s. let arg_types = args.iter().map(|a| a.type_id()); - let hash_fn = calc_fn_hash( - empty(), - fn_name, - if args.is_empty() { - // Distinguish between a script function and a native function with no parameters - usize::MAX - } else { - args.len() - }, - arg_types, - ); + let hash_fn = calc_native_fn_hash(empty(), fn_name, arg_types); match fn_name { // type_of @@ -741,7 +724,7 @@ impl Engine { let hash = if native { 0 } else { - calc_fn_hash(empty(), fn_name, args_len, empty()) + calc_script_fn_hash(empty(), fn_name, args_len) }; // Arguments are passed as-is, adding the curried arguments let mut curry = fn_ptr.curry().iter().cloned().collect::>(); @@ -768,7 +751,7 @@ impl Engine { let hash = if native { 0 } else { - calc_fn_hash(empty(), fn_name, args_len, empty()) + calc_script_fn_hash(empty(), fn_name, args_len) }; // Replace the first argument with the object pointer, adding the curried arguments let mut curry = fn_ptr.curry().iter().cloned().collect::>(); @@ -831,7 +814,7 @@ impl Engine { hash = if native { 0 } else { - calc_fn_hash(empty(), _fn_name, call_args.len(), empty()) + calc_script_fn_hash(empty(), _fn_name, call_args.len()) }; } } @@ -882,7 +865,7 @@ impl Engine { // Handle Fn() if name == KEYWORD_FN_PTR && args_expr.len() == 1 { - let hash_fn = calc_fn_hash(empty(), name, 1, once(TypeId::of::())); + let hash_fn = calc_native_fn_hash(empty(), name, once(TypeId::of::())); if !self.has_override(lib, hash_fn, hash_script, pub_only) { // Fn - only in function call style @@ -963,12 +946,12 @@ impl Engine { // Recalculate hash let args_len = args_expr.len() + curry.len(); - hash_script = calc_fn_hash(empty(), name, args_len, empty()); + hash_script = calc_script_fn_hash(empty(), name, args_len); } // Handle is_def_var() if name == KEYWORD_IS_DEF_VAR && args_expr.len() == 1 { - let hash_fn = calc_fn_hash(empty(), name, 1, once(TypeId::of::())); + let hash_fn = calc_native_fn_hash(empty(), name, once(TypeId::of::())); if !self.has_override(lib, hash_fn, hash_script, pub_only) { let var_name = @@ -982,10 +965,9 @@ impl Engine { // Handle is_def_fn() if name == KEYWORD_IS_DEF_FN && args_expr.len() == 2 { - let hash_fn = calc_fn_hash( + let hash_fn = calc_native_fn_hash( empty(), name, - 2, [TypeId::of::(), TypeId::of::()] .iter() .cloned(), @@ -1007,7 +989,7 @@ impl Engine { return Ok(if num_params < 0 { false } else { - let hash = calc_fn_hash(empty(), fn_name, num_params as usize, empty()); + let hash = calc_script_fn_hash(empty(), fn_name, num_params as usize); lib.iter().any(|&m| m.contains_fn(hash, false)) } .into()); @@ -1016,7 +998,7 @@ impl Engine { // Handle eval() if name == KEYWORD_EVAL && args_expr.len() == 1 { - let hash_fn = calc_fn_hash(empty(), name, 1, once(TypeId::of::())); + let hash_fn = calc_native_fn_hash(empty(), name, once(TypeId::of::())); if !self.has_override(lib, hash_fn, hash_script, pub_only) { // eval - only in function call style @@ -1185,8 +1167,9 @@ impl Engine { // 1) Calculate a hash in a similar manner to script-defined functions, // i.e. qualifiers + function name + number of arguments. // 2) Calculate a second hash with no qualifiers, empty function name, - // zero number of arguments, and the actual list of argument `TypeId`'.s - let hash_fn_args = calc_fn_hash(empty(), "", 0, args.iter().map(|a| a.type_id())); + // and the actual list of argument `TypeId`'.s + let hash_fn_args = + calc_native_fn_hash(empty(), "", args.iter().map(|a| a.type_id())); // 3) The final hash is the XOR of the two hashes. let hash_qualified_fn = hash_script ^ hash_fn_args; diff --git a/src/fn_func.rs b/src/fn_func.rs index 27e73bb0..888043c9 100644 --- a/src/fn_func.rs +++ b/src/fn_func.rs @@ -3,10 +3,10 @@ #![cfg(not(feature = "no_function"))] #![allow(non_snake_case)] -use crate::any::Variant; +use crate::ast::AST; +use crate::dynamic::Variant; use crate::engine::Engine; -use crate::error::ParseError; -use crate::parser::AST; +use crate::parse_error::ParseError; use crate::result::EvalAltResult; use crate::scope::Scope; diff --git a/src/fn_native.rs b/src/fn_native.rs index b3758e88..9eebffbe 100644 --- a/src/fn_native.rs +++ b/src/fn_native.rs @@ -1,6 +1,6 @@ //! Module defining interfaces to native-Rust functions. -use crate::any::Dynamic; +use crate::dynamic::Dynamic; use crate::engine::{Engine, EvalContext}; use crate::module::Module; use crate::parser::{FnAccess, ScriptFnDef}; @@ -8,7 +8,7 @@ use crate::plugin::PluginFunction; use crate::result::EvalAltResult; use crate::token::{is_valid_identifier, Position}; use crate::utils::ImmutableString; -use crate::{calc_fn_hash, StaticVec}; +use crate::{calc_script_fn_hash, StaticVec}; #[cfg(not(feature = "no_function"))] use crate::engine::FN_ANONYMOUS; @@ -176,7 +176,7 @@ impl FnPtr { let has_this = this_ptr.is_some(); let mut args = args_data.iter_mut().collect::>(); - let hash_script = calc_fn_hash(empty(), fn_name, args.len(), empty()); + let hash_script = calc_script_fn_hash(empty(), fn_name, args.len()); if let Some(obj) = this_ptr { args.insert(0, obj); diff --git a/src/fn_register.rs b/src/fn_register.rs index f394b348..2f0684f1 100644 --- a/src/fn_register.rs +++ b/src/fn_register.rs @@ -2,7 +2,7 @@ #![allow(non_snake_case)] -use crate::any::{Dynamic, DynamicWriteLock, Variant}; +use crate::dynamic::{Dynamic, DynamicWriteLock, Variant}; use crate::engine::Engine; use crate::fn_native::{CallableFunction, FnAny, FnCallArgs, NativeCallContext, SendSync}; use crate::parser::FnAccess; diff --git a/src/lib.rs b/src/lib.rs index d1896d9f..018a0138 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -57,10 +57,11 @@ #[cfg(feature = "no_std")] extern crate alloc; -mod any; -mod api; +mod ast; +mod dynamic; mod engine; -mod error; +mod engine_api; +mod engine_settings; mod fn_args; mod fn_call; mod fn_func; @@ -69,36 +70,37 @@ mod fn_register; mod module; mod optimize; pub mod packages; +mod parse_error; mod parser; pub mod plugin; mod result; mod scope; #[cfg(feature = "serde")] mod serde_impl; -mod settings; mod stdlib; mod syntax; mod token; mod r#unsafe; mod utils; -pub use any::Dynamic; +pub use ast::AST; +pub use dynamic::Dynamic; pub use engine::{Engine, EvalContext}; -pub use error::{ParseError, ParseErrorType}; pub use fn_native::{FnPtr, NativeCallContext}; pub use fn_register::{RegisterFn, RegisterResultFn}; pub use module::Module; -pub use parser::{ImmutableString, AST, INT}; +pub use parse_error::{ParseError, ParseErrorType}; +pub use parser::{ImmutableString, INT}; pub use result::EvalAltResult; pub use scope::Scope; pub use syntax::Expression; pub use token::Position; #[cfg(feature = "internals")] -pub use utils::calc_fn_hash; +pub use utils::{calc_native_fn_hash, calc_script_fn_hash}; #[cfg(not(feature = "internals"))] -pub(crate) use utils::calc_fn_hash; +pub(crate) use utils::{calc_native_fn_hash, calc_script_fn_hash}; pub use rhai_codegen::*; @@ -141,7 +143,7 @@ pub use optimize::OptimizationLevel; #[cfg(feature = "internals")] #[deprecated(note = "this type is volatile and may change")] -pub use error::LexError; +pub use parse_error::LexError; #[cfg(feature = "internals")] #[deprecated(note = "this type is volatile and may change")] diff --git a/src/module/mod.rs b/src/module/mod.rs index 176441e8..21050777 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -1,21 +1,21 @@ //! Module defining external-loaded modules for Rhai. -use crate::any::{Dynamic, Variant}; +use crate::dynamic::{Dynamic, Variant}; use crate::fn_native::{CallableFunction, FnCallArgs, IteratorFn, NativeCallContext, SendSync}; use crate::fn_register::by_value as cast_arg; use crate::parser::FnAccess; use crate::result::EvalAltResult; use crate::token::{Position, Token}; use crate::utils::{ImmutableString, StraightHasherBuilder}; -use crate::{calc_fn_hash, StaticVec}; +use crate::{calc_native_fn_hash, calc_script_fn_hash, StaticVec}; #[cfg(not(feature = "no_function"))] use crate::{fn_native::Shared, parser::ScriptFnDef}; #[cfg(not(feature = "no_module"))] use crate::{ + ast::AST, engine::{Engine, Imports}, - parser::AST, scope::{Entry as ScopeEntry, Scope}, }; @@ -268,7 +268,7 @@ impl Module { /// Get a mutable reference to a modules-qualified variable. /// Name and Position in `EvalAltResult` are None and must be set afterwards. /// - /// The `u64` hash is calculated by the function `crate::calc_fn_hash`. + /// The `u64` hash is calculated by the function `crate::calc_native_fn_hash`. #[inline(always)] pub(crate) fn get_qualified_var_mut( &mut self, @@ -291,7 +291,7 @@ impl Module { pub(crate) fn set_script_fn(&mut self, fn_def: Shared) -> u64 { // None + function name + number of arguments. let num_params = fn_def.params.len(); - let hash_script = calc_fn_hash(empty(), &fn_def.name, num_params, empty()); + let hash_script = calc_script_fn_hash(empty(), &fn_def.name, num_params); self.functions.insert( hash_script, ( @@ -399,7 +399,7 @@ impl Module { /// Does the particular Rust function exist in the module? /// - /// The `u64` hash is calculated by the function `crate::calc_fn_hash`. + /// The `u64` hash is calculated by the function `crate::calc_native_fn_hash`. /// It is also returned by the `set_fn_XXX` calls. /// /// # Example @@ -441,31 +441,24 @@ impl Module { ) -> u64 { let name = name.into(); - let args_len = if arg_types.is_empty() { - // Distinguish between a script function and a function with no parameters - usize::MAX - } else { - arg_types.len() - }; + let hash_fn = calc_native_fn_hash(empty(), &name, arg_types.iter().cloned()); let params = arg_types .into_iter() .cloned() .map(|id| { - if id == TypeId::of::<&str>() { - TypeId::of::() - } else if id == TypeId::of::() { + if id == TypeId::of::<&str>() || id == TypeId::of::() { TypeId::of::() } else { id } }) - .collect(); + .collect::>(); - let hash_fn = calc_fn_hash(empty(), &name, args_len, arg_types.iter().cloned()); - - self.functions - .insert(hash_fn, (name, access, args_len, Some(params), func.into())); + self.functions.insert( + hash_fn, + (name, access, params.len(), Some(params), func.into()), + ); self.indexed = false; @@ -1094,7 +1087,7 @@ impl Module { /// Get a Rust function. /// - /// The `u64` hash is calculated by the function `crate::calc_fn_hash`. + /// The `u64` hash is calculated by the function `crate::calc_native_fn_hash`. /// It is also returned by the `set_fn_XXX` calls. #[inline(always)] pub(crate) fn get_fn(&self, hash_fn: u64, public_only: bool) -> Option<&CallableFunction> { @@ -1114,7 +1107,7 @@ impl Module { /// Get a modules-qualified function. /// Name and Position in `EvalAltResult` are None and must be set afterwards. /// - /// The `u64` hash is calculated by the function `crate::calc_fn_hash` and must match + /// The `u64` hash is calculated by the function `crate::calc_native_fn_hash` and must match /// the hash calculated by `index_all_sub_modules`. #[inline(always)] pub(crate) fn get_qualified_fn(&self, hash_qualified_fn: u64) -> Option<&CallableFunction> { @@ -1408,7 +1401,7 @@ impl Module { // Index all variables module.variables.iter().for_each(|(var_name, value)| { // Qualifiers + variable name - let hash_var = calc_fn_hash(qualifiers.iter().map(|&v| v), var_name, 0, empty()); + let hash_var = calc_script_fn_hash(qualifiers.iter().map(|&v| v), var_name, 0); variables.push((hash_var, value.clone())); }); // Index all Rust functions @@ -1422,10 +1415,10 @@ impl Module { // 1) Calculate a hash in a similar manner to script-defined functions, // i.e. qualifiers + function name + number of arguments. let hash_qualified_script = - calc_fn_hash(qualifiers.iter().cloned(), name, params.len(), empty()); + calc_script_fn_hash(qualifiers.iter().cloned(), name, params.len()); // 2) Calculate a second hash with no qualifiers, empty function name, - // zero number of arguments, and the actual list of argument `TypeId`'.s - let hash_fn_args = calc_fn_hash(empty(), "", 0, params.iter().cloned()); + // and the actual list of argument `TypeId`'.s + let hash_fn_args = calc_native_fn_hash(empty(), "", params.iter().cloned()); // 3) The final hash is the XOR of the two hashes. let hash_qualified_fn = hash_qualified_script ^ hash_fn_args; @@ -1435,12 +1428,7 @@ impl Module { _hash } else { // Qualifiers + function name + number of arguments. - calc_fn_hash( - qualifiers.iter().map(|&v| v), - &name, - *_num_params, - empty(), - ) + calc_script_fn_hash(qualifiers.iter().map(|&v| v), &name, *_num_params) }; functions.push((hash_qualified_script, func.clone())); } diff --git a/src/module/resolvers/file.rs b/src/module/resolvers/file.rs index 73640768..a77bcc57 100644 --- a/src/module/resolvers/file.rs +++ b/src/module/resolvers/file.rs @@ -1,7 +1,7 @@ +use crate::ast::AST; use crate::engine::Engine; use crate::fn_native::Locked; use crate::module::{Module, ModuleResolver}; -use crate::parser::AST; use crate::result::EvalAltResult; use crate::token::Position; diff --git a/src/optimize.rs b/src/optimize.rs index de031742..a9433a3d 100644 --- a/src/optimize.rs +++ b/src/optimize.rs @@ -1,16 +1,17 @@ //! Module implementing the AST optimizer. -use crate::any::Dynamic; +use crate::ast::AST; +use crate::dynamic::Dynamic; use crate::engine::{ Engine, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_IS_DEF_FN, KEYWORD_IS_DEF_VAR, KEYWORD_PRINT, KEYWORD_TYPE_OF, }; use crate::fn_call::run_builtin_binary_op; use crate::module::Module; -use crate::parser::{map_dynamic_to_expr, BinaryExpr, CustomExpr, Expr, ScriptFnDef, Stmt, AST}; +use crate::parser::{map_dynamic_to_expr, BinaryExpr, CustomExpr, Expr, ScriptFnDef, Stmt}; use crate::scope::{Entry as ScopeEntry, Scope}; use crate::token::{is_valid_identifier, Position}; -use crate::{calc_fn_hash, StaticVec}; +use crate::{calc_native_fn_hash, StaticVec}; #[cfg(not(feature = "no_function"))] use crate::parser::ReturnType; @@ -134,12 +135,7 @@ fn call_fn_with_constant_arguments( arg_values: &mut [Dynamic], ) -> Option { // Search built-in's and external functions - let hash_fn = calc_fn_hash( - empty(), - fn_name, - arg_values.len(), - arg_values.iter().map(|a| a.type_id()), - ); + let hash_fn = calc_native_fn_hash(empty(), fn_name, arg_values.iter().map(|a| a.type_id())); state .engine diff --git a/src/packages/array_basic.rs b/src/packages/array_basic.rs index 77ef56e7..2bee4b0b 100644 --- a/src/packages/array_basic.rs +++ b/src/packages/array_basic.rs @@ -1,7 +1,7 @@ #![cfg(not(feature = "no_index"))] #![allow(non_snake_case)] -use crate::any::Dynamic; +use crate::dynamic::Dynamic; use crate::def_package; use crate::engine::Array; use crate::fn_native::{FnPtr, NativeCallContext}; diff --git a/src/packages/eval.rs b/src/packages/eval.rs index bb5716d8..312b7f02 100644 --- a/src/packages/eval.rs +++ b/src/packages/eval.rs @@ -1,4 +1,4 @@ -use crate::any::Dynamic; +use crate::dynamic::Dynamic; use crate::def_package; use crate::parser::ImmutableString; use crate::plugin::*; diff --git a/src/packages/iter_basic.rs b/src/packages/iter_basic.rs index 0d8d1e8f..d62963f4 100644 --- a/src/packages/iter_basic.rs +++ b/src/packages/iter_basic.rs @@ -1,4 +1,4 @@ -use crate::any::Variant; +use crate::dynamic::Variant; use crate::def_package; use crate::parser::INT; use crate::result::EvalAltResult; diff --git a/src/packages/map_basic.rs b/src/packages/map_basic.rs index 26fc3fe3..ae645fce 100644 --- a/src/packages/map_basic.rs +++ b/src/packages/map_basic.rs @@ -1,7 +1,7 @@ #![cfg(not(feature = "no_object"))] -use crate::any::Dynamic; use crate::def_package; +use crate::dynamic::Dynamic; use crate::engine::Map; use crate::parser::{ImmutableString, INT}; use crate::plugin::*; diff --git a/src/packages/string_more.rs b/src/packages/string_more.rs index 1d515558..9130a9d3 100644 --- a/src/packages/string_more.rs +++ b/src/packages/string_more.rs @@ -1,7 +1,7 @@ #![allow(non_snake_case)] -use crate::any::Dynamic; use crate::def_package; +use crate::dynamic::Dynamic; use crate::fn_native::FnPtr; use crate::parser::{ImmutableString, INT}; use crate::plugin::*; diff --git a/src/packages/time_basic.rs b/src/packages/time_basic.rs index 65b3ffaf..706233e2 100644 --- a/src/packages/time_basic.rs +++ b/src/packages/time_basic.rs @@ -2,8 +2,8 @@ use super::{arithmetic::make_err as make_arithmetic_err, math_basic::MAX_INT}; -use crate::any::Dynamic; use crate::def_package; +use crate::dynamic::Dynamic; use crate::parser::INT; use crate::plugin::*; use crate::result::EvalAltResult; diff --git a/src/error.rs b/src/parse_error.rs similarity index 100% rename from src/error.rs rename to src/parse_error.rs diff --git a/src/parser.rs b/src/parser.rs index fe9f9119..828c8c43 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1,16 +1,17 @@ //! Main module defining the lexer and parser. -use crate::any::{Dynamic, Union}; +use crate::ast::AST; +use crate::dynamic::{Dynamic, Union}; use crate::engine::{Engine, KEYWORD_THIS, MARKER_BLOCK, MARKER_EXPR, MARKER_IDENT}; -use crate::error::{LexError, ParseError, ParseErrorType}; use crate::fn_native::{FnPtr, Shared}; use crate::module::{Module, ModuleRef}; use crate::optimize::{optimize_into_ast, OptimizationLevel}; +use crate::parse_error::{LexError, ParseError, ParseErrorType}; use crate::scope::{EntryType as ScopeEntryType, Scope}; use crate::syntax::{CustomSyntax, FnCustomSyntaxEval}; use crate::token::{is_keyword_function, is_valid_identifier, Position, Token, TokenStream}; use crate::utils::StraightHasherBuilder; -use crate::{calc_fn_hash, StaticVec}; +use crate::{calc_script_fn_hash, StaticVec}; #[cfg(not(feature = "no_index"))] use crate::engine::Array; @@ -31,7 +32,6 @@ use crate::stdlib::{ hash::{Hash, Hasher}, iter::empty, num::NonZeroUsize, - ops::{Add, AddAssign}, string::{String, ToString}, vec, vec::Vec, @@ -72,422 +72,6 @@ pub use crate::utils::ImmutableString; type FunctionsLib = HashMap; -/// Compiled AST (abstract syntax tree) of a Rhai script. -/// -/// # Thread Safety -/// -/// Currently, `AST` is neither `Send` nor `Sync`. Turn on the `sync` feature to make it `Send + Sync`. -#[derive(Debug, Clone, Default)] -pub struct AST( - /// Global statements. - Vec, - /// Script-defined functions. - Module, -); - -impl AST { - /// Create a new `AST`. - #[inline(always)] - pub fn new(statements: Vec, lib: Module) -> Self { - Self(statements, lib) - } - - /// Get the statements. - #[cfg(not(feature = "internals"))] - #[inline(always)] - pub(crate) fn statements(&self) -> &[Stmt] { - &self.0 - } - - /// _[INTERNALS]_ Get the statements. - /// Exported under the `internals` feature only. - #[cfg(feature = "internals")] - #[deprecated(note = "this method is volatile and may change")] - #[inline(always)] - pub fn statements(&self) -> &[Stmt] { - &self.0 - } - - /// Get a mutable reference to the statements. - #[cfg(not(feature = "no_optimize"))] - #[inline(always)] - pub(crate) fn statements_mut(&mut self) -> &mut Vec { - &mut self.0 - } - - /// Get the internal `Module` containing all script-defined functions. - #[cfg(not(feature = "internals"))] - #[inline(always)] - pub(crate) fn lib(&self) -> &Module { - &self.1 - } - - /// _[INTERNALS]_ Get the internal `Module` containing all script-defined functions. - /// Exported under the `internals` feature only. - #[cfg(feature = "internals")] - #[deprecated(note = "this method is volatile and may change")] - #[inline(always)] - pub fn lib(&self) -> &Module { - &self.1 - } - - /// Clone the `AST`'s functions into a new `AST`. - /// No statements are cloned. - /// - /// This operation is cheap because functions are shared. - #[inline(always)] - pub fn clone_functions_only(&self) -> Self { - self.clone_functions_only_filtered(|_, _, _| true) - } - - /// Clone the `AST`'s functions into a new `AST` based on a filter predicate. - /// No statements are cloned. - /// - /// This operation is cheap because functions are shared. - #[inline(always)] - pub fn clone_functions_only_filtered( - &self, - mut filter: impl FnMut(FnAccess, &str, usize) -> bool, - ) -> Self { - let mut functions: Module = Default::default(); - functions.merge_filtered(&self.1, &mut filter); - Self(Default::default(), functions) - } - - /// Clone the `AST`'s script statements into a new `AST`. - /// No functions are cloned. - #[inline(always)] - pub fn clone_statements_only(&self) -> Self { - Self(self.0.clone(), Default::default()) - } - - /// Merge two `AST` into one. Both `AST`'s are untouched and a new, merged, version - /// is returned. - /// - /// Statements in the second `AST` are simply appended to the end of the first _without any processing_. - /// Thus, the return value of the first `AST` (if using expression-statement syntax) is buried. - /// Of course, if the first `AST` uses a `return` statement at the end, then - /// the second `AST` will essentially be dead code. - /// - /// All script-defined functions in the second `AST` overwrite similarly-named functions - /// in the first `AST` with the same number of parameters. - /// - /// # Example - /// - /// ``` - /// # fn main() -> Result<(), Box> { - /// # #[cfg(not(feature = "no_function"))] - /// # { - /// use rhai::Engine; - /// - /// let engine = Engine::new(); - /// - /// let ast1 = engine.compile(r#" - /// fn foo(x) { 42 + x } - /// foo(1) - /// "#)?; - /// - /// let ast2 = engine.compile(r#" - /// fn foo(n) { "hello" + n } - /// foo("!") - /// "#)?; - /// - /// let ast = ast1.merge(&ast2); // Merge 'ast2' into 'ast1' - /// - /// // Notice that using the '+' operator also works: - /// // let ast = &ast1 + &ast2; - /// - /// // 'ast' is essentially: - /// // - /// // fn foo(n) { "hello" + n } // <- definition of first 'foo' is overwritten - /// // foo(1) // <- notice this will be "hello1" instead of 43, - /// // // but it is no longer the return value - /// // foo("!") // returns "hello!" - /// - /// // Evaluate it - /// assert_eq!(engine.eval_ast::(&ast)?, "hello!"); - /// # } - /// # Ok(()) - /// # } - /// ``` - #[inline(always)] - pub fn merge(&self, other: &Self) -> Self { - self.merge_filtered(other, |_, _, _| true) - } - - /// Combine one `AST` with another. The second `AST` is consumed. - /// - /// Statements in the second `AST` are simply appended to the end of the first _without any processing_. - /// Thus, the return value of the first `AST` (if using expression-statement syntax) is buried. - /// Of course, if the first `AST` uses a `return` statement at the end, then - /// the second `AST` will essentially be dead code. - /// - /// All script-defined functions in the second `AST` overwrite similarly-named functions - /// in the first `AST` with the same number of parameters. - /// - /// # Example - /// - /// ``` - /// # fn main() -> Result<(), Box> { - /// # #[cfg(not(feature = "no_function"))] - /// # { - /// use rhai::Engine; - /// - /// let engine = Engine::new(); - /// - /// let mut ast1 = engine.compile(r#" - /// fn foo(x) { 42 + x } - /// foo(1) - /// "#)?; - /// - /// let ast2 = engine.compile(r#" - /// fn foo(n) { "hello" + n } - /// foo("!") - /// "#)?; - /// - /// ast1.combine(ast2); // Combine 'ast2' into 'ast1' - /// - /// // Notice that using the '+=' operator also works: - /// // ast1 += ast2; - /// - /// // 'ast1' is essentially: - /// // - /// // fn foo(n) { "hello" + n } // <- definition of first 'foo' is overwritten - /// // foo(1) // <- notice this will be "hello1" instead of 43, - /// // // but it is no longer the return value - /// // foo("!") // returns "hello!" - /// - /// // Evaluate it - /// assert_eq!(engine.eval_ast::(&ast1)?, "hello!"); - /// # } - /// # Ok(()) - /// # } - /// ``` - #[inline(always)] - pub fn combine(&mut self, other: Self) -> &mut Self { - self.combine_filtered(other, |_, _, _| true) - } - - /// Merge two `AST` into one. Both `AST`'s are untouched and a new, merged, version - /// is returned. - /// - /// Statements in the second `AST` are simply appended to the end of the first _without any processing_. - /// Thus, the return value of the first `AST` (if using expression-statement syntax) is buried. - /// Of course, if the first `AST` uses a `return` statement at the end, then - /// the second `AST` will essentially be dead code. - /// - /// All script-defined functions in the second `AST` are first selected based on a filter - /// predicate, then overwrite similarly-named functions in the first `AST` with the - /// same number of parameters. - /// - /// # Example - /// - /// ``` - /// # fn main() -> Result<(), Box> { - /// # #[cfg(not(feature = "no_function"))] - /// # { - /// use rhai::Engine; - /// - /// let engine = Engine::new(); - /// - /// let ast1 = engine.compile(r#" - /// fn foo(x) { 42 + x } - /// foo(1) - /// "#)?; - /// - /// let ast2 = engine.compile(r#" - /// fn foo(n) { "hello" + n } - /// fn error() { 0 } - /// foo("!") - /// "#)?; - /// - /// // Merge 'ast2', picking only 'error()' but not 'foo(_)', into 'ast1' - /// let ast = ast1.merge_filtered(&ast2, |_, name, params| name == "error" && params == 0); - /// - /// // 'ast' is essentially: - /// // - /// // fn foo(n) { 42 + n } // <- definition of 'ast1::foo' is not overwritten - /// // // because 'ast2::foo' is filtered away - /// // foo(1) // <- notice this will be 43 instead of "hello1", - /// // // but it is no longer the return value - /// // fn error() { 0 } // <- this function passes the filter and is merged - /// // foo("!") // <- returns "42!" - /// - /// // Evaluate it - /// assert_eq!(engine.eval_ast::(&ast)?, "42!"); - /// # } - /// # Ok(()) - /// # } - /// ``` - #[inline] - pub fn merge_filtered( - &self, - other: &Self, - mut filter: impl FnMut(FnAccess, &str, usize) -> bool, - ) -> Self { - let Self(statements, functions) = self; - - let ast = match (statements.is_empty(), other.0.is_empty()) { - (false, false) => { - let mut statements = statements.clone(); - statements.extend(other.0.iter().cloned()); - statements - } - (false, true) => statements.clone(), - (true, false) => other.0.clone(), - (true, true) => vec![], - }; - - let mut functions = functions.clone(); - functions.merge_filtered(&other.1, &mut filter); - - Self::new(ast, functions) - } - - /// Combine one `AST` with another. The second `AST` is consumed. - /// - /// Statements in the second `AST` are simply appended to the end of the first _without any processing_. - /// Thus, the return value of the first `AST` (if using expression-statement syntax) is buried. - /// Of course, if the first `AST` uses a `return` statement at the end, then - /// the second `AST` will essentially be dead code. - /// - /// All script-defined functions in the second `AST` are first selected based on a filter - /// predicate, then overwrite similarly-named functions in the first `AST` with the - /// same number of parameters. - /// - /// # Example - /// - /// ``` - /// # fn main() -> Result<(), Box> { - /// # #[cfg(not(feature = "no_function"))] - /// # { - /// use rhai::Engine; - /// - /// let engine = Engine::new(); - /// - /// let mut ast1 = engine.compile(r#" - /// fn foo(x) { 42 + x } - /// foo(1) - /// "#)?; - /// - /// let ast2 = engine.compile(r#" - /// fn foo(n) { "hello" + n } - /// fn error() { 0 } - /// foo("!") - /// "#)?; - /// - /// // Combine 'ast2', picking only 'error()' but not 'foo(_)', into 'ast1' - /// ast1.combine_filtered(ast2, |_, name, params| name == "error" && params == 0); - /// - /// // 'ast1' is essentially: - /// // - /// // fn foo(n) { 42 + n } // <- definition of 'ast1::foo' is not overwritten - /// // // because 'ast2::foo' is filtered away - /// // foo(1) // <- notice this will be 43 instead of "hello1", - /// // // but it is no longer the return value - /// // fn error() { 0 } // <- this function passes the filter and is merged - /// // foo("!") // <- returns "42!" - /// - /// // Evaluate it - /// assert_eq!(engine.eval_ast::(&ast1)?, "42!"); - /// # } - /// # Ok(()) - /// # } - /// ``` - #[inline(always)] - pub fn combine_filtered( - &mut self, - other: Self, - mut filter: impl FnMut(FnAccess, &str, usize) -> bool, - ) -> &mut Self { - let Self(ref mut statements, ref mut functions) = self; - statements.extend(other.0.into_iter()); - functions.merge_filtered(&other.1, &mut filter); - self - } - - /// Filter out the functions, retaining only some based on a filter predicate. - /// - /// # Example - /// - /// ``` - /// # fn main() -> Result<(), Box> { - /// # #[cfg(not(feature = "no_function"))] - /// # { - /// use rhai::Engine; - /// - /// let engine = Engine::new(); - /// - /// let mut ast = engine.compile(r#" - /// fn foo(n) { n + 1 } - /// fn bar() { print("hello"); } - /// "#)?; - /// - /// // Remove all functions except 'foo(_)' - /// ast.retain_functions(|_, name, params| name == "foo" && params == 1); - /// # } - /// # Ok(()) - /// # } - /// ``` - #[cfg(not(feature = "no_function"))] - #[inline(always)] - pub fn retain_functions(&mut self, filter: impl FnMut(FnAccess, &str, usize) -> bool) { - self.1.retain_functions(filter); - } - - /// Iterate through all functions - #[cfg(not(feature = "no_function"))] - #[inline(always)] - pub fn iter_functions<'a>( - &'a self, - ) -> impl Iterator)> + 'a { - self.1.iter_script_fn() - } - - /// Clear all function definitions in the `AST`. - #[cfg(not(feature = "no_function"))] - #[inline(always)] - pub fn clear_functions(&mut self) { - self.1 = Default::default(); - } - - /// Clear all statements in the `AST`, leaving only function definitions. - #[inline(always)] - pub fn clear_statements(&mut self) { - self.0 = vec![]; - } -} - -impl> Add for &AST { - type Output = AST; - - #[inline(always)] - fn add(self, rhs: A) -> Self::Output { - self.merge(rhs.as_ref()) - } -} - -impl> AddAssign for AST { - #[inline(always)] - fn add_assign(&mut self, rhs: A) { - self.combine(rhs.into()); - } -} - -impl AsRef<[Stmt]> for AST { - #[inline(always)] - fn as_ref(&self) -> &[Stmt] { - self.statements() - } -} - -impl AsRef for AST { - #[inline(always)] - fn as_ref(&self) -> &Module { - self.lib() - } -} - /// A type representing the access mode of a scripted function. #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] pub enum FnAccess { @@ -556,9 +140,10 @@ impl fmt::Display for ScriptFnDef { write!( f, "{}{}({})", - match self.access { - FnAccess::Public => "", - FnAccess::Private => "private ", + if self.access.is_private() { + "private " + } else { + "" }, self.name, self.params @@ -1488,10 +1073,10 @@ fn parse_fn_call( // zero number of arguments, and the actual list of argument `TypeId`'s. // 3) The final hash is the XOR of the two hashes. let qualifiers = modules.iter().map(|(m, _)| m.as_str()); - calc_fn_hash(qualifiers, &id, 0, empty()) + calc_script_fn_hash(qualifiers, &id, 0) } else { // Qualifiers (none) + function name + no parameters. - calc_fn_hash(empty(), &id, 0, empty()) + calc_script_fn_hash(empty(), &id, 0) }; return Ok(Expr::FnCall(Box::new(( @@ -1531,10 +1116,10 @@ fn parse_fn_call( // zero number of arguments, and the actual list of argument `TypeId`'s. // 3) The final hash is the XOR of the two hashes. let qualifiers = modules.iter().map(|(m, _)| m.as_str()); - calc_fn_hash(qualifiers, &id, args.len(), empty()) + calc_script_fn_hash(qualifiers, &id, args.len()) } else { // Qualifiers (none) + function name + number of arguments. - calc_fn_hash(empty(), &id, args.len(), empty()) + calc_script_fn_hash(empty(), &id, args.len()) }; return Ok(Expr::FnCall(Box::new(( @@ -2108,7 +1693,7 @@ fn parse_primary( let modules = modules.as_mut().unwrap(); // Qualifiers + variable name - *hash = calc_fn_hash(modules.iter().map(|(v, _)| v.as_str()), name, 0, empty()); + *hash = calc_script_fn_hash(modules.iter().map(|(v, _)| v.as_str()), name, 0); #[cfg(not(feature = "no_module"))] modules.set_index(state.find_module(&modules[0].0)); @@ -2171,7 +1756,7 @@ fn parse_unary( // Call negative function expr => { let op = "-"; - let hash = calc_fn_hash(empty(), op, 1, empty()); + let hash = calc_script_fn_hash(empty(), op, 1); let mut args = StaticVec::new(); args.push(expr); @@ -2198,7 +1783,7 @@ fn parse_unary( args.push(expr); let op = "!"; - let hash = calc_fn_hash(empty(), op, 1, empty()); + let hash = calc_script_fn_hash(empty(), op, 1); Ok(Expr::FnCall(Box::new(( (op.into(), true, false, pos), @@ -2238,7 +1823,7 @@ fn parse_unary( }); // Qualifiers (none) + function name + number of arguments. - let hash = calc_fn_hash(empty(), &func.name, func.params.len(), empty()); + let hash = calc_script_fn_hash(empty(), &func.name, func.params.len()); lib.insert(hash, func); @@ -2671,7 +2256,7 @@ fn parse_binary_op( let cmp_def = Some(false); let op = op_token.syntax(); - let hash = calc_fn_hash(empty(), &op, 2, empty()); + let hash = calc_script_fn_hash(empty(), &op, 2); let op = (op, true, false, pos); let mut args = StaticVec::new(); @@ -3377,7 +2962,7 @@ fn parse_stmt( let func = parse_fn(input, &mut new_state, lib, access, settings)?; // Qualifiers (none) + function name + number of arguments. - let hash = calc_fn_hash(empty(), &func.name, func.params.len(), empty()); + let hash = calc_script_fn_hash(empty(), &func.name, func.params.len()); lib.insert(hash, func); @@ -3654,7 +3239,7 @@ fn make_curry_from_externals(fn_expr: Expr, externals: StaticVec, pos: Po args.push(Expr::Variable(Box::new((x.clone(), None, 0, None)))); }); - let hash = calc_fn_hash(empty(), KEYWORD_FN_PTR_CURRY, num_externals + 1, empty()); + let hash = calc_script_fn_hash(empty(), KEYWORD_FN_PTR_CURRY, num_externals + 1); let expr = Expr::FnCall(Box::new(( (KEYWORD_FN_PTR_CURRY.into(), false, false, pos), diff --git a/src/plugin.rs b/src/plugin.rs index b2fde8e6..168de561 100644 --- a/src/plugin.rs +++ b/src/plugin.rs @@ -1,6 +1,6 @@ //! Module defining macros for developing _plugins_. -pub use crate::any::Dynamic; +pub use crate::dynamic::Dynamic; pub use crate::engine::Engine; pub use crate::fn_native::{CallableFunction, FnCallArgs, NativeCallContext}; pub use crate::fn_register::{RegisterFn, RegisterResultFn}; diff --git a/src/result.rs b/src/result.rs index cb43cff3..f0ec841b 100644 --- a/src/result.rs +++ b/src/result.rs @@ -1,7 +1,7 @@ //! Module containing error definitions for the evaluation process. -use crate::any::Dynamic; -use crate::error::ParseErrorType; +use crate::dynamic::Dynamic; +use crate::parse_error::ParseErrorType; use crate::parser::INT; use crate::token::Position; use crate::utils::ImmutableString; diff --git a/src/scope.rs b/src/scope.rs index d36fa478..05188843 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -1,6 +1,6 @@ //! Module that defines the `Scope` type representing a function call-stack scope. -use crate::any::{Dynamic, Variant}; +use crate::dynamic::{Dynamic, Variant}; use crate::parser::{map_dynamic_to_expr, Expr}; use crate::token::Position; diff --git a/src/serde_impl/de.rs b/src/serde_impl/de.rs index 8a199b14..3d14f1ad 100644 --- a/src/serde_impl/de.rs +++ b/src/serde_impl/de.rs @@ -1,8 +1,8 @@ //! Implement deserialization support of `Dynamic` for [`serde`](https://crates.io/crates/serde). use super::str::ImmutableStringDeserializer; -use crate::any::{Dynamic, Union}; -use crate::error::ParseErrorType; +use crate::dynamic::{Dynamic, Union}; +use crate::parse_error::ParseErrorType; use crate::result::EvalAltResult; use crate::token::Position; use crate::utils::ImmutableString; diff --git a/src/serde_impl/ser.rs b/src/serde_impl/ser.rs index 14689166..c7a77251 100644 --- a/src/serde_impl/ser.rs +++ b/src/serde_impl/ser.rs @@ -1,6 +1,6 @@ //! Implement serialization support of `Dynamic` for [`serde`](https://crates.io/crates/serde). -use crate::any::Dynamic; +use crate::dynamic::Dynamic; use crate::result::EvalAltResult; use crate::token::Position; diff --git a/src/syntax.rs b/src/syntax.rs index 83149911..1aefdc15 100644 --- a/src/syntax.rs +++ b/src/syntax.rs @@ -1,9 +1,9 @@ //! Module implementing custom syntax for `Engine`. -use crate::any::Dynamic; +use crate::dynamic::Dynamic; use crate::engine::{Engine, EvalContext, MARKER_BLOCK, MARKER_EXPR, MARKER_IDENT}; -use crate::error::{LexError, ParseError}; use crate::fn_native::{SendSync, Shared}; +use crate::parse_error::{LexError, ParseError}; use crate::parser::Expr; use crate::result::EvalAltResult; use crate::token::{is_valid_identifier, Position, Token}; diff --git a/src/token.rs b/src/token.rs index 82d7c611..044201b1 100644 --- a/src/token.rs +++ b/src/token.rs @@ -8,7 +8,7 @@ use crate::engine::{ #[cfg(not(feature = "no_closure"))] use crate::engine::KEYWORD_IS_SHARED; -use crate::error::LexError; +use crate::parse_error::LexError; use crate::parser::INT; use crate::StaticVec; diff --git a/src/unsafe.rs b/src/unsafe.rs index 2442459f..ed859735 100644 --- a/src/unsafe.rs +++ b/src/unsafe.rs @@ -1,6 +1,6 @@ //! A helper module containing unsafe utility functions. -use crate::any::Variant; +use crate::dynamic::Variant; use crate::stdlib::{ any::{Any, TypeId}, diff --git a/src/utils.rs b/src/utils.rs index 7c01117e..7596b6cb 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -9,7 +9,7 @@ use crate::stdlib::{ cmp::Ordering, fmt, hash::{BuildHasher, Hash, Hasher}, - iter::FromIterator, + iter::{empty, FromIterator}, ops::{Add, AddAssign, Deref}, str::FromStr, string::{String, ToString}, @@ -72,22 +72,69 @@ impl BuildHasher for StraightHasherBuilder { /// # Note /// /// The first module name is skipped. Hashing starts from the _second_ module in the chain. -pub fn calc_fn_hash<'a>( +#[inline(always)] +pub fn calc_native_fn_hash<'a>( + modules: impl Iterator, + fn_name: &str, + params: impl Iterator, +) -> u64 { + calc_fn_hash(modules, fn_name, None, params) +} + +/// _[INTERNALS]_ Calculate a `u64` hash key from a module-qualified function name and the number of parameters, +/// but no parameter types. +/// Exported under the `internals` feature only. +/// +/// Module names are passed in via `&str` references from an iterator. +/// Parameter types are passed in via `TypeId` values from an iterator. +/// +/// # Note +/// +/// The first module name is skipped. Hashing starts from the _second_ module in the chain. +#[inline(always)] +pub fn calc_script_fn_hash<'a>( modules: impl Iterator, fn_name: &str, num: usize, +) -> u64 { + calc_fn_hash(modules, fn_name, Some(num), empty()) +} + +/// Calculate a `u64` hash key from a module-qualified function name and parameter types. +/// +/// Module names are passed in via `&str` references from an iterator. +/// Parameter types are passed in via `TypeId` values from an iterator. +/// +/// # Note +/// +/// The first module name is skipped. Hashing starts from the _second_ module in the chain. +fn calc_fn_hash<'a>( + modules: impl Iterator, + fn_name: &str, + num: Option, params: impl Iterator, ) -> u64 { #[cfg(feature = "no_std")] - let mut s: AHasher = Default::default(); + let s: &mut AHasher = &mut Default::default(); #[cfg(not(feature = "no_std"))] - let mut s = DefaultHasher::new(); + let s = &mut DefaultHasher::new(); // We always skip the first module - modules.skip(1).for_each(|m| m.hash(&mut s)); + modules.skip(1).for_each(|m| m.hash(s)); s.write(fn_name.as_bytes()); + let num = if let Some(num) = num { + num + } else { + let mut count = 0; + + params.for_each(|t| { + count += 1; + t.hash(s); + }); + + count + }; s.write_usize(num); - params.for_each(|t| t.hash(&mut s)); s.finish() }