Code structure cleanup.
This commit is contained in:
parent
53adc58f63
commit
f5ffbfbe06
427
src/ast.rs
Normal file
427
src/ast.rs
Normal 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()
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
22
src/lib.rs
22
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")]
|
||||
|
@ -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()));
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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};
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::any::Dynamic;
|
||||
use crate::dynamic::Dynamic;
|
||||
use crate::def_package;
|
||||
use crate::parser::ImmutableString;
|
||||
use crate::plugin::*;
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::any::Variant;
|
||||
use crate::dynamic::Variant;
|
||||
use crate::def_package;
|
||||
use crate::parser::INT;
|
||||
use crate::result::EvalAltResult;
|
||||
|
@ -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::*;
|
||||
|
@ -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::*;
|
||||
|
@ -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;
|
||||
|
453
src/parser.rs
453
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<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),
|
||||
|
@ -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};
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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};
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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},
|
||||
|
59
src/utils.rs
59
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<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()
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user