Code structure cleanup.

This commit is contained in:
Stephen Chung 2020-10-28 22:18:44 +08:00
parent 53adc58f63
commit f5ffbfbe06
31 changed files with 590 additions and 563 deletions

427
src/ast.rs Normal file
View File

@ -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<Stmt>,
/// Script-defined functions.
Module,
);
impl AST {
/// Create a new `AST`.
#[inline(always)]
pub fn new(statements: Vec<Stmt>, 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<Stmt> {
&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<rhai::EvalAltResult>> {
/// # #[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::<String>(&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<rhai::EvalAltResult>> {
/// # #[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::<String>(&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<rhai::EvalAltResult>> {
/// # #[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::<String>(&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<rhai::EvalAltResult>> {
/// # #[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::<String>(&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<rhai::EvalAltResult>> {
/// # #[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<Item = (FnAccess, &str, usize, Shared<ScriptFnDef>)> + '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<A: AsRef<AST>> Add<A> for &AST {
type Output = AST;
#[inline(always)]
fn add(self, rhs: A) -> Self::Output {
self.merge(rhs.as_ref())
}
}
impl<A: Into<AST>> AddAssign<A> 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<Module> for AST {
#[inline(always)]
fn as_ref(&self) -> &Module {
self.lib()
}
}

View File

@ -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

View File

@ -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,
};

View File

@ -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.

View File

@ -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::<StaticVec<_>>();
@ -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::<StaticVec<_>>();
@ -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::<ImmutableString>()));
let hash_fn = calc_native_fn_hash(empty(), name, once(TypeId::of::<ImmutableString>()));
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::<ImmutableString>()));
let hash_fn = calc_native_fn_hash(empty(), name, once(TypeId::of::<ImmutableString>()));
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::<ImmutableString>(), TypeId::of::<INT>()]
.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::<ImmutableString>()));
let hash_fn = calc_native_fn_hash(empty(), name, once(TypeId::of::<ImmutableString>()));
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;

View File

@ -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;

View File

@ -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::<StaticVec<_>>();
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);

View File

@ -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;

View File

@ -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")]

View File

@ -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<ScriptFnDef>) -> 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::<ImmutableString>()
} else if id == TypeId::of::<String>() {
if id == TypeId::of::<&str>() || id == TypeId::of::<String>() {
TypeId::of::<ImmutableString>()
} else {
id
}
})
.collect();
.collect::<StaticVec<_>>();
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()));
}

View File

@ -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;

View File

@ -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<Dynamic> {
// 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

View File

@ -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};

View File

@ -1,4 +1,4 @@
use crate::any::Dynamic;
use crate::dynamic::Dynamic;
use crate::def_package;
use crate::parser::ImmutableString;
use crate::plugin::*;

View File

@ -1,4 +1,4 @@
use crate::any::Variant;
use crate::dynamic::Variant;
use crate::def_package;
use crate::parser::INT;
use crate::result::EvalAltResult;

View File

@ -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::*;

View File

@ -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::*;

View File

@ -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;

View File

@ -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<u64, ScriptFnDef, StraightHasherBuilder>;
/// 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<Stmt>,
/// Script-defined functions.
Module,
);
impl AST {
/// Create a new `AST`.
#[inline(always)]
pub fn new(statements: Vec<Stmt>, 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<Stmt> {
&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<rhai::EvalAltResult>> {
/// # #[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::<String>(&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<rhai::EvalAltResult>> {
/// # #[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::<String>(&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<rhai::EvalAltResult>> {
/// # #[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::<String>(&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<rhai::EvalAltResult>> {
/// # #[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::<String>(&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<rhai::EvalAltResult>> {
/// # #[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<Item = (FnAccess, &str, usize, Shared<ScriptFnDef>)> + '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<A: AsRef<AST>> Add<A> for &AST {
type Output = AST;
#[inline(always)]
fn add(self, rhs: A) -> Self::Output {
self.merge(rhs.as_ref())
}
}
impl<A: Into<AST>> AddAssign<A> 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<Module> 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<Ident>, 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),

View File

@ -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};

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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};

View File

@ -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;

View File

@ -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},

View File

@ -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<Item = &'a str>,
fn_name: &str,
params: impl Iterator<Item = TypeId>,
) -> 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<Item = &'a str>,
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<Item = &'a str>,
fn_name: &str,
num: Option<usize>,
params: impl Iterator<Item = TypeId>,
) -> 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()
}