commit
45effc6276
@ -5,7 +5,9 @@ Rhai Release Notes
|
||||
Version 0.19.4
|
||||
==============
|
||||
|
||||
This version adds a low-level API for more flexibility when defining custom syntax.
|
||||
This version basically cleans up the code structure in preparation for a potential `1.0` release in the future.
|
||||
|
||||
This version also adds a low-level API for more flexibility when defining custom syntax.
|
||||
|
||||
Bug fixes
|
||||
---------
|
||||
@ -24,6 +26,11 @@ New features
|
||||
* Low-level API for custom syntax allowing more flexibility in designing the syntax.
|
||||
* `Module::fill_with` to poly-fill a module with another.
|
||||
|
||||
Enhancements
|
||||
------------
|
||||
|
||||
* AST data structures are optimized to maximize cache friendliness. This may have speed impacts on large, complex scripts (benchmarks wanted!).
|
||||
|
||||
|
||||
Version 0.19.3
|
||||
==============
|
||||
|
@ -4,7 +4,7 @@ error[E0277]: the trait bound `NonClonable: Clone` is not satisfied
|
||||
11 | pub fn test_fn(input: f32) -> NonClonable {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Clone` is not implemented for `NonClonable`
|
||||
|
|
||||
::: $WORKSPACE/src/any.rs
|
||||
::: $WORKSPACE/src/dynamic.rs
|
||||
|
|
||||
| pub fn from<T: Variant + Clone>(value: T) -> Self {
|
||||
| ----- required by this bound in `rhai::Dynamic::from`
|
||||
|
@ -4,7 +4,7 @@ error[E0277]: the trait bound `NonClonable: Clone` is not satisfied
|
||||
12 | pub fn test_fn(input: f32) -> NonClonable {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Clone` is not implemented for `NonClonable`
|
||||
|
|
||||
::: $WORKSPACE/src/any.rs
|
||||
::: $WORKSPACE/src/dynamic.rs
|
||||
|
|
||||
| pub fn from<T: Variant + Clone>(value: T) -> Self {
|
||||
| ----- required by this bound in `rhai::Dynamic::from`
|
||||
|
1226
src/ast.rs
Normal file
1226
src/ast.rs
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,14 +1,15 @@
|
||||
//! Helper module which defines the `Any` trait to to allow dynamic value handling.
|
||||
|
||||
use crate::fn_native::{FnPtr, SendSync};
|
||||
use crate::parser::{ImmutableString, INT};
|
||||
use crate::r#unsafe::{unsafe_cast_box, unsafe_try_cast};
|
||||
use crate::utils::ImmutableString;
|
||||
use crate::INT;
|
||||
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
use crate::fn_native::{shared_try_take, Locked, Shared};
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
use crate::parser::FLOAT;
|
||||
use crate::FLOAT;
|
||||
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
use crate::engine::Array;
|
@ -1,21 +1,21 @@
|
||||
//! Main module defining the script evaluation `Engine`.
|
||||
|
||||
use crate::any::{map_std_type_name, Dynamic, Union, Variant};
|
||||
use crate::ast::{BinaryExpr, Expr, Ident, ReturnType, Stmt};
|
||||
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};
|
||||
use crate::optimize::OptimizationLevel;
|
||||
use crate::packages::{Package, PackagesCollection, StandardPackage};
|
||||
use crate::parser::{BinaryExpr, Expr, ReturnType, Stmt};
|
||||
use crate::r#unsafe::unsafe_cast_var_name_to_lifetime;
|
||||
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;
|
||||
use crate::INT;
|
||||
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
use crate::module::ModuleResolver;
|
||||
@ -29,10 +29,11 @@ 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,
|
||||
borrow::Cow,
|
||||
boxed::Box,
|
||||
collections::{HashMap, HashSet},
|
||||
fmt, format,
|
||||
@ -388,10 +389,10 @@ pub struct State {
|
||||
}
|
||||
|
||||
impl State {
|
||||
/// Create a new `State`.
|
||||
/// Is the state currently at global (root) level?
|
||||
#[inline(always)]
|
||||
pub fn new() -> Self {
|
||||
Default::default()
|
||||
pub fn is_global(&self) -> bool {
|
||||
self.scope_level == 0
|
||||
}
|
||||
}
|
||||
|
||||
@ -455,7 +456,7 @@ impl<'e, 'x, 'px, 'a, 's, 'm, 'pm, 't, 'pt> EvalContext<'e, 'x, 'px, 'a, 's, 'm,
|
||||
#[cfg(feature = "internals")]
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
#[inline(always)]
|
||||
pub fn imports(&self) -> &'a Imports {
|
||||
pub fn imports(&'a self) -> &'a Imports {
|
||||
self.mods
|
||||
}
|
||||
/// Get an iterator over the namespaces containing definition of all script-defined functions.
|
||||
@ -764,7 +765,7 @@ impl Engine {
|
||||
match expr {
|
||||
Expr::Variable(v) => match v.as_ref() {
|
||||
// Qualified variable
|
||||
((name, pos), Some(modules), hash_var, _) => {
|
||||
(Ident { name, pos }, Some(modules), hash_var, _) => {
|
||||
let module = search_imports_mut(mods, state, modules)?;
|
||||
let target = module.get_qualified_var_mut(*hash_var).map_err(|mut err| {
|
||||
match *err {
|
||||
@ -796,7 +797,7 @@ impl Engine {
|
||||
this_ptr: &'s mut Option<&mut Dynamic>,
|
||||
expr: &'a Expr,
|
||||
) -> Result<(Target<'s>, &'a str, ScopeEntryType, Position), Box<EvalAltResult>> {
|
||||
let ((name, pos), _, _, index) = match expr {
|
||||
let (Ident { name, pos }, _, _, index) = match expr {
|
||||
Expr::Variable(v) => v.as_ref(),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
@ -1187,7 +1188,10 @@ impl Engine {
|
||||
match dot_lhs {
|
||||
// id.??? or id[???]
|
||||
Expr::Variable(x) => {
|
||||
let (var_name, var_pos) = &x.0;
|
||||
let Ident {
|
||||
name: var_name,
|
||||
pos: var_pos,
|
||||
} = &x.0;
|
||||
|
||||
self.inc_operations(state)
|
||||
.map_err(|err| err.fill_position(*var_pos))?;
|
||||
@ -1438,8 +1442,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)
|
||||
@ -1491,14 +1494,16 @@ impl Engine {
|
||||
Expr::IntegerConstant(x) => Ok(x.0.into()),
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
Expr::FloatConstant(x) => Ok(x.0.into()),
|
||||
Expr::StringConstant(x) => Ok(x.0.to_string().into()),
|
||||
Expr::StringConstant(x) => Ok(x.name.to_string().into()),
|
||||
Expr::CharConstant(x) => Ok(x.0.into()),
|
||||
Expr::FnPointer(x) => Ok(FnPtr::new_unchecked(x.0.clone(), Default::default()).into()),
|
||||
Expr::Variable(x) if (x.0).0 == KEYWORD_THIS => {
|
||||
Expr::FnPointer(x) => {
|
||||
Ok(FnPtr::new_unchecked(x.name.clone(), Default::default()).into())
|
||||
}
|
||||
Expr::Variable(x) if (x.0).name == KEYWORD_THIS => {
|
||||
if let Some(val) = this_ptr {
|
||||
Ok(val.clone())
|
||||
} else {
|
||||
EvalAltResult::ErrorUnboundThis((x.0).1).into()
|
||||
EvalAltResult::ErrorUnboundThis((x.0).pos).into()
|
||||
}
|
||||
}
|
||||
Expr::Variable(_) => {
|
||||
@ -1533,9 +1538,9 @@ impl Engine {
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Expr::Map(x) => Ok(Dynamic(Union::Map(Box::new(
|
||||
x.0.iter()
|
||||
.map(|((key, _), expr)| {
|
||||
.map(|(key, expr)| {
|
||||
self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)
|
||||
.map(|val| (key.clone(), val))
|
||||
.map(|val| (key.name.clone(), val))
|
||||
})
|
||||
.collect::<Result<HashMap<_, _>, _>>()?,
|
||||
)))),
|
||||
@ -1687,7 +1692,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
|
||||
@ -1885,7 +1890,11 @@ impl Engine {
|
||||
|
||||
if let Some(func) = func {
|
||||
// Add the loop variable
|
||||
let var_name = unsafe_cast_var_name_to_lifetime(name, &state);
|
||||
let var_name: Cow<'_, str> = if state.is_global() {
|
||||
name.clone().into()
|
||||
} else {
|
||||
unsafe_cast_var_name_to_lifetime(name).into()
|
||||
};
|
||||
scope.push(var_name, ());
|
||||
let index = scope.len() - 1;
|
||||
state.scope_level += 1;
|
||||
@ -1929,7 +1938,7 @@ impl Engine {
|
||||
|
||||
// Try/Catch statement
|
||||
Stmt::TryCatch(x) => {
|
||||
let ((try_body, _), var_def, (catch_body, _)) = x.as_ref();
|
||||
let (try_body, var_def, catch_body, _) = x.as_ref();
|
||||
|
||||
let result = self
|
||||
.eval_stmt(scope, mods, state, lib, this_ptr, try_body, level)
|
||||
@ -1951,8 +1960,12 @@ impl Engine {
|
||||
let orig_scope_len = scope.len();
|
||||
state.scope_level += 1;
|
||||
|
||||
if let Some((var_name, _)) = var_def {
|
||||
let var_name = unsafe_cast_var_name_to_lifetime(var_name, &state);
|
||||
if let Some(Ident { name, .. }) = var_def {
|
||||
let var_name: Cow<'_, str> = if state.is_global() {
|
||||
name.clone().into()
|
||||
} else {
|
||||
unsafe_cast_var_name_to_lifetime(name).into()
|
||||
};
|
||||
scope.push(var_name, value);
|
||||
}
|
||||
|
||||
@ -2016,7 +2029,11 @@ impl Engine {
|
||||
} else {
|
||||
().into()
|
||||
};
|
||||
let var_name = unsafe_cast_var_name_to_lifetime(&var_def.0, &state);
|
||||
let var_name: Cow<'_, str> = if state.is_global() {
|
||||
var_def.name.clone().into()
|
||||
} else {
|
||||
unsafe_cast_var_name_to_lifetime(&var_def.name).into()
|
||||
};
|
||||
scope.push_dynamic_value(var_name, entry_type, val, false);
|
||||
Ok(Default::default())
|
||||
}
|
||||
@ -2039,7 +2056,7 @@ impl Engine {
|
||||
|
||||
if let Some(name_def) = alias {
|
||||
module.index_all_sub_modules();
|
||||
mods.push((name_def.0.clone(), module));
|
||||
mods.push((name_def.name.clone(), module));
|
||||
}
|
||||
|
||||
state.modules += 1;
|
||||
@ -2059,13 +2076,13 @@ impl Engine {
|
||||
// Export statement
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
Stmt::Export(list, _) => {
|
||||
for ((id, id_pos), rename) in list.iter() {
|
||||
for (Ident { name, pos: id_pos }, rename) in list.iter() {
|
||||
// Mark scope variables as public
|
||||
if let Some(index) = scope.get_index(id).map(|(i, _)| i) {
|
||||
let alias = rename.as_ref().map(|(n, _)| n).unwrap_or_else(|| id);
|
||||
if let Some(index) = scope.get_index(name).map(|(i, _)| i) {
|
||||
let alias = rename.as_ref().map(|x| &x.name).unwrap_or_else(|| name);
|
||||
scope.set_entry_alias(index, alias.clone());
|
||||
} else {
|
||||
return EvalAltResult::ErrorVariableNotFound(id.into(), *id_pos).into();
|
||||
return EvalAltResult::ErrorVariableNotFound(name.into(), *id_pos).into();
|
||||
}
|
||||
}
|
||||
Ok(Default::default())
|
||||
@ -2073,7 +2090,7 @@ impl Engine {
|
||||
|
||||
// Share statement
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
Stmt::Share(var_name, _) => {
|
||||
Stmt::Share(Ident { name: var_name, .. }) => {
|
||||
match scope.get_index(var_name) {
|
||||
Some((index, ScopeEntryType::Normal)) => {
|
||||
let (val, _) = scope.get_mut(index);
|
||||
|
@ -1,11 +1,11 @@
|
||||
//! Module that defines the extern API of `Engine`.
|
||||
|
||||
use crate::any::{Dynamic, Variant};
|
||||
use crate::engine::{Engine, EvalContext, Imports, State};
|
||||
use crate::error::ParseError;
|
||||
use crate::ast::AST;
|
||||
use crate::dynamic::{Dynamic, Variant};
|
||||
use crate::engine::{Engine, EvalContext, Imports};
|
||||
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,
|
||||
};
|
||||
|
||||
@ -1619,7 +1619,7 @@ impl Engine {
|
||||
.get_script_fn(name, args.len(), true)
|
||||
.ok_or_else(|| EvalAltResult::ErrorFunctionNotFound(name.into(), Position::none()))?;
|
||||
|
||||
let mut state = State::new();
|
||||
let mut state = Default::default();
|
||||
let mut mods = Default::default();
|
||||
|
||||
// Check for data race.
|
@ -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,30 +1,31 @@
|
||||
//! Implement function-calling mechanism for `Engine`.
|
||||
|
||||
use crate::any::Dynamic;
|
||||
use crate::ast::{Expr, Stmt};
|
||||
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::parser::{Expr, ImmutableString, Stmt, INT};
|
||||
use crate::parse_error::ParseErrorType;
|
||||
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::utils::ImmutableString;
|
||||
use crate::{calc_native_fn_hash, calc_script_fn_hash, StaticVec, INT};
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
use crate::{
|
||||
parser::ScriptFnDef, r#unsafe::unsafe_cast_var_name_to_lifetime,
|
||||
ast::ScriptFnDef, r#unsafe::unsafe_cast_var_name_to_lifetime,
|
||||
scope::EntryType as ScopeEntryType,
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
use crate::parser::FLOAT;
|
||||
use crate::FLOAT;
|
||||
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
use crate::engine::{FN_IDX_GET, FN_IDX_SET};
|
||||
@ -50,6 +51,9 @@ use crate::stdlib::{
|
||||
vec::Vec,
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
use crate::stdlib::borrow::Cow;
|
||||
|
||||
#[cfg(feature = "no_std")]
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
use num_traits::float::Float;
|
||||
@ -376,7 +380,7 @@ impl Engine {
|
||||
.iter()
|
||||
.zip(args.iter_mut().map(|v| mem::take(*v)))
|
||||
.map(|(name, value)| {
|
||||
let var_name = unsafe_cast_var_name_to_lifetime(name.as_str(), state);
|
||||
let var_name: Cow<'_, str> = unsafe_cast_var_name_to_lifetime(name).into();
|
||||
(var_name, ScopeEntryType::Normal, value)
|
||||
}),
|
||||
);
|
||||
@ -438,15 +442,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)
|
||||
}
|
||||
@ -502,17 +499,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
|
||||
@ -644,7 +631,7 @@ impl Engine {
|
||||
statements: impl IntoIterator<Item = &'a Stmt>,
|
||||
lib: &[&Module],
|
||||
) -> Result<(Dynamic, u64), Box<EvalAltResult>> {
|
||||
let mut state = State::new();
|
||||
let mut state = Default::default();
|
||||
|
||||
statements
|
||||
.into_iter()
|
||||
@ -740,7 +727,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<_>>();
|
||||
@ -767,7 +754,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<_>>();
|
||||
@ -830,7 +817,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())
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -881,7 +868,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
|
||||
@ -962,12 +949,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 =
|
||||
@ -981,10 +968,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(),
|
||||
@ -1006,7 +992,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());
|
||||
@ -1015,7 +1001,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
|
||||
@ -1184,8 +1170,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,14 +1,14 @@
|
||||
//! Module defining interfaces to native-Rust functions.
|
||||
|
||||
use crate::any::Dynamic;
|
||||
use crate::ast::{FnAccess, ScriptFnDef};
|
||||
use crate::dynamic::Dynamic;
|
||||
use crate::engine::{Engine, EvalContext};
|
||||
use crate::module::Module;
|
||||
use crate::parser::{FnAccess, ScriptFnDef};
|
||||
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,10 +2,10 @@
|
||||
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
use crate::any::{Dynamic, DynamicWriteLock, Variant};
|
||||
use crate::ast::FnAccess;
|
||||
use crate::dynamic::{Dynamic, DynamicWriteLock, Variant};
|
||||
use crate::engine::Engine;
|
||||
use crate::fn_native::{CallableFunction, FnAny, FnCallArgs, NativeCallContext, SendSync};
|
||||
use crate::parser::FnAccess;
|
||||
use crate::r#unsafe::unsafe_cast_box;
|
||||
use crate::result::EvalAltResult;
|
||||
use crate::utils::ImmutableString;
|
||||
|
51
src/lib.rs
51
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,43 +70,60 @@ 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;
|
||||
/// The system integer type.
|
||||
///
|
||||
/// If the `only_i32` feature is enabled, this will be `i32` instead.
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
pub type INT = i64;
|
||||
|
||||
/// The system integer type.
|
||||
///
|
||||
/// If the `only_i32` feature is not enabled, this will be `i64` instead.
|
||||
#[cfg(feature = "only_i32")]
|
||||
pub type INT = i32;
|
||||
|
||||
/// The system floating-point type.
|
||||
///
|
||||
/// Not available under the `no_float` feature.
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
pub type FLOAT = f64;
|
||||
|
||||
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 result::EvalAltResult;
|
||||
pub use scope::Scope;
|
||||
pub use syntax::Expression;
|
||||
pub use token::Position;
|
||||
pub use utils::ImmutableString;
|
||||
|
||||
#[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::*;
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
pub use parser::FnAccess;
|
||||
#[cfg(feature = "no_function")]
|
||||
pub use parser::FnAccess;
|
||||
pub use ast::FnAccess;
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
pub use fn_func::Func;
|
||||
@ -116,9 +134,6 @@ pub use engine::Array;
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
pub use engine::Map;
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
pub use parser::FLOAT;
|
||||
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
pub use module::ModuleResolver;
|
||||
|
||||
@ -141,7 +156,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")]
|
||||
@ -149,7 +164,9 @@ pub use token::{get_next_token, parse_string_literal, InputStream, Token, Tokeni
|
||||
|
||||
#[cfg(feature = "internals")]
|
||||
#[deprecated(note = "this type is volatile and may change")]
|
||||
pub use parser::{CustomExpr, Expr, FloatWrapper, ReturnType, ScriptFnDef, Stmt};
|
||||
pub use ast::{
|
||||
BinaryExpr, CustomExpr, Expr, FloatWrapper, Ident, IdentX, ReturnType, ScriptFnDef, Stmt,
|
||||
};
|
||||
|
||||
#[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::ast::FnAccess;
|
||||
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};
|
||||
use crate::{ast::ScriptFnDef, fn_native::Shared};
|
||||
|
||||
#[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,22 +1,20 @@
|
||||
//! Module implementing the AST optimizer.
|
||||
|
||||
use crate::any::Dynamic;
|
||||
use crate::ast::{BinaryExpr, CustomExpr, Expr, ScriptFnDef, Stmt, 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, Expr, ScriptFnDef, Stmt, AST};
|
||||
use crate::parser::map_dynamic_to_expr;
|
||||
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;
|
||||
|
||||
#[cfg(feature = "internals")]
|
||||
use crate::parser::CustomExpr;
|
||||
use crate::ast::ReturnType;
|
||||
|
||||
use crate::stdlib::{
|
||||
boxed::Box,
|
||||
@ -137,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
|
||||
@ -283,18 +276,18 @@ fn optimize_stmt(stmt: Stmt, state: &mut State, preserve_result: bool) -> Stmt {
|
||||
.into_iter()
|
||||
.map(|stmt| match stmt {
|
||||
// Add constant literals into the state
|
||||
Stmt::Const(name, Some(expr), pos) if expr.is_literal() => {
|
||||
Stmt::Const(var_def, Some(expr), pos) if expr.is_literal() => {
|
||||
state.set_dirty();
|
||||
state.push_constant(&name.0, expr);
|
||||
state.push_constant(&var_def.name, expr);
|
||||
Stmt::Noop(pos) // No need to keep constants
|
||||
}
|
||||
Stmt::Const(name, Some(expr), pos) if expr.is_literal() => {
|
||||
Stmt::Const(var_def, Some(expr), pos) if expr.is_literal() => {
|
||||
let expr = optimize_expr(expr, state);
|
||||
Stmt::Const(name, Some(expr), pos)
|
||||
Stmt::Const(var_def, Some(expr), pos)
|
||||
}
|
||||
Stmt::Const(name, None, pos) => {
|
||||
Stmt::Const(var_def, None, pos) => {
|
||||
state.set_dirty();
|
||||
state.push_constant(&name.0, Expr::Unit(name.1));
|
||||
state.push_constant(&var_def.name, Expr::Unit(var_def.pos));
|
||||
Stmt::Noop(pos) // No need to keep constants
|
||||
}
|
||||
// Optimize the statement
|
||||
@ -389,22 +382,25 @@ fn optimize_stmt(stmt: Stmt, state: &mut State, preserve_result: bool) -> Stmt {
|
||||
}
|
||||
}
|
||||
// try { block } catch ( var ) { block }
|
||||
Stmt::TryCatch(x) if (x.0).0.is_pure() => {
|
||||
Stmt::TryCatch(x) if x.0.is_pure() => {
|
||||
// If try block is pure, there will never be any exceptions
|
||||
state.set_dirty();
|
||||
let pos = (x.0).0.position();
|
||||
let mut statements: Vec<_> = Default::default();
|
||||
statements.push(optimize_stmt((x.0).0, state, preserve_result));
|
||||
let pos = x.0.position();
|
||||
let mut statements = match optimize_stmt(x.0, state, preserve_result) {
|
||||
Stmt::Block(statements, _) => statements,
|
||||
stmt => vec![stmt],
|
||||
};
|
||||
statements.push(Stmt::Noop(pos));
|
||||
Stmt::Block(statements, pos)
|
||||
}
|
||||
// try { block } catch ( var ) { block }
|
||||
Stmt::TryCatch(x) => {
|
||||
let ((try_block, try_pos), var_name, (catch_block, catch_pos)) = *x;
|
||||
let (try_block, var_name, catch_block, pos) = *x;
|
||||
Stmt::TryCatch(Box::new((
|
||||
(optimize_stmt(try_block, state, false), try_pos),
|
||||
optimize_stmt(try_block, state, false),
|
||||
var_name,
|
||||
(optimize_stmt(catch_block, state, false), catch_pos),
|
||||
optimize_stmt(catch_block, state, false),
|
||||
pos,
|
||||
)))
|
||||
}
|
||||
// expr;
|
||||
@ -463,7 +459,7 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr {
|
||||
// All other items can be thrown away.
|
||||
state.set_dirty();
|
||||
let pos = m.1;
|
||||
m.0.into_iter().find(|((name, _), _)| name == prop)
|
||||
m.0.into_iter().find(|(x, _)| &x.name == prop)
|
||||
.map(|(_, mut expr)| { expr.set_position(pos); expr })
|
||||
.unwrap_or_else(|| Expr::Unit(pos))
|
||||
}
|
||||
@ -495,15 +491,15 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr {
|
||||
// All other items can be thrown away.
|
||||
state.set_dirty();
|
||||
let pos = m.1;
|
||||
m.0.into_iter().find(|((name, _), _)| *name == s.0)
|
||||
m.0.into_iter().find(|(x, _)| x.name == s.name)
|
||||
.map(|(_, mut expr)| { expr.set_position(pos); expr })
|
||||
.unwrap_or_else(|| Expr::Unit(pos))
|
||||
}
|
||||
// string[int]
|
||||
(Expr::StringConstant(s), Expr::IntegerConstant(i)) if i.0 >= 0 && (i.0 as usize) < s.0.chars().count() => {
|
||||
(Expr::StringConstant(s), Expr::IntegerConstant(i)) if i.0 >= 0 && (i.0 as usize) < s.name.chars().count() => {
|
||||
// String literal indexing - get the character
|
||||
state.set_dirty();
|
||||
Expr::CharConstant(Box::new((s.0.chars().nth(i.0 as usize).unwrap(), s.1)))
|
||||
Expr::CharConstant(Box::new((s.name.chars().nth(i.0 as usize).unwrap(), s.pos)))
|
||||
}
|
||||
// lhs[rhs]
|
||||
(lhs, rhs) => Expr::Index(Box::new(BinaryExpr {
|
||||
@ -520,27 +516,27 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr {
|
||||
// [ items .. ]
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Expr::Map(m) => Expr::Map(Box::new((m.0
|
||||
.into_iter().map(|((key, pos), expr)| ((key, pos), optimize_expr(expr, state)))
|
||||
.into_iter().map(|(key, expr)| (key, optimize_expr(expr, state)))
|
||||
.collect(), m.1))),
|
||||
// lhs in rhs
|
||||
Expr::In(x) => match (x.lhs, x.rhs) {
|
||||
// "xxx" in "xxxxx"
|
||||
(Expr::StringConstant(a), Expr::StringConstant(b)) => {
|
||||
state.set_dirty();
|
||||
if b.0.contains(a.0.as_str()) { Expr::True(a.1) } else { Expr::False(a.1) }
|
||||
if b.name.contains(a.name.as_str()) { Expr::True(a.pos) } else { Expr::False(a.pos) }
|
||||
}
|
||||
// 'x' in "xxxxx"
|
||||
(Expr::CharConstant(a), Expr::StringConstant(b)) => {
|
||||
state.set_dirty();
|
||||
if b.0.contains(a.0) { Expr::True(a.1) } else { Expr::False(a.1) }
|
||||
if b.name.contains(a.0) { Expr::True(a.1) } else { Expr::False(a.1) }
|
||||
}
|
||||
// "xxx" in #{...}
|
||||
(Expr::StringConstant(a), Expr::Map(b)) => {
|
||||
state.set_dirty();
|
||||
if b.0.iter().find(|((name, _), _)| *name == a.0).is_some() {
|
||||
Expr::True(a.1)
|
||||
if b.0.iter().find(|(x, _)| x.name == a.name).is_some() {
|
||||
Expr::True(a.pos)
|
||||
} else {
|
||||
Expr::False(a.1)
|
||||
Expr::False(a.pos)
|
||||
}
|
||||
}
|
||||
// 'x' in #{...}
|
||||
@ -548,7 +544,7 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr {
|
||||
state.set_dirty();
|
||||
let ch = a.0.to_string();
|
||||
|
||||
if b.0.iter().find(|((name, _), _)| name == &ch).is_some() {
|
||||
if b.0.iter().find(|(x, _)| x.name == &ch).is_some() {
|
||||
Expr::True(a.1)
|
||||
} else {
|
||||
Expr::False(a.1)
|
||||
@ -697,24 +693,20 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr {
|
||||
}
|
||||
|
||||
// constant-name
|
||||
Expr::Variable(x) if x.1.is_none() && state.contains_constant(&(x.0).0) => {
|
||||
let (name, pos) = x.0;
|
||||
Expr::Variable(x) if x.1.is_none() && state.contains_constant(&x.0.name) => {
|
||||
state.set_dirty();
|
||||
|
||||
// Replace constant with value
|
||||
let mut expr = state.find_constant(&name).unwrap().clone();
|
||||
expr.set_position(pos);
|
||||
let mut expr = state.find_constant(&x.0.name).unwrap().clone();
|
||||
expr.set_position(x.0.pos);
|
||||
expr
|
||||
}
|
||||
|
||||
// Custom syntax
|
||||
#[cfg(feature = "internals")]
|
||||
Expr::Custom(x) => Expr::Custom(Box::new((
|
||||
CustomExpr(
|
||||
(x.0).0.into_iter().map(|expr| optimize_expr(expr, state)).collect(),
|
||||
(x.0).1),
|
||||
x.1
|
||||
))),
|
||||
Expr::Custom(x) => Expr::Custom(Box::new(CustomExpr {
|
||||
keywords: x.keywords.into_iter().map(|expr| optimize_expr(expr, state)).collect(),
|
||||
..*x
|
||||
})),
|
||||
|
||||
// All other expressions - skip
|
||||
expr => expr,
|
||||
@ -776,7 +768,7 @@ fn optimize(
|
||||
let expr = optimize_expr(expr, &mut state);
|
||||
|
||||
if expr.is_literal() {
|
||||
state.push_constant(&var_def.0, expr.clone());
|
||||
state.push_constant(&var_def.name, expr.clone());
|
||||
}
|
||||
|
||||
// Keep it in the global scope
|
||||
@ -788,7 +780,7 @@ fn optimize(
|
||||
}
|
||||
}
|
||||
Stmt::Const(ref var_def, None, _) => {
|
||||
state.push_constant(&var_def.0, Expr::Unit(var_def.1));
|
||||
state.push_constant(&var_def.name, Expr::Unit(var_def.pos));
|
||||
|
||||
// Keep it in the global scope
|
||||
stmt
|
||||
|
@ -1,13 +1,13 @@
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
use crate::def_package;
|
||||
use crate::parser::INT;
|
||||
use crate::plugin::*;
|
||||
use crate::INT;
|
||||
|
||||
use crate::{result::EvalAltResult, token::Position};
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
use crate::parser::FLOAT;
|
||||
use crate::FLOAT;
|
||||
|
||||
#[cfg(feature = "no_std")]
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
|
@ -1,14 +1,15 @@
|
||||
#![cfg(not(feature = "no_index"))]
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
use crate::any::Dynamic;
|
||||
use crate::def_package;
|
||||
use crate::dynamic::Dynamic;
|
||||
use crate::engine::Array;
|
||||
use crate::fn_native::{FnPtr, NativeCallContext};
|
||||
use crate::parser::{ImmutableString, INT};
|
||||
use crate::plugin::*;
|
||||
use crate::result::EvalAltResult;
|
||||
use crate::token::Position;
|
||||
use crate::utils::ImmutableString;
|
||||
use crate::INT;
|
||||
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
use crate::engine::Map;
|
||||
@ -40,12 +41,12 @@ macro_rules! gen_array_functions {
|
||||
}
|
||||
|
||||
#[rhai_fn(return_raw)]
|
||||
pub fn pad(context: NativeCallContext, list: &mut Array, len: INT, item: $arg_type) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
pub fn pad(_context: NativeCallContext, list: &mut Array, len: INT, item: $arg_type) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
// Check if array will be over max size limit
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
if context.engine().max_array_size() > 0 && len > 0 && (len as usize) > context.engine().max_array_size() {
|
||||
if _context.engine().max_array_size() > 0 && len > 0 && (len as usize) > _context.engine().max_array_size() {
|
||||
return EvalAltResult::ErrorDataTooLarge(
|
||||
"Size of array".to_string(), context.engine().max_array_size(), len as usize, Position::none(),
|
||||
"Size of array".to_string(), _context.engine().max_array_size(), len as usize, Position::none(),
|
||||
).into();
|
||||
}
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
use crate::any::Dynamic;
|
||||
use crate::def_package;
|
||||
use crate::parser::ImmutableString;
|
||||
use crate::dynamic::Dynamic;
|
||||
use crate::plugin::*;
|
||||
use crate::result::EvalAltResult;
|
||||
use crate::utils::ImmutableString;
|
||||
|
||||
def_package!(crate:EvalPackage:"Disable 'eval'.", lib, {
|
||||
combine_with_exported_module!(lib, "eval", eval_override);
|
||||
|
@ -1,7 +1,7 @@
|
||||
use crate::any::Variant;
|
||||
use crate::def_package;
|
||||
use crate::parser::INT;
|
||||
use crate::dynamic::Variant;
|
||||
use crate::result::EvalAltResult;
|
||||
use crate::INT;
|
||||
|
||||
use crate::stdlib::{
|
||||
boxed::Box,
|
||||
|
@ -1,10 +1,11 @@
|
||||
#![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::*;
|
||||
use crate::utils::ImmutableString;
|
||||
use crate::INT;
|
||||
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
use crate::engine::Array;
|
||||
|
@ -1,12 +1,12 @@
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
use crate::def_package;
|
||||
use crate::parser::INT;
|
||||
use crate::plugin::*;
|
||||
use crate::token::Position;
|
||||
use crate::INT;
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
use crate::parser::FLOAT;
|
||||
use crate::FLOAT;
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
use crate::result::EvalAltResult;
|
||||
@ -111,7 +111,7 @@ mod int_functions {
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
#[export_module]
|
||||
mod trig_functions {
|
||||
use crate::parser::FLOAT;
|
||||
use crate::FLOAT;
|
||||
|
||||
pub fn sin(x: FLOAT) -> FLOAT {
|
||||
x.to_radians().sin()
|
||||
@ -154,7 +154,7 @@ mod trig_functions {
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
#[export_module]
|
||||
mod float_functions {
|
||||
use crate::parser::FLOAT;
|
||||
use crate::FLOAT;
|
||||
|
||||
pub fn sqrt(x: FLOAT) -> FLOAT {
|
||||
x.sqrt()
|
||||
|
@ -3,8 +3,9 @@
|
||||
use crate::def_package;
|
||||
use crate::engine::{FN_TO_STRING, KEYWORD_DEBUG, KEYWORD_PRINT};
|
||||
use crate::fn_native::FnPtr;
|
||||
use crate::parser::{ImmutableString, INT};
|
||||
use crate::plugin::*;
|
||||
use crate::utils::ImmutableString;
|
||||
use crate::INT;
|
||||
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
use crate::engine::Array;
|
||||
|
@ -1,11 +1,12 @@
|
||||
#![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::*;
|
||||
use crate::utils::ImmutableString;
|
||||
use crate::StaticVec;
|
||||
use crate::INT;
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
use crate::{result::EvalAltResult, token::Position};
|
||||
|
@ -2,14 +2,14 @@
|
||||
|
||||
use super::{arithmetic::make_err as make_arithmetic_err, math_basic::MAX_INT};
|
||||
|
||||
use crate::any::Dynamic;
|
||||
use crate::def_package;
|
||||
use crate::parser::INT;
|
||||
use crate::dynamic::Dynamic;
|
||||
use crate::plugin::*;
|
||||
use crate::result::EvalAltResult;
|
||||
use crate::INT;
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
use crate::parser::FLOAT;
|
||||
use crate::FLOAT;
|
||||
|
||||
use crate::stdlib::boxed::Box;
|
||||
|
||||
|
1376
src/parser.rs
1376
src/parser.rs
File diff suppressed because it is too large
Load Diff
@ -1,11 +1,11 @@
|
||||
//! Module defining macros for developing _plugins_.
|
||||
|
||||
pub use crate::any::Dynamic;
|
||||
pub use crate::ast::FnAccess;
|
||||
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};
|
||||
pub use crate::module::Module;
|
||||
pub use crate::parser::FnAccess;
|
||||
pub use crate::result::EvalAltResult;
|
||||
pub use crate::utils::ImmutableString;
|
||||
|
||||
|
@ -1,10 +1,10 @@
|
||||
//! Module containing error definitions for the evaluation process.
|
||||
|
||||
use crate::any::Dynamic;
|
||||
use crate::error::ParseErrorType;
|
||||
use crate::parser::INT;
|
||||
use crate::dynamic::Dynamic;
|
||||
use crate::parse_error::ParseErrorType;
|
||||
use crate::token::Position;
|
||||
use crate::utils::ImmutableString;
|
||||
use crate::INT;
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
use crate::engine::is_anonymous_fn;
|
||||
|
@ -1,7 +1,8 @@
|
||||
//! Module that defines the `Scope` type representing a function call-stack scope.
|
||||
|
||||
use crate::any::{Dynamic, Variant};
|
||||
use crate::parser::{map_dynamic_to_expr, Expr};
|
||||
use crate::ast::Expr;
|
||||
use crate::dynamic::{Dynamic, Variant};
|
||||
use crate::parser::map_dynamic_to_expr;
|
||||
use crate::token::Position;
|
||||
|
||||
use crate::stdlib::{borrow::Cow, boxed::Box, iter, string::String, vec::Vec};
|
||||
|
@ -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,10 +1,10 @@
|
||||
//! Module implementing custom syntax for `Engine`.
|
||||
|
||||
use crate::any::Dynamic;
|
||||
use crate::ast::Expr;
|
||||
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::parser::Expr;
|
||||
use crate::parse_error::{LexError, ParseError};
|
||||
use crate::result::EvalAltResult;
|
||||
use crate::token::{is_valid_identifier, Position, Token};
|
||||
use crate::utils::ImmutableString;
|
||||
|
@ -8,12 +8,12 @@ use crate::engine::{
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
use crate::engine::KEYWORD_IS_SHARED;
|
||||
|
||||
use crate::error::LexError;
|
||||
use crate::parser::INT;
|
||||
use crate::parse_error::LexError;
|
||||
use crate::StaticVec;
|
||||
use crate::INT;
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
use crate::parser::FLOAT;
|
||||
use crate::FLOAT;
|
||||
|
||||
use crate::stdlib::{
|
||||
borrow::Cow,
|
||||
|
@ -1,14 +1,11 @@
|
||||
//! A helper module containing unsafe utility functions.
|
||||
|
||||
use crate::any::Variant;
|
||||
use crate::engine::State;
|
||||
use crate::dynamic::Variant;
|
||||
|
||||
use crate::stdlib::{
|
||||
any::{Any, TypeId},
|
||||
borrow::Cow,
|
||||
boxed::Box,
|
||||
mem, ptr,
|
||||
string::ToString,
|
||||
};
|
||||
|
||||
/// Cast a type into another type.
|
||||
@ -46,7 +43,7 @@ pub fn unsafe_cast_box<X: Variant, T: Variant>(item: Box<X>) -> Result<Box<T>, B
|
||||
|
||||
/// # DANGEROUS!!!
|
||||
///
|
||||
/// A dangerous function that blindly casts a `&str` from one lifetime to a `Cow<str>` of
|
||||
/// A dangerous function that blindly casts a `&str` from one lifetime to a `&str` of
|
||||
/// another lifetime. This is mainly used to let us push a block-local variable into the
|
||||
/// current `Scope` without cloning the variable name. Doing this is safe because all local
|
||||
/// variables in the `Scope` are cleared out before existing the block.
|
||||
@ -54,15 +51,8 @@ pub fn unsafe_cast_box<X: Variant, T: Variant>(item: Box<X>) -> Result<Box<T>, B
|
||||
/// Force-casting a local variable's lifetime to the current `Scope`'s larger lifetime saves
|
||||
/// on allocations and string cloning, thus avoids us having to maintain a chain of `Scope`'s.
|
||||
#[inline]
|
||||
pub fn unsafe_cast_var_name_to_lifetime<'s>(name: &str, state: &State) -> Cow<'s, str> {
|
||||
// If not at global level, we can force-cast
|
||||
if state.scope_level > 0 {
|
||||
pub fn unsafe_cast_var_name_to_lifetime<'s>(name: &str) -> &'s str {
|
||||
// WARNING - force-cast the variable name into the scope's lifetime to avoid cloning it
|
||||
// this is safe because all local variables are cleared at the end of the block
|
||||
unsafe { mem::transmute::<_, &'s str>(name) }.into()
|
||||
} else {
|
||||
// The variable is introduced at global (top) level and may persist after the script run.
|
||||
// Therefore, clone the variable name.
|
||||
name.to_string().into()
|
||||
}
|
||||
unsafe { mem::transmute(name) }
|
||||
}
|
||||
|
61
src/utils.rs
61
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},
|
||||
@ -42,14 +42,6 @@ impl Hasher for StraightHasher {
|
||||
}
|
||||
}
|
||||
|
||||
impl StraightHasher {
|
||||
/// Create a `StraightHasher`.
|
||||
#[inline(always)]
|
||||
pub fn new() -> Self {
|
||||
Self(0)
|
||||
}
|
||||
}
|
||||
|
||||
/// A hash builder for `StraightHasher`.
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Default)]
|
||||
pub struct StraightHasherBuilder;
|
||||
@ -59,7 +51,7 @@ impl BuildHasher for StraightHasherBuilder {
|
||||
|
||||
#[inline(always)]
|
||||
fn build_hasher(&self) -> Self::Hasher {
|
||||
StraightHasher::new()
|
||||
Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
@ -72,22 +64,61 @@ 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());
|
||||
if let Some(num) = num {
|
||||
s.write_usize(num);
|
||||
params.for_each(|t| t.hash(&mut s));
|
||||
} else {
|
||||
params.for_each(|t| t.hash(s));
|
||||
}
|
||||
s.finish()
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user