Merge pull request #252 from schungx/master
Code refactor and structural cleanup.
This commit is contained in:
commit
edd136c047
10
RELEASES.md
10
RELEASES.md
@ -13,13 +13,23 @@ Breaking changes
|
|||||||
* The `merge_namespaces` parameter to `Module::eval_ast_as_new` is removed and now defaults to `true`.
|
* The `merge_namespaces` parameter to `Module::eval_ast_as_new` is removed and now defaults to `true`.
|
||||||
* `GlobalFileModuleResolver` is removed because its performance gain over the `FileModuleResolver` is no longer very significant.
|
* `GlobalFileModuleResolver` is removed because its performance gain over the `FileModuleResolver` is no longer very significant.
|
||||||
* The following `EvalAltResult` variants are removed and merged into `EvalAltResult::ErrorMismatchDataType`: `ErrorCharMismatch`, `ErrorNumericIndexExpr`, `ErrorStringIndexExpr`, `ErrorImportExpr`, `ErrorLogicGuard`, `ErrorBooleanArgMismatch`
|
* The following `EvalAltResult` variants are removed and merged into `EvalAltResult::ErrorMismatchDataType`: `ErrorCharMismatch`, `ErrorNumericIndexExpr`, `ErrorStringIndexExpr`, `ErrorImportExpr`, `ErrorLogicGuard`, `ErrorBooleanArgMismatch`
|
||||||
|
* `Scope::iter_raw` returns an iterator with an additional field indicating whether the variable is constant or not.
|
||||||
|
* `rhai::ser` and `rhai::de` namespaces are merged into `rhai::serde`.
|
||||||
|
|
||||||
New features
|
New features
|
||||||
------------
|
------------
|
||||||
|
|
||||||
|
* `const` statements can now take any expression (or none at all) instead of only constant values.
|
||||||
* `OptimizationLevel::Simple` now eagerly evaluates built-in binary operators of primary types (if not overloaded).
|
* `OptimizationLevel::Simple` now eagerly evaluates built-in binary operators of primary types (if not overloaded).
|
||||||
* Added `is_def_var()` to detect if variable is defined, and `is_def_fn()` to detect if script function is defined.
|
* Added `is_def_var()` to detect if variable is defined, and `is_def_fn()` to detect if script function is defined.
|
||||||
* `Dynamic::from(&str)` now constructs a `Dynamic` with a copy of the string as value.
|
* `Dynamic::from(&str)` now constructs a `Dynamic` with a copy of the string as value.
|
||||||
|
* `AST::combine` and `AST::combine_filtered` allows combining two `AST`'s without creating a new one.
|
||||||
|
|
||||||
|
Enhancements
|
||||||
|
------------
|
||||||
|
|
||||||
|
* Many one-liners and few-liners are now marked `#[inline]` or `[inline(always)]`, just in case it helps when LTO is not turned on.
|
||||||
|
* Default call stack depth for `debug` builds is reduced to 12 (from 16).
|
||||||
|
|
||||||
|
|
||||||
Version 0.19.0
|
Version 0.19.0
|
||||||
|
14
benches/README.md
Normal file
14
benches/README.md
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
Micro Benchmarks
|
||||||
|
================
|
||||||
|
|
||||||
|
Micro benchmarks to measure the speed of Rhai.
|
||||||
|
|
||||||
|
As with all micro benchmarks, take these with a grain of salt.
|
||||||
|
|
||||||
|
|
||||||
|
How to Run
|
||||||
|
----------
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo +nightly bench
|
||||||
|
```
|
@ -3,7 +3,7 @@ name = "rhai_codegen"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
authors = ["jhwgh1968"]
|
authors = ["jhwgh1968"]
|
||||||
description = "Proceducral macro support package for Rhai, a scripting language for Rust"
|
description = "Procedural macro support package for Rhai, a scripting language for Rust"
|
||||||
homepage = "https://schungx.github.io/rhai/plugins/index.html"
|
homepage = "https://schungx.github.io/rhai/plugins/index.html"
|
||||||
repository = "https://github.com/jonathandturner/rhai"
|
repository = "https://github.com/jonathandturner/rhai"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
|
4
codegen/README.md
Normal file
4
codegen/README.md
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
Procedural Macros for Plugins
|
||||||
|
============================
|
||||||
|
|
||||||
|
This crate holds procedural macros for code generation, supporting Rhai's plugins system.
|
@ -106,13 +106,23 @@ mod test;
|
|||||||
///
|
///
|
||||||
/// # Usage
|
/// # Usage
|
||||||
///
|
///
|
||||||
/// ```,ignore
|
/// ```
|
||||||
|
/// # use rhai::{Engine, EvalAltResult};
|
||||||
/// use rhai::plugin::*;
|
/// use rhai::plugin::*;
|
||||||
///
|
///
|
||||||
/// #[export_fn]
|
/// #[export_fn]
|
||||||
/// fn my_plugin_function(...) {
|
/// fn my_plugin_function(x: i64) -> i64 {
|
||||||
/// ...
|
/// x * 2
|
||||||
/// }
|
/// }
|
||||||
|
///
|
||||||
|
/// # fn main() -> Result<(), Box<EvalAltResult>> {
|
||||||
|
/// let mut engine = Engine::new();
|
||||||
|
///
|
||||||
|
/// register_exported_fn!(engine, "func", my_plugin_function);
|
||||||
|
///
|
||||||
|
/// assert_eq!(engine.eval::<i64>("func(21)")?, 42);
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[proc_macro_attribute]
|
#[proc_macro_attribute]
|
||||||
pub fn export_fn(
|
pub fn export_fn(
|
||||||
@ -138,13 +148,26 @@ pub fn export_fn(
|
|||||||
///
|
///
|
||||||
/// # Usage
|
/// # Usage
|
||||||
///
|
///
|
||||||
/// ```,ignore
|
/// ```
|
||||||
|
/// # use rhai::{Engine, Module, EvalAltResult};
|
||||||
/// use rhai::plugin::*;
|
/// use rhai::plugin::*;
|
||||||
///
|
///
|
||||||
/// #[export_module]
|
/// #[export_module]
|
||||||
/// mod my_plugin_module {
|
/// mod my_plugin_module {
|
||||||
/// ...
|
/// pub fn foo(x: i64) -> i64 { x * 2 }
|
||||||
|
/// pub fn bar() -> i64 { 21 }
|
||||||
/// }
|
/// }
|
||||||
|
///
|
||||||
|
/// # fn main() -> Result<(), Box<EvalAltResult>> {
|
||||||
|
/// let mut engine = Engine::new();
|
||||||
|
///
|
||||||
|
/// let module = exported_module!(my_plugin_module);
|
||||||
|
///
|
||||||
|
/// engine.load_package(module);
|
||||||
|
///
|
||||||
|
/// assert_eq!(engine.eval::<i64>("foo(bar())")?, 42);
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[proc_macro_attribute]
|
#[proc_macro_attribute]
|
||||||
pub fn export_module(
|
pub fn export_module(
|
||||||
@ -164,19 +187,30 @@ pub fn export_module(
|
|||||||
proc_macro::TokenStream::from(tokens)
|
proc_macro::TokenStream::from(tokens)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Macro to generate a Rhai `Module` from a _plugin module_.
|
/// Macro to generate a Rhai `Module` from a _plugin module_ defined via `#[export_module]`.
|
||||||
///
|
///
|
||||||
/// # Usage
|
/// # Usage
|
||||||
///
|
///
|
||||||
/// ```,ignore
|
/// ```
|
||||||
|
/// # use rhai::{Engine, Module, EvalAltResult};
|
||||||
/// use rhai::plugin::*;
|
/// use rhai::plugin::*;
|
||||||
///
|
///
|
||||||
/// #[export_module]
|
/// #[export_module]
|
||||||
/// mod my_plugin_module {
|
/// mod my_plugin_module {
|
||||||
/// ...
|
/// pub fn foo(x: i64) -> i64 { x * 2 }
|
||||||
|
/// pub fn bar() -> i64 { 21 }
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
|
/// # fn main() -> Result<(), Box<EvalAltResult>> {
|
||||||
|
/// let mut engine = Engine::new();
|
||||||
|
///
|
||||||
/// let module = exported_module!(my_plugin_module);
|
/// let module = exported_module!(my_plugin_module);
|
||||||
|
///
|
||||||
|
/// engine.load_package(module);
|
||||||
|
///
|
||||||
|
/// assert_eq!(engine.eval::<i64>("foo(bar())")?, 42);
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[proc_macro]
|
#[proc_macro]
|
||||||
pub fn exported_module(module_path: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
pub fn exported_module(module_path: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||||
@ -203,17 +237,27 @@ pub fn exported_module(module_path: proc_macro::TokenStream) -> proc_macro::Toke
|
|||||||
///
|
///
|
||||||
/// # Usage
|
/// # Usage
|
||||||
///
|
///
|
||||||
/// ```,ignore
|
/// ```
|
||||||
|
/// # use rhai::{Engine, Module, EvalAltResult};
|
||||||
/// use rhai::plugin::*;
|
/// use rhai::plugin::*;
|
||||||
///
|
///
|
||||||
/// #[export_module]
|
/// #[export_module]
|
||||||
/// mod my_plugin_module {
|
/// mod my_plugin_module {
|
||||||
/// ...
|
/// pub fn foo(x: i64) -> i64 { x * 2 }
|
||||||
|
/// pub fn bar() -> i64 { 21 }
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// let mut module = Module::new();
|
/// # fn main() -> Result<(), Box<EvalAltResult>> {
|
||||||
|
/// let mut engine = Engine::new();
|
||||||
///
|
///
|
||||||
|
/// let mut module = Module::new();
|
||||||
/// combine_with_exported_module!(&mut module, "my_plugin_module_ID", my_plugin_module);
|
/// combine_with_exported_module!(&mut module, "my_plugin_module_ID", my_plugin_module);
|
||||||
|
///
|
||||||
|
/// engine.load_package(module);
|
||||||
|
///
|
||||||
|
/// assert_eq!(engine.eval::<i64>("foo(bar())")?, 42);
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[proc_macro]
|
#[proc_macro]
|
||||||
pub fn combine_with_exported_module(args: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
pub fn combine_with_exported_module(args: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||||
@ -228,21 +272,27 @@ pub fn combine_with_exported_module(args: proc_macro::TokenStream) -> proc_macro
|
|||||||
proc_macro::TokenStream::from(tokens)
|
proc_macro::TokenStream::from(tokens)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Macro to register a _plugin function_ into an `Engine`.
|
/// Macro to register a _plugin function_ (defined via `#[export_fn]`) into an `Engine`.
|
||||||
///
|
///
|
||||||
/// # Usage
|
/// # Usage
|
||||||
///
|
///
|
||||||
/// ```,ignore
|
/// ```
|
||||||
|
/// # use rhai::{Engine, EvalAltResult};
|
||||||
/// use rhai::plugin::*;
|
/// use rhai::plugin::*;
|
||||||
///
|
///
|
||||||
/// #[export_fn]
|
/// #[export_fn]
|
||||||
/// fn my_plugin_function(...) {
|
/// fn my_plugin_function(x: i64) -> i64 {
|
||||||
/// ...
|
/// x * 2
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
|
/// # fn main() -> Result<(), Box<EvalAltResult>> {
|
||||||
/// let mut engine = Engine::new();
|
/// let mut engine = Engine::new();
|
||||||
///
|
///
|
||||||
/// register_exported_fn!(engine, "calc", my_plugin_function);
|
/// register_exported_fn!(engine, "func", my_plugin_function);
|
||||||
|
///
|
||||||
|
/// assert_eq!(engine.eval::<i64>("func(21)")?, 42);
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[proc_macro]
|
#[proc_macro]
|
||||||
pub fn register_exported_fn(args: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
pub fn register_exported_fn(args: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||||
@ -262,17 +312,26 @@ pub fn register_exported_fn(args: proc_macro::TokenStream) -> proc_macro::TokenS
|
|||||||
///
|
///
|
||||||
/// # Usage
|
/// # Usage
|
||||||
///
|
///
|
||||||
/// ```,ignore
|
/// ```
|
||||||
|
/// # use rhai::{Engine, EvalAltResult};
|
||||||
/// use rhai::plugin::*;
|
/// use rhai::plugin::*;
|
||||||
///
|
///
|
||||||
/// #[export_fn]
|
/// #[export_fn]
|
||||||
/// fn my_plugin_function(...) {
|
/// fn my_plugin_function(x: i64) -> i64 {
|
||||||
/// ...
|
/// x * 2
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// let mut module = Module::new();
|
/// # fn main() -> Result<(), Box<EvalAltResult>> {
|
||||||
|
/// let mut engine = Engine::new();
|
||||||
///
|
///
|
||||||
/// set_exported_fn!(module, "calc", my_plugin_function);
|
/// let mut module = Module::new();
|
||||||
|
/// set_exported_fn!(module, "func", my_plugin_function);
|
||||||
|
///
|
||||||
|
/// engine.load_package(module);
|
||||||
|
///
|
||||||
|
/// assert_eq!(engine.eval::<i64>("func(21)")?, 42);
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[proc_macro]
|
#[proc_macro]
|
||||||
pub fn set_exported_fn(args: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
pub fn set_exported_fn(args: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||||
|
41
doc/README.md
Normal file
41
doc/README.md
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
The Rhai Book
|
||||||
|
=============
|
||||||
|
|
||||||
|
[_The Rhai Book_](https://schungx.github.io/rhai) serves as Rhai's primary
|
||||||
|
documentation and tutorial resource.
|
||||||
|
|
||||||
|
|
||||||
|
How to Build from Source
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
* Install [`mdbook`](https://github.com/rust-lang/mdBook):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo install mdbook
|
||||||
|
```
|
||||||
|
|
||||||
|
* Install [`mdbook-tera`](https://github.com/avitex/mdbook-tera) (for templating):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo install mdbook-tera
|
||||||
|
```
|
||||||
|
|
||||||
|
* Run build in source directory:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd doc
|
||||||
|
mdbook build
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
Configuration Settings
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
Settings stored in `context.json`:
|
||||||
|
|
||||||
|
| Setting | Description |
|
||||||
|
| ---------- | ------------------------------------------------------------------------------------------------- |
|
||||||
|
| `version` | version of Rhai |
|
||||||
|
| `repoHome` | points to the [root of the GitHub repo](https://github.com/jonathandturner/rhai/blob/master) |
|
||||||
|
| `repoTree` | points to the [root of the GitHub repo tree](https://github.com/jonathandturner/rhai/tree/master) |
|
||||||
|
| `rootUrl` | sub-directory for the root domain, e.g. `/rhai` |
|
@ -15,12 +15,10 @@ print(x * 2); // prints 84
|
|||||||
x = 123; // <- syntax error: cannot assign to constant
|
x = 123; // <- syntax error: cannot assign to constant
|
||||||
```
|
```
|
||||||
|
|
||||||
Unlike variables which need not have initial values (default to [`()`]),
|
|
||||||
constants must be assigned one, and it must be a [_literal value_](../appendix/literals.md),
|
|
||||||
not an expression.
|
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
const x = 40 + 2; // <- syntax error: cannot assign expression to constant
|
const x; // 'x' is a constant '()'
|
||||||
|
|
||||||
|
const x = 40 + 2; // 'x' is a constant 42
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
@ -33,9 +31,7 @@ running with that [`Scope`].
|
|||||||
When added to a custom [`Scope`], a constant can hold any value, not just a literal value.
|
When added to a custom [`Scope`], a constant can hold any value, not just a literal value.
|
||||||
|
|
||||||
It is very useful to have a constant value hold a [custom type], which essentially acts
|
It is very useful to have a constant value hold a [custom type], which essentially acts
|
||||||
as a [_singleton_](../patterns/singleton.md). The singleton object can be modified via its
|
as a [_singleton_](../patterns/singleton.md).
|
||||||
registered API - being a constant only prevents it from being re-assigned or operated upon by Rhai;
|
|
||||||
mutating it via a Rust function is still allowed.
|
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
use rhai::{Engine, Scope};
|
use rhai::{Engine, Scope};
|
||||||
@ -58,3 +54,11 @@ engine.consume_with_scope(&mut scope, r"
|
|||||||
print(MY_NUMBER.value); // prints 42
|
print(MY_NUMBER.value); // prints 42
|
||||||
")?;
|
")?;
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
Constants Can be Modified, Just Not Reassigned
|
||||||
|
---------------------------------------------
|
||||||
|
|
||||||
|
A custom type stored as a constant can be modified via its registered API -
|
||||||
|
being a constant only prevents it from being re-assigned or operated upon by Rhai;
|
||||||
|
mutating it via a Rust function is still allowed.
|
||||||
|
@ -49,7 +49,7 @@ f.call(2) == 42; // the value of 'x' is 40 because 'x' is sha
|
|||||||
// The above de-sugars into this:
|
// The above de-sugars into this:
|
||||||
fn anon$1001(x, y) { x + y } // parameter 'x' is inserted
|
fn anon$1001(x, y) { x + y } // parameter 'x' is inserted
|
||||||
|
|
||||||
make_shared(x); // convert variable 'x' into a shared value
|
$make_shared(x); // convert variable 'x' into a shared value
|
||||||
|
|
||||||
let f = Fn("anon$1001").curry(x); // shared 'x' is curried
|
let f = Fn("anon$1001").curry(x); // shared 'x' is curried
|
||||||
|
|
||||||
@ -57,6 +57,24 @@ f.call(2) == 42;
|
|||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
Constants are Not Captured
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
Constants are never shared. Their values are simply cloned.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
const x = 42; // constant variable 'x'
|
||||||
|
|
||||||
|
let f = |y| x += y; // constant 'x' is cloned and not captured
|
||||||
|
|
||||||
|
x.is_shared() == false; // 'x' is not shared
|
||||||
|
|
||||||
|
f.call(10); // the cloned copy of 'x' is changed
|
||||||
|
|
||||||
|
x == 42; // 'x' is not changed
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
Beware: Captured Variables are Truly Shared
|
Beware: Captured Variables are Truly Shared
|
||||||
------------------------------------------
|
------------------------------------------
|
||||||
|
|
||||||
@ -64,7 +82,7 @@ The example below is a typical tutorial sample for many languages to illustrate
|
|||||||
that may accompany capturing external scope variables in closures.
|
that may accompany capturing external scope variables in closures.
|
||||||
|
|
||||||
It prints `9`, `9`, `9`, ... `9`, `9`, not `0`, `1`, `2`, ... `8`, `9`, because there is
|
It prints `9`, `9`, `9`, ... `9`, `9`, not `0`, `1`, `2`, ... `8`, `9`, because there is
|
||||||
ever only one captured variable, and all ten closures capture the _same_ variable.
|
ever only _one_ captured variable, and all ten closures capture the _same_ variable.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
let funcs = [];
|
let funcs = [];
|
||||||
@ -78,7 +96,7 @@ funcs.len() == 10; // 10 closures stored in the array
|
|||||||
funcs[0].type_of() == "Fn"; // make sure these are closures
|
funcs[0].type_of() == "Fn"; // make sure these are closures
|
||||||
|
|
||||||
for f in funcs {
|
for f in funcs {
|
||||||
f.call(); // all the references to 'i' are the same variable!
|
f.call(); // all references to 'i' are the same variable!
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -10,8 +10,8 @@ Each Function is a Separate Compilation Unit
|
|||||||
This means that individual functions can be separated, exported, re-grouped, imported,
|
This means that individual functions can be separated, exported, re-grouped, imported,
|
||||||
and generally mix-'n-match-ed with other completely unrelated scripts.
|
and generally mix-'n-match-ed with other completely unrelated scripts.
|
||||||
|
|
||||||
For example, the `AST::merge` method allows merging all functions in one [`AST`] into another,
|
For example, the `AST::merge` and `AST::combine` methods (or the equivalent `+` and `+=` operators)
|
||||||
forming a new, combined, group of functions.
|
allow combining all functions in one [`AST`] into another, forming a new, unified, group of functions.
|
||||||
|
|
||||||
In general, there are two types of _namespaces_ where functions are looked up:
|
In general, there are two types of _namespaces_ where functions are looked up:
|
||||||
|
|
||||||
@ -58,10 +58,10 @@ let ast1 = engine.compile(
|
|||||||
// Compile another script with an overriding function
|
// Compile another script with an overriding function
|
||||||
let ast2 = engine.compile(r#"fn get_message() { "Boo!" }"#)?;
|
let ast2 = engine.compile(r#"fn get_message() { "Boo!" }"#)?;
|
||||||
|
|
||||||
// Merge the two AST's
|
// Combine the two AST's
|
||||||
let ast = ast1.merge(ast2); // 'message' will be overwritten
|
ast1 += ast2; // 'message' will be overwritten
|
||||||
|
|
||||||
engine.consume_ast(&ast)?; // prints 'Boo!'
|
engine.consume_ast(&ast1)?; // prints 'Boo!'
|
||||||
```
|
```
|
||||||
|
|
||||||
Therefore, care must be taken when _cross-calling_ functions to make sure that the correct
|
Therefore, care must be taken when _cross-calling_ functions to make sure that the correct
|
||||||
|
@ -29,8 +29,8 @@ Key Concepts
|
|||||||
|
|
||||||
* The lowest layer script is compiled into a base [`AST`].
|
* The lowest layer script is compiled into a base [`AST`].
|
||||||
|
|
||||||
* Higher layer scripts are also compiled into [`AST`] and _merged_ into the base using `AST::merge`,
|
* Higher layer scripts are also compiled into [`AST`] and _combined_ into the base using `AST::combine`
|
||||||
overriding any existing functions.
|
(or the `+=` operator), overriding any existing functions.
|
||||||
|
|
||||||
|
|
||||||
Examples
|
Examples
|
||||||
@ -83,7 +83,7 @@ fn baz() { print("hey!"); }
|
|||||||
fn foo(x) { x + 42 }
|
fn foo(x) { x + 42 }
|
||||||
```
|
```
|
||||||
|
|
||||||
Load and merge them sequentially:
|
Load and combine them sequentially:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
let engine = Engine::new();
|
let engine = Engine::new();
|
||||||
@ -91,17 +91,17 @@ let engine = Engine::new();
|
|||||||
// Compile the baseline default implementations.
|
// Compile the baseline default implementations.
|
||||||
let mut ast = engine.compile_file("default.rhai".into())?;
|
let mut ast = engine.compile_file("default.rhai".into())?;
|
||||||
|
|
||||||
// Merge in the first layer.
|
// Combine the first layer.
|
||||||
let lowest = engine.compile_file("lowest.rhai".into())?;
|
let lowest = engine.compile_file("lowest.rhai".into())?;
|
||||||
ast = ast.merge(&lowest);
|
ast += lowest;
|
||||||
|
|
||||||
// Merge in the second layer.
|
// Combine the second layer.
|
||||||
let middle = engine.compile_file("middle.rhai".into())?;
|
let middle = engine.compile_file("middle.rhai".into())?;
|
||||||
ast = ast.merge(&middle);
|
ast += lowest;
|
||||||
|
|
||||||
// Merge in the third layer.
|
// Combine the third layer.
|
||||||
let highest = engine.compile_file("highest.rhai".into())?;
|
let highest = engine.compile_file("highest.rhai".into())?;
|
||||||
ast = ast.merge(&highest);
|
ast += lowest;
|
||||||
|
|
||||||
// Now, 'ast' contains the following functions:
|
// Now, 'ast' contains the following functions:
|
||||||
//
|
//
|
||||||
|
@ -14,12 +14,12 @@ A [`Dynamic`] can be seamlessly converted to and from a type that implements
|
|||||||
Serialization
|
Serialization
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
The function `rhai::ser::to_dynamic` automatically converts any Rust type that implements
|
The function `rhai::serde::to_dynamic` automatically converts any Rust type that implements
|
||||||
[`serde::Serialize`](https://docs.serde.rs/serde/trait.Serialize.html) into a [`Dynamic`].
|
[`serde::Serialize`](https://docs.serde.rs/serde/trait.Serialize.html) into a [`Dynamic`].
|
||||||
|
|
||||||
This is usually not necessary because using [`Dynamic::from`][`Dynamic`] is much easier and is essentially
|
This is usually not necessary because using [`Dynamic::from`][`Dynamic`] is much easier and is essentially
|
||||||
the same thing. The only difference is treatment for integer values. `Dynamic::from` will keep the different
|
the same thing. The only difference is treatment for integer values. `Dynamic::from` will keep the different
|
||||||
integer types intact, while `rhai::ser::to_dynamic` will convert them all into [`INT`][standard types]
|
integer types intact, while `rhai::serde::to_dynamic` will convert them all into [`INT`][standard types]
|
||||||
(i.e. the system integer type which is `i64` or `i32` depending on the [`only_i32`] feature).
|
(i.e. the system integer type which is `i64` or `i32` depending on the [`only_i32`] feature).
|
||||||
|
|
||||||
In particular, Rust `struct`'s (or any type that is marked as a `serde` map) are converted into [object maps]
|
In particular, Rust `struct`'s (or any type that is marked as a `serde` map) are converted into [object maps]
|
||||||
@ -27,11 +27,11 @@ while Rust `Vec`'s (or any type that is marked as a `serde` sequence) are conver
|
|||||||
|
|
||||||
While it is also simple to serialize a Rust type to `JSON` via `serde`,
|
While it is also simple to serialize a Rust type to `JSON` via `serde`,
|
||||||
then use [`Engine::parse_json`]({{rootUrl}}/language/json.md) to convert it into an [object map],
|
then use [`Engine::parse_json`]({{rootUrl}}/language/json.md) to convert it into an [object map],
|
||||||
`rhai::ser::to_dynamic` serializes it to [`Dynamic`] directly via `serde` without going through the `JSON` step.
|
`rhai::serde::to_dynamic` serializes it to [`Dynamic`] directly via `serde` without going through the `JSON` step.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
use rhai::{Dynamic, Map};
|
use rhai::{Dynamic, Map};
|
||||||
use rhai::ser::to_dynamic;
|
use rhai::serde::to_dynamic;
|
||||||
|
|
||||||
#[derive(Debug, serde::Serialize)]
|
#[derive(Debug, serde::Serialize)]
|
||||||
struct Point {
|
struct Point {
|
||||||
@ -64,7 +64,7 @@ map.is::<Map>() == true;
|
|||||||
Deserialization
|
Deserialization
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
The function `rhai::de::from_dynamic` automatically converts a [`Dynamic`] value into any Rust type
|
The function `rhai::serde::from_dynamic` automatically converts a [`Dynamic`] value into any Rust type
|
||||||
that implements [`serde::Deserialize`](https://docs.serde.rs/serde/trait.Deserialize.html).
|
that implements [`serde::Deserialize`](https://docs.serde.rs/serde/trait.Deserialize.html).
|
||||||
|
|
||||||
In particular, [object maps] are converted into Rust `struct`'s (or any type that is marked as
|
In particular, [object maps] are converted into Rust `struct`'s (or any type that is marked as
|
||||||
@ -73,7 +73,7 @@ as a `serde` sequence).
|
|||||||
|
|
||||||
```rust
|
```rust
|
||||||
use rhai::{Engine, Dynamic};
|
use rhai::{Engine, Dynamic};
|
||||||
use rhai::de::from_dynamic;
|
use rhai::serde::from_dynamic;
|
||||||
|
|
||||||
#[derive(Debug, serde::Deserialize)]
|
#[derive(Debug, serde::Deserialize)]
|
||||||
struct Point {
|
struct Point {
|
||||||
|
@ -6,7 +6,7 @@ Maximum Call Stack Depth
|
|||||||
Limit How Stack Usage by Scripts
|
Limit How Stack Usage by Scripts
|
||||||
-------------------------------
|
-------------------------------
|
||||||
|
|
||||||
Rhai by default limits function calls to a maximum depth of 128 levels (16 levels in debug build).
|
Rhai by default limits function calls to a maximum depth of 128 levels (12 levels in debug build).
|
||||||
|
|
||||||
This limit may be changed via the `Engine::set_max_call_levels` method.
|
This limit may be changed via the `Engine::set_max_call_levels` method.
|
||||||
|
|
||||||
@ -17,9 +17,9 @@ This check can be disabled via the [`unchecked`] feature for higher performance
|
|||||||
```rust
|
```rust
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
engine.set_max_call_levels(10); // allow only up to 10 levels of function calls
|
engine.set_max_call_levels(10); // allow only up to 10 levels of function calls
|
||||||
|
|
||||||
engine.set_max_call_levels(0); // allow no function calls at all (max depth = zero)
|
engine.set_max_call_levels(0); // allow no function calls at all (max depth = zero)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
12
examples/README.md
Normal file
12
examples/README.md
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
Sample Applications
|
||||||
|
===================
|
||||||
|
|
||||||
|
Sample applications written in Rhai.
|
||||||
|
|
||||||
|
|
||||||
|
How to Run
|
||||||
|
----------
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo run --example sample_app_to_run
|
||||||
|
```
|
@ -20,10 +20,10 @@ impl TestStruct {
|
|||||||
fn main() {
|
fn main() {
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
engine.register_type::<TestStruct>();
|
engine
|
||||||
|
.register_type::<TestStruct>()
|
||||||
engine.register_fn("update", TestStruct::update);
|
.register_fn("update", TestStruct::update)
|
||||||
engine.register_fn("new_ts", TestStruct::new);
|
.register_fn("new_ts", TestStruct::new);
|
||||||
|
|
||||||
println!(
|
println!(
|
||||||
"{:?}",
|
"{:?}",
|
||||||
|
@ -19,10 +19,10 @@ impl TestStruct {
|
|||||||
fn main() -> Result<(), Box<EvalAltResult>> {
|
fn main() -> Result<(), Box<EvalAltResult>> {
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
engine.register_type::<TestStruct>();
|
engine
|
||||||
|
.register_type::<TestStruct>()
|
||||||
engine.register_fn("update", TestStruct::update);
|
.register_fn("update", TestStruct::update)
|
||||||
engine.register_fn("new_ts", TestStruct::new);
|
.register_fn("new_ts", TestStruct::new);
|
||||||
|
|
||||||
let result = engine.eval::<TestStruct>("let x = new_ts(); x.update(); x")?;
|
let result = engine.eval::<TestStruct>("let x = new_ts(); x.update(); x")?;
|
||||||
|
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
use rhai::{packages::*, Engine, EvalAltResult, INT};
|
use rhai::{Engine, EvalAltResult, INT};
|
||||||
|
|
||||||
fn main() -> Result<(), Box<EvalAltResult>> {
|
fn main() -> Result<(), Box<EvalAltResult>> {
|
||||||
let mut engine = Engine::new_raw();
|
let engine = Engine::new();
|
||||||
engine.load_package(ArithmeticPackage::new().get());
|
|
||||||
|
|
||||||
let result = engine.eval::<INT>("40 + 2")?;
|
let result = engine.eval::<INT>("40 + 2")?;
|
||||||
|
|
||||||
|
@ -114,15 +114,19 @@ fn main() {
|
|||||||
}
|
}
|
||||||
"exit" | "quit" => break, // quit
|
"exit" | "quit" => break, // quit
|
||||||
"scope" => {
|
"scope" => {
|
||||||
scope.iter_raw().enumerate().for_each(|(i, (name, value))| {
|
scope
|
||||||
println!(
|
.iter_raw()
|
||||||
"[{}] {}{} = {:?}",
|
.enumerate()
|
||||||
i + 1,
|
.for_each(|(i, (name, constant, value))| {
|
||||||
name,
|
println!(
|
||||||
if value.is_shared() { " (shared)" } else { "" },
|
"[{}] {}{}{} = {:?}",
|
||||||
*value.read_lock::<Dynamic>().unwrap(),
|
i + 1,
|
||||||
)
|
if constant { "const " } else { "" },
|
||||||
});
|
name,
|
||||||
|
if value.is_shared() { " (shared)" } else { "" },
|
||||||
|
*value.read_lock::<Dynamic>().unwrap(),
|
||||||
|
)
|
||||||
|
});
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
"astu" => {
|
"astu" => {
|
||||||
@ -155,7 +159,7 @@ fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Merge the AST into the main
|
// Merge the AST into the main
|
||||||
main_ast = main_ast.merge(&ast);
|
main_ast += ast.clone();
|
||||||
|
|
||||||
// Evaluate
|
// Evaluate
|
||||||
engine.eval_ast_with_scope::<Dynamic>(&mut scope, &main_ast)
|
engine.eval_ast_with_scope::<Dynamic>(&mut scope, &main_ast)
|
||||||
|
@ -13,7 +13,7 @@ fn main() {
|
|||||||
|
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
mod example {
|
mod example {
|
||||||
use rhai::{de::from_dynamic, ser::to_dynamic};
|
use rhai::serde::{from_dynamic, to_dynamic};
|
||||||
use rhai::{Dynamic, Engine, Map};
|
use rhai::{Dynamic, Engine, Map};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
@ -56,7 +56,7 @@ mod example {
|
|||||||
let result: Dynamic = engine
|
let result: Dynamic = engine
|
||||||
.eval(
|
.eval(
|
||||||
r#"
|
r#"
|
||||||
##{
|
#{
|
||||||
a: 42,
|
a: 42,
|
||||||
b: [ "hello", "world" ],
|
b: [ "hello", "world" ],
|
||||||
c: true,
|
c: true,
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
use rhai::{Engine, EvalAltResult, RegisterFn, INT};
|
use rhai::{Engine, EvalAltResult, RegisterFn, INT};
|
||||||
|
|
||||||
|
fn add(x: INT, y: INT) -> INT {
|
||||||
|
x + y
|
||||||
|
}
|
||||||
|
|
||||||
fn main() -> Result<(), Box<EvalAltResult>> {
|
fn main() -> Result<(), Box<EvalAltResult>> {
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
fn add(x: INT, y: INT) -> INT {
|
|
||||||
x + y
|
|
||||||
}
|
|
||||||
|
|
||||||
engine.register_fn("add", add);
|
engine.register_fn("add", add);
|
||||||
|
|
||||||
let result = engine.eval::<INT>("add(40, 2)")?;
|
let result = engine.eval::<INT>("add(40, 2)")?;
|
||||||
|
@ -29,17 +29,17 @@ fn main() -> Result<(), Box<EvalAltResult>> {
|
|||||||
let mut engine = Engine::new_raw();
|
let mut engine = Engine::new_raw();
|
||||||
|
|
||||||
// Register string functions
|
// Register string functions
|
||||||
engine.register_fn("trim", trim_string);
|
engine
|
||||||
engine.register_fn("len", count_string_bytes);
|
.register_fn("trim", trim_string)
|
||||||
engine.register_fn("index_of", find_substring);
|
.register_fn("len", count_string_bytes)
|
||||||
|
.register_fn("index_of", find_substring)
|
||||||
// Register string functions using closures
|
.register_fn("display", |label: &str, x: INT| {
|
||||||
engine.register_fn("display", |label: &str, x: INT| {
|
// Register string functions using closures
|
||||||
println!("{}: {}", label, x)
|
println!("{}: {}", label, x)
|
||||||
});
|
})
|
||||||
engine.register_fn("display", |label: ImmutableString, x: &str| {
|
.register_fn("display", |label: ImmutableString, x: &str| {
|
||||||
println!(r#"{}: "{}""#, label, x) // Quote the input string
|
println!(r#"{}: "{}""#, label, x) // Quote the input string
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut scope = Scope::new();
|
let mut scope = Scope::new();
|
||||||
let mut input = String::new();
|
let mut input = String::new();
|
||||||
|
20
scripts/README.md
Normal file
20
scripts/README.md
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
Testing Scripts
|
||||||
|
===============
|
||||||
|
|
||||||
|
Testing scripts written in Rhai.
|
||||||
|
|
||||||
|
|
||||||
|
How to Run
|
||||||
|
----------
|
||||||
|
|
||||||
|
Compile the `rhai_runner` example:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo build --example rhai_runner
|
||||||
|
```
|
||||||
|
|
||||||
|
Run it:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./target/debug/examples/rhai_runner ./scripts/test_script_to_run.rhai
|
||||||
|
```
|
@ -541,6 +541,8 @@ impl Dynamic {
|
|||||||
/// ```
|
/// ```
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn from<T: Variant + Clone>(value: T) -> Self {
|
pub fn from<T: Variant + Clone>(value: T) -> Self {
|
||||||
|
// Coded this way in order to maximally leverage potentials for dead-code removal.
|
||||||
|
|
||||||
if TypeId::of::<T>() == TypeId::of::<INT>() {
|
if TypeId::of::<T>() == TypeId::of::<INT>() {
|
||||||
return <dyn Any>::downcast_ref::<INT>(&value)
|
return <dyn Any>::downcast_ref::<INT>(&value)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
@ -677,6 +679,8 @@ impl Dynamic {
|
|||||||
/// ```
|
/// ```
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn try_cast<T: Variant>(self) -> Option<T> {
|
pub fn try_cast<T: Variant>(self) -> Option<T> {
|
||||||
|
// Coded this way in order to maximally leverage potentials for dead-code removal.
|
||||||
|
|
||||||
match self.0 {
|
match self.0 {
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
@ -964,6 +968,8 @@ impl Dynamic {
|
|||||||
/// Returns `None` if the cast fails, or if the value is shared.
|
/// Returns `None` if the cast fails, or if the value is shared.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn downcast_ref<T: Variant + Clone>(&self) -> Option<&T> {
|
pub(crate) fn downcast_ref<T: Variant + Clone>(&self) -> Option<&T> {
|
||||||
|
// Coded this way in order to maximally leverage potentials for dead-code removal.
|
||||||
|
|
||||||
if TypeId::of::<T>() == TypeId::of::<INT>() {
|
if TypeId::of::<T>() == TypeId::of::<INT>() {
|
||||||
return match &self.0 {
|
return match &self.0 {
|
||||||
Union::Int(value) => <dyn Any>::downcast_ref::<T>(value),
|
Union::Int(value) => <dyn Any>::downcast_ref::<T>(value),
|
||||||
@ -1052,6 +1058,8 @@ impl Dynamic {
|
|||||||
/// Returns `None` if the cast fails, or if the value is shared.
|
/// Returns `None` if the cast fails, or if the value is shared.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn downcast_mut<T: Variant + Clone>(&mut self) -> Option<&mut T> {
|
pub(crate) fn downcast_mut<T: Variant + Clone>(&mut self) -> Option<&mut T> {
|
||||||
|
// Coded this way in order to maximally leverage potentials for dead-code removal.
|
||||||
|
|
||||||
if TypeId::of::<T>() == TypeId::of::<INT>() {
|
if TypeId::of::<T>() == TypeId::of::<INT>() {
|
||||||
return match &mut self.0 {
|
return match &mut self.0 {
|
||||||
Union::Int(value) => <dyn Any>::downcast_mut::<T>(value),
|
Union::Int(value) => <dyn Any>::downcast_mut::<T>(value),
|
||||||
|
47
src/api.rs
47
src/api.rs
@ -28,7 +28,7 @@ use crate::{
|
|||||||
use crate::fn_register::{RegisterFn, RegisterResultFn};
|
use crate::fn_register::{RegisterFn, RegisterResultFn};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
use crate::{fn_args::FuncArgs, fn_call::ensure_no_data_race, utils::StaticVec};
|
use crate::{fn_args::FuncArgs, fn_call::ensure_no_data_race, StaticVec};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_optimize"))]
|
#[cfg(not(feature = "no_optimize"))]
|
||||||
use crate::optimize::optimize_into_ast;
|
use crate::optimize::optimize_into_ast;
|
||||||
@ -64,6 +64,7 @@ impl Engine {
|
|||||||
///
|
///
|
||||||
/// To access the first mutable parameter, use `args.get_mut(0).unwrap()`
|
/// To access the first mutable parameter, use `args.get_mut(0).unwrap()`
|
||||||
#[deprecated(note = "this function is volatile and may change")]
|
#[deprecated(note = "this function is volatile and may change")]
|
||||||
|
#[inline(always)]
|
||||||
pub fn register_raw_fn<T: Variant + Clone>(
|
pub fn register_raw_fn<T: Variant + Clone>(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: &str,
|
name: &str,
|
||||||
@ -111,6 +112,7 @@ impl Engine {
|
|||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
#[inline(always)]
|
||||||
pub fn register_type<T: Variant + Clone>(&mut self) -> &mut Self {
|
pub fn register_type<T: Variant + Clone>(&mut self) -> &mut Self {
|
||||||
self.register_type_with_name::<T>(type_name::<T>())
|
self.register_type_with_name::<T>(type_name::<T>())
|
||||||
}
|
}
|
||||||
@ -159,6 +161,7 @@ impl Engine {
|
|||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
#[inline(always)]
|
||||||
pub fn register_type_with_name<T: Variant + Clone>(&mut self, name: &str) -> &mut Self {
|
pub fn register_type_with_name<T: Variant + Clone>(&mut self, name: &str) -> &mut Self {
|
||||||
if self.type_names.is_none() {
|
if self.type_names.is_none() {
|
||||||
self.type_names = Some(Default::default());
|
self.type_names = Some(Default::default());
|
||||||
@ -173,6 +176,7 @@ impl Engine {
|
|||||||
|
|
||||||
/// Register an iterator adapter for a type with the `Engine`.
|
/// Register an iterator adapter for a type with the `Engine`.
|
||||||
/// This is an advanced feature.
|
/// This is an advanced feature.
|
||||||
|
#[inline(always)]
|
||||||
pub fn register_iterator<T: Variant + Clone>(&mut self, f: IteratorFn) -> &mut Self {
|
pub fn register_iterator<T: Variant + Clone>(&mut self, f: IteratorFn) -> &mut Self {
|
||||||
self.global_module.set_iter(TypeId::of::<T>(), f);
|
self.global_module.set_iter(TypeId::of::<T>(), f);
|
||||||
self
|
self
|
||||||
@ -214,6 +218,7 @@ impl Engine {
|
|||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
#[inline(always)]
|
||||||
pub fn register_get<T, U>(
|
pub fn register_get<T, U>(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: &str,
|
name: &str,
|
||||||
@ -265,6 +270,7 @@ impl Engine {
|
|||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
#[inline(always)]
|
||||||
pub fn register_get_result<T: Variant + Clone>(
|
pub fn register_get_result<T: Variant + Clone>(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: &str,
|
name: &str,
|
||||||
@ -310,6 +316,7 @@ impl Engine {
|
|||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
#[inline(always)]
|
||||||
pub fn register_set<T, U>(
|
pub fn register_set<T, U>(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: &str,
|
name: &str,
|
||||||
@ -363,6 +370,7 @@ impl Engine {
|
|||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
#[inline(always)]
|
||||||
pub fn register_set_result<T, U>(
|
pub fn register_set_result<T, U>(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: &str,
|
name: &str,
|
||||||
@ -417,6 +425,7 @@ impl Engine {
|
|||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
#[inline(always)]
|
||||||
pub fn register_get_set<T, U>(
|
pub fn register_get_set<T, U>(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: &str,
|
name: &str,
|
||||||
@ -472,6 +481,7 @@ impl Engine {
|
|||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
#[inline(always)]
|
||||||
pub fn register_indexer_get<T, X, U>(
|
pub fn register_indexer_get<T, X, U>(
|
||||||
&mut self,
|
&mut self,
|
||||||
callback: impl Fn(&mut T, X) -> U + SendSync + 'static,
|
callback: impl Fn(&mut T, X) -> U + SendSync + 'static,
|
||||||
@ -543,6 +553,7 @@ impl Engine {
|
|||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
#[inline(always)]
|
||||||
pub fn register_indexer_get_result<T, X>(
|
pub fn register_indexer_get_result<T, X>(
|
||||||
&mut self,
|
&mut self,
|
||||||
callback: impl Fn(&mut T, X) -> Result<Dynamic, Box<EvalAltResult>> + SendSync + 'static,
|
callback: impl Fn(&mut T, X) -> Result<Dynamic, Box<EvalAltResult>> + SendSync + 'static,
|
||||||
@ -610,6 +621,7 @@ impl Engine {
|
|||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
#[inline(always)]
|
||||||
pub fn register_indexer_set<T, X, U>(
|
pub fn register_indexer_set<T, X, U>(
|
||||||
&mut self,
|
&mut self,
|
||||||
callback: impl Fn(&mut T, X, U) + SendSync + 'static,
|
callback: impl Fn(&mut T, X, U) + SendSync + 'static,
|
||||||
@ -682,6 +694,7 @@ impl Engine {
|
|||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
#[inline(always)]
|
||||||
pub fn register_indexer_set_result<T, X, U>(
|
pub fn register_indexer_set_result<T, X, U>(
|
||||||
&mut self,
|
&mut self,
|
||||||
callback: impl Fn(&mut T, X, U) -> Result<(), Box<EvalAltResult>> + SendSync + 'static,
|
callback: impl Fn(&mut T, X, U) -> Result<(), Box<EvalAltResult>> + SendSync + 'static,
|
||||||
@ -752,6 +765,7 @@ impl Engine {
|
|||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
#[inline(always)]
|
||||||
pub fn register_indexer_get_set<T, X, U>(
|
pub fn register_indexer_get_set<T, X, U>(
|
||||||
&mut self,
|
&mut self,
|
||||||
getter: impl Fn(&mut T, X) -> U + SendSync + 'static,
|
getter: impl Fn(&mut T, X) -> U + SendSync + 'static,
|
||||||
@ -785,6 +799,7 @@ impl Engine {
|
|||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
|
#[inline(always)]
|
||||||
pub fn compile(&self, script: &str) -> Result<AST, ParseError> {
|
pub fn compile(&self, script: &str) -> Result<AST, ParseError> {
|
||||||
self.compile_with_scope(&Scope::new(), script)
|
self.compile_with_scope(&Scope::new(), script)
|
||||||
}
|
}
|
||||||
@ -827,6 +842,7 @@ impl Engine {
|
|||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
|
#[inline(always)]
|
||||||
pub fn compile_with_scope(&self, scope: &Scope, script: &str) -> Result<AST, ParseError> {
|
pub fn compile_with_scope(&self, scope: &Scope, script: &str) -> Result<AST, ParseError> {
|
||||||
self.compile_scripts_with_scope(scope, &[script])
|
self.compile_scripts_with_scope(scope, &[script])
|
||||||
}
|
}
|
||||||
@ -877,6 +893,7 @@ impl Engine {
|
|||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
|
#[inline(always)]
|
||||||
pub fn compile_scripts_with_scope(
|
pub fn compile_scripts_with_scope(
|
||||||
&self,
|
&self,
|
||||||
scope: &Scope,
|
scope: &Scope,
|
||||||
@ -886,6 +903,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Join a list of strings and compile into an `AST` using own scope at a specific 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(
|
pub(crate) fn compile_with_scope_and_optimization_level(
|
||||||
&self,
|
&self,
|
||||||
scope: &Scope,
|
scope: &Scope,
|
||||||
@ -899,6 +917,7 @@ impl Engine {
|
|||||||
/// Read the contents of a file into a string.
|
/// Read the contents of a file into a string.
|
||||||
#[cfg(not(feature = "no_std"))]
|
#[cfg(not(feature = "no_std"))]
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
#[inline]
|
||||||
fn read_file(path: PathBuf) -> Result<String, Box<EvalAltResult>> {
|
fn read_file(path: PathBuf) -> Result<String, Box<EvalAltResult>> {
|
||||||
let mut f = File::open(path.clone()).map_err(|err| {
|
let mut f = File::open(path.clone()).map_err(|err| {
|
||||||
EvalAltResult::ErrorReadingScriptFile(path.clone(), Position::none(), err)
|
EvalAltResult::ErrorReadingScriptFile(path.clone(), Position::none(), err)
|
||||||
@ -935,6 +954,7 @@ impl Engine {
|
|||||||
/// ```
|
/// ```
|
||||||
#[cfg(not(feature = "no_std"))]
|
#[cfg(not(feature = "no_std"))]
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
#[inline(always)]
|
||||||
pub fn compile_file(&self, path: PathBuf) -> Result<AST, Box<EvalAltResult>> {
|
pub fn compile_file(&self, path: PathBuf) -> Result<AST, Box<EvalAltResult>> {
|
||||||
self.compile_file_with_scope(&Scope::new(), path)
|
self.compile_file_with_scope(&Scope::new(), path)
|
||||||
}
|
}
|
||||||
@ -972,6 +992,7 @@ impl Engine {
|
|||||||
/// ```
|
/// ```
|
||||||
#[cfg(not(feature = "no_std"))]
|
#[cfg(not(feature = "no_std"))]
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
#[inline(always)]
|
||||||
pub fn compile_file_with_scope(
|
pub fn compile_file_with_scope(
|
||||||
&self,
|
&self,
|
||||||
scope: &Scope,
|
scope: &Scope,
|
||||||
@ -1081,6 +1102,7 @@ impl Engine {
|
|||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
|
#[inline(always)]
|
||||||
pub fn compile_expression(&self, script: &str) -> Result<AST, ParseError> {
|
pub fn compile_expression(&self, script: &str) -> Result<AST, ParseError> {
|
||||||
self.compile_expression_with_scope(&Scope::new(), script)
|
self.compile_expression_with_scope(&Scope::new(), script)
|
||||||
}
|
}
|
||||||
@ -1124,6 +1146,7 @@ impl Engine {
|
|||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
|
#[inline]
|
||||||
pub fn compile_expression_with_scope(
|
pub fn compile_expression_with_scope(
|
||||||
&self,
|
&self,
|
||||||
scope: &Scope,
|
scope: &Scope,
|
||||||
@ -1154,6 +1177,7 @@ impl Engine {
|
|||||||
/// ```
|
/// ```
|
||||||
#[cfg(not(feature = "no_std"))]
|
#[cfg(not(feature = "no_std"))]
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
#[inline(always)]
|
||||||
pub fn eval_file<T: Variant + Clone>(&self, path: PathBuf) -> Result<T, Box<EvalAltResult>> {
|
pub fn eval_file<T: Variant + Clone>(&self, path: PathBuf) -> Result<T, Box<EvalAltResult>> {
|
||||||
Self::read_file(path).and_then(|contents| self.eval::<T>(&contents))
|
Self::read_file(path).and_then(|contents| self.eval::<T>(&contents))
|
||||||
}
|
}
|
||||||
@ -1179,6 +1203,7 @@ impl Engine {
|
|||||||
/// ```
|
/// ```
|
||||||
#[cfg(not(feature = "no_std"))]
|
#[cfg(not(feature = "no_std"))]
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
#[inline(always)]
|
||||||
pub fn eval_file_with_scope<T: Variant + Clone>(
|
pub fn eval_file_with_scope<T: Variant + Clone>(
|
||||||
&self,
|
&self,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
@ -1201,6 +1226,7 @@ impl Engine {
|
|||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
|
#[inline(always)]
|
||||||
pub fn eval<T: Variant + Clone>(&self, script: &str) -> Result<T, Box<EvalAltResult>> {
|
pub fn eval<T: Variant + Clone>(&self, script: &str) -> Result<T, Box<EvalAltResult>> {
|
||||||
self.eval_with_scope(&mut Scope::new(), script)
|
self.eval_with_scope(&mut Scope::new(), script)
|
||||||
}
|
}
|
||||||
@ -1227,6 +1253,7 @@ impl Engine {
|
|||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
|
#[inline]
|
||||||
pub fn eval_with_scope<T: Variant + Clone>(
|
pub fn eval_with_scope<T: Variant + Clone>(
|
||||||
&self,
|
&self,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
@ -1254,6 +1281,7 @@ impl Engine {
|
|||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
|
#[inline(always)]
|
||||||
pub fn eval_expression<T: Variant + Clone>(
|
pub fn eval_expression<T: Variant + Clone>(
|
||||||
&self,
|
&self,
|
||||||
script: &str,
|
script: &str,
|
||||||
@ -1279,6 +1307,7 @@ impl Engine {
|
|||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
|
#[inline]
|
||||||
pub fn eval_expression_with_scope<T: Variant + Clone>(
|
pub fn eval_expression_with_scope<T: Variant + Clone>(
|
||||||
&self,
|
&self,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
@ -1311,6 +1340,7 @@ impl Engine {
|
|||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
|
#[inline(always)]
|
||||||
pub fn eval_ast<T: Variant + Clone>(&self, ast: &AST) -> Result<T, Box<EvalAltResult>> {
|
pub fn eval_ast<T: Variant + Clone>(&self, ast: &AST) -> Result<T, Box<EvalAltResult>> {
|
||||||
self.eval_ast_with_scope(&mut Scope::new(), ast)
|
self.eval_ast_with_scope(&mut Scope::new(), ast)
|
||||||
}
|
}
|
||||||
@ -1344,6 +1374,7 @@ impl Engine {
|
|||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
|
#[inline]
|
||||||
pub fn eval_ast_with_scope<T: Variant + Clone>(
|
pub fn eval_ast_with_scope<T: Variant + Clone>(
|
||||||
&self,
|
&self,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
@ -1365,6 +1396,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Evaluate an `AST` with own scope.
|
/// Evaluate an `AST` with own scope.
|
||||||
|
#[inline]
|
||||||
pub(crate) fn eval_ast_with_scope_raw<'a>(
|
pub(crate) fn eval_ast_with_scope_raw<'a>(
|
||||||
&self,
|
&self,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
@ -1389,6 +1421,7 @@ impl Engine {
|
|||||||
/// Useful for when you don't need the result, but still need to keep track of possible errors.
|
/// Useful for when you don't need the result, but still need to keep track of possible errors.
|
||||||
#[cfg(not(feature = "no_std"))]
|
#[cfg(not(feature = "no_std"))]
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
#[inline(always)]
|
||||||
pub fn consume_file(&self, path: PathBuf) -> Result<(), Box<EvalAltResult>> {
|
pub fn consume_file(&self, path: PathBuf) -> Result<(), Box<EvalAltResult>> {
|
||||||
Self::read_file(path).and_then(|contents| self.consume(&contents))
|
Self::read_file(path).and_then(|contents| self.consume(&contents))
|
||||||
}
|
}
|
||||||
@ -1397,6 +1430,7 @@ impl Engine {
|
|||||||
/// Useful for when you don't need the result, but still need to keep track of possible errors.
|
/// Useful for when you don't need the result, but still need to keep track of possible errors.
|
||||||
#[cfg(not(feature = "no_std"))]
|
#[cfg(not(feature = "no_std"))]
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
#[inline(always)]
|
||||||
pub fn consume_file_with_scope(
|
pub fn consume_file_with_scope(
|
||||||
&self,
|
&self,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
@ -1407,12 +1441,14 @@ impl Engine {
|
|||||||
|
|
||||||
/// Evaluate a string, but throw away the result and only return error (if any).
|
/// 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.
|
/// 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<EvalAltResult>> {
|
pub fn consume(&self, script: &str) -> Result<(), Box<EvalAltResult>> {
|
||||||
self.consume_with_scope(&mut Scope::new(), script)
|
self.consume_with_scope(&mut Scope::new(), script)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Evaluate a string with own scope, but throw away the result and only return error (if any).
|
/// 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.
|
/// Useful for when you don't need the result, but still need to keep track of possible errors.
|
||||||
|
#[inline]
|
||||||
pub fn consume_with_scope(
|
pub fn consume_with_scope(
|
||||||
&self,
|
&self,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
@ -1426,12 +1462,14 @@ impl Engine {
|
|||||||
|
|
||||||
/// Evaluate an AST, but throw away the result and only return error (if any).
|
/// 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.
|
/// 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<EvalAltResult>> {
|
pub fn consume_ast(&self, ast: &AST) -> Result<(), Box<EvalAltResult>> {
|
||||||
self.consume_ast_with_scope(&mut Scope::new(), ast)
|
self.consume_ast_with_scope(&mut Scope::new(), ast)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Evaluate an `AST` with own scope, but throw away the result and only return error (if any).
|
/// 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.
|
/// Useful for when you don't need the result, but still need to keep track of possible errors.
|
||||||
|
#[inline]
|
||||||
pub fn consume_ast_with_scope(
|
pub fn consume_ast_with_scope(
|
||||||
&self,
|
&self,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
@ -1491,6 +1529,7 @@ impl Engine {
|
|||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
#[inline]
|
||||||
pub fn call_fn<A: FuncArgs, T: Variant + Clone>(
|
pub fn call_fn<A: FuncArgs, T: Variant + Clone>(
|
||||||
&self,
|
&self,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
@ -1565,6 +1604,7 @@ impl Engine {
|
|||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
#[inline(always)]
|
||||||
pub fn call_fn_dynamic(
|
pub fn call_fn_dynamic(
|
||||||
&self,
|
&self,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
@ -1587,6 +1627,7 @@ impl Engine {
|
|||||||
/// Do not use the arguments after this call. If they are needed afterwards,
|
/// Do not use the arguments after this call. If they are needed afterwards,
|
||||||
/// clone them _before_ calling this function.
|
/// clone them _before_ calling this function.
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
#[inline]
|
||||||
pub(crate) fn call_fn_dynamic_raw(
|
pub(crate) fn call_fn_dynamic_raw(
|
||||||
&self,
|
&self,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
@ -1624,6 +1665,7 @@ impl Engine {
|
|||||||
/// compiled just once. Before evaluation, constants are passed into the `Engine` via an external scope
|
/// compiled just once. Before evaluation, constants are passed into the `Engine` via an external scope
|
||||||
/// (i.e. with `scope.push_constant(...)`). Then, the `AST is cloned and the copy re-optimized before running.
|
/// (i.e. with `scope.push_constant(...)`). Then, the `AST is cloned and the copy re-optimized before running.
|
||||||
#[cfg(not(feature = "no_optimize"))]
|
#[cfg(not(feature = "no_optimize"))]
|
||||||
|
#[inline]
|
||||||
pub fn optimize_ast(
|
pub fn optimize_ast(
|
||||||
&self,
|
&self,
|
||||||
scope: &Scope,
|
scope: &Scope,
|
||||||
@ -1678,6 +1720,7 @@ impl Engine {
|
|||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
|
#[inline(always)]
|
||||||
pub fn on_progress(
|
pub fn on_progress(
|
||||||
&mut self,
|
&mut self,
|
||||||
callback: impl Fn(&u64) -> bool + SendSync + 'static,
|
callback: impl Fn(&u64) -> bool + SendSync + 'static,
|
||||||
@ -1710,6 +1753,7 @@ impl Engine {
|
|||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
|
#[inline(always)]
|
||||||
pub fn on_print(&mut self, callback: impl Fn(&str) + SendSync + 'static) -> &mut Self {
|
pub fn on_print(&mut self, callback: impl Fn(&str) + SendSync + 'static) -> &mut Self {
|
||||||
self.print = Box::new(callback);
|
self.print = Box::new(callback);
|
||||||
self
|
self
|
||||||
@ -1739,6 +1783,7 @@ impl Engine {
|
|||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
|
#[inline(always)]
|
||||||
pub fn on_debug(&mut self, callback: impl Fn(&str) + SendSync + 'static) -> &mut Self {
|
pub fn on_debug(&mut self, callback: impl Fn(&str) + SendSync + 'static) -> &mut Self {
|
||||||
self.debug = Box::new(callback);
|
self.debug = Box::new(callback);
|
||||||
self
|
self
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
//! Main module defining the script evaluation `Engine`.
|
//! Main module defining the script evaluation `Engine`.
|
||||||
|
|
||||||
use crate::any::{map_std_type_name, Dynamic, Union};
|
use crate::any::{map_std_type_name, Dynamic, Union};
|
||||||
use crate::calc_fn_hash;
|
|
||||||
use crate::fn_call::run_builtin_op_assignment;
|
use crate::fn_call::run_builtin_op_assignment;
|
||||||
use crate::fn_native::{Callback, FnPtr};
|
use crate::fn_native::{Callback, FnPtr};
|
||||||
use crate::module::{Module, ModuleRef};
|
use crate::module::{Module, ModuleRef};
|
||||||
@ -13,7 +12,7 @@ use crate::result::EvalAltResult;
|
|||||||
use crate::scope::{EntryType as ScopeEntryType, Scope};
|
use crate::scope::{EntryType as ScopeEntryType, Scope};
|
||||||
use crate::syntax::{CustomSyntax, EvalContext};
|
use crate::syntax::{CustomSyntax, EvalContext};
|
||||||
use crate::token::Position;
|
use crate::token::Position;
|
||||||
use crate::utils::StaticVec;
|
use crate::{calc_fn_hash, StaticVec};
|
||||||
|
|
||||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||||
use crate::any::Variant;
|
use crate::any::Variant;
|
||||||
@ -75,7 +74,7 @@ pub type Imports = Vec<(ImmutableString, Module)>;
|
|||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
pub const MAX_CALL_STACK_DEPTH: usize = 16;
|
pub const MAX_CALL_STACK_DEPTH: usize = 12;
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
pub const MAX_EXPR_DEPTH: usize = 32;
|
pub const MAX_EXPR_DEPTH: usize = 32;
|
||||||
@ -440,6 +439,7 @@ impl fmt::Debug for Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Engine {
|
impl Default for Engine {
|
||||||
|
#[inline(always)]
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::new()
|
Self::new()
|
||||||
}
|
}
|
||||||
@ -606,6 +606,7 @@ pub fn search_scope_only<'s, 'a>(
|
|||||||
|
|
||||||
impl Engine {
|
impl Engine {
|
||||||
/// Create a new `Engine`
|
/// Create a new `Engine`
|
||||||
|
#[inline(always)]
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
// Create the new scripting Engine
|
// Create the new scripting Engine
|
||||||
let mut engine = Self {
|
let mut engine = Self {
|
||||||
@ -661,6 +662,7 @@ impl Engine {
|
|||||||
|
|
||||||
/// Create a new `Engine` with minimal built-in functions.
|
/// Create a new `Engine` with minimal built-in functions.
|
||||||
/// Use the `load_package` method to load additional packages of functions.
|
/// Use the `load_package` method to load additional packages of functions.
|
||||||
|
#[inline(always)]
|
||||||
pub fn new_raw() -> Self {
|
pub fn new_raw() -> Self {
|
||||||
Self {
|
Self {
|
||||||
id: None,
|
id: None,
|
||||||
@ -1777,39 +1779,26 @@ impl Engine {
|
|||||||
|
|
||||||
Stmt::ReturnWithVal(_) => unreachable!(),
|
Stmt::ReturnWithVal(_) => unreachable!(),
|
||||||
|
|
||||||
// Let statement
|
// Let/const statement
|
||||||
Stmt::Let(x) if x.1.is_some() => {
|
Stmt::Let(x) | Stmt::Const(x) => {
|
||||||
let ((var_name, _), expr, _) = x.as_ref();
|
let ((var_name, _), expr, _) = x.as_ref();
|
||||||
let expr = expr.as_ref().unwrap();
|
let entry_type = match stmt {
|
||||||
let val = self
|
Stmt::Let(_) => ScopeEntryType::Normal,
|
||||||
.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?
|
Stmt::Const(_) => ScopeEntryType::Constant,
|
||||||
.flatten();
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let val = if let Some(expr) = expr {
|
||||||
|
self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?
|
||||||
|
.flatten()
|
||||||
|
} else {
|
||||||
|
().into()
|
||||||
|
};
|
||||||
let var_name = unsafe_cast_var_name_to_lifetime(var_name, &state);
|
let var_name = unsafe_cast_var_name_to_lifetime(var_name, &state);
|
||||||
scope.push_dynamic_value(var_name, ScopeEntryType::Normal, val, false);
|
scope.push_dynamic_value(var_name, entry_type, val, false);
|
||||||
Ok(Default::default())
|
Ok(Default::default())
|
||||||
}
|
}
|
||||||
|
|
||||||
Stmt::Let(x) => {
|
|
||||||
let ((var_name, _), _, _) = x.as_ref();
|
|
||||||
let var_name = unsafe_cast_var_name_to_lifetime(var_name, &state);
|
|
||||||
scope.push(var_name, ());
|
|
||||||
Ok(Default::default())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Const statement
|
|
||||||
Stmt::Const(x) if x.1.is_constant() => {
|
|
||||||
let ((var_name, _), expr, _) = x.as_ref();
|
|
||||||
let val = self
|
|
||||||
.eval_expr(scope, mods, state, lib, this_ptr, &expr, level)?
|
|
||||||
.flatten();
|
|
||||||
let var_name = unsafe_cast_var_name_to_lifetime(var_name, &state);
|
|
||||||
scope.push_dynamic_value(var_name, ScopeEntryType::Constant, val, true);
|
|
||||||
Ok(Default::default())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Const expression not constant
|
|
||||||
Stmt::Const(_) => unreachable!(),
|
|
||||||
|
|
||||||
// Import statement
|
// Import statement
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
Stmt::Import(x) => {
|
Stmt::Import(x) => {
|
||||||
@ -2040,6 +2029,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Make a Box<EvalAltResult<ErrorMismatchDataType>>.
|
/// Make a Box<EvalAltResult<ErrorMismatchDataType>>.
|
||||||
|
#[inline(always)]
|
||||||
pub fn make_type_mismatch_err<T>(&self, typ: &str, pos: Position) -> Box<EvalAltResult> {
|
pub fn make_type_mismatch_err<T>(&self, typ: &str, pos: Position) -> Box<EvalAltResult> {
|
||||||
EvalAltResult::ErrorMismatchDataType(
|
EvalAltResult::ErrorMismatchDataType(
|
||||||
typ.into(),
|
typ.into(),
|
||||||
|
12
src/error.rs
12
src/error.rs
@ -60,6 +60,7 @@ impl fmt::Display for LexError {
|
|||||||
|
|
||||||
impl LexError {
|
impl LexError {
|
||||||
/// Convert a `LexError` into a `ParseError`.
|
/// Convert a `LexError` into a `ParseError`.
|
||||||
|
#[inline(always)]
|
||||||
pub fn into_err(&self, pos: Position) -> ParseError {
|
pub fn into_err(&self, pos: Position) -> ParseError {
|
||||||
ParseError(Box::new(self.into()), pos)
|
ParseError(Box::new(self.into()), pos)
|
||||||
}
|
}
|
||||||
@ -99,8 +100,6 @@ pub enum ParseErrorType {
|
|||||||
///
|
///
|
||||||
/// Never appears under the `no_object` feature.
|
/// Never appears under the `no_object` feature.
|
||||||
DuplicatedProperty(String),
|
DuplicatedProperty(String),
|
||||||
/// Invalid expression assigned to constant. Wrapped value is the name of the constant.
|
|
||||||
ForbiddenConstantExpr(String),
|
|
||||||
/// Missing a property name for custom types and maps.
|
/// Missing a property name for custom types and maps.
|
||||||
///
|
///
|
||||||
/// Never appears under the `no_object` feature.
|
/// Never appears under the `no_object` feature.
|
||||||
@ -157,6 +156,7 @@ pub enum ParseErrorType {
|
|||||||
|
|
||||||
impl ParseErrorType {
|
impl ParseErrorType {
|
||||||
/// Make a `ParseError` using the current type and position.
|
/// Make a `ParseError` using the current type and position.
|
||||||
|
#[inline(always)]
|
||||||
pub(crate) fn into_err(self, pos: Position) -> ParseError {
|
pub(crate) fn into_err(self, pos: Position) -> ParseError {
|
||||||
ParseError(Box::new(self), pos)
|
ParseError(Box::new(self), pos)
|
||||||
}
|
}
|
||||||
@ -172,7 +172,6 @@ impl ParseErrorType {
|
|||||||
Self::MalformedInExpr(_) => "Invalid 'in' expression",
|
Self::MalformedInExpr(_) => "Invalid 'in' expression",
|
||||||
Self::MalformedCapture(_) => "Invalid capturing",
|
Self::MalformedCapture(_) => "Invalid capturing",
|
||||||
Self::DuplicatedProperty(_) => "Duplicated property in object map literal",
|
Self::DuplicatedProperty(_) => "Duplicated property in object map literal",
|
||||||
Self::ForbiddenConstantExpr(_) => "Expecting a constant",
|
|
||||||
Self::PropertyExpected => "Expecting name of a property",
|
Self::PropertyExpected => "Expecting name of a property",
|
||||||
Self::VariableExpected => "Expecting name of a variable",
|
Self::VariableExpected => "Expecting name of a variable",
|
||||||
Self::Reserved(_) => "Invalid use of reserved keyword",
|
Self::Reserved(_) => "Invalid use of reserved keyword",
|
||||||
@ -199,9 +198,6 @@ impl fmt::Display for ParseErrorType {
|
|||||||
Self::BadInput(s) | ParseErrorType::MalformedCallExpr(s) => {
|
Self::BadInput(s) | ParseErrorType::MalformedCallExpr(s) => {
|
||||||
f.write_str(if s.is_empty() { self.desc() } else { s })
|
f.write_str(if s.is_empty() { self.desc() } else { s })
|
||||||
}
|
}
|
||||||
Self::ForbiddenConstantExpr(s) => {
|
|
||||||
write!(f, "Expecting a constant to assign to '{}'", s)
|
|
||||||
}
|
|
||||||
Self::UnknownOperator(s) => write!(f, "{}: '{}'", self.desc(), s),
|
Self::UnknownOperator(s) => write!(f, "{}: '{}'", self.desc(), s),
|
||||||
|
|
||||||
Self::MalformedIndexExpr(s) | Self::MalformedInExpr(s) | Self::MalformedCapture(s) => {
|
Self::MalformedIndexExpr(s) | Self::MalformedInExpr(s) | Self::MalformedCapture(s) => {
|
||||||
@ -247,6 +243,7 @@ impl fmt::Display for ParseErrorType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl From<&LexError> for ParseErrorType {
|
impl From<&LexError> for ParseErrorType {
|
||||||
|
#[inline(always)]
|
||||||
fn from(err: &LexError) -> Self {
|
fn from(err: &LexError) -> Self {
|
||||||
match err {
|
match err {
|
||||||
LexError::StringTooLong(max) => {
|
LexError::StringTooLong(max) => {
|
||||||
@ -264,6 +261,7 @@ pub struct ParseError(pub Box<ParseErrorType>, pub Position);
|
|||||||
impl Error for ParseError {}
|
impl Error for ParseError {}
|
||||||
|
|
||||||
impl fmt::Display for ParseError {
|
impl fmt::Display for ParseError {
|
||||||
|
#[inline]
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
fmt::Display::fmt(&self.0, f)?;
|
fmt::Display::fmt(&self.0, f)?;
|
||||||
|
|
||||||
@ -277,12 +275,14 @@ impl fmt::Display for ParseError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl From<ParseErrorType> for Box<EvalAltResult> {
|
impl From<ParseErrorType> for Box<EvalAltResult> {
|
||||||
|
#[inline(always)]
|
||||||
fn from(err: ParseErrorType) -> Self {
|
fn from(err: ParseErrorType) -> Self {
|
||||||
Box::new(EvalAltResult::ErrorParsing(err, Position::none()))
|
Box::new(EvalAltResult::ErrorParsing(err, Position::none()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ParseError> for Box<EvalAltResult> {
|
impl From<ParseError> for Box<EvalAltResult> {
|
||||||
|
#[inline(always)]
|
||||||
fn from(err: ParseError) -> Self {
|
fn from(err: ParseError) -> Self {
|
||||||
Box::new(EvalAltResult::ErrorParsing(*err.0, err.1))
|
Box::new(EvalAltResult::ErrorParsing(*err.0, err.1))
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
|
|
||||||
use crate::any::{Dynamic, Variant};
|
use crate::any::{Dynamic, Variant};
|
||||||
use crate::utils::StaticVec;
|
use crate::StaticVec;
|
||||||
|
|
||||||
/// Trait that represents arguments to a function call.
|
/// Trait that represents arguments to a function call.
|
||||||
/// Any data type that can be converted into a `Vec<Dynamic>` can be used
|
/// Any data type that can be converted into a `Vec<Dynamic>` can be used
|
||||||
@ -19,6 +19,7 @@ macro_rules! impl_args {
|
|||||||
($($p:ident),*) => {
|
($($p:ident),*) => {
|
||||||
impl<$($p: Variant + Clone),*> FuncArgs for ($($p,)*)
|
impl<$($p: Variant + Clone),*> FuncArgs for ($($p,)*)
|
||||||
{
|
{
|
||||||
|
#[inline]
|
||||||
fn into_vec(self) -> StaticVec<Dynamic> {
|
fn into_vec(self) -> StaticVec<Dynamic> {
|
||||||
let ($($p,)*) = self;
|
let ($($p,)*) = self;
|
||||||
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
//! Implement function-calling mechanism for `Engine`.
|
//! Implement function-calling mechanism for `Engine`.
|
||||||
|
|
||||||
use crate::any::Dynamic;
|
use crate::any::Dynamic;
|
||||||
use crate::calc_fn_hash;
|
|
||||||
use crate::engine::{
|
use crate::engine::{
|
||||||
search_imports, search_namespace, search_scope_only, Engine, Imports, State, KEYWORD_DEBUG,
|
search_imports, search_namespace, search_scope_only, Engine, Imports, State, KEYWORD_DEBUG,
|
||||||
KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_FN_PTR_CALL, KEYWORD_FN_PTR_CURRY, KEYWORD_IS_DEF_FN,
|
KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_FN_PTR_CALL, KEYWORD_FN_PTR_CURRY, KEYWORD_IS_DEF_FN,
|
||||||
@ -16,7 +15,7 @@ use crate::result::EvalAltResult;
|
|||||||
use crate::scope::Scope;
|
use crate::scope::Scope;
|
||||||
use crate::stdlib::ops::Deref;
|
use crate::stdlib::ops::Deref;
|
||||||
use crate::token::Position;
|
use crate::token::Position;
|
||||||
use crate::utils::StaticVec;
|
use crate::{calc_fn_hash, StaticVec};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -439,6 +438,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Has a system function an override?
|
// Has a system function an override?
|
||||||
|
#[inline]
|
||||||
pub(crate) fn has_override_by_name_and_arguments(
|
pub(crate) fn has_override_by_name_and_arguments(
|
||||||
&self,
|
&self,
|
||||||
lib: &Module,
|
lib: &Module,
|
||||||
@ -459,6 +459,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Has a system function an override?
|
// Has a system function an override?
|
||||||
|
#[inline(always)]
|
||||||
pub(crate) fn has_override(
|
pub(crate) fn has_override(
|
||||||
&self,
|
&self,
|
||||||
lib: &Module,
|
lib: &Module,
|
||||||
|
@ -96,6 +96,7 @@ macro_rules! def_anonymous_fn {
|
|||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
type Output = Box<dyn Fn($($par),*) -> Result<RET, Box<EvalAltResult>>>;
|
type Output = Box<dyn Fn($($par),*) -> Result<RET, Box<EvalAltResult>>>;
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
fn create_from_ast(self, ast: AST, entry_point: &str) -> Self::Output {
|
fn create_from_ast(self, ast: AST, entry_point: &str) -> Self::Output {
|
||||||
let fn_name = entry_point.to_string();
|
let fn_name = entry_point.to_string();
|
||||||
|
|
||||||
@ -104,6 +105,7 @@ macro_rules! def_anonymous_fn {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
fn create_from_script(self, script: &str, entry_point: &str) -> Result<Self::Output, ParseError> {
|
fn create_from_script(self, script: &str, entry_point: &str) -> Result<Self::Output, ParseError> {
|
||||||
let ast = self.compile(script)?;
|
let ast = self.compile(script)?;
|
||||||
Ok(Func::<($($par,)*), RET>::create_from_ast(self, ast, entry_point))
|
Ok(Func::<($($par,)*), RET>::create_from_ast(self, ast, entry_point))
|
||||||
|
@ -10,7 +10,7 @@ use crate::token::{is_valid_identifier, Position};
|
|||||||
use crate::utils::ImmutableString;
|
use crate::utils::ImmutableString;
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
use crate::{calc_fn_hash, module::FuncReturn, utils::StaticVec};
|
use crate::{calc_fn_hash, module::FuncReturn, StaticVec};
|
||||||
|
|
||||||
use crate::stdlib::{boxed::Box, convert::TryFrom, fmt, string::String, vec::Vec};
|
use crate::stdlib::{boxed::Box, convert::TryFrom, fmt, string::String, vec::Vec};
|
||||||
|
|
||||||
@ -78,22 +78,27 @@ pub struct FnPtr(ImmutableString, Vec<Dynamic>);
|
|||||||
|
|
||||||
impl FnPtr {
|
impl FnPtr {
|
||||||
/// Create a new function pointer.
|
/// Create a new function pointer.
|
||||||
|
#[inline(always)]
|
||||||
pub(crate) fn new_unchecked<S: Into<ImmutableString>>(name: S, curry: Vec<Dynamic>) -> Self {
|
pub(crate) fn new_unchecked<S: Into<ImmutableString>>(name: S, curry: Vec<Dynamic>) -> Self {
|
||||||
Self(name.into(), curry)
|
Self(name.into(), curry)
|
||||||
}
|
}
|
||||||
/// Get the name of the function.
|
/// Get the name of the function.
|
||||||
|
#[inline(always)]
|
||||||
pub fn fn_name(&self) -> &str {
|
pub fn fn_name(&self) -> &str {
|
||||||
self.get_fn_name().as_ref()
|
self.get_fn_name().as_ref()
|
||||||
}
|
}
|
||||||
/// Get the name of the function.
|
/// Get the name of the function.
|
||||||
|
#[inline(always)]
|
||||||
pub(crate) fn get_fn_name(&self) -> &ImmutableString {
|
pub(crate) fn get_fn_name(&self) -> &ImmutableString {
|
||||||
&self.0
|
&self.0
|
||||||
}
|
}
|
||||||
/// Get the underlying data of the function pointer.
|
/// Get the underlying data of the function pointer.
|
||||||
|
#[inline(always)]
|
||||||
pub(crate) fn take_data(self) -> (ImmutableString, Vec<Dynamic>) {
|
pub(crate) fn take_data(self) -> (ImmutableString, Vec<Dynamic>) {
|
||||||
(self.0, self.1)
|
(self.0, self.1)
|
||||||
}
|
}
|
||||||
/// Get the curried arguments.
|
/// Get the curried arguments.
|
||||||
|
#[inline(always)]
|
||||||
pub fn curry(&self) -> &[Dynamic] {
|
pub fn curry(&self) -> &[Dynamic] {
|
||||||
&self.1
|
&self.1
|
||||||
}
|
}
|
||||||
@ -153,6 +158,7 @@ impl FnPtr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for FnPtr {
|
impl fmt::Display for FnPtr {
|
||||||
|
#[inline(always)]
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
write!(f, "Fn({})", self.0)
|
write!(f, "Fn({})", self.0)
|
||||||
}
|
}
|
||||||
@ -161,6 +167,7 @@ impl fmt::Display for FnPtr {
|
|||||||
impl TryFrom<ImmutableString> for FnPtr {
|
impl TryFrom<ImmutableString> for FnPtr {
|
||||||
type Error = Box<EvalAltResult>;
|
type Error = Box<EvalAltResult>;
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
fn try_from(value: ImmutableString) -> Result<Self, Self::Error> {
|
fn try_from(value: ImmutableString) -> Result<Self, Self::Error> {
|
||||||
if is_valid_identifier(value.chars()) {
|
if is_valid_identifier(value.chars()) {
|
||||||
Ok(Self(value, Default::default()))
|
Ok(Self(value, Default::default()))
|
||||||
@ -173,6 +180,7 @@ impl TryFrom<ImmutableString> for FnPtr {
|
|||||||
impl TryFrom<String> for FnPtr {
|
impl TryFrom<String> for FnPtr {
|
||||||
type Error = Box<EvalAltResult>;
|
type Error = Box<EvalAltResult>;
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
fn try_from(value: String) -> Result<Self, Self::Error> {
|
fn try_from(value: String) -> Result<Self, Self::Error> {
|
||||||
let s: ImmutableString = value.into();
|
let s: ImmutableString = value.into();
|
||||||
Self::try_from(s)
|
Self::try_from(s)
|
||||||
@ -182,6 +190,7 @@ impl TryFrom<String> for FnPtr {
|
|||||||
impl TryFrom<&str> for FnPtr {
|
impl TryFrom<&str> for FnPtr {
|
||||||
type Error = Box<EvalAltResult>;
|
type Error = Box<EvalAltResult>;
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
fn try_from(value: &str) -> Result<Self, Self::Error> {
|
fn try_from(value: &str) -> Result<Self, Self::Error> {
|
||||||
let s: ImmutableString = value.into();
|
let s: ImmutableString = value.into();
|
||||||
Self::try_from(s)
|
Self::try_from(s)
|
||||||
@ -399,26 +408,31 @@ impl CallableFunction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Create a new `CallableFunction::Pure`.
|
/// Create a new `CallableFunction::Pure`.
|
||||||
|
#[inline(always)]
|
||||||
pub fn from_pure(func: Box<FnAny>) -> Self {
|
pub fn from_pure(func: Box<FnAny>) -> Self {
|
||||||
Self::Pure(func.into())
|
Self::Pure(func.into())
|
||||||
}
|
}
|
||||||
/// Create a new `CallableFunction::Method`.
|
/// Create a new `CallableFunction::Method`.
|
||||||
|
#[inline(always)]
|
||||||
pub fn from_method(func: Box<FnAny>) -> Self {
|
pub fn from_method(func: Box<FnAny>) -> Self {
|
||||||
Self::Method(func.into())
|
Self::Method(func.into())
|
||||||
}
|
}
|
||||||
/// Create a new `CallableFunction::Plugin`.
|
/// Create a new `CallableFunction::Plugin`.
|
||||||
|
#[inline(always)]
|
||||||
pub fn from_plugin(func: impl PluginFunction + 'static + SendSync) -> Self {
|
pub fn from_plugin(func: impl PluginFunction + 'static + SendSync) -> Self {
|
||||||
Self::Plugin((Box::new(func) as Box<FnPlugin>).into())
|
Self::Plugin((Box::new(func) as Box<FnPlugin>).into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<IteratorFn> for CallableFunction {
|
impl From<IteratorFn> for CallableFunction {
|
||||||
|
#[inline(always)]
|
||||||
fn from(func: IteratorFn) -> Self {
|
fn from(func: IteratorFn) -> Self {
|
||||||
Self::Iterator(func)
|
Self::Iterator(func)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ScriptFnDef> for CallableFunction {
|
impl From<ScriptFnDef> for CallableFunction {
|
||||||
|
#[inline(always)]
|
||||||
fn from(_func: ScriptFnDef) -> Self {
|
fn from(_func: ScriptFnDef) -> Self {
|
||||||
#[cfg(feature = "no_function")]
|
#[cfg(feature = "no_function")]
|
||||||
unreachable!();
|
unreachable!();
|
||||||
@ -429,6 +443,7 @@ impl From<ScriptFnDef> for CallableFunction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl From<Shared<ScriptFnDef>> for CallableFunction {
|
impl From<Shared<ScriptFnDef>> for CallableFunction {
|
||||||
|
#[inline(always)]
|
||||||
fn from(_func: Shared<ScriptFnDef>) -> Self {
|
fn from(_func: Shared<ScriptFnDef>) -> Self {
|
||||||
#[cfg(feature = "no_function")]
|
#[cfg(feature = "no_function")]
|
||||||
unreachable!();
|
unreachable!();
|
||||||
@ -439,12 +454,14 @@ impl From<Shared<ScriptFnDef>> for CallableFunction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<T: PluginFunction + 'static + SendSync> From<T> for CallableFunction {
|
impl<T: PluginFunction + 'static + SendSync> From<T> for CallableFunction {
|
||||||
|
#[inline(always)]
|
||||||
fn from(func: T) -> Self {
|
fn from(func: T) -> Self {
|
||||||
Self::from_plugin(func)
|
Self::from_plugin(func)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Shared<FnPlugin>> for CallableFunction {
|
impl From<Shared<FnPlugin>> for CallableFunction {
|
||||||
|
#[inline(always)]
|
||||||
fn from(func: Shared<FnPlugin>) -> Self {
|
fn from(func: Shared<FnPlugin>) -> Self {
|
||||||
Self::Plugin(func.into())
|
Self::Plugin(func.into())
|
||||||
}
|
}
|
||||||
|
@ -188,6 +188,7 @@ macro_rules! def_register {
|
|||||||
RET: Variant + Clone
|
RET: Variant + Clone
|
||||||
> RegisterFn<FN, ($($mark,)*), RET> for Engine
|
> RegisterFn<FN, ($($mark,)*), RET> for Engine
|
||||||
{
|
{
|
||||||
|
#[inline]
|
||||||
fn register_fn(&mut self, name: &str, f: FN) -> &mut Self {
|
fn register_fn(&mut self, name: &str, f: FN) -> &mut Self {
|
||||||
self.global_module.set_fn(name, FnAccess::Public,
|
self.global_module.set_fn(name, FnAccess::Public,
|
||||||
&[$(map_type_id::<$par>()),*],
|
&[$(map_type_id::<$par>()),*],
|
||||||
@ -202,6 +203,7 @@ macro_rules! def_register {
|
|||||||
FN: Fn($($param),*) -> Result<Dynamic, Box<EvalAltResult>> + SendSync + 'static,
|
FN: Fn($($param),*) -> Result<Dynamic, Box<EvalAltResult>> + SendSync + 'static,
|
||||||
> RegisterResultFn<FN, ($($mark,)*)> for Engine
|
> RegisterResultFn<FN, ($($mark,)*)> for Engine
|
||||||
{
|
{
|
||||||
|
#[inline]
|
||||||
fn register_result_fn(&mut self, name: &str, f: FN) -> &mut Self {
|
fn register_result_fn(&mut self, name: &str, f: FN) -> &mut Self {
|
||||||
self.global_module.set_fn(name, FnAccess::Public,
|
self.global_module.set_fn(name, FnAccess::Public,
|
||||||
&[$(map_type_id::<$par>()),*],
|
&[$(map_type_id::<$par>()),*],
|
||||||
|
33
src/lib.rs
33
src/lib.rs
@ -1,6 +1,6 @@
|
|||||||
//! # Rhai - embedded scripting for Rust
|
//! # Rhai - embedded scripting for Rust
|
||||||
//!
|
//!
|
||||||
//! Rhai is a tiny, simple and very fast embedded scripting language for Rust
|
//! Rhai is a tiny, simple and fast embedded scripting language for Rust
|
||||||
//! that gives you a safe and easy way to add scripting to your applications.
|
//! that gives you a safe and easy way to add scripting to your applications.
|
||||||
//! It provides a familiar syntax based on JavaScript and Rust and a simple Rust interface.
|
//! It provides a familiar syntax based on JavaScript and Rust and a simple Rust interface.
|
||||||
//! Here is a quick example.
|
//! Here is a quick example.
|
||||||
@ -74,7 +74,7 @@ pub mod plugin;
|
|||||||
mod result;
|
mod result;
|
||||||
mod scope;
|
mod scope;
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
mod serde;
|
mod serde_impl;
|
||||||
mod settings;
|
mod settings;
|
||||||
mod stdlib;
|
mod stdlib;
|
||||||
mod syntax;
|
mod syntax;
|
||||||
@ -124,21 +124,14 @@ pub use module::ModuleResolver;
|
|||||||
|
|
||||||
/// Module containing all built-in _module resolvers_ available to Rhai.
|
/// Module containing all built-in _module resolvers_ available to Rhai.
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
pub mod module_resolvers {
|
pub use crate::module::resolvers as module_resolvers;
|
||||||
pub use crate::module::resolvers::*;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// _[SERDE]_ Serialization support for [`serde`](https://crates.io/crates/serde).
|
/// _[SERDE]_ Serialization and deserialization support for [`serde`](https://crates.io/crates/serde).
|
||||||
/// Exported under the `serde` feature.
|
/// Exported under the `serde` feature.
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
pub mod ser {
|
pub mod serde {
|
||||||
pub use crate::serde::ser::to_dynamic;
|
pub use super::serde_impl::de::from_dynamic;
|
||||||
}
|
pub use super::serde_impl::ser::to_dynamic;
|
||||||
/// _[SERDE]_ Deserialization support for [`serde`](https://crates.io/crates/serde).
|
|
||||||
/// Exported under the `serde` feature.
|
|
||||||
#[cfg(feature = "serde")]
|
|
||||||
pub mod de {
|
|
||||||
pub use crate::serde::de::from_dynamic;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_optimize"))]
|
#[cfg(not(feature = "no_optimize"))]
|
||||||
@ -166,6 +159,14 @@ pub use engine::{Imports, Limits, State as EvalState};
|
|||||||
#[deprecated(note = "this type is volatile and may change")]
|
#[deprecated(note = "this type is volatile and may change")]
|
||||||
pub use module::ModuleRef;
|
pub use module::ModuleRef;
|
||||||
|
|
||||||
|
/// _[INTERNALS]_ Alias to [`smallvec::SmallVec<[T; 4]>`](https://crates.io/crates/smallvec),
|
||||||
|
/// which is a specialized `Vec` backed by a small, fixed-size array when there are <= 4 items stored.
|
||||||
|
/// Exported under the `internals` feature only.
|
||||||
|
#[cfg(not(feature = "internals"))]
|
||||||
|
type StaticVec<T> = smallvec::SmallVec<[T; 4]>;
|
||||||
|
|
||||||
|
/// _[INTERNALS]_ Alias to [`smallvec::SmallVec<[T; 4]>`](https://crates.io/crates/smallvec),
|
||||||
|
/// which is a specialized `Vec` backed by a small, fixed-size array when there are <= 4 items stored.
|
||||||
|
/// Exported under the `internals` feature only.
|
||||||
#[cfg(feature = "internals")]
|
#[cfg(feature = "internals")]
|
||||||
#[deprecated(note = "this type is volatile and may change")]
|
pub type StaticVec<T> = smallvec::SmallVec<[T; 4]>;
|
||||||
pub use utils::StaticVec;
|
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
//! Module defining external-loaded modules for Rhai.
|
//! Module defining external-loaded modules for Rhai.
|
||||||
|
|
||||||
use crate::any::{Dynamic, Variant};
|
use crate::any::{Dynamic, Variant};
|
||||||
use crate::calc_fn_hash;
|
|
||||||
use crate::engine::Engine;
|
use crate::engine::Engine;
|
||||||
use crate::fn_native::{CallableFunction, FnCallArgs, IteratorFn, SendSync};
|
use crate::fn_native::{CallableFunction, FnCallArgs, IteratorFn, SendSync};
|
||||||
use crate::fn_register::by_value as cast_arg;
|
use crate::fn_register::by_value as cast_arg;
|
||||||
use crate::parser::FnAccess;
|
use crate::parser::FnAccess;
|
||||||
use crate::result::EvalAltResult;
|
use crate::result::EvalAltResult;
|
||||||
use crate::token::{Position, Token};
|
use crate::token::{Position, Token};
|
||||||
use crate::utils::{ImmutableString, StaticVec, StraightHasherBuilder};
|
use crate::utils::{ImmutableString, StraightHasherBuilder};
|
||||||
|
use crate::{calc_fn_hash, StaticVec};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
use crate::{fn_native::Shared, parser::ScriptFnDef};
|
use crate::{fn_native::Shared, parser::ScriptFnDef};
|
||||||
@ -33,7 +33,7 @@ use crate::stdlib::{
|
|||||||
fmt, format,
|
fmt, format,
|
||||||
iter::empty,
|
iter::empty,
|
||||||
num::NonZeroUsize,
|
num::NonZeroUsize,
|
||||||
ops::{Deref, DerefMut},
|
ops::{Add, AddAssign, Deref, DerefMut},
|
||||||
string::{String, ToString},
|
string::{String, ToString},
|
||||||
vec::Vec,
|
vec::Vec,
|
||||||
};
|
};
|
||||||
@ -103,6 +103,7 @@ impl fmt::Debug for Module {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Clone for Module {
|
impl Clone for Module {
|
||||||
|
#[inline(always)]
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
// Only clone the index at the top level
|
// Only clone the index at the top level
|
||||||
Self {
|
Self {
|
||||||
@ -115,6 +116,7 @@ impl Clone for Module {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl AsRef<Module> for Module {
|
impl AsRef<Module> for Module {
|
||||||
|
#[inline(always)]
|
||||||
fn as_ref(&self) -> &Module {
|
fn as_ref(&self) -> &Module {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@ -132,6 +134,7 @@ impl Module {
|
|||||||
/// module.set_var("answer", 42_i64);
|
/// module.set_var("answer", 42_i64);
|
||||||
/// assert_eq!(module.get_var_value::<i64>("answer").unwrap(), 42);
|
/// assert_eq!(module.get_var_value::<i64>("answer").unwrap(), 42);
|
||||||
/// ```
|
/// ```
|
||||||
|
#[inline(always)]
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Default::default()
|
Default::default()
|
||||||
}
|
}
|
||||||
@ -147,6 +150,7 @@ impl Module {
|
|||||||
/// module.set_var("answer", 42_i64);
|
/// module.set_var("answer", 42_i64);
|
||||||
/// assert_eq!(module.get_var_value::<i64>("answer").unwrap(), 42);
|
/// assert_eq!(module.get_var_value::<i64>("answer").unwrap(), 42);
|
||||||
/// ```
|
/// ```
|
||||||
|
#[inline(always)]
|
||||||
pub fn new_with_capacity(capacity: usize) -> Self {
|
pub fn new_with_capacity(capacity: usize) -> Self {
|
||||||
Self {
|
Self {
|
||||||
functions: HashMap::with_capacity_and_hasher(capacity, StraightHasherBuilder),
|
functions: HashMap::with_capacity_and_hasher(capacity, StraightHasherBuilder),
|
||||||
@ -164,6 +168,7 @@ impl Module {
|
|||||||
/// let module = Module::new();
|
/// let module = Module::new();
|
||||||
/// assert!(module.is_empty());
|
/// assert!(module.is_empty());
|
||||||
/// ```
|
/// ```
|
||||||
|
#[inline(always)]
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.functions.is_empty()
|
self.functions.is_empty()
|
||||||
&& self.all_functions.is_empty()
|
&& self.all_functions.is_empty()
|
||||||
@ -174,6 +179,7 @@ impl Module {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Clone the module, optionally skipping the index.
|
/// Clone the module, optionally skipping the index.
|
||||||
|
#[inline(always)]
|
||||||
fn do_clone(&self, clone_index: bool) -> Self {
|
fn do_clone(&self, clone_index: bool) -> Self {
|
||||||
Self {
|
Self {
|
||||||
modules: if clone_index {
|
modules: if clone_index {
|
||||||
@ -202,6 +208,7 @@ impl Module {
|
|||||||
/// module.set_var("answer", 42_i64);
|
/// module.set_var("answer", 42_i64);
|
||||||
/// assert!(module.contains_var("answer"));
|
/// assert!(module.contains_var("answer"));
|
||||||
/// ```
|
/// ```
|
||||||
|
#[inline(always)]
|
||||||
pub fn contains_var(&self, name: &str) -> bool {
|
pub fn contains_var(&self, name: &str) -> bool {
|
||||||
self.variables.contains_key(name)
|
self.variables.contains_key(name)
|
||||||
}
|
}
|
||||||
@ -217,6 +224,7 @@ impl Module {
|
|||||||
/// module.set_var("answer", 42_i64);
|
/// module.set_var("answer", 42_i64);
|
||||||
/// assert_eq!(module.get_var_value::<i64>("answer").unwrap(), 42);
|
/// assert_eq!(module.get_var_value::<i64>("answer").unwrap(), 42);
|
||||||
/// ```
|
/// ```
|
||||||
|
#[inline(always)]
|
||||||
pub fn get_var_value<T: Variant + Clone>(&self, name: &str) -> Option<T> {
|
pub fn get_var_value<T: Variant + Clone>(&self, name: &str) -> Option<T> {
|
||||||
self.get_var(name).and_then(Dynamic::try_cast::<T>)
|
self.get_var(name).and_then(Dynamic::try_cast::<T>)
|
||||||
}
|
}
|
||||||
@ -232,6 +240,7 @@ impl Module {
|
|||||||
/// module.set_var("answer", 42_i64);
|
/// module.set_var("answer", 42_i64);
|
||||||
/// assert_eq!(module.get_var("answer").unwrap().cast::<i64>(), 42);
|
/// assert_eq!(module.get_var("answer").unwrap().cast::<i64>(), 42);
|
||||||
/// ```
|
/// ```
|
||||||
|
#[inline(always)]
|
||||||
pub fn get_var(&self, name: &str) -> Option<Dynamic> {
|
pub fn get_var(&self, name: &str) -> Option<Dynamic> {
|
||||||
self.variables.get(name).cloned()
|
self.variables.get(name).cloned()
|
||||||
}
|
}
|
||||||
@ -249,6 +258,7 @@ impl Module {
|
|||||||
/// module.set_var("answer", 42_i64);
|
/// module.set_var("answer", 42_i64);
|
||||||
/// assert_eq!(module.get_var_value::<i64>("answer").unwrap(), 42);
|
/// assert_eq!(module.get_var_value::<i64>("answer").unwrap(), 42);
|
||||||
/// ```
|
/// ```
|
||||||
|
#[inline(always)]
|
||||||
pub fn set_var(&mut self, name: impl Into<String>, value: impl Variant + Clone) -> &mut Self {
|
pub fn set_var(&mut self, name: impl Into<String>, value: impl Variant + Clone) -> &mut Self {
|
||||||
self.variables.insert(name.into(), Dynamic::from(value));
|
self.variables.insert(name.into(), Dynamic::from(value));
|
||||||
self.indexed = false;
|
self.indexed = false;
|
||||||
@ -259,6 +269,7 @@ impl Module {
|
|||||||
/// Name and Position in `EvalAltResult` are None and must be set afterwards.
|
/// Name and Position in `EvalAltResult` are None and must be set afterwards.
|
||||||
///
|
///
|
||||||
/// The `u64` hash is calculated by the function `crate::calc_fn_hash`.
|
/// The `u64` hash is calculated by the function `crate::calc_fn_hash`.
|
||||||
|
#[inline(always)]
|
||||||
pub(crate) fn get_qualified_var_mut(
|
pub(crate) fn get_qualified_var_mut(
|
||||||
&mut self,
|
&mut self,
|
||||||
hash_var: u64,
|
hash_var: u64,
|
||||||
@ -276,6 +287,7 @@ impl Module {
|
|||||||
///
|
///
|
||||||
/// If there is an existing function of the same name and number of arguments, it is replaced.
|
/// If there is an existing function of the same name and number of arguments, it is replaced.
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
#[inline]
|
||||||
pub(crate) fn set_script_fn(&mut self, fn_def: ScriptFnDef) -> u64 {
|
pub(crate) fn set_script_fn(&mut self, fn_def: ScriptFnDef) -> u64 {
|
||||||
// None + function name + number of arguments.
|
// None + function name + number of arguments.
|
||||||
let num_params = fn_def.params.len();
|
let num_params = fn_def.params.len();
|
||||||
@ -296,6 +308,7 @@ impl Module {
|
|||||||
|
|
||||||
/// Get a script-defined function in the module based on name and number of parameters.
|
/// Get a script-defined function in the module based on name and number of parameters.
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
#[inline(always)]
|
||||||
pub fn get_script_fn(
|
pub fn get_script_fn(
|
||||||
&self,
|
&self,
|
||||||
name: &str,
|
name: &str,
|
||||||
@ -324,6 +337,7 @@ impl Module {
|
|||||||
/// module.set_sub_module("question", sub_module);
|
/// module.set_sub_module("question", sub_module);
|
||||||
/// assert!(module.contains_sub_module("question"));
|
/// assert!(module.contains_sub_module("question"));
|
||||||
/// ```
|
/// ```
|
||||||
|
#[inline(always)]
|
||||||
pub fn contains_sub_module(&self, name: &str) -> bool {
|
pub fn contains_sub_module(&self, name: &str) -> bool {
|
||||||
self.modules.contains_key(name)
|
self.modules.contains_key(name)
|
||||||
}
|
}
|
||||||
@ -340,6 +354,7 @@ impl Module {
|
|||||||
/// module.set_sub_module("question", sub_module);
|
/// module.set_sub_module("question", sub_module);
|
||||||
/// assert!(module.get_sub_module("question").is_some());
|
/// assert!(module.get_sub_module("question").is_some());
|
||||||
/// ```
|
/// ```
|
||||||
|
#[inline(always)]
|
||||||
pub fn get_sub_module(&self, name: &str) -> Option<&Module> {
|
pub fn get_sub_module(&self, name: &str) -> Option<&Module> {
|
||||||
self.modules.get(name)
|
self.modules.get(name)
|
||||||
}
|
}
|
||||||
@ -356,6 +371,7 @@ impl Module {
|
|||||||
/// module.set_sub_module("question", sub_module);
|
/// module.set_sub_module("question", sub_module);
|
||||||
/// assert!(module.get_sub_module_mut("question").is_some());
|
/// assert!(module.get_sub_module_mut("question").is_some());
|
||||||
/// ```
|
/// ```
|
||||||
|
#[inline(always)]
|
||||||
pub fn get_sub_module_mut(&mut self, name: &str) -> Option<&mut Module> {
|
pub fn get_sub_module_mut(&mut self, name: &str) -> Option<&mut Module> {
|
||||||
self.modules.get_mut(name)
|
self.modules.get_mut(name)
|
||||||
}
|
}
|
||||||
@ -374,6 +390,7 @@ impl Module {
|
|||||||
/// module.set_sub_module("question", sub_module);
|
/// module.set_sub_module("question", sub_module);
|
||||||
/// assert!(module.get_sub_module("question").is_some());
|
/// assert!(module.get_sub_module("question").is_some());
|
||||||
/// ```
|
/// ```
|
||||||
|
#[inline(always)]
|
||||||
pub fn set_sub_module(&mut self, name: impl Into<String>, sub_module: Module) -> &mut Self {
|
pub fn set_sub_module(&mut self, name: impl Into<String>, sub_module: Module) -> &mut Self {
|
||||||
self.modules.insert(name.into(), sub_module.into());
|
self.modules.insert(name.into(), sub_module.into());
|
||||||
self.indexed = false;
|
self.indexed = false;
|
||||||
@ -394,6 +411,7 @@ impl Module {
|
|||||||
/// let hash = module.set_fn_0("calc", || Ok(42_i64));
|
/// let hash = module.set_fn_0("calc", || Ok(42_i64));
|
||||||
/// assert!(module.contains_fn(hash, true));
|
/// assert!(module.contains_fn(hash, true));
|
||||||
/// ```
|
/// ```
|
||||||
|
#[inline]
|
||||||
pub fn contains_fn(&self, hash_fn: u64, public_only: bool) -> bool {
|
pub fn contains_fn(&self, hash_fn: u64, public_only: bool) -> bool {
|
||||||
if hash_fn == 0 {
|
if hash_fn == 0 {
|
||||||
false
|
false
|
||||||
@ -513,6 +531,7 @@ impl Module {
|
|||||||
///
|
///
|
||||||
/// assert!(module.contains_fn(hash, true));
|
/// assert!(module.contains_fn(hash, true));
|
||||||
/// ```
|
/// ```
|
||||||
|
#[inline]
|
||||||
pub fn set_raw_fn<T: Variant + Clone>(
|
pub fn set_raw_fn<T: Variant + Clone>(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: impl Into<String>,
|
name: impl Into<String>,
|
||||||
@ -534,6 +553,7 @@ impl Module {
|
|||||||
/// are not determined), but the implementation is in Rust.
|
/// are not determined), but the implementation is in Rust.
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
|
#[inline]
|
||||||
pub(crate) fn set_raw_fn_as_scripted(
|
pub(crate) fn set_raw_fn_as_scripted(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: impl Into<String>,
|
name: impl Into<String>,
|
||||||
@ -571,6 +591,7 @@ impl Module {
|
|||||||
/// let hash = module.set_fn_0("calc", || Ok(42_i64));
|
/// let hash = module.set_fn_0("calc", || Ok(42_i64));
|
||||||
/// assert!(module.contains_fn(hash, true));
|
/// assert!(module.contains_fn(hash, true));
|
||||||
/// ```
|
/// ```
|
||||||
|
#[inline]
|
||||||
pub fn set_fn_0<T: Variant + Clone>(
|
pub fn set_fn_0<T: Variant + Clone>(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: impl Into<String>,
|
name: impl Into<String>,
|
||||||
@ -599,6 +620,7 @@ impl Module {
|
|||||||
/// let hash = module.set_fn_1("calc", |x: i64| Ok(x + 1));
|
/// let hash = module.set_fn_1("calc", |x: i64| Ok(x + 1));
|
||||||
/// assert!(module.contains_fn(hash, true));
|
/// assert!(module.contains_fn(hash, true));
|
||||||
/// ```
|
/// ```
|
||||||
|
#[inline]
|
||||||
pub fn set_fn_1<A: Variant + Clone, T: Variant + Clone>(
|
pub fn set_fn_1<A: Variant + Clone, T: Variant + Clone>(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: impl Into<String>,
|
name: impl Into<String>,
|
||||||
@ -629,6 +651,7 @@ impl Module {
|
|||||||
/// let hash = module.set_fn_1_mut("calc", |x: &mut i64| { *x += 1; Ok(*x) });
|
/// let hash = module.set_fn_1_mut("calc", |x: &mut i64| { *x += 1; Ok(*x) });
|
||||||
/// assert!(module.contains_fn(hash, true));
|
/// assert!(module.contains_fn(hash, true));
|
||||||
/// ```
|
/// ```
|
||||||
|
#[inline]
|
||||||
pub fn set_fn_1_mut<A: Variant + Clone, T: Variant + Clone>(
|
pub fn set_fn_1_mut<A: Variant + Clone, T: Variant + Clone>(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: impl Into<String>,
|
name: impl Into<String>,
|
||||||
@ -660,6 +683,7 @@ impl Module {
|
|||||||
/// assert!(module.contains_fn(hash, true));
|
/// assert!(module.contains_fn(hash, true));
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
#[inline]
|
||||||
pub fn set_getter_fn<A: Variant + Clone, T: Variant + Clone>(
|
pub fn set_getter_fn<A: Variant + Clone, T: Variant + Clone>(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: impl Into<String>,
|
name: impl Into<String>,
|
||||||
@ -683,6 +707,7 @@ impl Module {
|
|||||||
/// });
|
/// });
|
||||||
/// assert!(module.contains_fn(hash, true));
|
/// assert!(module.contains_fn(hash, true));
|
||||||
/// ```
|
/// ```
|
||||||
|
#[inline]
|
||||||
pub fn set_fn_2<A: Variant + Clone, B: Variant + Clone, T: Variant + Clone>(
|
pub fn set_fn_2<A: Variant + Clone, B: Variant + Clone, T: Variant + Clone>(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: impl Into<String>,
|
name: impl Into<String>,
|
||||||
@ -719,6 +744,7 @@ impl Module {
|
|||||||
/// });
|
/// });
|
||||||
/// assert!(module.contains_fn(hash, true));
|
/// assert!(module.contains_fn(hash, true));
|
||||||
/// ```
|
/// ```
|
||||||
|
#[inline]
|
||||||
pub fn set_fn_2_mut<A: Variant + Clone, B: Variant + Clone, T: Variant + Clone>(
|
pub fn set_fn_2_mut<A: Variant + Clone, B: Variant + Clone, T: Variant + Clone>(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: impl Into<String>,
|
name: impl Into<String>,
|
||||||
@ -757,6 +783,7 @@ impl Module {
|
|||||||
/// assert!(module.contains_fn(hash, true));
|
/// assert!(module.contains_fn(hash, true));
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
#[inline]
|
||||||
pub fn set_setter_fn<A: Variant + Clone, B: Variant + Clone>(
|
pub fn set_setter_fn<A: Variant + Clone, B: Variant + Clone>(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: impl Into<String>,
|
name: impl Into<String>,
|
||||||
@ -787,6 +814,7 @@ impl Module {
|
|||||||
/// assert!(module.contains_fn(hash, true));
|
/// assert!(module.contains_fn(hash, true));
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
#[inline]
|
||||||
pub fn set_indexer_get_fn<A: Variant + Clone, B: Variant + Clone, T: Variant + Clone>(
|
pub fn set_indexer_get_fn<A: Variant + Clone, B: Variant + Clone, T: Variant + Clone>(
|
||||||
&mut self,
|
&mut self,
|
||||||
func: impl Fn(&mut A, B) -> FuncReturn<T> + SendSync + 'static,
|
func: impl Fn(&mut A, B) -> FuncReturn<T> + SendSync + 'static,
|
||||||
@ -823,6 +851,7 @@ impl Module {
|
|||||||
/// });
|
/// });
|
||||||
/// assert!(module.contains_fn(hash, true));
|
/// assert!(module.contains_fn(hash, true));
|
||||||
/// ```
|
/// ```
|
||||||
|
#[inline]
|
||||||
pub fn set_fn_3<
|
pub fn set_fn_3<
|
||||||
A: Variant + Clone,
|
A: Variant + Clone,
|
||||||
B: Variant + Clone,
|
B: Variant + Clone,
|
||||||
@ -865,6 +894,7 @@ impl Module {
|
|||||||
/// });
|
/// });
|
||||||
/// assert!(module.contains_fn(hash, true));
|
/// assert!(module.contains_fn(hash, true));
|
||||||
/// ```
|
/// ```
|
||||||
|
#[inline]
|
||||||
pub fn set_fn_3_mut<
|
pub fn set_fn_3_mut<
|
||||||
A: Variant + Clone,
|
A: Variant + Clone,
|
||||||
B: Variant + Clone,
|
B: Variant + Clone,
|
||||||
@ -914,6 +944,7 @@ impl Module {
|
|||||||
/// assert!(module.contains_fn(hash, true));
|
/// assert!(module.contains_fn(hash, true));
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
#[inline]
|
||||||
pub fn set_indexer_set_fn<A: Variant + Clone, B: Variant + Clone, C: Variant + Clone>(
|
pub fn set_indexer_set_fn<A: Variant + Clone, B: Variant + Clone, C: Variant + Clone>(
|
||||||
&mut self,
|
&mut self,
|
||||||
func: impl Fn(&mut A, B, C) -> FuncReturn<()> + SendSync + 'static,
|
func: impl Fn(&mut A, B, C) -> FuncReturn<()> + SendSync + 'static,
|
||||||
@ -977,6 +1008,7 @@ impl Module {
|
|||||||
/// assert!(module.contains_fn(hash_set, true));
|
/// assert!(module.contains_fn(hash_set, true));
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
#[inline]
|
||||||
pub fn set_indexer_get_set_fn<A: Variant + Clone, B: Variant + Clone, T: Variant + Clone>(
|
pub fn set_indexer_get_set_fn<A: Variant + Clone, B: Variant + Clone, T: Variant + Clone>(
|
||||||
&mut self,
|
&mut self,
|
||||||
getter: impl Fn(&mut A, B) -> FuncReturn<T> + SendSync + 'static,
|
getter: impl Fn(&mut A, B) -> FuncReturn<T> + SendSync + 'static,
|
||||||
@ -1003,6 +1035,7 @@ impl Module {
|
|||||||
/// });
|
/// });
|
||||||
/// assert!(module.contains_fn(hash, true));
|
/// assert!(module.contains_fn(hash, true));
|
||||||
/// ```
|
/// ```
|
||||||
|
#[inline]
|
||||||
pub fn set_fn_4<
|
pub fn set_fn_4<
|
||||||
A: Variant + Clone,
|
A: Variant + Clone,
|
||||||
B: Variant + Clone,
|
B: Variant + Clone,
|
||||||
@ -1052,6 +1085,7 @@ impl Module {
|
|||||||
/// });
|
/// });
|
||||||
/// assert!(module.contains_fn(hash, true));
|
/// assert!(module.contains_fn(hash, true));
|
||||||
/// ```
|
/// ```
|
||||||
|
#[inline]
|
||||||
pub fn set_fn_4_mut<
|
pub fn set_fn_4_mut<
|
||||||
A: Variant + Clone,
|
A: Variant + Clone,
|
||||||
B: Variant + Clone,
|
B: Variant + Clone,
|
||||||
@ -1089,6 +1123,7 @@ impl Module {
|
|||||||
///
|
///
|
||||||
/// The `u64` hash is calculated by the function `crate::calc_fn_hash`.
|
/// The `u64` hash is calculated by the function `crate::calc_fn_hash`.
|
||||||
/// It is also returned by the `set_fn_XXX` calls.
|
/// It is also returned by the `set_fn_XXX` calls.
|
||||||
|
#[inline(always)]
|
||||||
pub(crate) fn get_fn(&self, hash_fn: u64, public_only: bool) -> Option<&CallableFunction> {
|
pub(crate) fn get_fn(&self, hash_fn: u64, public_only: bool) -> Option<&CallableFunction> {
|
||||||
if hash_fn == 0 {
|
if hash_fn == 0 {
|
||||||
None
|
None
|
||||||
@ -1108,12 +1143,14 @@ impl Module {
|
|||||||
///
|
///
|
||||||
/// The `u64` hash is calculated by the function `crate::calc_fn_hash` and must match
|
/// The `u64` hash is calculated by the function `crate::calc_fn_hash` and must match
|
||||||
/// the hash calculated by `index_all_sub_modules`.
|
/// the hash calculated by `index_all_sub_modules`.
|
||||||
|
#[inline(always)]
|
||||||
pub(crate) fn get_qualified_fn(&self, hash_qualified_fn: u64) -> Option<&CallableFunction> {
|
pub(crate) fn get_qualified_fn(&self, hash_qualified_fn: u64) -> Option<&CallableFunction> {
|
||||||
self.all_functions.get(&hash_qualified_fn)
|
self.all_functions.get(&hash_qualified_fn)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Combine another module into this module.
|
/// Combine another module into this module.
|
||||||
/// The other module is consumed to merge into this module.
|
/// The other module is consumed to merge into this module.
|
||||||
|
#[inline]
|
||||||
pub fn combine(&mut self, other: Self) -> &mut Self {
|
pub fn combine(&mut self, other: Self) -> &mut Self {
|
||||||
if !other.modules.is_empty() {
|
if !other.modules.is_empty() {
|
||||||
self.modules.extend(other.modules.into_iter());
|
self.modules.extend(other.modules.into_iter());
|
||||||
@ -1136,6 +1173,7 @@ impl Module {
|
|||||||
/// Combine another module into this module.
|
/// Combine another module into this module.
|
||||||
/// The other module is consumed to merge into this module.
|
/// The other module is consumed to merge into this module.
|
||||||
/// Sub-modules are flattened onto the root module, with higher level overriding lower level.
|
/// Sub-modules are flattened onto the root module, with higher level overriding lower level.
|
||||||
|
#[inline]
|
||||||
pub fn combine_flatten(&mut self, other: Self) -> &mut Self {
|
pub fn combine_flatten(&mut self, other: Self) -> &mut Self {
|
||||||
if !other.modules.is_empty() {
|
if !other.modules.is_empty() {
|
||||||
other.modules.into_iter().for_each(|(_, m)| {
|
other.modules.into_iter().for_each(|(_, m)| {
|
||||||
@ -1158,6 +1196,7 @@ impl Module {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Merge another module into this module.
|
/// Merge another module into this module.
|
||||||
|
#[inline(always)]
|
||||||
pub fn merge(&mut self, other: &Self) -> &mut Self {
|
pub fn merge(&mut self, other: &Self) -> &mut Self {
|
||||||
self.merge_filtered(other, &mut |_, _, _| true)
|
self.merge_filtered(other, &mut |_, _, _| true)
|
||||||
}
|
}
|
||||||
@ -1213,6 +1252,7 @@ impl Module {
|
|||||||
|
|
||||||
/// Filter out the functions, retaining only some based on a filter predicate.
|
/// Filter out the functions, retaining only some based on a filter predicate.
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
#[inline]
|
||||||
pub(crate) fn retain_functions(
|
pub(crate) fn retain_functions(
|
||||||
&mut self,
|
&mut self,
|
||||||
mut filter: impl FnMut(FnAccess, &str, usize) -> bool,
|
mut filter: impl FnMut(FnAccess, &str, usize) -> bool,
|
||||||
@ -1229,6 +1269,7 @@ impl Module {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get the number of variables, functions and type iterators in the module.
|
/// Get the number of variables, functions and type iterators in the module.
|
||||||
|
#[inline(always)]
|
||||||
pub fn count(&self) -> (usize, usize, usize) {
|
pub fn count(&self) -> (usize, usize, usize) {
|
||||||
(
|
(
|
||||||
self.variables.len(),
|
self.variables.len(),
|
||||||
@ -1238,11 +1279,14 @@ impl Module {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get an iterator to the variables in the module.
|
/// Get an iterator to the variables in the module.
|
||||||
|
#[inline(always)]
|
||||||
pub fn iter_var(&self) -> impl Iterator<Item = (&String, &Dynamic)> {
|
pub fn iter_var(&self) -> impl Iterator<Item = (&String, &Dynamic)> {
|
||||||
self.variables.iter()
|
self.variables.iter()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get an iterator to the functions in the module.
|
/// Get an iterator to the functions in the module.
|
||||||
|
#[cfg(not(feature = "no_optimize"))]
|
||||||
|
#[inline(always)]
|
||||||
pub(crate) fn iter_fn(&self) -> impl Iterator<Item = &FuncInfo> {
|
pub(crate) fn iter_fn(&self) -> impl Iterator<Item = &FuncInfo> {
|
||||||
self.functions.values()
|
self.functions.values()
|
||||||
}
|
}
|
||||||
@ -1255,6 +1299,7 @@ impl Module {
|
|||||||
/// 3) Number of parameters.
|
/// 3) Number of parameters.
|
||||||
/// 4) Shared reference to function definition `ScriptFnDef`.
|
/// 4) Shared reference to function definition `ScriptFnDef`.
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
#[inline(always)]
|
||||||
pub(crate) fn iter_script_fn<'a>(
|
pub(crate) fn iter_script_fn<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
) -> impl Iterator<Item = (FnAccess, &str, usize, Shared<ScriptFnDef>)> + 'a {
|
) -> impl Iterator<Item = (FnAccess, &str, usize, Shared<ScriptFnDef>)> + 'a {
|
||||||
@ -1277,6 +1322,7 @@ impl Module {
|
|||||||
/// 3) Number of parameters.
|
/// 3) Number of parameters.
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
#[cfg(not(feature = "internals"))]
|
#[cfg(not(feature = "internals"))]
|
||||||
|
#[inline(always)]
|
||||||
pub fn iter_script_fn_info(&self) -> impl Iterator<Item = (FnAccess, &str, usize)> {
|
pub fn iter_script_fn_info(&self) -> impl Iterator<Item = (FnAccess, &str, usize)> {
|
||||||
self.functions
|
self.functions
|
||||||
.values()
|
.values()
|
||||||
@ -1546,6 +1592,31 @@ impl From<StaticVec<(String, Position)>> for ModuleRef {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<M: AsRef<Module>> Add<M> for &Module {
|
||||||
|
type Output = Module;
|
||||||
|
|
||||||
|
fn add(self, rhs: M) -> Self::Output {
|
||||||
|
let mut module = self.clone();
|
||||||
|
module.merge(rhs.as_ref());
|
||||||
|
module
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<M: AsRef<Module>> Add<M> for Module {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn add(mut self, rhs: M) -> Self::Output {
|
||||||
|
self.merge(rhs.as_ref());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<M: Into<Module>> AddAssign<M> for Module {
|
||||||
|
fn add_assign(&mut self, rhs: M) {
|
||||||
|
self.combine(rhs.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ModuleRef {
|
impl ModuleRef {
|
||||||
pub(crate) fn index(&self) -> Option<NonZeroUsize> {
|
pub(crate) fn index(&self) -> Option<NonZeroUsize> {
|
||||||
self.1
|
self.1
|
||||||
|
@ -3,7 +3,7 @@ use crate::module::{Module, ModuleResolver};
|
|||||||
use crate::result::EvalAltResult;
|
use crate::result::EvalAltResult;
|
||||||
use crate::token::Position;
|
use crate::token::Position;
|
||||||
|
|
||||||
use crate::stdlib::{boxed::Box, vec::Vec};
|
use crate::stdlib::{boxed::Box, ops::AddAssign, vec::Vec};
|
||||||
|
|
||||||
/// Module resolution service that holds a collection of module resolves,
|
/// Module resolution service that holds a collection of module resolves,
|
||||||
/// to be searched in sequential order.
|
/// to be searched in sequential order.
|
||||||
@ -42,24 +42,48 @@ impl ModuleResolversCollection {
|
|||||||
/// let mut engine = Engine::new();
|
/// let mut engine = Engine::new();
|
||||||
/// engine.set_module_resolver(Some(collection));
|
/// engine.set_module_resolver(Some(collection));
|
||||||
/// ```
|
/// ```
|
||||||
|
#[inline(always)]
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Default::default()
|
Default::default()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl ModuleResolversCollection {
|
|
||||||
/// Add a module keyed by its path.
|
/// Add a module keyed by its path.
|
||||||
|
#[inline(always)]
|
||||||
pub fn push(&mut self, resolver: impl ModuleResolver + 'static) {
|
pub fn push(&mut self, resolver: impl ModuleResolver + 'static) {
|
||||||
self.0.push(Box::new(resolver));
|
self.0.push(Box::new(resolver));
|
||||||
}
|
}
|
||||||
/// Get an iterator of all the module resolvers.
|
/// Get an iterator of all the module resolvers.
|
||||||
|
#[inline(always)]
|
||||||
pub fn iter(&self) -> impl Iterator<Item = &dyn ModuleResolver> {
|
pub fn iter(&self) -> impl Iterator<Item = &dyn ModuleResolver> {
|
||||||
self.0.iter().map(|v| v.as_ref())
|
self.0.iter().map(|v| v.as_ref())
|
||||||
}
|
}
|
||||||
|
/// Get a mutable iterator of all the modules.
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn into_iter(self) -> impl Iterator<Item = Box<dyn ModuleResolver>> {
|
||||||
|
self.0.into_iter()
|
||||||
|
}
|
||||||
/// Remove all module resolvers.
|
/// Remove all module resolvers.
|
||||||
|
#[inline(always)]
|
||||||
pub fn clear(&mut self) {
|
pub fn clear(&mut self) {
|
||||||
self.0.clear();
|
self.0.clear();
|
||||||
}
|
}
|
||||||
|
/// Is this `ModuleResolversCollection` empty?
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.0.is_empty()
|
||||||
|
}
|
||||||
|
/// Get the number of module resolvers in this `ModuleResolversCollection`.
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.0.len()
|
||||||
|
}
|
||||||
|
/// Add another `ModuleResolversCollection` to the end of this collection.
|
||||||
|
/// The other `ModuleResolversCollection` is consumed.
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn append(&mut self, other: Self) {
|
||||||
|
if !other.is_empty() {
|
||||||
|
self.0.extend(other.0.into_iter());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ModuleResolver for ModuleResolversCollection {
|
impl ModuleResolver for ModuleResolversCollection {
|
||||||
@ -83,3 +107,10 @@ impl ModuleResolver for ModuleResolversCollection {
|
|||||||
EvalAltResult::ErrorModuleNotFound(path.into(), pos).into()
|
EvalAltResult::ErrorModuleNotFound(path.into(), pos).into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<M: ModuleResolver + 'static> AddAssign<M> for ModuleResolversCollection {
|
||||||
|
#[inline(always)]
|
||||||
|
fn add_assign(&mut self, rhs: M) {
|
||||||
|
self.push(rhs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -48,6 +48,7 @@ pub struct FileModuleResolver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Default for FileModuleResolver {
|
impl Default for FileModuleResolver {
|
||||||
|
#[inline(always)]
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::new_with_path(PathBuf::default())
|
Self::new_with_path(PathBuf::default())
|
||||||
}
|
}
|
||||||
@ -69,6 +70,7 @@ impl FileModuleResolver {
|
|||||||
/// let mut engine = Engine::new();
|
/// let mut engine = Engine::new();
|
||||||
/// engine.set_module_resolver(Some(resolver));
|
/// engine.set_module_resolver(Some(resolver));
|
||||||
/// ```
|
/// ```
|
||||||
|
#[inline(always)]
|
||||||
pub fn new_with_path<P: Into<PathBuf>>(path: P) -> Self {
|
pub fn new_with_path<P: Into<PathBuf>>(path: P) -> Self {
|
||||||
Self::new_with_path_and_extension(path, "rhai")
|
Self::new_with_path_and_extension(path, "rhai")
|
||||||
}
|
}
|
||||||
@ -90,6 +92,7 @@ impl FileModuleResolver {
|
|||||||
/// let mut engine = Engine::new();
|
/// let mut engine = Engine::new();
|
||||||
/// engine.set_module_resolver(Some(resolver));
|
/// engine.set_module_resolver(Some(resolver));
|
||||||
/// ```
|
/// ```
|
||||||
|
#[inline(always)]
|
||||||
pub fn new_with_path_and_extension<P: Into<PathBuf>, E: Into<String>>(
|
pub fn new_with_path_and_extension<P: Into<PathBuf>, E: Into<String>>(
|
||||||
path: P,
|
path: P,
|
||||||
extension: E,
|
extension: E,
|
||||||
@ -116,11 +119,13 @@ impl FileModuleResolver {
|
|||||||
/// let mut engine = Engine::new();
|
/// let mut engine = Engine::new();
|
||||||
/// engine.set_module_resolver(Some(resolver));
|
/// engine.set_module_resolver(Some(resolver));
|
||||||
/// ```
|
/// ```
|
||||||
|
#[inline(always)]
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Default::default()
|
Default::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a `Module` from a file path.
|
/// Create a `Module` from a file path.
|
||||||
|
#[inline(always)]
|
||||||
pub fn create_module<P: Into<PathBuf>>(
|
pub fn create_module<P: Into<PathBuf>>(
|
||||||
&self,
|
&self,
|
||||||
engine: &Engine,
|
engine: &Engine,
|
||||||
|
@ -3,7 +3,7 @@ use crate::module::{Module, ModuleResolver};
|
|||||||
use crate::result::EvalAltResult;
|
use crate::result::EvalAltResult;
|
||||||
use crate::token::Position;
|
use crate::token::Position;
|
||||||
|
|
||||||
use crate::stdlib::{boxed::Box, collections::HashMap, string::String};
|
use crate::stdlib::{boxed::Box, collections::HashMap, ops::AddAssign, string::String};
|
||||||
|
|
||||||
/// Module resolution service that serves modules added into it.
|
/// Module resolution service that serves modules added into it.
|
||||||
///
|
///
|
||||||
@ -42,51 +42,82 @@ impl StaticModuleResolver {
|
|||||||
/// let mut engine = Engine::new();
|
/// let mut engine = Engine::new();
|
||||||
/// engine.set_module_resolver(Some(resolver));
|
/// engine.set_module_resolver(Some(resolver));
|
||||||
/// ```
|
/// ```
|
||||||
|
#[inline(always)]
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Default::default()
|
Default::default()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl StaticModuleResolver {
|
|
||||||
/// Add a module keyed by its path.
|
/// Add a module keyed by its path.
|
||||||
|
#[inline(always)]
|
||||||
pub fn insert<S: Into<String>>(&mut self, path: S, module: Module) {
|
pub fn insert<S: Into<String>>(&mut self, path: S, module: Module) {
|
||||||
self.0.insert(path.into(), module);
|
self.0.insert(path.into(), module);
|
||||||
}
|
}
|
||||||
/// Remove a module given its path.
|
/// Remove a module given its path.
|
||||||
|
#[inline(always)]
|
||||||
pub fn remove(&mut self, path: &str) -> Option<Module> {
|
pub fn remove(&mut self, path: &str) -> Option<Module> {
|
||||||
self.0.remove(path)
|
self.0.remove(path)
|
||||||
}
|
}
|
||||||
/// Does the path exist?
|
/// Does the path exist?
|
||||||
|
#[inline(always)]
|
||||||
pub fn contains_path(&self, path: &str) -> bool {
|
pub fn contains_path(&self, path: &str) -> bool {
|
||||||
self.0.contains_key(path)
|
self.0.contains_key(path)
|
||||||
}
|
}
|
||||||
/// Get an iterator of all the modules.
|
/// Get an iterator of all the modules.
|
||||||
|
#[inline(always)]
|
||||||
pub fn iter(&self) -> impl Iterator<Item = (&str, &Module)> {
|
pub fn iter(&self) -> impl Iterator<Item = (&str, &Module)> {
|
||||||
self.0.iter().map(|(k, v)| (k.as_str(), v))
|
self.0.iter().map(|(k, v)| (k.as_str(), v))
|
||||||
}
|
}
|
||||||
/// Get a mutable iterator of all the modules.
|
/// Get a mutable iterator of all the modules.
|
||||||
|
#[inline(always)]
|
||||||
pub fn iter_mut(&mut self) -> impl Iterator<Item = (&str, &mut Module)> {
|
pub fn iter_mut(&mut self) -> impl Iterator<Item = (&str, &mut Module)> {
|
||||||
self.0.iter_mut().map(|(k, v)| (k.as_str(), v))
|
self.0.iter_mut().map(|(k, v)| (k.as_str(), v))
|
||||||
}
|
}
|
||||||
|
/// Get a mutable iterator of all the modules.
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn into_iter(self) -> impl Iterator<Item = (String, Module)> {
|
||||||
|
self.0.into_iter()
|
||||||
|
}
|
||||||
/// Get an iterator of all the module paths.
|
/// Get an iterator of all the module paths.
|
||||||
|
#[inline(always)]
|
||||||
pub fn paths(&self) -> impl Iterator<Item = &str> {
|
pub fn paths(&self) -> impl Iterator<Item = &str> {
|
||||||
self.0.keys().map(String::as_str)
|
self.0.keys().map(String::as_str)
|
||||||
}
|
}
|
||||||
/// Get an iterator of all the modules.
|
/// Get an iterator of all the modules.
|
||||||
|
#[inline(always)]
|
||||||
pub fn values(&self) -> impl Iterator<Item = &Module> {
|
pub fn values(&self) -> impl Iterator<Item = &Module> {
|
||||||
self.0.values()
|
self.0.values()
|
||||||
}
|
}
|
||||||
/// Get a mutable iterator of all the modules.
|
/// Get a mutable iterator of all the modules.
|
||||||
|
#[inline(always)]
|
||||||
pub fn values_mut(&mut self) -> impl Iterator<Item = &mut Module> {
|
pub fn values_mut(&mut self) -> impl Iterator<Item = &mut Module> {
|
||||||
self.0.values_mut()
|
self.0.values_mut()
|
||||||
}
|
}
|
||||||
/// Remove all modules.
|
/// Remove all modules.
|
||||||
|
#[inline(always)]
|
||||||
pub fn clear(&mut self) {
|
pub fn clear(&mut self) {
|
||||||
self.0.clear();
|
self.0.clear();
|
||||||
}
|
}
|
||||||
|
/// Is this `StaticModuleResolver` empty?
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.0.is_empty()
|
||||||
|
}
|
||||||
|
/// Get the number of modules in this `StaticModuleResolver`.
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.0.len()
|
||||||
|
}
|
||||||
|
/// Merge another `StaticModuleResolver` into this.
|
||||||
|
/// The other `StaticModuleResolver` is consumed.
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn merge(&mut self, other: Self) {
|
||||||
|
if !other.is_empty() {
|
||||||
|
self.0.extend(other.0.into_iter());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ModuleResolver for StaticModuleResolver {
|
impl ModuleResolver for StaticModuleResolver {
|
||||||
|
#[inline(always)]
|
||||||
fn resolve(&self, _: &Engine, path: &str, pos: Position) -> Result<Module, Box<EvalAltResult>> {
|
fn resolve(&self, _: &Engine, path: &str, pos: Position) -> Result<Module, Box<EvalAltResult>> {
|
||||||
self.0
|
self.0
|
||||||
.get(path)
|
.get(path)
|
||||||
@ -94,3 +125,10 @@ impl ModuleResolver for StaticModuleResolver {
|
|||||||
.ok_or_else(|| EvalAltResult::ErrorModuleNotFound(path.into(), pos).into())
|
.ok_or_else(|| EvalAltResult::ErrorModuleNotFound(path.into(), pos).into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl AddAssign<Self> for StaticModuleResolver {
|
||||||
|
#[inline(always)]
|
||||||
|
fn add_assign(&mut self, rhs: Self) {
|
||||||
|
self.merge(rhs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
137
src/optimize.rs
137
src/optimize.rs
@ -1,17 +1,16 @@
|
|||||||
//! Module implementing the AST optimizer.
|
//! Module implementing the AST optimizer.
|
||||||
|
|
||||||
use crate::any::Dynamic;
|
use crate::any::Dynamic;
|
||||||
use crate::calc_fn_hash;
|
|
||||||
use crate::engine::{
|
use crate::engine::{
|
||||||
Engine, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_IS_DEF_FN, KEYWORD_IS_DEF_VAR,
|
Engine, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_IS_DEF_FN, KEYWORD_IS_DEF_VAR, KEYWORD_PRINT,
|
||||||
KEYWORD_PRINT, KEYWORD_TYPE_OF,
|
KEYWORD_TYPE_OF,
|
||||||
};
|
};
|
||||||
use crate::fn_call::run_builtin_binary_op;
|
use crate::fn_call::run_builtin_binary_op;
|
||||||
use crate::fn_native::FnPtr;
|
|
||||||
use crate::module::Module;
|
use crate::module::Module;
|
||||||
use crate::parser::{map_dynamic_to_expr, Expr, ScriptFnDef, Stmt, AST};
|
use crate::parser::{map_dynamic_to_expr, Expr, ScriptFnDef, Stmt, AST};
|
||||||
use crate::scope::{Entry as ScopeEntry, EntryType as ScopeEntryType, Scope};
|
use crate::scope::{Entry as ScopeEntry, Scope};
|
||||||
use crate::utils::StaticVec;
|
use crate::token::{is_valid_identifier, Position};
|
||||||
|
use crate::{calc_fn_hash, StaticVec};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
use crate::parser::ReturnType;
|
use crate::parser::ReturnType;
|
||||||
@ -21,7 +20,6 @@ use crate::parser::CustomExpr;
|
|||||||
|
|
||||||
use crate::stdlib::{
|
use crate::stdlib::{
|
||||||
boxed::Box,
|
boxed::Box,
|
||||||
convert::TryFrom,
|
|
||||||
iter::empty,
|
iter::empty,
|
||||||
string::{String, ToString},
|
string::{String, ToString},
|
||||||
vec,
|
vec,
|
||||||
@ -44,22 +42,26 @@ pub enum OptimizationLevel {
|
|||||||
|
|
||||||
impl OptimizationLevel {
|
impl OptimizationLevel {
|
||||||
/// Is the `OptimizationLevel` None.
|
/// Is the `OptimizationLevel` None.
|
||||||
|
#[inline(always)]
|
||||||
pub fn is_none(self) -> bool {
|
pub fn is_none(self) -> bool {
|
||||||
self == Self::None
|
self == Self::None
|
||||||
}
|
}
|
||||||
/// Is the `OptimizationLevel` Simple.
|
/// Is the `OptimizationLevel` Simple.
|
||||||
#[cfg(not(feature = "no_optimize"))]
|
#[cfg(not(feature = "no_optimize"))]
|
||||||
|
#[inline(always)]
|
||||||
pub fn is_simple(self) -> bool {
|
pub fn is_simple(self) -> bool {
|
||||||
self == Self::Simple
|
self == Self::Simple
|
||||||
}
|
}
|
||||||
/// Is the `OptimizationLevel` Full.
|
/// Is the `OptimizationLevel` Full.
|
||||||
#[cfg(not(feature = "no_optimize"))]
|
#[cfg(not(feature = "no_optimize"))]
|
||||||
|
#[inline(always)]
|
||||||
pub fn is_full(self) -> bool {
|
pub fn is_full(self) -> bool {
|
||||||
self == Self::Full
|
self == Self::Full
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mutable state throughout an optimization pass.
|
/// Mutable state throughout an optimization pass.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
struct State<'a> {
|
struct State<'a> {
|
||||||
/// Has the AST been changed during this pass?
|
/// Has the AST been changed during this pass?
|
||||||
changed: bool,
|
changed: bool,
|
||||||
@ -75,6 +77,7 @@ struct State<'a> {
|
|||||||
|
|
||||||
impl<'a> State<'a> {
|
impl<'a> State<'a> {
|
||||||
/// Create a new State.
|
/// Create a new State.
|
||||||
|
#[inline(always)]
|
||||||
pub fn new(engine: &'a Engine, lib: &'a Module, level: OptimizationLevel) -> Self {
|
pub fn new(engine: &'a Engine, lib: &'a Module, level: OptimizationLevel) -> Self {
|
||||||
Self {
|
Self {
|
||||||
changed: false,
|
changed: false,
|
||||||
@ -85,30 +88,37 @@ impl<'a> State<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Reset the state from dirty to clean.
|
/// Reset the state from dirty to clean.
|
||||||
|
#[inline(always)]
|
||||||
pub fn reset(&mut self) {
|
pub fn reset(&mut self) {
|
||||||
self.changed = false;
|
self.changed = false;
|
||||||
}
|
}
|
||||||
/// Set the AST state to be dirty (i.e. changed).
|
/// Set the AST state to be dirty (i.e. changed).
|
||||||
|
#[inline(always)]
|
||||||
pub fn set_dirty(&mut self) {
|
pub fn set_dirty(&mut self) {
|
||||||
self.changed = true;
|
self.changed = true;
|
||||||
}
|
}
|
||||||
/// Is the AST dirty (i.e. changed)?
|
/// Is the AST dirty (i.e. changed)?
|
||||||
|
#[inline(always)]
|
||||||
pub fn is_dirty(&self) -> bool {
|
pub fn is_dirty(&self) -> bool {
|
||||||
self.changed
|
self.changed
|
||||||
}
|
}
|
||||||
/// Does a constant exist?
|
/// Does a constant exist?
|
||||||
|
#[inline(always)]
|
||||||
pub fn contains_constant(&self, name: &str) -> bool {
|
pub fn contains_constant(&self, name: &str) -> bool {
|
||||||
self.constants.iter().any(|(n, _)| n == name)
|
self.constants.iter().any(|(n, _)| n == name)
|
||||||
}
|
}
|
||||||
/// Prune the list of constants back to a specified size.
|
/// Prune the list of constants back to a specified size.
|
||||||
|
#[inline(always)]
|
||||||
pub fn restore_constants(&mut self, len: usize) {
|
pub fn restore_constants(&mut self, len: usize) {
|
||||||
self.constants.truncate(len)
|
self.constants.truncate(len)
|
||||||
}
|
}
|
||||||
/// Add a new constant to the list.
|
/// Add a new constant to the list.
|
||||||
|
#[inline(always)]
|
||||||
pub fn push_constant(&mut self, name: &str, value: Expr) {
|
pub fn push_constant(&mut self, name: &str, value: Expr) {
|
||||||
self.constants.push((name.into(), value))
|
self.constants.push((name.into(), value))
|
||||||
}
|
}
|
||||||
/// Look up a constant from the list.
|
/// Look up a constant from the list.
|
||||||
|
#[inline]
|
||||||
pub fn find_constant(&self, name: &str) -> Option<&Expr> {
|
pub fn find_constant(&self, name: &str) -> Option<&Expr> {
|
||||||
for (n, expr) in self.constants.iter().rev() {
|
for (n, expr) in self.constants.iter().rev() {
|
||||||
if n == name {
|
if n == name {
|
||||||
@ -271,12 +281,24 @@ fn optimize_stmt(stmt: Stmt, state: &mut State, preserve_result: bool) -> Stmt {
|
|||||||
let mut result: Vec<_> =
|
let mut result: Vec<_> =
|
||||||
x.0.into_iter()
|
x.0.into_iter()
|
||||||
.map(|stmt| match stmt {
|
.map(|stmt| match stmt {
|
||||||
// Add constant into the state
|
// Add constant literals into the state
|
||||||
Stmt::Const(v) => {
|
Stmt::Const(mut v) => {
|
||||||
let ((name, pos), expr, _) = *v;
|
if let Some(expr) = v.1 {
|
||||||
state.push_constant(&name, expr);
|
let expr = optimize_expr(expr, state);
|
||||||
state.set_dirty();
|
|
||||||
Stmt::Noop(pos) // No need to keep constants
|
if expr.is_literal() {
|
||||||
|
state.set_dirty();
|
||||||
|
state.push_constant(&(v.0).0, expr);
|
||||||
|
Stmt::Noop(pos) // No need to keep constants
|
||||||
|
} else {
|
||||||
|
v.1 = Some(expr);
|
||||||
|
Stmt::Const(v)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
state.set_dirty();
|
||||||
|
state.push_constant(&(v.0).0, Expr::Unit((v.0).1));
|
||||||
|
Stmt::Noop(pos) // No need to keep constants
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Optimize the statement
|
// Optimize the statement
|
||||||
_ => optimize_stmt(stmt, state, preserve_result),
|
_ => optimize_stmt(stmt, state, preserve_result),
|
||||||
@ -299,8 +321,7 @@ fn optimize_stmt(stmt: Stmt, state: &mut State, preserve_result: bool) -> Stmt {
|
|||||||
|
|
||||||
while let Some(expr) = result.pop() {
|
while let Some(expr) = result.pop() {
|
||||||
match expr {
|
match expr {
|
||||||
Stmt::Let(x) if x.1.is_none() => removed = true,
|
Stmt::Let(x) => removed = x.1.as_ref().map(Expr::is_pure).unwrap_or(true),
|
||||||
Stmt::Let(x) if x.1.is_some() => removed = x.1.unwrap().is_pure(),
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
Stmt::Import(x) => removed = x.0.is_pure(),
|
Stmt::Import(x) => removed = x.0.is_pure(),
|
||||||
_ => {
|
_ => {
|
||||||
@ -334,9 +355,7 @@ fn optimize_stmt(stmt: Stmt, state: &mut State, preserve_result: bool) -> Stmt {
|
|||||||
}
|
}
|
||||||
|
|
||||||
match stmt {
|
match stmt {
|
||||||
Stmt::ReturnWithVal(_) | Stmt::Break(_) => {
|
Stmt::ReturnWithVal(_) | Stmt::Break(_) => dead_code = true,
|
||||||
dead_code = true;
|
|
||||||
}
|
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -387,11 +406,11 @@ fn optimize_stmt(stmt: Stmt, state: &mut State, preserve_result: bool) -> Stmt {
|
|||||||
fn optimize_expr(expr: Expr, state: &mut State) -> Expr {
|
fn optimize_expr(expr: Expr, state: &mut State) -> Expr {
|
||||||
// These keywords are handled specially
|
// These keywords are handled specially
|
||||||
const DONT_EVAL_KEYWORDS: &[&str] = &[
|
const DONT_EVAL_KEYWORDS: &[&str] = &[
|
||||||
KEYWORD_PRINT,
|
KEYWORD_PRINT, // side effects
|
||||||
KEYWORD_DEBUG,
|
KEYWORD_DEBUG, // side effects
|
||||||
KEYWORD_EVAL,
|
KEYWORD_EVAL, // arbitrary scripts
|
||||||
KEYWORD_IS_DEF_FN,
|
KEYWORD_IS_DEF_FN, // functions collection is volatile
|
||||||
KEYWORD_IS_DEF_VAR,
|
KEYWORD_IS_DEF_VAR, // variables scope is volatile
|
||||||
];
|
];
|
||||||
|
|
||||||
match expr {
|
match expr {
|
||||||
@ -553,35 +572,18 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr {
|
|||||||
},
|
},
|
||||||
|
|
||||||
// Do not call some special keywords
|
// Do not call some special keywords
|
||||||
Expr::FnCall(mut x) if DONT_EVAL_KEYWORDS.contains(&(x.0).0.as_ref())=> {
|
Expr::FnCall(mut x) if DONT_EVAL_KEYWORDS.contains(&(x.0).0.as_ref()) => {
|
||||||
x.3 = x.3.into_iter().map(|a| optimize_expr(a, state)).collect();
|
x.3 = x.3.into_iter().map(|a| optimize_expr(a, state)).collect();
|
||||||
Expr::FnCall(x)
|
Expr::FnCall(x)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fn("...")
|
// Call built-in operators
|
||||||
Expr::FnCall(x)
|
|
||||||
if x.1.is_none()
|
|
||||||
&& (x.0).0 == KEYWORD_FN_PTR
|
|
||||||
&& x.3.len() == 1
|
|
||||||
&& matches!(x.3[0], Expr::StringConstant(_))
|
|
||||||
=> {
|
|
||||||
if let Expr::StringConstant(s) = &x.3[0] {
|
|
||||||
if let Ok(fn_ptr) = FnPtr::try_from(s.0.as_str()) {
|
|
||||||
Expr::FnPointer(Box::new((fn_ptr.take_data().0, s.1)))
|
|
||||||
} else {
|
|
||||||
Expr::FnCall(x)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
unreachable!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Call built-in functions
|
|
||||||
Expr::FnCall(mut x)
|
Expr::FnCall(mut x)
|
||||||
if x.1.is_none() // Non-qualified
|
if x.1.is_none() // Non-qualified
|
||||||
&& state.optimization_level == OptimizationLevel::Simple // simple optimizations
|
&& state.optimization_level == OptimizationLevel::Simple // simple optimizations
|
||||||
&& x.3.len() == 2 // binary call
|
&& x.3.len() == 2 // binary call
|
||||||
&& x.3.iter().all(Expr::is_constant) // all arguments are constants
|
&& x.3.iter().all(Expr::is_constant) // all arguments are constants
|
||||||
|
&& !is_valid_identifier((x.0).0.chars()) // cannot be scripted
|
||||||
=> {
|
=> {
|
||||||
let ((name, _, _, pos), _, _, args, _) = x.as_mut();
|
let ((name, _, _, pos), _, _, args, _) = x.as_mut();
|
||||||
|
|
||||||
@ -698,14 +700,21 @@ fn optimize(
|
|||||||
// Add constants from the scope into the state
|
// Add constants from the scope into the state
|
||||||
scope
|
scope
|
||||||
.to_iter()
|
.to_iter()
|
||||||
.filter(|ScopeEntry { typ, expr, .. }| {
|
// Get all the constants that can be made into a constant literal.
|
||||||
// Get all the constants with definite constant expressions
|
.filter(|ScopeEntry { typ, .. }| typ.is_constant())
|
||||||
*typ == ScopeEntryType::Constant
|
.for_each(
|
||||||
&& expr.as_ref().map(|v| v.is_constant()).unwrap_or(false)
|
|ScopeEntry {
|
||||||
})
|
name, expr, value, ..
|
||||||
.for_each(|ScopeEntry { name, expr, .. }| {
|
}| {
|
||||||
state.push_constant(name.as_ref(), expr.as_ref().unwrap().as_ref().clone())
|
if let Some(val) = expr
|
||||||
});
|
.as_ref()
|
||||||
|
.map(|expr| expr.as_ref().clone())
|
||||||
|
.or_else(|| map_dynamic_to_expr(value.clone(), Position::none()))
|
||||||
|
{
|
||||||
|
state.push_constant(name.as_ref(), val);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
let orig_constants_len = state.constants.len();
|
let orig_constants_len = state.constants.len();
|
||||||
|
|
||||||
@ -722,12 +731,28 @@ fn optimize(
|
|||||||
.into_iter()
|
.into_iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(i, stmt)| {
|
.map(|(i, stmt)| {
|
||||||
match &stmt {
|
match stmt {
|
||||||
Stmt::Const(v) => {
|
Stmt::Const(mut v) => {
|
||||||
// Load constants
|
// Load constants
|
||||||
let ((name, _), expr, _) = v.as_ref();
|
if let Some(expr) = v.1 {
|
||||||
state.push_constant(&name, expr.clone());
|
let expr = optimize_expr(expr, &mut state);
|
||||||
stmt // Keep it in the global scope
|
|
||||||
|
if expr.is_literal() {
|
||||||
|
state.push_constant(&(v.0).0, expr.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
v.1 = if expr.is_unit() {
|
||||||
|
state.set_dirty();
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(expr)
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
state.push_constant(&(v.0).0, Expr::Unit((v.0).1));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep it in the global scope
|
||||||
|
Stmt::Const(v)
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
// Keep all variable declarations at this level
|
// Keep all variable declarations at this level
|
||||||
|
@ -16,8 +16,8 @@ use num_traits::float::Float;
|
|||||||
use crate::stdlib::{format, string::String};
|
use crate::stdlib::{format, string::String};
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn make_err(msg: String) -> Box<EvalAltResult> {
|
pub fn make_err(msg: impl Into<String>) -> Box<EvalAltResult> {
|
||||||
EvalAltResult::ErrorArithmetic(msg, Position::none()).into()
|
EvalAltResult::ErrorArithmetic(msg.into(), Position::none()).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! gen_arithmetic_functions {
|
macro_rules! gen_arithmetic_functions {
|
||||||
@ -28,7 +28,6 @@ macro_rules! gen_arithmetic_functions {
|
|||||||
#[export_module]
|
#[export_module]
|
||||||
pub mod functions {
|
pub mod functions {
|
||||||
#[rhai_fn(name = "+", return_raw)]
|
#[rhai_fn(name = "+", return_raw)]
|
||||||
#[inline]
|
|
||||||
pub fn add(x: $arg_type, y: $arg_type) -> Result<Dynamic, Box<EvalAltResult>> {
|
pub fn add(x: $arg_type, y: $arg_type) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
if cfg!(not(feature = "unchecked")) {
|
if cfg!(not(feature = "unchecked")) {
|
||||||
x.checked_add(y).ok_or_else(|| make_err(format!("Addition overflow: {} + {}", x, y))).map(Dynamic::from)
|
x.checked_add(y).ok_or_else(|| make_err(format!("Addition overflow: {} + {}", x, y))).map(Dynamic::from)
|
||||||
@ -37,7 +36,6 @@ macro_rules! gen_arithmetic_functions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "-", return_raw)]
|
#[rhai_fn(name = "-", return_raw)]
|
||||||
#[inline]
|
|
||||||
pub fn subtract(x: $arg_type, y: $arg_type) -> Result<Dynamic, Box<EvalAltResult>> {
|
pub fn subtract(x: $arg_type, y: $arg_type) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
if cfg!(not(feature = "unchecked")) {
|
if cfg!(not(feature = "unchecked")) {
|
||||||
x.checked_sub(y).ok_or_else(|| make_err(format!("Subtraction overflow: {} - {}", x, y))).map(Dynamic::from)
|
x.checked_sub(y).ok_or_else(|| make_err(format!("Subtraction overflow: {} - {}", x, y))).map(Dynamic::from)
|
||||||
@ -46,7 +44,6 @@ macro_rules! gen_arithmetic_functions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "*", return_raw)]
|
#[rhai_fn(name = "*", return_raw)]
|
||||||
#[inline]
|
|
||||||
pub fn multiply(x: $arg_type, y: $arg_type) -> Result<Dynamic, Box<EvalAltResult>> {
|
pub fn multiply(x: $arg_type, y: $arg_type) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
if cfg!(not(feature = "unchecked")) {
|
if cfg!(not(feature = "unchecked")) {
|
||||||
x.checked_mul(y).ok_or_else(|| make_err(format!("Multiplication overflow: {} * {}", x, y))).map(Dynamic::from)
|
x.checked_mul(y).ok_or_else(|| make_err(format!("Multiplication overflow: {} * {}", x, y))).map(Dynamic::from)
|
||||||
@ -55,7 +52,6 @@ macro_rules! gen_arithmetic_functions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "/", return_raw)]
|
#[rhai_fn(name = "/", return_raw)]
|
||||||
#[inline]
|
|
||||||
pub fn divide(x: $arg_type, y: $arg_type) -> Result<Dynamic, Box<EvalAltResult>> {
|
pub fn divide(x: $arg_type, y: $arg_type) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
if cfg!(not(feature = "unchecked")) {
|
if cfg!(not(feature = "unchecked")) {
|
||||||
// Detect division by zero
|
// Detect division by zero
|
||||||
@ -69,7 +65,6 @@ macro_rules! gen_arithmetic_functions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "%", return_raw)]
|
#[rhai_fn(name = "%", return_raw)]
|
||||||
#[inline]
|
|
||||||
pub fn modulo(x: $arg_type, y: $arg_type) -> Result<Dynamic, Box<EvalAltResult>> {
|
pub fn modulo(x: $arg_type, y: $arg_type) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
if cfg!(not(feature = "unchecked")) {
|
if cfg!(not(feature = "unchecked")) {
|
||||||
x.checked_rem(y).ok_or_else(|| make_err(format!("Modulo division by zero or overflow: {} % {}", x, y))).map(Dynamic::from)
|
x.checked_rem(y).ok_or_else(|| make_err(format!("Modulo division by zero or overflow: {} % {}", x, y))).map(Dynamic::from)
|
||||||
@ -78,7 +73,6 @@ macro_rules! gen_arithmetic_functions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "~", return_raw)]
|
#[rhai_fn(name = "~", return_raw)]
|
||||||
#[inline]
|
|
||||||
pub fn power(x: INT, y: INT) -> Result<Dynamic, Box<EvalAltResult>> {
|
pub fn power(x: INT, y: INT) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
if cfg!(not(feature = "unchecked")) {
|
if cfg!(not(feature = "unchecked")) {
|
||||||
if cfg!(not(feature = "only_i32")) && y > (u32::MAX as INT) {
|
if cfg!(not(feature = "only_i32")) && y > (u32::MAX as INT) {
|
||||||
@ -94,7 +88,6 @@ macro_rules! gen_arithmetic_functions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[rhai_fn(name = "<<", return_raw)]
|
#[rhai_fn(name = "<<", return_raw)]
|
||||||
#[inline]
|
|
||||||
pub fn shift_left(x: $arg_type, y: INT) -> Result<Dynamic, Box<EvalAltResult>> {
|
pub fn shift_left(x: $arg_type, y: INT) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
if cfg!(not(feature = "unchecked")) {
|
if cfg!(not(feature = "unchecked")) {
|
||||||
if cfg!(not(feature = "only_i32")) && y > (u32::MAX as INT) {
|
if cfg!(not(feature = "only_i32")) && y > (u32::MAX as INT) {
|
||||||
@ -109,7 +102,6 @@ macro_rules! gen_arithmetic_functions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = ">>", return_raw)]
|
#[rhai_fn(name = ">>", return_raw)]
|
||||||
#[inline]
|
|
||||||
pub fn shift_right(x: $arg_type, y: INT) -> Result<Dynamic, Box<EvalAltResult>> {
|
pub fn shift_right(x: $arg_type, y: INT) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
if cfg!(not(feature = "unchecked")) {
|
if cfg!(not(feature = "unchecked")) {
|
||||||
if cfg!(not(feature = "only_i32")) && y > (u32::MAX as INT) {
|
if cfg!(not(feature = "only_i32")) && y > (u32::MAX as INT) {
|
||||||
@ -124,17 +116,14 @@ macro_rules! gen_arithmetic_functions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "&")]
|
#[rhai_fn(name = "&")]
|
||||||
#[inline(always)]
|
|
||||||
pub fn binary_and(x: $arg_type, y: $arg_type) -> $arg_type {
|
pub fn binary_and(x: $arg_type, y: $arg_type) -> $arg_type {
|
||||||
x & y
|
x & y
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "|")]
|
#[rhai_fn(name = "|")]
|
||||||
#[inline(always)]
|
|
||||||
pub fn binary_or(x: $arg_type, y: $arg_type) -> $arg_type {
|
pub fn binary_or(x: $arg_type, y: $arg_type) -> $arg_type {
|
||||||
x | y
|
x | y
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "^")]
|
#[rhai_fn(name = "^")]
|
||||||
#[inline(always)]
|
|
||||||
pub fn binary_xor(x: $arg_type, y: $arg_type) -> $arg_type {
|
pub fn binary_xor(x: $arg_type, y: $arg_type) -> $arg_type {
|
||||||
x ^ y
|
x ^ y
|
||||||
}
|
}
|
||||||
@ -151,7 +140,6 @@ macro_rules! gen_signed_functions {
|
|||||||
#[export_module]
|
#[export_module]
|
||||||
pub mod functions {
|
pub mod functions {
|
||||||
#[rhai_fn(name = "-", return_raw)]
|
#[rhai_fn(name = "-", return_raw)]
|
||||||
#[inline]
|
|
||||||
pub fn neg(x: $arg_type) -> Result<Dynamic, Box<EvalAltResult>> {
|
pub fn neg(x: $arg_type) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
if cfg!(not(feature = "unchecked")) {
|
if cfg!(not(feature = "unchecked")) {
|
||||||
x.checked_neg().ok_or_else(|| make_err(format!("Negation overflow: -{}", x))).map(Dynamic::from)
|
x.checked_neg().ok_or_else(|| make_err(format!("Negation overflow: -{}", x))).map(Dynamic::from)
|
||||||
@ -160,7 +148,6 @@ macro_rules! gen_signed_functions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[rhai_fn(return_raw)]
|
#[rhai_fn(return_raw)]
|
||||||
#[inline]
|
|
||||||
pub fn abs(x: $arg_type) -> Result<Dynamic, Box<EvalAltResult>> {
|
pub fn abs(x: $arg_type) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
if cfg!(not(feature = "unchecked")) {
|
if cfg!(not(feature = "unchecked")) {
|
||||||
x.checked_abs().ok_or_else(|| make_err(format!("Negation overflow: -{}", x))).map(Dynamic::from)
|
x.checked_abs().ok_or_else(|| make_err(format!("Negation overflow: -{}", x))).map(Dynamic::from)
|
||||||
@ -168,7 +155,6 @@ macro_rules! gen_signed_functions {
|
|||||||
Ok(Dynamic::from(x.abs()))
|
Ok(Dynamic::from(x.abs()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[inline]
|
|
||||||
pub fn sign(x: $arg_type) -> INT {
|
pub fn sign(x: $arg_type) -> INT {
|
||||||
if x == 0 {
|
if x == 0 {
|
||||||
0
|
0
|
||||||
@ -239,40 +225,32 @@ gen_signed_functions!(signed_num_128 => i128);
|
|||||||
#[export_module]
|
#[export_module]
|
||||||
mod f32_functions {
|
mod f32_functions {
|
||||||
#[rhai_fn(name = "+")]
|
#[rhai_fn(name = "+")]
|
||||||
#[inline(always)]
|
|
||||||
pub fn add(x: f32, y: f32) -> f32 {
|
pub fn add(x: f32, y: f32) -> f32 {
|
||||||
x + y
|
x + y
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "-")]
|
#[rhai_fn(name = "-")]
|
||||||
#[inline(always)]
|
|
||||||
pub fn subtract(x: f32, y: f32) -> f32 {
|
pub fn subtract(x: f32, y: f32) -> f32 {
|
||||||
x - y
|
x - y
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "*")]
|
#[rhai_fn(name = "*")]
|
||||||
#[inline(always)]
|
|
||||||
pub fn multiply(x: f32, y: f32) -> f32 {
|
pub fn multiply(x: f32, y: f32) -> f32 {
|
||||||
x * y
|
x * y
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "/")]
|
#[rhai_fn(name = "/")]
|
||||||
#[inline(always)]
|
|
||||||
pub fn divide(x: f32, y: f32) -> f32 {
|
pub fn divide(x: f32, y: f32) -> f32 {
|
||||||
x / y
|
x / y
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "%")]
|
#[rhai_fn(name = "%")]
|
||||||
#[inline(always)]
|
|
||||||
pub fn modulo(x: f32, y: f32) -> f32 {
|
pub fn modulo(x: f32, y: f32) -> f32 {
|
||||||
x % y
|
x % y
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "-")]
|
#[rhai_fn(name = "-")]
|
||||||
#[inline(always)]
|
|
||||||
pub fn neg(x: f32) -> f32 {
|
pub fn neg(x: f32) -> f32 {
|
||||||
-x
|
-x
|
||||||
}
|
}
|
||||||
#[inline(always)]
|
|
||||||
pub fn abs(x: f32) -> f32 {
|
pub fn abs(x: f32) -> f32 {
|
||||||
x.abs()
|
x.abs()
|
||||||
}
|
}
|
||||||
#[inline]
|
|
||||||
pub fn sign(x: f32) -> INT {
|
pub fn sign(x: f32) -> INT {
|
||||||
if x == 0.0 {
|
if x == 0.0 {
|
||||||
0
|
0
|
||||||
@ -283,12 +261,10 @@ mod f32_functions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "~", return_raw)]
|
#[rhai_fn(name = "~", return_raw)]
|
||||||
#[inline(always)]
|
|
||||||
pub fn pow_f_f(x: f32, y: f32) -> Result<Dynamic, Box<EvalAltResult>> {
|
pub fn pow_f_f(x: f32, y: f32) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
Ok(Dynamic::from(x.powf(y)))
|
Ok(Dynamic::from(x.powf(y)))
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "~", return_raw)]
|
#[rhai_fn(name = "~", return_raw)]
|
||||||
#[inline]
|
|
||||||
pub fn pow_f_i(x: f32, y: INT) -> Result<Dynamic, Box<EvalAltResult>> {
|
pub fn pow_f_i(x: f32, y: INT) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
if cfg!(not(feature = "unchecked")) && y > (i32::MAX as INT) {
|
if cfg!(not(feature = "unchecked")) && y > (i32::MAX as INT) {
|
||||||
Err(make_err(format!(
|
Err(make_err(format!(
|
||||||
@ -305,15 +281,12 @@ mod f32_functions {
|
|||||||
#[export_module]
|
#[export_module]
|
||||||
mod f64_functions {
|
mod f64_functions {
|
||||||
#[rhai_fn(name = "-")]
|
#[rhai_fn(name = "-")]
|
||||||
#[inline(always)]
|
|
||||||
pub fn neg(x: f64) -> f64 {
|
pub fn neg(x: f64) -> f64 {
|
||||||
-x
|
-x
|
||||||
}
|
}
|
||||||
#[inline(always)]
|
|
||||||
pub fn abs(x: f64) -> f64 {
|
pub fn abs(x: f64) -> f64 {
|
||||||
x.abs()
|
x.abs()
|
||||||
}
|
}
|
||||||
#[inline]
|
|
||||||
pub fn sign(x: f64) -> INT {
|
pub fn sign(x: f64) -> INT {
|
||||||
if x == 0.0 {
|
if x == 0.0 {
|
||||||
0
|
0
|
||||||
@ -324,7 +297,6 @@ mod f64_functions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "~", return_raw)]
|
#[rhai_fn(name = "~", return_raw)]
|
||||||
#[inline]
|
|
||||||
pub fn pow_f_i(x: FLOAT, y: INT) -> Result<Dynamic, Box<EvalAltResult>> {
|
pub fn pow_f_i(x: FLOAT, y: INT) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
if cfg!(not(feature = "unchecked")) && y > (i32::MAX as INT) {
|
if cfg!(not(feature = "unchecked")) && y > (i32::MAX as INT) {
|
||||||
Err(make_err(format!(
|
Err(make_err(format!(
|
||||||
|
@ -29,7 +29,6 @@ macro_rules! gen_array_functions {
|
|||||||
#[export_module]
|
#[export_module]
|
||||||
pub mod functions {
|
pub mod functions {
|
||||||
#[rhai_fn(name = "push", name = "+=")]
|
#[rhai_fn(name = "push", name = "+=")]
|
||||||
#[inline(always)]
|
|
||||||
pub fn push(list: &mut Array, item: $arg_type) {
|
pub fn push(list: &mut Array, item: $arg_type) {
|
||||||
list.push(Dynamic::from(item));
|
list.push(Dynamic::from(item));
|
||||||
}
|
}
|
||||||
@ -89,22 +88,18 @@ def_package!(crate:BasicArrayPackage:"Basic array utilities.", lib, {
|
|||||||
#[export_module]
|
#[export_module]
|
||||||
mod array_functions {
|
mod array_functions {
|
||||||
#[rhai_fn(name = "len", get = "len")]
|
#[rhai_fn(name = "len", get = "len")]
|
||||||
#[inline(always)]
|
|
||||||
pub fn len(list: &mut Array) -> INT {
|
pub fn len(list: &mut Array) -> INT {
|
||||||
list.len() as INT
|
list.len() as INT
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "append", name = "+=")]
|
#[rhai_fn(name = "append", name = "+=")]
|
||||||
#[inline(always)]
|
|
||||||
pub fn append(x: &mut Array, y: Array) {
|
pub fn append(x: &mut Array, y: Array) {
|
||||||
x.extend(y);
|
x.extend(y);
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "+")]
|
#[rhai_fn(name = "+")]
|
||||||
#[inline]
|
|
||||||
pub fn concat(mut x: Array, y: Array) -> Array {
|
pub fn concat(mut x: Array, y: Array) -> Array {
|
||||||
x.extend(y);
|
x.extend(y);
|
||||||
x
|
x
|
||||||
}
|
}
|
||||||
#[inline]
|
|
||||||
pub fn pop(list: &mut Array) -> Dynamic {
|
pub fn pop(list: &mut Array) -> Dynamic {
|
||||||
list.pop().unwrap_or_else(|| ().into())
|
list.pop().unwrap_or_else(|| ().into())
|
||||||
}
|
}
|
||||||
@ -122,7 +117,6 @@ mod array_functions {
|
|||||||
list.remove(len as usize)
|
list.remove(len as usize)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[inline(always)]
|
|
||||||
pub fn clear(list: &mut Array) {
|
pub fn clear(list: &mut Array) {
|
||||||
list.clear();
|
list.clear();
|
||||||
}
|
}
|
||||||
@ -133,7 +127,6 @@ mod array_functions {
|
|||||||
list.clear();
|
list.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[inline(always)]
|
|
||||||
pub fn reverse(list: &mut Array) {
|
pub fn reverse(list: &mut Array) {
|
||||||
list.reverse();
|
list.reverse();
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,6 @@ def_package!(crate:BasicFnPackage:"Basic Fn functions.", lib, {
|
|||||||
#[export_module]
|
#[export_module]
|
||||||
mod fn_ptr_functions {
|
mod fn_ptr_functions {
|
||||||
#[rhai_fn(name = "name", get = "name")]
|
#[rhai_fn(name = "name", get = "name")]
|
||||||
#[inline(always)]
|
|
||||||
pub fn name(f: &mut FnPtr) -> ImmutableString {
|
pub fn name(f: &mut FnPtr) -> ImmutableString {
|
||||||
f.get_fn_name().clone()
|
f.get_fn_name().clone()
|
||||||
}
|
}
|
||||||
|
@ -9,32 +9,26 @@ macro_rules! gen_cmp_functions {
|
|||||||
#[export_module]
|
#[export_module]
|
||||||
pub mod functions {
|
pub mod functions {
|
||||||
#[rhai_fn(name = "<")]
|
#[rhai_fn(name = "<")]
|
||||||
#[inline(always)]
|
|
||||||
pub fn lt(x: $arg_type, y: $arg_type) -> bool {
|
pub fn lt(x: $arg_type, y: $arg_type) -> bool {
|
||||||
x < y
|
x < y
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "<=")]
|
#[rhai_fn(name = "<=")]
|
||||||
#[inline(always)]
|
|
||||||
pub fn lte(x: $arg_type, y: $arg_type) -> bool {
|
pub fn lte(x: $arg_type, y: $arg_type) -> bool {
|
||||||
x <= y
|
x <= y
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = ">")]
|
#[rhai_fn(name = ">")]
|
||||||
#[inline(always)]
|
|
||||||
pub fn gt(x: $arg_type, y: $arg_type) -> bool {
|
pub fn gt(x: $arg_type, y: $arg_type) -> bool {
|
||||||
x > y
|
x > y
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = ">=")]
|
#[rhai_fn(name = ">=")]
|
||||||
#[inline(always)]
|
|
||||||
pub fn gte(x: $arg_type, y: $arg_type) -> bool {
|
pub fn gte(x: $arg_type, y: $arg_type) -> bool {
|
||||||
x >= y
|
x >= y
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "==")]
|
#[rhai_fn(name = "==")]
|
||||||
#[inline(always)]
|
|
||||||
pub fn eq(x: $arg_type, y: $arg_type) -> bool {
|
pub fn eq(x: $arg_type, y: $arg_type) -> bool {
|
||||||
x == y
|
x == y
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "!=")]
|
#[rhai_fn(name = "!=")]
|
||||||
#[inline(always)]
|
|
||||||
pub fn ne(x: $arg_type, y: $arg_type) -> bool {
|
pub fn ne(x: $arg_type, y: $arg_type) -> bool {
|
||||||
x != y
|
x != y
|
||||||
}
|
}
|
||||||
@ -67,7 +61,6 @@ def_package!(crate:LogicPackage:"Logical operators.", lib, {
|
|||||||
|
|
||||||
// Logic operators
|
// Logic operators
|
||||||
#[export_fn]
|
#[export_fn]
|
||||||
#[inline(always)]
|
|
||||||
fn not(x: bool) -> bool {
|
fn not(x: bool) -> bool {
|
||||||
!x
|
!x
|
||||||
}
|
}
|
||||||
|
@ -14,19 +14,15 @@ def_package!(crate:BasicMapPackage:"Basic object map utilities.", lib, {
|
|||||||
|
|
||||||
#[export_module]
|
#[export_module]
|
||||||
mod map_functions {
|
mod map_functions {
|
||||||
#[inline(always)]
|
|
||||||
pub fn has(map: &mut Map, prop: ImmutableString) -> bool {
|
pub fn has(map: &mut Map, prop: ImmutableString) -> bool {
|
||||||
map.contains_key(&prop)
|
map.contains_key(&prop)
|
||||||
}
|
}
|
||||||
#[inline(always)]
|
|
||||||
pub fn len(map: &mut Map) -> INT {
|
pub fn len(map: &mut Map) -> INT {
|
||||||
map.len() as INT
|
map.len() as INT
|
||||||
}
|
}
|
||||||
#[inline(always)]
|
|
||||||
pub fn clear(map: &mut Map) {
|
pub fn clear(map: &mut Map) {
|
||||||
map.clear();
|
map.clear();
|
||||||
}
|
}
|
||||||
#[inline(always)]
|
|
||||||
pub fn remove(x: &mut Map, name: ImmutableString) -> Dynamic {
|
pub fn remove(x: &mut Map, name: ImmutableString) -> Dynamic {
|
||||||
x.remove(&name).unwrap_or_else(|| ().into())
|
x.remove(&name).unwrap_or_else(|| ().into())
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,6 @@ macro_rules! gen_conversion_functions {
|
|||||||
use super::super::*;
|
use super::super::*;
|
||||||
|
|
||||||
#[export_fn]
|
#[export_fn]
|
||||||
#[inline(always)]
|
|
||||||
pub fn $func_name(x: $arg_type) -> $result_type {
|
pub fn $func_name(x: $arg_type) -> $result_type {
|
||||||
x as $result_type
|
x as $result_type
|
||||||
}
|
}
|
||||||
@ -86,51 +85,39 @@ def_package!(crate:BasicMathPackage:"Basic mathematic functions.", lib, {
|
|||||||
mod trig_functions {
|
mod trig_functions {
|
||||||
use crate::parser::FLOAT;
|
use crate::parser::FLOAT;
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn sin(x: FLOAT) -> FLOAT {
|
pub fn sin(x: FLOAT) -> FLOAT {
|
||||||
x.to_radians().sin()
|
x.to_radians().sin()
|
||||||
}
|
}
|
||||||
#[inline(always)]
|
|
||||||
pub fn cos(x: FLOAT) -> FLOAT {
|
pub fn cos(x: FLOAT) -> FLOAT {
|
||||||
x.to_radians().cos()
|
x.to_radians().cos()
|
||||||
}
|
}
|
||||||
#[inline(always)]
|
|
||||||
pub fn tan(x: FLOAT) -> FLOAT {
|
pub fn tan(x: FLOAT) -> FLOAT {
|
||||||
x.to_radians().tan()
|
x.to_radians().tan()
|
||||||
}
|
}
|
||||||
#[inline(always)]
|
|
||||||
pub fn sinh(x: FLOAT) -> FLOAT {
|
pub fn sinh(x: FLOAT) -> FLOAT {
|
||||||
x.to_radians().sinh()
|
x.to_radians().sinh()
|
||||||
}
|
}
|
||||||
#[inline(always)]
|
|
||||||
pub fn cosh(x: FLOAT) -> FLOAT {
|
pub fn cosh(x: FLOAT) -> FLOAT {
|
||||||
x.to_radians().cosh()
|
x.to_radians().cosh()
|
||||||
}
|
}
|
||||||
#[inline(always)]
|
|
||||||
pub fn tanh(x: FLOAT) -> FLOAT {
|
pub fn tanh(x: FLOAT) -> FLOAT {
|
||||||
x.to_radians().tanh()
|
x.to_radians().tanh()
|
||||||
}
|
}
|
||||||
#[inline(always)]
|
|
||||||
pub fn asin(x: FLOAT) -> FLOAT {
|
pub fn asin(x: FLOAT) -> FLOAT {
|
||||||
x.asin().to_degrees()
|
x.asin().to_degrees()
|
||||||
}
|
}
|
||||||
#[inline(always)]
|
|
||||||
pub fn acos(x: FLOAT) -> FLOAT {
|
pub fn acos(x: FLOAT) -> FLOAT {
|
||||||
x.acos().to_degrees()
|
x.acos().to_degrees()
|
||||||
}
|
}
|
||||||
#[inline(always)]
|
|
||||||
pub fn atan(x: FLOAT) -> FLOAT {
|
pub fn atan(x: FLOAT) -> FLOAT {
|
||||||
x.atan().to_degrees()
|
x.atan().to_degrees()
|
||||||
}
|
}
|
||||||
#[inline(always)]
|
|
||||||
pub fn asinh(x: FLOAT) -> FLOAT {
|
pub fn asinh(x: FLOAT) -> FLOAT {
|
||||||
x.asinh().to_degrees()
|
x.asinh().to_degrees()
|
||||||
}
|
}
|
||||||
#[inline(always)]
|
|
||||||
pub fn acosh(x: FLOAT) -> FLOAT {
|
pub fn acosh(x: FLOAT) -> FLOAT {
|
||||||
x.acosh().to_degrees()
|
x.acosh().to_degrees()
|
||||||
}
|
}
|
||||||
#[inline(always)]
|
|
||||||
pub fn atanh(x: FLOAT) -> FLOAT {
|
pub fn atanh(x: FLOAT) -> FLOAT {
|
||||||
x.atanh().to_degrees()
|
x.atanh().to_degrees()
|
||||||
}
|
}
|
||||||
@ -141,68 +128,54 @@ mod trig_functions {
|
|||||||
mod float_functions {
|
mod float_functions {
|
||||||
use crate::parser::FLOAT;
|
use crate::parser::FLOAT;
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn sqrt(x: FLOAT) -> FLOAT {
|
pub fn sqrt(x: FLOAT) -> FLOAT {
|
||||||
x.sqrt()
|
x.sqrt()
|
||||||
}
|
}
|
||||||
#[inline(always)]
|
|
||||||
pub fn exp(x: FLOAT) -> FLOAT {
|
pub fn exp(x: FLOAT) -> FLOAT {
|
||||||
x.exp()
|
x.exp()
|
||||||
}
|
}
|
||||||
#[inline(always)]
|
|
||||||
pub fn ln(x: FLOAT) -> FLOAT {
|
pub fn ln(x: FLOAT) -> FLOAT {
|
||||||
x.ln()
|
x.ln()
|
||||||
}
|
}
|
||||||
#[inline(always)]
|
|
||||||
pub fn log(x: FLOAT, base: FLOAT) -> FLOAT {
|
pub fn log(x: FLOAT, base: FLOAT) -> FLOAT {
|
||||||
x.log(base)
|
x.log(base)
|
||||||
}
|
}
|
||||||
#[inline(always)]
|
|
||||||
pub fn log10(x: FLOAT) -> FLOAT {
|
pub fn log10(x: FLOAT) -> FLOAT {
|
||||||
x.log10()
|
x.log10()
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "floor", get = "floor")]
|
#[rhai_fn(name = "floor", get = "floor")]
|
||||||
#[inline(always)]
|
|
||||||
pub fn floor(x: FLOAT) -> FLOAT {
|
pub fn floor(x: FLOAT) -> FLOAT {
|
||||||
x.floor()
|
x.floor()
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "ceiling", get = "ceiling")]
|
#[rhai_fn(name = "ceiling", get = "ceiling")]
|
||||||
#[inline(always)]
|
|
||||||
pub fn ceiling(x: FLOAT) -> FLOAT {
|
pub fn ceiling(x: FLOAT) -> FLOAT {
|
||||||
x.ceil()
|
x.ceil()
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "round", get = "round")]
|
#[rhai_fn(name = "round", get = "round")]
|
||||||
#[inline(always)]
|
|
||||||
pub fn round(x: FLOAT) -> FLOAT {
|
pub fn round(x: FLOAT) -> FLOAT {
|
||||||
x.ceil()
|
x.ceil()
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "int", get = "int")]
|
#[rhai_fn(name = "int", get = "int")]
|
||||||
#[inline(always)]
|
|
||||||
pub fn int(x: FLOAT) -> FLOAT {
|
pub fn int(x: FLOAT) -> FLOAT {
|
||||||
x.trunc()
|
x.trunc()
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "fraction", get = "fraction")]
|
#[rhai_fn(name = "fraction", get = "fraction")]
|
||||||
#[inline(always)]
|
|
||||||
pub fn fraction(x: FLOAT) -> FLOAT {
|
pub fn fraction(x: FLOAT) -> FLOAT {
|
||||||
x.fract()
|
x.fract()
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "is_nan", get = "is_nan")]
|
#[rhai_fn(name = "is_nan", get = "is_nan")]
|
||||||
#[inline(always)]
|
|
||||||
pub fn is_nan(x: FLOAT) -> bool {
|
pub fn is_nan(x: FLOAT) -> bool {
|
||||||
x.is_nan()
|
x.is_nan()
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "is_finite", get = "is_finite")]
|
#[rhai_fn(name = "is_finite", get = "is_finite")]
|
||||||
#[inline(always)]
|
|
||||||
pub fn is_finite(x: FLOAT) -> bool {
|
pub fn is_finite(x: FLOAT) -> bool {
|
||||||
x.is_finite()
|
x.is_finite()
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "is_infinite", get = "is_infinite")]
|
#[rhai_fn(name = "is_infinite", get = "is_infinite")]
|
||||||
#[inline(always)]
|
|
||||||
pub fn is_infinite(x: FLOAT) -> bool {
|
pub fn is_infinite(x: FLOAT) -> bool {
|
||||||
x.is_infinite()
|
x.is_infinite()
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "to_int", return_raw)]
|
#[rhai_fn(name = "to_int", return_raw)]
|
||||||
#[inline]
|
|
||||||
pub fn f32_to_int(x: f32) -> Result<Dynamic, Box<EvalAltResult>> {
|
pub fn f32_to_int(x: f32) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
if cfg!(not(feature = "unchecked")) && x > (MAX_INT as f32) {
|
if cfg!(not(feature = "unchecked")) && x > (MAX_INT as f32) {
|
||||||
EvalAltResult::ErrorArithmetic(
|
EvalAltResult::ErrorArithmetic(
|
||||||
@ -215,7 +188,6 @@ mod float_functions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "to_int", return_raw)]
|
#[rhai_fn(name = "to_int", return_raw)]
|
||||||
#[inline]
|
|
||||||
pub fn f64_to_int(x: f64) -> Result<Dynamic, Box<EvalAltResult>> {
|
pub fn f64_to_int(x: f64) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
if cfg!(not(feature = "unchecked")) && x > (MAX_INT as f64) {
|
if cfg!(not(feature = "unchecked")) && x > (MAX_INT as f64) {
|
||||||
EvalAltResult::ErrorArithmetic(
|
EvalAltResult::ErrorArithmetic(
|
||||||
@ -229,7 +201,6 @@ mod float_functions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[rhai_fn(return_raw)]
|
#[rhai_fn(return_raw)]
|
||||||
#[inline]
|
|
||||||
pub fn parse_float(s: &str) -> Result<Dynamic, Box<EvalAltResult>> {
|
pub fn parse_float(s: &str) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
s.trim()
|
s.trim()
|
||||||
.parse::<FLOAT>()
|
.parse::<FLOAT>()
|
||||||
@ -291,7 +262,6 @@ fn parse_int_radix(s: &str, radix: INT) -> Result<Dynamic, Box<EvalAltResult>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[export_fn(return_raw)]
|
#[export_fn(return_raw)]
|
||||||
#[inline(always)]
|
|
||||||
fn parse_int(s: &str) -> Result<Dynamic, Box<EvalAltResult>> {
|
fn parse_int(s: &str) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
parse_int_radix(s, 10)
|
parse_int_radix(s, 10)
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
use crate::fn_native::{CallableFunction, IteratorFn, Shared};
|
use crate::fn_native::{CallableFunction, IteratorFn, Shared};
|
||||||
use crate::module::Module;
|
use crate::module::Module;
|
||||||
use crate::utils::StaticVec;
|
use crate::StaticVec;
|
||||||
|
|
||||||
use crate::stdlib::any::TypeId;
|
use crate::stdlib::any::TypeId;
|
||||||
|
|
||||||
|
@ -26,7 +26,6 @@ macro_rules! gen_functions {
|
|||||||
use super::super::*;
|
use super::super::*;
|
||||||
|
|
||||||
#[export_fn]
|
#[export_fn]
|
||||||
#[inline(always)]
|
|
||||||
pub fn to_string_func(x: &mut $arg_type) -> ImmutableString {
|
pub fn to_string_func(x: &mut $arg_type) -> ImmutableString {
|
||||||
super::super::$fn_name(x)
|
super::super::$fn_name(x)
|
||||||
}
|
}
|
||||||
@ -124,30 +123,24 @@ gen_functions!(print_array => to_debug(Array));
|
|||||||
|
|
||||||
// Register print and debug
|
// Register print and debug
|
||||||
#[export_fn]
|
#[export_fn]
|
||||||
#[inline(always)]
|
|
||||||
fn print_empty_string() -> ImmutableString {
|
fn print_empty_string() -> ImmutableString {
|
||||||
"".to_string().into()
|
"".to_string().into()
|
||||||
}
|
}
|
||||||
#[export_fn]
|
#[export_fn]
|
||||||
#[inline(always)]
|
|
||||||
fn print_unit(_x: ()) -> ImmutableString {
|
fn print_unit(_x: ()) -> ImmutableString {
|
||||||
"".to_string().into()
|
"".to_string().into()
|
||||||
}
|
}
|
||||||
#[export_fn]
|
#[export_fn]
|
||||||
#[inline(always)]
|
|
||||||
fn print_string(s: ImmutableString) -> ImmutableString {
|
fn print_string(s: ImmutableString) -> ImmutableString {
|
||||||
s
|
s
|
||||||
}
|
}
|
||||||
#[export_fn]
|
#[export_fn]
|
||||||
#[inline(always)]
|
|
||||||
fn debug_fn_ptr(f: &mut FnPtr) -> ImmutableString {
|
fn debug_fn_ptr(f: &mut FnPtr) -> ImmutableString {
|
||||||
to_string(f)
|
to_string(f)
|
||||||
}
|
}
|
||||||
#[inline(always)]
|
|
||||||
fn to_string<T: Display>(x: &mut T) -> ImmutableString {
|
fn to_string<T: Display>(x: &mut T) -> ImmutableString {
|
||||||
x.to_string().into()
|
x.to_string().into()
|
||||||
}
|
}
|
||||||
#[inline]
|
|
||||||
fn to_debug<T: Debug>(x: &mut T) -> ImmutableString {
|
fn to_debug<T: Debug>(x: &mut T) -> ImmutableString {
|
||||||
format!("{:?}", x).into()
|
format!("{:?}", x).into()
|
||||||
}
|
}
|
||||||
@ -155,7 +148,7 @@ fn to_debug<T: Debug>(x: &mut T) -> ImmutableString {
|
|||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
mod format_map {
|
mod format_map {
|
||||||
use super::*;
|
use super::*;
|
||||||
#[inline]
|
|
||||||
#[export_fn]
|
#[export_fn]
|
||||||
pub fn format_map(x: &mut Map) -> ImmutableString {
|
pub fn format_map(x: &mut Map) -> ImmutableString {
|
||||||
format!("#{:?}", x).into()
|
format!("#{:?}", x).into()
|
||||||
|
@ -6,7 +6,7 @@ use crate::engine::Engine;
|
|||||||
use crate::fn_native::FnPtr;
|
use crate::fn_native::FnPtr;
|
||||||
use crate::parser::{ImmutableString, INT};
|
use crate::parser::{ImmutableString, INT};
|
||||||
use crate::plugin::*;
|
use crate::plugin::*;
|
||||||
use crate::utils::StaticVec;
|
use crate::StaticVec;
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
use crate::{result::EvalAltResult, token::Position};
|
use crate::{result::EvalAltResult, token::Position};
|
||||||
@ -23,13 +23,11 @@ macro_rules! gen_concat_functions {
|
|||||||
#[export_module]
|
#[export_module]
|
||||||
pub mod functions {
|
pub mod functions {
|
||||||
#[rhai_fn(name = "+")]
|
#[rhai_fn(name = "+")]
|
||||||
#[inline]
|
|
||||||
pub fn append_func(x: &str, y: $arg_type) -> String {
|
pub fn append_func(x: &str, y: $arg_type) -> String {
|
||||||
format!("{}{}", x, y)
|
format!("{}{}", x, y)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rhai_fn(name = "+")]
|
#[rhai_fn(name = "+")]
|
||||||
#[inline]
|
|
||||||
pub fn prepend_func(x: &mut $arg_type, y: &str) -> String {
|
pub fn prepend_func(x: &mut $arg_type, y: &str) -> String {
|
||||||
format!("{}{}", x, y)
|
format!("{}{}", x, y)
|
||||||
}
|
}
|
||||||
@ -133,34 +131,28 @@ gen_concat_functions!(float => f32, f64);
|
|||||||
#[export_module]
|
#[export_module]
|
||||||
mod string_functions {
|
mod string_functions {
|
||||||
#[rhai_fn(name = "+")]
|
#[rhai_fn(name = "+")]
|
||||||
#[inline(always)]
|
|
||||||
pub fn add_append_unit(s: ImmutableString, _x: ()) -> ImmutableString {
|
pub fn add_append_unit(s: ImmutableString, _x: ()) -> ImmutableString {
|
||||||
s
|
s
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "+")]
|
#[rhai_fn(name = "+")]
|
||||||
#[inline(always)]
|
|
||||||
pub fn add_prepend_unit(_x: (), s: ImmutableString) -> ImmutableString {
|
pub fn add_prepend_unit(_x: (), s: ImmutableString) -> ImmutableString {
|
||||||
s
|
s
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rhai_fn(name = "+=")]
|
#[rhai_fn(name = "+=")]
|
||||||
#[inline(always)]
|
|
||||||
pub fn append_char(s: &mut ImmutableString, ch: char) {
|
pub fn append_char(s: &mut ImmutableString, ch: char) {
|
||||||
*s += ch;
|
*s += ch;
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "+=")]
|
#[rhai_fn(name = "+=")]
|
||||||
#[inline(always)]
|
|
||||||
pub fn append_string(s: &mut ImmutableString, add: ImmutableString) {
|
pub fn append_string(s: &mut ImmutableString, add: ImmutableString) {
|
||||||
*s += &add;
|
*s += &add;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rhai_fn(name = "len", get = "len")]
|
#[rhai_fn(name = "len", get = "len")]
|
||||||
#[inline(always)]
|
|
||||||
pub fn len(s: &str) -> INT {
|
pub fn len(s: &str) -> INT {
|
||||||
s.chars().count() as INT
|
s.chars().count() as INT
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn clear(s: &mut ImmutableString) {
|
pub fn clear(s: &mut ImmutableString) {
|
||||||
s.make_mut().clear();
|
s.make_mut().clear();
|
||||||
}
|
}
|
||||||
@ -183,11 +175,9 @@ mod string_functions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[rhai_fn(name = "contains")]
|
#[rhai_fn(name = "contains")]
|
||||||
#[inline(always)]
|
|
||||||
pub fn contains_char(s: &str, ch: char) -> bool {
|
pub fn contains_char(s: &str, ch: char) -> bool {
|
||||||
s.contains(ch)
|
s.contains(ch)
|
||||||
}
|
}
|
||||||
#[inline(always)]
|
|
||||||
pub fn contains(s: &str, find: ImmutableString) -> bool {
|
pub fn contains(s: &str, find: ImmutableString) -> bool {
|
||||||
s.contains(find.as_str())
|
s.contains(find.as_str())
|
||||||
}
|
}
|
||||||
@ -263,7 +253,6 @@ mod string_functions {
|
|||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "sub_string")]
|
#[rhai_fn(name = "sub_string")]
|
||||||
#[inline(always)]
|
|
||||||
pub fn sub_string_starting_from(s: &str, start: INT) -> ImmutableString {
|
pub fn sub_string_starting_from(s: &str, start: INT) -> ImmutableString {
|
||||||
let len = s.len() as INT;
|
let len = s.len() as INT;
|
||||||
sub_string(s, start, len)
|
sub_string(s, start, len)
|
||||||
@ -296,28 +285,23 @@ mod string_functions {
|
|||||||
copy.extend(chars.iter().skip(offset).take(len));
|
copy.extend(chars.iter().skip(offset).take(len));
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "crop")]
|
#[rhai_fn(name = "crop")]
|
||||||
#[inline(always)]
|
|
||||||
pub fn crop_string_starting_from(s: &mut ImmutableString, start: INT) {
|
pub fn crop_string_starting_from(s: &mut ImmutableString, start: INT) {
|
||||||
crop(s, start, s.len() as INT);
|
crop(s, start, s.len() as INT);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rhai_fn(name = "replace")]
|
#[rhai_fn(name = "replace")]
|
||||||
#[inline(always)]
|
|
||||||
pub fn replace(s: &mut ImmutableString, find: ImmutableString, sub: ImmutableString) {
|
pub fn replace(s: &mut ImmutableString, find: ImmutableString, sub: ImmutableString) {
|
||||||
*s = s.replace(find.as_str(), sub.as_str()).into();
|
*s = s.replace(find.as_str(), sub.as_str()).into();
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "replace")]
|
#[rhai_fn(name = "replace")]
|
||||||
#[inline(always)]
|
|
||||||
pub fn replace_string_with_char(s: &mut ImmutableString, find: ImmutableString, sub: char) {
|
pub fn replace_string_with_char(s: &mut ImmutableString, find: ImmutableString, sub: char) {
|
||||||
*s = s.replace(find.as_str(), &sub.to_string()).into();
|
*s = s.replace(find.as_str(), &sub.to_string()).into();
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "replace")]
|
#[rhai_fn(name = "replace")]
|
||||||
#[inline(always)]
|
|
||||||
pub fn replace_char_with_string(s: &mut ImmutableString, find: char, sub: ImmutableString) {
|
pub fn replace_char_with_string(s: &mut ImmutableString, find: char, sub: ImmutableString) {
|
||||||
*s = s.replace(&find.to_string(), sub.as_str()).into();
|
*s = s.replace(&find.to_string(), sub.as_str()).into();
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "replace")]
|
#[rhai_fn(name = "replace")]
|
||||||
#[inline(always)]
|
|
||||||
pub fn replace_char(s: &mut ImmutableString, find: char, sub: char) {
|
pub fn replace_char(s: &mut ImmutableString, find: char, sub: char) {
|
||||||
*s = s.replace(&find.to_string(), &sub.to_string()).into();
|
*s = s.replace(&find.to_string(), &sub.to_string()).into();
|
||||||
}
|
}
|
||||||
@ -327,24 +311,20 @@ mod string_functions {
|
|||||||
use crate::engine::Array;
|
use crate::engine::Array;
|
||||||
|
|
||||||
#[rhai_fn(name = "+")]
|
#[rhai_fn(name = "+")]
|
||||||
#[inline]
|
|
||||||
pub fn append(x: &str, y: Array) -> String {
|
pub fn append(x: &str, y: Array) -> String {
|
||||||
format!("{}{:?}", x, y)
|
format!("{}{:?}", x, y)
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "+")]
|
#[rhai_fn(name = "+")]
|
||||||
#[inline]
|
|
||||||
pub fn prepend(x: &mut Array, y: &str) -> String {
|
pub fn prepend(x: &mut Array, y: &str) -> String {
|
||||||
format!("{:?}{}", x, y)
|
format!("{:?}{}", x, y)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn split(s: &str, delimiter: ImmutableString) -> Array {
|
pub fn split(s: &str, delimiter: ImmutableString) -> Array {
|
||||||
s.split(delimiter.as_str())
|
s.split(delimiter.as_str())
|
||||||
.map(Into::<Dynamic>::into)
|
.map(Into::<Dynamic>::into)
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "split")]
|
#[rhai_fn(name = "split")]
|
||||||
#[inline(always)]
|
|
||||||
pub fn split_char(s: &str, delimiter: char) -> Array {
|
pub fn split_char(s: &str, delimiter: char) -> Array {
|
||||||
s.split(delimiter).map(Into::<Dynamic>::into).collect()
|
s.split(delimiter).map(Into::<Dynamic>::into).collect()
|
||||||
}
|
}
|
||||||
@ -355,12 +335,10 @@ mod string_functions {
|
|||||||
use crate::engine::Map;
|
use crate::engine::Map;
|
||||||
|
|
||||||
#[rhai_fn(name = "+")]
|
#[rhai_fn(name = "+")]
|
||||||
#[inline]
|
|
||||||
pub fn append(x: &str, y: Map) -> String {
|
pub fn append(x: &str, y: Map) -> String {
|
||||||
format!("{}#{:?}", x, y)
|
format!("{}#{:?}", x, y)
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "+")]
|
#[rhai_fn(name = "+")]
|
||||||
#[inline]
|
|
||||||
pub fn prepend(x: &mut Map, y: &str) -> String {
|
pub fn prepend(x: &mut Map, y: &str) -> String {
|
||||||
format!("#{:?}{}", x, y)
|
format!("#{:?}{}", x, y)
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,6 @@ def_package!(crate:BasicTimePackage:"Basic timing utilities.", lib, {
|
|||||||
|
|
||||||
#[export_module]
|
#[export_module]
|
||||||
mod time_functions {
|
mod time_functions {
|
||||||
#[inline(always)]
|
|
||||||
pub fn timestamp() -> Instant {
|
pub fn timestamp() -> Instant {
|
||||||
Instant::now()
|
Instant::now()
|
||||||
}
|
}
|
||||||
@ -35,7 +34,11 @@ mod time_functions {
|
|||||||
pub fn elapsed(timestamp: &mut Instant) -> Result<Dynamic, Box<EvalAltResult>> {
|
pub fn elapsed(timestamp: &mut Instant) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
{
|
{
|
||||||
Ok((timestamp.elapsed().as_secs_f64() as FLOAT).into())
|
if *timestamp > Instant::now() {
|
||||||
|
Err(make_arithmetic_err("Time-stamp is later than now"))
|
||||||
|
} else {
|
||||||
|
Ok((timestamp.elapsed().as_secs_f64() as FLOAT).into())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "no_float")]
|
#[cfg(feature = "no_float")]
|
||||||
@ -47,6 +50,8 @@ mod time_functions {
|
|||||||
"Integer overflow for timestamp.elapsed: {}",
|
"Integer overflow for timestamp.elapsed: {}",
|
||||||
seconds
|
seconds
|
||||||
)))
|
)))
|
||||||
|
} else if *timestamp > Instant::now() {
|
||||||
|
Err(make_arithmetic_err("Time-stamp is later than now"))
|
||||||
} else {
|
} else {
|
||||||
Ok((seconds as INT).into())
|
Ok((seconds as INT).into())
|
||||||
}
|
}
|
||||||
@ -212,32 +217,26 @@ mod time_functions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[rhai_fn(name = "==")]
|
#[rhai_fn(name = "==")]
|
||||||
#[inline(always)]
|
|
||||||
pub fn eq(x: Instant, y: Instant) -> bool {
|
pub fn eq(x: Instant, y: Instant) -> bool {
|
||||||
x == y
|
x == y
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "!=")]
|
#[rhai_fn(name = "!=")]
|
||||||
#[inline(always)]
|
|
||||||
pub fn ne(x: Instant, y: Instant) -> bool {
|
pub fn ne(x: Instant, y: Instant) -> bool {
|
||||||
x != y
|
x != y
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "<")]
|
#[rhai_fn(name = "<")]
|
||||||
#[inline(always)]
|
|
||||||
pub fn lt(x: Instant, y: Instant) -> bool {
|
pub fn lt(x: Instant, y: Instant) -> bool {
|
||||||
x < y
|
x < y
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "<=")]
|
#[rhai_fn(name = "<=")]
|
||||||
#[inline(always)]
|
|
||||||
pub fn lte(x: Instant, y: Instant) -> bool {
|
pub fn lte(x: Instant, y: Instant) -> bool {
|
||||||
x <= y
|
x <= y
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = ">")]
|
#[rhai_fn(name = ">")]
|
||||||
#[inline(always)]
|
|
||||||
pub fn gt(x: Instant, y: Instant) -> bool {
|
pub fn gt(x: Instant, y: Instant) -> bool {
|
||||||
x > y
|
x > y
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = ">=")]
|
#[rhai_fn(name = ">=")]
|
||||||
#[inline(always)]
|
|
||||||
pub fn gte(x: Instant, y: Instant) -> bool {
|
pub fn gte(x: Instant, y: Instant) -> bool {
|
||||||
x >= y
|
x >= y
|
||||||
}
|
}
|
||||||
|
272
src/parser.rs
272
src/parser.rs
@ -1,7 +1,6 @@
|
|||||||
//! Main module defining the lexer and parser.
|
//! Main module defining the lexer and parser.
|
||||||
|
|
||||||
use crate::any::{Dynamic, Union};
|
use crate::any::{Dynamic, Union};
|
||||||
use crate::calc_fn_hash;
|
|
||||||
use crate::engine::{Engine, KEYWORD_THIS, MARKER_BLOCK, MARKER_EXPR, MARKER_IDENT};
|
use crate::engine::{Engine, KEYWORD_THIS, MARKER_BLOCK, MARKER_EXPR, MARKER_IDENT};
|
||||||
use crate::error::{LexError, ParseError, ParseErrorType};
|
use crate::error::{LexError, ParseError, ParseErrorType};
|
||||||
use crate::fn_native::{FnPtr, Shared};
|
use crate::fn_native::{FnPtr, Shared};
|
||||||
@ -10,7 +9,8 @@ use crate::optimize::{optimize_into_ast, OptimizationLevel};
|
|||||||
use crate::scope::{EntryType as ScopeEntryType, Scope};
|
use crate::scope::{EntryType as ScopeEntryType, Scope};
|
||||||
use crate::syntax::FnCustomSyntaxEval;
|
use crate::syntax::FnCustomSyntaxEval;
|
||||||
use crate::token::{is_keyword_function, is_valid_identifier, Position, Token, TokenStream};
|
use crate::token::{is_keyword_function, is_valid_identifier, Position, Token, TokenStream};
|
||||||
use crate::utils::{StaticVec, StraightHasherBuilder};
|
use crate::utils::StraightHasherBuilder;
|
||||||
|
use crate::{calc_fn_hash, StaticVec};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
use crate::engine::Array;
|
use crate::engine::Array;
|
||||||
@ -31,7 +31,7 @@ use crate::stdlib::{
|
|||||||
hash::{Hash, Hasher},
|
hash::{Hash, Hasher},
|
||||||
iter::empty,
|
iter::empty,
|
||||||
num::NonZeroUsize,
|
num::NonZeroUsize,
|
||||||
ops::Add,
|
ops::{Add, AddAssign},
|
||||||
string::{String, ToString},
|
string::{String, ToString},
|
||||||
vec,
|
vec,
|
||||||
vec::Vec,
|
vec::Vec,
|
||||||
@ -85,12 +85,14 @@ pub struct AST(
|
|||||||
|
|
||||||
impl AST {
|
impl AST {
|
||||||
/// Create a new `AST`.
|
/// Create a new `AST`.
|
||||||
|
#[inline(always)]
|
||||||
pub fn new(statements: Vec<Stmt>, lib: Module) -> Self {
|
pub fn new(statements: Vec<Stmt>, lib: Module) -> Self {
|
||||||
Self(statements, lib)
|
Self(statements, lib)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the statements.
|
/// Get the statements.
|
||||||
#[cfg(not(feature = "internals"))]
|
#[cfg(not(feature = "internals"))]
|
||||||
|
#[inline(always)]
|
||||||
pub(crate) fn statements(&self) -> &[Stmt] {
|
pub(crate) fn statements(&self) -> &[Stmt] {
|
||||||
&self.0
|
&self.0
|
||||||
}
|
}
|
||||||
@ -99,17 +101,20 @@ impl AST {
|
|||||||
/// Exported under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
#[cfg(feature = "internals")]
|
#[cfg(feature = "internals")]
|
||||||
#[deprecated(note = "this method is volatile and may change")]
|
#[deprecated(note = "this method is volatile and may change")]
|
||||||
|
#[inline(always)]
|
||||||
pub fn statements(&self) -> &[Stmt] {
|
pub fn statements(&self) -> &[Stmt] {
|
||||||
&self.0
|
&self.0
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a mutable reference to the statements.
|
/// Get a mutable reference to the statements.
|
||||||
|
#[inline(always)]
|
||||||
pub(crate) fn statements_mut(&mut self) -> &mut Vec<Stmt> {
|
pub(crate) fn statements_mut(&mut self) -> &mut Vec<Stmt> {
|
||||||
&mut self.0
|
&mut self.0
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the internal `Module` containing all script-defined functions.
|
/// Get the internal `Module` containing all script-defined functions.
|
||||||
#[cfg(not(feature = "internals"))]
|
#[cfg(not(feature = "internals"))]
|
||||||
|
#[inline(always)]
|
||||||
pub(crate) fn lib(&self) -> &Module {
|
pub(crate) fn lib(&self) -> &Module {
|
||||||
&self.1
|
&self.1
|
||||||
}
|
}
|
||||||
@ -118,6 +123,7 @@ impl AST {
|
|||||||
/// Exported under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
#[cfg(feature = "internals")]
|
#[cfg(feature = "internals")]
|
||||||
#[deprecated(note = "this method is volatile and may change")]
|
#[deprecated(note = "this method is volatile and may change")]
|
||||||
|
#[inline(always)]
|
||||||
pub fn lib(&self) -> &Module {
|
pub fn lib(&self) -> &Module {
|
||||||
&self.1
|
&self.1
|
||||||
}
|
}
|
||||||
@ -126,6 +132,7 @@ impl AST {
|
|||||||
/// No statements are cloned.
|
/// No statements are cloned.
|
||||||
///
|
///
|
||||||
/// This operation is cheap because functions are shared.
|
/// This operation is cheap because functions are shared.
|
||||||
|
#[inline(always)]
|
||||||
pub fn clone_functions_only(&self) -> Self {
|
pub fn clone_functions_only(&self) -> Self {
|
||||||
self.clone_functions_only_filtered(|_, _, _| true)
|
self.clone_functions_only_filtered(|_, _, _| true)
|
||||||
}
|
}
|
||||||
@ -134,6 +141,7 @@ impl AST {
|
|||||||
/// No statements are cloned.
|
/// No statements are cloned.
|
||||||
///
|
///
|
||||||
/// This operation is cheap because functions are shared.
|
/// This operation is cheap because functions are shared.
|
||||||
|
#[inline(always)]
|
||||||
pub fn clone_functions_only_filtered(
|
pub fn clone_functions_only_filtered(
|
||||||
&self,
|
&self,
|
||||||
mut filter: impl FnMut(FnAccess, &str, usize) -> bool,
|
mut filter: impl FnMut(FnAccess, &str, usize) -> bool,
|
||||||
@ -145,6 +153,7 @@ impl AST {
|
|||||||
|
|
||||||
/// Clone the `AST`'s script statements into a new `AST`.
|
/// Clone the `AST`'s script statements into a new `AST`.
|
||||||
/// No functions are cloned.
|
/// No functions are cloned.
|
||||||
|
#[inline(always)]
|
||||||
pub fn clone_statements_only(&self) -> Self {
|
pub fn clone_statements_only(&self) -> Self {
|
||||||
Self(self.0.clone(), Default::default())
|
Self(self.0.clone(), Default::default())
|
||||||
}
|
}
|
||||||
@ -152,7 +161,7 @@ impl AST {
|
|||||||
/// Merge two `AST` into one. Both `AST`'s are untouched and a new, merged, version
|
/// Merge two `AST` into one. Both `AST`'s are untouched and a new, merged, version
|
||||||
/// is returned.
|
/// is returned.
|
||||||
///
|
///
|
||||||
/// The second `AST` is simply appended to the end of the first _without any processing_.
|
/// Statements in the second `AST` are simply appended to the end of the first _without any processing_.
|
||||||
/// Thus, the return value of the first `AST` (if using expression-statement syntax) is buried.
|
/// Thus, the return value of the first `AST` (if using expression-statement syntax) is buried.
|
||||||
/// Of course, if the first `AST` uses a `return` statement at the end, then
|
/// Of course, if the first `AST` uses a `return` statement at the end, then
|
||||||
/// the second `AST` will essentially be dead code.
|
/// the second `AST` will essentially be dead code.
|
||||||
@ -198,14 +207,68 @@ impl AST {
|
|||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
|
#[inline(always)]
|
||||||
pub fn merge(&self, other: &Self) -> Self {
|
pub fn merge(&self, other: &Self) -> Self {
|
||||||
self.merge_filtered(other, |_, _, _| true)
|
self.merge_filtered(other, |_, _, _| true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Combine one `AST` with another. The second `AST` is consumed.
|
||||||
|
///
|
||||||
|
/// Statements in the second `AST` are simply appended to the end of the first _without any processing_.
|
||||||
|
/// Thus, the return value of the first `AST` (if using expression-statement syntax) is buried.
|
||||||
|
/// Of course, if the first `AST` uses a `return` statement at the end, then
|
||||||
|
/// the second `AST` will essentially be dead code.
|
||||||
|
///
|
||||||
|
/// All script-defined functions in the second `AST` overwrite similarly-named functions
|
||||||
|
/// in the first `AST` with the same number of parameters.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
||||||
|
/// # #[cfg(not(feature = "no_function"))]
|
||||||
|
/// # {
|
||||||
|
/// use rhai::Engine;
|
||||||
|
///
|
||||||
|
/// let engine = Engine::new();
|
||||||
|
///
|
||||||
|
/// let mut ast1 = engine.compile(r#"
|
||||||
|
/// fn foo(x) { 42 + x }
|
||||||
|
/// foo(1)
|
||||||
|
/// "#)?;
|
||||||
|
///
|
||||||
|
/// let ast2 = engine.compile(r#"
|
||||||
|
/// fn foo(n) { "hello" + n }
|
||||||
|
/// foo("!")
|
||||||
|
/// "#)?;
|
||||||
|
///
|
||||||
|
/// ast1.combine(ast2); // Combine 'ast2' into 'ast1'
|
||||||
|
///
|
||||||
|
/// // Notice that using the '+=' operator also works:
|
||||||
|
/// // ast1 += ast2;
|
||||||
|
///
|
||||||
|
/// // 'ast1' is essentially:
|
||||||
|
/// //
|
||||||
|
/// // fn foo(n) { "hello" + n } // <- definition of first 'foo' is overwritten
|
||||||
|
/// // foo(1) // <- notice this will be "hello1" instead of 43,
|
||||||
|
/// // // but it is no longer the return value
|
||||||
|
/// // foo("!") // returns "hello!"
|
||||||
|
///
|
||||||
|
/// // Evaluate it
|
||||||
|
/// assert_eq!(engine.eval_ast::<String>(&ast1)?, "hello!");
|
||||||
|
/// # }
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn combine(&mut self, other: Self) -> &mut Self {
|
||||||
|
self.combine_filtered(other, |_, _, _| true)
|
||||||
|
}
|
||||||
|
|
||||||
/// Merge two `AST` into one. Both `AST`'s are untouched and a new, merged, version
|
/// Merge two `AST` into one. Both `AST`'s are untouched and a new, merged, version
|
||||||
/// is returned.
|
/// is returned.
|
||||||
///
|
///
|
||||||
/// The second `AST` is simply appended to the end of the first _without any processing_.
|
/// Statements in the second `AST` are simply appended to the end of the first _without any processing_.
|
||||||
/// Thus, the return value of the first `AST` (if using expression-statement syntax) is buried.
|
/// Thus, the return value of the first `AST` (if using expression-statement syntax) is buried.
|
||||||
/// Of course, if the first `AST` uses a `return` statement at the end, then
|
/// Of course, if the first `AST` uses a `return` statement at the end, then
|
||||||
/// the second `AST` will essentially be dead code.
|
/// the second `AST` will essentially be dead code.
|
||||||
@ -253,6 +316,7 @@ impl AST {
|
|||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
|
#[inline]
|
||||||
pub fn merge_filtered(
|
pub fn merge_filtered(
|
||||||
&self,
|
&self,
|
||||||
other: &Self,
|
other: &Self,
|
||||||
@ -277,6 +341,73 @@ impl AST {
|
|||||||
Self::new(ast, functions)
|
Self::new(ast, functions)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Combine one `AST` with another. The second `AST` is consumed.
|
||||||
|
///
|
||||||
|
/// Statements in the second `AST` are simply appended to the end of the first _without any processing_.
|
||||||
|
/// Thus, the return value of the first `AST` (if using expression-statement syntax) is buried.
|
||||||
|
/// Of course, if the first `AST` uses a `return` statement at the end, then
|
||||||
|
/// the second `AST` will essentially be dead code.
|
||||||
|
///
|
||||||
|
/// All script-defined functions in the second `AST` are first selected based on a filter
|
||||||
|
/// predicate, then overwrite similarly-named functions in the first `AST` with the
|
||||||
|
/// same number of parameters.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
||||||
|
/// # #[cfg(not(feature = "no_function"))]
|
||||||
|
/// # {
|
||||||
|
/// use rhai::Engine;
|
||||||
|
///
|
||||||
|
/// let engine = Engine::new();
|
||||||
|
///
|
||||||
|
/// let mut ast1 = engine.compile(r#"
|
||||||
|
/// fn foo(x) { 42 + x }
|
||||||
|
/// foo(1)
|
||||||
|
/// "#)?;
|
||||||
|
///
|
||||||
|
/// let ast2 = engine.compile(r#"
|
||||||
|
/// fn foo(n) { "hello" + n }
|
||||||
|
/// fn error() { 0 }
|
||||||
|
/// foo("!")
|
||||||
|
/// "#)?;
|
||||||
|
///
|
||||||
|
/// // Combine 'ast2', picking only 'error()' but not 'foo(_)', into 'ast1'
|
||||||
|
/// ast1.combine_filtered(ast2, |_, name, params| name == "error" && params == 0);
|
||||||
|
///
|
||||||
|
/// // 'ast1' is essentially:
|
||||||
|
/// //
|
||||||
|
/// // fn foo(n) { 42 + n } // <- definition of 'ast1::foo' is not overwritten
|
||||||
|
/// // // because 'ast2::foo' is filtered away
|
||||||
|
/// // foo(1) // <- notice this will be 43 instead of "hello1",
|
||||||
|
/// // // but it is no longer the return value
|
||||||
|
/// // fn error() { 0 } // <- this function passes the filter and is merged
|
||||||
|
/// // foo("!") // <- returns "42!"
|
||||||
|
///
|
||||||
|
/// // Evaluate it
|
||||||
|
/// assert_eq!(engine.eval_ast::<String>(&ast1)?, "42!");
|
||||||
|
/// # }
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn combine_filtered(
|
||||||
|
&mut self,
|
||||||
|
other: Self,
|
||||||
|
mut filter: impl FnMut(FnAccess, &str, usize) -> bool,
|
||||||
|
) -> &mut Self {
|
||||||
|
let Self(ref mut statements, ref mut functions) = self;
|
||||||
|
|
||||||
|
if !other.0.is_empty() {
|
||||||
|
statements.extend(other.0.into_iter());
|
||||||
|
}
|
||||||
|
|
||||||
|
functions.merge_filtered(&other.1, &mut filter);
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Filter out the functions, retaining only some based on a filter predicate.
|
/// Filter out the functions, retaining only some based on a filter predicate.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
@ -301,12 +432,14 @@ impl AST {
|
|||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
#[inline(always)]
|
||||||
pub fn retain_functions(&mut self, filter: impl FnMut(FnAccess, &str, usize) -> bool) {
|
pub fn retain_functions(&mut self, filter: impl FnMut(FnAccess, &str, usize) -> bool) {
|
||||||
self.1.retain_functions(filter);
|
self.1.retain_functions(filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Iterate through all functions
|
/// Iterate through all functions
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
#[inline(always)]
|
||||||
pub fn iter_functions<'a>(
|
pub fn iter_functions<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
) -> impl Iterator<Item = (FnAccess, &str, usize, Shared<ScriptFnDef>)> + 'a {
|
) -> impl Iterator<Item = (FnAccess, &str, usize, Shared<ScriptFnDef>)> + 'a {
|
||||||
@ -315,31 +448,43 @@ impl AST {
|
|||||||
|
|
||||||
/// Clear all function definitions in the `AST`.
|
/// Clear all function definitions in the `AST`.
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
#[inline(always)]
|
||||||
pub fn clear_functions(&mut self) {
|
pub fn clear_functions(&mut self) {
|
||||||
self.1 = Default::default();
|
self.1 = Default::default();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Clear all statements in the `AST`, leaving only function definitions.
|
/// Clear all statements in the `AST`, leaving only function definitions.
|
||||||
|
#[inline(always)]
|
||||||
pub fn clear_statements(&mut self) {
|
pub fn clear_statements(&mut self) {
|
||||||
self.0 = vec![];
|
self.0 = vec![];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Add<Self> for &AST {
|
impl<A: AsRef<AST>> Add<A> for &AST {
|
||||||
type Output = AST;
|
type Output = AST;
|
||||||
|
|
||||||
fn add(self, rhs: Self) -> Self::Output {
|
#[inline(always)]
|
||||||
self.merge(rhs)
|
fn add(self, rhs: A) -> Self::Output {
|
||||||
|
self.merge(rhs.as_ref())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<A: Into<AST>> AddAssign<A> for AST {
|
||||||
|
#[inline(always)]
|
||||||
|
fn add_assign(&mut self, rhs: A) {
|
||||||
|
self.combine(rhs.into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsRef<[Stmt]> for AST {
|
impl AsRef<[Stmt]> for AST {
|
||||||
|
#[inline(always)]
|
||||||
fn as_ref(&self) -> &[Stmt] {
|
fn as_ref(&self) -> &[Stmt] {
|
||||||
self.statements()
|
self.statements()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsRef<Module> for AST {
|
impl AsRef<Module> for AST {
|
||||||
|
#[inline(always)]
|
||||||
fn as_ref(&self) -> &Module {
|
fn as_ref(&self) -> &Module {
|
||||||
self.lib()
|
self.lib()
|
||||||
}
|
}
|
||||||
@ -355,6 +500,7 @@ pub enum FnAccess {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for FnAccess {
|
impl fmt::Display for FnAccess {
|
||||||
|
#[inline(always)]
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Self::Private => write!(f, "private"),
|
Self::Private => write!(f, "private"),
|
||||||
@ -365,6 +511,7 @@ impl fmt::Display for FnAccess {
|
|||||||
|
|
||||||
impl FnAccess {
|
impl FnAccess {
|
||||||
/// Is this access mode private?
|
/// Is this access mode private?
|
||||||
|
#[inline(always)]
|
||||||
pub fn is_private(self) -> bool {
|
pub fn is_private(self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Self::Public => false,
|
Self::Public => false,
|
||||||
@ -372,6 +519,7 @@ impl FnAccess {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Is this access mode public?
|
/// Is this access mode public?
|
||||||
|
#[inline(always)]
|
||||||
pub fn is_public(self) -> bool {
|
pub fn is_public(self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Self::Public => true,
|
Self::Public => true,
|
||||||
@ -463,6 +611,7 @@ struct ParseState<'e> {
|
|||||||
|
|
||||||
impl<'e> ParseState<'e> {
|
impl<'e> ParseState<'e> {
|
||||||
/// Create a new `ParseState`.
|
/// Create a new `ParseState`.
|
||||||
|
#[inline(always)]
|
||||||
pub fn new(
|
pub fn new(
|
||||||
engine: &'e Engine,
|
engine: &'e Engine,
|
||||||
#[cfg(not(feature = "unchecked"))] max_expr_depth: usize,
|
#[cfg(not(feature = "unchecked"))] max_expr_depth: usize,
|
||||||
@ -490,6 +639,7 @@ impl<'e> ParseState<'e> {
|
|||||||
/// The return value is the offset to be deducted from `Stack::len`,
|
/// The return value is the offset to be deducted from `Stack::len`,
|
||||||
/// i.e. the top element of the `ParseState` is offset 1.
|
/// i.e. the top element of the `ParseState` is offset 1.
|
||||||
/// Return `None` when the variable name is not found in the `stack`.
|
/// Return `None` when the variable name is not found in the `stack`.
|
||||||
|
#[inline]
|
||||||
fn access_var(&mut self, name: &str, _pos: Position) -> Option<NonZeroUsize> {
|
fn access_var(&mut self, name: &str, _pos: Position) -> Option<NonZeroUsize> {
|
||||||
let index = self
|
let index = self
|
||||||
.stack
|
.stack
|
||||||
@ -515,6 +665,7 @@ impl<'e> ParseState<'e> {
|
|||||||
/// The return value is the offset to be deducted from `Stack::len`,
|
/// The return value is the offset to be deducted from `Stack::len`,
|
||||||
/// i.e. the top element of the `ParseState` is offset 1.
|
/// i.e. the top element of the `ParseState` is offset 1.
|
||||||
/// Return `None` when the variable name is not found in the `ParseState`.
|
/// Return `None` when the variable name is not found in the `ParseState`.
|
||||||
|
#[inline(always)]
|
||||||
pub fn find_module(&self, name: &str) -> Option<NonZeroUsize> {
|
pub fn find_module(&self, name: &str) -> Option<NonZeroUsize> {
|
||||||
self.modules
|
self.modules
|
||||||
.iter()
|
.iter()
|
||||||
@ -548,6 +699,7 @@ struct ParseSettings {
|
|||||||
|
|
||||||
impl ParseSettings {
|
impl ParseSettings {
|
||||||
/// Create a new `ParseSettings` with one higher expression level.
|
/// Create a new `ParseSettings` with one higher expression level.
|
||||||
|
#[inline(always)]
|
||||||
pub fn level_up(&self) -> Self {
|
pub fn level_up(&self) -> Self {
|
||||||
Self {
|
Self {
|
||||||
level: self.level + 1,
|
level: self.level + 1,
|
||||||
@ -556,6 +708,7 @@ impl ParseSettings {
|
|||||||
}
|
}
|
||||||
/// Make sure that the current level of expression nesting is within the maximum limit.
|
/// Make sure that the current level of expression nesting is within the maximum limit.
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
|
#[inline]
|
||||||
pub fn ensure_level_within_max_limit(&self, limit: usize) -> Result<(), ParseError> {
|
pub fn ensure_level_within_max_limit(&self, limit: usize) -> Result<(), ParseError> {
|
||||||
if limit == 0 {
|
if limit == 0 {
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -587,7 +740,7 @@ pub enum Stmt {
|
|||||||
/// let id = expr
|
/// let id = expr
|
||||||
Let(Box<((String, Position), Option<Expr>, Position)>),
|
Let(Box<((String, Position), Option<Expr>, Position)>),
|
||||||
/// const id = expr
|
/// const id = expr
|
||||||
Const(Box<((String, Position), Expr, Position)>),
|
Const(Box<((String, Position), Option<Expr>, Position)>),
|
||||||
/// { stmt; ... }
|
/// { stmt; ... }
|
||||||
Block(Box<(StaticVec<Stmt>, Position)>),
|
Block(Box<(StaticVec<Stmt>, Position)>),
|
||||||
/// expr
|
/// expr
|
||||||
@ -615,6 +768,7 @@ pub enum Stmt {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Stmt {
|
impl Default for Stmt {
|
||||||
|
#[inline(always)]
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::Noop(Default::default())
|
Self::Noop(Default::default())
|
||||||
}
|
}
|
||||||
@ -737,12 +891,14 @@ impl Stmt {
|
|||||||
pub struct CustomExpr(pub StaticVec<Expr>, pub Shared<FnCustomSyntaxEval>);
|
pub struct CustomExpr(pub StaticVec<Expr>, pub Shared<FnCustomSyntaxEval>);
|
||||||
|
|
||||||
impl fmt::Debug for CustomExpr {
|
impl fmt::Debug for CustomExpr {
|
||||||
|
#[inline(always)]
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
fmt::Debug::fmt(&self.0, f)
|
fmt::Debug::fmt(&self.0, f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Hash for CustomExpr {
|
impl Hash for CustomExpr {
|
||||||
|
#[inline(always)]
|
||||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
self.0.hash(state);
|
self.0.hash(state);
|
||||||
}
|
}
|
||||||
@ -763,6 +919,7 @@ pub struct FloatWrapper(pub FLOAT, pub Position);
|
|||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
impl Hash for FloatWrapper {
|
impl Hash for FloatWrapper {
|
||||||
|
#[inline(always)]
|
||||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
state.write(&self.0.to_le_bytes());
|
state.write(&self.0.to_le_bytes());
|
||||||
self.1.hash(state);
|
self.1.hash(state);
|
||||||
@ -845,6 +1002,7 @@ pub enum Expr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Expr {
|
impl Default for Expr {
|
||||||
|
#[inline(always)]
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::Unit(Default::default())
|
Self::Unit(Default::default())
|
||||||
}
|
}
|
||||||
@ -1006,6 +1164,48 @@ impl Expr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Is the expression the unit `()` literal?
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn is_unit(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
Self::Unit(_) => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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.0.iter().all(Self::is_literal),
|
||||||
|
|
||||||
|
// An map literal is literal if all items are literals
|
||||||
|
Self::Map(x) => x.0.iter().map(|(_, expr)| expr).all(Self::is_literal),
|
||||||
|
|
||||||
|
// Check in expression
|
||||||
|
Self::In(x) => match (&x.0, &x.1) {
|
||||||
|
(Self::StringConstant(_), Self::StringConstant(_))
|
||||||
|
| (Self::CharConstant(_), Self::StringConstant(_)) => true,
|
||||||
|
_ => false,
|
||||||
|
},
|
||||||
|
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Is the expression a constant?
|
/// Is the expression a constant?
|
||||||
pub fn is_constant(&self) -> bool {
|
pub fn is_constant(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
@ -1092,6 +1292,7 @@ impl Expr {
|
|||||||
|
|
||||||
/// Convert a `Variable` into a `Property`. All other variants are untouched.
|
/// Convert a `Variable` into a `Property`. All other variants are untouched.
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
#[inline]
|
||||||
pub(crate) fn into_property(self) -> Self {
|
pub(crate) fn into_property(self) -> Self {
|
||||||
match self {
|
match self {
|
||||||
Self::Variable(x) if x.1.is_none() => {
|
Self::Variable(x) if x.1.is_none() => {
|
||||||
@ -2684,45 +2885,23 @@ fn parse_let(
|
|||||||
};
|
};
|
||||||
|
|
||||||
// let name = ...
|
// let name = ...
|
||||||
if match_token(input, Token::Equals)? {
|
let init_value = if match_token(input, Token::Equals)? {
|
||||||
// let name = expr
|
// let name = expr
|
||||||
let init_value = parse_expr(input, state, lib, settings.level_up())?;
|
Some(parse_expr(input, state, lib, settings.level_up())?)
|
||||||
|
|
||||||
match var_type {
|
|
||||||
// let name = expr
|
|
||||||
ScopeEntryType::Normal => {
|
|
||||||
state.stack.push((name.clone(), ScopeEntryType::Normal));
|
|
||||||
Ok(Stmt::Let(Box::new((
|
|
||||||
(name, pos),
|
|
||||||
Some(init_value),
|
|
||||||
token_pos,
|
|
||||||
))))
|
|
||||||
}
|
|
||||||
// const name = { expr:constant }
|
|
||||||
ScopeEntryType::Constant if init_value.is_constant() => {
|
|
||||||
state.stack.push((name.clone(), ScopeEntryType::Constant));
|
|
||||||
Ok(Stmt::Const(Box::new(((name, pos), init_value, token_pos))))
|
|
||||||
}
|
|
||||||
// const name = expr: error
|
|
||||||
ScopeEntryType::Constant => {
|
|
||||||
Err(PERR::ForbiddenConstantExpr(name).into_err(init_value.position()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// let name
|
None
|
||||||
match var_type {
|
};
|
||||||
ScopeEntryType::Normal => {
|
|
||||||
state.stack.push((name.clone(), ScopeEntryType::Normal));
|
match var_type {
|
||||||
Ok(Stmt::Let(Box::new(((name, pos), None, token_pos))))
|
// let name = expr
|
||||||
}
|
ScopeEntryType::Normal => {
|
||||||
ScopeEntryType::Constant => {
|
state.stack.push((name.clone(), ScopeEntryType::Normal));
|
||||||
state.stack.push((name.clone(), ScopeEntryType::Constant));
|
Ok(Stmt::Let(Box::new(((name, pos), init_value, token_pos))))
|
||||||
Ok(Stmt::Const(Box::new((
|
}
|
||||||
(name, pos),
|
// const name = { expr:constant }
|
||||||
Expr::Unit(pos),
|
ScopeEntryType::Constant => {
|
||||||
token_pos,
|
state.stack.push((name.clone(), ScopeEntryType::Constant));
|
||||||
))))
|
Ok(Stmt::Const(Box::new(((name, pos), init_value, token_pos))))
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3487,6 +3666,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Run the parser on an input stream, returning an AST.
|
/// Run the parser on an input stream, returning an AST.
|
||||||
|
#[inline(always)]
|
||||||
pub(crate) fn parse(
|
pub(crate) fn parse(
|
||||||
&self,
|
&self,
|
||||||
input: &mut TokenStream,
|
input: &mut TokenStream,
|
||||||
|
@ -252,6 +252,7 @@ impl fmt::Display for EvalAltResult {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<T: AsRef<str>> From<T> for Box<EvalAltResult> {
|
impl<T: AsRef<str>> From<T> for Box<EvalAltResult> {
|
||||||
|
#[inline(always)]
|
||||||
fn from(err: T) -> Self {
|
fn from(err: T) -> Self {
|
||||||
Box::new(EvalAltResult::ErrorRuntime(
|
Box::new(EvalAltResult::ErrorRuntime(
|
||||||
err.as_ref().to_string(),
|
err.as_ref().to_string(),
|
||||||
@ -337,6 +338,7 @@ impl EvalAltResult {
|
|||||||
|
|
||||||
/// Consume the current `EvalAltResult` and return a new one with the specified `Position`
|
/// Consume the current `EvalAltResult` and return a new one with the specified `Position`
|
||||||
/// if the current position is `Position::None`.
|
/// if the current position is `Position::None`.
|
||||||
|
#[inline(always)]
|
||||||
pub(crate) fn new_position(mut self: Box<Self>, new_position: Position) -> Box<Self> {
|
pub(crate) fn new_position(mut self: Box<Self>, new_position: Position) -> Box<Self> {
|
||||||
if self.position().is_none() {
|
if self.position().is_none() {
|
||||||
self.set_position(new_position);
|
self.set_position(new_position);
|
||||||
@ -346,6 +348,7 @@ impl EvalAltResult {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<T> From<EvalAltResult> for Result<T, Box<EvalAltResult>> {
|
impl<T> From<EvalAltResult> for Result<T, Box<EvalAltResult>> {
|
||||||
|
#[inline(always)]
|
||||||
fn from(err: EvalAltResult) -> Self {
|
fn from(err: EvalAltResult) -> Self {
|
||||||
Err(err.into())
|
Err(err.into())
|
||||||
}
|
}
|
||||||
|
56
src/scope.rs
56
src/scope.rs
@ -15,6 +15,17 @@ pub enum EntryType {
|
|||||||
Constant,
|
Constant,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl EntryType {
|
||||||
|
/// Is this entry constant?
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn is_constant(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
Self::Normal => false,
|
||||||
|
Self::Constant => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// An entry in the Scope.
|
/// An entry in the Scope.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Entry<'a> {
|
pub struct Entry<'a> {
|
||||||
@ -74,6 +85,7 @@ impl<'a> Scope<'a> {
|
|||||||
/// my_scope.push("x", 42_i64);
|
/// my_scope.push("x", 42_i64);
|
||||||
/// assert_eq!(my_scope.get_value::<i64>("x").unwrap(), 42);
|
/// assert_eq!(my_scope.get_value::<i64>("x").unwrap(), 42);
|
||||||
/// ```
|
/// ```
|
||||||
|
#[inline(always)]
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Default::default()
|
Default::default()
|
||||||
}
|
}
|
||||||
@ -97,6 +109,7 @@ impl<'a> Scope<'a> {
|
|||||||
/// assert_eq!(my_scope.len(), 0);
|
/// assert_eq!(my_scope.len(), 0);
|
||||||
/// assert!(my_scope.is_empty());
|
/// assert!(my_scope.is_empty());
|
||||||
/// ```
|
/// ```
|
||||||
|
#[inline(always)]
|
||||||
pub fn clear(&mut self) -> &mut Self {
|
pub fn clear(&mut self) -> &mut Self {
|
||||||
self.0.clear();
|
self.0.clear();
|
||||||
self
|
self
|
||||||
@ -115,6 +128,7 @@ impl<'a> Scope<'a> {
|
|||||||
/// my_scope.push("x", 42_i64);
|
/// my_scope.push("x", 42_i64);
|
||||||
/// assert_eq!(my_scope.len(), 1);
|
/// assert_eq!(my_scope.len(), 1);
|
||||||
/// ```
|
/// ```
|
||||||
|
#[inline(always)]
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
self.0.len()
|
self.0.len()
|
||||||
}
|
}
|
||||||
@ -132,6 +146,7 @@ impl<'a> Scope<'a> {
|
|||||||
/// my_scope.push("x", 42_i64);
|
/// my_scope.push("x", 42_i64);
|
||||||
/// assert!(!my_scope.is_empty());
|
/// assert!(!my_scope.is_empty());
|
||||||
/// ```
|
/// ```
|
||||||
|
#[inline(always)]
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.0.len() == 0
|
self.0.len() == 0
|
||||||
}
|
}
|
||||||
@ -148,6 +163,7 @@ impl<'a> Scope<'a> {
|
|||||||
/// my_scope.push("x", 42_i64);
|
/// my_scope.push("x", 42_i64);
|
||||||
/// assert_eq!(my_scope.get_value::<i64>("x").unwrap(), 42);
|
/// assert_eq!(my_scope.get_value::<i64>("x").unwrap(), 42);
|
||||||
/// ```
|
/// ```
|
||||||
|
#[inline(always)]
|
||||||
pub fn push<K: Into<Cow<'a, str>>, T: Variant + Clone>(
|
pub fn push<K: Into<Cow<'a, str>>, T: Variant + Clone>(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: K,
|
name: K,
|
||||||
@ -168,6 +184,7 @@ impl<'a> Scope<'a> {
|
|||||||
/// my_scope.push_dynamic("x", Dynamic::from(42_i64));
|
/// my_scope.push_dynamic("x", Dynamic::from(42_i64));
|
||||||
/// assert_eq!(my_scope.get_value::<i64>("x").unwrap(), 42);
|
/// assert_eq!(my_scope.get_value::<i64>("x").unwrap(), 42);
|
||||||
/// ```
|
/// ```
|
||||||
|
#[inline(always)]
|
||||||
pub fn push_dynamic<K: Into<Cow<'a, str>>>(&mut self, name: K, value: Dynamic) -> &mut Self {
|
pub fn push_dynamic<K: Into<Cow<'a, str>>>(&mut self, name: K, value: Dynamic) -> &mut Self {
|
||||||
self.push_dynamic_value(name, EntryType::Normal, value, false)
|
self.push_dynamic_value(name, EntryType::Normal, value, false)
|
||||||
}
|
}
|
||||||
@ -190,6 +207,7 @@ impl<'a> Scope<'a> {
|
|||||||
/// my_scope.push_constant("x", 42_i64);
|
/// my_scope.push_constant("x", 42_i64);
|
||||||
/// assert_eq!(my_scope.get_value::<i64>("x").unwrap(), 42);
|
/// assert_eq!(my_scope.get_value::<i64>("x").unwrap(), 42);
|
||||||
/// ```
|
/// ```
|
||||||
|
#[inline(always)]
|
||||||
pub fn push_constant<K: Into<Cow<'a, str>>, T: Variant + Clone>(
|
pub fn push_constant<K: Into<Cow<'a, str>>, T: Variant + Clone>(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: K,
|
name: K,
|
||||||
@ -217,6 +235,7 @@ impl<'a> Scope<'a> {
|
|||||||
/// my_scope.push_constant_dynamic("x", Dynamic::from(42_i64));
|
/// my_scope.push_constant_dynamic("x", Dynamic::from(42_i64));
|
||||||
/// assert_eq!(my_scope.get_value::<i64>("x").unwrap(), 42);
|
/// assert_eq!(my_scope.get_value::<i64>("x").unwrap(), 42);
|
||||||
/// ```
|
/// ```
|
||||||
|
#[inline(always)]
|
||||||
pub fn push_constant_dynamic<K: Into<Cow<'a, str>>>(
|
pub fn push_constant_dynamic<K: Into<Cow<'a, str>>>(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: K,
|
name: K,
|
||||||
@ -226,6 +245,7 @@ impl<'a> Scope<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Add (push) a new entry with a `Dynamic` value to the Scope.
|
/// Add (push) a new entry with a `Dynamic` value to the Scope.
|
||||||
|
#[inline]
|
||||||
pub(crate) fn push_dynamic_value<K: Into<Cow<'a, str>>>(
|
pub(crate) fn push_dynamic_value<K: Into<Cow<'a, str>>>(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: K,
|
name: K,
|
||||||
@ -276,6 +296,7 @@ impl<'a> Scope<'a> {
|
|||||||
/// assert_eq!(my_scope.len(), 0);
|
/// assert_eq!(my_scope.len(), 0);
|
||||||
/// assert!(my_scope.is_empty());
|
/// assert!(my_scope.is_empty());
|
||||||
/// ```
|
/// ```
|
||||||
|
#[inline(always)]
|
||||||
pub fn rewind(&mut self, size: usize) -> &mut Self {
|
pub fn rewind(&mut self, size: usize) -> &mut Self {
|
||||||
self.0.truncate(size);
|
self.0.truncate(size);
|
||||||
self
|
self
|
||||||
@ -294,6 +315,7 @@ impl<'a> Scope<'a> {
|
|||||||
/// assert!(my_scope.contains("x"));
|
/// assert!(my_scope.contains("x"));
|
||||||
/// assert!(!my_scope.contains("y"));
|
/// assert!(!my_scope.contains("y"));
|
||||||
/// ```
|
/// ```
|
||||||
|
#[inline(always)]
|
||||||
pub fn contains(&self, name: &str) -> bool {
|
pub fn contains(&self, name: &str) -> bool {
|
||||||
self.0
|
self.0
|
||||||
.iter()
|
.iter()
|
||||||
@ -302,6 +324,7 @@ impl<'a> Scope<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Find an entry in the Scope, starting from the last.
|
/// Find an entry in the Scope, starting from the last.
|
||||||
|
#[inline(always)]
|
||||||
pub(crate) fn get_index(&self, name: &str) -> Option<(usize, EntryType)> {
|
pub(crate) fn get_index(&self, name: &str) -> Option<(usize, EntryType)> {
|
||||||
self.0
|
self.0
|
||||||
.iter()
|
.iter()
|
||||||
@ -317,6 +340,7 @@ impl<'a> Scope<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get an entry in the Scope, starting from the last.
|
/// Get an entry in the Scope, starting from the last.
|
||||||
|
#[inline(always)]
|
||||||
pub(crate) fn get_entry(&self, name: &str) -> Option<&Entry> {
|
pub(crate) fn get_entry(&self, name: &str) -> Option<&Entry> {
|
||||||
self.0
|
self.0
|
||||||
.iter()
|
.iter()
|
||||||
@ -336,6 +360,7 @@ impl<'a> Scope<'a> {
|
|||||||
/// my_scope.push("x", 42_i64);
|
/// my_scope.push("x", 42_i64);
|
||||||
/// assert_eq!(my_scope.get_value::<i64>("x").unwrap(), 42);
|
/// assert_eq!(my_scope.get_value::<i64>("x").unwrap(), 42);
|
||||||
/// ```
|
/// ```
|
||||||
|
#[inline(always)]
|
||||||
pub fn get_value<T: Variant + Clone>(&self, name: &str) -> Option<T> {
|
pub fn get_value<T: Variant + Clone>(&self, name: &str) -> Option<T> {
|
||||||
self.get_entry(name)
|
self.get_entry(name)
|
||||||
.and_then(|Entry { value, .. }| value.flatten_clone().try_cast())
|
.and_then(|Entry { value, .. }| value.flatten_clone().try_cast())
|
||||||
@ -362,6 +387,7 @@ impl<'a> Scope<'a> {
|
|||||||
/// my_scope.set_value("x", 0_i64);
|
/// my_scope.set_value("x", 0_i64);
|
||||||
/// assert_eq!(my_scope.get_value::<i64>("x").unwrap(), 0);
|
/// assert_eq!(my_scope.get_value::<i64>("x").unwrap(), 0);
|
||||||
/// ```
|
/// ```
|
||||||
|
#[inline(always)]
|
||||||
pub fn set_value<T: Variant + Clone>(&mut self, name: &'a str, value: T) -> &mut Self {
|
pub fn set_value<T: Variant + Clone>(&mut self, name: &'a str, value: T) -> &mut Self {
|
||||||
match self.get_index(name) {
|
match self.get_index(name) {
|
||||||
None => {
|
None => {
|
||||||
@ -376,6 +402,7 @@ impl<'a> Scope<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get a mutable reference to an entry in the Scope.
|
/// Get a mutable reference to an entry in the Scope.
|
||||||
|
#[inline(always)]
|
||||||
pub(crate) fn get_mut(&mut self, index: usize) -> (&mut Dynamic, EntryType) {
|
pub(crate) fn get_mut(&mut self, index: usize) -> (&mut Dynamic, EntryType) {
|
||||||
let entry = self.0.get_mut(index).expect("invalid index in Scope");
|
let entry = self.0.get_mut(index).expect("invalid index in Scope");
|
||||||
(&mut entry.value, entry.typ)
|
(&mut entry.value, entry.typ)
|
||||||
@ -383,6 +410,7 @@ impl<'a> Scope<'a> {
|
|||||||
|
|
||||||
/// Update the access type of an entry in the Scope.
|
/// Update the access type of an entry in the Scope.
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
|
#[inline(always)]
|
||||||
pub(crate) fn set_entry_alias(&mut self, index: usize, alias: String) -> &mut Self {
|
pub(crate) fn set_entry_alias(&mut self, index: usize, alias: String) -> &mut Self {
|
||||||
let entry = self.0.get_mut(index).expect("invalid index in Scope");
|
let entry = self.0.get_mut(index).expect("invalid index in Scope");
|
||||||
entry.alias = Some(Box::new(alias));
|
entry.alias = Some(Box::new(alias));
|
||||||
@ -391,6 +419,7 @@ impl<'a> Scope<'a> {
|
|||||||
|
|
||||||
/// Clone the Scope, keeping only the last instances of each variable name.
|
/// Clone the Scope, keeping only the last instances of each variable name.
|
||||||
/// Shadowed variables are omitted in the copy.
|
/// Shadowed variables are omitted in the copy.
|
||||||
|
#[inline]
|
||||||
pub(crate) fn flatten_clone(&self) -> Self {
|
pub(crate) fn flatten_clone(&self) -> Self {
|
||||||
let mut entries: Vec<Entry> = Default::default();
|
let mut entries: Vec<Entry> = Default::default();
|
||||||
|
|
||||||
@ -408,11 +437,13 @@ impl<'a> Scope<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get an iterator to entries in the Scope.
|
/// Get an iterator to entries in the Scope.
|
||||||
|
#[inline(always)]
|
||||||
pub(crate) fn into_iter(self) -> impl Iterator<Item = Entry<'a>> {
|
pub(crate) fn into_iter(self) -> impl Iterator<Item = Entry<'a>> {
|
||||||
self.0.into_iter()
|
self.0.into_iter()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get an iterator to entries in the Scope in reverse order.
|
/// Get an iterator to entries in the Scope in reverse order.
|
||||||
|
#[inline(always)]
|
||||||
pub(crate) fn to_iter(&self) -> impl Iterator<Item = &Entry> {
|
pub(crate) fn to_iter(&self) -> impl Iterator<Item = &Entry> {
|
||||||
self.0.iter().rev() // Always search a Scope in reverse order
|
self.0.iter().rev() // Always search a Scope in reverse order
|
||||||
}
|
}
|
||||||
@ -427,33 +458,40 @@ impl<'a> Scope<'a> {
|
|||||||
/// let mut my_scope = Scope::new();
|
/// let mut my_scope = Scope::new();
|
||||||
///
|
///
|
||||||
/// my_scope.push("x", 42_i64);
|
/// my_scope.push("x", 42_i64);
|
||||||
/// my_scope.push("foo", "hello".to_string());
|
/// my_scope.push_constant("foo", "hello".to_string());
|
||||||
///
|
///
|
||||||
/// let mut iter = my_scope.iter();
|
/// let mut iter = my_scope.iter();
|
||||||
///
|
///
|
||||||
/// let (name, value) = iter.next().unwrap();
|
/// let (name, constant, value) = iter.next().unwrap();
|
||||||
/// assert_eq!(name, "x");
|
/// assert_eq!(name, "x");
|
||||||
|
/// assert!(!constant);
|
||||||
/// assert_eq!(value.cast::<i64>(), 42);
|
/// assert_eq!(value.cast::<i64>(), 42);
|
||||||
///
|
///
|
||||||
/// let (name, value) = iter.next().unwrap();
|
/// let (name, constant, value) = iter.next().unwrap();
|
||||||
/// assert_eq!(name, "foo");
|
/// assert_eq!(name, "foo");
|
||||||
|
/// assert!(constant);
|
||||||
/// assert_eq!(value.cast::<String>(), "hello");
|
/// assert_eq!(value.cast::<String>(), "hello");
|
||||||
/// ```
|
/// ```
|
||||||
pub fn iter(&self) -> impl Iterator<Item = (&str, Dynamic)> {
|
#[inline(always)]
|
||||||
|
pub fn iter(&self) -> impl Iterator<Item = (&str, bool, Dynamic)> {
|
||||||
self.iter_raw()
|
self.iter_raw()
|
||||||
.map(|(name, value)| (name, value.flatten_clone()))
|
.map(|(name, constant, value)| (name, constant, value.flatten_clone()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get an iterator to entries in the Scope.
|
/// Get an iterator to entries in the Scope.
|
||||||
/// Shared values are not expanded.
|
/// Shared values are not expanded.
|
||||||
pub fn iter_raw(&self) -> impl Iterator<Item = (&str, &Dynamic)> {
|
#[inline(always)]
|
||||||
self.0
|
pub fn iter_raw(&self) -> impl Iterator<Item = (&str, bool, &Dynamic)> {
|
||||||
.iter()
|
self.0.iter().map(
|
||||||
.map(|Entry { name, value, .. }| (name.as_ref(), value))
|
|Entry {
|
||||||
|
name, typ, value, ..
|
||||||
|
}| { (name.as_ref(), typ.is_constant(), value) },
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, K: Into<Cow<'a, str>>> iter::Extend<(K, EntryType, Dynamic)> for Scope<'a> {
|
impl<'a, K: Into<Cow<'a, str>>> iter::Extend<(K, EntryType, Dynamic)> for Scope<'a> {
|
||||||
|
#[inline(always)]
|
||||||
fn extend<T: IntoIterator<Item = (K, EntryType, Dynamic)>>(&mut self, iter: T) {
|
fn extend<T: IntoIterator<Item = (K, EntryType, Dynamic)>>(&mut self, iter: T) {
|
||||||
self.0
|
self.0
|
||||||
.extend(iter.into_iter().map(|(name, typ, value)| Entry {
|
.extend(iter.into_iter().map(|(name, typ, value)| Entry {
|
||||||
|
@ -23,14 +23,6 @@ use serde::de::{EnumAccess, VariantAccess};
|
|||||||
|
|
||||||
use crate::stdlib::{any::type_name, boxed::Box, fmt, string::ToString};
|
use crate::stdlib::{any::type_name, boxed::Box, fmt, string::ToString};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_std"))]
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
use crate::stdlib::time::Instant;
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_std"))]
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
|
||||||
use instant::Instant;
|
|
||||||
|
|
||||||
/// Deserializer for `Dynamic` which is kept as a reference.
|
/// Deserializer for `Dynamic` which is kept as a reference.
|
||||||
///
|
///
|
||||||
/// The reference is necessary because the deserialized type may hold references
|
/// The reference is necessary because the deserialized type may hold references
|
||||||
@ -86,7 +78,7 @@ impl<'de> DynamicDeserializer<'de> {
|
|||||||
/// # #[cfg(not(feature = "no_object"))]
|
/// # #[cfg(not(feature = "no_object"))]
|
||||||
/// # {
|
/// # {
|
||||||
/// use rhai::{Dynamic, Array, Map, INT};
|
/// use rhai::{Dynamic, Array, Map, INT};
|
||||||
/// use rhai::de::from_dynamic;
|
/// use rhai::serde::from_dynamic;
|
||||||
/// use serde::Deserialize;
|
/// use serde::Deserialize;
|
||||||
///
|
///
|
||||||
/// #[derive(Debug, Deserialize, PartialEq)]
|
/// #[derive(Debug, Deserialize, PartialEq)]
|
@ -55,7 +55,7 @@ impl DynamicSerializer {
|
|||||||
/// # #[cfg(not(feature = "no_float"))]
|
/// # #[cfg(not(feature = "no_float"))]
|
||||||
/// # {
|
/// # {
|
||||||
/// use rhai::{Dynamic, Array, Map, INT};
|
/// use rhai::{Dynamic, Array, Map, INT};
|
||||||
/// use rhai::ser::to_dynamic;
|
/// use rhai::serde::to_dynamic;
|
||||||
/// use serde::Serialize;
|
/// use serde::Serialize;
|
||||||
///
|
///
|
||||||
/// #[derive(Debug, serde::Serialize, PartialEq)]
|
/// #[derive(Debug, serde::Serialize, PartialEq)]
|
@ -21,6 +21,7 @@ impl Engine {
|
|||||||
///
|
///
|
||||||
/// When searching for functions, packages loaded later are preferred.
|
/// When searching for functions, packages loaded later are preferred.
|
||||||
/// In other words, loaded packages are searched in reverse order.
|
/// In other words, loaded packages are searched in reverse order.
|
||||||
|
#[inline(always)]
|
||||||
pub fn load_package(&mut self, package: impl Into<PackageLibrary>) -> &mut Self {
|
pub fn load_package(&mut self, package: impl Into<PackageLibrary>) -> &mut Self {
|
||||||
// Push the package to the top - packages are searched in reverse order
|
// Push the package to the top - packages are searched in reverse order
|
||||||
self.packages.push(package.into());
|
self.packages.push(package.into());
|
||||||
@ -31,6 +32,7 @@ impl Engine {
|
|||||||
///
|
///
|
||||||
/// Not available under the `no_optimize` feature.
|
/// Not available under the `no_optimize` feature.
|
||||||
#[cfg(not(feature = "no_optimize"))]
|
#[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: OptimizationLevel) -> &mut Self {
|
||||||
self.optimization_level = optimization_level;
|
self.optimization_level = optimization_level;
|
||||||
self
|
self
|
||||||
@ -41,6 +43,7 @@ impl Engine {
|
|||||||
///
|
///
|
||||||
/// Not available under the `no_optimize` feature.
|
/// Not available under the `no_optimize` feature.
|
||||||
#[cfg(not(feature = "no_optimize"))]
|
#[cfg(not(feature = "no_optimize"))]
|
||||||
|
#[inline(always)]
|
||||||
pub fn optimization_level(&self) -> OptimizationLevel {
|
pub fn optimization_level(&self) -> OptimizationLevel {
|
||||||
self.optimization_level
|
self.optimization_level
|
||||||
}
|
}
|
||||||
@ -48,6 +51,7 @@ impl Engine {
|
|||||||
/// Set the maximum levels of function calls allowed for a script in order to avoid
|
/// Set the maximum levels of function calls allowed for a script in order to avoid
|
||||||
/// infinite recursion and stack overflows.
|
/// infinite recursion and stack overflows.
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
|
#[inline(always)]
|
||||||
pub fn set_max_call_levels(&mut self, levels: usize) -> &mut Self {
|
pub fn set_max_call_levels(&mut self, levels: usize) -> &mut Self {
|
||||||
self.limits.max_call_stack_depth = levels;
|
self.limits.max_call_stack_depth = levels;
|
||||||
self
|
self
|
||||||
@ -55,6 +59,7 @@ impl Engine {
|
|||||||
|
|
||||||
/// The maximum levels of function calls allowed for a script.
|
/// The maximum levels of function calls allowed for a script.
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
|
#[inline(always)]
|
||||||
pub fn max_call_levels(&self) -> usize {
|
pub fn max_call_levels(&self) -> usize {
|
||||||
self.limits.max_call_stack_depth
|
self.limits.max_call_stack_depth
|
||||||
}
|
}
|
||||||
@ -62,6 +67,7 @@ impl Engine {
|
|||||||
/// Set the maximum number of operations allowed for a script to run to avoid
|
/// Set the maximum number of operations allowed for a script to run to avoid
|
||||||
/// consuming too much resources (0 for unlimited).
|
/// consuming too much resources (0 for unlimited).
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
|
#[inline(always)]
|
||||||
pub fn set_max_operations(&mut self, operations: u64) -> &mut Self {
|
pub fn set_max_operations(&mut self, operations: u64) -> &mut Self {
|
||||||
self.limits.max_operations = if operations == u64::MAX {
|
self.limits.max_operations = if operations == u64::MAX {
|
||||||
0
|
0
|
||||||
@ -73,12 +79,14 @@ impl Engine {
|
|||||||
|
|
||||||
/// The maximum number of operations allowed for a script to run (0 for unlimited).
|
/// The maximum number of operations allowed for a script to run (0 for unlimited).
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
|
#[inline(always)]
|
||||||
pub fn max_operations(&self) -> u64 {
|
pub fn max_operations(&self) -> u64 {
|
||||||
self.limits.max_operations
|
self.limits.max_operations
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the maximum number of imported modules allowed for a script.
|
/// Set the maximum number of imported modules allowed for a script.
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
|
#[inline(always)]
|
||||||
pub fn set_max_modules(&mut self, modules: usize) -> &mut Self {
|
pub fn set_max_modules(&mut self, modules: usize) -> &mut Self {
|
||||||
self.limits.max_modules = modules;
|
self.limits.max_modules = modules;
|
||||||
self
|
self
|
||||||
@ -86,12 +94,14 @@ impl Engine {
|
|||||||
|
|
||||||
/// The maximum number of imported modules allowed for a script.
|
/// The maximum number of imported modules allowed for a script.
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
|
#[inline(always)]
|
||||||
pub fn max_modules(&self) -> usize {
|
pub fn max_modules(&self) -> usize {
|
||||||
self.limits.max_modules
|
self.limits.max_modules
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the depth limits for expressions (0 for unlimited).
|
/// Set the depth limits for expressions (0 for unlimited).
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
|
#[inline(always)]
|
||||||
pub fn set_max_expr_depths(
|
pub fn set_max_expr_depths(
|
||||||
&mut self,
|
&mut self,
|
||||||
max_expr_depth: usize,
|
max_expr_depth: usize,
|
||||||
@ -112,18 +122,21 @@ impl Engine {
|
|||||||
|
|
||||||
/// The depth limit for expressions (0 for unlimited).
|
/// The depth limit for expressions (0 for unlimited).
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
|
#[inline(always)]
|
||||||
pub fn max_expr_depth(&self) -> usize {
|
pub fn max_expr_depth(&self) -> usize {
|
||||||
self.limits.max_expr_depth
|
self.limits.max_expr_depth
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The depth limit for expressions in functions (0 for unlimited).
|
/// The depth limit for expressions in functions (0 for unlimited).
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
|
#[inline(always)]
|
||||||
pub fn max_function_expr_depth(&self) -> usize {
|
pub fn max_function_expr_depth(&self) -> usize {
|
||||||
self.limits.max_function_expr_depth
|
self.limits.max_function_expr_depth
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the maximum length of strings (0 for unlimited).
|
/// Set the maximum length of strings (0 for unlimited).
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
|
#[inline(always)]
|
||||||
pub fn set_max_string_size(&mut self, max_size: usize) -> &mut Self {
|
pub fn set_max_string_size(&mut self, max_size: usize) -> &mut Self {
|
||||||
self.limits.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
|
self
|
||||||
@ -131,6 +144,7 @@ impl Engine {
|
|||||||
|
|
||||||
/// The maximum length of strings (0 for unlimited).
|
/// The maximum length of strings (0 for unlimited).
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
|
#[inline(always)]
|
||||||
pub fn max_string_size(&self) -> usize {
|
pub fn max_string_size(&self) -> usize {
|
||||||
self.limits.max_string_size
|
self.limits.max_string_size
|
||||||
}
|
}
|
||||||
@ -138,6 +152,7 @@ impl Engine {
|
|||||||
/// Set the maximum length of arrays (0 for unlimited).
|
/// Set the maximum length of arrays (0 for unlimited).
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
#[inline(always)]
|
||||||
pub fn set_max_array_size(&mut self, max_size: usize) -> &mut Self {
|
pub fn set_max_array_size(&mut self, max_size: usize) -> &mut Self {
|
||||||
self.limits.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
|
self
|
||||||
@ -146,6 +161,7 @@ impl Engine {
|
|||||||
/// The maximum length of arrays (0 for unlimited).
|
/// The maximum length of arrays (0 for unlimited).
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
#[inline(always)]
|
||||||
pub fn max_array_size(&self) -> usize {
|
pub fn max_array_size(&self) -> usize {
|
||||||
self.limits.max_array_size
|
self.limits.max_array_size
|
||||||
}
|
}
|
||||||
@ -153,6 +169,7 @@ impl Engine {
|
|||||||
/// Set the maximum length of object maps (0 for unlimited).
|
/// Set the maximum length of object maps (0 for unlimited).
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
#[inline(always)]
|
||||||
pub fn set_max_map_size(&mut self, max_size: usize) -> &mut Self {
|
pub fn set_max_map_size(&mut self, max_size: usize) -> &mut Self {
|
||||||
self.limits.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
|
self
|
||||||
@ -161,6 +178,7 @@ impl Engine {
|
|||||||
/// The maximum length of object maps (0 for unlimited).
|
/// The maximum length of object maps (0 for unlimited).
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
#[inline(always)]
|
||||||
pub fn max_map_size(&self) -> usize {
|
pub fn max_map_size(&self) -> usize {
|
||||||
self.limits.max_map_size
|
self.limits.max_map_size
|
||||||
}
|
}
|
||||||
@ -169,6 +187,7 @@ impl Engine {
|
|||||||
///
|
///
|
||||||
/// Not available under the `no_module` feature.
|
/// Not available under the `no_module` feature.
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
|
#[inline(always)]
|
||||||
pub fn set_module_resolver(
|
pub fn set_module_resolver(
|
||||||
&mut self,
|
&mut self,
|
||||||
resolver: Option<impl ModuleResolver + 'static>,
|
resolver: Option<impl ModuleResolver + 'static>,
|
||||||
@ -213,6 +232,7 @@ impl Engine {
|
|||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
|
#[inline(always)]
|
||||||
pub fn disable_symbol(&mut self, symbol: &str) -> &mut Self {
|
pub fn disable_symbol(&mut self, symbol: &str) -> &mut Self {
|
||||||
if self.disabled_symbols.is_none() {
|
if self.disabled_symbols.is_none() {
|
||||||
self.disabled_symbols = Some(Default::default());
|
self.disabled_symbols = Some(Default::default());
|
||||||
|
@ -9,7 +9,7 @@ use crate::parser::Expr;
|
|||||||
use crate::result::EvalAltResult;
|
use crate::result::EvalAltResult;
|
||||||
use crate::scope::Scope;
|
use crate::scope::Scope;
|
||||||
use crate::token::{is_valid_identifier, Position, Token};
|
use crate::token::{is_valid_identifier, Position, Token};
|
||||||
use crate::utils::StaticVec;
|
use crate::StaticVec;
|
||||||
|
|
||||||
use crate::stdlib::{
|
use crate::stdlib::{
|
||||||
boxed::Box,
|
boxed::Box,
|
||||||
@ -36,6 +36,7 @@ pub type FnCustomSyntaxEval = dyn Fn(&Engine, &mut EvalContext, &mut Scope, &[Ex
|
|||||||
pub struct Expression<'a>(&'a Expr);
|
pub struct Expression<'a>(&'a Expr);
|
||||||
|
|
||||||
impl<'a> From<&'a Expr> for Expression<'a> {
|
impl<'a> From<&'a Expr> for Expression<'a> {
|
||||||
|
#[inline(always)]
|
||||||
fn from(expr: &'a Expr) -> Self {
|
fn from(expr: &'a Expr) -> Self {
|
||||||
Self(expr)
|
Self(expr)
|
||||||
}
|
}
|
||||||
@ -43,6 +44,7 @@ impl<'a> From<&'a Expr> for Expression<'a> {
|
|||||||
|
|
||||||
impl Expression<'_> {
|
impl Expression<'_> {
|
||||||
/// If this expression is a variable name, return it. Otherwise `None`.
|
/// If this expression is a variable name, return it. Otherwise `None`.
|
||||||
|
#[inline(always)]
|
||||||
pub fn get_variable_name(&self) -> Option<&str> {
|
pub fn get_variable_name(&self) -> Option<&str> {
|
||||||
match self.0 {
|
match self.0 {
|
||||||
Expr::Variable(x) => Some((x.0).0.as_str()),
|
Expr::Variable(x) => Some((x.0).0.as_str()),
|
||||||
@ -50,10 +52,12 @@ impl Expression<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Get the expression.
|
/// Get the expression.
|
||||||
|
#[inline(always)]
|
||||||
pub(crate) fn expr(&self) -> &Expr {
|
pub(crate) fn expr(&self) -> &Expr {
|
||||||
&self.0
|
&self.0
|
||||||
}
|
}
|
||||||
/// Get the position of this expression.
|
/// Get the position of this expression.
|
||||||
|
#[inline(always)]
|
||||||
pub fn position(&self) -> Position {
|
pub fn position(&self) -> Position {
|
||||||
self.0.position()
|
self.0.position()
|
||||||
}
|
}
|
||||||
@ -67,6 +71,7 @@ pub struct CustomSyntax {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for CustomSyntax {
|
impl fmt::Debug for CustomSyntax {
|
||||||
|
#[inline(always)]
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
fmt::Debug::fmt(&self.segments, f)
|
fmt::Debug::fmt(&self.segments, f)
|
||||||
}
|
}
|
||||||
@ -191,6 +196,7 @@ impl Engine {
|
|||||||
/// ## WARNING - Low Level API
|
/// ## WARNING - Low Level API
|
||||||
///
|
///
|
||||||
/// This function is very low level. It evaluates an expression from an AST.
|
/// This function is very low level. It evaluates an expression from an AST.
|
||||||
|
#[inline(always)]
|
||||||
pub fn eval_expression_tree(
|
pub fn eval_expression_tree(
|
||||||
&self,
|
&self,
|
||||||
context: &mut EvalContext,
|
context: &mut EvalContext,
|
||||||
|
24
src/token.rs
24
src/token.rs
@ -10,7 +10,7 @@ use crate::engine::KEYWORD_IS_SHARED;
|
|||||||
|
|
||||||
use crate::error::LexError;
|
use crate::error::LexError;
|
||||||
use crate::parser::INT;
|
use crate::parser::INT;
|
||||||
use crate::utils::StaticVec;
|
use crate::StaticVec;
|
||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
use crate::parser::FLOAT;
|
use crate::parser::FLOAT;
|
||||||
@ -55,6 +55,7 @@ impl Position {
|
|||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// Panics if `line` is zero.
|
/// Panics if `line` is zero.
|
||||||
|
#[inline(always)]
|
||||||
pub fn new(line: u16, position: u16) -> Self {
|
pub fn new(line: u16, position: u16) -> Self {
|
||||||
assert!(line != 0, "line cannot be zero");
|
assert!(line != 0, "line cannot be zero");
|
||||||
|
|
||||||
@ -65,6 +66,7 @@ impl Position {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get the line number (1-based), or `None` if there is no position.
|
/// Get the line number (1-based), or `None` if there is no position.
|
||||||
|
#[inline(always)]
|
||||||
pub fn line(&self) -> Option<usize> {
|
pub fn line(&self) -> Option<usize> {
|
||||||
if self.is_none() {
|
if self.is_none() {
|
||||||
None
|
None
|
||||||
@ -74,6 +76,7 @@ impl Position {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get the character position (1-based), or `None` if at beginning of a line.
|
/// Get the character position (1-based), or `None` if at beginning of a line.
|
||||||
|
#[inline(always)]
|
||||||
pub fn position(&self) -> Option<usize> {
|
pub fn position(&self) -> Option<usize> {
|
||||||
if self.is_none() || self.pos == 0 {
|
if self.is_none() || self.pos == 0 {
|
||||||
None
|
None
|
||||||
@ -83,6 +86,7 @@ impl Position {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Advance by one character position.
|
/// Advance by one character position.
|
||||||
|
#[inline(always)]
|
||||||
pub(crate) fn advance(&mut self) {
|
pub(crate) fn advance(&mut self) {
|
||||||
assert!(!self.is_none(), "cannot advance Position::none");
|
assert!(!self.is_none(), "cannot advance Position::none");
|
||||||
|
|
||||||
@ -97,6 +101,7 @@ impl Position {
|
|||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// Panics if already at beginning of a line - cannot rewind to a previous line.
|
/// Panics if already at beginning of a line - cannot rewind to a previous line.
|
||||||
|
#[inline(always)]
|
||||||
pub(crate) fn rewind(&mut self) {
|
pub(crate) fn rewind(&mut self) {
|
||||||
assert!(!self.is_none(), "cannot rewind Position::none");
|
assert!(!self.is_none(), "cannot rewind Position::none");
|
||||||
assert!(self.pos > 0, "cannot rewind at position 0");
|
assert!(self.pos > 0, "cannot rewind at position 0");
|
||||||
@ -104,6 +109,7 @@ impl Position {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Advance to the next line.
|
/// Advance to the next line.
|
||||||
|
#[inline(always)]
|
||||||
pub(crate) fn new_line(&mut self) {
|
pub(crate) fn new_line(&mut self) {
|
||||||
assert!(!self.is_none(), "cannot advance Position::none");
|
assert!(!self.is_none(), "cannot advance Position::none");
|
||||||
|
|
||||||
@ -115,23 +121,27 @@ impl Position {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Create a `Position` representing no position.
|
/// Create a `Position` representing no position.
|
||||||
|
#[inline(always)]
|
||||||
pub fn none() -> Self {
|
pub fn none() -> Self {
|
||||||
Self { line: 0, pos: 0 }
|
Self { line: 0, pos: 0 }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Is there no `Position`?
|
/// Is there no `Position`?
|
||||||
|
#[inline(always)]
|
||||||
pub fn is_none(&self) -> bool {
|
pub fn is_none(&self) -> bool {
|
||||||
self.line == 0 && self.pos == 0
|
self.line == 0 && self.pos == 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Position {
|
impl Default for Position {
|
||||||
|
#[inline(always)]
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::new(1, 0)
|
Self::new(1, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Position {
|
impl fmt::Display for Position {
|
||||||
|
#[inline(always)]
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
if self.is_none() {
|
if self.is_none() {
|
||||||
write!(f, "none")
|
write!(f, "none")
|
||||||
@ -142,6 +152,7 @@ impl fmt::Display for Position {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for Position {
|
impl fmt::Debug for Position {
|
||||||
|
#[inline(always)]
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
write!(f, "{}:{}", self.line, self.pos)
|
write!(f, "{}:{}", self.line, self.pos)
|
||||||
}
|
}
|
||||||
@ -521,6 +532,7 @@ impl Token {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Is this token EOF?
|
// Is this token EOF?
|
||||||
|
#[inline(always)]
|
||||||
pub fn is_eof(&self) -> bool {
|
pub fn is_eof(&self) -> bool {
|
||||||
use Token::*;
|
use Token::*;
|
||||||
|
|
||||||
@ -677,6 +689,7 @@ impl Token {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Is this token a reserved symbol?
|
/// Is this token a reserved symbol?
|
||||||
|
#[inline(always)]
|
||||||
pub fn is_reserved(&self) -> bool {
|
pub fn is_reserved(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Self::Reserved(_) => true,
|
Self::Reserved(_) => true,
|
||||||
@ -695,6 +708,7 @@ impl Token {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Is this token a custom keyword?
|
/// Is this token a custom keyword?
|
||||||
|
#[inline(always)]
|
||||||
pub fn is_custom(&self) -> bool {
|
pub fn is_custom(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Self::Custom(_) => true,
|
Self::Custom(_) => true,
|
||||||
@ -704,6 +718,7 @@ impl Token {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl From<Token> for String {
|
impl From<Token> for String {
|
||||||
|
#[inline(always)]
|
||||||
fn from(token: Token) -> Self {
|
fn from(token: Token) -> Self {
|
||||||
token.syntax().into()
|
token.syntax().into()
|
||||||
}
|
}
|
||||||
@ -879,6 +894,7 @@ pub fn parse_string_literal(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Consume the next character.
|
/// Consume the next character.
|
||||||
|
#[inline(always)]
|
||||||
fn eat_next(stream: &mut impl InputStream, pos: &mut Position) -> Option<char> {
|
fn eat_next(stream: &mut impl InputStream, pos: &mut Position) -> Option<char> {
|
||||||
pos.advance();
|
pos.advance();
|
||||||
stream.get_next()
|
stream.get_next()
|
||||||
@ -937,6 +953,7 @@ fn scan_comment(
|
|||||||
/// ## WARNING
|
/// ## WARNING
|
||||||
///
|
///
|
||||||
/// This type is volatile and may change.
|
/// This type is volatile and may change.
|
||||||
|
#[inline]
|
||||||
pub fn get_next_token(
|
pub fn get_next_token(
|
||||||
stream: &mut impl InputStream,
|
stream: &mut impl InputStream,
|
||||||
state: &mut TokenizeState,
|
state: &mut TokenizeState,
|
||||||
@ -953,6 +970,7 @@ pub fn get_next_token(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Test if the given character is a hex character.
|
/// Test if the given character is a hex character.
|
||||||
|
#[inline(always)]
|
||||||
fn is_hex_char(c: char) -> bool {
|
fn is_hex_char(c: char) -> bool {
|
||||||
match c {
|
match c {
|
||||||
'a'..='f' => true,
|
'a'..='f' => true,
|
||||||
@ -963,6 +981,7 @@ fn is_hex_char(c: char) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Test if the given character is an octal character.
|
/// Test if the given character is an octal character.
|
||||||
|
#[inline(always)]
|
||||||
fn is_octal_char(c: char) -> bool {
|
fn is_octal_char(c: char) -> bool {
|
||||||
match c {
|
match c {
|
||||||
'0'..='7' => true,
|
'0'..='7' => true,
|
||||||
@ -971,6 +990,7 @@ fn is_octal_char(c: char) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Test if the given character is a binary character.
|
/// Test if the given character is a binary character.
|
||||||
|
#[inline(always)]
|
||||||
fn is_binary_char(c: char) -> bool {
|
fn is_binary_char(c: char) -> bool {
|
||||||
match c {
|
match c {
|
||||||
'0' | '1' => true,
|
'0' | '1' => true,
|
||||||
@ -1533,6 +1553,7 @@ pub struct MultiInputsStream<'a> {
|
|||||||
|
|
||||||
impl InputStream for MultiInputsStream<'_> {
|
impl InputStream for MultiInputsStream<'_> {
|
||||||
/// Buffer a character.
|
/// Buffer a character.
|
||||||
|
#[inline(always)]
|
||||||
fn unread(&mut self, ch: char) {
|
fn unread(&mut self, ch: char) {
|
||||||
self.buf = Some(ch);
|
self.buf = Some(ch);
|
||||||
}
|
}
|
||||||
@ -1692,6 +1713,7 @@ impl<'a> Iterator for TokenIterator<'a, '_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Tokenize an input text stream.
|
/// Tokenize an input text stream.
|
||||||
|
#[inline]
|
||||||
pub fn lex<'a, 'e>(
|
pub fn lex<'a, 'e>(
|
||||||
input: &'a [&'a str],
|
input: &'a [&'a str],
|
||||||
map: Option<Box<dyn Fn(Token) -> Token>>,
|
map: Option<Box<dyn Fn(Token) -> Token>>,
|
||||||
|
46
src/utils.rs
46
src/utils.rs
@ -21,8 +21,6 @@ use crate::stdlib::collections::hash_map::DefaultHasher;
|
|||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use ahash::AHasher;
|
use ahash::AHasher;
|
||||||
|
|
||||||
use smallvec::SmallVec;
|
|
||||||
|
|
||||||
/// A hasher that only takes one single `u64` and returns it as a hash key.
|
/// A hasher that only takes one single `u64` and returns it as a hash key.
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
@ -32,9 +30,11 @@ use smallvec::SmallVec;
|
|||||||
pub struct StraightHasher(u64);
|
pub struct StraightHasher(u64);
|
||||||
|
|
||||||
impl Hasher for StraightHasher {
|
impl Hasher for StraightHasher {
|
||||||
|
#[inline(always)]
|
||||||
fn finish(&self) -> u64 {
|
fn finish(&self) -> u64 {
|
||||||
self.0
|
self.0
|
||||||
}
|
}
|
||||||
|
#[inline(always)]
|
||||||
fn write(&mut self, bytes: &[u8]) {
|
fn write(&mut self, bytes: &[u8]) {
|
||||||
let mut key = [0_u8; 8];
|
let mut key = [0_u8; 8];
|
||||||
key.copy_from_slice(&bytes[..8]); // Panics if fewer than 8 bytes
|
key.copy_from_slice(&bytes[..8]); // Panics if fewer than 8 bytes
|
||||||
@ -44,6 +44,7 @@ impl Hasher for StraightHasher {
|
|||||||
|
|
||||||
impl StraightHasher {
|
impl StraightHasher {
|
||||||
/// Create a `StraightHasher`.
|
/// Create a `StraightHasher`.
|
||||||
|
#[inline(always)]
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self(0)
|
Self(0)
|
||||||
}
|
}
|
||||||
@ -56,6 +57,7 @@ pub struct StraightHasherBuilder;
|
|||||||
impl BuildHasher for StraightHasherBuilder {
|
impl BuildHasher for StraightHasherBuilder {
|
||||||
type Hasher = StraightHasher;
|
type Hasher = StraightHasher;
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
fn build_hasher(&self) -> Self::Hasher {
|
fn build_hasher(&self) -> Self::Hasher {
|
||||||
StraightHasher::new()
|
StraightHasher::new()
|
||||||
}
|
}
|
||||||
@ -89,11 +91,6 @@ pub fn calc_fn_hash<'a>(
|
|||||||
s.finish()
|
s.finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// _[INTERNALS]_ Alias to [`smallvec::SmallVec<[T; 4]>`](https://crates.io/crates/smallvec),
|
|
||||||
/// which is a specialized `Vec` backed by a small, fixed-size array when there are <= 4 items stored.
|
|
||||||
/// Exported under the `internals` feature only.
|
|
||||||
pub type StaticVec<T> = SmallVec<[T; 4]>;
|
|
||||||
|
|
||||||
/// The system immutable string type.
|
/// The system immutable string type.
|
||||||
///
|
///
|
||||||
/// An `ImmutableString` wraps an `Rc<String>` (or `Arc<String>` under the `sync` feature)
|
/// An `ImmutableString` wraps an `Rc<String>` (or `Arc<String>` under the `sync` feature)
|
||||||
@ -132,47 +129,55 @@ pub struct ImmutableString(Shared<String>);
|
|||||||
impl Deref for ImmutableString {
|
impl Deref for ImmutableString {
|
||||||
type Target = String;
|
type Target = String;
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
&self.0
|
&self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsRef<String> for ImmutableString {
|
impl AsRef<String> for ImmutableString {
|
||||||
|
#[inline(always)]
|
||||||
fn as_ref(&self) -> &String {
|
fn as_ref(&self) -> &String {
|
||||||
&self.0
|
&self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Borrow<String> for ImmutableString {
|
impl Borrow<String> for ImmutableString {
|
||||||
|
#[inline(always)]
|
||||||
fn borrow(&self) -> &String {
|
fn borrow(&self) -> &String {
|
||||||
&self.0
|
&self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Borrow<str> for ImmutableString {
|
impl Borrow<str> for ImmutableString {
|
||||||
|
#[inline(always)]
|
||||||
fn borrow(&self) -> &str {
|
fn borrow(&self) -> &str {
|
||||||
self.0.as_str()
|
self.0.as_str()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&str> for ImmutableString {
|
impl From<&str> for ImmutableString {
|
||||||
|
#[inline(always)]
|
||||||
fn from(value: &str) -> Self {
|
fn from(value: &str) -> Self {
|
||||||
Self(value.to_string().into())
|
Self(value.to_string().into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl From<String> for ImmutableString {
|
impl From<String> for ImmutableString {
|
||||||
|
#[inline(always)]
|
||||||
fn from(value: String) -> Self {
|
fn from(value: String) -> Self {
|
||||||
Self(value.into())
|
Self(value.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Box<String>> for ImmutableString {
|
impl From<Box<String>> for ImmutableString {
|
||||||
|
#[inline(always)]
|
||||||
fn from(value: Box<String>) -> Self {
|
fn from(value: Box<String>) -> Self {
|
||||||
Self(value.into())
|
Self(value.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ImmutableString> for String {
|
impl From<ImmutableString> for String {
|
||||||
|
#[inline(always)]
|
||||||
fn from(value: ImmutableString) -> Self {
|
fn from(value: ImmutableString) -> Self {
|
||||||
value.into_owned()
|
value.into_owned()
|
||||||
}
|
}
|
||||||
@ -181,42 +186,49 @@ impl From<ImmutableString> for String {
|
|||||||
impl FromStr for ImmutableString {
|
impl FromStr for ImmutableString {
|
||||||
type Err = ();
|
type Err = ();
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
Ok(Self(s.to_string().into()))
|
Ok(Self(s.to_string().into()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromIterator<char> for ImmutableString {
|
impl FromIterator<char> for ImmutableString {
|
||||||
|
#[inline(always)]
|
||||||
fn from_iter<T: IntoIterator<Item = char>>(iter: T) -> Self {
|
fn from_iter<T: IntoIterator<Item = char>>(iter: T) -> Self {
|
||||||
Self(iter.into_iter().collect::<String>().into())
|
Self(iter.into_iter().collect::<String>().into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> FromIterator<&'a char> for ImmutableString {
|
impl<'a> FromIterator<&'a char> for ImmutableString {
|
||||||
|
#[inline(always)]
|
||||||
fn from_iter<T: IntoIterator<Item = &'a char>>(iter: T) -> Self {
|
fn from_iter<T: IntoIterator<Item = &'a char>>(iter: T) -> Self {
|
||||||
Self(iter.into_iter().cloned().collect::<String>().into())
|
Self(iter.into_iter().cloned().collect::<String>().into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> FromIterator<&'a str> for ImmutableString {
|
impl<'a> FromIterator<&'a str> for ImmutableString {
|
||||||
|
#[inline(always)]
|
||||||
fn from_iter<T: IntoIterator<Item = &'a str>>(iter: T) -> Self {
|
fn from_iter<T: IntoIterator<Item = &'a str>>(iter: T) -> Self {
|
||||||
Self(iter.into_iter().collect::<String>().into())
|
Self(iter.into_iter().collect::<String>().into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> FromIterator<String> for ImmutableString {
|
impl<'a> FromIterator<String> for ImmutableString {
|
||||||
|
#[inline(always)]
|
||||||
fn from_iter<T: IntoIterator<Item = String>>(iter: T) -> Self {
|
fn from_iter<T: IntoIterator<Item = String>>(iter: T) -> Self {
|
||||||
Self(iter.into_iter().collect::<String>().into())
|
Self(iter.into_iter().collect::<String>().into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for ImmutableString {
|
impl fmt::Display for ImmutableString {
|
||||||
|
#[inline(always)]
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
fmt::Display::fmt(self.0.as_str(), f)
|
fmt::Display::fmt(self.0.as_str(), f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for ImmutableString {
|
impl fmt::Debug for ImmutableString {
|
||||||
|
#[inline(always)]
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
fmt::Debug::fmt(self.0.as_str(), f)
|
fmt::Debug::fmt(self.0.as_str(), f)
|
||||||
}
|
}
|
||||||
@ -225,6 +237,7 @@ impl fmt::Debug for ImmutableString {
|
|||||||
impl Add for ImmutableString {
|
impl Add for ImmutableString {
|
||||||
type Output = Self;
|
type Output = Self;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn add(mut self, rhs: Self) -> Self::Output {
|
fn add(mut self, rhs: Self) -> Self::Output {
|
||||||
if rhs.is_empty() {
|
if rhs.is_empty() {
|
||||||
self
|
self
|
||||||
@ -240,6 +253,7 @@ impl Add for ImmutableString {
|
|||||||
impl Add for &ImmutableString {
|
impl Add for &ImmutableString {
|
||||||
type Output = ImmutableString;
|
type Output = ImmutableString;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn add(self, rhs: Self) -> Self::Output {
|
fn add(self, rhs: Self) -> Self::Output {
|
||||||
if rhs.is_empty() {
|
if rhs.is_empty() {
|
||||||
self.clone()
|
self.clone()
|
||||||
@ -254,6 +268,7 @@ impl Add for &ImmutableString {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl AddAssign<&ImmutableString> for ImmutableString {
|
impl AddAssign<&ImmutableString> for ImmutableString {
|
||||||
|
#[inline]
|
||||||
fn add_assign(&mut self, rhs: &ImmutableString) {
|
fn add_assign(&mut self, rhs: &ImmutableString) {
|
||||||
if !rhs.is_empty() {
|
if !rhs.is_empty() {
|
||||||
if self.is_empty() {
|
if self.is_empty() {
|
||||||
@ -266,6 +281,7 @@ impl AddAssign<&ImmutableString> for ImmutableString {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl AddAssign<ImmutableString> for ImmutableString {
|
impl AddAssign<ImmutableString> for ImmutableString {
|
||||||
|
#[inline]
|
||||||
fn add_assign(&mut self, rhs: ImmutableString) {
|
fn add_assign(&mut self, rhs: ImmutableString) {
|
||||||
if !rhs.is_empty() {
|
if !rhs.is_empty() {
|
||||||
if self.is_empty() {
|
if self.is_empty() {
|
||||||
@ -280,6 +296,7 @@ impl AddAssign<ImmutableString> for ImmutableString {
|
|||||||
impl Add<&str> for ImmutableString {
|
impl Add<&str> for ImmutableString {
|
||||||
type Output = Self;
|
type Output = Self;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn add(mut self, rhs: &str) -> Self::Output {
|
fn add(mut self, rhs: &str) -> Self::Output {
|
||||||
if rhs.is_empty() {
|
if rhs.is_empty() {
|
||||||
self
|
self
|
||||||
@ -293,6 +310,7 @@ impl Add<&str> for ImmutableString {
|
|||||||
impl Add<&str> for &ImmutableString {
|
impl Add<&str> for &ImmutableString {
|
||||||
type Output = ImmutableString;
|
type Output = ImmutableString;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn add(self, rhs: &str) -> Self::Output {
|
fn add(self, rhs: &str) -> Self::Output {
|
||||||
if rhs.is_empty() {
|
if rhs.is_empty() {
|
||||||
self.clone()
|
self.clone()
|
||||||
@ -305,6 +323,7 @@ impl Add<&str> for &ImmutableString {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl AddAssign<&str> for ImmutableString {
|
impl AddAssign<&str> for ImmutableString {
|
||||||
|
#[inline(always)]
|
||||||
fn add_assign(&mut self, rhs: &str) {
|
fn add_assign(&mut self, rhs: &str) {
|
||||||
if !rhs.is_empty() {
|
if !rhs.is_empty() {
|
||||||
self.make_mut().push_str(rhs);
|
self.make_mut().push_str(rhs);
|
||||||
@ -315,6 +334,7 @@ impl AddAssign<&str> for ImmutableString {
|
|||||||
impl Add<String> for ImmutableString {
|
impl Add<String> for ImmutableString {
|
||||||
type Output = Self;
|
type Output = Self;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn add(mut self, rhs: String) -> Self::Output {
|
fn add(mut self, rhs: String) -> Self::Output {
|
||||||
if rhs.is_empty() {
|
if rhs.is_empty() {
|
||||||
self
|
self
|
||||||
@ -330,6 +350,7 @@ impl Add<String> for ImmutableString {
|
|||||||
impl Add<String> for &ImmutableString {
|
impl Add<String> for &ImmutableString {
|
||||||
type Output = ImmutableString;
|
type Output = ImmutableString;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn add(self, rhs: String) -> Self::Output {
|
fn add(self, rhs: String) -> Self::Output {
|
||||||
if rhs.is_empty() {
|
if rhs.is_empty() {
|
||||||
self.clone()
|
self.clone()
|
||||||
@ -346,6 +367,7 @@ impl Add<String> for &ImmutableString {
|
|||||||
impl Add<char> for ImmutableString {
|
impl Add<char> for ImmutableString {
|
||||||
type Output = Self;
|
type Output = Self;
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
fn add(mut self, rhs: char) -> Self::Output {
|
fn add(mut self, rhs: char) -> Self::Output {
|
||||||
self.make_mut().push(rhs);
|
self.make_mut().push(rhs);
|
||||||
self
|
self
|
||||||
@ -355,6 +377,7 @@ impl Add<char> for ImmutableString {
|
|||||||
impl Add<char> for &ImmutableString {
|
impl Add<char> for &ImmutableString {
|
||||||
type Output = ImmutableString;
|
type Output = ImmutableString;
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
fn add(self, rhs: char) -> Self::Output {
|
fn add(self, rhs: char) -> Self::Output {
|
||||||
let mut s = self.clone();
|
let mut s = self.clone();
|
||||||
s.make_mut().push(rhs);
|
s.make_mut().push(rhs);
|
||||||
@ -363,42 +386,49 @@ impl Add<char> for &ImmutableString {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl AddAssign<char> for ImmutableString {
|
impl AddAssign<char> for ImmutableString {
|
||||||
|
#[inline(always)]
|
||||||
fn add_assign(&mut self, rhs: char) {
|
fn add_assign(&mut self, rhs: char) {
|
||||||
self.make_mut().push(rhs);
|
self.make_mut().push(rhs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S: AsRef<str>> PartialEq<S> for ImmutableString {
|
impl<S: AsRef<str>> PartialEq<S> for ImmutableString {
|
||||||
|
#[inline(always)]
|
||||||
fn eq(&self, other: &S) -> bool {
|
fn eq(&self, other: &S) -> bool {
|
||||||
self.as_str().eq(other.as_ref())
|
self.as_str().eq(other.as_ref())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialEq<ImmutableString> for str {
|
impl PartialEq<ImmutableString> for str {
|
||||||
|
#[inline(always)]
|
||||||
fn eq(&self, other: &ImmutableString) -> bool {
|
fn eq(&self, other: &ImmutableString) -> bool {
|
||||||
self.eq(other.as_str())
|
self.eq(other.as_str())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialEq<ImmutableString> for String {
|
impl PartialEq<ImmutableString> for String {
|
||||||
|
#[inline(always)]
|
||||||
fn eq(&self, other: &ImmutableString) -> bool {
|
fn eq(&self, other: &ImmutableString) -> bool {
|
||||||
self.eq(other.as_str())
|
self.eq(other.as_str())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S: AsRef<str>> PartialOrd<S> for ImmutableString {
|
impl<S: AsRef<str>> PartialOrd<S> for ImmutableString {
|
||||||
|
#[inline(always)]
|
||||||
fn partial_cmp(&self, other: &S) -> Option<Ordering> {
|
fn partial_cmp(&self, other: &S) -> Option<Ordering> {
|
||||||
self.as_str().partial_cmp(other.as_ref())
|
self.as_str().partial_cmp(other.as_ref())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialOrd<ImmutableString> for str {
|
impl PartialOrd<ImmutableString> for str {
|
||||||
|
#[inline(always)]
|
||||||
fn partial_cmp(&self, other: &ImmutableString) -> Option<Ordering> {
|
fn partial_cmp(&self, other: &ImmutableString) -> Option<Ordering> {
|
||||||
self.partial_cmp(other.as_str())
|
self.partial_cmp(other.as_str())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialOrd<ImmutableString> for String {
|
impl PartialOrd<ImmutableString> for String {
|
||||||
|
#[inline(always)]
|
||||||
fn partial_cmp(&self, other: &ImmutableString) -> Option<Ordering> {
|
fn partial_cmp(&self, other: &ImmutableString) -> Option<Ordering> {
|
||||||
self.as_str().partial_cmp(other.as_str())
|
self.as_str().partial_cmp(other.as_str())
|
||||||
}
|
}
|
||||||
@ -407,12 +437,14 @@ impl PartialOrd<ImmutableString> for String {
|
|||||||
impl ImmutableString {
|
impl ImmutableString {
|
||||||
/// Consume the `ImmutableString` and convert it into a `String`.
|
/// Consume the `ImmutableString` and convert it into a `String`.
|
||||||
/// If there are other references to the same string, a cloned copy is returned.
|
/// If there are other references to the same string, a cloned copy is returned.
|
||||||
|
#[inline(always)]
|
||||||
pub fn into_owned(mut self) -> String {
|
pub fn into_owned(mut self) -> String {
|
||||||
self.make_mut(); // Make sure it is unique reference
|
self.make_mut(); // Make sure it is unique reference
|
||||||
shared_take(self.0) // Should succeed
|
shared_take(self.0) // Should succeed
|
||||||
}
|
}
|
||||||
/// Make sure that the `ImmutableString` is unique (i.e. no other outstanding references).
|
/// Make sure that the `ImmutableString` is unique (i.e. no other outstanding references).
|
||||||
/// Then return a mutable reference to the `String`.
|
/// Then return a mutable reference to the `String`.
|
||||||
|
#[inline(always)]
|
||||||
pub fn make_mut(&mut self) -> &mut String {
|
pub fn make_mut(&mut self) -> &mut String {
|
||||||
shared_make_mut(&mut self.0)
|
shared_make_mut(&mut self.0)
|
||||||
}
|
}
|
||||||
|
12
tests/README.md
Normal file
12
tests/README.md
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
Tests
|
||||||
|
=====
|
||||||
|
|
||||||
|
Rhai engine tests.
|
||||||
|
|
||||||
|
|
||||||
|
How to Run
|
||||||
|
----------
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo test
|
||||||
|
```
|
@ -1,6 +1,6 @@
|
|||||||
#![cfg(not(feature = "no_optimize"))]
|
#![cfg(not(feature = "no_optimize"))]
|
||||||
|
|
||||||
use rhai::{Engine, EvalAltResult, OptimizationLevel, INT};
|
use rhai::{Engine, EvalAltResult, OptimizationLevel, RegisterFn, INT};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_optimizer_run() -> Result<(), Box<EvalAltResult>> {
|
fn test_optimizer_run() -> Result<(), Box<EvalAltResult>> {
|
||||||
@ -28,6 +28,23 @@ fn test_optimizer_run() -> Result<(), Box<EvalAltResult>> {
|
|||||||
engine.set_optimization_level(OptimizationLevel::Full);
|
engine.set_optimization_level(OptimizationLevel::Full);
|
||||||
run_test(&mut engine)?;
|
run_test(&mut engine)?;
|
||||||
|
|
||||||
|
// Override == operator
|
||||||
|
engine.register_fn("==", |_x: INT, _y: INT| false);
|
||||||
|
|
||||||
|
engine.set_optimization_level(OptimizationLevel::Simple);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<INT>(r"if 1 == 1 || 2 > 3 { 42 } else { 123 }")?,
|
||||||
|
123
|
||||||
|
);
|
||||||
|
|
||||||
|
engine.set_optimization_level(OptimizationLevel::Full);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<INT>(r"if 1 == 1 || 2 > 3 { 42 } else { 123 }")?,
|
||||||
|
123
|
||||||
|
);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
315
tests/serde.rs
315
tests/serde.rs
@ -1,6 +1,9 @@
|
|||||||
#![cfg(feature = "serde")]
|
#![cfg(feature = "serde")]
|
||||||
|
|
||||||
use rhai::{de::from_dynamic, ser::to_dynamic, Dynamic, Engine, EvalAltResult, INT};
|
use rhai::{
|
||||||
|
serde::{from_dynamic, to_dynamic},
|
||||||
|
Dynamic, Engine, EvalAltResult, INT,
|
||||||
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
@ -139,12 +142,10 @@ fn test_serde_ser_externally_tagged_enum() -> Result<(), Box<EvalAltResult>> {
|
|||||||
assert!(content.is_empty());
|
assert!(content.is_empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
let mut map = to_dynamic(MyEnum::VariantNewtype(123))?.cast::<Map>();
|
||||||
let mut map = to_dynamic(MyEnum::VariantNewtype(123))?.cast::<Map>();
|
let content = map.remove("VariantNewtype").unwrap();
|
||||||
let content = map.remove("VariantNewtype").unwrap();
|
assert!(map.is_empty());
|
||||||
assert!(map.is_empty());
|
assert_eq!(Ok(123), content.as_int());
|
||||||
assert_eq!(Ok(123), content.as_int());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
{
|
{
|
||||||
@ -156,20 +157,16 @@ fn test_serde_ser_externally_tagged_enum() -> Result<(), Box<EvalAltResult>> {
|
|||||||
assert_eq!(Ok(456), content[1].as_int());
|
assert_eq!(Ok(456), content[1].as_int());
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
let mut map = to_dynamic(MyEnum::VariantEmptyStruct {})?.cast::<Map>();
|
||||||
let mut map = to_dynamic(MyEnum::VariantEmptyStruct {})?.cast::<Map>();
|
let map_inner = map.remove("VariantEmptyStruct").unwrap().cast::<Map>();
|
||||||
let map_inner = map.remove("VariantEmptyStruct").unwrap().cast::<Map>();
|
assert!(map.is_empty());
|
||||||
assert!(map.is_empty());
|
assert!(map_inner.is_empty());
|
||||||
assert!(map_inner.is_empty());
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
let mut map = to_dynamic(MyEnum::VariantStruct { a: 123 })?.cast::<Map>();
|
||||||
let mut map = to_dynamic(MyEnum::VariantStruct { a: 123 })?.cast::<Map>();
|
let mut map_inner = map.remove("VariantStruct").unwrap().cast::<Map>();
|
||||||
let mut map_inner = map.remove("VariantStruct").unwrap().cast::<Map>();
|
assert!(map.is_empty());
|
||||||
assert!(map.is_empty());
|
assert_eq!(Ok(123), map_inner.remove("a").unwrap().as_int());
|
||||||
assert_eq!(Ok(123), map_inner.remove("a").unwrap().as_int());
|
assert!(map_inner.is_empty());
|
||||||
assert!(map_inner.is_empty());
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -217,11 +214,9 @@ fn test_serde_ser_adjacently_tagged_enum() -> Result<(), Box<EvalAltResult>> {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
let mut map = to_dynamic(MyEnum::VariantUnit)?.cast::<Map>();
|
||||||
let mut map = to_dynamic(MyEnum::VariantUnit)?.cast::<Map>();
|
assert_eq!(Ok("VariantUnit"), map.remove("tag").unwrap().as_str());
|
||||||
assert_eq!(Ok("VariantUnit"), map.remove("tag").unwrap().as_str());
|
assert!(map.is_empty());
|
||||||
assert!(map.is_empty());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
{
|
{
|
||||||
@ -232,13 +227,11 @@ fn test_serde_ser_adjacently_tagged_enum() -> Result<(), Box<EvalAltResult>> {
|
|||||||
assert!(content.is_empty());
|
assert!(content.is_empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
let mut map = to_dynamic(MyEnum::VariantNewtype(123))?.cast::<Map>();
|
||||||
let mut map = to_dynamic(MyEnum::VariantNewtype(123))?.cast::<Map>();
|
assert_eq!(Ok("VariantNewtype"), map.remove("tag").unwrap().as_str());
|
||||||
assert_eq!(Ok("VariantNewtype"), map.remove("tag").unwrap().as_str());
|
let content = map.remove("content").unwrap();
|
||||||
let content = map.remove("content").unwrap();
|
assert!(map.is_empty());
|
||||||
assert!(map.is_empty());
|
assert_eq!(Ok(123), content.as_int());
|
||||||
assert_eq!(Ok(123), content.as_int());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
{
|
{
|
||||||
@ -251,25 +244,21 @@ fn test_serde_ser_adjacently_tagged_enum() -> Result<(), Box<EvalAltResult>> {
|
|||||||
assert_eq!(Ok(456), content[1].as_int());
|
assert_eq!(Ok(456), content[1].as_int());
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
let mut map = to_dynamic(MyEnum::VariantEmptyStruct {})?.cast::<Map>();
|
||||||
let mut map = to_dynamic(MyEnum::VariantEmptyStruct {})?.cast::<Map>();
|
assert_eq!(
|
||||||
assert_eq!(
|
Ok("VariantEmptyStruct"),
|
||||||
Ok("VariantEmptyStruct"),
|
map.remove("tag").unwrap().as_str()
|
||||||
map.remove("tag").unwrap().as_str()
|
);
|
||||||
);
|
let map_inner = map.remove("content").unwrap().cast::<Map>();
|
||||||
let map_inner = map.remove("content").unwrap().cast::<Map>();
|
assert!(map.is_empty());
|
||||||
assert!(map.is_empty());
|
assert!(map_inner.is_empty());
|
||||||
assert!(map_inner.is_empty());
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
let mut map = to_dynamic(MyEnum::VariantStruct { a: 123 })?.cast::<Map>();
|
||||||
let mut map = to_dynamic(MyEnum::VariantStruct { a: 123 })?.cast::<Map>();
|
assert_eq!(Ok("VariantStruct"), map.remove("tag").unwrap().as_str());
|
||||||
assert_eq!(Ok("VariantStruct"), map.remove("tag").unwrap().as_str());
|
let mut map_inner = map.remove("content").unwrap().cast::<Map>();
|
||||||
let mut map_inner = map.remove("content").unwrap().cast::<Map>();
|
assert!(map.is_empty());
|
||||||
assert!(map.is_empty());
|
assert_eq!(Ok(123), map_inner.remove("a").unwrap().as_int());
|
||||||
assert_eq!(Ok(123), map_inner.remove("a").unwrap().as_int());
|
assert!(map_inner.is_empty());
|
||||||
assert!(map_inner.is_empty());
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -285,22 +274,16 @@ fn test_serde_ser_untagged_enum() -> Result<(), Box<EvalAltResult>> {
|
|||||||
VariantStruct2 { b: i32 },
|
VariantStruct2 { b: i32 },
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
let map = to_dynamic(MyEnum::VariantEmptyStruct {})?.cast::<Map>();
|
||||||
let map = to_dynamic(MyEnum::VariantEmptyStruct {})?.cast::<Map>();
|
assert!(map.is_empty());
|
||||||
assert!(map.is_empty());
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
let mut map = to_dynamic(MyEnum::VariantStruct1 { a: 123 })?.cast::<Map>();
|
||||||
let mut map = to_dynamic(MyEnum::VariantStruct1 { a: 123 })?.cast::<Map>();
|
assert_eq!(Ok(123), map.remove("a").unwrap().as_int());
|
||||||
assert_eq!(Ok(123), map.remove("a").unwrap().as_int());
|
assert!(map.is_empty());
|
||||||
assert!(map.is_empty());
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
let mut map = to_dynamic(MyEnum::VariantStruct2 { b: 123 })?.cast::<Map>();
|
||||||
let mut map = to_dynamic(MyEnum::VariantStruct2 { b: 123 })?.cast::<Map>();
|
assert_eq!(Ok(123), map.remove("b").unwrap().as_int());
|
||||||
assert_eq!(Ok(123), map.remove("b").unwrap().as_int());
|
assert!(map.is_empty());
|
||||||
assert!(map.is_empty());
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -433,15 +416,11 @@ fn test_serde_de_unit_enum() -> Result<(), Box<EvalAltResult>> {
|
|||||||
VariantBar,
|
VariantBar,
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
let d = Dynamic::from("VariantFoo".to_string());
|
||||||
let d = Dynamic::from("VariantFoo".to_string());
|
assert_eq!(MyEnum::VariantFoo, from_dynamic(&d)?);
|
||||||
assert_eq!(MyEnum::VariantFoo, from_dynamic(&d)?);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
let d = Dynamic::from("VariantBar".to_string());
|
||||||
let d = Dynamic::from("VariantBar".to_string());
|
assert_eq!(MyEnum::VariantBar, from_dynamic(&d)?);
|
||||||
assert_eq!(MyEnum::VariantBar, from_dynamic(&d)?);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -464,10 +443,8 @@ fn test_serde_de_externally_tagged_enum() -> Result<(), Box<EvalAltResult>> {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
let d = Dynamic::from("VariantUnit".to_string());
|
||||||
let d = Dynamic::from("VariantUnit".to_string());
|
assert_eq!(MyEnum::VariantUnit, from_dynamic(&d).unwrap());
|
||||||
assert_eq!(MyEnum::VariantUnit, from_dynamic(&d).unwrap());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
{
|
{
|
||||||
@ -480,14 +457,12 @@ fn test_serde_de_externally_tagged_enum() -> Result<(), Box<EvalAltResult>> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
let mut map_outer = Map::new();
|
||||||
let mut map_outer = Map::new();
|
map_outer.insert("VariantNewtype".into(), (123 as INT).into());
|
||||||
map_outer.insert("VariantNewtype".into(), (123 as INT).into());
|
assert_eq!(
|
||||||
assert_eq!(
|
MyEnum::VariantNewtype(123),
|
||||||
MyEnum::VariantNewtype(123),
|
from_dynamic(&map_outer.into()).unwrap()
|
||||||
from_dynamic(&map_outer.into()).unwrap()
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
{
|
{
|
||||||
@ -500,26 +475,22 @@ fn test_serde_de_externally_tagged_enum() -> Result<(), Box<EvalAltResult>> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
let map_inner = Map::new();
|
||||||
let map_inner = Map::new();
|
let mut map_outer = Map::new();
|
||||||
let mut map_outer = Map::new();
|
map_outer.insert("VariantEmptyStruct".into(), map_inner.into());
|
||||||
map_outer.insert("VariantEmptyStruct".into(), map_inner.into());
|
assert_eq!(
|
||||||
assert_eq!(
|
MyEnum::VariantEmptyStruct {},
|
||||||
MyEnum::VariantEmptyStruct {},
|
from_dynamic(&map_outer.into()).unwrap()
|
||||||
from_dynamic(&map_outer.into()).unwrap()
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
let mut map_inner = Map::new();
|
||||||
let mut map_inner = Map::new();
|
map_inner.insert("a".into(), (123 as INT).into());
|
||||||
map_inner.insert("a".into(), (123 as INT).into());
|
let mut map_outer = Map::new();
|
||||||
let mut map_outer = Map::new();
|
map_outer.insert("VariantStruct".into(), map_inner.into());
|
||||||
map_outer.insert("VariantStruct".into(), map_inner.into());
|
assert_eq!(
|
||||||
assert_eq!(
|
MyEnum::VariantStruct { a: 123 },
|
||||||
MyEnum::VariantStruct { a: 123 },
|
from_dynamic(&map_outer.into()).unwrap()
|
||||||
from_dynamic(&map_outer.into()).unwrap()
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -534,24 +505,20 @@ fn test_serde_de_internally_tagged_enum() -> Result<(), Box<EvalAltResult>> {
|
|||||||
VariantStruct { a: i32 },
|
VariantStruct { a: i32 },
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
let mut map = Map::new();
|
||||||
let mut map = Map::new();
|
map.insert("tag".into(), "VariantStruct".into());
|
||||||
map.insert("tag".into(), "VariantStruct".into());
|
map.insert("a".into(), (123 as INT).into());
|
||||||
map.insert("a".into(), (123 as INT).into());
|
assert_eq!(
|
||||||
assert_eq!(
|
MyEnum::VariantStruct { a: 123 },
|
||||||
MyEnum::VariantStruct { a: 123 },
|
from_dynamic(&map.into()).unwrap()
|
||||||
from_dynamic(&map.into()).unwrap()
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
let mut map = Map::new();
|
||||||
let mut map = Map::new();
|
map.insert("tag".into(), "VariantEmptyStruct".into());
|
||||||
map.insert("tag".into(), "VariantEmptyStruct".into());
|
assert_eq!(
|
||||||
assert_eq!(
|
MyEnum::VariantEmptyStruct {},
|
||||||
MyEnum::VariantEmptyStruct {},
|
from_dynamic(&map.into()).unwrap()
|
||||||
from_dynamic(&map.into()).unwrap()
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -574,14 +541,12 @@ fn test_serde_de_adjacently_tagged_enum() -> Result<(), Box<EvalAltResult>> {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
let mut map_outer = Map::new();
|
||||||
let mut map_outer = Map::new();
|
map_outer.insert("tag".into(), "VariantUnit".into());
|
||||||
map_outer.insert("tag".into(), "VariantUnit".into());
|
assert_eq!(
|
||||||
assert_eq!(
|
MyEnum::VariantUnit,
|
||||||
MyEnum::VariantUnit,
|
from_dynamic(&map_outer.into()).unwrap()
|
||||||
from_dynamic(&map_outer.into()).unwrap()
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
{
|
{
|
||||||
@ -595,15 +560,13 @@ fn test_serde_de_adjacently_tagged_enum() -> Result<(), Box<EvalAltResult>> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
let mut map_outer = Map::new();
|
||||||
let mut map_outer = Map::new();
|
map_outer.insert("tag".into(), "VariantNewtype".into());
|
||||||
map_outer.insert("tag".into(), "VariantNewtype".into());
|
map_outer.insert("content".into(), (123 as INT).into());
|
||||||
map_outer.insert("content".into(), (123 as INT).into());
|
assert_eq!(
|
||||||
assert_eq!(
|
MyEnum::VariantNewtype(123),
|
||||||
MyEnum::VariantNewtype(123),
|
from_dynamic(&map_outer.into()).unwrap()
|
||||||
from_dynamic(&map_outer.into()).unwrap()
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
{
|
{
|
||||||
@ -617,28 +580,24 @@ fn test_serde_de_adjacently_tagged_enum() -> Result<(), Box<EvalAltResult>> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
let map_inner = Map::new();
|
||||||
let map_inner = Map::new();
|
let mut map_outer = Map::new();
|
||||||
let mut map_outer = Map::new();
|
map_outer.insert("tag".into(), "VariantEmptyStruct".into());
|
||||||
map_outer.insert("tag".into(), "VariantEmptyStruct".into());
|
map_outer.insert("content".into(), map_inner.into());
|
||||||
map_outer.insert("content".into(), map_inner.into());
|
assert_eq!(
|
||||||
assert_eq!(
|
MyEnum::VariantEmptyStruct {},
|
||||||
MyEnum::VariantEmptyStruct {},
|
from_dynamic(&map_outer.into()).unwrap()
|
||||||
from_dynamic(&map_outer.into()).unwrap()
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
let mut map_inner = Map::new();
|
||||||
let mut map_inner = Map::new();
|
map_inner.insert("a".into(), (123 as INT).into());
|
||||||
map_inner.insert("a".into(), (123 as INT).into());
|
let mut map_outer = Map::new();
|
||||||
let mut map_outer = Map::new();
|
map_outer.insert("tag".into(), "VariantStruct".into());
|
||||||
map_outer.insert("tag".into(), "VariantStruct".into());
|
map_outer.insert("content".into(), map_inner.into());
|
||||||
map_outer.insert("content".into(), map_inner.into());
|
assert_eq!(
|
||||||
assert_eq!(
|
MyEnum::VariantStruct { a: 123 },
|
||||||
MyEnum::VariantStruct { a: 123 },
|
from_dynamic(&map_outer.into()).unwrap()
|
||||||
from_dynamic(&map_outer.into()).unwrap()
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -654,31 +613,25 @@ fn test_serde_de_untagged_enum() -> Result<(), Box<EvalAltResult>> {
|
|||||||
VariantStruct2 { b: i32 },
|
VariantStruct2 { b: i32 },
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
let map = Map::new();
|
||||||
let map = Map::new();
|
assert_eq!(
|
||||||
assert_eq!(
|
MyEnum::VariantEmptyStruct {},
|
||||||
MyEnum::VariantEmptyStruct {},
|
from_dynamic(&map.into()).unwrap()
|
||||||
from_dynamic(&map.into()).unwrap()
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
let mut map = Map::new();
|
||||||
let mut map = Map::new();
|
map.insert("a".into(), (123 as INT).into());
|
||||||
map.insert("a".into(), (123 as INT).into());
|
assert_eq!(
|
||||||
assert_eq!(
|
MyEnum::VariantStruct1 { a: 123 },
|
||||||
MyEnum::VariantStruct1 { a: 123 },
|
from_dynamic(&map.into()).unwrap()
|
||||||
from_dynamic(&map.into()).unwrap()
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
let mut map = Map::new();
|
||||||
let mut map = Map::new();
|
map.insert("b".into(), (123 as INT).into());
|
||||||
map.insert("b".into(), (123 as INT).into());
|
assert_eq!(
|
||||||
assert_eq!(
|
MyEnum::VariantStruct2 { b: 123 },
|
||||||
MyEnum::VariantStruct2 { b: 123 },
|
from_dynamic(&map.into()).unwrap()
|
||||||
from_dynamic(&map.into()).unwrap()
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -10,10 +10,10 @@ fn test_stack_overflow_fn_calls() -> Result<(), Box<EvalAltResult>> {
|
|||||||
engine.eval::<INT>(
|
engine.eval::<INT>(
|
||||||
r"
|
r"
|
||||||
fn foo(n) { if n <= 1 { 0 } else { n + foo(n-1) } }
|
fn foo(n) { if n <= 1 { 0 } else { n + foo(n-1) } }
|
||||||
foo(8)
|
foo(7)
|
||||||
",
|
",
|
||||||
)?,
|
)?,
|
||||||
35
|
27
|
||||||
);
|
);
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
|
Loading…
Reference in New Issue
Block a user