diff --git a/Cargo.toml b/Cargo.toml
index 4fc4877a..db72ea25 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -6,7 +6,7 @@ members = [
[package]
name = "rhai"
-version = "0.19.5"
+version = "0.19.6"
edition = "2018"
authors = ["Jonathan Turner", "Lukáš Hozda", "Stephen Chung", "jhwgh1968"]
description = "Embedded scripting for Rust"
diff --git a/RELEASES.md b/RELEASES.md
index 1e200e97..4913a137 100644
--- a/RELEASES.md
+++ b/RELEASES.md
@@ -1,6 +1,17 @@
Rhai Release Notes
==================
+Version 0.19.6
+==============
+
+This version adds the `switch` statement.
+
+New features
+------------
+
+* `switch` statement.
+
+
Version 0.19.5
==============
@@ -209,7 +220,7 @@ Bug fixes
---------
* Fixes bug that prevents calling functions in closures.
-* Fixes bug that erroneously consumes the first argument to a module-qualified function call.
+* Fixes bug that erroneously consumes the first argument to a namespace-qualified function call.
New features
------------
@@ -299,7 +310,7 @@ New features
* The boolean `^` (XOR) operator is added.
* `FnPtr` is exposed as the function pointer type.
* `rhai::module_resolvers::ModuleResolversCollection` added to try a list of module resolvers.
-* It is now possible to mutate the first argument of a module-qualified function call when the argument is a simple variable (but not a module constant).
+* It is now possible to mutate the first argument of a namespace-qualified function call when the argument is a simple variable (but not a module constant).
* Many configuration/setting API's now returns `&mut Self` so that the calls can be chained.
* `String` parameters in functions are supported (but inefficiently).
diff --git a/doc/src/SUMMARY.md b/doc/src/SUMMARY.md
index 3d7a3ca9..f0ee7b6a 100644
--- a/doc/src/SUMMARY.md
+++ b/doc/src/SUMMARY.md
@@ -77,13 +77,14 @@ The Rhai Scripting Language
7. [Logic Operators](language/logic.md)
8. [Other Operators](language/other-op.md)
9. [If Statement](language/if.md)
- 10. [While Loop](language/while.md)
- 11. [Loop Statement](language/loop.md)
- 12. [For Loop](language/for.md)
- 13. [Return Values](language/return.md)
- 14. [Throw Exception on Error](language/throw.md)
- 15. [Catch Exceptions](language/try-catch.md)
- 16. [Functions](language/functions.md)
+ 10. [Switch Expression](language/switch.md)
+ 11. [While Loop](language/while.md)
+ 12. [Loop Statement](language/loop.md)
+ 13. [For Loop](language/for.md)
+ 14. [Return Values](language/return.md)
+ 15. [Throw Exception on Error](language/throw.md)
+ 16. [Catch Exceptions](language/try-catch.md)
+ 17. [Functions](language/functions.md)
1. [Call Method as Function](language/method.md)
2. [Overloading](language/overload.md)
3. [Namespaces](language/fn-namespaces.md)
@@ -91,11 +92,11 @@ The Rhai Scripting Language
5. [Currying](language/fn-curry.md)
6. [Anonymous Functions](language/fn-anon.md)
7. [Closures](language/fn-closure.md)
- 17. [Print and Debug](language/print-debug.md)
- 18. [Modules](language/modules/index.md)
+ 18. [Print and Debug](language/print-debug.md)
+ 19. [Modules](language/modules/index.md)
1. [Export Variables, Functions and Sub-Modules](language/modules/export.md)
2. [Import Modules](language/modules/import.md)
- 19. [Eval Statement](language/eval.md)
+ 20. [Eval Statement](language/eval.md)
6. [Safety and Protection](safety/index.md)
1. [Checked Arithmetic](safety/checked.md)
2. [Sand-Boxing](safety/sandbox.md)
diff --git a/doc/src/appendix/keywords.md b/doc/src/appendix/keywords.md
index acf603f8..ad8d3e26 100644
--- a/doc/src/appendix/keywords.md
+++ b/doc/src/appendix/keywords.md
@@ -13,6 +13,7 @@ Keywords List
| `is_shared` | is a value shared? | [`no_closure`] | yes | no |
| `if` | if statement | | no | |
| `else` | else block of if statement | | no | |
+| `switch` | matching | | no | |
| `while` | while loop | | no | |
| `loop` | infinite loop | | no | |
| `for` | for loop | | no | |
@@ -52,7 +53,6 @@ Reserved Keywords
| `then` | control flow |
| `goto` | control flow |
| `exit` | control flow |
-| `switch` | matching |
| `match` | matching |
| `case` | matching |
| `public` | function/field access |
diff --git a/doc/src/appendix/operators.md b/doc/src/appendix/operators.md
index ff9b7fed..0ca26be5 100644
--- a/doc/src/appendix/operators.md
+++ b/doc/src/appendix/operators.md
@@ -39,6 +39,7 @@ Symbols and Patterns
| Symbol | Name | Description |
| ---------------------------------- | :------------------: | ------------------------------------- |
+| `_` | underscore | default `switch` case |
| `;` | semicolon | statement separator |
| `,` | comma | list separator |
| `:` | colon | [object map] property value separator |
@@ -52,6 +53,7 @@ Symbols and Patterns
| \|
.. \|
| pipes | closure |
| `[` .. `]` | brackets | [array] literal |
| `!` | bang | function call in calling scope |
+| `=>` | double arrow | `switch` expression case separator |
| `//` | comment | line comment |
| `/*` .. `*/` | comment | block comment |
| `(*` .. `*)` | comment | _reserved_ |
@@ -64,7 +66,6 @@ Symbols and Patterns
| `#` | hash | _reserved_ |
| `@` | at | _reserved_ |
| `$` | dollar | _reserved_ |
-| `=>` | double arrow | _reserved_ |
| `->` | arrow | _reserved_ |
| `<-` | left arrow | _reserved_ |
| `===` | strict equals to | _reserved_ |
diff --git a/doc/src/context.json b/doc/src/context.json
index 597eb0df..7bb1a3ce 100644
--- a/doc/src/context.json
+++ b/doc/src/context.json
@@ -1,5 +1,5 @@
{
- "version": "0.19.5",
+ "version": "0.19.6",
"repoHome": "https://github.com/jonathandturner/rhai/blob/master",
"repoTree": "https://github.com/jonathandturner/rhai/tree/master",
"rootUrl": "",
diff --git a/doc/src/engine/dsl.md b/doc/src/engine/dsl.md
index 593a7eb0..efb9ac81 100644
--- a/doc/src/engine/dsl.md
+++ b/doc/src/engine/dsl.md
@@ -49,7 +49,7 @@ For example:
let animal = "rabbit";
let food = "carrot";
-animal eats food // custom operator - 'eats'
+animal eats food // custom operator 'eats'
eats(animal, food) // <- the above really de-sugars to this
```
@@ -61,7 +61,7 @@ nevertheless it makes the DSL syntax much simpler and expressive.
Custom Syntax
-------------
-For advanced DSL scenarios, it is possible to define entire expression [_syntax_][custom syntax] -
+For advanced DSL scenarios, it is possible to define entire expression [_syntax_][custom syntax] &ndash
essentially custom statement types.
For example, the following is a SQL-like syntax for some obscure DSL operation:
diff --git a/doc/src/engine/scope.md b/doc/src/engine/scope.md
index 3deecc7d..27f5d9b8 100644
--- a/doc/src/engine/scope.md
+++ b/doc/src/engine/scope.md
@@ -1,5 +1,5 @@
`Scope` - Initializing and Maintaining State
-===========================================
+=================================================
{{#include ../links.md}}
diff --git a/doc/src/language/dynamic.md b/doc/src/language/dynamic.md
index 9de60a1c..a89c9446 100644
--- a/doc/src/language/dynamic.md
+++ b/doc/src/language/dynamic.md
@@ -12,27 +12,19 @@ Use `type_of()` to Get Value Type
Because [`type_of()`] a `Dynamic` value returns the type of the actual value,
it is usually used to perform type-specific actions based on the actual value's type.
-```rust
+```c
let mystery = get_some_dynamic_value();
-if type_of(mystery) == "i64" {
- print("Hey, I got an integer here!");
-} else if type_of(mystery) == "f64" {
- print("Hey, I got a float here!");
-} else if type_of(mystery) == "string" {
- print("Hey, I got a string here!");
-} else if type_of(mystery) == "bool" {
- print("Hey, I got a boolean here!");
-} else if type_of(mystery) == "array" {
- print("Hey, I got an array here!");
-} else if type_of(mystery) == "map" {
- print("Hey, I got an object map here!");
-} else if type_of(mystery) == "Fn" {
- print("Hey, I got a function pointer here!");
-} else if type_of(mystery) == "TestStruct" {
- print("Hey, I got the TestStruct custom type here!");
-} else {
- print("I don't know what this is: " + type_of(mystery));
+switch mystery {
+ "i64" => print("Hey, I got an integer here!"),
+ "f64" => print("Hey, I got a float here!"),
+ "string" => print("Hey, I got a string here!"),
+ "bool" => print("Hey, I got a boolean here!"),
+ "array" => print("Hey, I got an array here!"),
+ "map" => print("Hey, I got an object map here!"),
+ "Fn" => print("Hey, I got a function pointer here!"),
+ "TestStruct" => print("Hey, I got the TestStruct custom type here!"),
+ _ => print("I don't know what this is: " + type_of(mystery))
}
```
diff --git a/doc/src/language/fn-namespaces.md b/doc/src/language/fn-namespaces.md
index 0d798ae4..ba551367 100644
--- a/doc/src/language/fn-namespaces.md
+++ b/doc/src/language/fn-namespaces.md
@@ -15,10 +15,10 @@ allow combining all functions in one [`AST`] into another, forming a new, unifie
In general, there are two types of _namespaces_ where functions are looked up:
-| Namespace | Source | Lookup method | Sub-modules? | Variables? |
-| --------- | ------------------------------------------------------------------------------------- | ------------------------------ | :----------: | :--------: |
-| Global | 1) `Engine::register_XXX` API
2) [`AST`] being evaluated
3) [packages] loaded | simple function name | ignored | ignored |
-| Module | [`Module`] | module-qualified function name | yes | yes |
+| Namespace | Source | Lookup method | Sub-modules? | Variables? |
+| --------- | ------------------------------------------------------------------------------------- | --------------------------------- | :----------: | :--------: |
+| Global | 1) `Engine::register_XXX` API
2) [`AST`] being evaluated
3) [packages] loaded | simple function name | ignored | ignored |
+| Module | [`Module`] | namespace-qualified function name | yes | yes |
Global Namespace
diff --git a/doc/src/language/switch.md b/doc/src/language/switch.md
new file mode 100644
index 00000000..45b9385d
--- /dev/null
+++ b/doc/src/language/switch.md
@@ -0,0 +1,97 @@
+`switch` Expression
+===================
+
+{{#include ../links.md}}
+
+The `switch` _expression_ allows matching on literal values, and it mostly follows Rust's
+`match` syntax:
+
+```c
+switch calc_secret_value(x) {
+ 1 => print("It's one!"),
+ 2 => {
+ print("It's two!");
+ print("Again!");
+ }
+ 3 => print("Go!"),
+ // _ is the default when no cases match
+ _ => print("Oops! Something's wrong: " + x)
+}
+```
+
+
+Expression, Not Statement
+------------------------
+
+`switch` is not a statement, but an expression. This means that a `switch` expression can
+appear anywhere a regular expression can, e.g. as function call arguments.
+
+```c
+let x = switch foo { 1 => true, _ => false };
+
+func(switch foo {
+ "hello" => 42,
+ "world" => 123,
+ _ => 0
+});
+
+// The above is somewhat equivalent to:
+
+let x = if foo == 1 { true } else { false };
+
+if foo == "hello" {
+ func(42);
+} else if foo == "world" {
+ func(123);
+} else {
+ func(0);
+}
+```
+
+
+Array and Object Map Literals Also Work
+--------------------------------------
+
+The `switch` expression can match against any _literal_, including [array] and [object map] literals.
+
+```c
+// Match on arrays
+switch [foo, bar, baz] {
+ ["hello", 42, true] => { ... }
+ ["hello", 123, false] => { ... }
+ ["world", 1, true] => { ... }
+ _ => { ... }
+}
+
+// Match on object maps
+switch map {
+ #{ a: 1, b: 2, c: true } => { ... }
+ #{ a: 42, d: "hello" } => { ... }
+ _ => { ... }
+}
+```
+
+
+Difference From If-Else Chain
+-----------------------------
+
+Although a `switch` expression looks _almost_ the same as an `if`-`else` chain,
+there are subtle differences between the two.
+
+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.
+
+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.
+
+`switch` expressions do _not_ use the `==` operator for comparison;
+instead, they _hash_ the data values and jump directly to the correct
+statements via a pre-compiled look-up table. This makes matching extremely
+efficient, but it also means that [overloading][operator overloading]
+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.
diff --git a/doc/src/rust/modules/create.md b/doc/src/rust/modules/create.md
index 51462b22..069e5896 100644
--- a/doc/src/rust/modules/create.md
+++ b/doc/src/rust/modules/create.md
@@ -69,9 +69,9 @@ resolver.insert("question", module);
let mut engine = Engine::new();
engine.set_module_resolver(Some(resolver));
-// Use module-qualified variables
+// Use namespace-qualified variables
engine.eval::(r#"import "question" as q; q::answer + 1"#)? == 42;
-// Call module-qualified functions
+// Call namespace-qualified functions
engine.eval::(r#"import "question" as q; q::inc(q::answer)"#)? == 42;
```
diff --git a/src/ast.rs b/src/ast.rs
index e5409b24..b0a4ac00 100644
--- a/src/ast.rs
+++ b/src/ast.rs
@@ -2,10 +2,10 @@
use crate::dynamic::{Dynamic, Union};
use crate::fn_native::{FnPtr, Shared};
-use crate::module::{Module, ModuleRef};
+use crate::module::{Module, NamespaceRef};
use crate::syntax::FnCustomSyntaxEval;
use crate::token::{Position, Token, NO_POS};
-use crate::utils::ImmutableString;
+use crate::utils::{ImmutableString, StraightHasherBuilder};
use crate::StaticVec;
use crate::INT;
@@ -16,7 +16,7 @@ use crate::FLOAT;
use crate::engine::Array;
#[cfg(not(feature = "no_object"))]
-use crate::engine::{make_getter, make_setter, Map};
+use crate::engine::Map;
#[cfg(not(feature = "no_module"))]
use crate::engine::Imports;
@@ -25,8 +25,9 @@ use crate::stdlib::{
any::TypeId,
borrow::Cow,
boxed::Box,
+ collections::HashMap,
fmt,
- hash::{Hash, Hasher},
+ hash::Hash,
num::NonZeroUsize,
ops::{Add, AddAssign},
string::String,
@@ -34,9 +35,6 @@ use crate::stdlib::{
vec::Vec,
};
-#[cfg(not(feature = "no_float"))]
-use crate::stdlib::ops::Neg;
-
#[cfg(not(feature = "no_closure"))]
use crate::stdlib::collections::HashSet;
@@ -98,10 +96,10 @@ pub struct ScriptFnDef {
/// Function access mode.
pub access: FnAccess,
/// Names of function parameters.
- pub params: StaticVec,
+ pub params: StaticVec,
/// Access to external variables.
#[cfg(not(feature = "no_closure"))]
- pub externals: HashSet,
+ pub externals: HashSet,
}
impl fmt::Display for ScriptFnDef {
@@ -602,7 +600,7 @@ pub enum ReturnType {
/// ## WARNING
///
/// This type is volatile and may change.
-#[derive(Debug, Clone, Hash)]
+#[derive(Debug, Clone)]
pub enum Stmt {
/// No-op.
Noop(Position),
@@ -637,10 +635,10 @@ pub enum Stmt {
Import(Expr, Option>, Position),
/// export var as var, ...
#[cfg(not(feature = "no_module"))]
- Export(Vec<(Ident, Option)>, Position),
+ Export(Vec<(IdentX, Option)>, Position),
/// Convert a variable to shared.
#[cfg(not(feature = "no_closure"))]
- Share(Box),
+ Share(IdentX),
}
impl Default for Stmt {
@@ -778,7 +776,7 @@ impl Stmt {
}
}
-/// _[INTERNALS]_ A type wrapping a custom syntax definition.
+/// _[INTERNALS]_ A custom syntax definition.
/// Exported under the `internals` feature only.
///
/// ## WARNING
@@ -799,13 +797,6 @@ impl fmt::Debug for CustomExpr {
}
}
-impl Hash for CustomExpr {
- #[inline(always)]
- fn hash(&self, state: &mut H) {
- self.keywords.hash(state);
- }
-}
-
impl CustomExpr {
/// Get the keywords for this `CustomExpr`.
#[inline(always)]
@@ -819,50 +810,13 @@ impl CustomExpr {
}
}
-/// _[INTERNALS]_ A type wrapping a floating-point number.
-/// Exported under the `internals` feature only.
-///
-/// This type is mainly used to provide a standard `Hash` implementation
-/// to floating-point numbers, allowing `Expr` to derive `Hash` automatically.
-///
-/// ## WARNING
-///
-/// This type is volatile and may change.
-#[cfg(not(feature = "no_float"))]
-#[derive(Debug, PartialEq, PartialOrd, Clone)]
-pub struct FloatWrapper(pub FLOAT);
-
-#[cfg(not(feature = "no_float"))]
-impl Hash for FloatWrapper {
- #[inline(always)]
- fn hash(&self, state: &mut H) {
- state.write(&self.0.to_le_bytes());
- }
-}
-
-#[cfg(not(feature = "no_float"))]
-impl Neg for FloatWrapper {
- type Output = Self;
-
- fn neg(self) -> Self::Output {
- Self(-self.0)
- }
-}
-
-#[cfg(not(feature = "no_float"))]
-impl From for FloatWrapper {
- fn from(value: INT) -> Self {
- Self(value as FLOAT)
- }
-}
-
-/// A binary expression structure.
+/// _[INTERNALS]_ A binary expression.
/// Exported under the `internals` feature only.
///
/// ## WARNING
///
/// This type is volatile and may change.
-#[derive(Debug, Clone, Hash)]
+#[derive(Debug, Clone)]
pub struct BinaryExpr {
/// LHS expression.
pub lhs: Expr,
@@ -876,8 +830,8 @@ pub struct BinaryExpr {
/// ## WARNING
///
/// This type is volatile and may change.
-#[derive(Debug, Clone, Hash, Default)]
-pub struct FnCallInfo {
+#[derive(Debug, Clone, Default)]
+pub struct FnCallExpr {
/// Pre-calculated hash for a script-defined function of the same name and number of parameters.
pub hash: u64,
/// Call native functions only? Set to `true` to skip searching for script-defined function overrides
@@ -889,7 +843,7 @@ pub struct FnCallInfo {
/// Type is `bool` in order for `FnCallInfo` to be `Hash`
pub def_value: Option,
/// Namespace of the function, if any. Boxed because it occurs rarely.
- pub namespace: Option>,
+ pub namespace: Option>,
/// Function name.
/// Use `Cow<'static, str>` because a lot of operators (e.g. `==`, `>=`) are implemented as function calls
/// and the function names are predictable, so no need to allocate a new `String`.
@@ -904,49 +858,58 @@ pub struct FnCallInfo {
/// ## WARNING
///
/// This type is volatile and may change.
-#[derive(Debug, Clone, Hash)]
+#[derive(Debug, Clone)]
pub enum Expr {
/// Integer constant.
IntegerConstant(INT, Position),
/// Floating-point constant.
#[cfg(not(feature = "no_float"))]
- FloatConstant(FloatWrapper, Position),
+ FloatConstant(FLOAT, Position),
/// Character constant.
CharConstant(char, Position),
/// String constant.
- StringConstant(Box),
+ StringConstant(ImmutableString, Position),
/// FnPtr constant.
- FnPointer(Box),
- /// Variable access - (optional index, optional modules, hash, variable name)
- Variable(Box<(Option, Option>, u64, Ident)>),
- /// Property access - (getter, setter), prop
- Property(Box<((String, String), IdentX)>),
- /// { stmt }
- Stmt(Box>, Position),
- /// Wrapped expression - should not be optimized away.
- Expr(Box),
- /// func(expr, ... )
- FnCall(Box, Position),
- /// lhs.rhs
- Dot(Box, Position),
- /// expr[expr]
- Index(Box, Position),
+ FnPointer(ImmutableString, Position),
/// [ expr, ... ]
Array(Box>, Position),
/// #{ name:expr, ... }
Map(Box>, Position),
- /// lhs in rhs
- In(Box, Position),
- /// lhs && rhs
- And(Box, Position),
- /// lhs || rhs
- Or(Box, Position),
/// true
True(Position),
/// false
False(Position),
/// ()
Unit(Position),
+ /// Variable access - (optional index, optional modules, hash, variable name)
+ Variable(Box<(Option, Option>, u64, IdentX)>),
+ /// Property access - (getter, setter), prop
+ Property(Box<((ImmutableString, ImmutableString), IdentX)>),
+ /// { stmt }
+ Stmt(Box>, Position),
+ /// Wrapped expression - should not be optimized away.
+ Expr(Box),
+ /// func(expr, ... )
+ FnCall(Box, Position),
+ /// lhs.rhs
+ 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
+ And(Box, Position),
+ /// lhs || rhs
+ Or(Box, Position),
/// Custom syntax
Custom(Box, Position),
}
@@ -970,8 +933,8 @@ impl Expr {
#[cfg(not(feature = "no_float"))]
Self::FloatConstant(_, _) => TypeId::of::(),
Self::CharConstant(_, _) => TypeId::of::(),
- Self::StringConstant(_) => TypeId::of::(),
- Self::FnPointer(_) => TypeId::of::(),
+ Self::StringConstant(_, _) => TypeId::of::(),
+ Self::FnPointer(_, _) => TypeId::of::(),
Self::True(_) | Self::False(_) | Self::In(_, _) | Self::And(_, _) | Self::Or(_, _) => {
TypeId::of::()
}
@@ -996,11 +959,11 @@ impl Expr {
Self::IntegerConstant(x, _) => (*x).into(),
#[cfg(not(feature = "no_float"))]
- Self::FloatConstant(x, _) => x.0.into(),
+ Self::FloatConstant(x, _) => (*x).into(),
Self::CharConstant(x, _) => (*x).into(),
- Self::StringConstant(x) => x.name.clone().into(),
- Self::FnPointer(x) => Dynamic(Union::FnPtr(Box::new(FnPtr::new_unchecked(
- x.name.clone(),
+ Self::StringConstant(x, _) => x.clone().into(),
+ Self::FnPointer(x, _) => Dynamic(Union::FnPtr(Box::new(FnPtr::new_unchecked(
+ x.clone(),
Default::default(),
)))),
Self::True(_) => true.into(),
@@ -1043,14 +1006,15 @@ impl Expr {
Self::IntegerConstant(_, pos) => *pos,
Self::CharConstant(_, pos) => *pos,
- Self::StringConstant(x) => x.pos,
- Self::FnPointer(x) => x.pos,
+ Self::StringConstant(_, pos) => *pos,
+ Self::FnPointer(_, pos) => *pos,
Self::Array(_, pos) => *pos,
Self::Map(_, pos) => *pos,
Self::Property(x) => (x.1).pos,
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(),
@@ -1074,14 +1038,15 @@ impl Expr {
Self::IntegerConstant(_, pos) => *pos = new_pos,
Self::CharConstant(_, pos) => *pos = new_pos,
- Self::StringConstant(x) => x.pos = new_pos,
- Self::FnPointer(x) => x.pos = new_pos,
+ Self::StringConstant(_, pos) => *pos = new_pos,
+ Self::FnPointer(_, pos) => *pos = new_pos,
Self::Array(_, pos) => *pos = new_pos,
Self::Map(_, pos) => *pos = new_pos,
Self::Variable(x) => (x.3).pos = new_pos,
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,
@@ -1123,39 +1088,6 @@ impl Expr {
}
}
- /// Is the expression a simple constant literal?
- pub fn is_literal(&self) -> bool {
- match self {
- Self::Expr(x) => x.is_literal(),
-
- #[cfg(not(feature = "no_float"))]
- Self::FloatConstant(_, _) => true,
-
- Self::IntegerConstant(_, _)
- | Self::CharConstant(_, _)
- | Self::StringConstant(_)
- | Self::FnPointer(_)
- | Self::True(_)
- | Self::False(_)
- | Self::Unit(_) => true,
-
- // An array literal is literal if all items are literals
- Self::Array(x, _) => x.iter().all(Self::is_literal),
-
- // An map literal is literal if all items are literals
- Self::Map(x, _) => x.iter().map(|(_, expr)| expr).all(Self::is_literal),
-
- // Check in expression
- Self::In(x, _) => match (&x.lhs, &x.rhs) {
- (Self::StringConstant(_), Self::StringConstant(_))
- | (Self::CharConstant(_, _), Self::StringConstant(_)) => true,
- _ => false,
- },
-
- _ => false,
- }
- }
-
/// Is the expression a constant?
pub fn is_constant(&self) -> bool {
match self {
@@ -1166,8 +1098,8 @@ impl Expr {
Self::IntegerConstant(_, _)
| Self::CharConstant(_, _)
- | Self::StringConstant(_)
- | Self::FnPointer(_)
+ | Self::StringConstant(_, _)
+ | Self::FnPointer(_, _)
| Self::True(_)
| Self::False(_)
| Self::Unit(_) => true,
@@ -1180,8 +1112,8 @@ impl Expr {
// Check in expression
Self::In(x, _) => match (&x.lhs, &x.rhs) {
- (Self::StringConstant(_), Self::StringConstant(_))
- | (Self::CharConstant(_, _), Self::StringConstant(_)) => true,
+ (Self::StringConstant(_, _), Self::StringConstant(_, _))
+ | (Self::CharConstant(_, _), Self::StringConstant(_, _)) => true,
_ => false,
},
@@ -1199,7 +1131,7 @@ impl Expr {
Self::IntegerConstant(_, _)
| Self::CharConstant(_, _)
- | Self::FnPointer(_)
+ | Self::FnPointer(_, _)
| Self::In(_, _)
| Self::And(_, _)
| Self::Or(_, _)
@@ -1207,9 +1139,10 @@ impl Expr {
| Self::False(_)
| Self::Unit(_) => false,
- Self::StringConstant(_)
+ Self::StringConstant(_, _)
| Self::Stmt(_, _)
| Self::FnCall(_, _)
+ | Self::Switch(_, _)
| Self::Dot(_, _)
| Self::Index(_, _)
| Self::Array(_, _)
@@ -1238,21 +1171,6 @@ impl Expr {
Self::Custom(_, _) => false,
}
}
-
- /// Convert a `Variable` into a `Property`. All other variants are untouched.
- #[cfg(not(feature = "no_object"))]
- #[inline]
- pub(crate) fn into_property(self) -> Self {
- match self {
- Self::Variable(x) if x.1.is_none() => {
- let ident = x.3;
- let getter = make_getter(&ident.name);
- let setter = make_setter(&ident.name);
- Self::Property(Box::new(((getter, setter), ident.into())))
- }
- _ => self,
- }
- }
}
#[cfg(test)]
diff --git a/src/dynamic.rs b/src/dynamic.rs
index 6b3cc8b1..b0bee022 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::INT;
+use crate::{StaticVec, INT};
#[cfg(not(feature = "no_closure"))]
use crate::fn_native::{shared_try_take, Locked, Shared};
@@ -21,6 +21,7 @@ use crate::stdlib::{
any::{type_name, Any, TypeId},
boxed::Box,
fmt,
+ hash::{Hash, Hasher},
ops::{Deref, DerefMut},
string::{String, ToString},
};
@@ -361,6 +362,44 @@ impl Dynamic {
}
}
+impl Hash for Dynamic {
+ fn hash(&self, state: &mut H) {
+ match &self.0 {
+ Union::Unit(_) => ().hash(state),
+ Union::Bool(value) => value.hash(state),
+ Union::Str(s) => s.hash(state),
+ 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());
+ }
+ #[cfg(not(feature = "no_index"))]
+ Union::Array(a) => a.hash(state),
+ #[cfg(not(feature = "no_object"))]
+ Union::Map(m) => {
+ let mut buf: StaticVec<_> = m.keys().collect();
+ buf.sort();
+
+ buf.into_iter().for_each(|key| {
+ key.hash(state);
+ m[key].hash(state);
+ })
+ }
+
+ #[cfg(not(feature = "no_closure"))]
+ #[cfg(not(feature = "sync"))]
+ Union::Shared(cell) => (*cell.borrow()).hash(state),
+ #[cfg(not(feature = "no_closure"))]
+ #[cfg(feature = "sync")]
+ Union::Shared(cell) => (*cell.read().unwrap()).hash(state),
+
+ _ => unimplemented!(),
+ }
+ }
+}
+
/// Map the name of a standard type into a friendly form.
#[inline]
pub(crate) fn map_std_type_name(name: &str) -> &str {
diff --git a/src/engine.rs b/src/engine.rs
index 57ced10c..16558bb5 100644
--- a/src/engine.rs
+++ b/src/engine.rs
@@ -1,10 +1,10 @@
//! Main module defining the script evaluation `Engine`.
-use crate::ast::{BinaryExpr, Expr, FnCallInfo, Ident, IdentX, ReturnType, Stmt};
+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::module::{Module, ModuleRef};
+use crate::module::{Module, NamespaceRef};
use crate::optimize::OptimizationLevel;
use crate::packages::{Package, PackagesCollection, StandardPackage};
use crate::r#unsafe::unsafe_cast_var_name_to_lifetime;
@@ -12,6 +12,7 @@ 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::{calc_native_fn_hash, StaticVec};
#[cfg(not(feature = "no_index"))]
@@ -38,6 +39,7 @@ use crate::stdlib::{
boxed::Box,
collections::{HashMap, HashSet},
fmt, format,
+ hash::{Hash, Hasher},
iter::{empty, once},
num::NonZeroUsize,
ops::DerefMut,
@@ -593,7 +595,7 @@ pub struct Engine {
/// Max limits.
#[cfg(not(feature = "unchecked"))]
- pub(crate) limits_set: Limits,
+ pub(crate) limits: Limits,
}
impl fmt::Debug for Engine {
@@ -636,6 +638,7 @@ pub fn is_anonymous_fn(fn_name: &str) -> bool {
}
/// Print/debug to stdout
+#[inline(always)]
fn default_print(_s: &str) {
#[cfg(not(feature = "no_std"))]
#[cfg(not(target_arch = "wasm32"))]
@@ -647,15 +650,15 @@ fn default_print(_s: &str) {
pub fn search_imports(
mods: &Imports,
state: &mut State,
- modules: &ModuleRef,
+ namespace: &NamespaceRef,
) -> Result, Box> {
- let Ident { name: root, pos } = &modules[0];
+ let IdentX { name: root, pos } = &namespace[0];
// Qualified - check if the root module is directly indexed
let index = if state.always_search {
0
} else {
- modules.index().map_or(0, NonZeroUsize::get)
+ namespace.index().map_or(0, NonZeroUsize::get)
};
Ok(if index > 0 {
@@ -670,7 +673,7 @@ pub fn search_imports(
impl Engine {
/// Create a new `Engine`
- #[inline(always)]
+ #[inline]
pub fn new() -> Self {
// Create the new scripting Engine
let mut engine = Self {
@@ -710,7 +713,7 @@ impl Engine {
},
#[cfg(not(feature = "unchecked"))]
- limits_set: Limits {
+ limits: Limits {
max_call_stack_depth: MAX_CALL_STACK_DEPTH,
max_expr_depth: MAX_EXPR_DEPTH,
#[cfg(not(feature = "no_function"))]
@@ -733,7 +736,7 @@ impl Engine {
/// Create a new `Engine` with minimal built-in functions.
/// Use the `load_package` method to load additional packages of functions.
- #[inline(always)]
+ #[inline]
pub fn new_raw() -> Self {
Self {
id: Default::default(),
@@ -762,7 +765,7 @@ impl Engine {
},
#[cfg(not(feature = "unchecked"))]
- limits_set: Limits {
+ limits: Limits {
max_call_stack_depth: MAX_CALL_STACK_DEPTH,
max_expr_depth: MAX_EXPR_DEPTH,
#[cfg(not(feature = "no_function"))]
@@ -780,7 +783,7 @@ impl Engine {
}
/// Search for a variable within the scope or within imports,
- /// depending on whether the variable name is qualified.
+ /// depending on whether the variable name is namespace-qualified.
pub(crate) fn search_namespace<'s, 'a>(
&self,
scope: &'s mut Scope,
@@ -793,7 +796,7 @@ impl Engine {
match expr {
Expr::Variable(v) => match v.as_ref() {
// Qualified variable
- (_, Some(modules), hash_var, Ident { name, pos }) => {
+ (_, Some(modules), hash_var, IdentX { name, pos }) => {
let module = search_imports(mods, state, modules)?;
let target = module.get_qualified_var(*hash_var).map_err(|mut err| {
match *err {
@@ -825,13 +828,13 @@ impl Engine {
this_ptr: &'s mut Option<&mut Dynamic>,
expr: &'a Expr,
) -> Result<(Target<'s>, &'a str, ScopeEntryType, Position), Box> {
- let (index, _, _, Ident { name, pos }) = match expr {
+ let (index, _, _, IdentX { name, pos }) = match expr {
Expr::Variable(v) => v.as_ref(),
_ => unreachable!(),
};
// Check if the variable is `this`
- if name == KEYWORD_THIS {
+ if name.as_str() == KEYWORD_THIS {
if let Some(val) = this_ptr {
return Ok(((*val).into(), KEYWORD_THIS, ScopeEntryType::Normal, *pos));
} else {
@@ -870,7 +873,7 @@ impl Engine {
// Find the variable in the scope
scope
.get_index(name)
- .ok_or_else(|| EvalAltResult::ErrorVariableNotFound(name.into(), *pos))?
+ .ok_or_else(|| EvalAltResult::ErrorVariableNotFound(name.to_string(), *pos))?
.0
};
@@ -1000,7 +1003,7 @@ impl Engine {
match rhs {
// xxx.fn_name(arg_expr_list)
Expr::FnCall(x, pos) if x.namespace.is_none() => {
- let FnCallInfo {
+ let FnCallExpr {
name,
native_only: native,
hash,
@@ -1073,7 +1076,7 @@ impl Engine {
}
// {xxx:map}.fn_name(arg_expr_list)[expr] | {xxx:map}.fn_name(arg_expr_list).expr
Expr::FnCall(x, pos) if x.namespace.is_none() => {
- let FnCallInfo {
+ let FnCallExpr {
name,
native_only: native,
hash,
@@ -1157,7 +1160,7 @@ impl Engine {
}
// xxx.fn_name(arg_expr_list)[expr] | xxx.fn_name(arg_expr_list).expr
Expr::FnCall(f, pos) if f.namespace.is_none() => {
- let FnCallInfo {
+ let FnCallExpr {
name,
native_only: native,
hash,
@@ -1209,14 +1212,7 @@ impl Engine {
level: usize,
new_val: Option<(Dynamic, Position)>,
) -> Result> {
- let (
- BinaryExpr {
- lhs: dot_lhs,
- rhs: dot_rhs,
- },
- chain_type,
- op_pos,
- ) = match expr {
+ let (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!(),
@@ -1230,17 +1226,17 @@ impl Engine {
state,
lib,
this_ptr,
- dot_rhs,
+ rhs,
chain_type,
&mut idx_values,
0,
level,
)?;
- match dot_lhs {
+ match lhs {
// id.??? or id[???]
Expr::Variable(x) => {
- let Ident {
+ let IdentX {
name: var_name,
pos: var_pos,
} = &x.3;
@@ -1249,7 +1245,7 @@ impl Engine {
.map_err(|err| err.fill_position(*var_pos))?;
let (target, _, typ, pos) =
- self.search_namespace(scope, mods, state, lib, this_ptr, dot_lhs)?;
+ self.search_namespace(scope, mods, state, lib, this_ptr, lhs)?;
// Constants cannot be modified
match typ {
@@ -1262,7 +1258,7 @@ impl Engine {
let obj_ptr = &mut target.into();
self.eval_dot_index_chain_helper(
- mods, state, lib, &mut None, obj_ptr, dot_rhs, idx_values, chain_type, level,
+ mods, state, lib, &mut None, obj_ptr, rhs, idx_values, chain_type, level,
new_val,
)
.map(|(v, _)| v)
@@ -1275,7 +1271,7 @@ impl Engine {
let val = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?;
let obj_ptr = &mut val.into();
self.eval_dot_index_chain_helper(
- mods, state, lib, this_ptr, obj_ptr, dot_rhs, idx_values, chain_type, level,
+ mods, state, lib, this_ptr, obj_ptr, rhs, idx_values, chain_type, level,
new_val,
)
.map(|(v, _)| v)
@@ -1549,12 +1545,10 @@ impl Engine {
Expr::IntegerConstant(x, _) => Ok((*x).into()),
#[cfg(not(feature = "no_float"))]
- Expr::FloatConstant(x, _) => Ok(x.0.into()),
- Expr::StringConstant(x) => Ok(x.name.clone().into()),
+ Expr::FloatConstant(x, _) => Ok((*x).into()),
+ Expr::StringConstant(x, _) => Ok(x.clone().into()),
Expr::CharConstant(x, _) => Ok((*x).into()),
- Expr::FnPointer(x) => {
- Ok(FnPtr::new_unchecked(x.name.clone(), Default::default()).into())
- }
+ Expr::FnPointer(x, _) => Ok(FnPtr::new_unchecked(x.clone(), Default::default()).into()),
Expr::Variable(x) if (x.3).name == KEYWORD_THIS => {
if let Some(val) = this_ptr {
Ok(val.clone())
@@ -1605,7 +1599,7 @@ impl Engine {
// Normal function call
Expr::FnCall(x, pos) if x.namespace.is_none() => {
- let FnCallInfo {
+ let FnCallExpr {
name,
native_only: native,
capture: cap_scope,
@@ -1622,9 +1616,9 @@ impl Engine {
.map_err(|err| err.fill_position(*pos))
}
- // Module-qualified function call
+ // Namespace-qualified function call
Expr::FnCall(x, pos) if x.namespace.is_some() => {
- let FnCallInfo {
+ let FnCallExpr {
name,
namespace,
hash,
@@ -1632,9 +1626,9 @@ impl Engine {
def_value,
..
} = x.as_ref();
- let modules = namespace.as_ref().map(|v| v.as_ref());
+ let namespace = namespace.as_ref().map(|v| v.as_ref());
self.make_qualified_function_call(
- scope, mods, state, lib, this_ptr, modules, name, args, *def_value, *hash,
+ scope, mods, state, lib, this_ptr, namespace, name, args, *def_value, *hash,
level,
)
.map_err(|err| err.fill_position(*pos))
@@ -1674,6 +1668,25 @@ impl Engine {
Expr::False(_) => Ok(false.into()),
Expr::Unit(_) => Ok(().into()),
+ 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);
+ 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()
@@ -2184,13 +2197,14 @@ impl Engine {
// Export statement
#[cfg(not(feature = "no_module"))]
Stmt::Export(list, _) => {
- for (Ident { name, pos: id_pos }, rename) in list.iter() {
+ for (IdentX { name, pos: id_pos }, rename) in list.iter() {
// Mark scope variables as public
if let Some(index) = scope.get_index(name).map(|(i, _)| i) {
let alias = rename.as_ref().map(|x| &x.name).unwrap_or_else(|| name);
- scope.add_entry_alias(index, alias.clone());
+ scope.add_entry_alias(index, alias.to_string());
} else {
- return EvalAltResult::ErrorVariableNotFound(name.into(), *id_pos).into();
+ return EvalAltResult::ErrorVariableNotFound(name.to_string(), *id_pos)
+ .into();
}
}
Ok(Default::default())
diff --git a/src/engine_api.rs b/src/engine_api.rs
index 084c2040..7db48d05 100644
--- a/src/engine_api.rs
+++ b/src/engine_api.rs
@@ -9,6 +9,7 @@ use crate::parse_error::ParseError;
use crate::result::EvalAltResult;
use crate::scope::Scope;
use crate::token::{Position, NO_POS};
+use crate::utils::get_hasher;
#[cfg(not(feature = "no_index"))]
use crate::{
@@ -35,6 +36,7 @@ use crate::optimize::optimize_into_ast;
use crate::stdlib::{
any::{type_name, TypeId},
boxed::Box,
+ hash::{Hash, Hasher},
string::String,
};
@@ -45,6 +47,13 @@ use crate::stdlib::mem;
#[cfg(not(target_arch = "wasm32"))]
use crate::stdlib::{fs::File, io::prelude::*, path::PathBuf};
+/// Calculate a unique hash for a script.
+fn calc_hash_for_scripts<'a>(scripts: impl IntoIterator- ) -> u64 {
+ let s = &mut get_hasher();
+ scripts.into_iter().for_each(|&script| script.hash(s));
+ s.finish()
+}
+
/// Engine public API
impl Engine {
/// Register a function of the `Engine`.
@@ -907,8 +916,9 @@ impl Engine {
scripts: &[&str],
optimization_level: OptimizationLevel,
) -> Result {
+ let hash = calc_hash_for_scripts(scripts);
let stream = self.lex(scripts, None);
- self.parse(&mut stream.peekable(), scope, optimization_level)
+ self.parse(hash, &mut stream.peekable(), scope, optimization_level)
}
/// Read the contents of a file into a string.
@@ -1061,6 +1071,7 @@ impl Engine {
.into());
};
+ let hash = calc_hash_for_scripts(&scripts);
let stream = self.lex(
&scripts,
if has_null {
@@ -1073,8 +1084,12 @@ impl Engine {
None
},
);
- let ast =
- self.parse_global_expr(&mut stream.peekable(), &scope, OptimizationLevel::None)?;
+ let ast = self.parse_global_expr(
+ hash,
+ &mut stream.peekable(),
+ &scope,
+ OptimizationLevel::None,
+ )?;
// Handle null - map to ()
if has_null {
@@ -1155,11 +1170,11 @@ impl Engine {
script: &str,
) -> Result {
let scripts = [script];
+ let hash = calc_hash_for_scripts(&scripts);
let stream = self.lex(&scripts, None);
- {
- let mut peekable = stream.peekable();
- self.parse_global_expr(&mut peekable, scope, self.optimization_level)
- }
+
+ let mut peekable = stream.peekable();
+ self.parse_global_expr(hash, &mut peekable, scope, self.optimization_level)
}
/// Evaluate a script file.
@@ -1316,10 +1331,12 @@ impl Engine {
script: &str,
) -> Result> {
let scripts = [script];
+ let hash = calc_hash_for_scripts(&scripts);
let stream = self.lex(&scripts, None);
// No need to optimize a lone expression
- let ast = self.parse_global_expr(&mut stream.peekable(), scope, OptimizationLevel::None)?;
+ let ast =
+ self.parse_global_expr(hash, &mut stream.peekable(), scope, OptimizationLevel::None)?;
self.eval_ast_with_scope(scope, &ast)
}
@@ -1446,8 +1463,9 @@ impl Engine {
script: &str,
) -> Result<(), Box> {
let scripts = [script];
+ let hash = calc_hash_for_scripts(&scripts);
let stream = self.lex(&scripts, None);
- let ast = self.parse(&mut stream.peekable(), scope, self.optimization_level)?;
+ let ast = self.parse(hash, &mut stream.peekable(), scope, self.optimization_level)?;
self.consume_ast_with_scope(scope, &ast)
}
diff --git a/src/engine_settings.rs b/src/engine_settings.rs
index 987187f1..b6d7cfd9 100644
--- a/src/engine_settings.rs
+++ b/src/engine_settings.rs
@@ -53,7 +53,7 @@ impl Engine {
#[cfg(not(feature = "unchecked"))]
#[inline(always)]
pub fn set_max_call_levels(&mut self, levels: usize) -> &mut Self {
- self.limits_set.max_call_stack_depth = levels;
+ self.limits.max_call_stack_depth = levels;
self
}
@@ -61,7 +61,7 @@ impl Engine {
#[cfg(not(feature = "unchecked"))]
#[inline(always)]
pub fn max_call_levels(&self) -> usize {
- self.limits_set.max_call_stack_depth
+ self.limits.max_call_stack_depth
}
/// Set the maximum number of operations allowed for a script to run to avoid
@@ -69,7 +69,7 @@ impl Engine {
#[cfg(not(feature = "unchecked"))]
#[inline(always)]
pub fn set_max_operations(&mut self, operations: u64) -> &mut Self {
- self.limits_set.max_operations = if operations == u64::MAX {
+ self.limits.max_operations = if operations == u64::MAX {
0
} else {
operations
@@ -81,7 +81,7 @@ impl Engine {
#[cfg(not(feature = "unchecked"))]
#[inline(always)]
pub fn max_operations(&self) -> u64 {
- self.limits_set.max_operations
+ self.limits.max_operations
}
/// Set the maximum number of imported modules allowed for a script.
@@ -89,7 +89,7 @@ impl Engine {
#[cfg(not(feature = "no_module"))]
#[inline(always)]
pub fn set_max_modules(&mut self, modules: usize) -> &mut Self {
- self.limits_set.max_modules = modules;
+ self.limits.max_modules = modules;
self
}
@@ -98,7 +98,7 @@ impl Engine {
#[cfg(not(feature = "no_module"))]
#[inline(always)]
pub fn max_modules(&self) -> usize {
- self.limits_set.max_modules
+ self.limits.max_modules
}
/// Set the depth limits for expressions (0 for unlimited).
@@ -109,14 +109,14 @@ impl Engine {
max_expr_depth: usize,
#[cfg(not(feature = "no_function"))] max_function_expr_depth: usize,
) -> &mut Self {
- self.limits_set.max_expr_depth = if max_expr_depth == usize::MAX {
+ self.limits.max_expr_depth = if max_expr_depth == usize::MAX {
0
} else {
max_expr_depth
};
#[cfg(not(feature = "no_function"))]
{
- self.limits_set.max_function_expr_depth = if max_function_expr_depth == usize::MAX {
+ self.limits.max_function_expr_depth = if max_function_expr_depth == usize::MAX {
0
} else {
max_function_expr_depth
@@ -129,7 +129,7 @@ impl Engine {
#[cfg(not(feature = "unchecked"))]
#[inline(always)]
pub fn max_expr_depth(&self) -> usize {
- self.limits_set.max_expr_depth
+ self.limits.max_expr_depth
}
/// The depth limit for expressions in functions (0 for unlimited).
@@ -137,14 +137,14 @@ impl Engine {
#[cfg(not(feature = "no_function"))]
#[inline(always)]
pub fn max_function_expr_depth(&self) -> usize {
- self.limits_set.max_function_expr_depth
+ self.limits.max_function_expr_depth
}
/// Set the maximum length of strings (0 for unlimited).
#[cfg(not(feature = "unchecked"))]
#[inline(always)]
pub fn set_max_string_size(&mut self, max_size: usize) -> &mut Self {
- self.limits_set.max_string_size = if max_size == usize::MAX { 0 } else { max_size };
+ self.limits.max_string_size = if max_size == usize::MAX { 0 } else { max_size };
self
}
@@ -152,7 +152,7 @@ impl Engine {
#[cfg(not(feature = "unchecked"))]
#[inline(always)]
pub fn max_string_size(&self) -> usize {
- self.limits_set.max_string_size
+ self.limits.max_string_size
}
/// Set the maximum length of arrays (0 for unlimited).
@@ -160,7 +160,7 @@ impl Engine {
#[cfg(not(feature = "no_index"))]
#[inline(always)]
pub fn set_max_array_size(&mut self, max_size: usize) -> &mut Self {
- self.limits_set.max_array_size = if max_size == usize::MAX { 0 } else { max_size };
+ self.limits.max_array_size = if max_size == usize::MAX { 0 } else { max_size };
self
}
@@ -169,7 +169,7 @@ impl Engine {
#[cfg(not(feature = "no_index"))]
#[inline(always)]
pub fn max_array_size(&self) -> usize {
- self.limits_set.max_array_size
+ self.limits.max_array_size
}
/// Set the maximum length of object maps (0 for unlimited).
@@ -177,7 +177,7 @@ impl Engine {
#[cfg(not(feature = "no_object"))]
#[inline(always)]
pub fn set_max_map_size(&mut self, max_size: usize) -> &mut Self {
- self.limits_set.max_map_size = if max_size == usize::MAX { 0 } else { max_size };
+ self.limits.max_map_size = if max_size == usize::MAX { 0 } else { max_size };
self
}
@@ -186,7 +186,7 @@ impl Engine {
#[cfg(not(feature = "no_object"))]
#[inline(always)]
pub fn max_map_size(&self) -> usize {
- self.limits_set.max_map_size
+ self.limits.max_map_size
}
/// Set the module resolution service used by the `Engine`.
diff --git a/src/fn_call.rs b/src/fn_call.rs
index 3c258f93..cc740594 100644
--- a/src/fn_call.rs
+++ b/src/fn_call.rs
@@ -8,7 +8,7 @@ use crate::engine::{
KEYWORD_PRINT, KEYWORD_TYPE_OF,
};
use crate::fn_native::{FnCallArgs, FnPtr};
-use crate::module::{Module, ModuleRef};
+use crate::module::{Module, NamespaceRef};
use crate::optimize::OptimizationLevel;
use crate::parse_error::ParseErrorType;
use crate::result::EvalAltResult;
@@ -432,13 +432,13 @@ impl Engine {
pub(crate) fn has_override_by_name_and_arguments(
&self,
lib: &[&Module],
- name: &str,
+ fn_name: &str,
arg_types: impl AsRef<[TypeId]>,
pub_only: bool,
) -> bool {
let arg_types = arg_types.as_ref();
- let hash_fn = calc_native_fn_hash(empty(), name, arg_types.iter().cloned());
- let hash_script = calc_script_fn_hash(empty(), name, arg_types.len());
+ 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)
}
@@ -694,7 +694,7 @@ impl Engine {
mods: &mut Imports,
state: &mut State,
lib: &[&Module],
- name: &str,
+ fn_name: &str,
hash_script: u64,
target: &mut Target,
mut call_args: StaticVec,
@@ -707,9 +707,9 @@ impl Engine {
// Get a reference to the mutation target Dynamic
let obj = target.as_mut();
- let mut _fn_name = name;
+ let mut fn_name = fn_name;
- let (result, updated) = if _fn_name == KEYWORD_FN_PTR_CALL && obj.is::() {
+ let (result, updated) = if fn_name == KEYWORD_FN_PTR_CALL && obj.is::() {
// FnPtr call
let fn_ptr = obj.read_lock::().unwrap();
// Redirect function name
@@ -733,7 +733,7 @@ impl Engine {
self.exec_fn_call(
mods, state, lib, fn_name, hash, args, false, false, pub_only, None, def_val, level,
)
- } else if _fn_name == KEYWORD_FN_PTR_CALL
+ } else if fn_name == KEYWORD_FN_PTR_CALL
&& call_args.len() > 0
&& call_args[0].is::()
{
@@ -760,7 +760,7 @@ impl Engine {
self.exec_fn_call(
mods, state, lib, fn_name, hash, args, is_ref, true, pub_only, None, def_val, level,
)
- } else if _fn_name == KEYWORD_FN_PTR_CURRY && obj.is::() {
+ } else if fn_name == KEYWORD_FN_PTR_CURRY && obj.is::() {
// Curry call
let fn_ptr = obj.read_lock::().unwrap();
Ok((
@@ -779,7 +779,7 @@ impl Engine {
} else if {
#[cfg(not(feature = "no_closure"))]
{
- _fn_name == KEYWORD_IS_SHARED && call_args.is_empty()
+ fn_name == KEYWORD_IS_SHARED && call_args.is_empty()
}
#[cfg(feature = "no_closure")]
false
@@ -793,11 +793,11 @@ impl Engine {
// Check if it is a map method call in OOP style
#[cfg(not(feature = "no_object"))]
if let Some(map) = obj.read_lock::