From b0c66eb5e573b92a64040bebc21303907ff55975 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 13 Nov 2020 19:35:51 +0800 Subject: [PATCH 01/19] Add benchmarks for switch statement. --- benches/eval_expression.rs | 54 ++++++++++++++++++++++++++++++++++++++ src/parser.rs | 2 +- 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/benches/eval_expression.rs b/benches/eval_expression.rs index 82fd1b78..b9ac24f2 100644 --- a/benches/eval_expression.rs +++ b/benches/eval_expression.rs @@ -156,3 +156,57 @@ fn bench_eval_loop_strings_no_build(bench: &mut Bencher) { bench.iter(|| engine.consume_ast(&ast).unwrap()); } + +#[bench] +fn bench_eval_switch(bench: &mut Bencher) { + let script = r#" + let sum = 0; + + for x in range(0, 10000) { + sum += switch x % 5 { + 0 => 10, + 1 => 12, + 2 => 42, + 3 => 1, + _ => 9 + } + } + "#; + + let mut engine = Engine::new(); + engine.set_optimization_level(OptimizationLevel::None); + + let ast = engine.compile(script).unwrap(); + + bench.iter(|| engine.consume_ast(&ast).unwrap()); +} + +#[bench] +fn bench_eval_nested_if(bench: &mut Bencher) { + let script = r#" + let sum = 0; + + for x in range(0, 10000) { + let rem = x % 5; + + sum += if rem == 0 { + 10 + } else if rem == 1 { + 12 + } else if rem == 2 { + 42 + } else if rem == 3 { + 1 + } else{ + 9 + }; + } + "#; + + let mut engine = Engine::new(); + engine.set_optimization_level(OptimizationLevel::None); + + let ast = engine.compile(script).unwrap(); + + bench.iter(|| engine.consume_ast(&ast).unwrap()); +} diff --git a/src/parser.rs b/src/parser.rs index 4d6091e3..93d78a89 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -2590,7 +2590,7 @@ fn parse_fn( (Token::RightParen, _) => break, (Token::Identifier(s), pos) => { if params.iter().any(|(p, _)| p == &s) { - return Err(PERR::FnDuplicatedParam(name.to_string(), s).into_err(pos)); + return Err(PERR::FnDuplicatedParam(name, s).into_err(pos)); } let s = state.get_interned_string(s); state.stack.push((s.clone(), ScopeEntryType::Normal)); From 83c7c101d11c963da48a4089a062f0b58967fa61 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 14 Nov 2020 09:38:16 +0800 Subject: [PATCH 02/19] Add docs and tests for switch. --- doc/src/SUMMARY.md | 15 ++++----- doc/src/language/switch.md | 26 ++++++++++++---- doc/src/patterns/enums.md | 64 ++++++++++++++++++++++++++++++++++++++ doc/src/rust/custom.md | 7 +++++ tests/switch.rs | 57 ++++++++++++++++++++++++++++++++- 5 files changed, 155 insertions(+), 14 deletions(-) create mode 100644 doc/src/patterns/enums.md diff --git a/doc/src/SUMMARY.md b/doc/src/SUMMARY.md index f0ee7b6a..8bdce36f 100644 --- a/doc/src/SUMMARY.md +++ b/doc/src/SUMMARY.md @@ -117,13 +117,14 @@ The Rhai Scripting Language 6. [Subtle Semantic Changes](engine/optimize/semantics.md) 8. [Usage Patterns](patterns/index.md) 1. [Object-Oriented Programming (OOP)](patterns/oop.md) - 2. [Loadable Configuration](patterns/config.md) - 3. [Control Layer](patterns/control.md) - 4. [Singleton Command](patterns/singleton.md) - 5. [Multi-Layer Functions](patterns/multi-layer.md) - 6. [One Engine Instance Per Call](patterns/parallel.md) - 7. [Scriptable Event Handler with State](patterns/events.md) - 8. [Dynamic Constants Provider](patterns/dynamic-const.md) + 2. [Working With Rust Enums](patterns/enums.md) + 3. [Loadable Configuration](patterns/config.md) + 4. [Control Layer](patterns/control.md) + 5. [Singleton Command](patterns/singleton.md) + 6. [Multi-Layer Functions](patterns/multi-layer.md) + 7. [One Engine Instance Per Call](patterns/parallel.md) + 8. [Scriptable Event Handler with State](patterns/events.md) + 9. [Dynamic Constants Provider](patterns/dynamic-const.md) 9. [Advanced Topics](advanced.md) 1. [Capture Scope for Function Call](language/fn-capture.md) 2. [Low-Level API](rust/register-raw.md) diff --git a/doc/src/language/switch.md b/doc/src/language/switch.md index 45b9385d..2d05787c 100644 --- a/doc/src/language/switch.md +++ b/doc/src/language/switch.md @@ -71,20 +71,25 @@ switch map { } ``` +Switching on [arrays] is very useful when working with Rust enums (see [this chapter]({{rootUrl}}/patterns/enums.md) +for more details). -Difference From If-Else Chain ------------------------------ -Although a `switch` expression looks _almost_ the same as an `if`-`else` chain, +Difference From `if` - `else if` Chain +------------------------------------- + +Although a `switch` expression looks _almost_ the same as an `if`-`else if` chain, there are subtle differences between the two. +### Look-up Table vs `x == y` + A `switch` expression matches through _hashing_ via a look-up table. -Therefore, matching is very fast. Walking down an `if`-`else` chain -will be _much_ slower. +Therefore, matching is very fast. Walking down an `if`-`else if` chain +is _much_ slower. On the other hand, operators can be [overloaded][operator overloading] in Rhai, meaning that it is possible to override the `==` operator for integers such -that `if x == y` returns a different result from the built-in default. +that `x == y` returns a different result from the built-in default. `switch` expressions do _not_ use the `==` operator for comparison; instead, they _hash_ the data values and jump directly to the correct @@ -95,3 +100,12 @@ the `==` operator will have no effect. Therefore, in environments where it is desirable to [overload][operator overloading] the `==` operator - though it is difficult to think of valid scenarios where you'd want `1 == 1` to return something other than `true` - avoid using the `switch` expression. + +### Efficiency + +Because the `switch` expression works through a look-up table, it is very efficient +even for _large_ number of cases; in fact, switching is an O(1) operation regardless +of the size of the data and number of cases to match. + +A long `if`-`else if` chain becomes increasingly slower with each additional case +because essentially an O(n) _linear scan_ is performed. diff --git a/doc/src/patterns/enums.md b/doc/src/patterns/enums.md new file mode 100644 index 00000000..c5663b44 --- /dev/null +++ b/doc/src/patterns/enums.md @@ -0,0 +1,64 @@ +Working With Rust Enums +======================= + +{{#include ../links.md}} + +Enums in Rust are typically used with _pattern matching_. Rhai is dynamic, so although +it integrates with Rust enum variants just fine (treated transparently as [custom types]), +it is impossible (short of registering a complete API) to distinguish between individual +enum variants or to extract internal data from them. + + +Switch Through Arrays +--------------------- + +An easy way to work with Rust enums is through exposing the internal data of each enum variant +as an [array], usually with the name of the variant as the first item: + +```rust +use rhai::{Engine, Array}; + +#[derive(Debug, Clone)] +enum MyEnum { + Foo, + Bar(i64), + Baz(String, bool) +} + +impl MyEnum { + fn get_enum_data(&mut self) -> Array { + match self { + Self::Foo => vec![ + "Foo".into() + ] as Array, + Self::Bar(num) => vec![ + "Bar".into(), (*num).into() + ] as Array, + Self::Baz(name, option) => vec![ + "Baz".into(), name.clone().into(), (*option).into() + ] as Array + } + } +} + +engine + .register_type_with_name::("MyEnum") + .register_get("enum_data", MyEnum::get_enum_data); +``` + +Then it is a simple matter to match an enum via the `switch` expression: + +```c +// Assume 'value' = 'MyEnum::Baz("hello", true)' +// 'get_data' creates a variable-length array with 'MyEnum' data +let x = switch value.enum_data { + ["Foo"] => 1, + ["Bar", 42] => 2, + ["Bar", 123] => 3, + ["Baz", "hello", false] => 4, + ["Baz", "hello", true] => 5, + _ => 9 +}; + +x == 5; +``` diff --git a/doc/src/rust/custom.md b/doc/src/rust/custom.md index 4c187d78..36394e3a 100644 --- a/doc/src/rust/custom.md +++ b/doc/src/rust/custom.md @@ -191,3 +191,10 @@ engine.register_fn("==", let item = new_ts(); // construct a new 'TestStruct' item in array; // 'in' operator uses '==' ``` + + +Working With Enums +------------------ + +It is quite easy to use Rust enums with Rhai. +See [this chapter]({{rootUrl}}/patterns/enums.md) for more details. diff --git a/tests/switch.rs b/tests/switch.rs index 64fe9d31..9e6dcad2 100644 --- a/tests/switch.rs +++ b/tests/switch.rs @@ -1,4 +1,4 @@ -use rhai::{Engine, EvalAltResult, Scope, INT}; +use rhai::{Dynamic, Engine, EvalAltResult, Scope, INT}; #[test] fn test_switch() -> Result<(), Box> { @@ -62,3 +62,58 @@ fn test_switch() -> Result<(), Box> { Ok(()) } + +#[cfg(not(feature = "no_index"))] +mod test_switch_enum { + use super::*; + use rhai::Array; + #[derive(Debug, Clone)] + enum MyEnum { + Foo, + Bar(i64), + Baz(String, bool), + } + + impl MyEnum { + fn get_enum_data(&mut self) -> Array { + match self { + Self::Foo => vec!["Foo".into()] as Array, + Self::Bar(num) => vec!["Bar".into(), (*num).into()] as Array, + Self::Baz(name, option) => { + vec!["Baz".into(), name.clone().into(), (*option).into()] as Array + } + } + } + } + + #[test] + fn test_switch_enum() -> Result<(), Box> { + let mut engine = Engine::new(); + + engine + .register_type_with_name::("MyEnum") + .register_get("get_data", MyEnum::get_enum_data); + + let mut scope = Scope::new(); + scope.push("x", MyEnum::Baz("hello".to_string(), true)); + + assert_eq!( + engine.eval_with_scope::( + &mut scope, + r#" + switch x.get_data { + ["Foo"] => 1, + ["Bar", 42] => 2, + ["Bar", 123] => 3, + ["Baz", "hello", false] => 4, + ["Baz", "hello", true] => 5, + _ => 9 + } + "# + )?, + 5 + ); + + Ok(()) + } +} From 56fbe39b7bea3da6ff573a93b33aec20c387aaa1 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 14 Nov 2020 16:08:48 +0800 Subject: [PATCH 03/19] Use references for switch expressions, if possible. --- src/dynamic.rs | 4 +- src/engine.rs | 234 ++++++++++++++++++++++++++++++++++++---------- src/engine_api.rs | 4 +- src/fn_call.rs | 7 +- 4 files changed, 195 insertions(+), 54 deletions(-) diff --git a/src/dynamic.rs b/src/dynamic.rs index b0bee022..3ad3ea11 100644 --- a/src/dynamic.rs +++ b/src/dynamic.rs @@ -3,7 +3,7 @@ use crate::fn_native::{FnPtr, SendSync}; use crate::r#unsafe::{unsafe_cast_box, unsafe_try_cast}; use crate::utils::ImmutableString; -use crate::{StaticVec, INT}; +use crate::INT; #[cfg(not(feature = "no_closure"))] use crate::fn_native::{shared_try_take, Locked, Shared}; @@ -15,7 +15,7 @@ use crate::FLOAT; use crate::engine::Array; #[cfg(not(feature = "no_object"))] -use crate::engine::Map; +use crate::{engine::Map, StaticVec}; use crate::stdlib::{ any::{type_name, Any, TypeId}, diff --git a/src/engine.rs b/src/engine.rs index 16558bb5..dfbf7102 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -44,7 +44,6 @@ use crate::stdlib::{ num::NonZeroUsize, ops::DerefMut, string::{String, ToString}, - vec::Vec, }; #[cfg(not(feature = "no_index"))] @@ -57,7 +56,7 @@ use crate::stdlib::mem; /// /// Not available under the `no_index` feature. #[cfg(not(feature = "no_index"))] -pub type Array = Vec; +pub type Array = crate::stdlib::vec::Vec; /// Hash map of `Dynamic` values with `ImmutableString` keys. /// @@ -338,18 +337,10 @@ impl<'a> Target<'a> { _ => None, } } - /// Get a mutable reference from the `Target`. + /// Convert a shared or reference `Target` into a target with an owned value. #[inline(always)] - pub fn as_mut(&mut self) -> &mut Dynamic { - match self { - Self::Ref(r) => *r, - #[cfg(not(feature = "no_closure"))] - #[cfg(not(feature = "no_object"))] - Self::LockGuard((r, _)) => r.deref_mut(), - Self::Value(ref mut r) => r, - #[cfg(not(feature = "no_index"))] - Self::StringChar(_, _, ref mut r) => r, - } + pub fn into_owned(self) -> Target<'static> { + self.take_or_clone().into() } /// Propagate a changed value back to the original source. /// This has no effect except for string indexing. @@ -420,6 +411,36 @@ impl<'a> From<&'a mut Dynamic> for Target<'a> { } } +impl AsRef for Target<'_> { + #[inline(always)] + fn as_ref(&self) -> &Dynamic { + match self { + Self::Ref(r) => *r, + #[cfg(not(feature = "no_closure"))] + #[cfg(not(feature = "no_object"))] + Self::LockGuard((r, _)) => &**r, + Self::Value(ref r) => r, + #[cfg(not(feature = "no_index"))] + Self::StringChar(_, _, ref r) => r, + } + } +} + +impl AsMut for Target<'_> { + #[inline(always)] + fn as_mut(&mut self) -> &mut Dynamic { + match self { + Self::Ref(r) => *r, + #[cfg(not(feature = "no_closure"))] + #[cfg(not(feature = "no_object"))] + Self::LockGuard((r, _)) => r.deref_mut(), + Self::Value(ref mut r) => r, + #[cfg(not(feature = "no_index"))] + Self::StringChar(_, _, ref mut r) => r, + } + } +} + impl> From for Target<'_> { #[inline(always)] fn from(value: T) -> Self { @@ -919,6 +940,8 @@ impl Engine { // Pop the last index value let idx_val = idx_values.pop().unwrap(); + let target_val = target.as_mut(); + match chain_type { #[cfg(not(feature = "no_index"))] ChainType::Index => { @@ -930,7 +953,8 @@ impl Engine { let idx_pos = x.lhs.position(); let idx_val = idx_val.as_value(); let obj_ptr = &mut self.get_indexed_mut( - mods, state, lib, target, idx_val, idx_pos, false, true, level, + mods, state, lib, target_val, idx_val, idx_pos, false, is_ref, true, + level, )?; self.eval_dot_index_chain_helper( @@ -946,7 +970,7 @@ impl Engine { // `call_setter` is introduced to bypass double mutable borrowing of target let _call_setter = match self.get_indexed_mut( - mods, state, lib, target, idx_val, pos, true, false, level, + mods, state, lib, target_val, idx_val, pos, true, is_ref, false, level, ) { // Indexed value is a reference - update directly Ok(ref mut obj_ptr) => { @@ -964,9 +988,8 @@ impl Engine { #[cfg(not(feature = "no_index"))] if let Some(mut new_val) = _call_setter { - let val = target.as_mut(); - let val_type_name = val.type_name(); - let args = &mut [val, &mut idx_val2, &mut new_val.0]; + let val_type_name = target_val.type_name(); + let args = &mut [target_val, &mut idx_val2, &mut new_val.0]; self.exec_fn_call( mods, state, lib, FN_IDX_SET, 0, args, is_ref, true, false, None, @@ -991,7 +1014,7 @@ impl Engine { _ => { let idx_val = idx_val.as_value(); self.get_indexed_mut( - mods, state, lib, target, idx_val, pos, false, true, level, + mods, state, lib, target_val, idx_val, pos, false, is_ref, true, level, ) .map(|v| (v.take_or_clone(), false)) } @@ -1021,22 +1044,22 @@ impl Engine { // xxx.module::fn_name(...) - syntax error Expr::FnCall(_, _) => unreachable!(), // {xxx:map}.id = ??? - Expr::Property(x) if target.is::() && new_val.is_some() => { + Expr::Property(x) if target_val.is::() && new_val.is_some() => { let IdentX { name, pos } = &x.1; let index = name.clone().into(); let mut val = self.get_indexed_mut( - mods, state, lib, target, index, *pos, true, false, level, + mods, state, lib, target_val, index, *pos, true, is_ref, false, level, )?; val.set_value(new_val.unwrap())?; Ok((Default::default(), true)) } // {xxx:map}.id - Expr::Property(x) if target.is::() => { + Expr::Property(x) if target_val.is::() => { let IdentX { name, pos } = &x.1; let index = name.clone().into(); let val = self.get_indexed_mut( - mods, state, lib, target, index, *pos, false, false, level, + mods, state, lib, target_val, index, *pos, false, is_ref, false, level, )?; Ok((val.take_or_clone(), false)) @@ -1045,7 +1068,7 @@ impl Engine { Expr::Property(x) if new_val.is_some() => { let ((_, setter), IdentX { pos, .. }) = x.as_ref(); let mut new_val = new_val; - let mut args = [target.as_mut(), &mut new_val.as_mut().unwrap().0]; + let mut args = [target_val, &mut new_val.as_mut().unwrap().0]; self.exec_fn_call( mods, state, lib, setter, 0, &mut args, is_ref, true, false, None, None, level, @@ -1056,7 +1079,7 @@ impl Engine { // xxx.id Expr::Property(x) => { let ((getter, _), IdentX { pos, .. }) = x.as_ref(); - let mut args = [target.as_mut()]; + let mut args = [target_val]; self.exec_fn_call( mods, state, lib, getter, 0, &mut args, is_ref, true, false, None, None, level, @@ -1065,13 +1088,14 @@ impl Engine { .map_err(|err| err.fill_position(*pos)) } // {xxx:map}.sub_lhs[expr] | {xxx:map}.sub_lhs.expr - Expr::Index(x, x_pos) | Expr::Dot(x, x_pos) if target.is::() => { + Expr::Index(x, x_pos) | Expr::Dot(x, x_pos) if target_val.is::() => { let mut val = match &x.lhs { Expr::Property(p) => { let IdentX { name, pos } = &p.1; let index = name.clone().into(); self.get_indexed_mut( - mods, state, lib, target, index, *pos, false, true, level, + mods, state, lib, target_val, index, *pos, false, is_ref, true, + level, )? } // {xxx:map}.fn_name(arg_expr_list)[expr] | {xxx:map}.fn_name(arg_expr_list).expr @@ -1111,7 +1135,7 @@ impl Engine { // xxx.prop[expr] | xxx.prop.expr Expr::Property(p) => { let ((getter, setter), IdentX { pos, .. }) = p.as_ref(); - let arg_values = &mut [target.as_mut(), &mut Default::default()]; + let arg_values = &mut [target_val, &mut Default::default()]; let args = &mut arg_values[..1]; let (mut val, updated) = self .exec_fn_call( @@ -1358,26 +1382,22 @@ impl Engine { /// Get the value at the indexed position of a base type /// Position in `EvalAltResult` may be None and should be set afterwards. #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] - fn get_indexed_mut<'a>( + fn get_indexed_mut<'t>( &self, _mods: &mut Imports, state: &mut State, _lib: &[&Module], - target: &'a mut Target, + target: &'t mut Dynamic, idx: Dynamic, idx_pos: Position, _create: bool, + _is_ref: bool, _indexers: bool, _level: usize, - ) -> Result, Box> { + ) -> Result, Box> { self.inc_operations(state)?; - #[cfg(not(feature = "no_index"))] - let is_ref = target.is_ref(); - - let val = target.as_mut(); - - match val { + match target { #[cfg(not(feature = "no_index"))] Dynamic(Union::Array(arr)) => { // val_array[idx] @@ -1431,7 +1451,7 @@ impl Engine { let ch = s.chars().nth(offset).ok_or_else(|| { EvalAltResult::ErrorStringBounds(chars_len, index, idx_pos) })?; - Ok(Target::StringChar(val, offset, ch.into())) + Ok(Target::StringChar(target, offset, ch.into())) } else { EvalAltResult::ErrorStringBounds(chars_len, index, idx_pos).into() } @@ -1439,11 +1459,11 @@ impl Engine { #[cfg(not(feature = "no_index"))] _ if _indexers => { - let type_name = val.type_name(); + let type_name = target.type_name(); let mut idx = idx; - let args = &mut [val, &mut idx]; + let args = &mut [target, &mut idx]; self.exec_fn_call( - _mods, state, _lib, FN_IDX_GET, 0, args, is_ref, true, false, None, None, + _mods, state, _lib, FN_IDX_GET, 0, args, _is_ref, true, false, None, None, _level, ) .map(|(v, _)| v.into()) @@ -1455,10 +1475,11 @@ impl Engine { }) } - _ => { - EvalAltResult::ErrorIndexingType(self.map_type_name(val.type_name()).into(), NO_POS) - .into() - } + _ => EvalAltResult::ErrorIndexingType( + self.map_type_name(target.type_name()).into(), + NO_POS, + ) + .into(), } } @@ -1526,6 +1547,119 @@ impl Engine { } } + /// Get a `Target` from an expression. + pub(crate) fn eval_expr_as_target<'s>( + &self, + scope: &'s mut Scope, + mods: &mut Imports, + state: &mut State, + lib: &[&Module], + this_ptr: &'s mut Option<&mut Dynamic>, + expr: &Expr, + no_const: bool, + level: usize, + ) -> Result<(Target<'s>, Position), Box> { + match expr { + // var - point directly to the value + Expr::Variable(_) => { + let (target, _, typ, pos) = + self.search_namespace(scope, mods, state, lib, this_ptr, expr)?; + + Ok(( + match typ { + // If necessary, constants are cloned + ScopeEntryType::Constant if no_const => target.into_owned(), + _ => target, + }, + pos, + )) + } + // var[...] + #[cfg(not(feature = "no_index"))] + Expr::Index(x, _) if x.lhs.get_variable_access(false).is_some() => match x.rhs { + Expr::Property(_) => unreachable!(), + // var[...]... + Expr::FnCall(_, _) | Expr::Index(_, _) | Expr::Dot(_, _) => self + .eval_expr(scope, mods, state, lib, this_ptr, expr, level) + .map(|v| (v.into(), expr.position())), + // var[expr] - point directly to the item + _ => { + let idx = self.eval_expr(scope, mods, state, lib, this_ptr, &x.rhs, level)?; + let idx_pos = x.rhs.position(); + let (mut target, pos) = self.eval_expr_as_target( + scope, mods, state, lib, this_ptr, &x.lhs, no_const, level, + )?; + + let is_ref = target.is_ref(); + + if target.is_shared() || target.is_value() { + let target_ref = target.as_mut(); + self.get_indexed_mut( + mods, state, lib, target_ref, idx, idx_pos, false, is_ref, true, level, + ) + .map(Target::into_owned) + } else { + let target_ref = target.take_ref().unwrap(); + self.get_indexed_mut( + mods, state, lib, target_ref, idx, idx_pos, false, is_ref, true, level, + ) + } + .map(|v| (v, pos)) + } + }, + // var.prop + #[cfg(not(feature = "no_object"))] + Expr::Dot(x, _) if x.lhs.get_variable_access(false).is_some() => match x.rhs { + Expr::Variable(_) => unreachable!(), + // var.prop + Expr::Property(ref p) => { + let (mut target, _) = self.eval_expr_as_target( + scope, mods, state, lib, this_ptr, &x.lhs, no_const, level, + )?; + let is_ref = target.is_ref(); + + if target.is::() { + // map.prop - point directly to the item + let (_, IdentX { name, pos }) = p.as_ref(); + let idx = name.clone().into(); + + if target.is_shared() || target.is_value() { + let target_ref = target.as_mut(); + self.get_indexed_mut( + mods, state, lib, target_ref, idx, *pos, false, is_ref, true, level, + ) + .map(Target::into_owned) + } else { + let target_ref = target.take_ref().unwrap(); + self.get_indexed_mut( + mods, state, lib, target_ref, idx, *pos, false, is_ref, true, level, + ) + } + .map(|v| (v, *pos)) + } else { + // var.prop - call property getter + let ((getter, _), IdentX { pos, .. }) = p.as_ref(); + let mut args = [target.as_mut()]; + self.exec_fn_call( + mods, state, lib, getter, 0, &mut args, is_ref, true, false, None, + None, level, + ) + .map(|(v, _)| (v.into(), *pos)) + .map_err(|err| err.fill_position(*pos)) + } + } + // var.??? + _ => self + .eval_expr(scope, mods, state, lib, this_ptr, expr, level) + .map(|v| (v.into(), expr.position())), + }, + // expr + _ => self + .eval_expr(scope, mods, state, lib, this_ptr, expr, level) + .map(|v| (v.into(), expr.position())), + } + } + /// Evaluate an expression pub(crate) fn eval_expr( &self, @@ -1671,11 +1805,13 @@ impl Engine { Expr::Switch(x, _) => { let (match_expr, table, def_stmt) = x.as_ref(); - let match_item = - self.eval_expr(scope, mods, state, lib, this_ptr, match_expr, level)?; - let hasher = &mut get_hasher(); - match_item.hash(hasher); + self.eval_expr_as_target( + scope, mods, state, lib, this_ptr, match_expr, false, level, + )? + .0 + .as_ref() + .hash(hasher); let hash = hasher.finish(); if let Some(stmt) = table.get(&hash) { diff --git a/src/engine_api.rs b/src/engine_api.rs index 7db48d05..ecfa96a0 100644 --- a/src/engine_api.rs +++ b/src/engine_api.rs @@ -8,7 +8,7 @@ use crate::optimize::OptimizationLevel; use crate::parse_error::ParseError; use crate::result::EvalAltResult; use crate::scope::Scope; -use crate::token::{Position, NO_POS}; +use crate::token::NO_POS; use crate::utils::get_hasher; #[cfg(not(feature = "no_index"))] @@ -21,7 +21,7 @@ use crate::{ use crate::{ engine::{make_getter, make_setter, Map}, parse_error::ParseErrorType, - token::Token, + token::{Position, Token}, }; #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] diff --git a/src/fn_call.rs b/src/fn_call.rs index cc740594..68c51605 100644 --- a/src/fn_call.rs +++ b/src/fn_call.rs @@ -1042,9 +1042,14 @@ impl Engine { .map(|expr| self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)) .collect::>()?; - let (target, _, _, pos) = + let (target, _, typ, pos) = self.search_namespace(scope, mods, state, lib, this_ptr, &args_expr[0])?; + let target = match typ { + ScopeEntryType::Normal => target, + ScopeEntryType::Constant => target.into_owned(), + }; + self.inc_operations(state) .map_err(|err| err.fill_position(pos))?; From 89254a04c4fe37fc03daa84b5d45795a8ca19853 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 14 Nov 2020 17:22:01 +0800 Subject: [PATCH 04/19] Fix tests. --- benches/eval_expression.rs | 8 ++++++-- src/engine.rs | 2 +- src/fn_call.rs | 7 ++----- tests/switch.rs | 4 +--- 4 files changed, 10 insertions(+), 11 deletions(-) diff --git a/benches/eval_expression.rs b/benches/eval_expression.rs index b9ac24f2..79d606b0 100644 --- a/benches/eval_expression.rs +++ b/benches/eval_expression.rs @@ -161,9 +161,12 @@ fn bench_eval_loop_strings_no_build(bench: &mut Bencher) { fn bench_eval_switch(bench: &mut Bencher) { let script = r#" let sum = 0; + let rem = 0; for x in range(0, 10000) { - sum += switch x % 5 { + rem = x % 5; + + sum += switch rem { 0 => 10, 1 => 12, 2 => 42, @@ -185,9 +188,10 @@ fn bench_eval_switch(bench: &mut Bencher) { fn bench_eval_nested_if(bench: &mut Bencher) { let script = r#" let sum = 0; + let rem = 0; for x in range(0, 10000) { - let rem = x % 5; + rem = x % 5; sum += if rem == 0 { 10 diff --git a/src/engine.rs b/src/engine.rs index dfbf7102..a401160e 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -1718,7 +1718,7 @@ impl Engine { Expr::Array(x, _) => Ok(Dynamic(Union::Array(Box::new( x.iter() .map(|item| self.eval_expr(scope, mods, state, lib, this_ptr, item, level)) - .collect::, _>>()?, + .collect::>()?, )))), #[cfg(not(feature = "no_object"))] diff --git a/src/fn_call.rs b/src/fn_call.rs index 68c51605..99991bea 100644 --- a/src/fn_call.rs +++ b/src/fn_call.rs @@ -12,17 +12,14 @@ use crate::module::{Module, NamespaceRef}; use crate::optimize::OptimizationLevel; use crate::parse_error::ParseErrorType; use crate::result::EvalAltResult; -use crate::scope::Scope; +use crate::scope::{EntryType as ScopeEntryType, Scope}; use crate::stdlib::ops::Deref; use crate::token::NO_POS; use crate::utils::ImmutableString; use crate::{calc_native_fn_hash, calc_script_fn_hash, StaticVec, INT}; #[cfg(not(feature = "no_function"))] -use crate::{ - ast::ScriptFnDef, r#unsafe::unsafe_cast_var_name_to_lifetime, - scope::EntryType as ScopeEntryType, -}; +use crate::{ast::ScriptFnDef, r#unsafe::unsafe_cast_var_name_to_lifetime}; #[cfg(not(feature = "no_float"))] use crate::FLOAT; diff --git a/tests/switch.rs b/tests/switch.rs index 9e6dcad2..b5992005 100644 --- a/tests/switch.rs +++ b/tests/switch.rs @@ -90,9 +90,7 @@ mod test_switch_enum { fn test_switch_enum() -> Result<(), Box> { let mut engine = Engine::new(); - engine - .register_type_with_name::("MyEnum") - .register_get("get_data", MyEnum::get_enum_data); + engine.register_get("get_data", MyEnum::get_enum_data); let mut scope = Scope::new(); scope.push("x", MyEnum::Baz("hello".to_string(), true)); From c104afbdce03f13e3be0c418b9142717140b1354 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 14 Nov 2020 18:30:26 +0800 Subject: [PATCH 05/19] Fix switch test. --- tests/switch.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/switch.rs b/tests/switch.rs index b5992005..5699da6d 100644 --- a/tests/switch.rs +++ b/tests/switch.rs @@ -64,13 +64,14 @@ fn test_switch() -> Result<(), Box> { } #[cfg(not(feature = "no_index"))] +#[cfg(not(feature = "no_object"))] mod test_switch_enum { use super::*; use rhai::Array; #[derive(Debug, Clone)] enum MyEnum { Foo, - Bar(i64), + Bar(INT), Baz(String, bool), } @@ -90,7 +91,9 @@ mod test_switch_enum { fn test_switch_enum() -> Result<(), Box> { let mut engine = Engine::new(); - engine.register_get("get_data", MyEnum::get_enum_data); + engine + .register_type_with_name::("MyEnum") + .register_get("get_data", MyEnum::get_enum_data); let mut scope = Scope::new(); scope.push("x", MyEnum::Baz("hello".to_string(), true)); From 28de155f086f037c988032aea7425e04bc2333ce Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 14 Nov 2020 19:04:49 +0800 Subject: [PATCH 06/19] Add Expr::DynamicConstant. --- src/ast.rs | 12 ++++++++++-- src/engine.rs | 1 + src/optimize.rs | 22 ++++++++++++++++++++++ 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/ast.rs b/src/ast.rs index b0a4ac00..44847ac7 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -860,6 +860,8 @@ pub struct FnCallExpr { /// This type is volatile and may change. #[derive(Debug, Clone)] pub enum Expr { + /// Dynamic constant. + DynamicConstant(Box, Position), /// Integer constant. IntegerConstant(INT, Position), /// Floating-point constant. @@ -929,6 +931,7 @@ impl Expr { Some(match self { Self::Expr(x) => return x.get_type_id(), + Self::DynamicConstant(x, _) => x.type_id(), Self::IntegerConstant(_, _) => TypeId::of::(), #[cfg(not(feature = "no_float"))] Self::FloatConstant(_, _) => TypeId::of::(), @@ -957,6 +960,7 @@ impl Expr { Some(match self { Self::Expr(x) => return x.get_constant_value(), + Self::DynamicConstant(x, _) => x.as_ref().clone(), Self::IntegerConstant(x, _) => (*x).into(), #[cfg(not(feature = "no_float"))] Self::FloatConstant(x, _) => (*x).into(), @@ -1004,6 +1008,7 @@ impl Expr { #[cfg(not(feature = "no_float"))] Self::FloatConstant(_, pos) => *pos, + Self::DynamicConstant(_, pos) => *pos, Self::IntegerConstant(_, pos) => *pos, Self::CharConstant(_, pos) => *pos, Self::StringConstant(_, pos) => *pos, @@ -1036,6 +1041,7 @@ impl Expr { #[cfg(not(feature = "no_float"))] Self::FloatConstant(_, pos) => *pos = new_pos, + Self::DynamicConstant(_, pos) => *pos = new_pos, Self::IntegerConstant(_, pos) => *pos = new_pos, Self::CharConstant(_, pos) => *pos = new_pos, Self::StringConstant(_, pos) => *pos = new_pos, @@ -1096,7 +1102,8 @@ impl Expr { #[cfg(not(feature = "no_float"))] Self::FloatConstant(_, _) => true, - Self::IntegerConstant(_, _) + Self::DynamicConstant(_, _) + | Self::IntegerConstant(_, _) | Self::CharConstant(_, _) | Self::StringConstant(_, _) | Self::FnPointer(_, _) @@ -1129,7 +1136,8 @@ impl Expr { #[cfg(not(feature = "no_float"))] Self::FloatConstant(_, _) => false, - Self::IntegerConstant(_, _) + Self::DynamicConstant(_, _) + | Self::IntegerConstant(_, _) | Self::CharConstant(_, _) | Self::FnPointer(_, _) | Self::In(_, _) diff --git a/src/engine.rs b/src/engine.rs index a401160e..d2a5d684 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -1677,6 +1677,7 @@ impl Engine { let result = match expr { Expr::Expr(x) => self.eval_expr(scope, mods, state, lib, this_ptr, x, level), + Expr::DynamicConstant(x, _) => Ok(x.as_ref().clone()), Expr::IntegerConstant(x, _) => Ok((*x).into()), #[cfg(not(feature = "no_float"))] Expr::FloatConstant(x, _) => Ok((*x).into()), diff --git a/src/optimize.rs b/src/optimize.rs index 11e4fae3..292d3fc1 100644 --- a/src/optimize.rs +++ b/src/optimize.rs @@ -17,6 +17,12 @@ use crate::{calc_native_fn_hash, StaticVec}; #[cfg(not(feature = "no_function"))] use crate::ast::ReturnType; +#[cfg(not(feature = "no_index"))] +use crate::Array; + +#[cfg(not(feature = "no_object"))] +use crate::Map; + use crate::stdlib::{ boxed::Box, hash::{Hash, Hasher}, @@ -523,9 +529,25 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) { // lhs[rhs] (lhs, rhs) => { optimize_expr(lhs, state); optimize_expr(rhs, state); } }, + // [ constant .. ] + #[cfg(not(feature = "no_index"))] + Expr::Array(a, pos) if a.iter().all(Expr::is_constant) => { + state.set_dirty(); + let mut arr: Array = Default::default(); + arr.extend(mem::take(a).into_iter().map(|expr| expr.get_constant_value().unwrap())); + *expr = Expr::DynamicConstant(Box::new(arr.into()), *pos); + } // [ items .. ] #[cfg(not(feature = "no_index"))] Expr::Array(a, _) => a.iter_mut().for_each(|expr| optimize_expr(expr, state)), + // #{ key:constant, .. } + #[cfg(not(feature = "no_object"))] + Expr::Map(m, pos) if m.iter().all(|(_, expr)| expr.is_constant()) => { + state.set_dirty(); + let mut map: Map = Default::default(); + map.extend(mem::take(m).into_iter().map(|(key, expr)| (key.name, expr.get_constant_value().unwrap()))); + *expr = Expr::DynamicConstant(Box::new(map.into()), *pos); + } // #{ key:value, .. } #[cfg(not(feature = "no_object"))] Expr::Map(m, _) => m.iter_mut().for_each(|(_, expr)| optimize_expr(expr, state)), From eb49a4b40ac5a07be20a2b419ebc9d2f9016f3c9 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 14 Nov 2020 19:23:10 +0800 Subject: [PATCH 07/19] Speed up switch benches. --- benches/eval_expression.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/benches/eval_expression.rs b/benches/eval_expression.rs index 79d606b0..e8da7025 100644 --- a/benches/eval_expression.rs +++ b/benches/eval_expression.rs @@ -163,7 +163,7 @@ fn bench_eval_switch(bench: &mut Bencher) { let sum = 0; let rem = 0; - for x in range(0, 10000) { + for x in range(0, 10) { rem = x % 5; sum += switch rem { @@ -190,7 +190,7 @@ fn bench_eval_nested_if(bench: &mut Bencher) { let sum = 0; let rem = 0; - for x in range(0, 10000) { + for x in range(0, 10) { rem = x % 5; sum += if rem == 0 { From fce2c62f02a833196d24489d763411fedb3a3d15 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 14 Nov 2020 22:43:56 +0800 Subject: [PATCH 08/19] Reformat code files. --- src/ast.rs | 65 ++---------------------------------------- src/dynamic.rs | 4 --- src/engine_api.rs | 46 ------------------------------ src/engine_settings.rs | 20 ------------- src/result.rs | 4 --- src/syntax.rs | 1 - 6 files changed, 2 insertions(+), 138 deletions(-) diff --git a/src/ast.rs b/src/ast.rs index 44847ac7..6626d595 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -12,17 +12,10 @@ use crate::INT; #[cfg(not(feature = "no_float"))] use crate::FLOAT; -#[cfg(not(feature = "no_index"))] -use crate::engine::Array; - -#[cfg(not(feature = "no_object"))] -use crate::engine::Map; - #[cfg(not(feature = "no_module"))] use crate::engine::Imports; use crate::stdlib::{ - any::TypeId, borrow::Cow, boxed::Box, collections::HashMap, @@ -141,14 +134,12 @@ impl AST { pub fn new(statements: Vec, lib: Module) -> Self { Self(statements, lib) } - /// Get the statements. #[cfg(not(feature = "internals"))] #[inline(always)] pub(crate) fn statements(&self) -> &[Stmt] { &self.0 } - /// _[INTERNALS]_ Get the statements. /// Exported under the `internals` feature only. #[cfg(feature = "internals")] @@ -157,21 +148,18 @@ impl AST { pub fn statements(&self) -> &[Stmt] { &self.0 } - /// Get a mutable reference to the statements. #[cfg(not(feature = "no_optimize"))] #[inline(always)] pub(crate) fn statements_mut(&mut self) -> &mut Vec { &mut self.0 } - /// Get the internal `Module` containing all script-defined functions. #[cfg(not(feature = "internals"))] #[inline(always)] pub(crate) fn lib(&self) -> &Module { &self.1 } - /// _[INTERNALS]_ Get the internal `Module` containing all script-defined functions. /// Exported under the `internals` feature only. #[cfg(feature = "internals")] @@ -180,7 +168,6 @@ impl AST { pub fn lib(&self) -> &Module { &self.1 } - /// Clone the `AST`'s functions into a new `AST`. /// No statements are cloned. /// @@ -190,7 +177,6 @@ impl AST { 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. /// @@ -205,14 +191,12 @@ impl AST { 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. /// @@ -266,7 +250,6 @@ impl AST { 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_. @@ -319,7 +302,6 @@ impl AST { 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. /// @@ -395,7 +377,6 @@ impl AST { 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_. @@ -457,7 +438,6 @@ impl AST { functions.merge_filtered(&other.1, &mut filter); self } - /// Filter out the functions, retaining only some based on a filter predicate. /// /// # Example @@ -486,7 +466,6 @@ impl AST { 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)] @@ -495,14 +474,12 @@ impl AST { ) -> impl Iterator)> + 'a { self.1.iter_script_fn() } - /// Clear all function definitions in the `AST`. #[cfg(not(feature = "no_function"))] #[inline(always)] pub fn clear_functions(&mut self) { self.1 = Default::default(); } - /// Clear all statements in the `AST`, leaving only function definitions. #[inline(always)] pub fn clear_statements(&mut self) { @@ -656,7 +633,6 @@ impl Stmt { _ => false, } } - /// Get the `Position` of this statement. pub fn position(&self) -> Position { match self { @@ -685,7 +661,6 @@ impl Stmt { Self::Share(x) => x.pos, } } - /// Override the `Position` of this statement. pub fn set_position(&mut self, new_pos: Position) -> &mut Self { match self { @@ -718,7 +693,6 @@ impl Stmt { self } - /// Is this statement self-terminated (i.e. no need for a semicolon terminator)? pub fn is_self_terminated(&self) -> bool { match self { @@ -747,7 +721,6 @@ impl Stmt { Self::Share(_) => false, } } - /// Is this statement _pure_? pub fn is_pure(&self) -> bool { match self { @@ -861,6 +834,8 @@ pub struct FnCallExpr { #[derive(Debug, Clone)] pub enum Expr { /// Dynamic constant. + /// Used to hold either an Array or Map literal for quick cloning. + /// All other primitive data types should use the appropriate variants for better speed. DynamicConstant(Box, Position), /// Integer constant. IntegerConstant(INT, Position), @@ -924,35 +899,6 @@ impl Default for Expr { } impl Expr { - /// Get the type of an expression. - /// - /// Returns `None` if the expression's result type is not constant. - pub fn get_type_id(&self) -> Option { - Some(match self { - Self::Expr(x) => return x.get_type_id(), - - Self::DynamicConstant(x, _) => x.type_id(), - Self::IntegerConstant(_, _) => TypeId::of::(), - #[cfg(not(feature = "no_float"))] - Self::FloatConstant(_, _) => TypeId::of::(), - Self::CharConstant(_, _) => TypeId::of::(), - Self::StringConstant(_, _) => TypeId::of::(), - Self::FnPointer(_, _) => TypeId::of::(), - Self::True(_) | Self::False(_) | Self::In(_, _) | Self::And(_, _) | Self::Or(_, _) => { - TypeId::of::() - } - Self::Unit(_) => TypeId::of::<()>(), - - #[cfg(not(feature = "no_index"))] - Self::Array(_, _) => TypeId::of::(), - - #[cfg(not(feature = "no_object"))] - Self::Map(_, _) => TypeId::of::(), - - _ => return None, - }) - } - /// Get the `Dynamic` value of a constant expression. /// /// Returns `None` if the expression is not constant. @@ -991,7 +937,6 @@ impl Expr { _ => return None, }) } - /// Is the expression a simple variable access? pub(crate) fn get_variable_access(&self, non_qualified: bool) -> Option<&str> { match self { @@ -999,7 +944,6 @@ impl Expr { _ => None, } } - /// Get the `Position` of the expression. pub fn position(&self) -> Position { match self { @@ -1030,7 +974,6 @@ impl Expr { Self::Custom(_, pos) => *pos, } } - /// Override the `Position` of the expression. pub fn set_position(&mut self, new_pos: Position) -> &mut Self { match self { @@ -1061,7 +1004,6 @@ impl Expr { self } - /// Is the expression pure? /// /// A pure expression has no side effects. @@ -1084,7 +1026,6 @@ impl Expr { _ => self.is_constant(), } } - /// Is the expression the unit `()` literal? #[inline(always)] pub fn is_unit(&self) -> bool { @@ -1093,7 +1034,6 @@ impl Expr { _ => false, } } - /// Is the expression a constant? pub fn is_constant(&self) -> bool { match self { @@ -1127,7 +1067,6 @@ impl Expr { _ => false, } } - /// Is a particular token allowed as a postfix operator to this expression? pub fn is_valid_postfix(&self, token: &Token) -> bool { match self { diff --git a/src/dynamic.rs b/src/dynamic.rs index 3ad3ea11..3ad4f10f 100644 --- a/src/dynamic.rs +++ b/src/dynamic.rs @@ -263,7 +263,6 @@ impl Dynamic { _ => false, } } - /// Does this `Dynamic` hold a shared data type /// instead of one of the supported system primitive types? #[inline(always)] @@ -274,7 +273,6 @@ impl Dynamic { _ => false, } } - /// Is the value held by this `Dynamic` a particular type? /// /// If the `Dynamic` is a Shared variant checking is performed on @@ -289,7 +287,6 @@ impl Dynamic { self.type_id() == target_type_id } - /// Get the TypeId of the value held by this `Dynamic`. /// /// # Panics or Deadlocks When Value is Shared @@ -323,7 +320,6 @@ impl Dynamic { Union::Shared(cell) => (*cell.read().unwrap()).type_id(), } } - /// Get the name of the type of the value held by this `Dynamic`. /// /// # Panics or Deadlocks When Value is Shared diff --git a/src/engine_api.rs b/src/engine_api.rs index ecfa96a0..3fc8891e 100644 --- a/src/engine_api.rs +++ b/src/engine_api.rs @@ -84,7 +84,6 @@ impl Engine { self.global_module.set_raw_fn(name, arg_types, func); self } - /// Register a custom type for use with the `Engine`. /// The type must implement `Clone`. /// @@ -126,7 +125,6 @@ impl Engine { pub fn register_type(&mut self) -> &mut Self { self.register_type_with_name::(type_name::()) } - /// Register a custom type for use with the `Engine`, with a pretty-print name /// for the `type_of` function. The type must implement `Clone`. /// @@ -177,7 +175,6 @@ impl Engine { self.type_names.insert(type_name::().into(), name.into()); self } - /// Register an iterator adapter for an iterable type with the `Engine`. /// This is an advanced feature. #[inline(always)] @@ -189,7 +186,6 @@ impl Engine { self.global_module.set_iterable::(); self } - /// Register a getter function for a member of a registered type with the `Engine`. /// /// The function signature must start with `&mut self` and not `&self`. @@ -238,7 +234,6 @@ impl Engine { { self.register_fn(&make_getter(name), callback) } - /// Register a getter function for a member of a registered type with the `Engine`. /// Returns `Result>`. /// @@ -286,7 +281,6 @@ impl Engine { ) -> &mut Self { self.register_result_fn(&make_getter(name), callback) } - /// Register a setter function for a member of a registered type with the `Engine`. /// /// # Example @@ -336,7 +330,6 @@ impl Engine { { self.register_fn(&make_setter(name), callback) } - /// Register a setter function for a member of a registered type with the `Engine`. /// Returns `Result<(), Box>`. /// @@ -392,7 +385,6 @@ impl Engine { callback(obj, value).map(Into::into) }) } - /// Short-hand for registering both getter and setter functions /// of a registered type with the `Engine`. /// @@ -445,7 +437,6 @@ impl Engine { { self.register_get(name, get_fn).register_set(name, set_fn) } - /// Register an index getter for a custom type with the `Engine`. /// /// The function signature must start with `&mut self` and not `&self`. @@ -514,7 +505,6 @@ impl Engine { self.register_fn(FN_IDX_GET, callback) } - /// Register an index getter for a custom type with the `Engine`. /// Returns `Result>`. /// @@ -585,7 +575,6 @@ impl Engine { self.register_result_fn(FN_IDX_GET, callback) } - /// Register an index setter for a custom type with the `Engine`. /// /// # Panics @@ -654,7 +643,6 @@ impl Engine { self.register_fn(FN_IDX_SET, callback) } - /// Register an index setter for a custom type with the `Engine`. /// Returns `Result<(), Box>`. /// @@ -729,7 +717,6 @@ impl Engine { callback(obj, index, value).map(Into::into) }) } - /// Short-hand for register both index getter and setter functions for a custom type with the `Engine`. /// /// # Panics @@ -785,7 +772,6 @@ impl Engine { self.register_indexer_get(getter) .register_indexer_set(setter) } - /// Compile a string into an `AST`, which can be used later for evaluation. /// /// # Example @@ -809,7 +795,6 @@ impl Engine { pub fn compile(&self, script: &str) -> Result { self.compile_with_scope(&Default::default(), script) } - /// Compile a string into an `AST` using own scope, which can be used later for evaluation. /// /// The scope is useful for passing constants into the script for optimization @@ -852,7 +837,6 @@ impl Engine { pub fn compile_with_scope(&self, scope: &Scope, script: &str) -> Result { self.compile_scripts_with_scope(scope, &[script]) } - /// When passed a list of strings, first join the strings into one large script, /// and then compile them into an `AST` using own scope, which can be used later for evaluation. /// @@ -907,7 +891,6 @@ impl Engine { ) -> Result { self.compile_with_scope_and_optimization_level(scope, scripts, self.optimization_level) } - /// Join a list of strings and compile into an `AST` using own scope at a specific optimization level. #[inline(always)] pub(crate) fn compile_with_scope_and_optimization_level( @@ -920,7 +903,6 @@ impl Engine { let stream = self.lex(scripts, None); self.parse(hash, &mut stream.peekable(), scope, optimization_level) } - /// Read the contents of a file into a string. #[cfg(not(feature = "no_std"))] #[cfg(not(target_arch = "wasm32"))] @@ -944,7 +926,6 @@ impl Engine { Ok(contents) } - /// Compile a script file into an `AST`, which can be used later for evaluation. /// /// # Example @@ -971,7 +952,6 @@ impl Engine { pub fn compile_file(&self, path: PathBuf) -> Result> { self.compile_file_with_scope(&Default::default(), path) } - /// Compile a script file into an `AST` using own scope, which can be used later for evaluation. /// /// The scope is useful for passing constants into the script for optimization @@ -1013,7 +993,6 @@ impl Engine { ) -> Result> { Self::read_file(path).and_then(|contents| Ok(self.compile_with_scope(scope, &contents)?)) } - /// Parse a JSON string into a map. /// /// The JSON string must be an object hash. It cannot be a simple JavaScript primitive. @@ -1098,7 +1077,6 @@ impl Engine { self.eval_ast_with_scope(&mut scope, &ast) } - /// Compile a string containing an expression into an `AST`, /// which can be used later for evaluation. /// @@ -1123,7 +1101,6 @@ impl Engine { pub fn compile_expression(&self, script: &str) -> Result { self.compile_expression_with_scope(&Default::default(), script) } - /// Compile a string containing an expression into an `AST` using own scope, /// which can be used later for evaluation. /// @@ -1176,7 +1153,6 @@ impl Engine { let mut peekable = stream.peekable(); self.parse_global_expr(hash, &mut peekable, scope, self.optimization_level) } - /// Evaluate a script file. /// /// # Example @@ -1198,7 +1174,6 @@ impl Engine { pub fn eval_file(&self, path: PathBuf) -> Result> { Self::read_file(path).and_then(|contents| self.eval::(&contents)) } - /// Evaluate a script file with own scope. /// /// # Example @@ -1228,7 +1203,6 @@ impl Engine { ) -> Result> { Self::read_file(path).and_then(|contents| self.eval_with_scope::(scope, &contents)) } - /// Evaluate a string. /// /// # Example @@ -1247,7 +1221,6 @@ impl Engine { pub fn eval(&self, script: &str) -> Result> { self.eval_with_scope(&mut Default::default(), script) } - /// Evaluate a string with own scope. /// /// # Example @@ -1283,7 +1256,6 @@ impl Engine { )?; self.eval_ast_with_scope(scope, &ast) } - /// Evaluate a string containing an expression. /// /// # Example @@ -1305,7 +1277,6 @@ impl Engine { ) -> Result> { self.eval_expression_with_scope(&mut Default::default(), script) } - /// Evaluate a string containing an expression with own scope. /// /// # Example @@ -1340,7 +1311,6 @@ impl Engine { self.eval_ast_with_scope(scope, &ast) } - /// Evaluate an `AST`. /// /// # Example @@ -1363,7 +1333,6 @@ impl Engine { pub fn eval_ast(&self, ast: &AST) -> Result> { self.eval_ast_with_scope(&mut Default::default(), ast) } - /// Evaluate an `AST` with own scope. /// /// # Example @@ -1413,7 +1382,6 @@ impl Engine { .into() }); } - /// Evaluate an `AST` with own scope. #[inline(always)] pub(crate) fn eval_ast_with_scope_raw<'a>( @@ -1424,7 +1392,6 @@ impl Engine { ) -> Result<(Dynamic, u64), Box> { self.eval_statements_raw(scope, mods, ast.statements(), &[ast.lib()]) } - /// Evaluate a file, but throw away the result and only return error (if any). /// Useful for when you don't need the result, but still need to keep track of possible errors. #[cfg(not(feature = "no_std"))] @@ -1433,7 +1400,6 @@ impl Engine { pub fn consume_file(&self, path: PathBuf) -> Result<(), Box> { Self::read_file(path).and_then(|contents| self.consume(&contents)) } - /// Evaluate a file with own scope, but throw away the result and only return error (if any). /// Useful for when you don't need the result, but still need to keep track of possible errors. #[cfg(not(feature = "no_std"))] @@ -1446,14 +1412,12 @@ impl Engine { ) -> Result<(), Box> { Self::read_file(path).and_then(|contents| self.consume_with_scope(scope, &contents)) } - /// Evaluate a string, but throw away the result and only return error (if any). /// Useful for when you don't need the result, but still need to keep track of possible errors. #[inline(always)] pub fn consume(&self, script: &str) -> Result<(), Box> { self.consume_with_scope(&mut Default::default(), script) } - /// Evaluate a string with own scope, but throw away the result and only return error (if any). /// Useful for when you don't need the result, but still need to keep track of possible errors. #[inline] @@ -1468,14 +1432,12 @@ impl Engine { let ast = self.parse(hash, &mut stream.peekable(), scope, self.optimization_level)?; self.consume_ast_with_scope(scope, &ast) } - /// Evaluate an AST, but throw away the result and only return error (if any). /// Useful for when you don't need the result, but still need to keep track of possible errors. #[inline(always)] pub fn consume_ast(&self, ast: &AST) -> Result<(), Box> { self.consume_ast_with_scope(&mut Default::default(), ast) } - /// Evaluate an `AST` with own scope, but throw away the result and only return error (if any). /// Useful for when you don't need the result, but still need to keep track of possible errors. #[inline(always)] @@ -1488,7 +1450,6 @@ impl Engine { self.eval_statements_raw(scope, &mut mods, ast.statements(), &[ast.lib()]) .map(|_| ()) } - /// Call a script function defined in an `AST` with multiple arguments. /// Arguments are passed as a tuple. /// @@ -1551,7 +1512,6 @@ impl Engine { .into() }); } - /// Call a script function defined in an `AST` with multiple `Dynamic` arguments /// and optionally a value for binding to the 'this' pointer. /// @@ -1615,7 +1575,6 @@ impl Engine { self.call_fn_dynamic_raw(scope, &[lib.as_ref()], name, &mut this_ptr, args.as_mut()) } - /// Call a script function defined in an `AST` with multiple `Dynamic` arguments. /// /// ## WARNING @@ -1649,7 +1608,6 @@ impl Engine { self.call_script_fn(scope, &mut mods, &mut state, lib, this_ptr, fn_def, args, 0) } - /// Optimize the `AST` with constants defined in an external Scope. /// An optimized copy of the `AST` is returned while the original `AST` is consumed. /// @@ -1683,7 +1641,6 @@ impl Engine { let stmt = mem::take(ast.statements_mut()); optimize_into_ast(self, scope, stmt, lib, optimization_level) } - /// Provide a callback that will be invoked before each variable access. /// /// ## Return Value of Callback @@ -1726,7 +1683,6 @@ impl Engine { self.resolve_var = Some(Box::new(callback)); self } - /// Register a callback for script evaluation progress. /// /// # Example @@ -1769,7 +1725,6 @@ impl Engine { self.progress = Some(Box::new(callback)); self } - /// Override default action of `print` (print to stdout using `println!`) /// /// # Example @@ -1799,7 +1754,6 @@ impl Engine { self.print = Box::new(callback); self } - /// Override default action of `debug` (print to stdout using `println!`) /// /// # Example diff --git a/src/engine_settings.rs b/src/engine_settings.rs index b6d7cfd9..aebcfdb3 100644 --- a/src/engine_settings.rs +++ b/src/engine_settings.rs @@ -27,7 +27,6 @@ impl Engine { self.packages.push(package.into()); self } - /// Control whether and how the `Engine` will optimize an AST after compilation. /// /// Not available under the `no_optimize` feature. @@ -37,7 +36,6 @@ impl Engine { self.optimization_level = optimization_level; self } - /// The current optimization level. /// It controls whether and how the `Engine` will optimize an AST after compilation. /// @@ -47,7 +45,6 @@ impl Engine { pub fn optimization_level(&self) -> OptimizationLevel { self.optimization_level } - /// Set the maximum levels of function calls allowed for a script in order to avoid /// infinite recursion and stack overflows. #[cfg(not(feature = "unchecked"))] @@ -56,14 +53,12 @@ impl Engine { self.limits.max_call_stack_depth = levels; self } - /// The maximum levels of function calls allowed for a script. #[cfg(not(feature = "unchecked"))] #[inline(always)] pub fn max_call_levels(&self) -> usize { self.limits.max_call_stack_depth } - /// Set the maximum number of operations allowed for a script to run to avoid /// consuming too much resources (0 for unlimited). #[cfg(not(feature = "unchecked"))] @@ -76,14 +71,12 @@ impl Engine { }; self } - /// The maximum number of operations allowed for a script to run (0 for unlimited). #[cfg(not(feature = "unchecked"))] #[inline(always)] pub fn max_operations(&self) -> u64 { self.limits.max_operations } - /// Set the maximum number of imported modules allowed for a script. #[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "no_module"))] @@ -92,7 +85,6 @@ impl Engine { self.limits.max_modules = modules; self } - /// The maximum number of imported modules allowed for a script. #[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "no_module"))] @@ -100,7 +92,6 @@ impl Engine { pub fn max_modules(&self) -> usize { self.limits.max_modules } - /// Set the depth limits for expressions (0 for unlimited). #[cfg(not(feature = "unchecked"))] #[inline(always)] @@ -124,14 +115,12 @@ impl Engine { } self } - /// The depth limit for expressions (0 for unlimited). #[cfg(not(feature = "unchecked"))] #[inline(always)] pub fn max_expr_depth(&self) -> usize { self.limits.max_expr_depth } - /// The depth limit for expressions in functions (0 for unlimited). #[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "no_function"))] @@ -139,7 +128,6 @@ impl Engine { pub fn max_function_expr_depth(&self) -> usize { self.limits.max_function_expr_depth } - /// Set the maximum length of strings (0 for unlimited). #[cfg(not(feature = "unchecked"))] #[inline(always)] @@ -147,14 +135,12 @@ impl Engine { self.limits.max_string_size = if max_size == usize::MAX { 0 } else { max_size }; self } - /// The maximum length of strings (0 for unlimited). #[cfg(not(feature = "unchecked"))] #[inline(always)] pub fn max_string_size(&self) -> usize { self.limits.max_string_size } - /// Set the maximum length of arrays (0 for unlimited). #[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "no_index"))] @@ -163,7 +149,6 @@ impl Engine { self.limits.max_array_size = if max_size == usize::MAX { 0 } else { max_size }; self } - /// The maximum length of arrays (0 for unlimited). #[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "no_index"))] @@ -171,7 +156,6 @@ impl Engine { pub fn max_array_size(&self) -> usize { self.limits.max_array_size } - /// Set the maximum length of object maps (0 for unlimited). #[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "no_object"))] @@ -180,7 +164,6 @@ impl Engine { self.limits.max_map_size = if max_size == usize::MAX { 0 } else { max_size }; self } - /// The maximum length of object maps (0 for unlimited). #[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "no_object"))] @@ -188,7 +171,6 @@ impl Engine { pub fn max_map_size(&self) -> usize { self.limits.max_map_size } - /// Set the module resolution service used by the `Engine`. /// /// Not available under the `no_module` feature. @@ -201,7 +183,6 @@ impl Engine { self.module_resolver = resolver.map(|f| Box::new(f) as Box); self } - /// Disable a particular keyword or operator in the language. /// /// # Examples @@ -243,7 +224,6 @@ impl Engine { self.disabled_symbols.insert(symbol.into()); self } - /// Register a custom operator into the language. /// /// The operator must be a valid identifier (i.e. it cannot be a symbol). diff --git a/src/result.rs b/src/result.rs index 8153a2c5..8cf405a2 100644 --- a/src/result.rs +++ b/src/result.rs @@ -303,7 +303,6 @@ impl EvalAltResult { Self::LoopBreak(_, _) | Self::Return(_, _) => unreachable!(), } } - /// Is this error a system exception? pub fn is_system_exception(&self) -> bool { match self { @@ -322,7 +321,6 @@ impl EvalAltResult { _ => false, } } - /// Get the `Position` of this error. pub fn position(&self) -> Position { match self { @@ -356,7 +354,6 @@ impl EvalAltResult { | Self::Return(_, pos) => *pos, } } - /// Override the `Position` of this error. pub fn set_position(&mut self, new_position: Position) { match self { @@ -390,7 +387,6 @@ impl EvalAltResult { | Self::Return(_, pos) => *pos = new_position, } } - /// Consume the current `EvalAltResult` and return a new one with the specified `Position` /// if the current position is `Position::None`. #[inline(always)] diff --git a/src/syntax.rs b/src/syntax.rs index 7a60d787..a7214eec 100644 --- a/src/syntax.rs +++ b/src/syntax.rs @@ -193,7 +193,6 @@ impl Engine { Ok(self) } - /// Register a custom syntax with the `Engine`. /// /// ## WARNING - Low Level API From 01821177592db8d04f8b8bc3715c6c433075a0d0 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 14 Nov 2020 22:55:23 +0800 Subject: [PATCH 09/19] Change Stmt Variant names. --- src/ast.rs | 22 +++++++++++----------- src/engine.rs | 10 +++++----- src/optimize.rs | 26 +++++++++++--------------- src/parser.rs | 16 ++++------------ 4 files changed, 31 insertions(+), 43 deletions(-) diff --git a/src/ast.rs b/src/ast.rs index 6626d595..5fcb2438 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -582,7 +582,7 @@ pub enum Stmt { /// No-op. Noop(Position), /// if expr { stmt } else { stmt } - IfThenElse(Expr, Box<(Stmt, Option)>, Position), + If(Expr, Box<(Stmt, Option)>, Position), /// while expr { stmt } While(Expr, Box, Position), /// loop { stmt } @@ -606,7 +606,7 @@ pub enum Stmt { /// break Break(Position), /// return/throw - ReturnWithVal((ReturnType, Position), Option, Position), + Return((ReturnType, Position), Option, Position), /// import expr as var #[cfg(not(feature = "no_module"))] Import(Expr, Option>, Position), @@ -641,11 +641,11 @@ impl Stmt { | Self::Break(pos) | Self::Block(_, pos) | Self::Assignment(_, pos) - | Self::IfThenElse(_, _, pos) + | Self::If(_, _, pos) | Self::While(_, _, pos) | Self::Loop(_, pos) | Self::For(_, _, pos) - | Self::ReturnWithVal((_, pos), _, _) + | Self::Return((_, pos), _, _) | Self::Let(_, _, _, pos) | Self::Const(_, _, _, pos) | Self::TryCatch(_, pos, _) => *pos, @@ -669,11 +669,11 @@ impl Stmt { | Self::Break(pos) | Self::Block(_, pos) | Self::Assignment(_, pos) - | Self::IfThenElse(_, _, pos) + | Self::If(_, _, pos) | Self::While(_, _, pos) | Self::Loop(_, pos) | Self::For(_, _, pos) - | Self::ReturnWithVal((_, pos), _, _) + | Self::Return((_, pos), _, _) | Self::Let(_, _, _, pos) | Self::Const(_, _, _, pos) | Self::TryCatch(_, pos, _) => *pos = new_pos, @@ -696,7 +696,7 @@ impl Stmt { /// Is this statement self-terminated (i.e. no need for a semicolon terminator)? pub fn is_self_terminated(&self) -> bool { match self { - Self::IfThenElse(_, _, _) + Self::If(_, _, _) | Self::While(_, _, _) | Self::Loop(_, _) | Self::For(_, _, _) @@ -712,7 +712,7 @@ impl Stmt { | Self::Expr(_) | Self::Continue(_) | Self::Break(_) - | Self::ReturnWithVal(_, _, _) => false, + | Self::Return(_, _, _) => false, #[cfg(not(feature = "no_module"))] Self::Import(_, _, _) | Self::Export(_, _) => false, @@ -726,16 +726,16 @@ impl Stmt { match self { Self::Noop(_) => true, Self::Expr(expr) => expr.is_pure(), - Self::IfThenElse(condition, x, _) if x.1.is_some() => { + Self::If(condition, x, _) if x.1.is_some() => { condition.is_pure() && x.0.is_pure() && x.1.as_ref().unwrap().is_pure() } - Self::IfThenElse(condition, x, _) => condition.is_pure() && x.0.is_pure(), + Self::If(condition, x, _) => condition.is_pure() && x.0.is_pure(), Self::While(condition, block, _) => condition.is_pure() && block.is_pure(), Self::Loop(block, _) => block.is_pure(), Self::For(iterable, x, _) => iterable.is_pure() && x.1.is_pure(), Self::Let(_, _, _, _) | Self::Const(_, _, _, _) | Self::Assignment(_, _) => false, Self::Block(block, _) => block.iter().all(|stmt| stmt.is_pure()), - Self::Continue(_) | Self::Break(_) | Self::ReturnWithVal(_, _, _) => false, + Self::Continue(_) | Self::Break(_) | Self::Return(_, _, _) => false, Self::TryCatch(x, _, _) => x.0.is_pure() && x.2.is_pure(), #[cfg(not(feature = "no_module"))] diff --git a/src/engine.rs b/src/engine.rs index d2a5d684..40383a12 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -2064,7 +2064,7 @@ impl Engine { } // If-else statement - Stmt::IfThenElse(expr, x, _) => { + Stmt::If(expr, x, _) => { let (if_block, else_block) = x.as_ref(); self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)? .as_bool() @@ -2231,25 +2231,25 @@ impl Engine { } // Return value - Stmt::ReturnWithVal((ReturnType::Return, pos), Some(expr), _) => EvalAltResult::Return( + Stmt::Return((ReturnType::Return, pos), Some(expr), _) => EvalAltResult::Return( self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?, *pos, ) .into(), // Empty return - Stmt::ReturnWithVal((ReturnType::Return, pos), None, _) => { + Stmt::Return((ReturnType::Return, pos), None, _) => { EvalAltResult::Return(Default::default(), *pos).into() } // Throw value - Stmt::ReturnWithVal((ReturnType::Exception, pos), Some(expr), _) => { + Stmt::Return((ReturnType::Exception, pos), Some(expr), _) => { let val = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?; EvalAltResult::ErrorRuntime(val, *pos).into() } // Empty throw - Stmt::ReturnWithVal((ReturnType::Exception, pos), None, _) => { + Stmt::Return((ReturnType::Exception, pos), None, _) => { EvalAltResult::ErrorRuntime(().into(), *pos).into() } diff --git a/src/optimize.rs b/src/optimize.rs index 292d3fc1..a90dc7fd 100644 --- a/src/optimize.rs +++ b/src/optimize.rs @@ -243,7 +243,7 @@ fn optimize_stmt_block( } match stmt { - Stmt::ReturnWithVal(_, _, _) | Stmt::Break(_) => dead_code = true, + Stmt::Return(_, _, _) | Stmt::Break(_) => dead_code = true, _ => (), } @@ -292,19 +292,17 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) { } }, // if false { if_block } -> Noop - Stmt::IfThenElse(Expr::False(pos), x, _) if x.1.is_none() => { + Stmt::If(Expr::False(pos), x, _) if x.1.is_none() => { state.set_dirty(); *stmt = Stmt::Noop(*pos); } // if true { if_block } -> if_block - Stmt::IfThenElse(Expr::True(_), x, _) if x.1.is_none() => { + Stmt::If(Expr::True(_), x, _) if x.1.is_none() => { *stmt = mem::take(&mut x.0); optimize_stmt(stmt, state, true); } // if expr { Noop } - Stmt::IfThenElse(ref mut condition, x, _) - if x.1.is_none() && matches!(x.0, Stmt::Noop(_)) => - { + Stmt::If(ref mut condition, x, _) if x.1.is_none() && matches!(x.0, Stmt::Noop(_)) => { state.set_dirty(); let pos = condition.position(); @@ -323,22 +321,22 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) { }; } // if expr { if_block } - Stmt::IfThenElse(ref mut condition, ref mut x, _) if x.1.is_none() => { + Stmt::If(ref mut condition, ref mut x, _) if x.1.is_none() => { optimize_expr(condition, state); optimize_stmt(&mut x.0, state, true); } // if false { if_block } else { else_block } -> else_block - Stmt::IfThenElse(Expr::False(_), x, _) if x.1.is_some() => { + Stmt::If(Expr::False(_), x, _) if x.1.is_some() => { *stmt = mem::take(x.1.as_mut().unwrap()); optimize_stmt(stmt, state, true); } // if true { if_block } else { else_block } -> if_block - Stmt::IfThenElse(Expr::True(_), x, _) => { + Stmt::If(Expr::True(_), x, _) => { *stmt = mem::take(&mut x.0); optimize_stmt(stmt, state, true); } // if expr { if_block } else { else_block } - Stmt::IfThenElse(ref mut condition, ref mut x, _) => { + Stmt::If(ref mut condition, ref mut x, _) => { optimize_expr(condition, state); optimize_stmt(&mut x.0, state, true); if let Some(else_block) = x.1.as_mut() { @@ -441,7 +439,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) { // expr; Stmt::Expr(ref mut expr) => optimize_expr(expr, state), // return expr; - Stmt::ReturnWithVal(_, Some(ref mut expr), _) => optimize_expr(expr, state), + Stmt::Return(_, Some(ref mut expr), _) => optimize_expr(expr, state), // All other statements - skip _ => (), @@ -908,11 +906,9 @@ pub fn optimize_into_ast( // {} -> Noop fn_def.body = match body.pop().unwrap_or_else(|| Stmt::Noop(pos)) { // { return val; } -> val - Stmt::ReturnWithVal((ReturnType::Return, _), Some(expr), _) => { - Stmt::Expr(expr) - } + Stmt::Return((ReturnType::Return, _), Some(expr), _) => Stmt::Expr(expr), // { return; } -> () - Stmt::ReturnWithVal((ReturnType::Return, pos), None, _) => { + Stmt::Return((ReturnType::Return, pos), None, _) => { Stmt::Expr(Expr::Unit(pos)) } // All others diff --git a/src/parser.rs b/src/parser.rs index 93d78a89..d37bf0e1 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1970,11 +1970,7 @@ fn parse_if( None }; - Ok(Stmt::IfThenElse( - guard, - Box::new((if_body, else_body)), - token_pos, - )) + Ok(Stmt::If(guard, Box::new((if_body, else_body)), token_pos)) } /// Parse a while loop. @@ -2451,13 +2447,9 @@ fn parse_stmt( match input.peek().unwrap() { // `return`/`throw` at - (Token::EOF, pos) => Ok(Some(Stmt::ReturnWithVal( - (return_type, token_pos), - None, - *pos, - ))), + (Token::EOF, pos) => Ok(Some(Stmt::Return((return_type, token_pos), None, *pos))), // `return;` or `throw;` - (Token::SemiColon, _) => Ok(Some(Stmt::ReturnWithVal( + (Token::SemiColon, _) => Ok(Some(Stmt::Return( (return_type, token_pos), None, settings.pos, @@ -2466,7 +2458,7 @@ fn parse_stmt( (_, _) => { let expr = parse_expr(input, state, lib, settings.level_up())?; let pos = expr.position(); - Ok(Some(Stmt::ReturnWithVal( + Ok(Some(Stmt::Return( (return_type, token_pos), Some(expr), pos, From a63f14b59c4cc41e2e250b645950aa451f079ccc Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 14 Nov 2020 23:43:36 +0800 Subject: [PATCH 10/19] Expr::Switch -> Stmt::Switch. --- src/ast.rs | 33 +++++++++++---------- src/engine.rs | 45 +++++++++++++++-------------- src/optimize.rs | 73 ++++++++++++++++++++++++----------------------- src/parser.rs | 26 +++++++++++++---- tests/comments.rs | 27 ++++++++++++------ tests/switch.rs | 2 +- 6 files changed, 119 insertions(+), 87 deletions(-) diff --git a/src/ast.rs b/src/ast.rs index 5fcb2438..c1d44fe9 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -583,6 +583,12 @@ pub enum Stmt { Noop(Position), /// if expr { stmt } else { stmt } If(Expr, Box<(Stmt, Option)>, Position), + /// switch expr { literal or _ => stmt, ... } + Switch( + Expr, + Box<(HashMap, Option)>, + Position, + ), /// while expr { stmt } While(Expr, Box, Position), /// loop { stmt } @@ -642,6 +648,7 @@ impl Stmt { | Self::Block(_, pos) | Self::Assignment(_, pos) | Self::If(_, _, pos) + | Self::Switch(_, _, pos) | Self::While(_, _, pos) | Self::Loop(_, pos) | Self::For(_, _, pos) @@ -670,6 +677,7 @@ impl Stmt { | Self::Block(_, pos) | Self::Assignment(_, pos) | Self::If(_, _, pos) + | Self::Switch(_, _, pos) | Self::While(_, _, pos) | Self::Loop(_, pos) | Self::For(_, _, pos) @@ -697,6 +705,7 @@ impl Stmt { pub fn is_self_terminated(&self) -> bool { match self { Self::If(_, _, _) + | Self::Switch(_, _, _) | Self::While(_, _, _) | Self::Loop(_, _) | Self::For(_, _, _) @@ -726,10 +735,16 @@ impl Stmt { match self { Self::Noop(_) => true, Self::Expr(expr) => expr.is_pure(), - Self::If(condition, x, _) if x.1.is_some() => { - condition.is_pure() && x.0.is_pure() && x.1.as_ref().unwrap().is_pure() + Self::If(condition, x, _) => { + condition.is_pure() + && x.0.is_pure() + && x.1.as_ref().map(Stmt::is_pure).unwrap_or(true) + } + Self::Switch(expr, x, _) => { + expr.is_pure() + && x.0.values().all(Stmt::is_pure) + && x.1.as_ref().map(Stmt::is_pure).unwrap_or(true) } - Self::If(condition, x, _) => condition.is_pure() && x.0.is_pure(), Self::While(condition, block, _) => condition.is_pure() && block.is_pure(), Self::Loop(block, _) => block.is_pure(), Self::For(iterable, x, _) => iterable.is_pure() && x.1.is_pure(), @@ -872,15 +887,6 @@ pub enum Expr { Dot(Box, Position), /// expr[expr] Index(Box, Position), - /// switch expr { literal or _ => stmt, ... } - Switch( - Box<( - Expr, - HashMap, - Option, - )>, - Position, - ), /// lhs in rhs In(Box, Position), /// lhs && rhs @@ -963,7 +969,6 @@ impl Expr { Self::Stmt(_, pos) => *pos, Self::Variable(x) => (x.3).pos, Self::FnCall(_, pos) => *pos, - Self::Switch(_, pos) => *pos, Self::And(x, _) | Self::Or(x, _) | Self::In(x, _) => x.lhs.position(), @@ -995,7 +1000,6 @@ impl Expr { Self::Property(x) => (x.1).pos = new_pos, Self::Stmt(_, pos) => *pos = new_pos, Self::FnCall(_, pos) => *pos = new_pos, - Self::Switch(_, pos) => *pos = new_pos, Self::And(_, pos) | Self::Or(_, pos) | Self::In(_, pos) => *pos = new_pos, Self::True(pos) | Self::False(pos) | Self::Unit(pos) => *pos = new_pos, Self::Dot(_, pos) | Self::Index(_, pos) => *pos = new_pos, @@ -1089,7 +1093,6 @@ impl Expr { Self::StringConstant(_, _) | Self::Stmt(_, _) | Self::FnCall(_, _) - | Self::Switch(_, _) | Self::Dot(_, _) | Self::Index(_, _) | Self::Array(_, _) diff --git a/src/engine.rs b/src/engine.rs index 40383a12..8da32270 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -1803,27 +1803,6 @@ impl Engine { Expr::False(_) => Ok(false.into()), Expr::Unit(_) => Ok(().into()), - Expr::Switch(x, _) => { - let (match_expr, table, def_stmt) = x.as_ref(); - - let hasher = &mut get_hasher(); - self.eval_expr_as_target( - scope, mods, state, lib, this_ptr, match_expr, false, level, - )? - .0 - .as_ref() - .hash(hasher); - let hash = hasher.finish(); - - if let Some(stmt) = table.get(&hash) { - self.eval_stmt(scope, mods, state, lib, this_ptr, stmt, level) - } else if let Some(def_stmt) = def_stmt { - self.eval_stmt(scope, mods, state, lib, this_ptr, def_stmt, level) - } else { - Ok(().into()) - } - } - Expr::Custom(custom, _) => { let expressions = custom .keywords() @@ -2063,7 +2042,7 @@ impl Engine { self.eval_statements(scope, mods, state, lib, this_ptr, statements, level) } - // If-else statement + // If statement Stmt::If(expr, x, _) => { let (if_block, else_block) = x.as_ref(); self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)? @@ -2080,6 +2059,28 @@ impl Engine { }) } + // Switch statement + Stmt::Switch(match_expr, x, _) => { + let (table, def_stmt) = x.as_ref(); + + let hasher = &mut get_hasher(); + self.eval_expr_as_target( + scope, mods, state, lib, this_ptr, match_expr, false, level, + )? + .0 + .as_ref() + .hash(hasher); + let hash = hasher.finish(); + + if let Some(stmt) = table.get(&hash) { + self.eval_stmt(scope, mods, state, lib, this_ptr, stmt, level) + } else if let Some(def_stmt) = def_stmt { + self.eval_stmt(scope, mods, state, lib, this_ptr, def_stmt, level) + } else { + Ok(().into()) + } + } + // While loop Stmt::While(expr, body, _) => loop { match self diff --git a/src/optimize.rs b/src/optimize.rs index a90dc7fd..501e1132 100644 --- a/src/optimize.rs +++ b/src/optimize.rs @@ -291,6 +291,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) { optimize_expr(&mut x.2, state); } }, + // if false { if_block } -> Noop Stmt::If(Expr::False(pos), x, _) if x.1.is_none() => { state.set_dirty(); @@ -348,6 +349,42 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) { } } + // switch const { ... } + Stmt::Switch(expr, x, pos) if expr.is_constant() => { + let value = expr.get_constant_value().unwrap(); + let hasher = &mut get_hasher(); + value.hash(hasher); + let hash = hasher.finish(); + + state.set_dirty(); + + let table = &mut x.0; + + if let Some(stmt) = table.get_mut(&hash) { + optimize_stmt(stmt, state, true); + *expr = Expr::Stmt(Box::new(vec![mem::take(stmt)].into()), *pos); + } else if let Some(def_stmt) = x.1.as_mut() { + optimize_stmt(def_stmt, state, true); + *expr = Expr::Stmt(Box::new(vec![mem::take(def_stmt)].into()), *pos); + } else { + *expr = Expr::Unit(*pos); + } + } + // switch + Stmt::Switch(expr, x, _) => { + optimize_expr(expr, state); + x.0.values_mut() + .for_each(|stmt| optimize_stmt(stmt, state, true)); + if let Some(def_stmt) = x.1.as_mut() { + optimize_stmt(def_stmt, state, true); + + match def_stmt { + Stmt::Noop(_) | Stmt::Expr(Expr::Unit(_)) => x.1 = None, + _ => (), + } + } + } + // while false { block } -> Noop Stmt::While(Expr::False(pos), _, _) => { state.set_dirty(); @@ -717,42 +754,6 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) { *expr = result; } - // switch const { ... } - Expr::Switch(x, pos) if x.0.is_constant() => { - let value = x.0.get_constant_value().unwrap(); - let hasher = &mut get_hasher(); - value.hash(hasher); - let hash = hasher.finish(); - - state.set_dirty(); - - let table = &mut x.1; - - if let Some(stmt) = table.get_mut(&hash) { - optimize_stmt(stmt, state, true); - *expr = Expr::Stmt(Box::new(vec![mem::take(stmt)].into()), *pos); - } else if let Some(def_stmt) = x.2.as_mut() { - optimize_stmt(def_stmt, state, true); - *expr = Expr::Stmt(Box::new(vec![mem::take(def_stmt)].into()), *pos); - } else { - *expr = Expr::Unit(*pos); - } - } - - // switch - Expr::Switch(x, _) => { - optimize_expr(&mut x.0, state); - x.1.values_mut().for_each(|stmt| optimize_stmt(stmt, state, true)); - if let Some(def_stmt) = x.2.as_mut() { - optimize_stmt(def_stmt, state, true); - - match def_stmt { - Stmt::Noop(_) | Stmt::Expr(Expr::Unit(_)) => x.2 = None, - _ => () - } - } - } - // Custom syntax Expr::Custom(x, _) => x.keywords.iter_mut().for_each(|expr| optimize_expr(expr, state)), diff --git a/src/parser.rs b/src/parser.rs index d37bf0e1..11e79a17 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -192,6 +192,8 @@ struct ParseSettings { allow_anonymous_fn: bool, /// Is if-expression allowed? allow_if_expr: bool, + /// Is switch expression allowed? + allow_switch_expr: bool, /// Is statement-expression allowed? allow_stmt_expr: bool, /// Current expression nesting level. @@ -793,8 +795,12 @@ fn parse_switch( input: &mut TokenStream, state: &mut ParseState, lib: &mut FunctionsLib, - settings: ParseSettings, -) -> Result { + mut settings: ParseSettings, +) -> Result { + // switch ... + let token_pos = eat_token(input, Token::Switch); + settings.pos = token_pos; + #[cfg(not(feature = "unchecked"))] settings.ensure_level_within_max_limit(state.max_expr_depth)?; @@ -901,8 +907,9 @@ fn parse_switch( } } - Ok(Expr::Switch( - Box::new((item, table, def_stmt)), + Ok(Stmt::Switch( + item, + Box::new((table, def_stmt)), settings.pos, )) } @@ -1004,7 +1011,6 @@ fn parse_primary( Token::LeftBracket => parse_array_literal(input, state, lib, settings.level_up())?, #[cfg(not(feature = "no_object"))] Token::MapStart => parse_map_literal(input, state, lib, settings.level_up())?, - Token::Switch => parse_switch(input, state, lib, settings.level_up())?, Token::True => Expr::True(settings.pos), Token::False => Expr::False(settings.pos), Token::LexError(err) => return Err(err.into_err(settings.pos)), @@ -1136,6 +1142,12 @@ fn parse_unary( block.push(parse_if(input, state, lib, settings.level_up())?); Ok(Expr::Stmt(Box::new(block), settings.pos)) } + // Switch statement is allowed to act as expressions + Token::Switch if settings.allow_switch_expr => { + let mut block: StaticVec<_> = Default::default(); + block.push(parse_switch(input, state, lib, settings.level_up())?); + Ok(Expr::Stmt(Box::new(block), settings.pos)) + } // -expr Token::UnaryMinus => { let pos = eat_token(input, Token::UnaryMinus); @@ -1219,6 +1231,7 @@ fn parse_unary( let settings = ParseSettings { allow_if_expr: true, + allow_switch_expr: true, allow_stmt_expr: true, allow_anonymous_fn: true, is_global: false, @@ -2388,6 +2401,7 @@ fn parse_stmt( let settings = ParseSettings { allow_if_expr: true, + allow_switch_expr: true, allow_stmt_expr: true, allow_anonymous_fn: true, is_global: false, @@ -2827,6 +2841,7 @@ impl Engine { let settings = ParseSettings { allow_if_expr: false, + allow_switch_expr: false, allow_stmt_expr: false, allow_anonymous_fn: false, is_global: true, @@ -2879,6 +2894,7 @@ impl Engine { while !input.peek().unwrap().0.is_eof() { let settings = ParseSettings { allow_if_expr: true, + allow_switch_expr: true, allow_stmt_expr: true, allow_anonymous_fn: true, is_global: true, diff --git a/tests/comments.rs b/tests/comments.rs index d49b7586..d7525960 100644 --- a/tests/comments.rs +++ b/tests/comments.rs @@ -1,14 +1,25 @@ -use rhai::{Engine, INT}; +use rhai::{Engine, EvalAltResult, INT}; #[test] -fn test_comments() { +fn test_comments() -> Result<(), Box> { let engine = Engine::new(); - assert!(engine - .eval::("let x = 5; x // I am a single line comment, yay!") - .is_ok()); + assert_eq!( + engine.eval::("let x = 42; x // I am a single line comment, yay!")?, + 42 + ); - assert!(engine - .eval::("let /* I am a multi-line comment, yay! */ x = 5; x") - .is_ok()); + assert_eq!( + engine.eval::( + r#" + let /* I am a + multi-line + comment, yay! + */ x = 42; x + "# + )?, + 42 + ); + + Ok(()) } diff --git a/tests/switch.rs b/tests/switch.rs index 5699da6d..9f2cca35 100644 --- a/tests/switch.rs +++ b/tests/switch.rs @@ -1,4 +1,4 @@ -use rhai::{Dynamic, Engine, EvalAltResult, Scope, INT}; +use rhai::{Engine, EvalAltResult, Scope, INT}; #[test] fn test_switch() -> Result<(), Box> { From bde8917ed4a2551bdc3cf315771a876751aa6fef Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sun, 15 Nov 2020 12:07:35 +0800 Subject: [PATCH 11/19] Set capacity of hash maps. --- src/ast.rs | 28 +++++++++++++++------ src/engine.rs | 57 ++++++++++++++++++++++++++---------------- src/engine_settings.rs | 3 +-- src/module/mod.rs | 26 ++++++++++++++----- src/optimize.rs | 22 +++++----------- src/packages/mod.rs | 4 ++- src/parser.rs | 32 +++++++++++------------- 7 files changed, 100 insertions(+), 72 deletions(-) diff --git a/src/ast.rs b/src/ast.rs index c1d44fe9..44970d9c 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -15,6 +15,12 @@ use crate::FLOAT; #[cfg(not(feature = "no_module"))] use crate::engine::Imports; +#[cfg(not(feature = "no_index"))] +use crate::engine::TYPICAL_ARRAY_SIZE; + +#[cfg(not(feature = "no_object"))] +use crate::engine::TYPICAL_MAP_SIZE; + use crate::stdlib::{ borrow::Cow, boxed::Box, @@ -31,6 +37,9 @@ use crate::stdlib::{ #[cfg(not(feature = "no_closure"))] use crate::stdlib::collections::HashSet; +#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] +use crate::stdlib::cmp::max; + /// A type representing the access mode of a scripted function. #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] pub enum FnAccess { @@ -927,17 +936,20 @@ impl Expr { Self::Unit(_) => ().into(), #[cfg(not(feature = "no_index"))] - Self::Array(x, _) if x.iter().all(Self::is_constant) => Dynamic(Union::Array( - Box::new(x.iter().map(|v| v.get_constant_value().unwrap()).collect()), - )), + Self::Array(x, _) if self.is_constant() => { + let mut arr = Vec::with_capacity(max(TYPICAL_ARRAY_SIZE, x.len())); + arr.extend(x.iter().map(|v| v.get_constant_value().unwrap())); + Dynamic(Union::Array(Box::new(arr))) + } #[cfg(not(feature = "no_object"))] - Self::Map(x, _) if x.iter().all(|(_, v)| v.is_constant()) => { - Dynamic(Union::Map(Box::new( + Self::Map(x, _) if self.is_constant() => { + let mut map = HashMap::with_capacity(max(TYPICAL_MAP_SIZE, x.len())); + map.extend( x.iter() - .map(|(k, v)| (k.name.clone(), v.get_constant_value().unwrap())) - .collect(), - ))) + .map(|(k, v)| (k.name.clone(), v.get_constant_value().unwrap())), + ); + Dynamic(Union::Map(Box::new(map))) } _ => return None, diff --git a/src/engine.rs b/src/engine.rs index 8da32270..06a249af 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -58,12 +58,21 @@ use crate::stdlib::mem; #[cfg(not(feature = "no_index"))] pub type Array = crate::stdlib::vec::Vec; +#[cfg(not(feature = "no_index"))] +pub const TYPICAL_ARRAY_SIZE: usize = 8; // Small arrays are typical + +#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] +use crate::stdlib::cmp::max; + /// Hash map of `Dynamic` values with `ImmutableString` keys. /// /// Not available under the `no_object` feature. #[cfg(not(feature = "no_object"))] pub type Map = HashMap; +#[cfg(not(feature = "no_object"))] +pub const TYPICAL_MAP_SIZE: usize = 8; // Small maps are typical + /// _[INTERNALS]_ A stack of imported modules. /// Exported under the `internals` feature only. /// @@ -597,7 +606,7 @@ pub struct Engine { /// A hashset containing symbols to disable. pub(crate) disabled_symbols: HashSet, - /// A hashset containing custom keywords and precedence to recognize. + /// A hashmap containing custom keywords and precedence to recognize. pub(crate) custom_keywords: HashMap>, /// Custom syntax. pub(crate) custom_syntax: HashMap, @@ -711,10 +720,10 @@ impl Engine { #[cfg(any(feature = "no_std", target_arch = "wasm32",))] module_resolver: None, - type_names: Default::default(), - disabled_symbols: Default::default(), - custom_keywords: Default::default(), - custom_syntax: Default::default(), + type_names: HashMap::with_capacity(8), + disabled_symbols: HashSet::with_capacity(4), + custom_keywords: HashMap::with_capacity(4), + custom_syntax: HashMap::with_capacity(4), // variable resolver resolve_var: None, @@ -768,10 +777,10 @@ impl Engine { #[cfg(not(feature = "no_module"))] module_resolver: None, - type_names: Default::default(), - disabled_symbols: Default::default(), - custom_keywords: Default::default(), - custom_syntax: Default::default(), + type_names: HashMap::with_capacity(8), + disabled_symbols: HashSet::with_capacity(4), + custom_keywords: HashMap::with_capacity(4), + custom_syntax: HashMap::with_capacity(4), resolve_var: None, @@ -1716,21 +1725,25 @@ impl Engine { } #[cfg(not(feature = "no_index"))] - Expr::Array(x, _) => Ok(Dynamic(Union::Array(Box::new( - x.iter() - .map(|item| self.eval_expr(scope, mods, state, lib, this_ptr, item, level)) - .collect::>()?, - )))), + Expr::Array(x, _) => { + let mut arr = Vec::with_capacity(max(TYPICAL_ARRAY_SIZE, x.len())); + for item in x.as_ref() { + arr.push(self.eval_expr(scope, mods, state, lib, this_ptr, item, level)?); + } + Ok(Dynamic(Union::Array(Box::new(arr)))) + } #[cfg(not(feature = "no_object"))] - Expr::Map(x, _) => Ok(Dynamic(Union::Map(Box::new( - x.iter() - .map(|(key, expr)| { - self.eval_expr(scope, mods, state, lib, this_ptr, expr, level) - .map(|val| (key.name.clone(), val)) - }) - .collect::, _>>()?, - )))), + Expr::Map(x, _) => { + let mut map = HashMap::with_capacity(max(TYPICAL_MAP_SIZE, x.len())); + for (IdentX { name: key, .. }, expr) in x.as_ref() { + map.insert( + key.clone(), + self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?, + ); + } + Ok(Dynamic(Union::Map(Box::new(map)))) + } // Normal function call Expr::FnCall(x, pos) if x.namespace.is_none() => { diff --git a/src/engine_settings.rs b/src/engine_settings.rs index aebcfdb3..8add94b8 100644 --- a/src/engine_settings.rs +++ b/src/engine_settings.rs @@ -23,8 +23,7 @@ impl Engine { /// In other words, loaded packages are searched in reverse order. #[inline(always)] pub fn load_package(&mut self, package: impl Into) -> &mut Self { - // Push the package to the top - packages are searched in reverse order - self.packages.push(package.into()); + self.packages.add(package.into()); self } /// Control whether and how the `Engine` will optimize an AST after compilation. diff --git a/src/module/mod.rs b/src/module/mod.rs index 9df9aec5..53882fa8 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -59,7 +59,7 @@ pub struct FuncInfo { /// and/or script-defined functions. /// /// Not available under the `no_module` feature. -#[derive(Default, Clone)] +#[derive(Clone)] pub struct Module { /// Sub-modules. modules: HashMap>, @@ -69,15 +69,29 @@ pub struct Module { all_variables: HashMap, /// External Rust functions. functions: HashMap, - /// Iterator functions, keyed by the type producing the iterator. - type_iterators: HashMap, /// Flattened collection of all external Rust functions, native or scripted, /// including those in sub-modules. all_functions: HashMap, + /// Iterator functions, keyed by the type producing the iterator. + type_iterators: HashMap, /// Is the module indexed? indexed: bool, } +impl Default for Module { + fn default() -> Self { + Self { + modules: HashMap::with_capacity(4), + variables: HashMap::with_capacity(4), + all_variables: HashMap::with_capacity_and_hasher(32, StraightHasherBuilder), + functions: HashMap::with_capacity_and_hasher(64, StraightHasherBuilder), + all_functions: HashMap::with_capacity_and_hasher(256, StraightHasherBuilder), + type_iterators: HashMap::with_capacity(4), + indexed: false, + } + } +} + impl fmt::Debug for Module { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( @@ -1462,9 +1476,9 @@ impl Module { } if !self.indexed { - let mut qualifiers: Vec<_> = Default::default(); - let mut variables: Vec<_> = Default::default(); - let mut functions: Vec<_> = Default::default(); + let mut qualifiers = Vec::with_capacity(4); + let mut variables = Vec::with_capacity(8); + let mut functions = Vec::with_capacity(16); qualifiers.push("root"); diff --git a/src/optimize.rs b/src/optimize.rs index 501e1132..03b2bd3b 100644 --- a/src/optimize.rs +++ b/src/optimize.rs @@ -17,12 +17,6 @@ use crate::{calc_native_fn_hash, StaticVec}; #[cfg(not(feature = "no_function"))] use crate::ast::ReturnType; -#[cfg(not(feature = "no_index"))] -use crate::Array; - -#[cfg(not(feature = "no_object"))] -use crate::Map; - use crate::stdlib::{ boxed::Box, hash::{Hash, Hasher}, @@ -566,26 +560,22 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) { }, // [ constant .. ] #[cfg(not(feature = "no_index"))] - Expr::Array(a, pos) if a.iter().all(Expr::is_constant) => { + Expr::Array(_, _) if expr.is_constant() => { state.set_dirty(); - let mut arr: Array = Default::default(); - arr.extend(mem::take(a).into_iter().map(|expr| expr.get_constant_value().unwrap())); - *expr = Expr::DynamicConstant(Box::new(arr.into()), *pos); + *expr = Expr::DynamicConstant(Box::new(expr.get_constant_value().unwrap()), expr.position()); } // [ items .. ] #[cfg(not(feature = "no_index"))] - Expr::Array(a, _) => a.iter_mut().for_each(|expr| optimize_expr(expr, state)), + Expr::Array(x, _) => x.iter_mut().for_each(|expr| optimize_expr(expr, state)), // #{ key:constant, .. } #[cfg(not(feature = "no_object"))] - Expr::Map(m, pos) if m.iter().all(|(_, expr)| expr.is_constant()) => { + Expr::Map(_, _) if expr.is_constant()=> { state.set_dirty(); - let mut map: Map = Default::default(); - map.extend(mem::take(m).into_iter().map(|(key, expr)| (key.name, expr.get_constant_value().unwrap()))); - *expr = Expr::DynamicConstant(Box::new(map.into()), *pos); + *expr = Expr::DynamicConstant(Box::new(expr.get_constant_value().unwrap()), expr.position()); } // #{ key:value, .. } #[cfg(not(feature = "no_object"))] - Expr::Map(m, _) => m.iter_mut().for_each(|(_, expr)| optimize_expr(expr, state)), + Expr::Map(x, _) => x.iter_mut().for_each(|(_, expr)| optimize_expr(expr, state)), // lhs in rhs Expr::In(x, _) => match (&mut x.lhs, &mut x.rhs) { // "xxx" in "xxxxx" diff --git a/src/packages/mod.rs b/src/packages/mod.rs index 072de190..dae31183 100644 --- a/src/packages/mod.rs +++ b/src/packages/mod.rs @@ -56,7 +56,9 @@ pub(crate) struct PackagesCollection(StaticVec); impl PackagesCollection { /// Add a `PackageLibrary` into the `PackagesCollection`. - pub fn push(&mut self, package: PackageLibrary) { + /// + /// Packages are searched in reverse order. + pub fn add(&mut self, package: PackageLibrary) { // Later packages override previous ones. self.0.insert(0, package); } diff --git a/src/parser.rs b/src/parser.rs index 11e79a17..8897232f 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -69,7 +69,7 @@ struct ParseState<'e> { allow_capture: bool, /// Encapsulates a local stack with imported module names. #[cfg(not(feature = "no_module"))] - modules: Vec, + modules: StaticVec, /// Maximum levels of expression nesting. #[cfg(not(feature = "unchecked"))] max_expr_depth: usize, @@ -99,11 +99,11 @@ impl<'e> ParseState<'e> { #[cfg(not(feature = "no_function"))] max_function_expr_depth, #[cfg(not(feature = "no_closure"))] - externals: Default::default(), + externals: HashMap::with_capacity(8), #[cfg(not(feature = "no_closure"))] allow_capture: true, - strings: Default::default(), - stack: Default::default(), + strings: HashMap::with_capacity(64), + stack: Vec::with_capacity(16), #[cfg(not(feature = "no_module"))] modules: Default::default(), } @@ -818,7 +818,7 @@ fn parse_switch( } } - let mut table: HashMap = Default::default(); + let mut table = HashMap::with_capacity_and_hasher(16, StraightHasherBuilder); let mut def_stmt = None; loop { @@ -1137,17 +1137,15 @@ fn parse_unary( match token { // If statement is allowed to act as expressions - Token::If if settings.allow_if_expr => { - let mut block: StaticVec<_> = Default::default(); - block.push(parse_if(input, state, lib, settings.level_up())?); - Ok(Expr::Stmt(Box::new(block), settings.pos)) - } + Token::If if settings.allow_if_expr => Ok(Expr::Stmt( + Box::new(vec![parse_if(input, state, lib, settings.level_up())?].into()), + settings.pos, + )), // Switch statement is allowed to act as expressions - Token::Switch if settings.allow_switch_expr => { - let mut block: StaticVec<_> = Default::default(); - block.push(parse_switch(input, state, lib, settings.level_up())?); - Ok(Expr::Stmt(Box::new(block), settings.pos)) - } + Token::Switch if settings.allow_switch_expr => Ok(Expr::Stmt( + Box::new(vec![parse_switch(input, state, lib, settings.level_up())?].into()), + settings.pos, + )), // -expr Token::UnaryMinus => { let pos = eat_token(input, Token::UnaryMinus); @@ -2635,7 +2633,7 @@ fn parse_fn( let params: StaticVec<_> = params.into_iter().map(|(p, _)| p).collect(); #[cfg(not(feature = "no_closure"))] - let externals: HashSet<_> = state + let externals = state .externals .iter() .map(|(name, _)| name) @@ -2802,7 +2800,7 @@ fn parse_anon_fn( access: FnAccess::Public, params, #[cfg(not(feature = "no_closure"))] - externals: Default::default(), + externals: HashSet::with_capacity(4), body, lib: None, #[cfg(not(feature = "no_module"))] From c919ee4e46df09477cb110b13743571fd1326c58 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sun, 15 Nov 2020 13:49:54 +0800 Subject: [PATCH 12/19] Fine tune hash map sizes. --- codegen/src/module.rs | 2 +- src/ast.rs | 20 +++++++++++++------- src/engine.rs | 20 ++++++++++---------- src/module/mod.rs | 12 ++++++------ src/optimize.rs | 24 +++++++++++++----------- src/packages/array_basic.rs | 12 ++++++------ src/packages/mod.rs | 2 +- src/parser.rs | 19 ++++++++----------- src/scope.rs | 12 +++++++++++- 9 files changed, 69 insertions(+), 54 deletions(-) diff --git a/codegen/src/module.rs b/codegen/src/module.rs index eb3497bf..283436ab 100644 --- a/codegen/src/module.rs +++ b/codegen/src/module.rs @@ -259,7 +259,7 @@ impl Module { let mut mod_all = mod_all.unwrap(); let mod_name = mod_all.ident.clone(); let (_, orig_content) = mod_all.content.take().unwrap(); - let mod_attrs = mem::replace(&mut mod_all.attrs, Vec::with_capacity(0)); + let mod_attrs = mem::take(&mut mod_all.attrs); if !params.skip { // Generate new module items. diff --git a/src/ast.rs b/src/ast.rs index 44970d9c..0421a03b 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -16,10 +16,10 @@ use crate::FLOAT; use crate::engine::Imports; #[cfg(not(feature = "no_index"))] -use crate::engine::TYPICAL_ARRAY_SIZE; +use crate::{engine::TYPICAL_ARRAY_SIZE, Array}; #[cfg(not(feature = "no_object"))] -use crate::engine::TYPICAL_MAP_SIZE; +use crate::{engine::TYPICAL_MAP_SIZE, Map}; use crate::stdlib::{ borrow::Cow, @@ -129,7 +129,7 @@ impl fmt::Display for ScriptFnDef { /// # Thread Safety /// /// Currently, `AST` is neither `Send` nor `Sync`. Turn on the `sync` feature to make it `Send + Sync`. -#[derive(Debug, Clone, Default)] +#[derive(Debug, Clone)] pub struct AST( /// Global statements. Vec, @@ -137,11 +137,17 @@ pub struct AST( Module, ); +impl Default for AST { + fn default() -> Self { + Self(Vec::with_capacity(16), Default::default()) + } +} + impl AST { /// Create a new `AST`. #[inline(always)] - pub fn new(statements: Vec, lib: Module) -> Self { - Self(statements, lib) + pub fn new(statements: impl IntoIterator, lib: Module) -> Self { + Self(statements.into_iter().collect(), lib) } /// Get the statements. #[cfg(not(feature = "internals"))] @@ -937,14 +943,14 @@ impl Expr { #[cfg(not(feature = "no_index"))] Self::Array(x, _) if self.is_constant() => { - let mut arr = Vec::with_capacity(max(TYPICAL_ARRAY_SIZE, x.len())); + let mut arr = Array::with_capacity(max(TYPICAL_ARRAY_SIZE, x.len())); arr.extend(x.iter().map(|v| v.get_constant_value().unwrap())); Dynamic(Union::Array(Box::new(arr))) } #[cfg(not(feature = "no_object"))] Self::Map(x, _) if self.is_constant() => { - let mut map = HashMap::with_capacity(max(TYPICAL_MAP_SIZE, x.len())); + let mut map = Map::with_capacity(max(TYPICAL_MAP_SIZE, x.len())); map.extend( x.iter() .map(|(k, v)| (k.name.clone(), v.get_constant_value().unwrap())), diff --git a/src/engine.rs b/src/engine.rs index 06a249af..99a717be 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -720,10 +720,10 @@ impl Engine { #[cfg(any(feature = "no_std", target_arch = "wasm32",))] module_resolver: None, - type_names: HashMap::with_capacity(8), - disabled_symbols: HashSet::with_capacity(4), - custom_keywords: HashMap::with_capacity(4), - custom_syntax: HashMap::with_capacity(4), + type_names: Default::default(), + disabled_symbols: Default::default(), + custom_keywords: Default::default(), + custom_syntax: Default::default(), // variable resolver resolve_var: None, @@ -777,10 +777,10 @@ impl Engine { #[cfg(not(feature = "no_module"))] module_resolver: None, - type_names: HashMap::with_capacity(8), - disabled_symbols: HashSet::with_capacity(4), - custom_keywords: HashMap::with_capacity(4), - custom_syntax: HashMap::with_capacity(4), + type_names: Default::default(), + disabled_symbols: Default::default(), + custom_keywords: Default::default(), + custom_syntax: Default::default(), resolve_var: None, @@ -1726,7 +1726,7 @@ impl Engine { #[cfg(not(feature = "no_index"))] Expr::Array(x, _) => { - let mut arr = Vec::with_capacity(max(TYPICAL_ARRAY_SIZE, x.len())); + let mut arr = Array::with_capacity(max(TYPICAL_ARRAY_SIZE, x.len())); for item in x.as_ref() { arr.push(self.eval_expr(scope, mods, state, lib, this_ptr, item, level)?); } @@ -1735,7 +1735,7 @@ impl Engine { #[cfg(not(feature = "no_object"))] Expr::Map(x, _) => { - let mut map = HashMap::with_capacity(max(TYPICAL_MAP_SIZE, x.len())); + let mut map = Map::with_capacity(max(TYPICAL_MAP_SIZE, x.len())); for (IdentX { name: key, .. }, expr) in x.as_ref() { map.insert( key.clone(), diff --git a/src/module/mod.rs b/src/module/mod.rs index 53882fa8..7d798f70 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -81,12 +81,12 @@ pub struct Module { impl Default for Module { fn default() -> Self { Self { - modules: HashMap::with_capacity(4), - variables: HashMap::with_capacity(4), - all_variables: HashMap::with_capacity_and_hasher(32, StraightHasherBuilder), + modules: Default::default(), + variables: Default::default(), + all_variables: Default::default(), functions: HashMap::with_capacity_and_hasher(64, StraightHasherBuilder), all_functions: HashMap::with_capacity_and_hasher(256, StraightHasherBuilder), - type_iterators: HashMap::with_capacity(4), + type_iterators: Default::default(), indexed: false, } } @@ -1477,8 +1477,8 @@ impl Module { if !self.indexed { let mut qualifiers = Vec::with_capacity(4); - let mut variables = Vec::with_capacity(8); - let mut functions = Vec::with_capacity(16); + let mut variables = Vec::with_capacity(16); + let mut functions = Vec::with_capacity(256); qualifiers.push("root"); diff --git a/src/optimize.rs b/src/optimize.rs index 03b2bd3b..071dfe8f 100644 --- a/src/optimize.rs +++ b/src/optimize.rs @@ -753,7 +753,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) { } fn optimize( - statements: Vec, + mut statements: Vec, engine: &Engine, scope: &Scope, lib: &[&Module], @@ -761,6 +761,7 @@ fn optimize( ) -> Vec { // If optimization level is None then skip optimizing if level == OptimizationLevel::None { + statements.shrink_to_fit(); return statements; } @@ -779,16 +780,14 @@ fn optimize( let orig_constants_len = state.constants.len(); - let mut result = statements; - // Optimization loop loop { state.reset(); state.restore_constants(orig_constants_len); - let num_statements = result.len(); + let num_statements = statements.len(); - result.iter_mut().enumerate().for_each(|(i, stmt)| { + statements.iter_mut().enumerate().for_each(|(i, stmt)| { match stmt { Stmt::Const(var_def, expr, _, _) if expr.is_some() => { // Load constants @@ -828,26 +827,27 @@ fn optimize( } // Eliminate code that is pure but always keep the last statement - let last_stmt = result.pop(); + let last_stmt = statements.pop(); // Remove all pure statements at global level - result.retain(|stmt| !stmt.is_pure()); + statements.retain(|stmt| !stmt.is_pure()); // Add back the last statement unless it is a lone No-op if let Some(stmt) = last_stmt { - if !result.is_empty() || !stmt.is_noop() { - result.push(stmt); + if !statements.is_empty() || !stmt.is_noop() { + statements.push(stmt); } } - result + statements.shrink_to_fit(); + statements } /// Optimize an AST. pub fn optimize_into_ast( engine: &Engine, scope: &Scope, - statements: Vec, + mut statements: Vec, _functions: Vec, level: OptimizationLevel, ) -> AST { @@ -922,6 +922,8 @@ pub fn optimize_into_ast( #[cfg(feature = "no_function")] let lib = Default::default(); + statements.shrink_to_fit(); + AST::new( match level { OptimizationLevel::None => statements, diff --git a/src/packages/array_basic.rs b/src/packages/array_basic.rs index 339c4c94..c3554529 100644 --- a/src/packages/array_basic.rs +++ b/src/packages/array_basic.rs @@ -3,7 +3,7 @@ use crate::def_package; use crate::dynamic::Dynamic; -use crate::engine::{Array, OP_EQUALS}; +use crate::engine::{Array, OP_EQUALS, TYPICAL_ARRAY_SIZE}; use crate::fn_native::{FnPtr, NativeCallContext}; use crate::plugin::*; use crate::result::EvalAltResult; @@ -14,7 +14,7 @@ use crate::INT; #[cfg(not(feature = "no_object"))] use crate::engine::Map; -use crate::stdlib::{any::TypeId, boxed::Box, cmp::Ordering, string::ToString}; +use crate::stdlib::{any::TypeId, boxed::Box, cmp::max, cmp::Ordering, string::ToString}; pub type Unit = (); @@ -201,7 +201,7 @@ mod array_functions { list: &mut Array, mapper: FnPtr, ) -> Result> { - let mut array = Array::with_capacity(list.len()); + let mut array = Array::with_capacity(max(TYPICAL_ARRAY_SIZE, list.len())); for (i, item) in list.iter().enumerate() { array.push( @@ -233,7 +233,7 @@ mod array_functions { list: &mut Array, filter: FnPtr, ) -> Result> { - let mut array = Array::with_capacity(list.len()); + let mut array = Array::with_capacity(max(TYPICAL_ARRAY_SIZE, list.len())); for (i, item) in list.iter().enumerate() { if filter @@ -537,7 +537,7 @@ mod array_functions { list: &mut Array, filter: FnPtr, ) -> Result> { - let mut drained = Array::with_capacity(list.len()); + let mut drained = Array::with_capacity(max(TYPICAL_ARRAY_SIZE, list.len())); let mut i = list.len(); @@ -596,7 +596,7 @@ mod array_functions { list: &mut Array, filter: FnPtr, ) -> Result> { - let mut drained = Array::with_capacity(list.len()); + let mut drained = Array::with_capacity(max(TYPICAL_ARRAY_SIZE, list.len())); let mut i = list.len(); diff --git a/src/packages/mod.rs b/src/packages/mod.rs index dae31183..563da34a 100644 --- a/src/packages/mod.rs +++ b/src/packages/mod.rs @@ -130,7 +130,7 @@ macro_rules! def_package { impl $package { pub fn new() -> Self { - let mut module = $root::Module::new_with_capacity(512); + let mut module = $root::Module::new_with_capacity(1024); ::init(&mut module); Self(module.into()) } diff --git a/src/parser.rs b/src/parser.rs index 8897232f..bae5dbe4 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -41,9 +41,6 @@ use crate::stdlib::{ vec::Vec, }; -#[cfg(not(feature = "no_closure"))] -use crate::stdlib::collections::HashSet; - type PERR = ParseErrorType; type FunctionsLib = HashMap; @@ -99,7 +96,7 @@ impl<'e> ParseState<'e> { #[cfg(not(feature = "no_function"))] max_function_expr_depth, #[cfg(not(feature = "no_closure"))] - externals: HashMap::with_capacity(8), + externals: Default::default(), #[cfg(not(feature = "no_closure"))] allow_capture: true, strings: HashMap::with_capacity(64), @@ -2211,7 +2208,7 @@ fn parse_export( _ => (), } - let mut exports = Vec::new(); + let mut exports = Vec::with_capacity(4); loop { let (id, id_pos) = match input.next().unwrap() { @@ -2279,7 +2276,7 @@ fn parse_block( #[cfg(not(feature = "unchecked"))] settings.ensure_level_within_max_limit(state.max_expr_depth)?; - let mut statements = Vec::new(); + let mut statements = Vec::with_capacity(8); let prev_stack_len = state.stack.len(); #[cfg(not(feature = "no_module"))] @@ -2584,7 +2581,7 @@ fn parse_fn( (_, pos) => return Err(PERR::FnMissingParams(name).into_err(*pos)), }; - let mut params = Vec::new(); + let mut params: StaticVec<_> = Default::default(); if !match_token(input, Token::RightParen).0 { let sep_err = format!("to separate the parameters of function '{}'", name); @@ -2716,7 +2713,7 @@ fn parse_anon_fn( #[cfg(not(feature = "unchecked"))] settings.ensure_level_within_max_limit(state.max_expr_depth)?; - let mut params = Vec::new(); + let mut params: StaticVec<_> = Default::default(); if input.next().unwrap().0 != Token::Or { if !match_token(input, Token::Pipe).0 { @@ -2800,7 +2797,7 @@ fn parse_anon_fn( access: FnAccess::Public, params, #[cfg(not(feature = "no_closure"))] - externals: HashSet::with_capacity(4), + externals: Default::default(), body, lib: None, #[cfg(not(feature = "no_module"))] @@ -2877,8 +2874,8 @@ impl Engine { script_hash: u64, input: &mut TokenStream, ) -> Result<(Vec, Vec), ParseError> { - let mut statements: Vec = Default::default(); - let mut functions = Default::default(); + let mut statements = Vec::with_capacity(16); + let mut functions = HashMap::with_capacity_and_hasher(16, StraightHasherBuilder); let mut state = ParseState::new( self, script_hash, diff --git a/src/scope.rs b/src/scope.rs index 901755f0..eda857ce 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -65,7 +65,7 @@ impl EntryType { // // Since `Dynamic` is reasonably small, packing it tightly improves cache locality when variables are accessed. // The variable type is packed separately into another array because it is even smaller. -#[derive(Debug, Clone, Default)] +#[derive(Debug, Clone)] pub struct Scope<'a> { /// Current value of the entry. values: Vec, @@ -75,6 +75,16 @@ pub struct Scope<'a> { names: Vec<(Cow<'a, str>, Box>)>, } +impl Default for Scope<'_> { + fn default() -> Self { + Self { + values: Vec::with_capacity(16), + types: Vec::with_capacity(16), + names: Vec::with_capacity(16), + } + } +} + impl<'a> Scope<'a> { /// Create a new Scope. /// From fbe9425794544490bcc7334eb8007844db376602 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sun, 15 Nov 2020 18:39:23 +0800 Subject: [PATCH 13/19] Add discriminant to Dynamic::hash. --- src/dynamic.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/dynamic.rs b/src/dynamic.rs index 3ad4f10f..29c9aef9 100644 --- a/src/dynamic.rs +++ b/src/dynamic.rs @@ -22,6 +22,7 @@ use crate::stdlib::{ boxed::Box, fmt, hash::{Hash, Hasher}, + mem, ops::{Deref, DerefMut}, string::{String, ToString}, }; @@ -360,6 +361,8 @@ impl Dynamic { impl Hash for Dynamic { fn hash(&self, state: &mut H) { + mem::discriminant(self).hash(state); + match &self.0 { Union::Unit(_) => ().hash(state), Union::Bool(value) => value.hash(state), @@ -367,20 +370,17 @@ impl Hash for Dynamic { Union::Char(ch) => ch.hash(state), Union::Int(i) => i.hash(state), #[cfg(not(feature = "no_float"))] - Union::Float(f) => { - TypeId::of::().hash(state); - state.write(&f.to_le_bytes()); - } + Union::Float(f) => f.to_le_bytes().hash(state), #[cfg(not(feature = "no_index"))] - Union::Array(a) => a.hash(state), + Union::Array(a) => (**a).hash(state), #[cfg(not(feature = "no_object"))] Union::Map(m) => { - let mut buf: StaticVec<_> = m.keys().collect(); - buf.sort(); + let mut buf: StaticVec<_> = m.iter().collect(); + buf.sort_by(|(a, _), (b, _)| a.cmp(b)); - buf.into_iter().for_each(|key| { + buf.into_iter().for_each(|(key, value)| { key.hash(state); - m[key].hash(state); + value.hash(state); }) } From 937b45a18786e9ee89179b211b0f6c1d252ac4d2 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sun, 15 Nov 2020 23:14:16 +0800 Subject: [PATCH 14/19] Add Engine::load_module. --- doc/src/patterns/enums.md | 151 ++++++++++++++++++++++++++++----- doc/src/plugins/module.md | 36 ++++++++ doc/src/rust/modules/create.md | 22 ++++- src/engine.rs | 8 +- src/engine_api.rs | 50 ++++++++++- src/module/mod.rs | 18 ++-- 6 files changed, 251 insertions(+), 34 deletions(-) diff --git a/doc/src/patterns/enums.md b/doc/src/patterns/enums.md index c5663b44..b27eba08 100644 --- a/doc/src/patterns/enums.md +++ b/doc/src/patterns/enums.md @@ -9,48 +9,143 @@ it is impossible (short of registering a complete API) to distinguish between in enum variants or to extract internal data from them. -Switch Through Arrays ---------------------- - -An easy way to work with Rust enums is through exposing the internal data of each enum variant -as an [array], usually with the name of the variant as the first item: +Simulate an Enum API +-------------------- ```rust -use rhai::{Engine, Array}; +use rhai::{Engine, RegisterFn, Dynamic, EvalAltResult}; +use rhai::plugin::*; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Eq, PartialEq, Hash)] enum MyEnum { Foo, Bar(i64), Baz(String, bool) } -impl MyEnum { - fn get_enum_data(&mut self) -> Array { - match self { - Self::Foo => vec![ - "Foo".into() - ] as Array, - Self::Bar(num) => vec![ - "Bar".into(), (*num).into() - ] as Array, - Self::Baz(name, option) => vec![ - "Baz".into(), name.clone().into(), (*option).into() - ] as Array - } - } +// Create a plugin module with functions constructing the 'MyEnum' variants +#[export_module] +pub mod MyEnumModule { + // 'MyEnum' variants + pub const Foo: &MyEnum = MyEnum::Foo; + pub fn Bar(value: i64) -> MyEnum { MyEnum::Bar(value) } + pub fn Baz(val1: String, val2: bool) -> MyEnum { MyEnum::Baz(val1, val2) } } +let mut engine = Engine::new(); + +// Register API for 'MyEnum' engine + // Register enum custom type .register_type_with_name::("MyEnum") - .register_get("enum_data", MyEnum::get_enum_data); + // Register access to fields + .register_get("type", |a: &mut MyEnum| match a { + MyEnum::Foo => "Foo".to_string(), + MyEnum::Bar(_) => "Bar".to_string(), + MyEnum::Baz(_, _) => "Baz".to_string() + }) + .register_get("field_0", |a: &mut MyEnum| match a { + MyEnum::Foo => Dynamic::UNIT, + MyEnum::Bar(x) => Dynamic::from(x), + MyEnum::Baz(x, _) => Dynamic::from(x) + }) + .register_get("field_1", |a: &mut MyEnum| match a { + MyEnum::Foo | MyEnum::Bar(_) => Dynamic::UNIT, + MyEnum::Baz(_, x) => Dynamic::from(x) + }) + // Register printing + .register_fn("to_string", |a: &mut MyEnum| format!("{:?}", a)) + .register_fn("print", |a: &mut MyEnum| format!("{:?}", a)) + .register_fn("debug", |a: &mut MyEnum| format!("{:?}", a)) + .register_fn("+", |s: &str, a: MyEnum| format!("{}{:?}", s, a)) + .register_fn("+", |a: &mut MyEnum, s: &str| format!("{:?}", a).push_str(s)) + .register_fn("+=", |s: &mut ImmutableString, a: MyEnum| s += a.to_string()) + // Register '==' and '!=' operators + .register_fn("==", |a: &mut MyEnum, b: MyEnum| a == &b) + .register_fn("!=", |a: &mut MyEnum, b: MyEnum| a != &b) + // Register array functions + .register_fn("push", |list: &mut Array, item: MyEnum| list.push(Dynamic::from(item))) + .register_fn("+=", |list: &mut Array, item: MyEnum| list.push(Dynamic::from(item))) + .register_fn("insert", |list: &mut Array, position: i64, item: MyEnum| { + if position <= 0 { + list.insert(0, Dynamic::from(item)); + } else if (position as usize) >= list.len() - 1 { + list.push(item); + } else { + list.insert(position as usize, Dynamic::from(item)); + } + }).register_fn("pad", |list: &mut Array, len: i64, item: MyEnum| { + if len as usize > list.len() { list.resize(len as usize, item); } + }) + // Load the module as the module namespace "MyEnum" + .register_module("MyEnum", exported_module!(MyEnumModule)); +``` + +Instead of registering all these manually, it is often convenient to wrap them up into +a [custom package] that can be loaded into any [`Engine`]. + +With this API in place, working with enums will be almost the same as in Rust: + +```rust +let x = MyEnum::Foo; + +let y = MyEnum::Bar(42); + +let z = MyEnum::Baz("hello", true); + +x == MyEnum::Foo; + +y != MyEnum::Bar(0); + +// Detect enum types + +x.type == "Foo"; + +y.type == "Bar"; + +z.type == "Baz"; + +// Extract enum fields + +y.field_0 == 42; + +y.field_1 == (); + +z.field_0 == "hello"; + +z.field_1 == true; +``` + + +Use `switch` Through Arrays +--------------------------- + +Since enums are internally treated as [custom types], they are not _literals_ and cannot be +used as a match case in `switch` expressions. This is quite a limitation because the equivalent +`match` statement is commonly used in Rust to work with enums. + +One way to work with Rust enums in a `switch` expression is through exposing the internal data +of each enum variant as an [array], usually with the name of the variant as the first item: + +```rust +use rhai::Array; + +engine.register_get("enum_data", |x: &mut Enum} { + match x { + Enum::Foo => vec!["Foo".into()] as Array, + Enum::Bar(value) => vec!["Bar".into(), (*value).into()] as Array, + Enum::Baz(val1, val2) => vec![ + "Baz".into(), val1.clone().into(), (*val2).into() + ] as Array + } +}); ``` Then it is a simple matter to match an enum via the `switch` expression: ```c // Assume 'value' = 'MyEnum::Baz("hello", true)' -// 'get_data' creates a variable-length array with 'MyEnum' data +// 'enum_data' creates a variable-length array with 'MyEnum' data let x = switch value.enum_data { ["Foo"] => 1, ["Bar", 42] => 2, @@ -61,4 +156,14 @@ let x = switch value.enum_data { }; x == 5; + +// Which is essentially the same as: +let x = switch [value.type, value.field_0, value.field_1] { + ["Foo", (), ()] => 1, + ["Bar", 42, ()] => 2, + ["Bar", 123, ()] => 3, + ["Baz", "hello", false] => 4, + ["Baz", "hello", true] => 5, + _ => 9 +} ``` diff --git a/doc/src/plugins/module.md b/doc/src/plugins/module.md index e51f5103..29bac196 100644 --- a/doc/src/plugins/module.md +++ b/doc/src/plugins/module.md @@ -123,6 +123,42 @@ x == 43; Notice that, when using a [module] as a [package], only functions registered at the _top level_ can be accessed. Variables as well as sub-modules are ignored. +### Use `Engine::load_module` + +Another simple way to load this into an [`Engine`] is, again, to use the `exported_module!` macro +to turn it into a normal Rhai [module], then use the `Engine::load_module` method on it: + +```rust +fn main() { + let mut engine = Engine::new(); + + // The macro call creates a Rhai module from the plugin module. + let module = exported_module!(my_module); + + // A module can simply be loaded as a globally-available module. + engine.load_module("service", module); +} +``` + +The functions contained within the module definition (i.e. `greet`, `get_num` and `increment`), +plus the constant `MY_NUMBER`, are automatically loaded under the module namespace `service`: + +```rust +let x = service::greet("world"); +x == "hello, world!"; + +service::MY_NUMBER == 42; + +let x = service::greet(service::get_num().to_string()); +x == "hello, 42!"; + +let x = service::get_num(); +x == 42; + +service::increment(x); +x == 43; +``` + ### Use as loadable `Module` Using this directly as a dynamically-loadable Rhai [module] is almost the same, except that a diff --git a/doc/src/rust/modules/create.md b/doc/src/rust/modules/create.md index 069e5896..0a163837 100644 --- a/doc/src/rust/modules/create.md +++ b/doc/src/rust/modules/create.md @@ -25,7 +25,8 @@ Make the `Module` Available to the `Engine` `Engine::load_package` supports loading a [module] as a [package]. Since it acts as a [package], all functions will be registered into the _global_ namespace -and can be accessed without _module qualifiers_. +and can be accessed without _namespace qualifiers_. This is by far the easiest way to expose +a module's functionalities to Rhai. ```rust use rhai::{Engine, Module}; @@ -41,6 +42,25 @@ engine.eval::("inc(41)")? == 42; // no need to import module ``` +Make the `Module` a Global Module +------------------------------------ + +`Engine::load_module` loads a [module] and makes it available globally under a specific namespace. + +```rust +use rhai::{Engine, Module}; + +let mut module = Module::new(); // new module +module.set_fn_1("inc", |x: i64| Ok(x+1)); // use the 'set_fn_XXX' API to add functions + +// Load the module into the Engine as a sub-module named 'calc' +let mut engine = Engine::new(); +engine.load_module("calc", module); + +engine.eval::("calc::inc(41)")? == 42; // refer to the 'Calc' module +``` + + Make the `Module` Dynamically Loadable ------------------------------------- diff --git a/src/engine.rs b/src/engine.rs index 99a717be..af9b5b82 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -596,6 +596,8 @@ pub struct Engine { pub(crate) global_module: Module, /// A collection of all library packages loaded into the Engine. pub(crate) packages: PackagesCollection, + /// A collection of all sub-modules directly loaded into the Engine. + pub(crate) global_sub_modules: Imports, /// A module resolution service. #[cfg(not(feature = "no_module"))] @@ -711,6 +713,7 @@ impl Engine { packages: Default::default(), global_module: Default::default(), + global_sub_modules: Default::default(), #[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_std"))] @@ -773,6 +776,7 @@ impl Engine { packages: Default::default(), global_module: Default::default(), + global_sub_modules: Default::default(), #[cfg(not(feature = "no_module"))] module_resolver: None, @@ -1814,7 +1818,7 @@ impl Engine { Expr::True(_) => Ok(true.into()), Expr::False(_) => Ok(false.into()), - Expr::Unit(_) => Ok(().into()), + Expr::Unit(_) => Ok(Dynamic::UNIT), Expr::Custom(custom, _) => { let expressions = custom @@ -2090,7 +2094,7 @@ impl Engine { } else if let Some(def_stmt) = def_stmt { self.eval_stmt(scope, mods, state, lib, this_ptr, def_stmt, level) } else { - Ok(().into()) + Ok(Dynamic::UNIT) } } diff --git a/src/engine_api.rs b/src/engine_api.rs index 3fc8891e..b526f545 100644 --- a/src/engine_api.rs +++ b/src/engine_api.rs @@ -30,6 +30,9 @@ use crate::fn_register::{RegisterFn, RegisterResultFn}; #[cfg(not(feature = "no_function"))] use crate::{fn_args::FuncArgs, fn_call::ensure_no_data_race, module::Module, StaticVec}; +#[cfg(not(feature = "no_module"))] +use crate::fn_native::{shared_take_or_clone, Shared}; + #[cfg(not(feature = "no_optimize"))] use crate::optimize::optimize_into_ast; @@ -772,6 +775,45 @@ impl Engine { self.register_indexer_get(getter) .register_indexer_set(setter) } + /// Register a `Module` as a sub-module with the `Engine`. + /// + /// # Example + /// + /// ``` + /// # fn main() -> Result<(), Box> { + /// use rhai::{Engine, Module}; + /// + /// let mut engine = Engine::new(); + /// + /// // Create the module + /// let mut module = Module::new(); + /// module.set_fn_1("calc", |x: i64| Ok(x + 1)); + /// + /// // Register the module as a sub-module + /// engine.register_module("CalcService", module); + /// + /// assert_eq!(engine.eval::("CalcService::calc(41)")?, 42); + /// # Ok(()) + /// # } + /// ``` + #[cfg(not(feature = "no_module"))] + pub fn register_module( + &mut self, + name: impl Into, + module: impl Into>, + ) -> &mut Self { + let module = module.into(); + + if !module.is_indexed() { + // Index the module (making a clone copy if necessary) if it is not indexed + let mut module = shared_take_or_clone(module); + module.build_index(); + self.global_sub_modules.push(name, module); + } else { + self.global_sub_modules.push(name, module); + } + self + } /// Compile a string into an `AST`, which can be used later for evaluation. /// /// # Example @@ -1368,7 +1410,8 @@ impl Engine { scope: &mut Scope, ast: &AST, ) -> Result> { - let mut mods = Default::default(); + let mut mods = self.global_sub_modules.clone(); + let (result, _) = self.eval_ast_with_scope_raw(scope, &mut mods, ast)?; let typ = self.map_type_name(result.type_name()); @@ -1446,7 +1489,8 @@ impl Engine { scope: &mut Scope, ast: &AST, ) -> Result<(), Box> { - let mut mods = Default::default(); + let mut mods = self.global_sub_modules.clone(); + self.eval_statements_raw(scope, &mut mods, ast.statements(), &[ast.lib()]) .map(|_| ()) } @@ -1599,7 +1643,7 @@ impl Engine { .ok_or_else(|| EvalAltResult::ErrorFunctionNotFound(name.into(), NO_POS))?; let mut state = Default::default(); - let mut mods = Default::default(); + let mut mods = self.global_sub_modules.clone(); // Check for data race. if cfg!(not(feature = "no_closure")) { diff --git a/src/module/mod.rs b/src/module/mod.rs index 7d798f70..ebd15a3a 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -16,7 +16,11 @@ use crate::{calc_native_fn_hash, calc_script_fn_hash, StaticVec}; use crate::ast::ScriptFnDef; #[cfg(not(feature = "no_module"))] -use crate::{ast::AST, engine::Engine, scope::Scope}; +use crate::{ + ast::AST, + engine::{Engine, Imports}, + scope::Scope, +}; #[cfg(not(feature = "no_index"))] use crate::engine::{Array, FN_IDX_GET, FN_IDX_SET}; @@ -1361,7 +1365,8 @@ impl Module { ast: &AST, engine: &Engine, ) -> Result> { - let mut mods = Default::default(); + let mut mods = engine.global_sub_modules.clone(); + let orig_mods_len = mods.len(); // Run the script engine.eval_ast_with_scope_raw(&mut scope, &mut mods, &ast)?; @@ -1380,8 +1385,11 @@ impl Module { } }); - // Modules left in the scope become sub-modules - mods.iter().for_each(|(alias, m)| { + // Extra modules left in the scope become sub-modules + let mut func_mods: Imports = Default::default(); + + mods.into_iter().skip(orig_mods_len).for_each(|(alias, m)| { + func_mods.push(alias.clone(), m.clone()); module.set_sub_module(alias, m); }); @@ -1396,7 +1404,7 @@ impl Module { // Encapsulate AST environment let mut func = func.as_ref().clone(); func.lib = Some(ast_lib.clone()); - func.mods = mods.clone(); + func.mods = func_mods.clone(); module.set_script_fn(func.into()); }); } From b75964e383f3dc18d450cf7f34cbb3a539d56855 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sun, 15 Nov 2020 23:14:29 +0800 Subject: [PATCH 15/19] Add Dynamic::UNIT. --- RELEASES.md | 6 ++++++ doc/src/engine/custom-op.md | 2 +- doc/src/engine/custom-syntax.md | 2 +- doc/src/patterns/control.md | 2 +- doc/src/patterns/events.md | 2 +- doc/src/patterns/singleton.md | 2 +- doc/src/rust/packages/index.md | 2 +- doc/src/rust/register-raw.md | 2 +- doc/src/safety/progress.md | 2 +- src/dynamic.rs | 4 +++- src/fn_call.rs | 2 +- src/packages/array_basic.rs | 8 ++++---- src/packages/string_more.rs | 4 ++-- src/packages/time_basic.rs | 8 ++++---- src/parser.rs | 2 +- src/serde_impl/ser.rs | 4 ++-- tests/syntax.rs | 6 +++--- 17 files changed, 34 insertions(+), 26 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index 4913a137..22339cef 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -10,6 +10,12 @@ New features ------------ * `switch` statement. +* `Engine::register_module` to register a module as a sub-module in the global namespace. + +Enhancements +------------ + +* New constant `Dynamic::UNIT`. Version 0.19.5 diff --git a/doc/src/engine/custom-op.md b/doc/src/engine/custom-op.md index fa2b1487..e2a118e9 100644 --- a/doc/src/engine/custom-op.md +++ b/doc/src/engine/custom-op.md @@ -94,7 +94,7 @@ The following _precedence table_ shows the built-in precedence of standard Rhai | ------------------- | :-------------------------------------------------------------------------------------: | :----------------: | | Assignments | `=`, `+=`, `-=`, `*=`, `/=`, `~=`, `%=`,
`<<=`, `>>=`, `&=`, \|=, `^=` | 0 | | Logic and bit masks | \|\|, \|, `^` | 30 | -| Logic and bit masks | `&`, `&&` | 60 | +| Logic and bit masks | `&&`, `&` | 60 | | Comparisons | `==`, `!=` | 90 | | | `in` | 110 | | Comparisons | `>`, `>=`, `<`, `<=` | 130 | diff --git a/doc/src/engine/custom-syntax.md b/doc/src/engine/custom-syntax.md index ee086ec9..c9b77c90 100644 --- a/doc/src/engine/custom-syntax.md +++ b/doc/src/engine/custom-syntax.md @@ -216,7 +216,7 @@ fn implementation_func( } } - Ok(().into()) + Ok(Dynamic::UNIT) } // Register the custom syntax (sample): exec |x| -> { x += 1 } while x < 0; diff --git a/doc/src/patterns/control.md b/doc/src/patterns/control.md index 8743d260..25d73231 100644 --- a/doc/src/patterns/control.md +++ b/doc/src/patterns/control.md @@ -118,7 +118,7 @@ engine.register_result_fn("bunny_set_speed", move |speed: i64| return Err("Bunny is not yet going!".into()); } - Ok(().into()) + Ok(Dynamic::UNIT) ); ``` diff --git a/doc/src/patterns/events.md b/doc/src/patterns/events.md index 9334e726..0312e42e 100644 --- a/doc/src/patterns/events.md +++ b/doc/src/patterns/events.md @@ -137,7 +137,7 @@ impl Handler { // Default implementation of 'update' event handler self.scope.set_value("state2", SomeType::new(42)); // Turn function-not-found into a success - Ok(().into()) + Ok(Dynamic::UNIT) } _ => Err(err.into()) }) diff --git a/doc/src/patterns/singleton.md b/doc/src/patterns/singleton.md index 877180df..6f59f70a 100644 --- a/doc/src/patterns/singleton.md +++ b/doc/src/patterns/singleton.md @@ -131,7 +131,7 @@ pub mod bunny_api { Err("Bunny is not yet going!".into()) } else { b.borrow_mut().set_speed(speed); - Ok(().into()) + Ok(Dynamic::UNIT) } } pub fn turn_left(bunny: &mut SharedBunny) { diff --git a/doc/src/rust/packages/index.md b/doc/src/rust/packages/index.md index 020097cc..fe6fc36f 100644 --- a/doc/src/rust/packages/index.md +++ b/doc/src/rust/packages/index.md @@ -10,7 +10,7 @@ packages to be used. Packages typically contain Rust functions that are callable within a Rhai script. All functions registered in a package is loaded under the _global namespace_ -(i.e. they're available without module qualifiers). +(i.e. they're available without namespace qualifiers). Once a package is created (e.g. via `Package::new`), it can be _shared_ (via `Package::get`) among multiple instances of [`Engine`], even across threads (under [`sync`]). diff --git a/doc/src/rust/register-raw.md b/doc/src/rust/register-raw.md index c277436c..4d326e78 100644 --- a/doc/src/rust/register-raw.md +++ b/doc/src/rust/register-raw.md @@ -45,7 +45,7 @@ engine.register_raw_fn( *x += y; // perform the action - Ok(().into()) // must be 'Result>' + Ok(Dynamic::UNIT) // must be 'Result>' } ); diff --git a/doc/src/safety/progress.md b/doc/src/safety/progress.md index 39e274df..44260b43 100644 --- a/doc/src/safety/progress.md +++ b/doc/src/safety/progress.md @@ -37,7 +37,7 @@ wrapping this value. The termination token is commonly used to provide information on the _reason_ or _source_ behind the termination decision. -If the termination token is not needed, simply return `Some(().into())` to terminate the script +If the termination token is not needed, simply return `Some(Dynamic::UNIT)` to terminate the script run with [`()`] as the token. diff --git a/src/dynamic.rs b/src/dynamic.rs index 29c9aef9..592ca677 100644 --- a/src/dynamic.rs +++ b/src/dynamic.rs @@ -532,11 +532,13 @@ impl Clone for Dynamic { impl Default for Dynamic { #[inline(always)] fn default() -> Self { - Self(Union::Unit(())) + Self::UNIT } } impl Dynamic { + pub const UNIT: Dynamic = Self(Union::Unit(())); + /// Create a `Dynamic` from any type. A `Dynamic` value is simply returned as is. /// /// # Safety diff --git a/src/fn_call.rs b/src/fn_call.rs index 99991bea..7373c285 100644 --- a/src/fn_call.rs +++ b/src/fn_call.rs @@ -651,7 +651,7 @@ impl Engine { let script = script.trim(); if script.is_empty() { - return Ok(().into()); + return Ok(Dynamic::UNIT); } // Check for stack overflow diff --git a/src/packages/array_basic.rs b/src/packages/array_basic.rs index c3554529..41119a31 100644 --- a/src/packages/array_basic.rs +++ b/src/packages/array_basic.rs @@ -54,7 +54,7 @@ macro_rules! gen_array_functions { list.resize(len as usize, Dynamic::from(item)); } - Ok(().into()) + Ok(Dynamic::UNIT) } } })* } @@ -367,7 +367,7 @@ mod array_functions { list: &mut Array, reducer: FnPtr, ) -> Result> { - let mut result: Dynamic = ().into(); + let mut result: Dynamic = Dynamic::UNIT; for (i, item) in list.iter().enumerate() { result = reducer @@ -434,7 +434,7 @@ mod array_functions { list: &mut Array, reducer: FnPtr, ) -> Result> { - let mut result: Dynamic = ().into(); + let mut result: Dynamic = Dynamic::UNIT; for (i, item) in list.iter().enumerate().rev() { result = reducer @@ -529,7 +529,7 @@ mod array_functions { }) }); - Ok(().into()) + Ok(Dynamic::UNIT) } #[rhai_fn(return_raw)] pub fn drain( diff --git a/src/packages/string_more.rs b/src/packages/string_more.rs index 6025ad3f..fbf588c5 100644 --- a/src/packages/string_more.rs +++ b/src/packages/string_more.rs @@ -284,7 +284,7 @@ mod string_functions { } } - Ok(().into()) + Ok(Dynamic::UNIT) } #[rhai_fn(name = "pad", return_raw)] pub fn pad_with_string( @@ -328,7 +328,7 @@ mod string_functions { } } - Ok(().into()) + Ok(Dynamic::UNIT) } #[cfg(not(feature = "no_index"))] diff --git a/src/packages/time_basic.rs b/src/packages/time_basic.rs index f55288f2..e419905b 100644 --- a/src/packages/time_basic.rs +++ b/src/packages/time_basic.rs @@ -150,7 +150,7 @@ mod time_functions { #[rhai_fn(return_raw, name = "+=")] pub fn add_assign(x: &mut Instant, seconds: FLOAT) -> Result> { *x = add_impl(*x, seconds)?; - Ok(().into()) + Ok(Dynamic::UNIT) } #[rhai_fn(return_raw, name = "-")] pub fn subtract(x: Instant, seconds: FLOAT) -> Result> { @@ -162,7 +162,7 @@ mod time_functions { seconds: FLOAT, ) -> Result> { *x = subtract_impl(*x, seconds)?; - Ok(().into()) + Ok(Dynamic::UNIT) } } @@ -204,7 +204,7 @@ mod time_functions { #[rhai_fn(return_raw, name = "+=")] pub fn add_assign(x: &mut Instant, seconds: INT) -> Result> { *x = add_impl(*x, seconds)?; - Ok(().into()) + Ok(Dynamic::UNIT) } #[rhai_fn(return_raw, name = "-")] pub fn subtract(x: Instant, seconds: INT) -> Result> { @@ -213,7 +213,7 @@ mod time_functions { #[rhai_fn(return_raw, name = "-=")] pub fn subtract_assign(x: &mut Instant, seconds: INT) -> Result> { *x = subtract_impl(*x, seconds)?; - Ok(().into()) + Ok(Dynamic::UNIT) } #[rhai_fn(name = "==")] diff --git a/src/parser.rs b/src/parser.rs index bae5dbe4..2a04f708 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -957,7 +957,7 @@ fn parse_primary( let var_name_def = IdentX::new(state.get_interned_string(s), settings.pos); Expr::Variable(Box::new((None, None, 0, var_name_def))) } - // Module qualification + // Namespace qualification #[cfg(not(feature = "no_module"))] Token::Identifier(s) if *next_token == Token::DoubleColon => { // Once the identifier consumed we must enable next variables capturing diff --git a/src/serde_impl/ser.rs b/src/serde_impl/ser.rs index 4074577c..ba559859 100644 --- a/src/serde_impl/ser.rs +++ b/src/serde_impl/ser.rs @@ -247,7 +247,7 @@ impl Serializer for &mut DynamicSerializer { } fn serialize_none(self) -> Result> { - Ok(().into()) + Ok(Dynamic::UNIT) } fn serialize_some( @@ -258,7 +258,7 @@ impl Serializer for &mut DynamicSerializer { } fn serialize_unit(self) -> Result> { - Ok(().into()) + Ok(Dynamic::UNIT) } fn serialize_unit_struct(self, _name: &'static str) -> Result> { diff --git a/tests/syntax.rs b/tests/syntax.rs index d6a875cc..70272cef 100644 --- a/tests/syntax.rs +++ b/tests/syntax.rs @@ -1,4 +1,4 @@ -use rhai::{Engine, EvalAltResult, LexError, ParseError, ParseErrorType, INT, NO_POS}; +use rhai::{Dynamic, Engine, EvalAltResult, LexError, ParseError, ParseErrorType, INT, NO_POS}; #[test] fn test_custom_syntax() -> Result<(), Box> { @@ -48,7 +48,7 @@ fn test_custom_syntax() -> Result<(), Box> { } } - Ok(().into()) + Ok(Dynamic::UNIT) }, )?; @@ -65,7 +65,7 @@ fn test_custom_syntax() -> Result<(), Box> { // The first symbol must be an identifier assert_eq!( *engine - .register_custom_syntax(&["!"], 0, |_, _| Ok(().into())) + .register_custom_syntax(&["!"], 0, |_, _| Ok(Dynamic::UNIT)) .expect_err("should error") .0, ParseErrorType::BadInput(LexError::ImproperSymbol( From b6d35ab3103e537a230c815b8cfed902c044c742 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 16 Nov 2020 09:30:17 +0800 Subject: [PATCH 16/19] Fix no_module build. --- src/engine_api.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/engine_api.rs b/src/engine_api.rs index b526f545..e4ece6f8 100644 --- a/src/engine_api.rs +++ b/src/engine_api.rs @@ -28,11 +28,14 @@ use crate::{ use crate::fn_register::{RegisterFn, RegisterResultFn}; #[cfg(not(feature = "no_function"))] -use crate::{fn_args::FuncArgs, fn_call::ensure_no_data_race, module::Module, StaticVec}; +use crate::{fn_args::FuncArgs, fn_call::ensure_no_data_race, StaticVec}; #[cfg(not(feature = "no_module"))] use crate::fn_native::{shared_take_or_clone, Shared}; +#[cfg(any(not(feature = "no_function"), not(feature = "no_module")))] +use crate::module::Module; + #[cfg(not(feature = "no_optimize"))] use crate::optimize::optimize_into_ast; From cd621042962c8a38773962c6f7e7216487447889 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 16 Nov 2020 13:56:07 +0800 Subject: [PATCH 17/19] More Dynamic constants. --- src/dynamic.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/dynamic.rs b/src/dynamic.rs index 592ca677..4f6a48c4 100644 --- a/src/dynamic.rs +++ b/src/dynamic.rs @@ -537,7 +537,27 @@ impl Default for Dynamic { } impl Dynamic { + /// A `Dynamic` containing a `()`. pub const UNIT: Dynamic = Self(Union::Unit(())); + /// A `Dynamic` containing a `true`. + pub const TRUE: Dynamic = Self(Union::Bool(true)); + /// A `Dynamic` containing a `false`. + pub const FALSE: Dynamic = Self(Union::Bool(false)); + /// A `Dynamic` containing the integer zero. + pub const ZERO: Dynamic = Self(Union::Int(0)); + /// A `Dynamic` containing the integer one. + pub const ONE: Dynamic = Self(Union::Int(1)); + /// A `Dynamic` containing the integer negative one. + pub const NEGATIVE_ONE: Dynamic = Self(Union::Int(-1)); + /// A `Dynamic` containing the floating-point zero. + #[cfg(not(feature = "no_float"))] + pub const FLOAT_ZERO: Dynamic = Self(Union::Float(0.0)); + /// A `Dynamic` containing the floating-point one. + #[cfg(not(feature = "no_float"))] + pub const FLOAT_ONE: Dynamic = Self(Union::Float(1.0)); + /// A `Dynamic` containing the floating-point negative one. + #[cfg(not(feature = "no_float"))] + pub const FLOAT_NEGATIVE_ONE: Dynamic = Self(Union::Float(-1.0)); /// Create a `Dynamic` from any type. A `Dynamic` value is simply returned as is. /// From ef02150afdf3daf7634003affdf6ac0e78a3960d Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 16 Nov 2020 14:07:48 +0800 Subject: [PATCH 18/19] Expose methods for Engine::register_module. --- RELEASES.md | 2 +- doc/src/patterns/enums.md | 182 ++++++++++++++++++++++----------- doc/src/plugins/module.md | 17 ++- doc/src/rust/modules/create.md | 23 ++++- src/engine.rs | 73 ++++++++++--- src/engine_api.rs | 4 +- src/fn_call.rs | 46 +++++---- src/fn_native.rs | 6 +- src/module/mod.rs | 92 +++++++++++++---- src/optimize.rs | 2 +- src/packages/mod.rs | 18 +--- tests/for.rs | 44 +++++++- tests/modules.rs | 25 +++-- 13 files changed, 385 insertions(+), 149 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index 22339cef..7d904b64 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -10,7 +10,7 @@ New features ------------ * `switch` statement. -* `Engine::register_module` to register a module as a sub-module in the global namespace. +* `Engine::register_module` to register a module as a sub-module in the global namespace, while at the same time exposing its method functions globally. This is convenient when registering an API for a custom type. Enhancements ------------ diff --git a/doc/src/patterns/enums.md b/doc/src/patterns/enums.md index b27eba08..a33c0303 100644 --- a/doc/src/patterns/enums.md +++ b/doc/src/patterns/enums.md @@ -12,8 +12,10 @@ enum variants or to extract internal data from them. Simulate an Enum API -------------------- +A [plugin module] is extremely handy in creating an entire API for a custom enum type. + ```rust -use rhai::{Engine, RegisterFn, Dynamic, EvalAltResult}; +use rhai::{Engine, Dynamic, EvalAltResult}; use rhai::plugin::*; #[derive(Debug, Clone, Eq, PartialEq, Hash)] @@ -25,48 +27,76 @@ enum MyEnum { // Create a plugin module with functions constructing the 'MyEnum' variants #[export_module] -pub mod MyEnumModule { - // 'MyEnum' variants +mod MyEnumModule { + // Constructors for 'MyEnum' variants pub const Foo: &MyEnum = MyEnum::Foo; - pub fn Bar(value: i64) -> MyEnum { MyEnum::Bar(value) } - pub fn Baz(val1: String, val2: bool) -> MyEnum { MyEnum::Baz(val1, val2) } -} - -let mut engine = Engine::new(); - -// Register API for 'MyEnum' -engine - // Register enum custom type - .register_type_with_name::("MyEnum") - // Register access to fields - .register_get("type", |a: &mut MyEnum| match a { - MyEnum::Foo => "Foo".to_string(), - MyEnum::Bar(_) => "Bar".to_string(), - MyEnum::Baz(_, _) => "Baz".to_string() - }) - .register_get("field_0", |a: &mut MyEnum| match a { - MyEnum::Foo => Dynamic::UNIT, - MyEnum::Bar(x) => Dynamic::from(x), - MyEnum::Baz(x, _) => Dynamic::from(x) - }) - .register_get("field_1", |a: &mut MyEnum| match a { - MyEnum::Foo | MyEnum::Bar(_) => Dynamic::UNIT, - MyEnum::Baz(_, x) => Dynamic::from(x) - }) - // Register printing - .register_fn("to_string", |a: &mut MyEnum| format!("{:?}", a)) - .register_fn("print", |a: &mut MyEnum| format!("{:?}", a)) - .register_fn("debug", |a: &mut MyEnum| format!("{:?}", a)) - .register_fn("+", |s: &str, a: MyEnum| format!("{}{:?}", s, a)) - .register_fn("+", |a: &mut MyEnum, s: &str| format!("{:?}", a).push_str(s)) - .register_fn("+=", |s: &mut ImmutableString, a: MyEnum| s += a.to_string()) - // Register '==' and '!=' operators - .register_fn("==", |a: &mut MyEnum, b: MyEnum| a == &b) - .register_fn("!=", |a: &mut MyEnum, b: MyEnum| a != &b) - // Register array functions - .register_fn("push", |list: &mut Array, item: MyEnum| list.push(Dynamic::from(item))) - .register_fn("+=", |list: &mut Array, item: MyEnum| list.push(Dynamic::from(item))) - .register_fn("insert", |list: &mut Array, position: i64, item: MyEnum| { + pub fn Bar(value: i64) -> MyEnum { + MyEnum::Bar(value) + } + pub fn Baz(val1: String, val2: bool) -> MyEnum { + MyEnum::Baz(val1, val2) + } + // Access to fields + #[rhai_fn(get = "enum_type")] + pub fn get_type(a: &mut MyEnum) -> String { + match a { + MyEnum::Foo => "Foo".to_string(), + MyEnum::Bar(_) => "Bar".to_string(), + MyEnum::Baz(_, _) => "Baz".to_string() + } + } + #[rhai_fn(get = "field_0")] + pub fn get_field_0(a: &mut MyEnum) -> Dynamic { + match a { + MyEnum::Foo => Dynamic::UNIT, + MyEnum::Bar(x) => Dynamic::from(x), + MyEnum::Baz(x, _) => Dynamic::from(x) + } + } + #[rhai_fn(get = "field_1")] + pub fn get_field_1(a: &mut MyEnum) -> Dynamic { + match a { + MyEnum::Foo | MyEnum::Bar(_) => Dynamic::UNIT, + MyEnum::Baz(_, x) => Dynamic::from(x) + } + } + // Printing + #[rhai(name = "to_string", name = "print", name = "debug")] + pub fn to_string(a: &mut MyEnum) -> String { + format!("{:?}", a)) + } + #[rhai_fn(name = "+")] + pub fn add_to_str(s: &str, a: MyEnum) -> String { + format!("{}{:?}", s, a)) + } + #[rhai_fn(name = "+")] + pub fn add_str(a: &mut MyEnum, s: &str) -> String { + format!("{:?}", a).push_str(s)) + } + #[rhai_fn(name = "+=")] + pub fn append_to_str(s: &mut ImmutableString, a: MyEnum) -> String { + s += a.to_string()) + } + // '==' and '!=' operators + #[rhai_fn(name = "==")] + pub fn eq(a: &mut MyEnum, b: MyEnum) -> bool { + a == &b + } + #[rhai_fn(name = "!=")] + pub fn neq(a: &mut MyEnum, b: MyEnum) -> bool { + a != &b + } + // Array functions + #[rhai_fn(name = "push")] + pub fn append_to_array(list: &mut Array, item: MyEnum) { + list.push(Dynamic::from(item))); + } + #[rhai_fn(name = "+=")] + pub fn append_to_array_op(list: &mut Array, item: MyEnum) { + list.push(Dynamic::from(item))); + } + #[rhai_fn(name = "insert")] + pub fn insert_to_array(list: &mut Array, position: i64, item: MyEnum) { if position <= 0 { list.insert(0, Dynamic::from(item)); } else if (position as usize) >= list.len() - 1 { @@ -74,17 +104,22 @@ engine } else { list.insert(position as usize, Dynamic::from(item)); } - }).register_fn("pad", |list: &mut Array, len: i64, item: MyEnum| { + } + #[rhai_fn(name = "pad")] + pub fn pad_array(list: &mut Array, len: i64, item: MyEnum) { if len as usize > list.len() { list.resize(len as usize, item); } - }) - // Load the module as the module namespace "MyEnum" + } +} + +let mut engine = Engine::new(); + +// Load the module as the module namespace "MyEnum" +engine + .register_type_with_name::("MyEnum") .register_module("MyEnum", exported_module!(MyEnumModule)); ``` -Instead of registering all these manually, it is often convenient to wrap them up into -a [custom package] that can be loaded into any [`Engine`]. - -With this API in place, working with enums will be almost the same as in Rust: +With this API in place, working with enums feels almost the same as in Rust: ```rust let x = MyEnum::Foo; @@ -99,11 +134,11 @@ y != MyEnum::Bar(0); // Detect enum types -x.type == "Foo"; +x.enum_type == "Foo"; -y.type == "Bar"; +y.enum_type == "Bar"; -z.type == "Baz"; +z.enum_type == "Baz"; // Extract enum fields @@ -116,24 +151,49 @@ z.field_0 == "hello"; z.field_1 == true; ``` +Since enums are internally treated as [custom types], they are not _literals_ and cannot be +used as a match case in `switch` expressions. This is quite a limitation because the equivalent +`match` statement is commonly used in Rust to work with enums and bind variables to +variant-internal data. + +It is possible, however, to `switch` through enum variants based on their types: + +```c +switch x.enum_type { + "Foo" => ..., + "Bar" => { + let value = foo.field_0; + ... + } + "Baz" => { + let val1 = foo.field_0; + let val2 = foo.field_1; + ... + } +} +``` + Use `switch` Through Arrays --------------------------- -Since enums are internally treated as [custom types], they are not _literals_ and cannot be -used as a match case in `switch` expressions. This is quite a limitation because the equivalent -`match` statement is commonly used in Rust to work with enums. - -One way to work with Rust enums in a `switch` expression is through exposing the internal data -of each enum variant as an [array], usually with the name of the variant as the first item: +Another way to work with Rust enums in a `switch` expression is through exposing the internal data +of each enum variant as a variable-length [array], usually with the name of the variant as +the first item for convenience: ```rust use rhai::Array; -engine.register_get("enum_data", |x: &mut Enum} { +engine.register_get("enum_data", |x: &mut Enum| { match x { - Enum::Foo => vec!["Foo".into()] as Array, - Enum::Bar(value) => vec!["Bar".into(), (*value).into()] as Array, + Enum::Foo => vec![ + "Foo".into() + ] as Array, + + Enum::Bar(value) => vec![ + "Bar".into(), (*value).into() + ] as Array, + Enum::Baz(val1, val2) => vec![ "Baz".into(), val1.clone().into(), (*val2).into() ] as Array diff --git a/doc/src/plugins/module.md b/doc/src/plugins/module.md index 29bac196..1bd676c5 100644 --- a/doc/src/plugins/module.md +++ b/doc/src/plugins/module.md @@ -123,10 +123,10 @@ x == 43; Notice that, when using a [module] as a [package], only functions registered at the _top level_ can be accessed. Variables as well as sub-modules are ignored. -### Use `Engine::load_module` +### Use `Engine::register_module` Another simple way to load this into an [`Engine`] is, again, to use the `exported_module!` macro -to turn it into a normal Rhai [module], then use the `Engine::load_module` method on it: +to turn it into a normal Rhai [module], then use the `Engine::register_module` method on it: ```rust fn main() { @@ -136,7 +136,7 @@ fn main() { let module = exported_module!(my_module); // A module can simply be loaded as a globally-available module. - engine.load_module("service", module); + engine.register_module("service", module); } ``` @@ -159,6 +159,17 @@ service::increment(x); x == 43; ``` +`Engine::register_module` also exposes all _methods_ and _iterators_ from the module to the +_global_ namespace, so [getters/setters] and [indexers] for [custom types] work as expected. + +Therefore, in the example able, `increment` works fine when called in method-call style: + +```rust +let x = 42; +x.increment(); +x == 43; +``` + ### Use as loadable `Module` Using this directly as a dynamically-loadable Rhai [module] is almost the same, except that a diff --git a/doc/src/rust/modules/create.md b/doc/src/rust/modules/create.md index 0a163837..c47fa6bf 100644 --- a/doc/src/rust/modules/create.md +++ b/doc/src/rust/modules/create.md @@ -45,7 +45,7 @@ engine.eval::("inc(41)")? == 42; // no need to import module Make the `Module` a Global Module ------------------------------------ -`Engine::load_module` loads a [module] and makes it available globally under a specific namespace. +`Engine::register_module` loads a [module] and makes it available globally under a specific namespace. ```rust use rhai::{Engine, Module}; @@ -55,11 +55,30 @@ module.set_fn_1("inc", |x: i64| Ok(x+1)); // use the 'set_fn_XXX' API to add f // Load the module into the Engine as a sub-module named 'calc' let mut engine = Engine::new(); -engine.load_module("calc", module); +engine.register_module("calc", module); engine.eval::("calc::inc(41)")? == 42; // refer to the 'Calc' module ``` +`Engine::register_module` also exposes all _methods_ and _iterators_ from the module to the +_global_ namespace, so [getters/setters] and [indexers] for [custom types] work as expected. + +```rust +use rhai::{Engine, Module}; + +let mut module = Module::new(); // new module +module.set_fn_1_mut("inc", // add new method + |x: &mut i64| Ok(x+1) +); + +// Load the module into the Engine as a sub-module named 'calc' +let mut engine = Engine::new(); +engine.register_module("calc", module); + +// The method 'inc' works as expected because it is exposed to the global namespace +engine.eval::("let x = 41; x.inc()")? == 42; +``` + Make the `Module` Dynamically Loadable ------------------------------------- diff --git a/src/engine.rs b/src/engine.rs index af9b5b82..41c560b2 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -3,7 +3,7 @@ use crate::ast::{BinaryExpr, Expr, FnCallExpr, Ident, IdentX, 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, Shared}; +use crate::fn_native::{CallableFunction, Callback, FnPtr, IteratorFn, OnVarCallback, Shared}; use crate::module::{Module, NamespaceRef}; use crate::optimize::OptimizationLevel; use crate::packages::{Package, PackagesCollection, StandardPackage}; @@ -85,7 +85,7 @@ pub const TYPICAL_MAP_SIZE: usize = 8; // Small maps are typical // We cannot use &str or Cow here because `eval` may load a module and the module name will live beyond // the AST of the eval script text. The best we can do is a shared reference. #[derive(Debug, Clone, Default)] -pub struct Imports(StaticVec<(ImmutableString, Shared)>); +pub struct Imports(StaticVec<(ImmutableString, bool, Shared)>); impl Imports { /// Get the length of this stack of imported modules. @@ -98,7 +98,7 @@ impl Imports { } /// Get the imported module at a particular index. pub fn get(&self, index: usize) -> Option> { - self.0.get(index).map(|(_, m)| m).cloned() + self.0.get(index).map(|(_, _, m)| m).cloned() } /// Get the index of an imported module by name. pub fn find(&self, name: &str) -> Option { @@ -106,12 +106,21 @@ impl Imports { .iter() .enumerate() .rev() - .find(|(_, (key, _))| key.as_str() == name) + .find(|(_, (key, _, _))| key.as_str() == name) .map(|(index, _)| index) } /// Push an imported module onto the stack. pub fn push(&mut self, name: impl Into, module: impl Into>) { - self.0.push((name.into(), module.into())); + self.0.push((name.into(), false, module.into())); + } + /// Push a fixed module onto the stack. + #[cfg(not(feature = "no_module"))] + pub(crate) fn push_fixed( + &mut self, + name: impl Into, + module: impl Into>, + ) { + self.0.push((name.into(), true, module.into())); } /// Truncate the stack of imported modules to a particular length. pub fn truncate(&mut self, size: usize) { @@ -119,26 +128,59 @@ impl Imports { } /// Get an iterator to this stack of imported modules. #[allow(dead_code)] - pub fn iter(&self) -> impl Iterator)> { + pub fn iter(&self) -> impl Iterator)> { self.0 .iter() - .map(|(name, module)| (name.as_str(), module.clone())) + .map(|(name, fixed, module)| (name.as_str(), *fixed, module.clone())) } /// Get an iterator to this stack of imported modules. #[allow(dead_code)] pub(crate) fn iter_raw<'a>( &'a self, - ) -> impl Iterator)> + 'a { + ) -> impl Iterator)> + 'a { self.0.iter().cloned() } /// Get a consuming iterator to this stack of imported modules. - pub fn into_iter(self) -> impl Iterator)> { + pub fn into_iter(self) -> impl Iterator)> { self.0.into_iter() } /// Add a stream of imported modules. - pub fn extend(&mut self, stream: impl Iterator)>) { + pub fn extend( + &mut self, + stream: impl Iterator)>, + ) { self.0.extend(stream) } + /// Does the specified function hash key exist in this stack of imported modules? + #[allow(dead_code)] + pub fn contains_fn(&self, hash: u64) -> bool { + self.0 + .iter() + .any(|(_, fixed, m)| *fixed && m.contains_qualified_fn(hash)) + } + /// Get specified function via its hash key. + pub fn get_fn(&self, hash: u64) -> Option<&CallableFunction> { + self.0 + .iter() + .rev() + .filter(|&&(_, fixed, _)| fixed) + .find_map(|(_, _, m)| m.get_qualified_fn(hash)) + } + /// Does the specified TypeId iterator exist in this stack of imported modules? + #[allow(dead_code)] + pub fn contains_iter(&self, id: TypeId) -> bool { + self.0 + .iter() + .any(|(_, fixed, m)| *fixed && m.contains_qualified_iter(id)) + } + /// Get the specified TypeId iterator. + pub fn get_iter(&self, id: TypeId) -> Option { + self.0 + .iter() + .rev() + .filter(|&&(_, fixed, _)| fixed) + .find_map(|(_, _, m)| m.get_qualified_iter(id)) + } } #[cfg(not(feature = "unchecked"))] @@ -1947,7 +1989,8 @@ impl Engine { match self .global_module .get_fn(hash_fn, false) - .or_else(|| self.packages.get_fn(hash_fn, false)) + .or_else(|| self.packages.get_fn(hash_fn)) + .or_else(|| mods.get_fn(hash_fn)) { // op= function registered as method Some(func) if func.is_method() => { @@ -1965,9 +2008,10 @@ impl Engine { // Overriding exact implementation if func.is_plugin_fn() { - func.get_plugin_fn().call((self, mods, lib).into(), args)?; + func.get_plugin_fn() + .call((self, &*mods, lib).into(), args)?; } else { - func.get_native_fn()((self, mods, lib).into(), args)?; + func.get_native_fn()((self, &*mods, lib).into(), args)?; } } // Built-in op-assignment function @@ -2142,7 +2186,8 @@ impl Engine { let func = self .global_module .get_iter(iter_type) - .or_else(|| self.packages.get_iter(iter_type)); + .or_else(|| self.packages.get_iter(iter_type)) + .or_else(|| mods.get_iter(iter_type)); if let Some(func) = func { // Add the loop variable diff --git a/src/engine_api.rs b/src/engine_api.rs index e4ece6f8..60cc8603 100644 --- a/src/engine_api.rs +++ b/src/engine_api.rs @@ -811,9 +811,9 @@ impl Engine { // Index the module (making a clone copy if necessary) if it is not indexed let mut module = shared_take_or_clone(module); module.build_index(); - self.global_sub_modules.push(name, module); + self.global_sub_modules.push_fixed(name, module); } else { - self.global_sub_modules.push(name, module); + self.global_sub_modules.push_fixed(name, module); } self } diff --git a/src/fn_call.rs b/src/fn_call.rs index 7373c285..6b9f833b 100644 --- a/src/fn_call.rs +++ b/src/fn_call.rs @@ -175,7 +175,7 @@ impl Engine { /// **DO NOT** reuse the argument values unless for the first `&mut` argument - all others are silently replaced by `()`! pub(crate) fn call_native_fn( &self, - mods: &mut Imports, + mods: &Imports, state: &mut State, lib: &[&Module], fn_name: &str, @@ -192,7 +192,8 @@ impl Engine { // Then search packages let func = //lib.get_fn(hash_fn, pub_only) self.global_module.get_fn(hash_fn, pub_only) - .or_else(|| self.packages.get_fn(hash_fn, pub_only)); + .or_else(|| self.packages.get_fn(hash_fn)) + .or_else(|| mods.get_fn(hash_fn)); if let Some(func) = func { assert!(func.is_native()); @@ -428,6 +429,7 @@ impl Engine { #[inline] pub(crate) fn has_override_by_name_and_arguments( &self, + mods: &Imports, lib: &[&Module], fn_name: &str, arg_types: impl AsRef<[TypeId]>, @@ -437,13 +439,14 @@ impl Engine { let hash_fn = calc_native_fn_hash(empty(), fn_name, arg_types.iter().cloned()); let hash_script = calc_script_fn_hash(empty(), fn_name, arg_types.len()); - self.has_override(lib, hash_fn, hash_script, pub_only) + self.has_override(mods, lib, hash_fn, hash_script, pub_only) } // Has a system function an override? #[inline(always)] pub(crate) fn has_override( &self, + mods: &Imports, lib: &[&Module], hash_fn: u64, hash_script: u64, @@ -456,10 +459,13 @@ impl Engine { //|| lib.iter().any(|&m| m.contains_fn(hash_fn, pub_only)) // Then check registered functions //|| self.global_module.contains_fn(hash_script, pub_only) - || self.global_module.contains_fn(hash_fn, pub_only) + || self.global_module.contains_fn(hash_fn, false) // Then check packages - || self.packages.contains_fn(hash_script, pub_only) - || self.packages.contains_fn(hash_fn, pub_only) + || self.packages.contains_fn(hash_script) + || self.packages.contains_fn(hash_fn) + // Then check imported modules + || mods.contains_fn(hash_script) + || mods.contains_fn(hash_fn) } /// Perform an actual function call, native Rust or scripted, taking care of special functions. @@ -497,7 +503,8 @@ impl Engine { match fn_name { // type_of KEYWORD_TYPE_OF - if args.len() == 1 && !self.has_override(lib, hash_fn, hash_script, pub_only) => + if args.len() == 1 + && !self.has_override(mods, lib, hash_fn, hash_script, pub_only) => { Ok(( self.map_type_name(args[0].type_name()).to_string().into(), @@ -508,7 +515,8 @@ impl Engine { // Fn/eval - reaching this point it must be a method-style call, mostly like redirected // by a function pointer so it isn't caught at parse time. KEYWORD_FN_PTR | KEYWORD_EVAL - if args.len() == 1 && !self.has_override(lib, hash_fn, hash_script, pub_only) => + if args.len() == 1 + && !self.has_override(mods, lib, hash_fn, hash_script, pub_only) => { EvalAltResult::ErrorRuntime( format!( @@ -523,16 +531,14 @@ impl Engine { // Script-like function found #[cfg(not(feature = "no_function"))] - _ if lib.iter().any(|&m| m.contains_fn(hash_script, pub_only)) - //|| self.global_module.contains_fn(hash_script, pub_only) - || self.packages.contains_fn(hash_script, pub_only) => - { + _ if self.has_override(mods, lib, 0, hash_script, pub_only) => { // Get function let func = lib .iter() .find_map(|&m| m.get_fn(hash_script, pub_only)) //.or_else(|| self.global_module.get_fn(hash_script, pub_only)) - .or_else(|| self.packages.get_fn(hash_script, pub_only)) + .or_else(|| self.packages.get_fn(hash_script)) + //.or_else(|| mods.get_fn(hash_script)) .unwrap(); if func.is_script() { @@ -860,7 +866,7 @@ impl Engine { let hash_fn = calc_native_fn_hash(empty(), fn_name, once(TypeId::of::())); - if !self.has_override(lib, hash_fn, hash_script, pub_only) { + if !self.has_override(mods, lib, hash_fn, hash_script, pub_only) { // Fn - only in function call style return self .eval_expr(scope, mods, state, lib, this_ptr, &args_expr[0], level)? @@ -916,7 +922,7 @@ impl Engine { if name == KEYWORD_FN_PTR_CALL && args_expr.len() >= 1 - && !self.has_override(lib, 0, hash_script, pub_only) + && !self.has_override(mods, lib, 0, hash_script, pub_only) { let fn_ptr = self.eval_expr(scope, mods, state, lib, this_ptr, &args_expr[0], level)?; @@ -946,7 +952,7 @@ impl Engine { if name == KEYWORD_IS_DEF_VAR && args_expr.len() == 1 { let hash_fn = calc_native_fn_hash(empty(), name, once(TypeId::of::())); - if !self.has_override(lib, hash_fn, hash_script, pub_only) { + if !self.has_override(mods, lib, hash_fn, hash_script, pub_only) { let var_name = self.eval_expr(scope, mods, state, lib, this_ptr, &args_expr[0], level)?; let var_name = var_name.as_str().map_err(|err| { @@ -966,7 +972,7 @@ impl Engine { .cloned(), ); - if !self.has_override(lib, hash_fn, hash_script, pub_only) { + if !self.has_override(mods, lib, hash_fn, hash_script, pub_only) { let fn_name = self.eval_expr(scope, mods, state, lib, this_ptr, &args_expr[0], level)?; let num_params = @@ -993,7 +999,7 @@ impl Engine { if name == KEYWORD_EVAL && args_expr.len() == 1 { let hash_fn = calc_native_fn_hash(empty(), name, once(TypeId::of::())); - if !self.has_override(lib, hash_fn, hash_script, pub_only) { + if !self.has_override(mods, lib, hash_fn, hash_script, pub_only) { // eval - only in function call style let prev_len = scope.len(); let script = @@ -1194,7 +1200,7 @@ impl Engine { Some(f) if f.is_plugin_fn() => f .get_plugin_fn() .clone() - .call((self, mods, lib).into(), args.as_mut()), + .call((self, &*mods, lib).into(), args.as_mut()), Some(f) if f.is_native() => { if !f.is_method() { // Clone first argument @@ -1205,7 +1211,7 @@ impl Engine { } } - f.get_native_fn().clone()((self, mods, lib).into(), args.as_mut()) + f.get_native_fn().clone()((self, &*mods, lib).into(), args.as_mut()) } Some(_) => unreachable!(), None if def_val.is_some() => Ok(def_val.unwrap().into()), diff --git a/src/fn_native.rs b/src/fn_native.rs index 4e4fc6a7..83f3a7a7 100644 --- a/src/fn_native.rs +++ b/src/fn_native.rs @@ -56,10 +56,10 @@ pub struct NativeCallContext<'e, 'a, 'm, 'pm: 'm> { lib: &'m [&'pm Module], } -impl<'e, 'a, 'm, 'pm: 'm, M: AsRef<[&'pm Module]> + ?Sized> - From<(&'e Engine, &'a mut Imports, &'m M)> for NativeCallContext<'e, 'a, 'm, 'pm> +impl<'e, 'a, 'm, 'pm: 'm, M: AsRef<[&'pm Module]> + ?Sized> From<(&'e Engine, &'a Imports, &'m M)> + for NativeCallContext<'e, 'a, 'm, 'pm> { - fn from(value: (&'e Engine, &'a mut Imports, &'m M)) -> Self { + fn from(value: (&'e Engine, &'a Imports, &'m M)) -> Self { Self { engine: value.0, mods: Some(value.1), diff --git a/src/module/mod.rs b/src/module/mod.rs index ebd15a3a..580414f8 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -73,11 +73,13 @@ pub struct Module { all_variables: HashMap, /// External Rust functions. functions: HashMap, - /// Flattened collection of all external Rust functions, native or scripted, + /// Flattened collection of all external Rust functions, native or scripted. /// including those in sub-modules. all_functions: HashMap, /// Iterator functions, keyed by the type producing the iterator. type_iterators: HashMap, + /// Flattened collection of iterator functions, including those in sub-modules. + all_type_iterators: HashMap, /// Is the module indexed? indexed: bool, } @@ -91,6 +93,7 @@ impl Default for Module { functions: HashMap::with_capacity_and_hasher(64, StraightHasherBuilder), all_functions: HashMap::with_capacity_and_hasher(256, StraightHasherBuilder), type_iterators: Default::default(), + all_type_iterators: Default::default(), indexed: false, } } @@ -181,6 +184,7 @@ impl Module { && self.all_variables.is_empty() && self.modules.is_empty() && self.type_iterators.is_empty() + && self.all_type_iterators.is_empty() } /// Is the module indexed? @@ -1123,6 +1127,15 @@ impl Module { } } + /// Does the particular namespace-qualified function exist in the module? + /// + /// The `u64` hash is calculated by the function `crate::calc_native_fn_hash` and must match + /// the hash calculated by `build_index`. + #[inline] + pub fn contains_qualified_fn(&self, hash_fn: u64) -> bool { + self.all_functions.contains_key(&hash_fn) + } + /// Get a namespace-qualified function. /// Name and Position in `EvalAltResult` are None and must be set afterwards. /// @@ -1143,6 +1156,7 @@ impl Module { self.type_iterators.extend(other.type_iterators.into_iter()); self.all_functions.clear(); self.all_variables.clear(); + self.all_type_iterators.clear(); self.indexed = false; self } @@ -1160,6 +1174,7 @@ impl Module { self.type_iterators.extend(other.type_iterators.into_iter()); self.all_functions.clear(); self.all_variables.clear(); + self.all_type_iterators.clear(); self.indexed = false; self } @@ -1186,6 +1201,7 @@ impl Module { }); self.all_functions.clear(); self.all_variables.clear(); + self.all_type_iterators.clear(); self.indexed = false; self } @@ -1231,6 +1247,7 @@ impl Module { self.type_iterators.extend(other.type_iterators.iter()); self.all_functions.clear(); self.all_variables.clear(); + self.all_type_iterators.clear(); self.indexed = false; self } @@ -1250,6 +1267,7 @@ impl Module { self.all_functions.clear(); self.all_variables.clear(); + self.all_type_iterators.clear(); self.indexed = false; self } @@ -1388,10 +1406,13 @@ impl Module { // Extra modules left in the scope become sub-modules let mut func_mods: Imports = Default::default(); - mods.into_iter().skip(orig_mods_len).for_each(|(alias, m)| { - func_mods.push(alias.clone(), m.clone()); - module.set_sub_module(alias, m); - }); + mods.into_iter() + .skip(orig_mods_len) + .filter(|&(_, fixed, _)| !fixed) + .for_each(|(alias, _, m)| { + func_mods.push(alias.clone(), m.clone()); + module.set_sub_module(alias, m); + }); // Non-private functions defined become module functions #[cfg(not(feature = "no_function"))] @@ -1422,13 +1443,14 @@ impl Module { fn index_module<'a>( module: &'a Module, qualifiers: &mut Vec<&'a str>, - variables: &mut Vec<(u64, Dynamic)>, - functions: &mut Vec<(u64, CallableFunction)>, + variables: &mut HashMap, + functions: &mut HashMap, + type_iterators: &mut HashMap, ) { module.modules.iter().for_each(|(name, m)| { // Index all the sub-modules first. qualifiers.push(name); - index_module(m, qualifiers, variables, functions); + index_module(m, qualifiers, variables, functions, type_iterators); qualifiers.pop(); }); @@ -1436,8 +1458,14 @@ impl Module { module.variables.iter().for_each(|(var_name, value)| { // Qualifiers + variable name let hash_var = calc_script_fn_hash(qualifiers.iter().map(|&v| v), var_name, 0); - variables.push((hash_var, value.clone())); + variables.insert(hash_var, value.clone()); }); + + // Index type iterators + module.type_iterators.iter().for_each(|(&type_id, func)| { + type_iterators.insert(type_id, func.clone()); + }); + // Index all Rust functions module .functions @@ -1445,7 +1473,7 @@ impl Module { .filter(|(_, FuncInfo { access, .. })| access.is_public()) .for_each( |( - &_hash, + &hash, FuncInfo { name, params, @@ -1454,6 +1482,12 @@ impl Module { .. }, )| { + // Flatten all methods so they can be available without namespace qualifiers + #[cfg(not(feature = "no_object"))] + if func.is_method() { + functions.insert(hash, func.clone()); + } + if let Some(param_types) = types { assert_eq!(*params, param_types.len()); @@ -1469,15 +1503,17 @@ impl Module { // 3) The final hash is the XOR of the two hashes. let hash_qualified_fn = hash_qualified_script ^ hash_fn_args; - functions.push((hash_qualified_fn, func.clone())); + functions.insert(hash_qualified_fn, func.clone()); } else if cfg!(not(feature = "no_function")) { - let hash_qualified_script = if qualifiers.is_empty() { - _hash + let hash_qualified_script = if cfg!(feature = "no_object") + && qualifiers.is_empty() + { + hash } else { // Qualifiers + function name + number of arguments. calc_script_fn_hash(qualifiers.iter().map(|&v| v), &name, *params) }; - functions.push((hash_qualified_script, func.clone())); + functions.insert(hash_qualified_script, func.clone()); } }, ); @@ -1485,19 +1521,32 @@ impl Module { if !self.indexed { let mut qualifiers = Vec::with_capacity(4); - let mut variables = Vec::with_capacity(16); - let mut functions = Vec::with_capacity(256); + let mut variables = HashMap::with_capacity_and_hasher(16, StraightHasherBuilder); + let mut functions = HashMap::with_capacity_and_hasher(256, StraightHasherBuilder); + let mut type_iterators = HashMap::with_capacity(16); qualifiers.push("root"); - index_module(self, &mut qualifiers, &mut variables, &mut functions); + index_module( + self, + &mut qualifiers, + &mut variables, + &mut functions, + &mut type_iterators, + ); - self.all_variables = variables.into_iter().collect(); - self.all_functions = functions.into_iter().collect(); + self.all_variables = variables; + self.all_functions = functions; + self.all_type_iterators = type_iterators; self.indexed = true; } } + /// Does a type iterator exist in the entire module tree? + pub fn contains_qualified_iter(&self, id: TypeId) -> bool { + self.all_type_iterators.contains_key(&id) + } + /// Does a type iterator exist in the module? pub fn contains_iter(&self, id: TypeId) -> bool { self.type_iterators.contains_key(&id) @@ -1532,6 +1581,11 @@ impl Module { }) } + /// Get the specified type iterator. + pub(crate) fn get_qualified_iter(&self, id: TypeId) -> Option { + self.all_type_iterators.get(&id).cloned() + } + /// Get the specified type iterator. pub(crate) fn get_iter(&self, id: TypeId) -> Option { self.type_iterators.get(&id).cloned() diff --git a/src/optimize.rs b/src/optimize.rs index 071dfe8f..673a9ac5 100644 --- a/src/optimize.rs +++ b/src/optimize.rs @@ -673,7 +673,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) { let arg_types: StaticVec<_> = arg_values.iter().map(Dynamic::type_id).collect(); // Search for overloaded operators (can override built-in). - if !state.engine.has_override_by_name_and_arguments(state.lib, x.name.as_ref(), arg_types.as_ref(), false) { + if !state.engine.has_override_by_name_and_arguments(&state.engine.global_sub_modules, state.lib, x.name.as_ref(), arg_types.as_ref(), false) { if let Some(result) = run_builtin_binary_op(x.name.as_ref(), &arg_values[0], &arg_values[1]) .ok().flatten() .and_then(|result| map_dynamic_to_expr(result, *pos)) diff --git a/src/packages/mod.rs b/src/packages/mod.rs index 563da34a..df067fe1 100644 --- a/src/packages/mod.rs +++ b/src/packages/mod.rs @@ -64,16 +64,12 @@ impl PackagesCollection { } /// Does the specified function hash key exist in the `PackagesCollection`? #[allow(dead_code)] - pub fn contains_fn(&self, hash: u64, public_only: bool) -> bool { - self.0.iter().any(|p| p.contains_fn(hash, public_only)) + pub fn contains_fn(&self, hash: u64) -> bool { + self.0.iter().any(|p| p.contains_fn(hash, false)) } /// Get specified function via its hash key. - pub fn get_fn(&self, hash: u64, public_only: bool) -> Option<&CallableFunction> { - self.0 - .iter() - .map(|p| p.get_fn(hash, public_only)) - .find(|f| f.is_some()) - .flatten() + pub fn get_fn(&self, hash: u64) -> Option<&CallableFunction> { + self.0.iter().find_map(|p| p.get_fn(hash, false)) } /// Does the specified TypeId iterator exist in the `PackagesCollection`? #[allow(dead_code)] @@ -82,11 +78,7 @@ impl PackagesCollection { } /// Get the specified TypeId iterator. pub fn get_iter(&self, id: TypeId) -> Option { - self.0 - .iter() - .map(|p| p.get_iter(id)) - .find(|f| f.is_some()) - .flatten() + self.0.iter().find_map(|p| p.get_iter(id)) } } diff --git a/tests/for.rs b/tests/for.rs index 0de226f0..56899fd1 100644 --- a/tests/for.rs +++ b/tests/for.rs @@ -1,4 +1,4 @@ -use rhai::{Engine, EvalAltResult, INT}; +use rhai::{Engine, EvalAltResult, Module, INT}; #[cfg(not(feature = "no_index"))] #[test] @@ -75,3 +75,45 @@ fn test_for_object() -> Result<(), Box> { Ok(()) } + +#[derive(Debug, Clone)] +struct MyIterableType(String); + +impl IntoIterator for MyIterableType { + type Item = char; + type IntoIter = std::vec::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.0.chars().collect::>().into_iter() + } +} + +#[cfg(not(feature = "no_module"))] +#[test] +fn test_for_module_iterator() -> Result<(), Box> { + let mut engine = Engine::new(); + + // Set a type iterator deep inside a nested module chain + let mut sub_module = Module::new(); + sub_module.set_iterable::(); + sub_module.set_fn_0("new_ts", || Ok(MyIterableType("hello".to_string()))); + + let mut module = Module::new(); + module.set_sub_module("inner", sub_module); + + engine.register_module("testing", module); + + let script = r#" + let item = testing::inner::new_ts(); + let result = ""; + + for x in item { + result += x; + } + result + "#; + + assert_eq!(engine.eval::(script)?, "hello"); + + Ok(()) +} diff --git a/tests/modules.rs b/tests/modules.rs index 5bdbf99c..bd4f7730 100644 --- a/tests/modules.rs +++ b/tests/modules.rs @@ -23,6 +23,7 @@ fn test_module_sub_module() -> Result<(), Box> { sub_module2.set_var("answer", 41 as INT); let hash_inc = sub_module2.set_fn_1("inc", |x: INT| Ok(x + 1)); + sub_module2.set_fn_1_mut("super_inc", |x: &mut INT| Ok(*x + 1)); sub_module.set_sub_module("universe", sub_module2); module.set_sub_module("life", sub_module); @@ -39,26 +40,32 @@ fn test_module_sub_module() -> Result<(), Box> { assert_eq!(m2.get_var_value::("answer").unwrap(), 41); - let mut resolver = StaticModuleResolver::new(); - resolver.insert("question", module); - let mut engine = Engine::new(); - engine.set_module_resolver(Some(resolver)); + engine.register_module("question", module); + assert_eq!(engine.eval::("question::MYSTIC_NUMBER")?, 42); + assert!(engine.eval::("MYSTIC_NUMBER").is_err()); assert_eq!( - engine.eval::(r#"import "question" as q; q::MYSTIC_NUMBER"#)?, + engine.eval::("question::life::universe::answer + 1")?, 42 ); assert_eq!( - engine.eval::(r#"import "question" as q; q::life::universe::answer + 1"#)?, + engine.eval::("question::life::universe::inc(question::life::universe::answer)")?, 42 ); + assert!(engine + .eval::("inc(question::life::universe::answer)") + .is_err()); + + #[cfg(not(feature = "no_object"))] assert_eq!( - engine.eval::( - r#"import "question" as q; q::life::universe::inc(q::life::universe::answer)"# - )?, + engine.eval::("super_inc(question::life::universe::answer)")?, 42 ); + #[cfg(feature = "no_object")] + assert!(engine + .eval::("super_inc(question::life::universe::answer)") + .is_err()); Ok(()) } From adb902326e641b4ca2512ef65a536cdb08a34093 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 16 Nov 2020 16:28:04 +0800 Subject: [PATCH 19/19] Reduce feature gates on imports. --- src/ast.rs | 13 ++--- src/dynamic.rs | 47 ++++++------------ src/engine.rs | 58 +++++++--------------- src/engine_api.rs | 95 +++++++++++++++--------------------- src/engine_settings.rs | 17 +++---- src/fn_call.rs | 37 +++++--------- src/fn_native.rs | 20 +++++--- src/lib.rs | 3 ++ src/module/mod.rs | 83 +++++++++++++++---------------- src/optimize.rs | 9 ++-- src/packages/array_basic.rs | 2 +- src/packages/map_basic.rs | 2 +- src/packages/string_basic.rs | 4 +- src/packages/string_more.rs | 31 +++++++----- src/parser.rs | 33 +++++-------- src/result.rs | 5 +- src/scope.rs | 1 + src/serde_impl/de.rs | 11 ++--- src/serde_impl/ser.rs | 20 +++----- src/token.rs | 7 +-- 20 files changed, 206 insertions(+), 292 deletions(-) diff --git a/src/ast.rs b/src/ast.rs index 0421a03b..70e1425f 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -12,14 +12,11 @@ use crate::INT; #[cfg(not(feature = "no_float"))] use crate::FLOAT; -#[cfg(not(feature = "no_module"))] -use crate::engine::Imports; - #[cfg(not(feature = "no_index"))] -use crate::{engine::TYPICAL_ARRAY_SIZE, Array}; +use crate::Array; #[cfg(not(feature = "no_object"))] -use crate::{engine::TYPICAL_MAP_SIZE, Map}; +use crate::Map; use crate::stdlib::{ borrow::Cow, @@ -92,7 +89,7 @@ pub struct ScriptFnDef { pub lib: Option>, /// Encapsulated imported modules. #[cfg(not(feature = "no_module"))] - pub mods: Imports, + pub mods: crate::engine::Imports, /// Function name. pub name: ImmutableString, /// Function access mode. @@ -943,14 +940,14 @@ impl Expr { #[cfg(not(feature = "no_index"))] Self::Array(x, _) if self.is_constant() => { - let mut arr = Array::with_capacity(max(TYPICAL_ARRAY_SIZE, x.len())); + let mut arr = Array::with_capacity(max(crate::engine::TYPICAL_ARRAY_SIZE, x.len())); arr.extend(x.iter().map(|v| v.get_constant_value().unwrap())); Dynamic(Union::Array(Box::new(arr))) } #[cfg(not(feature = "no_object"))] Self::Map(x, _) if self.is_constant() => { - let mut map = Map::with_capacity(max(TYPICAL_MAP_SIZE, x.len())); + let mut map = Map::with_capacity(max(crate::engine::TYPICAL_MAP_SIZE, x.len())); map.extend( x.iter() .map(|(k, v)| (k.name.clone(), v.get_constant_value().unwrap())), diff --git a/src/dynamic.rs b/src/dynamic.rs index 4f6a48c4..ae23098d 100644 --- a/src/dynamic.rs +++ b/src/dynamic.rs @@ -5,17 +5,14 @@ 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::FLOAT; #[cfg(not(feature = "no_index"))] -use crate::engine::Array; +use crate::Array; #[cfg(not(feature = "no_object"))] -use crate::{engine::Map, StaticVec}; +use crate::Map; use crate::stdlib::{ any::{type_name, Any, TypeId}, @@ -27,20 +24,6 @@ use crate::stdlib::{ string::{String, ToString}, }; -#[cfg(not(feature = "no_closure"))] -#[cfg(not(feature = "sync"))] -use crate::stdlib::cell::{Ref, RefMut}; - -#[cfg(not(feature = "no_closure"))] -#[cfg(feature = "sync")] -use crate::stdlib::sync::{RwLockReadGuard, RwLockWriteGuard}; - -#[cfg(not(feature = "no_object"))] -use crate::stdlib::collections::HashMap; - -#[cfg(not(feature = "no_index"))] -use crate::stdlib::vec::Vec; - #[cfg(not(feature = "no_std"))] #[cfg(not(target_arch = "wasm32"))] use crate::stdlib::time::Instant; @@ -165,7 +148,7 @@ pub enum Union { Variant(Box>), #[cfg(not(feature = "no_closure"))] - Shared(Shared>), + Shared(crate::Shared>), } /// Underlying `Variant` read guard for `Dynamic`. @@ -184,11 +167,11 @@ enum DynamicReadLockInner<'d, T: Variant + Clone> { /// A read guard to a shared `RefCell`. #[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "sync"))] - Guard(Ref<'d, Dynamic>), + Guard(crate::stdlib::cell::Ref<'d, Dynamic>), /// A read guard to a shared `RwLock`. #[cfg(not(feature = "no_closure"))] #[cfg(feature = "sync")] - Guard(RwLockReadGuard<'d, Dynamic>), + Guard(crate::stdlib::sync::RwLockReadGuard<'d, Dynamic>), } impl<'d, T: Variant + Clone> Deref for DynamicReadLock<'d, T> { @@ -221,11 +204,11 @@ enum DynamicWriteLockInner<'d, T: Variant + Clone> { /// A write guard to a shared `RefCell`. #[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "sync"))] - Guard(RefMut<'d, Dynamic>), + Guard(crate::stdlib::cell::RefMut<'d, Dynamic>), /// A write guard to a shared `RwLock`. #[cfg(not(feature = "no_closure"))] #[cfg(feature = "sync")] - Guard(RwLockWriteGuard<'d, Dynamic>), + Guard(crate::stdlib::sync::RwLockWriteGuard<'d, Dynamic>), } impl<'d, T: Variant + Clone> Deref for DynamicWriteLock<'d, T> { @@ -375,7 +358,7 @@ impl Hash for Dynamic { Union::Array(a) => (**a).hash(state), #[cfg(not(feature = "no_object"))] Union::Map(m) => { - let mut buf: StaticVec<_> = m.iter().collect(); + let mut buf: crate::StaticVec<_> = m.iter().collect(); buf.sort_by(|(a, _), (b, _)| a.cmp(b)); buf.into_iter().for_each(|(key, value)| { @@ -694,7 +677,7 @@ impl Dynamic { #[cfg(not(feature = "no_closure"))] return match self.0 { Union::Shared(..) => self, - _ => Self(Union::Shared(Locked::new(self).into())), + _ => Self(Union::Shared(crate::Locked::new(self).into())), }; #[cfg(feature = "no_closure")] @@ -902,7 +885,7 @@ impl Dynamic { pub fn flatten(self) -> Self { match self.0 { #[cfg(not(feature = "no_closure"))] - Union::Shared(cell) => shared_try_take(cell).map_or_else( + Union::Shared(cell) => crate::fn_native::shared_try_take(cell).map_or_else( |cell| { #[cfg(not(feature = "sync"))] return cell.borrow().clone(); @@ -1312,9 +1295,9 @@ impl> From for Dynamic { } } #[cfg(not(feature = "no_index"))] -impl From> for Dynamic { +impl From> for Dynamic { #[inline(always)] - fn from(value: Vec) -> Self { + fn from(value: crate::stdlib::vec::Vec) -> Self { Self(Union::Array(Box::new( value.into_iter().map(Dynamic::from).collect(), ))) @@ -1330,9 +1313,11 @@ impl From<&[T]> for Dynamic { } } #[cfg(not(feature = "no_object"))] -impl, T: Variant + Clone> From> for Dynamic { +impl, T: Variant + Clone> From> + for Dynamic +{ #[inline(always)] - fn from(value: HashMap) -> Self { + fn from(value: crate::stdlib::collections::HashMap) -> Self { Self(Union::Map(Box::new( value .into_iter() diff --git a/src/engine.rs b/src/engine.rs index 41c560b2..ec1aadb0 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -1,6 +1,6 @@ //! Main module defining the script evaluation `Engine`. -use crate::ast::{BinaryExpr, Expr, FnCallExpr, Ident, IdentX, ReturnType, Stmt}; +use crate::ast::{Expr, FnCallExpr, Ident, IdentX, ReturnType, Stmt}; use crate::dynamic::{map_std_type_name, Dynamic, Union, Variant}; use crate::fn_call::run_builtin_op_assignment; use crate::fn_native::{CallableFunction, Callback, FnPtr, IteratorFn, OnVarCallback, Shared}; @@ -12,29 +12,11 @@ use crate::result::EvalAltResult; use crate::scope::{EntryType as ScopeEntryType, Scope}; use crate::syntax::CustomSyntax; use crate::token::{Position, NO_POS}; -use crate::utils::get_hasher; +use crate::utils::{get_hasher, ImmutableString}; use crate::{calc_native_fn_hash, StaticVec}; -#[cfg(not(feature = "no_index"))] -use crate::INT; - -#[cfg(not(feature = "no_module"))] -use crate::{fn_native::shared_take_or_clone, module::ModuleResolver}; - -#[cfg(not(feature = "no_std"))] -#[cfg(not(feature = "no_module"))] -#[cfg(not(target_arch = "wasm32"))] -use crate::module::resolvers; - -#[cfg(any(not(feature = "no_object"), not(feature = "no_module")))] -use crate::utils::ImmutableString; - -#[cfg(not(feature = "no_closure"))] -#[cfg(not(feature = "no_object"))] -use crate::dynamic::DynamicWriteLock; - use crate::stdlib::{ - any::type_name, + any::{type_name, TypeId}, borrow::Cow, boxed::Box, collections::{HashMap, HashSet}, @@ -46,12 +28,6 @@ use crate::stdlib::{ string::{String, ToString}, }; -#[cfg(not(feature = "no_index"))] -use crate::stdlib::any::TypeId; - -#[cfg(not(feature = "no_closure"))] -use crate::stdlib::mem; - /// Variable-sized array of `Dynamic` values. /// /// Not available under the `no_index` feature. @@ -61,9 +37,6 @@ pub type Array = crate::stdlib::vec::Vec; #[cfg(not(feature = "no_index"))] pub const TYPICAL_ARRAY_SIZE: usize = 8; // Small arrays are typical -#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] -use crate::stdlib::cmp::max; - /// Hash map of `Dynamic` values with `ImmutableString` keys. /// /// Not available under the `no_object` feature. @@ -228,6 +201,7 @@ pub const FN_IDX_GET: &str = "index$get$"; pub const FN_IDX_SET: &str = "index$set$"; #[cfg(not(feature = "no_function"))] pub const FN_ANONYMOUS: &str = "anon$"; +#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] pub const OP_EQUALS: &str = "=="; pub const MARKER_EXPR: &str = "$expr$"; pub const MARKER_BLOCK: &str = "$block$"; @@ -301,7 +275,7 @@ pub enum Target<'a> { /// It holds both the access guard and the original shared value. #[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_object"))] - LockGuard((DynamicWriteLock<'a, Dynamic>, Dynamic)), + LockGuard((crate::dynamic::DynamicWriteLock<'a, Dynamic>, Dynamic)), /// The target is a temporary `Dynamic` value (i.e. the mutation can cause no side effects). Value(Dynamic), /// The target is a character inside a String. @@ -643,7 +617,7 @@ pub struct Engine { /// A module resolution service. #[cfg(not(feature = "no_module"))] - pub(crate) module_resolver: Option>, + pub(crate) module_resolver: Option>, /// A hashmap mapping type names to pretty-print names. pub(crate) type_names: HashMap, @@ -760,7 +734,7 @@ impl Engine { #[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_std"))] #[cfg(not(target_arch = "wasm32"))] - module_resolver: Some(Box::new(resolvers::FileModuleResolver::new())), + module_resolver: Some(Box::new(crate::module::resolvers::FileModuleResolver::new())), #[cfg(not(feature = "no_module"))] #[cfg(any(feature = "no_std", target_arch = "wasm32",))] module_resolver: None, @@ -1291,7 +1265,7 @@ impl Engine { level: usize, new_val: Option<(Dynamic, Position)>, ) -> Result> { - let (BinaryExpr { lhs, rhs }, chain_type, op_pos) = match expr { + let (crate::ast::BinaryExpr { lhs, rhs }, chain_type, op_pos) = match expr { Expr::Index(x, pos) => (x.as_ref(), ChainType::Index, *pos), Expr::Dot(x, pos) => (x.as_ref(), ChainType::Dot, *pos), _ => unreachable!(), @@ -1393,7 +1367,7 @@ impl Engine { Expr::FnCall(_, _) => unreachable!(), Expr::Property(_) => idx_values.push(IndexChainValue::None), Expr::Index(x, _) | Expr::Dot(x, _) => { - let BinaryExpr { lhs, rhs, .. } = x.as_ref(); + let crate::ast::BinaryExpr { lhs, rhs, .. } = x.as_ref(); // Evaluate in left-to-right order let lhs_val = match lhs { @@ -1458,7 +1432,7 @@ impl Engine { // val_array[idx] let index = idx .as_int() - .map_err(|err| self.make_type_mismatch_err::(err, idx_pos))?; + .map_err(|err| self.make_type_mismatch_err::(err, idx_pos))?; let arr_len = arr.len(); @@ -1499,7 +1473,7 @@ impl Engine { let chars_len = s.chars().count(); let index = idx .as_int() - .map_err(|err| self.make_type_mismatch_err::(err, idx_pos))?; + .map_err(|err| self.make_type_mismatch_err::(err, idx_pos))?; if index >= 0 { let offset = index as usize; @@ -1772,7 +1746,8 @@ impl Engine { #[cfg(not(feature = "no_index"))] Expr::Array(x, _) => { - let mut arr = Array::with_capacity(max(TYPICAL_ARRAY_SIZE, x.len())); + let mut arr = + Array::with_capacity(crate::stdlib::cmp::max(TYPICAL_ARRAY_SIZE, x.len())); for item in x.as_ref() { arr.push(self.eval_expr(scope, mods, state, lib, this_ptr, item, level)?); } @@ -1781,7 +1756,8 @@ impl Engine { #[cfg(not(feature = "no_object"))] Expr::Map(x, _) => { - let mut map = Map::with_capacity(max(TYPICAL_MAP_SIZE, x.len())); + let mut map = + Map::with_capacity(crate::stdlib::cmp::max(TYPICAL_MAP_SIZE, x.len())); for (IdentX { name: key, .. }, expr) in x.as_ref() { map.insert( key.clone(), @@ -2372,7 +2348,7 @@ impl Engine { if let Some(name_def) = alias { if !module.is_indexed() { // Index the module (making a clone copy if necessary) if it is not indexed - let mut module = shared_take_or_clone(module); + let mut module = crate::fn_native::shared_take_or_clone(module); module.build_index(); mods.push(name_def.name.clone(), module); } else { @@ -2419,7 +2395,7 @@ impl Engine { if !val.is_shared() { // Replace the variable with a shared value. - *val = mem::take(val).into_shared(); + *val = crate::stdlib::mem::take(val).into_shared(); } } _ => (), diff --git a/src/engine_api.rs b/src/engine_api.rs index 60cc8603..dcff03a4 100644 --- a/src/engine_api.rs +++ b/src/engine_api.rs @@ -12,32 +12,10 @@ use crate::token::NO_POS; use crate::utils::get_hasher; #[cfg(not(feature = "no_index"))] -use crate::{ - engine::{Array, FN_IDX_GET, FN_IDX_SET}, - utils::ImmutableString, -}; +use crate::Array; #[cfg(not(feature = "no_object"))] -use crate::{ - engine::{make_getter, make_setter, Map}, - parse_error::ParseErrorType, - token::{Position, Token}, -}; - -#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] -use crate::fn_register::{RegisterFn, RegisterResultFn}; - -#[cfg(not(feature = "no_function"))] -use crate::{fn_args::FuncArgs, fn_call::ensure_no_data_race, StaticVec}; - -#[cfg(not(feature = "no_module"))] -use crate::fn_native::{shared_take_or_clone, Shared}; - -#[cfg(any(not(feature = "no_function"), not(feature = "no_module")))] -use crate::module::Module; - -#[cfg(not(feature = "no_optimize"))] -use crate::optimize::optimize_into_ast; +use crate::Map; use crate::stdlib::{ any::{type_name, TypeId}, @@ -46,9 +24,6 @@ use crate::stdlib::{ string::String, }; -#[cfg(not(feature = "no_optimize"))] -use crate::stdlib::mem; - #[cfg(not(feature = "no_std"))] #[cfg(not(target_arch = "wasm32"))] use crate::stdlib::{fs::File, io::prelude::*, path::PathBuf}; @@ -238,7 +213,7 @@ impl Engine { T: Variant + Clone, U: Variant + Clone, { - self.register_fn(&make_getter(name), callback) + crate::RegisterFn::register_fn(self, &crate::engine::make_getter(name), callback) } /// Register a getter function for a member of a registered type with the `Engine`. /// Returns `Result>`. @@ -285,7 +260,11 @@ impl Engine { name: &str, callback: impl Fn(&mut T) -> Result> + SendSync + 'static, ) -> &mut Self { - self.register_result_fn(&make_getter(name), callback) + crate::RegisterResultFn::register_result_fn( + self, + &crate::engine::make_getter(name), + callback, + ) } /// Register a setter function for a member of a registered type with the `Engine`. /// @@ -334,7 +313,7 @@ impl Engine { T: Variant + Clone, U: Variant + Clone, { - self.register_fn(&make_setter(name), callback) + crate::RegisterFn::register_fn(self, &crate::engine::make_setter(name), callback) } /// Register a setter function for a member of a registered type with the `Engine`. /// Returns `Result<(), Box>`. @@ -387,9 +366,11 @@ impl Engine { T: Variant + Clone, U: Variant + Clone, { - self.register_result_fn(&make_setter(name), move |obj: &mut T, value: U| { - callback(obj, value).map(Into::into) - }) + crate::RegisterResultFn::register_result_fn( + self, + &crate::engine::make_setter(name), + move |obj: &mut T, value: U| callback(obj, value).map(Into::into), + ) } /// Short-hand for registering both getter and setter functions /// of a registered type with the `Engine`. @@ -504,12 +485,12 @@ impl Engine { } if TypeId::of::() == TypeId::of::() || TypeId::of::() == TypeId::of::<&str>() - || TypeId::of::() == TypeId::of::() + || TypeId::of::() == TypeId::of::() { panic!("Cannot register indexer for strings."); } - self.register_fn(FN_IDX_GET, callback) + crate::RegisterFn::register_fn(self, crate::engine::FN_IDX_GET, callback) } /// Register an index getter for a custom type with the `Engine`. /// Returns `Result>`. @@ -574,12 +555,12 @@ impl Engine { } if TypeId::of::() == TypeId::of::() || TypeId::of::() == TypeId::of::<&str>() - || TypeId::of::() == TypeId::of::() + || TypeId::of::() == TypeId::of::() { panic!("Cannot register indexer for strings."); } - self.register_result_fn(FN_IDX_GET, callback) + crate::RegisterResultFn::register_result_fn(self, crate::engine::FN_IDX_GET, callback) } /// Register an index setter for a custom type with the `Engine`. /// @@ -642,12 +623,12 @@ impl Engine { } if TypeId::of::() == TypeId::of::() || TypeId::of::() == TypeId::of::<&str>() - || TypeId::of::() == TypeId::of::() + || TypeId::of::() == TypeId::of::() { panic!("Cannot register indexer for strings."); } - self.register_fn(FN_IDX_SET, callback) + crate::RegisterFn::register_fn(self, crate::engine::FN_IDX_SET, callback) } /// Register an index setter for a custom type with the `Engine`. /// Returns `Result<(), Box>`. @@ -714,14 +695,16 @@ impl Engine { } if TypeId::of::() == TypeId::of::() || TypeId::of::() == TypeId::of::<&str>() - || TypeId::of::() == TypeId::of::() + || TypeId::of::() == TypeId::of::() { panic!("Cannot register indexer for strings."); } - self.register_result_fn(FN_IDX_SET, move |obj: &mut T, index: X, value: U| { - callback(obj, index, value).map(Into::into) - }) + crate::RegisterResultFn::register_result_fn( + self, + crate::engine::FN_IDX_SET, + move |obj: &mut T, index: X, value: U| callback(obj, index, value).map(Into::into), + ) } /// Short-hand for register both index getter and setter functions for a custom type with the `Engine`. /// @@ -802,14 +785,14 @@ impl Engine { #[cfg(not(feature = "no_module"))] pub fn register_module( &mut self, - name: impl Into, - module: impl Into>, + name: impl Into, + module: impl Into>, ) -> &mut Self { let module = module.into(); if !module.is_indexed() { // Index the module (making a clone copy if necessary) if it is not indexed - let mut module = shared_take_or_clone(module); + let mut module = crate::fn_native::shared_take_or_clone(module); module.build_index(); self.global_sub_modules.push_fixed(name, module); } else { @@ -1078,6 +1061,8 @@ impl Engine { /// ``` #[cfg(not(feature = "no_object"))] pub fn parse_json(&self, json: &str, has_null: bool) -> Result> { + use crate::token::{Position, Token}; + let mut scope = Default::default(); // Trims the JSON string and add a '#' in front @@ -1087,7 +1072,7 @@ impl Engine { } else if json_text.starts_with(Token::LeftBrace.syntax().as_ref()) { ["#", json_text] } else { - return Err(ParseErrorType::MissingToken( + return Err(crate::ParseErrorType::MissingToken( Token::LeftBrace.syntax().into(), "to start a JSON object hash".into(), ) @@ -1535,7 +1520,7 @@ impl Engine { /// ``` #[cfg(not(feature = "no_function"))] #[inline] - pub fn call_fn( + pub fn call_fn( &self, scope: &mut Scope, ast: &AST, @@ -1543,7 +1528,7 @@ impl Engine { args: A, ) -> Result> { let mut arg_values = args.into_vec(); - let mut args: StaticVec<_> = arg_values.as_mut().iter_mut().collect(); + let mut args: crate::StaticVec<_> = arg_values.as_mut().iter_mut().collect(); let result = self.call_fn_dynamic_raw(scope, &[ast.lib()], name, &mut None, args.as_mut())?; @@ -1613,12 +1598,12 @@ impl Engine { pub fn call_fn_dynamic( &self, scope: &mut Scope, - lib: impl AsRef, + lib: impl AsRef, name: &str, mut this_ptr: Option<&mut Dynamic>, mut arg_values: impl AsMut<[Dynamic]>, ) -> Result> { - let mut args: StaticVec<_> = arg_values.as_mut().iter_mut().collect(); + let mut args: crate::StaticVec<_> = arg_values.as_mut().iter_mut().collect(); self.call_fn_dynamic_raw(scope, &[lib.as_ref()], name, &mut this_ptr, args.as_mut()) } @@ -1635,7 +1620,7 @@ impl Engine { pub(crate) fn call_fn_dynamic_raw( &self, scope: &mut Scope, - lib: &[&Module], + lib: &[&crate::Module], name: &str, this_ptr: &mut Option<&mut Dynamic>, args: &mut FnCallArgs, @@ -1650,7 +1635,7 @@ impl Engine { // Check for data race. if cfg!(not(feature = "no_closure")) { - ensure_no_data_race(name, args, false)?; + crate::fn_call::ensure_no_data_race(name, args, false)?; } self.call_script_fn(scope, &mut mods, &mut state, lib, this_ptr, fn_def, args, 0) @@ -1685,8 +1670,8 @@ impl Engine { #[cfg(feature = "no_function")] let lib = Default::default(); - let stmt = mem::take(ast.statements_mut()); - optimize_into_ast(self, scope, stmt, lib, optimization_level) + let stmt = crate::stdlib::mem::take(ast.statements_mut()); + crate::optimize::optimize_into_ast(self, scope, stmt, lib, optimization_level) } /// Provide a callback that will be invoked before each variable access. /// diff --git a/src/engine_settings.rs b/src/engine_settings.rs index 8add94b8..2c87a48a 100644 --- a/src/engine_settings.rs +++ b/src/engine_settings.rs @@ -4,12 +4,6 @@ use crate::engine::Engine; use crate::packages::PackageLibrary; use crate::token::{is_valid_identifier, Token}; -#[cfg(not(feature = "no_module"))] -use crate::module::ModuleResolver; - -#[cfg(not(feature = "no_optimize"))] -use crate::optimize::OptimizationLevel; - use crate::stdlib::{format, string::String}; #[cfg(not(feature = "no_module"))] @@ -31,7 +25,10 @@ impl Engine { /// Not available under the `no_optimize` feature. #[cfg(not(feature = "no_optimize"))] #[inline(always)] - pub fn set_optimization_level(&mut self, optimization_level: OptimizationLevel) -> &mut Self { + pub fn set_optimization_level( + &mut self, + optimization_level: crate::OptimizationLevel, + ) -> &mut Self { self.optimization_level = optimization_level; self } @@ -41,7 +38,7 @@ impl Engine { /// Not available under the `no_optimize` feature. #[cfg(not(feature = "no_optimize"))] #[inline(always)] - pub fn optimization_level(&self) -> OptimizationLevel { + pub fn optimization_level(&self) -> crate::OptimizationLevel { self.optimization_level } /// Set the maximum levels of function calls allowed for a script in order to avoid @@ -177,9 +174,9 @@ impl Engine { #[inline(always)] pub fn set_module_resolver( &mut self, - resolver: Option, + resolver: Option, ) -> &mut Self { - self.module_resolver = resolver.map(|f| Box::new(f) as Box); + self.module_resolver = resolver.map(|f| Box::new(f) as Box); self } /// Disable a particular keyword or operator in the language. diff --git a/src/fn_call.rs b/src/fn_call.rs index 6b9f833b..2a1973f3 100644 --- a/src/fn_call.rs +++ b/src/fn_call.rs @@ -18,20 +18,11 @@ use crate::token::NO_POS; use crate::utils::ImmutableString; use crate::{calc_native_fn_hash, calc_script_fn_hash, StaticVec, INT}; -#[cfg(not(feature = "no_function"))] -use crate::{ast::ScriptFnDef, r#unsafe::unsafe_cast_var_name_to_lifetime}; - #[cfg(not(feature = "no_float"))] use crate::FLOAT; -#[cfg(not(feature = "no_index"))] -use crate::engine::{FN_IDX_GET, FN_IDX_SET}; - #[cfg(not(feature = "no_object"))] -use crate::engine::{Map, Target, FN_GET, FN_SET}; - -#[cfg(not(feature = "no_closure"))] -use crate::engine::KEYWORD_IS_SHARED; +use crate::Map; use crate::stdlib::{ any::{type_name, TypeId}, @@ -44,9 +35,6 @@ 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; @@ -55,8 +43,8 @@ use num_traits::float::Float; #[inline(always)] fn extract_prop_from_getter(_fn_name: &str) -> Option<&str> { #[cfg(not(feature = "no_object"))] - if _fn_name.starts_with(FN_GET) { - return Some(&_fn_name[FN_GET.len()..]); + if _fn_name.starts_with(crate::engine::FN_GET) { + return Some(&_fn_name[crate::engine::FN_GET.len()..]); } None @@ -66,8 +54,8 @@ fn extract_prop_from_getter(_fn_name: &str) -> Option<&str> { #[inline(always)] fn extract_prop_from_setter(_fn_name: &str) -> Option<&str> { #[cfg(not(feature = "no_object"))] - if _fn_name.starts_with(FN_SET) { - return Some(&_fn_name[FN_SET.len()..]); + if _fn_name.starts_with(crate::engine::FN_SET) { + return Some(&_fn_name[crate::engine::FN_SET.len()..]); } None @@ -284,7 +272,7 @@ impl Engine { // index getter function not found? #[cfg(not(feature = "no_index"))] - if fn_name == FN_IDX_GET && args.len() == 2 { + if fn_name == crate::engine::FN_IDX_GET && args.len() == 2 { return EvalAltResult::ErrorFunctionNotFound( format!( "{} [{}]", @@ -298,7 +286,7 @@ impl Engine { // index setter function not found? #[cfg(not(feature = "no_index"))] - if fn_name == FN_IDX_SET { + if fn_name == crate::engine::FN_IDX_SET { return EvalAltResult::ErrorFunctionNotFound( format!( "{} [{}]=", @@ -345,7 +333,7 @@ impl Engine { state: &mut State, lib: &[&Module], this_ptr: &mut Option<&mut Dynamic>, - fn_def: &ScriptFnDef, + fn_def: &crate::ast::ScriptFnDef, args: &mut FnCallArgs, level: usize, ) -> Result> { @@ -372,7 +360,8 @@ impl Engine { .iter() .zip(args.iter_mut().map(|v| mem::take(*v))) .map(|(name, value)| { - let var_name: Cow<'_, str> = unsafe_cast_var_name_to_lifetime(name).into(); + let var_name: crate::stdlib::borrow::Cow<'_, str> = + crate::r#unsafe::unsafe_cast_var_name_to_lifetime(name).into(); (var_name, ScopeEntryType::Normal, value) }), ); @@ -699,7 +688,7 @@ impl Engine { lib: &[&Module], fn_name: &str, hash_script: u64, - target: &mut Target, + target: &mut crate::engine::Target, mut call_args: StaticVec, def_val: Option, native: bool, @@ -782,7 +771,7 @@ impl Engine { } else if { #[cfg(not(feature = "no_closure"))] { - fn_name == KEYWORD_IS_SHARED && call_args.is_empty() + fn_name == crate::engine::KEYWORD_IS_SHARED && call_args.is_empty() } #[cfg(feature = "no_closure")] false @@ -908,7 +897,7 @@ impl Engine { // Handle is_shared() #[cfg(not(feature = "no_closure"))] - if fn_name == KEYWORD_IS_SHARED && args_expr.len() == 1 { + if fn_name == crate::engine::KEYWORD_IS_SHARED && args_expr.len() == 1 { let value = self.eval_expr(scope, mods, state, lib, this_ptr, &args_expr[0], level)?; return Ok(value.is_shared().into()); diff --git a/src/fn_native.rs b/src/fn_native.rs index 83f3a7a7..6715d98d 100644 --- a/src/fn_native.rs +++ b/src/fn_native.rs @@ -10,15 +10,19 @@ use crate::token::{is_valid_identifier, NO_POS}; use crate::utils::ImmutableString; use crate::{calc_script_fn_hash, StaticVec}; -#[cfg(not(feature = "no_function"))] -use crate::engine::FN_ANONYMOUS; - use crate::stdlib::{boxed::Box, convert::TryFrom, fmt, iter::empty, mem, string::String}; -#[cfg(feature = "sync")] -use crate::stdlib::sync::{Arc, RwLock}; #[cfg(not(feature = "sync"))] -use crate::stdlib::{cell::RefCell, rc::Rc}; +use crate::stdlib::rc::Rc; +#[cfg(feature = "sync")] +use crate::stdlib::sync::Arc; + +#[cfg(any(not(feature = "no_closure"), not(feature = "no_module")))] +#[cfg(not(feature = "sync"))] +use crate::stdlib::cell::RefCell; +#[cfg(any(not(feature = "no_closure"), not(feature = "no_module")))] +#[cfg(feature = "sync")] +use crate::stdlib::sync::RwLock; /// Trait that maps to `Send + Sync` only under the `sync` feature. #[cfg(feature = "sync")] @@ -42,9 +46,11 @@ pub type Shared = Rc; pub type Shared = Arc; /// Synchronized shared object. +#[cfg(any(not(feature = "no_closure"), not(feature = "no_module")))] #[cfg(not(feature = "sync"))] pub type Locked = RefCell; /// Synchronized shared object. +#[cfg(any(not(feature = "no_closure"), not(feature = "no_module")))] #[cfg(feature = "sync")] pub type Locked = RwLock; @@ -249,7 +255,7 @@ impl FnPtr { #[cfg(not(feature = "no_function"))] #[inline(always)] pub fn is_anonymous(&self) -> bool { - self.0.starts_with(FN_ANONYMOUS) + self.0.starts_with(crate::engine::FN_ANONYMOUS) } /// Call the function pointer with curried arguments (if any). /// diff --git a/src/lib.rs b/src/lib.rs index aeca9235..4ab6b3a5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -124,6 +124,9 @@ pub use syntax::Expression; pub use token::{Position, NO_POS}; pub use utils::ImmutableString; +#[allow(dead_code)] +use fn_native::{Locked, Shared}; + #[cfg(feature = "internals")] pub use utils::{calc_native_fn_hash, calc_script_fn_hash}; diff --git a/src/module/mod.rs b/src/module/mod.rs index 580414f8..b22da434 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -10,27 +10,14 @@ use crate::fn_register::by_value as cast_arg; use crate::result::EvalAltResult; use crate::token::{Token, NO_POS}; use crate::utils::{ImmutableString, StraightHasherBuilder}; -use crate::{calc_native_fn_hash, calc_script_fn_hash, StaticVec}; - -#[cfg(not(feature = "no_function"))] -use crate::ast::ScriptFnDef; - -#[cfg(not(feature = "no_module"))] -use crate::{ - ast::AST, - engine::{Engine, Imports}, - scope::Scope, -}; +use crate::StaticVec; #[cfg(not(feature = "no_index"))] -use crate::engine::{Array, FN_IDX_GET, FN_IDX_SET}; - -#[cfg(not(feature = "no_object"))] -use crate::engine::{make_getter, make_setter}; +use crate::Array; #[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_object"))] -use crate::engine::Map; +use crate::Map; use crate::stdlib::{ any::TypeId, @@ -295,10 +282,10 @@ impl Module { /// If there is an existing function of the same name and number of arguments, it is replaced. #[cfg(not(feature = "no_function"))] #[inline] - pub(crate) fn set_script_fn(&mut self, fn_def: Shared) -> u64 { + pub(crate) fn set_script_fn(&mut self, fn_def: Shared) -> u64 { // None + function name + number of arguments. let num_params = fn_def.params.len(); - let hash_script = calc_script_fn_hash(empty(), &fn_def.name, num_params); + let hash_script = crate::calc_script_fn_hash(empty(), &fn_def.name, num_params); self.functions.insert( hash_script, FuncInfo { @@ -321,7 +308,7 @@ impl Module { name: &str, num_params: usize, public_only: bool, - ) -> Option<&Shared> { + ) -> Option<&Shared> { self.functions .values() .find( @@ -459,7 +446,7 @@ impl Module { ) -> u64 { let name = name.into(); - let hash_fn = calc_native_fn_hash(empty(), &name, arg_types.iter().cloned()); + let hash_fn = crate::calc_native_fn_hash(empty(), &name, arg_types.iter().cloned()); let params = arg_types .into_iter() @@ -678,7 +665,7 @@ impl Module { name: impl Into, func: impl Fn(&mut A) -> Result> + SendSync + 'static, ) -> u64 { - self.set_fn_1_mut(make_getter(&name.into()), func) + self.set_fn_1_mut(crate::engine::make_getter(&name.into()), func) } /// Set a Rust function taking two parameters into the module, returning a hash key. @@ -778,7 +765,7 @@ impl Module { name: impl Into, func: impl Fn(&mut A, B) -> Result<(), Box> + SendSync + 'static, ) -> u64 { - self.set_fn_2_mut(make_setter(&name.into()), func) + self.set_fn_2_mut(crate::engine::make_setter(&name.into()), func) } /// Set a Rust index getter taking two parameters (the first one mutable) into the module, @@ -822,7 +809,7 @@ impl Module { panic!("Cannot register indexer for strings."); } - self.set_fn_2_mut(FN_IDX_GET, func) + self.set_fn_2_mut(crate::engine::FN_IDX_GET, func) } /// Set a Rust function taking three parameters into the module, returning a hash key. @@ -961,7 +948,7 @@ impl Module { }; let arg_types = [TypeId::of::(), TypeId::of::(), TypeId::of::()]; self.set_fn( - FN_IDX_SET, + crate::engine::FN_IDX_SET, FnAccess::Public, &arg_types, CallableFunction::from_method(Box::new(f)), @@ -1307,7 +1294,7 @@ impl Module { #[inline(always)] pub(crate) fn iter_script_fn<'a>( &'a self, - ) -> impl Iterator)> + 'a { + ) -> impl Iterator)> + 'a { self.functions .values() .map(|f| &f.func) @@ -1352,7 +1339,7 @@ impl Module { #[inline(always)] pub fn iter_script_fn_info( &self, - ) -> impl Iterator)> { + ) -> impl Iterator)> { self.iter_script_fn() } @@ -1379,9 +1366,9 @@ impl Module { /// ``` #[cfg(not(feature = "no_module"))] pub fn eval_ast_as_new( - mut scope: Scope, - ast: &AST, - engine: &Engine, + mut scope: crate::Scope, + ast: &crate::AST, + engine: &crate::Engine, ) -> Result> { let mut mods = engine.global_sub_modules.clone(); let orig_mods_len = mods.len(); @@ -1404,7 +1391,7 @@ impl Module { }); // Extra modules left in the scope become sub-modules - let mut func_mods: Imports = Default::default(); + let mut func_mods: crate::engine::Imports = Default::default(); mods.into_iter() .skip(orig_mods_len) @@ -1457,7 +1444,8 @@ impl Module { // Index all variables module.variables.iter().for_each(|(var_name, value)| { // Qualifiers + variable name - let hash_var = calc_script_fn_hash(qualifiers.iter().map(|&v| v), var_name, 0); + let hash_var = + crate::calc_script_fn_hash(qualifiers.iter().map(|&v| v), var_name, 0); variables.insert(hash_var, value.clone()); }); @@ -1494,25 +1482,34 @@ impl Module { // Namespace-qualified Rust functions are indexed in two steps: // 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_script_fn_hash(qualifiers.iter().cloned(), name, *params); + let hash_qualified_script = crate::calc_script_fn_hash( + qualifiers.iter().cloned(), + name, + *params, + ); // 2) Calculate a second hash with no qualifiers, empty function name, // and the actual list of argument `TypeId`'.s - let hash_fn_args = - calc_native_fn_hash(empty(), "", param_types.iter().cloned()); + let hash_fn_args = crate::calc_native_fn_hash( + empty(), + "", + param_types.iter().cloned(), + ); // 3) The final hash is the XOR of the two hashes. let hash_qualified_fn = hash_qualified_script ^ hash_fn_args; functions.insert(hash_qualified_fn, func.clone()); } else if cfg!(not(feature = "no_function")) { - let hash_qualified_script = if cfg!(feature = "no_object") - && qualifiers.is_empty() - { - hash - } else { - // Qualifiers + function name + number of arguments. - calc_script_fn_hash(qualifiers.iter().map(|&v| v), &name, *params) - }; + let hash_qualified_script = + if cfg!(feature = "no_object") && qualifiers.is_empty() { + hash + } else { + // Qualifiers + function name + number of arguments. + crate::calc_script_fn_hash( + qualifiers.iter().map(|&v| v), + &name, + *params, + ) + }; functions.insert(hash_qualified_script, func.clone()); } }, diff --git a/src/optimize.rs b/src/optimize.rs index 673a9ac5..ab95b2fe 100644 --- a/src/optimize.rs +++ b/src/optimize.rs @@ -14,9 +14,6 @@ use crate::token::{is_valid_identifier, Position, NO_POS}; use crate::utils::get_hasher; use crate::{calc_native_fn_hash, StaticVec}; -#[cfg(not(feature = "no_function"))] -use crate::ast::ReturnType; - use crate::stdlib::{ boxed::Box, hash::{Hash, Hasher}, @@ -897,9 +894,11 @@ pub fn optimize_into_ast( // {} -> Noop fn_def.body = match body.pop().unwrap_or_else(|| Stmt::Noop(pos)) { // { return val; } -> val - Stmt::Return((ReturnType::Return, _), Some(expr), _) => Stmt::Expr(expr), + Stmt::Return((crate::ast::ReturnType::Return, _), Some(expr), _) => { + Stmt::Expr(expr) + } // { return; } -> () - Stmt::Return((ReturnType::Return, pos), None, _) => { + Stmt::Return((crate::ast::ReturnType::Return, pos), None, _) => { Stmt::Expr(Expr::Unit(pos)) } // All others diff --git a/src/packages/array_basic.rs b/src/packages/array_basic.rs index 41119a31..6d077d7b 100644 --- a/src/packages/array_basic.rs +++ b/src/packages/array_basic.rs @@ -12,7 +12,7 @@ use crate::utils::ImmutableString; use crate::INT; #[cfg(not(feature = "no_object"))] -use crate::engine::Map; +use crate::Map; use crate::stdlib::{any::TypeId, boxed::Box, cmp::max, cmp::Ordering, string::ToString}; diff --git a/src/packages/map_basic.rs b/src/packages/map_basic.rs index 104fbf6b..a46fb67d 100644 --- a/src/packages/map_basic.rs +++ b/src/packages/map_basic.rs @@ -8,7 +8,7 @@ use crate::utils::ImmutableString; use crate::INT; #[cfg(not(feature = "no_index"))] -use crate::engine::Array; +use crate::Array; def_package!(crate:BasicMapPackage:"Basic object map utilities.", lib, { combine_with_exported_module!(lib, "map", map_functions); diff --git a/src/packages/string_basic.rs b/src/packages/string_basic.rs index e02a1a20..99369195 100644 --- a/src/packages/string_basic.rs +++ b/src/packages/string_basic.rs @@ -8,10 +8,10 @@ use crate::utils::ImmutableString; use crate::INT; #[cfg(not(feature = "no_index"))] -use crate::engine::Array; +use crate::Array; #[cfg(not(feature = "no_object"))] -use crate::engine::Map; +use crate::Map; use crate::stdlib::{ fmt::{Debug, Display}, diff --git a/src/packages/string_more.rs b/src/packages/string_more.rs index fbf588c5..0e336f3e 100644 --- a/src/packages/string_more.rs +++ b/src/packages/string_more.rs @@ -8,9 +8,6 @@ use crate::utils::ImmutableString; use crate::StaticVec; use crate::INT; -#[cfg(not(feature = "unchecked"))] -use crate::{result::EvalAltResult, token::NO_POS}; - use crate::stdlib::{ any::TypeId, boxed::Box, format, mem, string::String, string::ToString, vec::Vec, }; @@ -255,11 +252,15 @@ mod string_functions { s: &mut ImmutableString, len: INT, ch: char, - ) -> Result> { + ) -> Result> { // Check if string will be over max size limit #[cfg(not(feature = "unchecked"))] if _ctx.engine().max_string_size() > 0 && len as usize > _ctx.engine().max_string_size() { - return EvalAltResult::ErrorDataTooLarge("Length of string".to_string(), NO_POS).into(); + return crate::EvalAltResult::ErrorDataTooLarge( + "Length of string".to_string(), + crate::NO_POS, + ) + .into(); } if len > 0 { @@ -275,9 +276,9 @@ mod string_functions { #[cfg(not(feature = "unchecked"))] if _ctx.engine().max_string_size() > 0 && s.len() > _ctx.engine().max_string_size() { - return EvalAltResult::ErrorDataTooLarge( + return crate::EvalAltResult::ErrorDataTooLarge( "Length of string".to_string(), - NO_POS, + crate::NO_POS, ) .into(); } @@ -292,11 +293,15 @@ mod string_functions { s: &mut ImmutableString, len: INT, padding: &str, - ) -> Result> { + ) -> Result> { // Check if string will be over max size limit #[cfg(not(feature = "unchecked"))] if _ctx.engine().max_string_size() > 0 && len as usize > _ctx.engine().max_string_size() { - return EvalAltResult::ErrorDataTooLarge("Length of string".to_string(), NO_POS).into(); + return crate::EvalAltResult::ErrorDataTooLarge( + "Length of string".to_string(), + crate::NO_POS, + ) + .into(); } if len > 0 { @@ -319,9 +324,9 @@ mod string_functions { #[cfg(not(feature = "unchecked"))] if _ctx.engine().max_string_size() > 0 && s.len() > _ctx.engine().max_string_size() { - return EvalAltResult::ErrorDataTooLarge( + return crate::EvalAltResult::ErrorDataTooLarge( "Length of string".to_string(), - NO_POS, + crate::NO_POS, ) .into(); } @@ -333,7 +338,7 @@ mod string_functions { #[cfg(not(feature = "no_index"))] pub mod arrays { - use crate::engine::Array; + use crate::Array; #[rhai_fn(name = "+")] pub fn append(x: &str, y: Array) -> String { @@ -356,7 +361,7 @@ mod string_functions { #[cfg(not(feature = "no_object"))] pub mod maps { - use crate::engine::Map; + use crate::Map; #[rhai_fn(name = "+")] pub fn append(x: &str, y: Map) -> String { diff --git a/src/parser.rs b/src/parser.rs index 2a04f708..59e4d4f8 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -19,15 +19,6 @@ use crate::{calc_script_fn_hash, StaticVec}; #[cfg(not(feature = "no_float"))] use crate::FLOAT; -#[cfg(not(feature = "no_object"))] -use crate::engine::{make_getter, make_setter, KEYWORD_EVAL, KEYWORD_FN_PTR}; - -#[cfg(not(feature = "no_function"))] -use crate::{ - ast::FnAccess, - engine::{FN_ANONYMOUS, KEYWORD_FN_PTR_CURRY}, -}; - use crate::stdlib::{ borrow::Cow, boxed::Box, @@ -228,8 +219,8 @@ impl Expr { match self { Self::Variable(x) if x.1.is_none() => { let ident = x.3; - let getter = state.get_interned_string(make_getter(&ident.name)); - let setter = state.get_interned_string(make_setter(&ident.name)); + let getter = state.get_interned_string(crate::engine::make_getter(&ident.name)); + let setter = state.get_interned_string(crate::engine::make_setter(&ident.name)); Self::Property(Box::new(((getter, setter), ident.into()))) } _ => self, @@ -1389,8 +1380,8 @@ fn make_dot_expr( // lhs.id (lhs, Expr::Variable(x)) if x.1.is_none() => { let ident = x.3; - let getter = state.get_interned_string(make_getter(&ident.name)); - let setter = state.get_interned_string(make_setter(&ident.name)); + let getter = state.get_interned_string(crate::engine::make_getter(&ident.name)); + let setter = state.get_interned_string(crate::engine::make_setter(&ident.name)); let rhs = Expr::Property(Box::new(((getter, setter), ident))); Expr::Dot(Box::new(BinaryExpr { lhs, rhs }), op_pos) @@ -1427,7 +1418,7 @@ fn make_dot_expr( } // lhs.Fn() or lhs.eval() (_, Expr::FnCall(x, pos)) - if x.args.len() == 0 && [KEYWORD_FN_PTR, KEYWORD_EVAL].contains(&x.name.as_ref()) => + if x.args.len() == 0 && [crate::engine::KEYWORD_FN_PTR, crate::engine::KEYWORD_EVAL].contains(&x.name.as_ref()) => { return Err(PERR::BadInput(LexError::ImproperSymbol(format!( "'{}' should not be called in method style. Try {}(...);", @@ -2378,9 +2369,9 @@ fn parse_stmt( Token::Fn | Token::Private => { let access = if matches!(token, Token::Private) { eat_token(input, Token::Private); - FnAccess::Private + crate::FnAccess::Private } else { - FnAccess::Public + crate::FnAccess::Public }; match input.next().unwrap() { @@ -2561,7 +2552,7 @@ fn parse_fn( input: &mut TokenStream, state: &mut ParseState, lib: &mut FunctionsLib, - access: FnAccess, + access: crate::FnAccess, mut settings: ParseSettings, ) -> Result { #[cfg(not(feature = "unchecked"))] @@ -2673,11 +2664,11 @@ fn make_curry_from_externals(fn_expr: Expr, externals: StaticVec, pos: P args.push(Expr::Variable(Box::new((None, None, 0, x.clone().into())))); }); - let hash = calc_script_fn_hash(empty(), KEYWORD_FN_PTR_CURRY, num_externals + 1); + let hash = calc_script_fn_hash(empty(), crate::engine::KEYWORD_FN_PTR_CURRY, num_externals + 1); let expr = Expr::FnCall( Box::new(FnCallExpr { - name: KEYWORD_FN_PTR_CURRY.into(), + name: crate::engine::KEYWORD_FN_PTR_CURRY.into(), hash, args, ..Default::default() @@ -2789,12 +2780,12 @@ fn parse_anon_fn( settings.pos.hash(hasher); let hash = hasher.finish(); - let fn_name: ImmutableString = format!("{}{:016x}", FN_ANONYMOUS, hash).into(); + let fn_name: ImmutableString = format!("{}{:016x}", crate::engine::FN_ANONYMOUS, hash).into(); // Define the function let script = ScriptFnDef { name: fn_name.clone(), - access: FnAccess::Public, + access: crate::FnAccess::Public, params, #[cfg(not(feature = "no_closure"))] externals: Default::default(), diff --git a/src/result.rs b/src/result.rs index 8cf405a2..7404fa2f 100644 --- a/src/result.rs +++ b/src/result.rs @@ -6,9 +6,6 @@ use crate::token::{Position, NO_POS}; use crate::utils::ImmutableString; use crate::INT; -#[cfg(not(feature = "no_function"))] -use crate::engine::is_anonymous_fn; - use crate::stdlib::{ boxed::Box, error::Error, @@ -159,7 +156,7 @@ impl fmt::Display for EvalAltResult { Self::ErrorParsing(p, _) => write!(f, "Syntax error: {}", p)?, #[cfg(not(feature = "no_function"))] - Self::ErrorInFunctionCall(s, err, _) if is_anonymous_fn(s) => { + Self::ErrorInFunctionCall(s, err, _) if crate::engine::is_anonymous_fn(s) => { write!(f, "Error in call to closure: {}", err)? } Self::ErrorInFunctionCall(s, err, _) => { diff --git a/src/scope.rs b/src/scope.rs index eda857ce..3152a27a 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -427,6 +427,7 @@ impl<'a> Scope<'a> { } /// Get an iterator to entries in the Scope. #[inline(always)] + #[allow(dead_code)] pub(crate) fn into_iter( self, ) -> impl Iterator, EntryType, Dynamic, Vec)> { diff --git a/src/serde_impl/de.rs b/src/serde_impl/de.rs index c040b191..8c17824a 100644 --- a/src/serde_impl/de.rs +++ b/src/serde_impl/de.rs @@ -13,13 +13,10 @@ use serde::de::{ use serde::Deserialize; #[cfg(not(feature = "no_index"))] -use crate::engine::Array; +use crate::Array; #[cfg(not(feature = "no_object"))] -use crate::engine::Map; - -#[cfg(not(feature = "no_object"))] -use serde::de::{EnumAccess, VariantAccess}; +use crate::Map; use crate::stdlib::{any::type_name, boxed::Box, fmt, string::ToString}; @@ -542,7 +539,7 @@ struct EnumDeserializer<'t, 'de: 't> { } #[cfg(not(feature = "no_object"))] -impl<'t, 'de> EnumAccess<'de> for EnumDeserializer<'t, 'de> { +impl<'t, 'de> serde::de::EnumAccess<'de> for EnumDeserializer<'t, 'de> { type Error = Box; type Variant = Self; @@ -556,7 +553,7 @@ impl<'t, 'de> EnumAccess<'de> for EnumDeserializer<'t, 'de> { } #[cfg(not(feature = "no_object"))] -impl<'t, 'de> VariantAccess<'de> for EnumDeserializer<'t, 'de> { +impl<'t, 'de> serde::de::VariantAccess<'de> for EnumDeserializer<'t, 'de> { type Error = Box; fn unit_variant(mut self) -> Result<(), Self::Error> { diff --git a/src/serde_impl/ser.rs b/src/serde_impl/ser.rs index ba559859..04cc03fd 100644 --- a/src/serde_impl/ser.rs +++ b/src/serde_impl/ser.rs @@ -5,9 +5,10 @@ use crate::result::EvalAltResult; use crate::token::NO_POS; #[cfg(not(feature = "no_index"))] -use crate::engine::Array; +use crate::Array; + #[cfg(not(feature = "no_object"))] -use crate::engine::Map; +use crate::Map; use serde::ser::{ Error, SerializeMap, SerializeSeq, SerializeStruct, SerializeTuple, SerializeTupleStruct, @@ -15,17 +16,8 @@ use serde::ser::{ }; use serde::Serialize; -#[cfg(not(any(feature = "no_object", feature = "no_index")))] -use serde::ser::SerializeTupleVariant; - -#[cfg(not(feature = "no_object"))] -use serde::ser::SerializeStructVariant; - use crate::stdlib::{boxed::Box, fmt, string::ToString}; -#[cfg(not(feature = "no_object"))] -use crate::stdlib::mem; - /// Serializer for `Dynamic` which is kept as a reference. pub struct DynamicSerializer { /// Buffer to hold a temporary key. @@ -477,7 +469,7 @@ impl SerializeMap for DynamicSerializer { ) -> Result<(), Box> { #[cfg(not(feature = "no_object"))] { - let key = mem::take(&mut self._key) + let key = crate::stdlib::mem::take(&mut self._key) .take_immutable_string() .map_err(|typ| { EvalAltResult::ErrorMismatchOutputType("string".into(), typ.into(), NO_POS) @@ -554,7 +546,7 @@ pub struct TupleVariantSerializer { } #[cfg(not(any(feature = "no_object", feature = "no_index")))] -impl SerializeTupleVariant for TupleVariantSerializer { +impl serde::ser::SerializeTupleVariant for TupleVariantSerializer { type Ok = Dynamic; type Error = Box; @@ -579,7 +571,7 @@ pub struct StructVariantSerializer { } #[cfg(not(feature = "no_object"))] -impl SerializeStructVariant for StructVariantSerializer { +impl serde::ser::SerializeStructVariant for StructVariantSerializer { type Ok = Dynamic; type Error = Box; diff --git a/src/token.rs b/src/token.rs index 3a7ef783..ba12ff22 100644 --- a/src/token.rs +++ b/src/token.rs @@ -5,9 +5,6 @@ use crate::engine::{ KEYWORD_IS_DEF_FN, KEYWORD_IS_DEF_VAR, KEYWORD_PRINT, KEYWORD_THIS, KEYWORD_TYPE_OF, }; -#[cfg(not(feature = "no_closure"))] -use crate::engine::KEYWORD_IS_SHARED; - use crate::parse_error::LexError; use crate::StaticVec; use crate::INT; @@ -545,7 +542,7 @@ impl Token { | KEYWORD_IS_DEF_FN | KEYWORD_THIS => Reserved(syntax.into()), #[cfg(not(feature = "no_closure"))] - KEYWORD_IS_SHARED => Reserved(syntax.into()), + crate::engine::KEYWORD_IS_SHARED => Reserved(syntax.into()), _ => return None, }) @@ -1515,7 +1512,7 @@ fn get_identifier( pub fn is_keyword_function(name: &str) -> bool { match name { #[cfg(not(feature = "no_closure"))] - KEYWORD_IS_SHARED => true, + crate::engine::KEYWORD_IS_SHARED => true, KEYWORD_PRINT | KEYWORD_DEBUG | KEYWORD_TYPE_OF | KEYWORD_EVAL | KEYWORD_FN_PTR | KEYWORD_FN_PTR_CALL | KEYWORD_FN_PTR_CURRY | KEYWORD_IS_DEF_VAR | KEYWORD_IS_DEF_FN => { true