Merge pull request #682 from schungx/master
New optimizations and features.
This commit is contained in:
commit
b87243bfe2
4
.github/workflows/build.yml
vendored
4
.github/workflows/build.yml
vendored
@ -178,8 +178,8 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- {toolchain: nightly, os: ubuntu-latest, experimental: false, flags: "--features metadata"}
|
||||
- {toolchain: nightly, os: windows-latest, experimental: false, flags: "--features metadata"}
|
||||
- {toolchain: stable, os: ubuntu-latest, experimental: false, flags: "--features metadata"}
|
||||
- {toolchain: stable, os: windows-latest, experimental: false, flags: "--features metadata"}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
18
CHANGELOG.md
18
CHANGELOG.md
@ -22,12 +22,13 @@ Deprecated API's
|
||||
|
||||
* `Module::with_capacity` is deprecated.
|
||||
* The internal method `Engine::eval_statements_raw` is deprecated.
|
||||
* Array overloaded methods that take function names (as string) are deprecated in favor of using the `Fn("...")` call.
|
||||
|
||||
Speed improvements
|
||||
------------------
|
||||
|
||||
* The functions registration mechanism is revamped to take advantage of constant generics, among others.
|
||||
* This yields a 10-20% speed improvements on certain real-life, function-call-heavy workloads.
|
||||
* The function registration mechanism is revamped to take advantage of constant generics, among others, to omit checking code where possible. This yields a 10-20% speed improvements on certain real-life, function-call-heavy workloads.
|
||||
* Functions taking function pointers as parameters, usually called with closures, now run faster because a link to the anonymous function (generated by the closure) is stored together with the function pointer itself. This allows short-circuiting the function lookup step.
|
||||
|
||||
Net features
|
||||
------------
|
||||
@ -36,7 +37,7 @@ Net features
|
||||
|
||||
* A function pointer created via a closure definition now links to the particular anonymous function itself.
|
||||
* This avoids a potentially expensive function lookup when the function pointer is called, speeding up closures.
|
||||
* It does _not_, however, allow the function pointer to be `export`ed as a constant from a script module because the closure may cross-call other functions defined in the module and the function pointer won't keep the fully encapsulated environment.
|
||||
* Closures now also encapsulate their defining environment, so function pointers can now be freely `export`ed from modules!
|
||||
|
||||
### `!in`
|
||||
|
||||
@ -48,6 +49,12 @@ Net features
|
||||
* The options are for future-proofing the API.
|
||||
* In this version, it gains the ability to set the value of the _custom state_ (accessible via `NativeCallContext::tag`) for a function evaluation, overriding `Engine::set_default_tag`.
|
||||
|
||||
### Compact a script for compression
|
||||
|
||||
* `Engine::compact_script` is added which takes a valid script (it still returns parsing errors) and returns a _compacted_ version of the script with all insignificant whitespaces and all comments removed.
|
||||
* A compact script compresses better than one with liberal whitespaces and comments.
|
||||
* Unlike some uglifiers or minifiers, `Engine::compact_script` does not optimize the script in any way, nor does it rename variables.
|
||||
|
||||
Enhancements
|
||||
------------
|
||||
|
||||
@ -61,6 +68,11 @@ Enhancements
|
||||
* Block-style doc-comments are now "un-indented" for better formatting.
|
||||
* Doc-comments on plugin modules are now captured in the module's `doc` field.
|
||||
* Expression nesting levels is refined such that it grows less excessively for common patterns.
|
||||
* The traits `Index` and `IndexMut` are added to `FnPtr`.
|
||||
* `FnPtr::iter_curry` and `FnPtr::iter_curry_mut` are added.
|
||||
* `Dynamic::deep_scan` is added to recursively scan for `Dynamic` values.
|
||||
* `>>` and `<<` operators on integers no longer throw errors when the number of bits to shift is out of bounds. Shifting by a negative number of bits simply reverses the shift direction.
|
||||
* `find` and `find_map` are added for arrays.
|
||||
|
||||
|
||||
Version 1.11.0
|
||||
|
@ -2,7 +2,7 @@ error: `cfg` attributes are not allowed for `export_fn`
|
||||
--> ui_tests/export_fn_cfg.rs:9:1
|
||||
|
|
||||
9 | #[cfg(not(feature = "foo"))]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
| ^
|
||||
|
||||
error[E0425]: cannot find function `test_fn` in this scope
|
||||
--> ui_tests/export_fn_cfg.rs:20:8
|
||||
|
@ -2,7 +2,7 @@ error: expecting attribute name
|
||||
--> ui_tests/export_fn_path_attr.rs:9:13
|
||||
|
|
||||
9 | #[export_fn(rhai::name = "thing")]
|
||||
| ^^^^^^^^^^
|
||||
| ^^^^
|
||||
|
||||
error[E0425]: cannot find function `test_fn` in this scope
|
||||
--> ui_tests/export_fn_path_attr.rs:19:8
|
||||
|
@ -2,7 +2,7 @@ error: expecting attribute name
|
||||
--> ui_tests/export_mod_path_attr.rs:11:11
|
||||
|
|
||||
11 | #[rhai_fn(rhai::name = "thing")]
|
||||
| ^^^^^^^^^^
|
||||
| ^^^^
|
||||
|
||||
error[E0433]: failed to resolve: use of undeclared crate or module `test_mod`
|
||||
--> ui_tests/export_mod_path_attr.rs:22:8
|
||||
|
@ -2,7 +2,7 @@ error: references from Rhai in this position must be mutable
|
||||
--> ui_tests/first_shared_ref.rs:11:23
|
||||
|
|
||||
11 | pub fn test_fn(input: &NonClonable) -> bool {
|
||||
| ^^^^^^^^^^^^
|
||||
| ^
|
||||
|
||||
error[E0425]: cannot find function `test_fn` in this scope
|
||||
--> ui_tests/first_shared_ref.rs:22:8
|
||||
|
@ -2,7 +2,7 @@ error: Rhai functions cannot return references
|
||||
--> ui_tests/return_mut_ref.rs:12:38
|
||||
|
|
||||
12 | pub fn test_fn(input: &mut Clonable) -> &mut bool {
|
||||
| ^^^^^^^^^^^^
|
||||
| ^
|
||||
|
||||
error[E0425]: cannot find function `test_fn` in this scope
|
||||
--> ui_tests/return_mut_ref.rs:23:8
|
||||
|
@ -2,7 +2,7 @@ error: Rhai functions cannot return pointers
|
||||
--> ui_tests/return_pointer.rs:12:33
|
||||
|
|
||||
12 | pub fn test_fn(input: Clonable) -> *const str {
|
||||
| ^^^^^^^^^^^^^
|
||||
| ^
|
||||
|
||||
error[E0425]: cannot find function `test_fn` in this scope
|
||||
--> ui_tests/return_pointer.rs:24:19
|
||||
|
@ -2,7 +2,7 @@ error: Rhai functions cannot return references
|
||||
--> ui_tests/return_shared_ref.rs:12:33
|
||||
|
|
||||
12 | pub fn test_fn(input: Clonable) -> &'static str {
|
||||
| ^^^^^^^^^^^^^^^
|
||||
| ^
|
||||
|
||||
error[E0425]: cannot find function `test_fn` in this scope
|
||||
--> ui_tests/return_shared_ref.rs:23:20
|
||||
|
@ -2,16 +2,16 @@ error: duplicated attribute 'rhai_fn'
|
||||
--> ui_tests/rhai_fn_duplicate_attr.rs:6:5
|
||||
|
|
||||
6 | #[rhai_fn(pure)]
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
||||
error[E0425]: cannot find value `n` in this scope
|
||||
--> ui_tests/rhai_fn_duplicate_attr.rs:13:29
|
||||
|
|
||||
13 | if test_module::test_fn(n) {
|
||||
| ^ not found in this scope
|
||||
| ^
|
||||
|
||||
error[E0433]: failed to resolve: use of undeclared crate or module `test_module`
|
||||
--> ui_tests/rhai_fn_duplicate_attr.rs:13:8
|
||||
|
|
||||
13 | if test_module::test_fn(n) {
|
||||
| ^^^^^^^^^^^ use of undeclared crate or module `test_module`
|
||||
|
||||
error[E0425]: cannot find value `n` in this scope
|
||||
--> ui_tests/rhai_fn_duplicate_attr.rs:13:29
|
||||
|
|
||||
13 | if test_module::test_fn(n) {
|
||||
| ^ not found in this scope
|
||||
|
@ -2,7 +2,7 @@ error: conflicting setter
|
||||
--> ui_tests/rhai_fn_getter_conflict.rs:12:42
|
||||
|
|
||||
12 | #[rhai_fn(name = "foo", get = "foo", set = "bar")]
|
||||
| ^^^^^^^^^^^
|
||||
| ^^^
|
||||
|
||||
error[E0433]: failed to resolve: use of undeclared crate or module `test_module`
|
||||
--> ui_tests/rhai_fn_getter_conflict.rs:23:8
|
||||
|
@ -2,7 +2,7 @@ error: conflicting getter
|
||||
--> ui_tests/rhai_fn_getter_multiple.rs:12:42
|
||||
|
|
||||
12 | #[rhai_fn(name = "foo", get = "foo", get = "bar")]
|
||||
| ^^^^^^^^^^^
|
||||
| ^^^
|
||||
|
||||
error[E0433]: failed to resolve: use of undeclared crate or module `test_module`
|
||||
--> ui_tests/rhai_fn_getter_multiple.rs:23:8
|
||||
|
@ -2,7 +2,7 @@ error: property getter must return a value
|
||||
--> ui_tests/rhai_fn_getter_return.rs:13:9
|
||||
|
|
||||
13 | pub fn test_fn(input: &mut Point) {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
| ^^
|
||||
|
||||
error[E0433]: failed to resolve: use of undeclared crate or module `test_module`
|
||||
--> ui_tests/rhai_fn_getter_return.rs:23:5
|
||||
|
@ -2,7 +2,7 @@ error: property getter requires exactly 1 parameter
|
||||
--> ui_tests/rhai_fn_getter_signature.rs:13:20
|
||||
|
|
||||
13 | pub fn test_fn(input: Point, value: bool) -> bool {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
| ^^^^^
|
||||
|
||||
error[E0433]: failed to resolve: use of undeclared crate or module `test_module`
|
||||
--> ui_tests/rhai_fn_getter_signature.rs:23:8
|
||||
|
@ -2,7 +2,7 @@ error: index getter must return a value
|
||||
--> ui_tests/rhai_fn_index_getter_return.rs:13:9
|
||||
|
|
||||
13 | pub fn test_fn(input: &mut Point, i: f32) {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
| ^^
|
||||
|
||||
error[E0433]: failed to resolve: use of undeclared crate or module `test_module`
|
||||
--> ui_tests/rhai_fn_index_getter_return.rs:23:8
|
||||
|
@ -2,7 +2,7 @@ error: index getter requires exactly 2 parameters
|
||||
--> ui_tests/rhai_fn_index_getter_signature.rs:13:20
|
||||
|
|
||||
13 | pub fn test_fn(input: Point) -> bool {
|
||||
| ^^^^^^^^^^^^
|
||||
| ^^^^^
|
||||
|
||||
error[E0433]: failed to resolve: use of undeclared crate or module `test_module`
|
||||
--> ui_tests/rhai_fn_index_getter_signature.rs:23:8
|
||||
|
@ -2,7 +2,7 @@ error: expecting attribute name
|
||||
--> ui_tests/rhai_fn_path_attr.rs:11:11
|
||||
|
|
||||
11 | #[rhai_fn(rhai::name = "thing")]
|
||||
| ^^^^^^^^^^
|
||||
| ^^^^
|
||||
|
||||
error[E0433]: failed to resolve: use of undeclared crate or module `test_module`
|
||||
--> ui_tests/rhai_fn_path_attr.rs:22:8
|
||||
|
@ -2,13 +2,13 @@ error: duplicate Rhai signature for 'foo'
|
||||
--> ui_tests/rhai_fn_rename_collision.rs:17:15
|
||||
|
|
||||
17 | #[rhai_fn(name = "foo")]
|
||||
| ^^^^^^^^^^^^
|
||||
| ^^^^
|
||||
|
||||
error: duplicated function renamed 'foo'
|
||||
--> ui_tests/rhai_fn_rename_collision.rs:12:15
|
||||
|
|
||||
12 | #[rhai_fn(name = "foo")]
|
||||
| ^^^^^^^^^^^^
|
||||
| ^^^^
|
||||
|
||||
error[E0433]: failed to resolve: use of undeclared crate or module `test_module`
|
||||
--> ui_tests/rhai_fn_rename_collision.rs:28:8
|
||||
|
@ -8,7 +8,7 @@ error: duplicated function 'foo'
|
||||
--> ui_tests/rhai_fn_rename_collision_oneattr.rs:12:15
|
||||
|
|
||||
12 | #[rhai_fn(name = "foo")]
|
||||
| ^^^^^^^^^^^^
|
||||
| ^^^^
|
||||
|
||||
error[E0433]: failed to resolve: use of undeclared crate or module `test_module`
|
||||
--> ui_tests/rhai_fn_rename_collision_oneattr.rs:27:8
|
||||
|
@ -2,13 +2,13 @@ error: duplicate Rhai signature for 'bar'
|
||||
--> ui_tests/rhai_fn_rename_collision_oneattr_multiple.rs:17:15
|
||||
|
|
||||
17 | #[rhai_fn(get = "bar")]
|
||||
| ^^^^^^^^^^^
|
||||
| ^^^
|
||||
|
||||
error: duplicated function renamed 'bar'
|
||||
--> ui_tests/rhai_fn_rename_collision_oneattr_multiple.rs:12:15
|
||||
|
|
||||
12 | #[rhai_fn(name = "foo", get = "bar")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
| ^^^^
|
||||
|
||||
error[E0433]: failed to resolve: use of undeclared crate or module `test_module`
|
||||
--> ui_tests/rhai_fn_rename_collision_oneattr_multiple.rs:25:8
|
||||
|
@ -2,13 +2,13 @@ error: duplicate Rhai signature for 'foo'
|
||||
--> ui_tests/rhai_fn_rename_collision_with_itself.rs:12:15
|
||||
|
|
||||
12 | #[rhai_fn(name = "foo", name = "bar", name = "foo")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
| ^^^^
|
||||
|
||||
error: duplicated function renamed 'foo'
|
||||
--> ui_tests/rhai_fn_rename_collision_with_itself.rs:12:15
|
||||
|
|
||||
12 | #[rhai_fn(name = "foo", name = "bar", name = "foo")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
| ^^^^
|
||||
|
||||
error[E0433]: failed to resolve: use of undeclared crate or module `test_module`
|
||||
--> ui_tests/rhai_fn_rename_collision_with_itself.rs:20:8
|
||||
|
@ -2,7 +2,7 @@ error: use attribute 'index_get' instead
|
||||
--> ui_tests/rhai_fn_rename_to_index_getter.rs:12:15
|
||||
|
|
||||
12 | #[rhai_fn(name = "index$get$")]
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
| ^^^^
|
||||
|
||||
error[E0433]: failed to resolve: use of undeclared crate or module `test_module`
|
||||
--> ui_tests/rhai_fn_rename_to_index_getter.rs:23:8
|
||||
|
@ -2,7 +2,7 @@ error: use attribute 'index_set' instead
|
||||
--> ui_tests/rhai_fn_rename_to_index_setter.rs:12:15
|
||||
|
|
||||
12 | #[rhai_fn(name = "index$set$")]
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
| ^^^^
|
||||
|
||||
error[E0433]: failed to resolve: use of undeclared crate or module `test_module`
|
||||
--> ui_tests/rhai_fn_rename_to_index_setter.rs:23:8
|
||||
|
@ -2,7 +2,7 @@ error: use attribute 'getter = "foo"' instead
|
||||
--> ui_tests/rhai_fn_rename_to_prop_getter.rs:12:15
|
||||
|
|
||||
12 | #[rhai_fn(name = "get$foo")]
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
| ^^^^
|
||||
|
||||
error[E0433]: failed to resolve: use of undeclared crate or module `test_module`
|
||||
--> ui_tests/rhai_fn_rename_to_prop_getter.rs:23:8
|
||||
|
@ -2,7 +2,7 @@ error: use attribute 'getter = "foo"' instead
|
||||
--> ui_tests/rhai_fn_rename_to_prop_setter.rs:12:15
|
||||
|
|
||||
12 | #[rhai_fn(name = "get$foo")]
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
| ^^^^
|
||||
|
||||
error[E0433]: failed to resolve: use of undeclared crate or module `test_module`
|
||||
--> ui_tests/rhai_fn_rename_to_prop_setter.rs:23:8
|
||||
|
@ -2,7 +2,7 @@ error: index setter requires exactly 3 parameters
|
||||
--> ui_tests/rhai_fn_setter_index_signature.rs:13:20
|
||||
|
|
||||
13 | pub fn test_fn(input: Point) -> bool {
|
||||
| ^^^^^^^^^^^^
|
||||
| ^^^^^
|
||||
|
||||
error[E0433]: failed to resolve: use of undeclared crate or module `test_module`
|
||||
--> ui_tests/rhai_fn_setter_index_signature.rs:23:8
|
||||
|
@ -2,7 +2,7 @@ error: conflicting setter
|
||||
--> ui_tests/rhai_fn_setter_multiple.rs:12:42
|
||||
|
|
||||
12 | #[rhai_fn(name = "foo", set = "foo", set = "bar")]
|
||||
| ^^^^^^^^^^^
|
||||
| ^^^
|
||||
|
||||
error[E0433]: failed to resolve: use of undeclared crate or module `test_module`
|
||||
--> ui_tests/rhai_fn_setter_multiple.rs:23:8
|
||||
|
@ -2,7 +2,7 @@ error: property setter cannot return any value
|
||||
--> ui_tests/rhai_fn_setter_return.rs:13:51
|
||||
|
|
||||
13 | pub fn test_fn(input: &mut Point, value: f32) -> bool {
|
||||
| ^^^^^^^
|
||||
| ^
|
||||
|
||||
error[E0433]: failed to resolve: use of undeclared crate or module `test_module`
|
||||
--> ui_tests/rhai_fn_setter_return.rs:24:8
|
||||
|
@ -2,7 +2,7 @@ error: property setter requires exactly 2 parameters
|
||||
--> ui_tests/rhai_fn_setter_signature.rs:13:20
|
||||
|
|
||||
13 | pub fn test_fn(input: Point) -> bool {
|
||||
| ^^^^^^^^^^^^
|
||||
| ^^^^^
|
||||
|
||||
error[E0433]: failed to resolve: use of undeclared crate or module `test_module`
|
||||
--> ui_tests/rhai_fn_setter_signature.rs:23:8
|
||||
|
@ -2,7 +2,7 @@ error: expecting attribute name
|
||||
--> ui_tests/rhai_mod_path_attr.rs:11:12
|
||||
|
|
||||
11 | #[rhai_mod(rhai::name = "thing")]
|
||||
| ^^^^^^^^^^
|
||||
| ^^^^
|
||||
|
||||
error[E0433]: failed to resolve: use of undeclared crate or module `test_module`
|
||||
--> ui_tests/rhai_mod_path_attr.rs:24:8
|
||||
|
@ -2,7 +2,7 @@ error: function parameters other than the first one cannot be passed by referenc
|
||||
--> ui_tests/second_shared_ref.rs:12:41
|
||||
|
|
||||
12 | pub fn test_fn(input: Clonable, factor: &bool) -> bool {
|
||||
| ^^^^^
|
||||
| ^
|
||||
|
||||
error[E0425]: cannot find function `test_fn` in this scope
|
||||
--> ui_tests/second_shared_ref.rs:23:8
|
||||
|
@ -219,17 +219,11 @@ impl Engine {
|
||||
|
||||
let orig_lib_len = global.lib.len();
|
||||
|
||||
let mut orig_tag = None;
|
||||
|
||||
if let Some(value) = options.tag {
|
||||
orig_tag = Some(mem::replace(&mut global.tag, value));
|
||||
}
|
||||
let orig_tag = options.tag.map(|v| mem::replace(&mut global.tag, v));
|
||||
let mut this_ptr = options.this_ptr;
|
||||
|
||||
global.lib.push(ast.shared_lib().clone());
|
||||
|
||||
let mut no_this_ptr = Dynamic::NULL;
|
||||
let this_ptr = options.this_ptr.unwrap_or(&mut no_this_ptr);
|
||||
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
let orig_embedded_module_resolver = std::mem::replace(
|
||||
&mut global.embedded_module_resolver,
|
||||
@ -264,7 +258,8 @@ impl Engine {
|
||||
global,
|
||||
caches,
|
||||
scope,
|
||||
this_ptr,
|
||||
this_ptr.as_deref_mut(),
|
||||
None,
|
||||
fn_def,
|
||||
args,
|
||||
rewind_scope,
|
||||
@ -278,7 +273,7 @@ impl Engine {
|
||||
if self.is_debugger_registered() {
|
||||
global.debugger_mut().status = crate::eval::DebuggerStatus::Terminate;
|
||||
let node = &crate::ast::Stmt::Noop(Position::NONE);
|
||||
self.run_debugger(global, caches, scope, this_ptr, node)?;
|
||||
self.run_debugger(global, caches, scope, this_ptr.as_deref_mut(), node)?;
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
|
@ -1,6 +1,7 @@
|
||||
//! Module containing all deprecated API that will be removed in the next major version.
|
||||
|
||||
use crate::func::RegisterNativeFunction;
|
||||
use crate::plugin::*;
|
||||
use crate::types::dynamic::Variant;
|
||||
use crate::{
|
||||
Dynamic, Engine, EvalAltResult, FnPtr, Identifier, ImmutableString, Module, NativeCallContext,
|
||||
@ -631,3 +632,593 @@ impl Module {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
#[export_module]
|
||||
pub mod deprecated_array_functions {
|
||||
use crate::packages::array_basic::array_functions::*;
|
||||
use crate::{Array, INT};
|
||||
|
||||
/// Iterate through all the elements in the array, applying a function named by `mapper` to each
|
||||
/// element in turn, and return the results as a new array.
|
||||
///
|
||||
/// # Deprecated API
|
||||
///
|
||||
/// This method is deprecated and will be removed from the next major version.
|
||||
/// Use `array.map(Fn("fn_name"))` instead.
|
||||
///
|
||||
/// # Function Parameters
|
||||
///
|
||||
/// A function with the same name as the value of `mapper` must exist taking these parameters:
|
||||
///
|
||||
/// * `element`: copy of array element
|
||||
/// * `index` _(optional)_: current index in the array
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// fn square(x) { x * x }
|
||||
///
|
||||
/// fn multiply(x, i) { x * i }
|
||||
///
|
||||
/// let x = [1, 2, 3, 4, 5];
|
||||
///
|
||||
/// let y = x.map("square");
|
||||
///
|
||||
/// print(y); // prints "[1, 4, 9, 16, 25]"
|
||||
///
|
||||
/// let y = x.map("multiply");
|
||||
///
|
||||
/// print(y); // prints "[0, 2, 6, 12, 20]"
|
||||
/// ```
|
||||
#[rhai_fn(name = "map", return_raw)]
|
||||
pub fn map_by_fn_name(
|
||||
ctx: NativeCallContext,
|
||||
array: Array,
|
||||
mapper: &str,
|
||||
) -> RhaiResultOf<Array> {
|
||||
map(ctx, array, FnPtr::new(mapper)?)
|
||||
}
|
||||
/// Iterate through all the elements in the array, applying a function named by `filter` to each
|
||||
/// element in turn, and return a copy of all elements (in order) that return `true` as a new array.
|
||||
///
|
||||
/// # Deprecated API
|
||||
///
|
||||
/// This method is deprecated and will be removed from the next major version.
|
||||
/// Use `array.filter(Fn("fn_name"))` instead.
|
||||
///
|
||||
/// # Function Parameters
|
||||
///
|
||||
/// A function with the same name as the value of `filter` must exist taking these parameters:
|
||||
///
|
||||
/// * `element`: copy of array element
|
||||
/// * `index` _(optional)_: current index in the array
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// fn screen(x, i) { x * i >= 10 }
|
||||
///
|
||||
/// let x = [1, 2, 3, 4, 5];
|
||||
///
|
||||
/// let y = x.filter("is_odd");
|
||||
///
|
||||
/// print(y); // prints "[1, 3, 5]"
|
||||
///
|
||||
/// let y = x.filter("screen");
|
||||
///
|
||||
/// print(y); // prints "[12, 20]"
|
||||
/// ```
|
||||
#[rhai_fn(name = "filter", return_raw)]
|
||||
pub fn filter_by_fn_name(
|
||||
ctx: NativeCallContext,
|
||||
array: Array,
|
||||
filter_func: &str,
|
||||
) -> RhaiResultOf<Array> {
|
||||
filter(ctx, array, FnPtr::new(filter_func)?)
|
||||
}
|
||||
/// Iterate through all the elements in the array, applying a function named by `filter` to each
|
||||
/// element in turn, and return the index of the first element that returns `true`.
|
||||
/// If no element returns `true`, `-1` is returned.
|
||||
///
|
||||
/// # Deprecated API
|
||||
///
|
||||
/// This method is deprecated and will be removed from the next major version.
|
||||
/// Use `array.index_of(Fn("fn_name"))` instead.
|
||||
///
|
||||
/// # Function Parameters
|
||||
///
|
||||
/// A function with the same name as the value of `filter` must exist taking these parameters:
|
||||
///
|
||||
/// * `element`: copy of array element
|
||||
/// * `index` _(optional)_: current index in the array
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// fn is_special(x) { x > 3 }
|
||||
///
|
||||
/// fn is_dumb(x) { x > 8 }
|
||||
///
|
||||
/// let x = [1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 5];
|
||||
///
|
||||
/// print(x.index_of("is_special")); // prints 3
|
||||
///
|
||||
/// print(x.index_of("is_dumb")); // prints -1
|
||||
/// ```
|
||||
#[rhai_fn(name = "index_of", return_raw, pure)]
|
||||
pub fn index_of_by_fn_name(
|
||||
ctx: NativeCallContext,
|
||||
array: &mut Array,
|
||||
filter: &str,
|
||||
) -> RhaiResultOf<INT> {
|
||||
index_of_filter(ctx, array, FnPtr::new(filter)?)
|
||||
}
|
||||
/// Iterate through all the elements in the array, starting from a particular `start` position,
|
||||
/// applying a function named by `filter` to each element in turn, and return the index of the
|
||||
/// first element that returns `true`. If no element returns `true`, `-1` is returned.
|
||||
///
|
||||
/// * If `start` < 0, position counts from the end of the array (`-1` is the last element).
|
||||
/// * If `start` < -length of array, position counts from the beginning of the array.
|
||||
/// * If `start` ≥ length of array, `-1` is returned.
|
||||
///
|
||||
/// # Deprecated API
|
||||
///
|
||||
/// This method is deprecated and will be removed from the next major version.
|
||||
/// Use `array.index_of(Fn("fn_name"), start)` instead.
|
||||
///
|
||||
/// # Function Parameters
|
||||
///
|
||||
/// A function with the same name as the value of `filter` must exist taking these parameters:
|
||||
///
|
||||
/// * `element`: copy of array element
|
||||
/// * `index` _(optional)_: current index in the array
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// fn plural(x) { x > 1 }
|
||||
///
|
||||
/// fn singular(x) { x < 2 }
|
||||
///
|
||||
/// fn screen(x, i) { x * i > 20 }
|
||||
///
|
||||
/// let x = [1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 5];
|
||||
///
|
||||
/// print(x.index_of("plural", 3)); // prints 5: 2 > 1
|
||||
///
|
||||
/// print(x.index_of("singular", 9)); // prints -1: nothing < 2 past index 9
|
||||
///
|
||||
/// print(x.index_of("plural", 15)); // prints -1: nothing found past end of array
|
||||
///
|
||||
/// print(x.index_of("plural", -5)); // prints 9: -5 = start from index 8
|
||||
///
|
||||
/// print(x.index_of("plural", -99)); // prints 1: -99 = start from beginning
|
||||
///
|
||||
/// print(x.index_of("screen", 8)); // prints 10: 3 * 10 > 20
|
||||
/// ```
|
||||
#[rhai_fn(name = "index_of", return_raw, pure)]
|
||||
pub fn index_of_by_fn_name_starting_from(
|
||||
ctx: NativeCallContext,
|
||||
array: &mut Array,
|
||||
filter: &str,
|
||||
start: INT,
|
||||
) -> RhaiResultOf<INT> {
|
||||
index_of_filter_starting_from(ctx, array, FnPtr::new(filter)?, start)
|
||||
}
|
||||
/// Return `true` if any element in the array that returns `true` when applied a function named
|
||||
/// by `filter`.
|
||||
///
|
||||
/// # Deprecated API
|
||||
///
|
||||
/// This method is deprecated and will be removed from the next major version.
|
||||
/// Use `array.some(Fn("fn_name"))` instead.
|
||||
///
|
||||
/// # Function Parameters
|
||||
///
|
||||
/// A function with the same name as the value of `filter` must exist taking these parameters:
|
||||
///
|
||||
/// * `element`: copy of array element
|
||||
/// * `index` _(optional)_: current index in the array
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// fn large(x) { x > 3 }
|
||||
///
|
||||
/// fn huge(x) { x > 10 }
|
||||
///
|
||||
/// fn screen(x, i) { i > x }
|
||||
///
|
||||
/// let x = [1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 5];
|
||||
///
|
||||
/// print(x.some("large")); // prints true
|
||||
///
|
||||
/// print(x.some("huge")); // prints false
|
||||
///
|
||||
/// print(x.some("screen")); // prints true
|
||||
/// ```
|
||||
#[rhai_fn(name = "some", return_raw, pure)]
|
||||
pub fn some_by_fn_name(
|
||||
ctx: NativeCallContext,
|
||||
array: &mut Array,
|
||||
filter: &str,
|
||||
) -> RhaiResultOf<bool> {
|
||||
some(ctx, array, FnPtr::new(filter)?)
|
||||
}
|
||||
/// Return `true` if all elements in the array return `true` when applied a function named by `filter`.
|
||||
///
|
||||
/// # Deprecated API
|
||||
///
|
||||
/// This method is deprecated and will be removed from the next major version.
|
||||
/// Use `array.all(Fn("fn_name"))` instead.
|
||||
///
|
||||
/// # Function Parameters
|
||||
///
|
||||
/// A function with the same name as the value of `filter` must exist taking these parameters:
|
||||
///
|
||||
/// * `element`: copy of array element
|
||||
/// * `index` _(optional)_: current index in the array
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let x = [1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 5];
|
||||
///
|
||||
/// print(x.all(|v| v > 3)); // prints false
|
||||
///
|
||||
/// print(x.all(|v| v > 1)); // prints true
|
||||
///
|
||||
/// print(x.all(|v, i| i > v)); // prints false
|
||||
/// ```
|
||||
#[rhai_fn(name = "all", return_raw, pure)]
|
||||
pub fn all_by_fn_name(
|
||||
ctx: NativeCallContext,
|
||||
array: &mut Array,
|
||||
filter: &str,
|
||||
) -> RhaiResultOf<bool> {
|
||||
all(ctx, array, FnPtr::new(filter)?)
|
||||
}
|
||||
/// Remove duplicated _consecutive_ elements from the array that return `true` when applied a
|
||||
/// function named by `comparer`.
|
||||
///
|
||||
/// # Deprecated API
|
||||
///
|
||||
/// This method is deprecated and will be removed from the next major version.
|
||||
/// Use `array.dedup(Fn("fn_name"))` instead.
|
||||
///
|
||||
/// No element is removed if the correct `comparer` function does not exist.
|
||||
///
|
||||
/// # Function Parameters
|
||||
///
|
||||
/// * `element1`: copy of the current array element to compare
|
||||
/// * `element2`: copy of the next array element to compare
|
||||
///
|
||||
/// ## Return Value
|
||||
///
|
||||
/// `true` if `element1 == element2`, otherwise `false`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// fn declining(a, b) { a >= b }
|
||||
///
|
||||
/// let x = [1, 2, 2, 2, 3, 1, 2, 3, 4, 3, 3, 2, 1];
|
||||
///
|
||||
/// x.dedup("declining");
|
||||
///
|
||||
/// print(x); // prints "[1, 2, 3, 4]"
|
||||
/// ```
|
||||
#[rhai_fn(name = "dedup", return_raw)]
|
||||
pub fn dedup_by_fn_name(
|
||||
ctx: NativeCallContext,
|
||||
array: &mut Array,
|
||||
comparer: &str,
|
||||
) -> RhaiResultOf<()> {
|
||||
Ok(dedup_by_comparer(ctx, array, FnPtr::new(comparer)?))
|
||||
}
|
||||
/// Reduce an array by iterating through all elements while applying a function named by `reducer`.
|
||||
///
|
||||
/// # Deprecated API
|
||||
///
|
||||
/// This method is deprecated and will be removed from the next major version.
|
||||
/// Use `array.reduce(Fn("fn_name"))` instead.
|
||||
///
|
||||
/// # Function Parameters
|
||||
///
|
||||
/// A function with the same name as the value of `reducer` must exist taking these parameters:
|
||||
///
|
||||
/// * `result`: accumulated result, initially `()`
|
||||
/// * `element`: copy of array element
|
||||
/// * `index` _(optional)_: current index in the array
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// fn process(r, x) {
|
||||
/// x + (r ?? 0)
|
||||
/// }
|
||||
/// fn process_extra(r, x, i) {
|
||||
/// x + i + (r ?? 0)
|
||||
/// }
|
||||
///
|
||||
/// let x = [1, 2, 3, 4, 5];
|
||||
///
|
||||
/// let y = x.reduce("process");
|
||||
///
|
||||
/// print(y); // prints 15
|
||||
///
|
||||
/// let y = x.reduce("process_extra");
|
||||
///
|
||||
/// print(y); // prints 25
|
||||
/// ```
|
||||
#[rhai_fn(name = "reduce", return_raw, pure)]
|
||||
pub fn reduce_by_fn_name(
|
||||
ctx: NativeCallContext,
|
||||
array: &mut Array,
|
||||
reducer: &str,
|
||||
) -> RhaiResult {
|
||||
reduce(ctx, array, FnPtr::new(reducer)?)
|
||||
}
|
||||
/// Reduce an array by iterating through all elements while applying a function named by `reducer`.
|
||||
///
|
||||
/// # Deprecated API
|
||||
///
|
||||
/// This method is deprecated and will be removed from the next major version.
|
||||
/// Use `array.reduce(Fn("fn_name"), initial)` instead.
|
||||
///
|
||||
/// # Function Parameters
|
||||
///
|
||||
/// A function with the same name as the value of `reducer` must exist taking these parameters:
|
||||
///
|
||||
/// * `result`: accumulated result, starting with the value of `initial`
|
||||
/// * `element`: copy of array element
|
||||
/// * `index` _(optional)_: current index in the array
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// fn process(r, x) { x + r }
|
||||
///
|
||||
/// fn process_extra(r, x, i) { x + i + r }
|
||||
///
|
||||
/// let x = [1, 2, 3, 4, 5];
|
||||
///
|
||||
/// let y = x.reduce("process", 5);
|
||||
///
|
||||
/// print(y); // prints 20
|
||||
///
|
||||
/// let y = x.reduce("process_extra", 5);
|
||||
///
|
||||
/// print(y); // prints 30
|
||||
/// ```
|
||||
#[rhai_fn(name = "reduce", return_raw, pure)]
|
||||
pub fn reduce_by_fn_name_with_initial(
|
||||
ctx: NativeCallContext,
|
||||
array: &mut Array,
|
||||
reducer: &str,
|
||||
initial: Dynamic,
|
||||
) -> RhaiResult {
|
||||
reduce_with_initial(ctx, array, FnPtr::new(reducer)?, initial)
|
||||
}
|
||||
/// Reduce an array by iterating through all elements, in _reverse_ order,
|
||||
/// while applying a function named by `reducer`.
|
||||
///
|
||||
/// # Deprecated API
|
||||
///
|
||||
/// This method is deprecated and will be removed from the next major version.
|
||||
/// Use `array.reduce_rev(Fn("fn_name"))` instead.
|
||||
///
|
||||
/// # Function Parameters
|
||||
///
|
||||
/// A function with the same name as the value of `reducer` must exist taking these parameters:
|
||||
///
|
||||
/// * `result`: accumulated result, initially `()`
|
||||
/// * `element`: copy of array element
|
||||
/// * `index` _(optional)_: current index in the array
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// fn process(r, x) {
|
||||
/// x + (r ?? 0)
|
||||
/// }
|
||||
/// fn process_extra(r, x, i) {
|
||||
/// x + i + (r ?? 0)
|
||||
/// }
|
||||
///
|
||||
/// let x = [1, 2, 3, 4, 5];
|
||||
///
|
||||
/// let y = x.reduce_rev("process");
|
||||
///
|
||||
/// print(y); // prints 15
|
||||
///
|
||||
/// let y = x.reduce_rev("process_extra");
|
||||
///
|
||||
/// print(y); // prints 25
|
||||
/// ```
|
||||
#[rhai_fn(name = "reduce_rev", return_raw, pure)]
|
||||
pub fn reduce_rev_by_fn_name(
|
||||
ctx: NativeCallContext,
|
||||
array: &mut Array,
|
||||
reducer: &str,
|
||||
) -> RhaiResult {
|
||||
reduce_rev(ctx, array, FnPtr::new(reducer)?)
|
||||
}
|
||||
/// Reduce an array by iterating through all elements, in _reverse_ order,
|
||||
/// while applying a function named by `reducer`.
|
||||
///
|
||||
/// # Deprecated API
|
||||
///
|
||||
/// This method is deprecated and will be removed from the next major version.
|
||||
/// Use `array.reduce_rev(Fn("fn_name"), initial)` instead.
|
||||
///
|
||||
/// # Function Parameters
|
||||
///
|
||||
/// A function with the same name as the value of `reducer` must exist taking these parameters:
|
||||
///
|
||||
/// * `result`: accumulated result, starting with the value of `initial`
|
||||
/// * `element`: copy of array element
|
||||
/// * `index` _(optional)_: current index in the array
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// fn process(r, x) { x + r }
|
||||
///
|
||||
/// fn process_extra(r, x, i) { x + i + r }
|
||||
///
|
||||
/// let x = [1, 2, 3, 4, 5];
|
||||
///
|
||||
/// let y = x.reduce_rev("process", 5);
|
||||
///
|
||||
/// print(y); // prints 20
|
||||
///
|
||||
/// let y = x.reduce_rev("process_extra", 5);
|
||||
///
|
||||
/// print(y); // prints 30
|
||||
/// ```
|
||||
#[rhai_fn(name = "reduce_rev", return_raw, pure)]
|
||||
pub fn reduce_rev_by_fn_name_with_initial(
|
||||
ctx: NativeCallContext,
|
||||
array: &mut Array,
|
||||
reducer: &str,
|
||||
initial: Dynamic,
|
||||
) -> RhaiResult {
|
||||
reduce_rev_with_initial(ctx, array, FnPtr::new(reducer)?, initial)
|
||||
}
|
||||
/// Sort the array based on applying a function named by `comparer`.
|
||||
///
|
||||
/// # Deprecated API
|
||||
///
|
||||
/// This method is deprecated and will be removed from the next major version.
|
||||
/// Use `array.sort(Fn("fn_name"))` instead.
|
||||
///
|
||||
/// # Function Parameters
|
||||
///
|
||||
/// A function with the same name as the value of `comparer` must exist taking these parameters:
|
||||
///
|
||||
/// * `element1`: copy of the current array element to compare
|
||||
/// * `element2`: copy of the next array element to compare
|
||||
///
|
||||
/// ## Return Value
|
||||
///
|
||||
/// * Any integer > 0 if `element1 > element2`
|
||||
/// * Zero if `element1 == element2`
|
||||
/// * Any integer < 0 if `element1 < element2`
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// fn reverse(a, b) {
|
||||
/// if a > b {
|
||||
/// -1
|
||||
/// } else if a < b {
|
||||
/// 1
|
||||
/// } else {
|
||||
/// 0
|
||||
/// }
|
||||
/// }
|
||||
/// let x = [1, 3, 5, 7, 9, 2, 4, 6, 8, 10];
|
||||
///
|
||||
/// x.sort("reverse");
|
||||
///
|
||||
/// print(x); // prints "[10, 9, 8, 7, 6, 5, 4, 3, 2, 1]"
|
||||
/// ```
|
||||
#[rhai_fn(name = "sort", return_raw)]
|
||||
pub fn sort_by_fn_name(
|
||||
ctx: NativeCallContext,
|
||||
array: &mut Array,
|
||||
comparer: &str,
|
||||
) -> RhaiResultOf<()> {
|
||||
sort(ctx, array, FnPtr::new(comparer)?)
|
||||
}
|
||||
/// Remove all elements in the array that returns `true` when applied a function named by `filter`
|
||||
/// and return them as a new array.
|
||||
///
|
||||
/// # Deprecated API
|
||||
///
|
||||
/// This method is deprecated and will be removed from the next major version.
|
||||
/// Use `array.drain(Fn("fn_name"))` instead.
|
||||
///
|
||||
/// # Function Parameters
|
||||
///
|
||||
/// A function with the same name as the value of `filter` must exist taking these parameters:
|
||||
///
|
||||
/// * `element`: copy of array element
|
||||
/// * `index` _(optional)_: current index in the array
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// fn small(x) { x < 3 }
|
||||
///
|
||||
/// fn screen(x, i) { x + i > 5 }
|
||||
///
|
||||
/// let x = [1, 2, 3, 4, 5];
|
||||
///
|
||||
/// let y = x.drain("small");
|
||||
///
|
||||
/// print(x); // prints "[3, 4, 5]"
|
||||
///
|
||||
/// print(y); // prints "[1, 2]"
|
||||
///
|
||||
/// let z = x.drain("screen");
|
||||
///
|
||||
/// print(x); // prints "[3, 4]"
|
||||
///
|
||||
/// print(z); // prints "[5]"
|
||||
/// ```
|
||||
#[rhai_fn(name = "drain", return_raw)]
|
||||
pub fn drain_by_fn_name(
|
||||
ctx: NativeCallContext,
|
||||
array: &mut Array,
|
||||
filter: &str,
|
||||
) -> RhaiResultOf<Array> {
|
||||
drain(ctx, array, FnPtr::new(filter)?)
|
||||
}
|
||||
/// Remove all elements in the array that do not return `true` when applied a function named by
|
||||
/// `filter` and return them as a new array.
|
||||
///
|
||||
/// # Deprecated API
|
||||
///
|
||||
/// This method is deprecated and will be removed from the next major version.
|
||||
/// Use `array.retain(Fn("fn_name"))` instead.
|
||||
///
|
||||
/// # Function Parameters
|
||||
///
|
||||
/// A function with the same name as the value of `filter` must exist taking these parameters:
|
||||
///
|
||||
/// * `element`: copy of array element
|
||||
/// * `index` _(optional)_: current index in the array
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// fn large(x) { x >= 3 }
|
||||
///
|
||||
/// fn screen(x, i) { x + i <= 5 }
|
||||
///
|
||||
/// let x = [1, 2, 3, 4, 5];
|
||||
///
|
||||
/// let y = x.retain("large");
|
||||
///
|
||||
/// print(x); // prints "[3, 4, 5]"
|
||||
///
|
||||
/// print(y); // prints "[1, 2]"
|
||||
///
|
||||
/// let z = x.retain("screen");
|
||||
///
|
||||
/// print(x); // prints "[3, 4]"
|
||||
///
|
||||
/// print(z); // prints "[5]"
|
||||
/// ```
|
||||
#[rhai_fn(name = "retain", return_raw)]
|
||||
pub fn retain_by_fn_name(
|
||||
ctx: NativeCallContext,
|
||||
array: &mut Array,
|
||||
filter: &str,
|
||||
) -> RhaiResultOf<Array> {
|
||||
retain(ctx, array, FnPtr::new(filter)?)
|
||||
}
|
||||
}
|
||||
|
@ -250,9 +250,8 @@ impl Engine {
|
||||
#[cfg(feature = "debugging")]
|
||||
if self.is_debugger_registered() {
|
||||
global.debugger_mut().status = crate::eval::DebuggerStatus::Terminate;
|
||||
let mut this_ptr = Dynamic::NULL;
|
||||
let node = &crate::ast::Stmt::Noop(Position::NONE);
|
||||
self.run_debugger(global, caches, scope, &mut this_ptr, node)?;
|
||||
self.run_debugger(global, caches, scope, None, node)?;
|
||||
}
|
||||
Ok(r)
|
||||
})
|
||||
|
@ -1,6 +1,10 @@
|
||||
//! Module that provide formatting services to the [`Engine`].
|
||||
use crate::packages::iter_basic::{BitRange, CharsStream, StepRange};
|
||||
use crate::parser::{ParseResult, ParseState};
|
||||
use crate::types::StringsInterner;
|
||||
use crate::{
|
||||
Engine, ExclusiveRange, FnPtr, ImmutableString, InclusiveRange, Position, RhaiError, ERR,
|
||||
Engine, ExclusiveRange, FnPtr, ImmutableString, InclusiveRange, OptimizationLevel, Position,
|
||||
RhaiError, Scope, SmartString, ERR,
|
||||
};
|
||||
use std::any::type_name;
|
||||
#[cfg(feature = "no_std")]
|
||||
@ -263,4 +267,33 @@ impl Engine {
|
||||
let t = self.map_type_name(type_name::<T>()).into();
|
||||
ERR::ErrorMismatchDataType(t, typ.into(), pos).into()
|
||||
}
|
||||
|
||||
/// Compact a script to eliminate insignificant whitespaces and comments.
|
||||
///
|
||||
/// This is useful to prepare a script for further compressing.
|
||||
///
|
||||
/// The output script is semantically identical to the input script, except smaller in size.
|
||||
///
|
||||
/// Unlike other uglifiers and minifiers, this method does not rename variables nor perform any
|
||||
/// optimization on the input script.
|
||||
#[inline]
|
||||
pub fn compact_script(&self, script: impl AsRef<str>) -> ParseResult<String> {
|
||||
let scripts = [script];
|
||||
let (mut stream, tc) = self.lex_raw(&scripts, self.token_mapper.as_deref());
|
||||
tc.borrow_mut().compressed = Some(String::new());
|
||||
stream.state.last_token = Some(SmartString::new_const());
|
||||
let scope = Scope::new();
|
||||
let mut interner = StringsInterner::new();
|
||||
let mut state = ParseState::new(&scope, &mut interner, tc);
|
||||
let mut _ast = self.parse(
|
||||
stream.peekable(),
|
||||
&mut state,
|
||||
#[cfg(not(feature = "no_optimize"))]
|
||||
OptimizationLevel::None,
|
||||
#[cfg(feature = "no_optimize")]
|
||||
(),
|
||||
)?;
|
||||
let tc = state.tokenizer_control.borrow();
|
||||
Ok(tc.compressed.as_ref().unwrap().into())
|
||||
}
|
||||
}
|
@ -23,7 +23,7 @@ pub mod limits_unchecked;
|
||||
|
||||
pub mod events;
|
||||
|
||||
pub mod type_names;
|
||||
pub mod formatting;
|
||||
|
||||
pub mod custom_syntax;
|
||||
|
||||
|
@ -131,9 +131,8 @@ impl Engine {
|
||||
#[cfg(feature = "debugging")]
|
||||
if self.is_debugger_registered() {
|
||||
global.debugger_mut().status = crate::eval::DebuggerStatus::Terminate;
|
||||
let mut this_ptr = crate::Dynamic::NULL;
|
||||
let node = &crate::ast::Stmt::Noop(crate::Position::NONE);
|
||||
self.run_debugger(global, caches, scope, &mut this_ptr, node)?;
|
||||
self.run_debugger(global, caches, scope, None, node)?;
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
|
@ -750,10 +750,8 @@ impl AST {
|
||||
#[cfg(feature = "internals")]
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
#[inline]
|
||||
pub fn iter_fn_def(&self) -> impl Iterator<Item = &super::ScriptFnDef> {
|
||||
self.lib
|
||||
.iter_script_fn()
|
||||
.map(|(.., fn_def)| fn_def.as_ref())
|
||||
pub fn iter_fn_def(&self) -> impl Iterator<Item = &crate::Shared<super::ScriptFnDef>> {
|
||||
self.lib.iter_script_fn().map(|(.., fn_def)| fn_def)
|
||||
}
|
||||
/// Iterate through all function definitions.
|
||||
///
|
||||
@ -762,10 +760,8 @@ impl AST {
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
#[allow(dead_code)]
|
||||
#[inline]
|
||||
pub(crate) fn iter_fn_def(&self) -> impl Iterator<Item = &super::ScriptFnDef> {
|
||||
self.lib
|
||||
.iter_script_fn()
|
||||
.map(|(.., fn_def)| fn_def.as_ref())
|
||||
pub(crate) fn iter_fn_def(&self) -> impl Iterator<Item = &crate::Shared<super::ScriptFnDef>> {
|
||||
self.lib.iter_script_fn().map(|(.., fn_def)| fn_def)
|
||||
}
|
||||
/// Iterate through all function definitions.
|
||||
///
|
||||
|
@ -19,9 +19,6 @@ pub use ident::Ident;
|
||||
pub use namespace::Namespace;
|
||||
#[cfg(feature = "no_module")]
|
||||
pub use namespace_none::Namespace;
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
pub use script_fn::EncapsulatedEnviron;
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
pub use script_fn::{ScriptFnDef, ScriptFnMetadata};
|
||||
pub use stmt::{
|
||||
|
@ -7,34 +7,12 @@ use crate::{FnArgsVec, ImmutableString};
|
||||
use std::prelude::v1::*;
|
||||
use std::{fmt, hash::Hash};
|
||||
|
||||
/// _(internals)_ Encapsulated AST environment.
|
||||
/// Exported under the `internals` feature only.
|
||||
///
|
||||
/// 1) other functions defined within the same AST
|
||||
/// 2) the stack of imported [modules][crate::Module]
|
||||
/// 3) global constants
|
||||
///
|
||||
/// Not available under `no_module` or `no_function`.
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct EncapsulatedEnviron {
|
||||
/// Functions defined within the same [`AST`][crate::AST].
|
||||
pub lib: crate::SharedModule,
|
||||
/// Imported [modules][crate::Module].
|
||||
pub imports: Box<[(ImmutableString, crate::SharedModule)]>,
|
||||
/// Globally-defined constants.
|
||||
pub constants: Option<crate::eval::SharedGlobalConstants>,
|
||||
}
|
||||
|
||||
/// _(internals)_ A type containing information on a script-defined function.
|
||||
/// Exported under the `internals` feature only.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ScriptFnDef {
|
||||
/// Function body.
|
||||
pub body: StmtBlock,
|
||||
/// Encapsulated AST environment, if any.
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
pub environ: Option<crate::Shared<EncapsulatedEnviron>>,
|
||||
/// Function name.
|
||||
pub name: ImmutableString,
|
||||
/// Function access mode.
|
||||
|
@ -573,7 +573,7 @@ pub enum Stmt {
|
||||
Assignment(Box<(OpAssignment, BinaryExpr)>),
|
||||
/// func `(` expr `,` ... `)`
|
||||
///
|
||||
/// Note - this is a duplicate of [`Expr::FnCall`] to cover the very common pattern of a single
|
||||
/// This is a duplicate of [`Expr::FnCall`] to cover the very common pattern of a single
|
||||
/// function call forming one statement.
|
||||
FnCall(Box<FnCallExpr>, Position),
|
||||
/// `{` stmt`;` ... `}`
|
||||
|
@ -340,7 +340,7 @@ impl Engine {
|
||||
global: &mut GlobalRuntimeState,
|
||||
caches: &mut Caches,
|
||||
scope: &mut Scope,
|
||||
this_ptr: &mut Dynamic,
|
||||
mut this_ptr: Option<&mut Dynamic>,
|
||||
expr: &Expr,
|
||||
new_val: Option<(Dynamic, &OpAssignment)>,
|
||||
) -> RhaiResult {
|
||||
@ -379,7 +379,13 @@ impl Engine {
|
||||
}
|
||||
// All other patterns - evaluate the arguments chain
|
||||
_ => self.eval_dot_index_chain_arguments(
|
||||
global, caches, scope, this_ptr, expr, rhs, idx_values,
|
||||
global,
|
||||
caches,
|
||||
scope,
|
||||
this_ptr.as_deref_mut(),
|
||||
expr,
|
||||
rhs,
|
||||
idx_values,
|
||||
)?,
|
||||
}
|
||||
|
||||
@ -387,22 +393,13 @@ impl Engine {
|
||||
// id.??? or id[???]
|
||||
Expr::Variable(.., var_pos) => {
|
||||
#[cfg(feature = "debugging")]
|
||||
self.run_debugger(global, caches, scope, this_ptr, lhs)?;
|
||||
self.run_debugger(global, caches, scope, this_ptr.as_deref_mut(), lhs)?;
|
||||
self.track_operation(global, *var_pos)?;
|
||||
|
||||
let target = &mut self.search_namespace(global, caches, scope, this_ptr, lhs)?;
|
||||
let mut this_ptr = Dynamic::NULL;
|
||||
|
||||
self.eval_dot_index_chain_raw(
|
||||
global,
|
||||
caches,
|
||||
&mut this_ptr,
|
||||
lhs,
|
||||
expr,
|
||||
target,
|
||||
rhs,
|
||||
idx_values,
|
||||
new_val,
|
||||
global, caches, None, lhs, expr, target, rhs, idx_values, new_val,
|
||||
)
|
||||
}
|
||||
// {expr}.??? = ??? or {expr}[???] = ???
|
||||
@ -410,7 +407,7 @@ impl Engine {
|
||||
// {expr}.??? or {expr}[???]
|
||||
lhs_expr => {
|
||||
let value = self
|
||||
.eval_expr(global, caches, scope, this_ptr, lhs_expr)?
|
||||
.eval_expr(global, caches, scope, this_ptr.as_deref_mut(), lhs_expr)?
|
||||
.flatten();
|
||||
let obj_ptr = &mut value.into();
|
||||
|
||||
@ -428,7 +425,7 @@ impl Engine {
|
||||
global: &mut GlobalRuntimeState,
|
||||
caches: &mut Caches,
|
||||
scope: &mut Scope,
|
||||
this_ptr: &mut Dynamic,
|
||||
mut this_ptr: Option<&mut Dynamic>,
|
||||
parent: &Expr,
|
||||
expr: &Expr,
|
||||
idx_values: &mut FnArgsVec<Dynamic>,
|
||||
@ -440,12 +437,10 @@ impl Engine {
|
||||
match expr {
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Expr::MethodCall(x, ..) if chain_type == ChainType::Dotting && !x.is_qualified() => {
|
||||
for arg_expr in &x.args {
|
||||
idx_values.push(
|
||||
self.get_arg_value(global, caches, scope, this_ptr, arg_expr)?
|
||||
.0
|
||||
.flatten(),
|
||||
);
|
||||
for expr in &x.args {
|
||||
let arg_value =
|
||||
self.get_arg_value(global, caches, scope, this_ptr.as_deref_mut(), expr)?;
|
||||
idx_values.push(arg_value.0.flatten());
|
||||
}
|
||||
}
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
@ -474,12 +469,15 @@ impl Engine {
|
||||
Expr::MethodCall(x, ..)
|
||||
if chain_type == ChainType::Dotting && !x.is_qualified() =>
|
||||
{
|
||||
for arg_expr in &x.args {
|
||||
_arg_values.push(
|
||||
self.get_arg_value(global, caches, scope, this_ptr, arg_expr)?
|
||||
.0
|
||||
.flatten(),
|
||||
);
|
||||
for expr in &x.args {
|
||||
let arg_value = self.get_arg_value(
|
||||
global,
|
||||
caches,
|
||||
scope,
|
||||
this_ptr.as_deref_mut(),
|
||||
expr,
|
||||
)?;
|
||||
_arg_values.push(arg_value.0.flatten());
|
||||
}
|
||||
}
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
@ -493,7 +491,7 @@ impl Engine {
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
_ if chain_type == ChainType::Indexing => {
|
||||
_arg_values.push(
|
||||
self.eval_expr(global, caches, scope, this_ptr, lhs)?
|
||||
self.eval_expr(global, caches, scope, this_ptr.as_deref_mut(), lhs)?
|
||||
.flatten(),
|
||||
);
|
||||
}
|
||||
@ -530,7 +528,7 @@ impl Engine {
|
||||
&self,
|
||||
global: &mut GlobalRuntimeState,
|
||||
caches: &mut Caches,
|
||||
this_ptr: &mut Dynamic,
|
||||
mut this_ptr: Option<&mut Dynamic>,
|
||||
root: &Expr,
|
||||
parent: &Expr,
|
||||
target: &mut Target,
|
||||
@ -560,7 +558,7 @@ impl Engine {
|
||||
if !parent.options().contains(ASTFlags::BREAK) =>
|
||||
{
|
||||
#[cfg(feature = "debugging")]
|
||||
self.run_debugger(global, caches, scope, this_ptr, parent)?;
|
||||
self.run_debugger(global, caches, scope, this_ptr.as_deref_mut(), parent)?;
|
||||
|
||||
let idx_val = &mut idx_values.pop().unwrap();
|
||||
let mut idx_val_for_setter = idx_val.clone();
|
||||
@ -843,7 +841,13 @@ impl Engine {
|
||||
let val_target = &mut match x.lhs {
|
||||
Expr::Property(ref p, pos) => {
|
||||
#[cfg(feature = "debugging")]
|
||||
self.run_debugger(global, caches, scope, this_ptr, _node)?;
|
||||
self.run_debugger(
|
||||
global,
|
||||
caches,
|
||||
scope,
|
||||
this_ptr.as_deref_mut(),
|
||||
_node,
|
||||
)?;
|
||||
|
||||
let index = &mut p.2.clone().into();
|
||||
self.get_indexed_mut(
|
||||
@ -854,7 +858,11 @@ impl Engine {
|
||||
Expr::MethodCall(ref x, pos) if !x.is_qualified() => {
|
||||
#[cfg(feature = "debugging")]
|
||||
let reset = self.run_debugger_with_reset(
|
||||
global, caches, scope, this_ptr, _node,
|
||||
global,
|
||||
caches,
|
||||
scope,
|
||||
this_ptr.as_deref_mut(),
|
||||
_node,
|
||||
)?;
|
||||
#[cfg(feature = "debugging")]
|
||||
auto_restore!(global if Some(reset) => move |g| g.debugger_mut().reset_status(reset));
|
||||
@ -896,7 +904,13 @@ impl Engine {
|
||||
// xxx.prop[expr] | xxx.prop.expr
|
||||
Expr::Property(ref p, pos) => {
|
||||
#[cfg(feature = "debugging")]
|
||||
self.run_debugger(global, caches, scope, this_ptr, _node)?;
|
||||
self.run_debugger(
|
||||
global,
|
||||
caches,
|
||||
scope,
|
||||
this_ptr.as_deref_mut(),
|
||||
_node,
|
||||
)?;
|
||||
|
||||
let ((getter, hash_get), (setter, hash_set), name) = &**p;
|
||||
let args = &mut [target.as_mut()];
|
||||
@ -972,7 +986,11 @@ impl Engine {
|
||||
let val = {
|
||||
#[cfg(feature = "debugging")]
|
||||
let reset = self.run_debugger_with_reset(
|
||||
global, caches, scope, this_ptr, _node,
|
||||
global,
|
||||
caches,
|
||||
scope,
|
||||
this_ptr.as_deref_mut(),
|
||||
_node,
|
||||
)?;
|
||||
#[cfg(feature = "debugging")]
|
||||
auto_restore!(global if Some(reset) => move |g| g.debugger_mut().reset_status(reset));
|
||||
|
@ -410,7 +410,7 @@ impl Engine {
|
||||
global: &mut GlobalRuntimeState,
|
||||
caches: &mut Caches,
|
||||
scope: &mut Scope,
|
||||
this_ptr: &mut Dynamic,
|
||||
this_ptr: Option<&mut Dynamic>,
|
||||
node: impl Into<ASTNode<'a>>,
|
||||
) -> RhaiResultOf<()> {
|
||||
if self.is_debugger_registered() {
|
||||
@ -435,7 +435,7 @@ impl Engine {
|
||||
global: &mut GlobalRuntimeState,
|
||||
caches: &mut Caches,
|
||||
scope: &mut Scope,
|
||||
this_ptr: &mut Dynamic,
|
||||
this_ptr: Option<&mut Dynamic>,
|
||||
node: impl Into<ASTNode<'a>>,
|
||||
) -> RhaiResultOf<Option<DebuggerStatus>> {
|
||||
if self.is_debugger_registered() {
|
||||
@ -456,7 +456,7 @@ impl Engine {
|
||||
global: &mut GlobalRuntimeState,
|
||||
caches: &mut Caches,
|
||||
scope: &mut Scope,
|
||||
this_ptr: &mut Dynamic,
|
||||
this_ptr: Option<&mut Dynamic>,
|
||||
node: impl Into<ASTNode<'a>>,
|
||||
) -> RhaiResultOf<Option<DebuggerStatus>> {
|
||||
let node = node.into();
|
||||
@ -502,7 +502,7 @@ impl Engine {
|
||||
global: &mut GlobalRuntimeState,
|
||||
caches: &mut Caches,
|
||||
scope: &mut Scope,
|
||||
this_ptr: &mut Dynamic,
|
||||
this_ptr: Option<&mut Dynamic>,
|
||||
node: ASTNode<'a>,
|
||||
event: DebuggerEvent,
|
||||
) -> Result<Option<DebuggerStatus>, Box<crate::EvalAltResult>> {
|
||||
|
@ -17,7 +17,7 @@ pub struct EvalContext<'a, 's, 'ps, 'g, 'c, 't> {
|
||||
/// The current [`Scope`].
|
||||
scope: &'s mut Scope<'ps>,
|
||||
/// The current bound `this` pointer, if any.
|
||||
this_ptr: &'t mut Dynamic,
|
||||
this_ptr: Option<&'t mut Dynamic>,
|
||||
}
|
||||
|
||||
impl<'a, 's, 'ps, 'g, 'c, 't> EvalContext<'a, 's, 'ps, 'g, 'c, 't> {
|
||||
@ -29,7 +29,7 @@ impl<'a, 's, 'ps, 'g, 'c, 't> EvalContext<'a, 's, 'ps, 'g, 'c, 't> {
|
||||
global: &'g mut GlobalRuntimeState,
|
||||
caches: &'c mut Caches,
|
||||
scope: &'s mut Scope<'ps>,
|
||||
this_ptr: &'t mut Dynamic,
|
||||
this_ptr: Option<&'t mut Dynamic>,
|
||||
) -> Self {
|
||||
Self {
|
||||
engine,
|
||||
@ -118,24 +118,16 @@ impl<'a, 's, 'ps, 'g, 'c, 't> EvalContext<'a, 's, 'ps, 'g, 'c, 't> {
|
||||
&self.global.lib
|
||||
}
|
||||
/// The current bound `this` pointer, if any.
|
||||
#[inline]
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn this_ptr(&self) -> Option<&Dynamic> {
|
||||
if self.this_ptr.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(self.this_ptr)
|
||||
}
|
||||
self.this_ptr.as_deref()
|
||||
}
|
||||
/// Mutable reference to the current bound `this` pointer, if any.
|
||||
#[inline]
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn this_ptr_mut(&mut self) -> Option<&mut Dynamic> {
|
||||
if self.this_ptr.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(self.this_ptr)
|
||||
}
|
||||
self.this_ptr.as_deref_mut()
|
||||
}
|
||||
/// The current nesting level of function calls.
|
||||
#[inline(always)]
|
||||
@ -177,19 +169,20 @@ impl<'a, 's, 'ps, 'g, 'c, 't> EvalContext<'a, 's, 'ps, 'g, 'c, 't> {
|
||||
rewind_scope: bool,
|
||||
) -> crate::RhaiResult {
|
||||
let expr: &crate::ast::Expr = expr;
|
||||
let this_ptr = self.this_ptr.as_deref_mut();
|
||||
|
||||
match expr {
|
||||
crate::ast::Expr::Stmt(statements) => self.engine.eval_stmt_block(
|
||||
crate::ast::Expr::Stmt(stmts) => self.engine.eval_stmt_block(
|
||||
self.global,
|
||||
self.caches,
|
||||
self.scope,
|
||||
self.this_ptr,
|
||||
statements,
|
||||
this_ptr,
|
||||
stmts,
|
||||
rewind_scope,
|
||||
),
|
||||
_ => self
|
||||
.engine
|
||||
.eval_expr(self.global, self.caches, self.scope, self.this_ptr, expr),
|
||||
.eval_expr(self.global, self.caches, self.scope, this_ptr, expr),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -53,7 +53,7 @@ impl Engine {
|
||||
global: &mut GlobalRuntimeState,
|
||||
caches: &mut Caches,
|
||||
scope: &'s mut Scope,
|
||||
this_ptr: &'s mut Dynamic,
|
||||
this_ptr: Option<&'s mut Dynamic>,
|
||||
expr: &Expr,
|
||||
) -> RhaiResultOf<Target<'s>> {
|
||||
match expr {
|
||||
@ -132,7 +132,7 @@ impl Engine {
|
||||
global: &mut GlobalRuntimeState,
|
||||
caches: &mut Caches,
|
||||
scope: &'s mut Scope,
|
||||
this_ptr: &'s mut Dynamic,
|
||||
this_ptr: Option<&'s mut Dynamic>,
|
||||
expr: &Expr,
|
||||
) -> RhaiResultOf<Target<'s>> {
|
||||
// Make sure that the pointer indirection is taken only when absolutely necessary.
|
||||
@ -142,10 +142,10 @@ impl Engine {
|
||||
Expr::Variable(v, None, ..)
|
||||
if v.0.is_none() && v.1.is_empty() && v.3 == KEYWORD_THIS =>
|
||||
{
|
||||
return if this_ptr.is_null() {
|
||||
Err(ERR::ErrorUnboundThis(expr.position()).into())
|
||||
} else {
|
||||
return if let Some(this_ptr) = this_ptr {
|
||||
Ok(this_ptr.into())
|
||||
} else {
|
||||
Err(ERR::ErrorUnboundThis(expr.position()).into())
|
||||
};
|
||||
}
|
||||
|
||||
@ -223,7 +223,7 @@ impl Engine {
|
||||
global: &mut GlobalRuntimeState,
|
||||
caches: &mut Caches,
|
||||
scope: &mut Scope,
|
||||
this_ptr: &mut Dynamic,
|
||||
mut this_ptr: Option<&mut Dynamic>,
|
||||
expr: &Expr,
|
||||
) -> RhaiResult {
|
||||
// Coded this way for better branch prediction.
|
||||
@ -233,7 +233,8 @@ impl Engine {
|
||||
// binary operators are also function calls.
|
||||
if let Expr::FnCall(x, pos) = expr {
|
||||
#[cfg(feature = "debugging")]
|
||||
let reset = self.run_debugger_with_reset(global, caches, scope, this_ptr, expr)?;
|
||||
let reset =
|
||||
self.run_debugger_with_reset(global, caches, scope, this_ptr.as_deref_mut(), expr)?;
|
||||
#[cfg(feature = "debugging")]
|
||||
auto_restore!(global if Some(reset) => move |g| g.debugger_mut().reset_status(reset));
|
||||
|
||||
@ -247,16 +248,14 @@ impl Engine {
|
||||
// will cost more than the mis-predicted `match` branch.
|
||||
if let Expr::Variable(x, index, var_pos) = expr {
|
||||
#[cfg(feature = "debugging")]
|
||||
self.run_debugger(global, caches, scope, this_ptr, expr)?;
|
||||
self.run_debugger(global, caches, scope, this_ptr.as_deref_mut(), expr)?;
|
||||
|
||||
self.track_operation(global, expr.position())?;
|
||||
|
||||
return if index.is_none() && x.0.is_none() && x.3 == KEYWORD_THIS {
|
||||
if this_ptr.is_null() {
|
||||
ERR::ErrorUnboundThis(*var_pos).into()
|
||||
} else {
|
||||
Ok(this_ptr.clone())
|
||||
}
|
||||
this_ptr
|
||||
.ok_or_else(|| ERR::ErrorUnboundThis(*var_pos).into())
|
||||
.cloned()
|
||||
} else {
|
||||
self.search_namespace(global, caches, scope, this_ptr, expr)
|
||||
.map(Target::take_or_clone)
|
||||
@ -264,7 +263,8 @@ impl Engine {
|
||||
}
|
||||
|
||||
#[cfg(feature = "debugging")]
|
||||
let reset = self.run_debugger_with_reset(global, caches, scope, this_ptr, expr)?;
|
||||
let reset =
|
||||
self.run_debugger_with_reset(global, caches, scope, this_ptr.as_deref_mut(), expr)?;
|
||||
#[cfg(feature = "debugging")]
|
||||
auto_restore!(global if Some(reset) => move |g| g.debugger_mut().reset_status(reset));
|
||||
|
||||
@ -291,7 +291,7 @@ impl Engine {
|
||||
x.iter()
|
||||
.try_for_each(|expr| {
|
||||
let item = self
|
||||
.eval_expr(global, caches, scope, this_ptr, expr)?
|
||||
.eval_expr(global, caches, scope, this_ptr.as_deref_mut(), expr)?
|
||||
.flatten();
|
||||
|
||||
op_info.pos = expr.start_position();
|
||||
@ -312,7 +312,13 @@ impl Engine {
|
||||
crate::Array::with_capacity(x.len()),
|
||||
|mut array, item_expr| {
|
||||
let value = self
|
||||
.eval_expr(global, caches, scope, this_ptr, item_expr)?
|
||||
.eval_expr(
|
||||
global,
|
||||
caches,
|
||||
scope,
|
||||
this_ptr.as_deref_mut(),
|
||||
item_expr,
|
||||
)?
|
||||
.flatten();
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
@ -344,7 +350,7 @@ impl Engine {
|
||||
x.0.iter()
|
||||
.try_fold(x.1.clone(), |mut map, (key, value_expr)| {
|
||||
let value = self
|
||||
.eval_expr(global, caches, scope, this_ptr, value_expr)?
|
||||
.eval_expr(global, caches, scope, this_ptr.as_deref_mut(), value_expr)?
|
||||
.flatten();
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
@ -367,7 +373,7 @@ impl Engine {
|
||||
}
|
||||
|
||||
Expr::And(x, ..) => Ok((self
|
||||
.eval_expr(global, caches, scope, this_ptr, &x.lhs)?
|
||||
.eval_expr(global, caches, scope, this_ptr.as_deref_mut(), &x.lhs)?
|
||||
.as_bool()
|
||||
.map_err(|typ| self.make_type_mismatch_err::<bool>(typ, x.lhs.position()))?
|
||||
&& self
|
||||
@ -377,7 +383,7 @@ impl Engine {
|
||||
.into()),
|
||||
|
||||
Expr::Or(x, ..) => Ok((self
|
||||
.eval_expr(global, caches, scope, this_ptr, &x.lhs)?
|
||||
.eval_expr(global, caches, scope, this_ptr.as_deref_mut(), &x.lhs)?
|
||||
.as_bool()
|
||||
.map_err(|typ| self.make_type_mismatch_err::<bool>(typ, x.lhs.position()))?
|
||||
|| self
|
||||
@ -387,7 +393,8 @@ impl Engine {
|
||||
.into()),
|
||||
|
||||
Expr::Coalesce(x, ..) => {
|
||||
let value = self.eval_expr(global, caches, scope, this_ptr, &x.lhs)?;
|
||||
let value =
|
||||
self.eval_expr(global, caches, scope, this_ptr.as_deref_mut(), &x.lhs)?;
|
||||
|
||||
if value.is_unit() {
|
||||
self.eval_expr(global, caches, scope, this_ptr, &x.rhs)
|
||||
|
@ -32,7 +32,7 @@ impl Engine {
|
||||
global: &mut GlobalRuntimeState,
|
||||
caches: &mut Caches,
|
||||
scope: &mut Scope,
|
||||
this_ptr: &mut Dynamic,
|
||||
mut this_ptr: Option<&mut Dynamic>,
|
||||
statements: &[Stmt],
|
||||
restore_orig_state: bool,
|
||||
) -> RhaiResult {
|
||||
@ -71,6 +71,8 @@ impl Engine {
|
||||
|
||||
// Run the statements
|
||||
statements.iter().try_fold(Dynamic::UNIT, |_, stmt| {
|
||||
let this_ptr = this_ptr.as_deref_mut();
|
||||
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
let imports_len = global.num_imports();
|
||||
|
||||
@ -198,12 +200,13 @@ impl Engine {
|
||||
global: &mut GlobalRuntimeState,
|
||||
caches: &mut Caches,
|
||||
scope: &mut Scope,
|
||||
this_ptr: &mut Dynamic,
|
||||
mut this_ptr: Option<&mut Dynamic>,
|
||||
stmt: &Stmt,
|
||||
rewind_scope: bool,
|
||||
) -> RhaiResult {
|
||||
#[cfg(feature = "debugging")]
|
||||
let reset = self.run_debugger_with_reset(global, caches, scope, this_ptr, stmt)?;
|
||||
let reset =
|
||||
self.run_debugger_with_reset(global, caches, scope, this_ptr.as_deref_mut(), stmt)?;
|
||||
#[cfg(feature = "debugging")]
|
||||
auto_restore!(global if Some(reset) => move |g| g.debugger_mut().reset_status(reset));
|
||||
|
||||
@ -227,7 +230,7 @@ impl Engine {
|
||||
|
||||
if let Expr::Variable(x, ..) = lhs {
|
||||
let rhs_val = self
|
||||
.eval_expr(global, caches, scope, this_ptr, rhs)?
|
||||
.eval_expr(global, caches, scope, this_ptr.as_deref_mut(), rhs)?
|
||||
.flatten();
|
||||
|
||||
let mut target = self.search_namespace(global, caches, scope, this_ptr, lhs)?;
|
||||
@ -259,7 +262,7 @@ impl Engine {
|
||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||
{
|
||||
let rhs_val = self
|
||||
.eval_expr(global, caches, scope, this_ptr, rhs)?
|
||||
.eval_expr(global, caches, scope, this_ptr.as_deref_mut(), rhs)?
|
||||
.flatten()
|
||||
.intern_string(self);
|
||||
|
||||
@ -310,7 +313,7 @@ impl Engine {
|
||||
let (expr, if_block, else_block) = &**x;
|
||||
|
||||
let guard_val = self
|
||||
.eval_expr(global, caches, scope, this_ptr, expr)?
|
||||
.eval_expr(global, caches, scope, this_ptr.as_deref_mut(), expr)?
|
||||
.as_bool()
|
||||
.map_err(|typ| self.make_type_mismatch_err::<bool>(typ, expr.position()))?;
|
||||
|
||||
@ -337,7 +340,7 @@ impl Engine {
|
||||
|
||||
let mut result = None;
|
||||
|
||||
let value = self.eval_expr(global, caches, scope, this_ptr, expr)?;
|
||||
let value = self.eval_expr(global, caches, scope, this_ptr.as_deref_mut(), expr)?;
|
||||
|
||||
if value.is_hashable() {
|
||||
let hasher = &mut get_hasher();
|
||||
@ -354,7 +357,7 @@ impl Engine {
|
||||
let cond_result = match block.condition {
|
||||
Expr::BoolConstant(b, ..) => b,
|
||||
ref c => self
|
||||
.eval_expr(global, caches, scope, this_ptr, c)?
|
||||
.eval_expr(global, caches, scope, this_ptr.as_deref_mut(), c)?
|
||||
.as_bool()
|
||||
.map_err(|typ| {
|
||||
self.make_type_mismatch_err::<bool>(typ, c.position())
|
||||
@ -376,7 +379,7 @@ impl Engine {
|
||||
let cond_result = match block.condition {
|
||||
Expr::BoolConstant(b, ..) => b,
|
||||
ref c => self
|
||||
.eval_expr(global, caches, scope, this_ptr, c)?
|
||||
.eval_expr(global, caches, scope, this_ptr.as_deref_mut(), c)?
|
||||
.as_bool()
|
||||
.map_err(|typ| {
|
||||
self.make_type_mismatch_err::<bool>(typ, c.position())
|
||||
@ -409,14 +412,15 @@ impl Engine {
|
||||
}
|
||||
|
||||
loop {
|
||||
if let Err(err) =
|
||||
self.eval_stmt_block(global, caches, scope, this_ptr, body, true)
|
||||
{
|
||||
match *err {
|
||||
let this_ptr = this_ptr.as_deref_mut();
|
||||
|
||||
match self.eval_stmt_block(global, caches, scope, this_ptr, body, true) {
|
||||
Ok(..) => (),
|
||||
Err(err) => match *err {
|
||||
ERR::LoopBreak(false, ..) => (),
|
||||
ERR::LoopBreak(true, value, ..) => break Ok(value),
|
||||
_ => break Err(err),
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -427,7 +431,7 @@ impl Engine {
|
||||
|
||||
loop {
|
||||
let condition = self
|
||||
.eval_expr(global, caches, scope, this_ptr, expr)?
|
||||
.eval_expr(global, caches, scope, this_ptr.as_deref_mut(), expr)?
|
||||
.as_bool()
|
||||
.map_err(|typ| self.make_type_mismatch_err::<bool>(typ, expr.position()))?;
|
||||
|
||||
@ -439,14 +443,15 @@ impl Engine {
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Err(err) =
|
||||
self.eval_stmt_block(global, caches, scope, this_ptr, body, true)
|
||||
{
|
||||
match *err {
|
||||
let this_ptr = this_ptr.as_deref_mut();
|
||||
|
||||
match self.eval_stmt_block(global, caches, scope, this_ptr, body, true) {
|
||||
Ok(..) => (),
|
||||
Err(err) => match *err {
|
||||
ERR::LoopBreak(false, ..) => (),
|
||||
ERR::LoopBreak(true, value, ..) => break Ok(value),
|
||||
_ => break Err(err),
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -458,19 +463,20 @@ impl Engine {
|
||||
|
||||
loop {
|
||||
if !body.is_empty() {
|
||||
if let Err(err) =
|
||||
self.eval_stmt_block(global, caches, scope, this_ptr, body, true)
|
||||
{
|
||||
match *err {
|
||||
let this_ptr = this_ptr.as_deref_mut();
|
||||
|
||||
match self.eval_stmt_block(global, caches, scope, this_ptr, body, true) {
|
||||
Ok(..) => (),
|
||||
Err(err) => match *err {
|
||||
ERR::LoopBreak(false, ..) => continue,
|
||||
ERR::LoopBreak(true, value, ..) => break Ok(value),
|
||||
_ => break Err(err),
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
let condition = self
|
||||
.eval_expr(global, caches, scope, this_ptr, expr)?
|
||||
.eval_expr(global, caches, scope, this_ptr.as_deref_mut(), expr)?
|
||||
.as_bool()
|
||||
.map_err(|typ| self.make_type_mismatch_err::<bool>(typ, expr.position()))?;
|
||||
|
||||
@ -485,7 +491,7 @@ impl Engine {
|
||||
let (var_name, counter, expr, statements) = &**x;
|
||||
|
||||
let iter_obj = self
|
||||
.eval_expr(global, caches, scope, this_ptr, expr)?
|
||||
.eval_expr(global, caches, scope, this_ptr.as_deref_mut(), expr)?
|
||||
.flatten();
|
||||
|
||||
let iter_type = iter_obj.type_id();
|
||||
@ -566,6 +572,8 @@ impl Engine {
|
||||
continue;
|
||||
}
|
||||
|
||||
let this_ptr = this_ptr.as_deref_mut();
|
||||
|
||||
match self.eval_stmt_block(global, caches, scope, this_ptr, statements, true) {
|
||||
Ok(_) => (),
|
||||
Err(err) => match *err {
|
||||
@ -603,7 +611,14 @@ impl Engine {
|
||||
catch_block,
|
||||
} = &**x;
|
||||
|
||||
match self.eval_stmt_block(global, caches, scope, this_ptr, try_block, true) {
|
||||
match self.eval_stmt_block(
|
||||
global,
|
||||
caches,
|
||||
scope,
|
||||
this_ptr.as_deref_mut(),
|
||||
try_block,
|
||||
true,
|
||||
) {
|
||||
r @ Ok(_) => r,
|
||||
Err(err) if err.is_pseudo_error() => Err(err),
|
||||
Err(err) if !err.is_catchable() => Err(err),
|
||||
@ -650,6 +665,8 @@ impl Engine {
|
||||
scope.push(catch_var.name.clone(), err_value);
|
||||
}
|
||||
|
||||
let this_ptr = this_ptr.as_deref_mut();
|
||||
|
||||
self.eval_stmt_block(global, caches, scope, this_ptr, catch_block, true)
|
||||
.map(|_| Dynamic::UNIT)
|
||||
.map_err(|result_err| match *result_err {
|
||||
@ -707,7 +724,8 @@ impl Engine {
|
||||
nesting_level: global.scope_level,
|
||||
will_shadow,
|
||||
};
|
||||
let context = EvalContext::new(self, global, caches, scope, this_ptr);
|
||||
let context =
|
||||
EvalContext::new(self, global, caches, scope, this_ptr.as_deref_mut());
|
||||
|
||||
if !filter(true, info, context)? {
|
||||
return Err(ERR::ErrorForbiddenVariable(var_name.to_string(), *pos).into());
|
||||
@ -866,7 +884,7 @@ impl Engine {
|
||||
|
||||
/// Evaluate a list of statements with no `this` pointer.
|
||||
/// This is commonly used to evaluate a list of statements in an [`AST`][crate::AST] or a script function body.
|
||||
#[inline]
|
||||
#[inline(always)]
|
||||
pub(crate) fn eval_global_statements(
|
||||
&self,
|
||||
global: &mut GlobalRuntimeState,
|
||||
@ -874,9 +892,7 @@ impl Engine {
|
||||
scope: &mut Scope,
|
||||
statements: &[Stmt],
|
||||
) -> RhaiResult {
|
||||
let mut this_ptr = Dynamic::NULL;
|
||||
|
||||
self.eval_stmt_block(global, caches, scope, &mut this_ptr, statements, false)
|
||||
self.eval_stmt_block(global, caches, scope, None, statements, false)
|
||||
.or_else(|err| match *err {
|
||||
ERR::Return(out, ..) => Ok(out),
|
||||
ERR::LoopBreak(..) => {
|
||||
|
@ -118,6 +118,11 @@ pub fn get_builtin_binary_op_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Option<F
|
||||
let y = args[1].$yy().unwrap() as $base;
|
||||
Ok(x.$func(y as $yyy).into())
|
||||
}, false) };
|
||||
($base:ty => Ok($func:ident ( $xx:ident, $yy:ident ))) => { (|_, args| {
|
||||
let x = args[0].$xx().unwrap() as $base;
|
||||
let y = args[1].$yy().unwrap() as $base;
|
||||
Ok($func(x, y).into())
|
||||
}, false) };
|
||||
($base:ty => $func:ident ( $xx:ident, $yy:ident )) => { (|_, args| {
|
||||
let x = args[0].$xx().unwrap() as $base;
|
||||
let y = args[1].$yy().unwrap() as $base;
|
||||
@ -133,6 +138,11 @@ pub fn get_builtin_binary_op_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Option<F
|
||||
let y = <$base>::from(args[1].$yy().unwrap());
|
||||
Ok(x.$func(y).into())
|
||||
}, false) };
|
||||
(from $base:ty => Ok($func:ident ( $xx:ident, $yy:ident ))) => { (|_, args| {
|
||||
let x = <$base>::from(args[0].$xx().unwrap());
|
||||
let y = <$base>::from(args[1].$yy().unwrap());
|
||||
Ok($func(x, y).into())
|
||||
}, false) };
|
||||
(from $base:ty => $func:ident ( $xx:ident, $yy:ident )) => { (|_, args| {
|
||||
let x = <$base>::from(args[0].$xx().unwrap());
|
||||
let y = <$base>::from(args[1].$yy().unwrap());
|
||||
@ -155,8 +165,8 @@ pub fn get_builtin_binary_op_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Option<F
|
||||
Divide => return Some(impl_op!(INT => divide(as_int, as_int))),
|
||||
Modulo => return Some(impl_op!(INT => modulo(as_int, as_int))),
|
||||
PowerOf => return Some(impl_op!(INT => power(as_int, as_int))),
|
||||
RightShift => return Some(impl_op!(INT => shift_right(as_int, as_int))),
|
||||
LeftShift => return Some(impl_op!(INT => shift_left(as_int, as_int))),
|
||||
RightShift => return Some(impl_op!(INT => Ok(shift_right(as_int, as_int)))),
|
||||
LeftShift => return Some(impl_op!(INT => Ok(shift_left(as_int, as_int)))),
|
||||
_ => (),
|
||||
}
|
||||
|
||||
@ -168,8 +178,26 @@ pub fn get_builtin_binary_op_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Option<F
|
||||
Divide => return Some(impl_op!(INT => as_int / as_int)),
|
||||
Modulo => return Some(impl_op!(INT => as_int % as_int)),
|
||||
PowerOf => return Some(impl_op!(INT => as_int.pow(as_int as u32))),
|
||||
RightShift => return Some(impl_op!(INT => as_int >> as_int)),
|
||||
LeftShift => return Some(impl_op!(INT => as_int << as_int)),
|
||||
RightShift => {
|
||||
return Some((
|
||||
|_, args| {
|
||||
let x = args[0].as_int().unwrap();
|
||||
let y = args[1].as_int().unwrap();
|
||||
Ok((if y < 0 { x << -y } else { x >> y }).into())
|
||||
},
|
||||
false,
|
||||
))
|
||||
}
|
||||
LeftShift => {
|
||||
return Some((
|
||||
|_, args| {
|
||||
let x = args[0].as_int().unwrap();
|
||||
let y = args[1].as_int().unwrap();
|
||||
Ok((if y < 0 { x >> -y } else { x << y }).into())
|
||||
},
|
||||
false,
|
||||
))
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
@ -614,6 +642,12 @@ pub fn get_builtin_op_assignment_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Opti
|
||||
let y = args[1].$yy().unwrap() as $x;
|
||||
Ok((*args[0].write_lock::<$x>().unwrap() = x.$func(y as $yyy)).into())
|
||||
}, false) };
|
||||
($x:ty => Ok($func:ident ( $xx:ident, $yy:ident ))) => { (|_, args| {
|
||||
let x = args[0].$xx().unwrap();
|
||||
let y = args[1].$yy().unwrap() as $x;
|
||||
let v: Dynamic = $func(x, y).into();
|
||||
Ok((*args[0].write_lock().unwrap() = v).into())
|
||||
}, false) };
|
||||
($x:ty => $func:ident ( $xx:ident, $yy:ident )) => { (|_, args| {
|
||||
let x = args[0].$xx().unwrap();
|
||||
let y = args[1].$yy().unwrap() as $x;
|
||||
@ -628,6 +662,11 @@ pub fn get_builtin_op_assignment_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Opti
|
||||
let y = <$x>::from(args[1].$yy().unwrap());
|
||||
Ok((*args[0].write_lock::<$x>().unwrap() = x.$func(y)).into())
|
||||
}, false) };
|
||||
(from $x:ty => Ok($func:ident ( $xx:ident, $yy:ident ))) => { (|_, args| {
|
||||
let x = args[0].$xx().unwrap();
|
||||
let y = <$x>::from(args[1].$yy().unwrap());
|
||||
Ok((*args[0].write_lock().unwrap() = $func(x, y).into()).into())
|
||||
}, false) };
|
||||
(from $x:ty => $func:ident ( $xx:ident, $yy:ident )) => { (|_, args| {
|
||||
let x = args[0].$xx().unwrap();
|
||||
let y = <$x>::from(args[1].$yy().unwrap());
|
||||
@ -650,8 +689,8 @@ pub fn get_builtin_op_assignment_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Opti
|
||||
DivideAssign => return Some(impl_op!(INT => divide(as_int, as_int))),
|
||||
ModuloAssign => return Some(impl_op!(INT => modulo(as_int, as_int))),
|
||||
PowerOfAssign => return Some(impl_op!(INT => power(as_int, as_int))),
|
||||
RightShiftAssign => return Some(impl_op!(INT => shift_right(as_int, as_int))),
|
||||
LeftShiftAssign => return Some(impl_op!(INT => shift_left(as_int, as_int))),
|
||||
RightShiftAssign => return Some(impl_op!(INT => Ok(shift_right(as_int, as_int)))),
|
||||
LeftShiftAssign => return Some(impl_op!(INT => Ok(shift_left(as_int, as_int)))),
|
||||
_ => (),
|
||||
}
|
||||
|
||||
@ -663,8 +702,28 @@ pub fn get_builtin_op_assignment_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Opti
|
||||
DivideAssign => return Some(impl_op!(INT /= as_int)),
|
||||
ModuloAssign => return Some(impl_op!(INT %= as_int)),
|
||||
PowerOfAssign => return Some(impl_op!(INT => as_int.pow(as_int as u32))),
|
||||
RightShiftAssign => return Some(impl_op!(INT >>= as_int)),
|
||||
LeftShiftAssign => return Some(impl_op!(INT <<= as_int)),
|
||||
RightShiftAssign => {
|
||||
return Some((
|
||||
|_, args| {
|
||||
let x = args[0].as_int().unwrap();
|
||||
let y = args[1].as_int().unwrap();
|
||||
let v = if y < 0 { x << -y } else { x >> y };
|
||||
Ok((*args[0].write_lock::<Dynamic>().unwrap() = v.into()).into())
|
||||
},
|
||||
false,
|
||||
))
|
||||
}
|
||||
LeftShiftAssign => {
|
||||
return Some((
|
||||
|_, args| {
|
||||
let x = args[0].as_int().unwrap();
|
||||
let y = args[1].as_int().unwrap();
|
||||
let v = if y < 0 { x >> -y } else { x << y };
|
||||
Ok((*args[0].write_lock::<Dynamic>().unwrap() = v.into()).into())
|
||||
},
|
||||
false,
|
||||
))
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
|
152
src/func/call.rs
152
src/func/call.rs
@ -270,21 +270,27 @@ impl Engine {
|
||||
}
|
||||
|
||||
// Try to find a built-in version
|
||||
let builtin =
|
||||
args.and_then(|args| match op_token {
|
||||
let builtin = args.and_then(|args| match op_token {
|
||||
Token::NONE => None,
|
||||
token if token.is_op_assignment() => {
|
||||
let (first_arg, rest_args) = args.split_first().unwrap();
|
||||
|
||||
get_builtin_op_assignment_fn(token, first_arg, rest_args[0])
|
||||
.map(|(f, ctx)| FnResolutionCacheEntry {
|
||||
func: CallableFunction::Method(Shared::new(f), ctx),
|
||||
get_builtin_op_assignment_fn(token, first_arg, rest_args[0]).map(
|
||||
|(f, has_context)| FnResolutionCacheEntry {
|
||||
func: CallableFunction::Method {
|
||||
func: Shared::new(f),
|
||||
has_context,
|
||||
},
|
||||
source: None,
|
||||
})
|
||||
},
|
||||
)
|
||||
}
|
||||
token => get_builtin_binary_op_fn(token, args[0], args[1]).map(
|
||||
|(f, ctx)| FnResolutionCacheEntry {
|
||||
func: CallableFunction::Method(Shared::new(f), ctx),
|
||||
|(f, has_context)| FnResolutionCacheEntry {
|
||||
func: CallableFunction::Method {
|
||||
func: Shared::new(f),
|
||||
has_context,
|
||||
},
|
||||
source: None,
|
||||
},
|
||||
),
|
||||
@ -380,7 +386,6 @@ impl Engine {
|
||||
// Clone the first argument
|
||||
backup.change_first_arg_to_copy(args);
|
||||
}
|
||||
auto_restore!(args if swap => move |a| backup.restore_first_arg(a));
|
||||
|
||||
#[cfg(feature = "debugging")]
|
||||
if self.is_debugger_registered() {
|
||||
@ -418,6 +423,10 @@ impl Engine {
|
||||
.and_then(|r| self.check_data_size(r, pos))
|
||||
.map_err(|err| err.fill_position(pos));
|
||||
|
||||
if swap {
|
||||
backup.restore_first_arg(args);
|
||||
}
|
||||
|
||||
#[cfg(feature = "debugging")]
|
||||
if self.is_debugger_registered() {
|
||||
use crate::eval::{DebuggerEvent, DebuggerStatus};
|
||||
@ -429,7 +438,6 @@ impl Engine {
|
||||
};
|
||||
if trigger {
|
||||
let scope = &mut Scope::new();
|
||||
let mut this_ptr = Dynamic::NULL;
|
||||
let node = crate::ast::Stmt::Noop(pos);
|
||||
let node = (&node).into();
|
||||
let event = match _result {
|
||||
@ -437,10 +445,9 @@ impl Engine {
|
||||
Err(ref err) => DebuggerEvent::FunctionExitWithError(err),
|
||||
};
|
||||
|
||||
if let Err(err) =
|
||||
self.run_debugger_raw(global, caches, scope, &mut this_ptr, node, event)
|
||||
{
|
||||
_result = Err(err);
|
||||
match self.run_debugger_raw(global, caches, scope, None, node, event) {
|
||||
Ok(..) => (),
|
||||
Err(err) => _result = Err(err),
|
||||
}
|
||||
}
|
||||
|
||||
@ -644,9 +651,10 @@ impl Engine {
|
||||
// Script function call
|
||||
assert!(func.is_script());
|
||||
|
||||
let func = func.get_script_fn_def().expect("script-defined function");
|
||||
let f = func.get_script_fn_def().expect("script-defined function");
|
||||
let environ = func.get_encapsulated_environ();
|
||||
|
||||
if func.body.is_empty() {
|
||||
if f.body.is_empty() {
|
||||
return Ok((Dynamic::UNIT, false));
|
||||
}
|
||||
|
||||
@ -666,7 +674,15 @@ impl Engine {
|
||||
let (first_arg, rest_args) = _args.split_first_mut().unwrap();
|
||||
|
||||
self.call_script_fn(
|
||||
global, caches, scope, first_arg, func, rest_args, true, pos,
|
||||
global,
|
||||
caches,
|
||||
scope,
|
||||
Some(first_arg),
|
||||
environ,
|
||||
f,
|
||||
rest_args,
|
||||
true,
|
||||
pos,
|
||||
)
|
||||
} else {
|
||||
// Normal call of script function
|
||||
@ -681,9 +697,7 @@ impl Engine {
|
||||
|
||||
auto_restore!(args = (_args) if swap => move |a| backup.restore_first_arg(a));
|
||||
|
||||
let mut this_ptr = Dynamic::NULL;
|
||||
|
||||
self.call_script_fn(global, caches, scope, &mut this_ptr, func, args, true, pos)
|
||||
self.call_script_fn(global, caches, scope, None, environ, f, args, true, pos)
|
||||
}
|
||||
.map(|r| (r, false));
|
||||
}
|
||||
@ -704,7 +718,7 @@ impl Engine {
|
||||
global: &mut GlobalRuntimeState,
|
||||
caches: &mut Caches,
|
||||
scope: &mut Scope,
|
||||
this_ptr: &mut Dynamic,
|
||||
this_ptr: Option<&mut Dynamic>,
|
||||
arg_expr: &Expr,
|
||||
) -> RhaiResultOf<(Dynamic, Position)> {
|
||||
// Literal values
|
||||
@ -762,14 +776,13 @@ impl Engine {
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
if let Some(fn_def) = fn_ptr.fn_def() {
|
||||
if fn_def.params.len() == args.len() {
|
||||
let mut this_ptr = Dynamic::NULL;
|
||||
|
||||
return self
|
||||
.call_script_fn(
|
||||
global,
|
||||
caches,
|
||||
&mut Scope::new(),
|
||||
&mut this_ptr,
|
||||
None,
|
||||
fn_ptr.encapsulated_environ(),
|
||||
fn_def,
|
||||
args,
|
||||
true,
|
||||
@ -822,15 +835,15 @@ impl Engine {
|
||||
let fn_ptr = mem::take(&mut call_args[0]).cast::<FnPtr>();
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
let (fn_name, is_anon, fn_curry, fn_def) = {
|
||||
let (fn_name, is_anon, fn_curry, _environ, fn_def) = {
|
||||
let is_anon = fn_ptr.is_anonymous();
|
||||
let (fn_name, fn_curry, fn_def) = fn_ptr.take_data();
|
||||
(fn_name, is_anon, fn_curry, fn_def)
|
||||
let (fn_name, fn_curry, environ, fn_def) = fn_ptr.take_data();
|
||||
(fn_name, is_anon, fn_curry, environ, fn_def)
|
||||
};
|
||||
#[cfg(feature = "no_function")]
|
||||
let (fn_name, is_anon, fn_curry) = {
|
||||
let (fn_name, fn_curry) = fn_ptr.take_data();
|
||||
(fn_name, false, fn_curry)
|
||||
let (fn_name, is_anon, fn_curry, _environ) = {
|
||||
let (fn_name, fn_curry, environ) = fn_ptr.take_data();
|
||||
(fn_name, false, fn_curry, environ)
|
||||
};
|
||||
|
||||
// Replace the first argument with the object pointer, adding the curried arguments
|
||||
@ -855,7 +868,8 @@ impl Engine {
|
||||
global,
|
||||
caches,
|
||||
&mut Scope::new(),
|
||||
target,
|
||||
Some(target),
|
||||
_environ.as_deref(),
|
||||
&fn_def,
|
||||
args,
|
||||
true,
|
||||
@ -996,7 +1010,7 @@ impl Engine {
|
||||
global: &mut GlobalRuntimeState,
|
||||
caches: &mut Caches,
|
||||
scope: &mut Scope,
|
||||
this_ptr: &mut Dynamic,
|
||||
mut this_ptr: Option<&mut Dynamic>,
|
||||
fn_name: &str,
|
||||
op_token: Token,
|
||||
first_arg: Option<&Expr>,
|
||||
@ -1020,7 +1034,7 @@ impl Engine {
|
||||
KEYWORD_FN_PTR_CALL if total_args >= 1 => {
|
||||
let arg = first_arg.unwrap();
|
||||
let (arg_value, arg_pos) =
|
||||
self.get_arg_value(global, caches, scope, this_ptr, arg)?;
|
||||
self.get_arg_value(global, caches, scope, this_ptr.as_deref_mut(), arg)?;
|
||||
|
||||
if !arg_value.is_fnptr() {
|
||||
let typ = self.map_type_name(arg_value.type_name());
|
||||
@ -1030,15 +1044,15 @@ impl Engine {
|
||||
let fn_ptr = arg_value.cast::<FnPtr>();
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
let (fn_name, is_anon, fn_curry, fn_def) = {
|
||||
let (fn_name, is_anon, fn_curry, _environ, fn_def) = {
|
||||
let is_anon = fn_ptr.is_anonymous();
|
||||
let (fn_name, fn_curry, fn_def) = fn_ptr.take_data();
|
||||
(fn_name, is_anon, fn_curry, fn_def)
|
||||
let (fn_name, fn_curry, environ, fn_def) = fn_ptr.take_data();
|
||||
(fn_name, is_anon, fn_curry, environ, fn_def)
|
||||
};
|
||||
#[cfg(feature = "no_function")]
|
||||
let (fn_name, is_anon, fn_curry) = {
|
||||
let (fn_name, fn_curry) = fn_ptr.take_data();
|
||||
(fn_name, false, fn_curry)
|
||||
let (fn_name, is_anon, fn_curry, _environ) = {
|
||||
let (fn_name, fn_curry, environ) = fn_ptr.take_data();
|
||||
(fn_name, false, fn_curry, environ)
|
||||
};
|
||||
|
||||
curry.extend(fn_curry.into_iter());
|
||||
@ -1052,22 +1066,17 @@ impl Engine {
|
||||
.into_iter()
|
||||
.map(Ok)
|
||||
.chain(a_expr.iter().map(|expr| -> Result<_, RhaiError> {
|
||||
let this_ptr = this_ptr.as_deref_mut();
|
||||
self.get_arg_value(global, caches, scope, this_ptr, expr)
|
||||
.map(|(v, ..)| v)
|
||||
}))
|
||||
.collect::<RhaiResultOf<FnArgsVec<_>>>()?;
|
||||
let args = &mut arg_values.iter_mut().collect::<FnArgsVec<_>>();
|
||||
let mut this_ptr = Dynamic::NULL;
|
||||
let scope = &mut Scope::new();
|
||||
let environ = _environ.as_deref();
|
||||
|
||||
return self.call_script_fn(
|
||||
global,
|
||||
caches,
|
||||
&mut Scope::new(),
|
||||
&mut this_ptr,
|
||||
&fn_def,
|
||||
args,
|
||||
true,
|
||||
pos,
|
||||
global, caches, scope, None, environ, &fn_def, args, true, pos,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1111,7 +1120,7 @@ impl Engine {
|
||||
KEYWORD_FN_PTR_CURRY if total_args > 1 => {
|
||||
let first = first_arg.unwrap();
|
||||
let (arg_value, arg_pos) =
|
||||
self.get_arg_value(global, caches, scope, this_ptr, first)?;
|
||||
self.get_arg_value(global, caches, scope, this_ptr.as_deref_mut(), first)?;
|
||||
|
||||
if !arg_value.is_fnptr() {
|
||||
let typ = self.map_type_name(arg_value.type_name());
|
||||
@ -1122,7 +1131,8 @@ impl Engine {
|
||||
|
||||
// Append the new curried arguments to the existing list.
|
||||
a_expr.iter().try_for_each(|expr| -> Result<_, RhaiError> {
|
||||
let (value, ..) = self.get_arg_value(global, caches, scope, this_ptr, expr)?;
|
||||
let (value, ..) =
|
||||
self.get_arg_value(global, caches, scope, this_ptr.as_deref_mut(), expr)?;
|
||||
fn_ptr.add_curry(value);
|
||||
Ok(())
|
||||
})?;
|
||||
@ -1134,7 +1144,8 @@ impl Engine {
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
crate::engine::KEYWORD_IS_SHARED if total_args == 1 => {
|
||||
let arg = first_arg.unwrap();
|
||||
let (arg_value, ..) = self.get_arg_value(global, caches, scope, this_ptr, arg)?;
|
||||
let (arg_value, ..) =
|
||||
self.get_arg_value(global, caches, scope, this_ptr.as_deref_mut(), arg)?;
|
||||
return Ok(arg_value.is_shared().into());
|
||||
}
|
||||
|
||||
@ -1143,7 +1154,7 @@ impl Engine {
|
||||
crate::engine::KEYWORD_IS_DEF_FN if total_args == 2 => {
|
||||
let first = first_arg.unwrap();
|
||||
let (arg_value, arg_pos) =
|
||||
self.get_arg_value(global, caches, scope, this_ptr, first)?;
|
||||
self.get_arg_value(global, caches, scope, this_ptr.as_deref_mut(), first)?;
|
||||
|
||||
let fn_name = arg_value
|
||||
.into_immutable_string()
|
||||
@ -1235,7 +1246,7 @@ impl Engine {
|
||||
.copied()
|
||||
.chain(a_expr.iter())
|
||||
.try_for_each(|expr| {
|
||||
self.get_arg_value(global, caches, scope, this_ptr, expr)
|
||||
self.get_arg_value(global, caches, scope, this_ptr.as_deref_mut(), expr)
|
||||
.map(|(value, ..)| arg_values.push(value.flatten()))
|
||||
})?;
|
||||
args.extend(curry.iter_mut());
|
||||
@ -1263,11 +1274,11 @@ impl Engine {
|
||||
let first_expr = first_arg.unwrap();
|
||||
|
||||
#[cfg(feature = "debugging")]
|
||||
self.run_debugger(global, caches, scope, this_ptr, first_expr)?;
|
||||
self.run_debugger(global, caches, scope, this_ptr.as_deref_mut(), first_expr)?;
|
||||
|
||||
// func(x, ...) -> x.func(...)
|
||||
a_expr.iter().try_for_each(|expr| {
|
||||
self.get_arg_value(global, caches, scope, this_ptr, expr)
|
||||
self.get_arg_value(global, caches, scope, this_ptr.as_deref_mut(), expr)
|
||||
.map(|(value, ..)| arg_values.push(value.flatten()))
|
||||
})?;
|
||||
|
||||
@ -1294,7 +1305,7 @@ impl Engine {
|
||||
.into_iter()
|
||||
.chain(a_expr.iter())
|
||||
.try_for_each(|expr| {
|
||||
self.get_arg_value(global, caches, scope, this_ptr, expr)
|
||||
self.get_arg_value(global, caches, scope, this_ptr.as_deref_mut(), expr)
|
||||
.map(|(value, ..)| arg_values.push(value.flatten()))
|
||||
})?;
|
||||
args.extend(curry.iter_mut());
|
||||
@ -1316,7 +1327,7 @@ impl Engine {
|
||||
global: &mut GlobalRuntimeState,
|
||||
caches: &mut Caches,
|
||||
scope: &mut Scope,
|
||||
this_ptr: &mut Dynamic,
|
||||
mut this_ptr: Option<&mut Dynamic>,
|
||||
namespace: &crate::ast::Namespace,
|
||||
fn_name: &str,
|
||||
args_expr: &[Expr],
|
||||
@ -1335,13 +1346,19 @@ impl Engine {
|
||||
// and avoid cloning the value
|
||||
if !args_expr.is_empty() && args_expr[0].is_variable_access(true) {
|
||||
#[cfg(feature = "debugging")]
|
||||
self.run_debugger(global, caches, scope, this_ptr, &args_expr[0])?;
|
||||
self.run_debugger(
|
||||
global,
|
||||
caches,
|
||||
scope,
|
||||
this_ptr.as_deref_mut(),
|
||||
&args_expr[0],
|
||||
)?;
|
||||
|
||||
// func(x, ...) -> x.func(...)
|
||||
arg_values.push(Dynamic::UNIT);
|
||||
|
||||
args_expr.iter().skip(1).try_for_each(|expr| {
|
||||
self.get_arg_value(global, caches, scope, this_ptr, expr)
|
||||
self.get_arg_value(global, caches, scope, this_ptr.as_deref_mut(), expr)
|
||||
.map(|(value, ..)| arg_values.push(value.flatten()))
|
||||
})?;
|
||||
|
||||
@ -1370,7 +1387,7 @@ impl Engine {
|
||||
} else {
|
||||
// func(..., ...) or func(mod::x, ...)
|
||||
args_expr.iter().try_for_each(|expr| {
|
||||
self.get_arg_value(global, caches, scope, this_ptr, expr)
|
||||
self.get_arg_value(global, caches, scope, this_ptr.as_deref_mut(), expr)
|
||||
.map(|(value, ..)| arg_values.push(value.flatten()))
|
||||
})?;
|
||||
args.extend(arg_values.iter_mut());
|
||||
@ -1441,15 +1458,16 @@ impl Engine {
|
||||
|
||||
match func {
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
Some(f) if f.is_script() => {
|
||||
let f = f.get_script_fn_def().expect("script-defined function");
|
||||
Some(func) if func.is_script() => {
|
||||
let f = func.get_script_fn_def().expect("script-defined function");
|
||||
|
||||
let environ = func.get_encapsulated_environ();
|
||||
let scope = &mut Scope::new();
|
||||
let mut this_ptr = Dynamic::NULL;
|
||||
|
||||
let orig_source = mem::replace(&mut global.source, module.id_raw().cloned());
|
||||
auto_restore!(global => move |g| g.source = orig_source);
|
||||
|
||||
self.call_script_fn(global, caches, scope, &mut this_ptr, f, args, true, pos)
|
||||
self.call_script_fn(global, caches, scope, None, environ, f, args, true, pos)
|
||||
}
|
||||
|
||||
Some(f) if f.is_plugin_fn() => {
|
||||
@ -1546,7 +1564,7 @@ impl Engine {
|
||||
global: &mut GlobalRuntimeState,
|
||||
caches: &mut Caches,
|
||||
scope: &mut Scope,
|
||||
this_ptr: &mut Dynamic,
|
||||
mut this_ptr: Option<&mut Dynamic>,
|
||||
expr: &FnCallExpr,
|
||||
pos: Position,
|
||||
) -> RhaiResult {
|
||||
@ -1566,7 +1584,7 @@ impl Engine {
|
||||
// Short-circuit native unary operator call if under Fast Operators mode
|
||||
if op_token == Token::Bang && self.fast_operators() && args.len() == 1 {
|
||||
let mut value = self
|
||||
.get_arg_value(global, caches, scope, this_ptr, &args[0])?
|
||||
.get_arg_value(global, caches, scope, this_ptr.as_deref_mut(), &args[0])?
|
||||
.0
|
||||
.flatten();
|
||||
|
||||
@ -1582,7 +1600,7 @@ impl Engine {
|
||||
// Short-circuit native binary operator call if under Fast Operators mode
|
||||
if op_token != NO_TOKEN && self.fast_operators() && args.len() == 2 {
|
||||
let mut lhs = self
|
||||
.get_arg_value(global, caches, scope, this_ptr, &args[0])?
|
||||
.get_arg_value(global, caches, scope, this_ptr.as_deref_mut(), &args[0])?
|
||||
.0
|
||||
.flatten();
|
||||
|
||||
|
@ -8,23 +8,64 @@ use std::fmt;
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
|
||||
/// _(internals)_ Encapsulated AST environment.
|
||||
/// Exported under the `internals` feature only.
|
||||
///
|
||||
/// 1) functions defined within the same AST
|
||||
/// 2) the stack of imported [modules][crate::Module]
|
||||
/// 3) global constants
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct EncapsulatedEnviron {
|
||||
/// Functions defined within the same [`AST`][crate::AST].
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
pub lib: crate::SharedModule,
|
||||
/// Imported [modules][crate::Module].
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
pub imports: Box<crate::StaticVec<(crate::ImmutableString, crate::SharedModule)>>,
|
||||
/// Globally-defined constants.
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
pub constants: Option<crate::eval::SharedGlobalConstants>,
|
||||
}
|
||||
|
||||
/// _(internals)_ A type encapsulating a function callable by Rhai.
|
||||
/// Exported under the `internals` feature only.
|
||||
#[derive(Clone)]
|
||||
#[non_exhaustive]
|
||||
pub enum CallableFunction {
|
||||
/// A pure native Rust function with all arguments passed by value.
|
||||
Pure(Shared<FnAny>, bool),
|
||||
Pure {
|
||||
/// Shared function pointer.
|
||||
func: Shared<FnAny>,
|
||||
/// Does the function take a [`NativeCallContext`][crate::NativeCallContext] parameter?
|
||||
has_context: bool,
|
||||
},
|
||||
/// A native Rust object method with the first argument passed by reference,
|
||||
/// and the rest passed by value.
|
||||
Method(Shared<FnAny>, bool),
|
||||
Method {
|
||||
/// Shared function pointer.
|
||||
func: Shared<FnAny>,
|
||||
/// Does the function take a [`NativeCallContext`][crate::NativeCallContext] parameter?
|
||||
has_context: bool,
|
||||
},
|
||||
/// An iterator function.
|
||||
Iterator(Shared<IteratorFn>),
|
||||
Iterator {
|
||||
/// Shared function pointer.
|
||||
func: Shared<IteratorFn>,
|
||||
},
|
||||
/// A plugin function,
|
||||
Plugin(Shared<FnPlugin>),
|
||||
Plugin {
|
||||
/// Shared function pointer.
|
||||
func: Shared<FnPlugin>,
|
||||
},
|
||||
/// A script-defined function.
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
Script(Shared<crate::ast::ScriptFnDef>),
|
||||
Script {
|
||||
/// Shared reference to the [`ScriptFnDef`][crate::ast::ScriptFnDef] function definition.
|
||||
fn_def: Shared<crate::ast::ScriptFnDef>,
|
||||
/// Encapsulated environment, if any.
|
||||
environ: Option<Shared<EncapsulatedEnviron>>,
|
||||
},
|
||||
}
|
||||
|
||||
impl fmt::Debug for CallableFunction {
|
||||
@ -32,13 +73,13 @@ impl fmt::Debug for CallableFunction {
|
||||
#[inline(never)]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::Pure(..) => f.write_str("NativePureFunction"),
|
||||
Self::Method(..) => f.write_str("NativeMethod"),
|
||||
Self::Iterator(..) => f.write_str("NativeIterator"),
|
||||
Self::Plugin(..) => f.write_str("PluginFunction"),
|
||||
Self::Pure { .. } => f.write_str("NativePureFunction"),
|
||||
Self::Method { .. } => f.write_str("NativeMethod"),
|
||||
Self::Iterator { .. } => f.write_str("NativeIterator"),
|
||||
Self::Plugin { .. } => f.write_str("PluginFunction"),
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
Self::Script(fn_def) => fmt::Debug::fmt(fn_def, f),
|
||||
Self::Script { fn_def, .. } => fmt::Debug::fmt(fn_def, f),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -46,13 +87,13 @@ impl fmt::Debug for CallableFunction {
|
||||
impl fmt::Display for CallableFunction {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::Pure(..) => f.write_str("NativePureFunction"),
|
||||
Self::Method(..) => f.write_str("NativeMethod"),
|
||||
Self::Iterator(..) => f.write_str("NativeIterator"),
|
||||
Self::Plugin(..) => f.write_str("PluginFunction"),
|
||||
Self::Pure { .. } => f.write_str("NativePureFunction"),
|
||||
Self::Method { .. } => f.write_str("NativeMethod"),
|
||||
Self::Iterator { .. } => f.write_str("NativeIterator"),
|
||||
Self::Plugin { .. } => f.write_str("PluginFunction"),
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
Self::Script(s) => fmt::Display::fmt(s, f),
|
||||
Self::Script { fn_def, .. } => fmt::Display::fmt(fn_def, f),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -63,13 +104,13 @@ impl CallableFunction {
|
||||
#[must_use]
|
||||
pub fn is_pure(&self) -> bool {
|
||||
match self {
|
||||
Self::Pure(..) => true,
|
||||
Self::Method(..) | Self::Iterator(..) => false,
|
||||
Self::Pure { .. } => true,
|
||||
Self::Method { .. } | Self::Iterator { .. } => false,
|
||||
|
||||
Self::Plugin(p) => !p.is_method_call(),
|
||||
Self::Plugin { func, .. } => !func.is_method_call(),
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
Self::Script(..) => false,
|
||||
Self::Script { .. } => false,
|
||||
}
|
||||
}
|
||||
/// Is this a native Rust method function?
|
||||
@ -77,13 +118,13 @@ impl CallableFunction {
|
||||
#[must_use]
|
||||
pub fn is_method(&self) -> bool {
|
||||
match self {
|
||||
Self::Method(..) => true,
|
||||
Self::Pure(..) | Self::Iterator(..) => false,
|
||||
Self::Method { .. } => true,
|
||||
Self::Pure { .. } | Self::Iterator { .. } => false,
|
||||
|
||||
Self::Plugin(p) => p.is_method_call(),
|
||||
Self::Plugin { func, .. } => func.is_method_call(),
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
Self::Script(..) => false,
|
||||
Self::Script { .. } => false,
|
||||
}
|
||||
}
|
||||
/// Is this an iterator function?
|
||||
@ -91,11 +132,11 @@ impl CallableFunction {
|
||||
#[must_use]
|
||||
pub const fn is_iter(&self) -> bool {
|
||||
match self {
|
||||
Self::Iterator(..) => true,
|
||||
Self::Pure(..) | Self::Method(..) | Self::Plugin(..) => false,
|
||||
Self::Iterator { .. } => true,
|
||||
Self::Pure { .. } | Self::Method { .. } | Self::Plugin { .. } => false,
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
Self::Script(..) => false,
|
||||
Self::Script { .. } => false,
|
||||
}
|
||||
}
|
||||
/// Is this a script-defined function?
|
||||
@ -107,8 +148,11 @@ impl CallableFunction {
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
match self {
|
||||
Self::Script(..) => true,
|
||||
Self::Pure(..) | Self::Method(..) | Self::Iterator(..) | Self::Plugin(..) => false,
|
||||
Self::Script { .. } => true,
|
||||
Self::Pure { .. }
|
||||
| Self::Method { .. }
|
||||
| Self::Iterator { .. }
|
||||
| Self::Plugin { .. } => false,
|
||||
}
|
||||
}
|
||||
/// Is this a plugin function?
|
||||
@ -116,11 +160,11 @@ impl CallableFunction {
|
||||
#[must_use]
|
||||
pub const fn is_plugin_fn(&self) -> bool {
|
||||
match self {
|
||||
Self::Plugin(..) => true,
|
||||
Self::Pure(..) | Self::Method(..) | Self::Iterator(..) => false,
|
||||
Self::Plugin { .. } => true,
|
||||
Self::Pure { .. } | Self::Method { .. } | Self::Iterator { .. } => false,
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
Self::Script(..) => false,
|
||||
Self::Script { .. } => false,
|
||||
}
|
||||
}
|
||||
/// Is this a native Rust function?
|
||||
@ -132,8 +176,11 @@ impl CallableFunction {
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
match self {
|
||||
Self::Pure(..) | Self::Method(..) | Self::Plugin(..) | Self::Iterator(..) => true,
|
||||
Self::Script(..) => false,
|
||||
Self::Pure { .. }
|
||||
| Self::Method { .. }
|
||||
| Self::Plugin { .. }
|
||||
| Self::Iterator { .. } => true,
|
||||
Self::Script { .. } => false,
|
||||
}
|
||||
}
|
||||
/// Is there a [`NativeCallContext`][crate::NativeCallContext] parameter?
|
||||
@ -141,11 +188,11 @@ impl CallableFunction {
|
||||
#[must_use]
|
||||
pub fn has_context(&self) -> bool {
|
||||
match self {
|
||||
Self::Pure(.., ctx) | Self::Method(.., ctx) => *ctx,
|
||||
Self::Plugin(f) => f.has_context(),
|
||||
Self::Iterator(..) => false,
|
||||
Self::Pure { has_context, .. } | Self::Method { has_context, .. } => *has_context,
|
||||
Self::Plugin { func, .. } => func.has_context(),
|
||||
Self::Iterator { .. } => false,
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
Self::Script(..) => false,
|
||||
Self::Script { .. } => false,
|
||||
}
|
||||
}
|
||||
/// Get the access mode.
|
||||
@ -157,10 +204,11 @@ impl CallableFunction {
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
match self {
|
||||
Self::Plugin(..) | Self::Pure(..) | Self::Method(..) | Self::Iterator(..) => {
|
||||
FnAccess::Public
|
||||
}
|
||||
Self::Script(f) => f.access,
|
||||
Self::Plugin { .. }
|
||||
| Self::Pure { .. }
|
||||
| Self::Method { .. }
|
||||
| Self::Iterator { .. } => FnAccess::Public,
|
||||
Self::Script { fn_def, .. } => fn_def.access,
|
||||
}
|
||||
}
|
||||
/// Get a shared reference to a native Rust function.
|
||||
@ -168,11 +216,11 @@ impl CallableFunction {
|
||||
#[must_use]
|
||||
pub fn get_native_fn(&self) -> Option<&Shared<FnAny>> {
|
||||
match self {
|
||||
Self::Pure(f, ..) | Self::Method(f, ..) => Some(f),
|
||||
Self::Iterator(..) | Self::Plugin(..) => None,
|
||||
Self::Pure { func, .. } | Self::Method { func, .. } => Some(func),
|
||||
Self::Iterator { .. } | Self::Plugin { .. } => None,
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
Self::Script(..) => None,
|
||||
Self::Script { .. } => None,
|
||||
}
|
||||
}
|
||||
/// Get a shared reference to a script-defined function definition.
|
||||
@ -183,8 +231,27 @@ impl CallableFunction {
|
||||
#[must_use]
|
||||
pub const fn get_script_fn_def(&self) -> Option<&Shared<crate::ast::ScriptFnDef>> {
|
||||
match self {
|
||||
Self::Pure(..) | Self::Method(..) | Self::Iterator(..) | Self::Plugin(..) => None,
|
||||
Self::Script(f) => Some(f),
|
||||
Self::Pure { .. }
|
||||
| Self::Method { .. }
|
||||
| Self::Iterator { .. }
|
||||
| Self::Plugin { .. } => None,
|
||||
Self::Script { fn_def, .. } => Some(fn_def),
|
||||
}
|
||||
}
|
||||
/// Get a reference to the shared encapsulated environment of the function definition.
|
||||
///
|
||||
/// Not available under `no_function` or `no_module`.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn get_encapsulated_environ(&self) -> Option<&EncapsulatedEnviron> {
|
||||
match self {
|
||||
Self::Pure { .. }
|
||||
| Self::Method { .. }
|
||||
| Self::Iterator { .. }
|
||||
| Self::Plugin { .. } => None,
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
Self::Script { environ, .. } => environ.as_deref(),
|
||||
}
|
||||
}
|
||||
/// Get a reference to an iterator function.
|
||||
@ -192,11 +259,11 @@ impl CallableFunction {
|
||||
#[must_use]
|
||||
pub fn get_iter_fn(&self) -> Option<&IteratorFn> {
|
||||
match self {
|
||||
Self::Iterator(f) => Some(&**f),
|
||||
Self::Pure(..) | Self::Method(..) | Self::Plugin(..) => None,
|
||||
Self::Iterator { func, .. } => Some(&**func),
|
||||
Self::Pure { .. } | Self::Method { .. } | Self::Plugin { .. } => None,
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
Self::Script(..) => None,
|
||||
Self::Script { .. } => None,
|
||||
}
|
||||
}
|
||||
/// Get a shared reference to a plugin function.
|
||||
@ -204,11 +271,11 @@ impl CallableFunction {
|
||||
#[must_use]
|
||||
pub fn get_plugin_fn(&self) -> Option<&Shared<FnPlugin>> {
|
||||
match self {
|
||||
Self::Plugin(f) => Some(f),
|
||||
Self::Pure(..) | Self::Method(..) | Self::Iterator(..) => None,
|
||||
Self::Plugin { func, .. } => Some(func),
|
||||
Self::Pure { .. } | Self::Method { .. } | Self::Iterator { .. } => None,
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
Self::Script(..) => None,
|
||||
Self::Script { .. } => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -216,29 +283,37 @@ impl CallableFunction {
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
impl From<crate::ast::ScriptFnDef> for CallableFunction {
|
||||
#[inline(always)]
|
||||
fn from(func: crate::ast::ScriptFnDef) -> Self {
|
||||
Self::Script(func.into())
|
||||
fn from(fn_def: crate::ast::ScriptFnDef) -> Self {
|
||||
Self::Script {
|
||||
fn_def: fn_def.into(),
|
||||
environ: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
impl From<Shared<crate::ast::ScriptFnDef>> for CallableFunction {
|
||||
#[inline(always)]
|
||||
fn from(func: Shared<crate::ast::ScriptFnDef>) -> Self {
|
||||
Self::Script(func)
|
||||
fn from(fn_def: Shared<crate::ast::ScriptFnDef>) -> Self {
|
||||
Self::Script {
|
||||
fn_def,
|
||||
environ: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PluginFunction + 'static + SendSync> From<T> for CallableFunction {
|
||||
#[inline(always)]
|
||||
fn from(func: T) -> Self {
|
||||
Self::Plugin(Shared::new(func))
|
||||
Self::Plugin {
|
||||
func: Shared::new(func),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Shared<FnPlugin>> for CallableFunction {
|
||||
#[inline(always)]
|
||||
fn from(func: Shared<FnPlugin>) -> Self {
|
||||
Self::Plugin(func)
|
||||
Self::Plugin { func }
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ pub use call::ensure_no_data_race;
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
pub use call::is_anonymous_fn;
|
||||
pub use call::FnCallArgs;
|
||||
pub use callable_function::CallableFunction;
|
||||
pub use callable_function::{CallableFunction, EncapsulatedEnviron};
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
pub use func::Func;
|
||||
pub use hashing::{calc_fn_hash, calc_fn_hash_full, calc_var_hash, get_hasher, StraightHashMap};
|
||||
|
@ -162,7 +162,7 @@ macro_rules! def_register {
|
||||
#[inline(always)] fn param_types() -> [TypeId;$n] { [$(TypeId::of::<$par>()),*] }
|
||||
#[cfg(feature = "metadata")] #[inline(always)] fn param_names() -> [&'static str;$n] { [$(type_name::<$param>()),*] }
|
||||
#[inline(always)] fn into_callable_function(self, fn_name: Identifier, no_const: bool) -> CallableFunction {
|
||||
CallableFunction::$abi(Shared::new(move |_, args: &mut FnCallArgs| {
|
||||
CallableFunction::$abi { func: Shared::new(move |_, args: &mut FnCallArgs| {
|
||||
// The arguments are assumed to be of the correct number and types!
|
||||
check_constant!($abi, $n, fn_name, no_const, args);
|
||||
|
||||
@ -174,7 +174,7 @@ macro_rules! def_register {
|
||||
|
||||
// Map the result
|
||||
Ok(Dynamic::from(r))
|
||||
}), false)
|
||||
}), has_context: false }
|
||||
}
|
||||
}
|
||||
|
||||
@ -186,7 +186,7 @@ macro_rules! def_register {
|
||||
#[inline(always)] fn param_types() -> [TypeId;$n] { [$(TypeId::of::<$par>()),*] }
|
||||
#[cfg(feature = "metadata")] #[inline(always)] fn param_names() -> [&'static str;$n] { [$(type_name::<$param>()),*] }
|
||||
#[inline(always)] fn into_callable_function(self, fn_name: Identifier, no_const: bool) -> CallableFunction {
|
||||
CallableFunction::$abi(Shared::new(move |ctx: Option<NativeCallContext>, args: &mut FnCallArgs| {
|
||||
CallableFunction::$abi { func: Shared::new(move |ctx: Option<NativeCallContext>, args: &mut FnCallArgs| {
|
||||
let ctx = ctx.unwrap();
|
||||
|
||||
// The arguments are assumed to be of the correct number and types!
|
||||
@ -200,7 +200,7 @@ macro_rules! def_register {
|
||||
|
||||
// Map the result
|
||||
Ok(Dynamic::from(r))
|
||||
}), true)
|
||||
}), has_context: true }
|
||||
}
|
||||
}
|
||||
|
||||
@ -213,7 +213,7 @@ macro_rules! def_register {
|
||||
#[cfg(feature = "metadata")] #[inline(always)] fn param_names() -> [&'static str;$n] { [$(type_name::<$param>()),*] }
|
||||
#[cfg(feature = "metadata")] #[inline(always)] fn return_type_name() -> &'static str { type_name::<RhaiResultOf<RET>>() }
|
||||
#[inline(always)] fn into_callable_function(self, fn_name: Identifier, no_const: bool) -> CallableFunction {
|
||||
CallableFunction::$abi(Shared::new(move |_, args: &mut FnCallArgs| {
|
||||
CallableFunction::$abi { func: Shared::new(move |_, args: &mut FnCallArgs| {
|
||||
// The arguments are assumed to be of the correct number and types!
|
||||
check_constant!($abi, $n, fn_name, no_const, args);
|
||||
|
||||
@ -222,7 +222,7 @@ macro_rules! def_register {
|
||||
|
||||
// Call the function with each argument value
|
||||
self($($arg),*).map(Dynamic::from)
|
||||
}), false)
|
||||
}), has_context: false }
|
||||
}
|
||||
}
|
||||
|
||||
@ -235,7 +235,7 @@ macro_rules! def_register {
|
||||
#[cfg(feature = "metadata")] #[inline(always)] fn param_names() -> [&'static str;$n] { [$(type_name::<$param>()),*] }
|
||||
#[cfg(feature = "metadata")] #[inline(always)] fn return_type_name() -> &'static str { type_name::<RhaiResultOf<RET>>() }
|
||||
#[inline(always)] fn into_callable_function(self, fn_name: Identifier, no_const: bool) -> CallableFunction {
|
||||
CallableFunction::$abi(Shared::new(move |ctx: Option<NativeCallContext>, args: &mut FnCallArgs| {
|
||||
CallableFunction::$abi { func: Shared::new(move |ctx: Option<NativeCallContext>, args: &mut FnCallArgs| {
|
||||
let ctx = ctx.unwrap();
|
||||
|
||||
// The arguments are assumed to be of the correct number and types!
|
||||
@ -246,7 +246,7 @@ macro_rules! def_register {
|
||||
|
||||
// Call the function with each argument value
|
||||
self(ctx, $($arg),*).map(Dynamic::from)
|
||||
}), true)
|
||||
}), has_context: true }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,7 @@
|
||||
use super::call::FnCallArgs;
|
||||
use crate::ast::ScriptFnDef;
|
||||
use crate::eval::{Caches, GlobalRuntimeState};
|
||||
use crate::func::EncapsulatedEnviron;
|
||||
use crate::{Dynamic, Engine, Position, RhaiResult, Scope, ERR};
|
||||
use std::mem;
|
||||
#[cfg(feature = "no_std")]
|
||||
@ -27,7 +28,8 @@ impl Engine {
|
||||
global: &mut GlobalRuntimeState,
|
||||
caches: &mut Caches,
|
||||
scope: &mut Scope,
|
||||
this_ptr: &mut Dynamic,
|
||||
mut this_ptr: Option<&mut Dynamic>,
|
||||
_environ: Option<&EncapsulatedEnviron>,
|
||||
fn_def: &ScriptFnDef,
|
||||
args: &mut FnCallArgs,
|
||||
rewind_scope: bool,
|
||||
@ -84,12 +86,12 @@ impl Engine {
|
||||
let orig_fn_resolution_caches_len = caches.fn_resolution_caches_len();
|
||||
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
let orig_constants = if let Some(ref environ) = fn_def.environ {
|
||||
let crate::ast::EncapsulatedEnviron {
|
||||
ref lib,
|
||||
ref imports,
|
||||
ref constants,
|
||||
} = **environ;
|
||||
let orig_constants = if let Some(environ) = _environ {
|
||||
let EncapsulatedEnviron {
|
||||
lib,
|
||||
imports,
|
||||
constants,
|
||||
} = environ;
|
||||
|
||||
imports
|
||||
.iter()
|
||||
@ -106,12 +108,19 @@ impl Engine {
|
||||
#[cfg(feature = "debugging")]
|
||||
if self.is_debugger_registered() {
|
||||
let node = crate::ast::Stmt::Noop(fn_def.body.position());
|
||||
self.run_debugger(global, caches, scope, this_ptr, &node)?;
|
||||
self.run_debugger(global, caches, scope, this_ptr.as_deref_mut(), &node)?;
|
||||
}
|
||||
|
||||
// Evaluate the function
|
||||
let mut _result: RhaiResult = self
|
||||
.eval_stmt_block(global, caches, scope, this_ptr, &fn_def.body, rewind_scope)
|
||||
.eval_stmt_block(
|
||||
global,
|
||||
caches,
|
||||
scope,
|
||||
this_ptr.as_deref_mut(),
|
||||
&fn_def.body,
|
||||
rewind_scope,
|
||||
)
|
||||
.or_else(|err| match *err {
|
||||
// Convert return statement to return value
|
||||
ERR::Return(x, ..) => Ok(x),
|
||||
@ -124,8 +133,7 @@ impl Engine {
|
||||
_ => Err(ERR::ErrorInFunctionCall(
|
||||
fn_def.name.to_string(),
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
fn_def
|
||||
.environ
|
||||
_environ
|
||||
.as_deref()
|
||||
.and_then(|environ| environ.lib.id())
|
||||
.unwrap_or_else(|| global.source().unwrap_or(""))
|
||||
|
31
src/lib.rs
31
src/lib.rs
@ -88,6 +88,8 @@ use std::prelude::v1::*;
|
||||
#[macro_use]
|
||||
mod reify;
|
||||
#[macro_use]
|
||||
mod restore;
|
||||
#[macro_use]
|
||||
mod types;
|
||||
|
||||
mod api;
|
||||
@ -100,6 +102,8 @@ mod module;
|
||||
mod optimizer;
|
||||
pub mod packages;
|
||||
mod parser;
|
||||
#[cfg(feature = "serde")]
|
||||
pub mod serde;
|
||||
mod tests;
|
||||
mod tokenizer;
|
||||
|
||||
@ -201,6 +205,8 @@ type InclusiveRange = std::ops::RangeInclusive<INT>;
|
||||
|
||||
#[allow(deprecated)]
|
||||
pub use api::build_type::{CustomType, TypeBuilder};
|
||||
#[cfg(not(feature = "no_custom_syntax"))]
|
||||
pub use api::custom_syntax::Expression;
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
pub use api::files::{eval_file, run_file};
|
||||
@ -208,18 +214,18 @@ pub use api::{eval::eval, events::VarDefInfo, run::run};
|
||||
pub use ast::{FnAccess, AST};
|
||||
pub use engine::{Engine, OP_CONTAINS, OP_EQUALS};
|
||||
pub use eval::EvalContext;
|
||||
pub use func::{NativeCallContext, RegisterNativeFunction};
|
||||
use func::{calc_fn_hash, calc_fn_hash_full, calc_var_hash};
|
||||
pub use func::{plugin, FuncArgs, NativeCallContext, RegisterNativeFunction};
|
||||
pub use module::{FnNamespace, Module};
|
||||
use restore::RestoreOnDrop;
|
||||
pub use rhai_codegen::*;
|
||||
#[cfg(not(feature = "no_time"))]
|
||||
pub use types::Instant;
|
||||
pub use types::Position;
|
||||
pub use types::{
|
||||
Dynamic, EvalAltResult, FnPtr, ImmutableString, LexError, ParseError, ParseErrorType, Scope,
|
||||
Dynamic, EvalAltResult, FnPtr, ImmutableString, LexError, ParseError, ParseErrorType, Position,
|
||||
Scope,
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "no_custom_syntax"))]
|
||||
pub use api::custom_syntax::Expression;
|
||||
|
||||
/// _(debugging)_ Module containing types for debugging.
|
||||
/// Exported under the `debugging` feature only.
|
||||
#[cfg(feature = "debugging")]
|
||||
@ -245,15 +251,9 @@ pub use func::Shared;
|
||||
/// Alias to [`RefCell`][std::cell::RefCell] or [`RwLock`][std::sync::RwLock] depending on the `sync` feature flag.
|
||||
pub use func::Locked;
|
||||
|
||||
use func::{calc_fn_hash, calc_fn_hash_full, calc_var_hash};
|
||||
|
||||
/// A shared [`Module`].
|
||||
type SharedModule = Shared<Module>;
|
||||
|
||||
pub use rhai_codegen::*;
|
||||
|
||||
pub use func::{plugin, FuncArgs};
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
pub use func::Func;
|
||||
|
||||
@ -294,9 +294,6 @@ pub use module::ModuleResolver;
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
pub use module::resolvers as module_resolvers;
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
pub mod serde;
|
||||
|
||||
#[cfg(not(feature = "no_optimize"))]
|
||||
pub use optimizer::OptimizationLevel;
|
||||
|
||||
@ -341,9 +338,7 @@ pub use ast::CustomExpr;
|
||||
pub use ast::Namespace;
|
||||
|
||||
#[cfg(feature = "internals")]
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
pub use ast::EncapsulatedEnviron;
|
||||
pub use func::EncapsulatedEnviron;
|
||||
|
||||
#[cfg(feature = "internals")]
|
||||
pub use eval::{Caches, FnResolutionCache, FnResolutionCacheEntry, GlobalRuntimeState};
|
||||
|
@ -1,7 +1,7 @@
|
||||
//! Module defining external-loaded modules for Rhai.
|
||||
|
||||
#[cfg(feature = "metadata")]
|
||||
use crate::api::type_names::format_type;
|
||||
use crate::api::formatting::format_type;
|
||||
use crate::ast::FnAccess;
|
||||
use crate::func::{
|
||||
shared_take_or_clone, CallableFunction, FnCallArgs, IteratorFn, RegisterNativeFunction,
|
||||
@ -1221,7 +1221,10 @@ impl Module {
|
||||
access,
|
||||
None,
|
||||
arg_types,
|
||||
CallableFunction::Method(Shared::new(f), true),
|
||||
CallableFunction::Method {
|
||||
func: Shared::new(f),
|
||||
has_context: true,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@ -2138,31 +2141,22 @@ impl Module {
|
||||
// The return value is thrown away and not used
|
||||
let _ = result?;
|
||||
|
||||
// Variables with an alias left in the scope become module variables
|
||||
for (_name, value, mut aliases) in scope {
|
||||
// It is an error to export function pointers that refer to encapsulated local functions.
|
||||
//
|
||||
// Even if the function pointer already links to a scripted function definition, it may
|
||||
// cross-call other functions inside the module and won't have the full encapsulated
|
||||
// environment available.
|
||||
// Encapsulated environment
|
||||
let environ = Shared::new(crate::func::EncapsulatedEnviron {
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
if let Some(fn_ptr) = value.downcast_ref::<crate::FnPtr>() {
|
||||
if ast.iter_fn_def().any(|f| f.name == fn_ptr.fn_name()) {
|
||||
return Err(crate::ERR::ErrorMismatchDataType(
|
||||
String::new(),
|
||||
if fn_ptr.is_anonymous() {
|
||||
format!("cannot export closure in variable {_name}")
|
||||
} else {
|
||||
format!(
|
||||
"cannot export function pointer to local function '{}' in variable {_name}",
|
||||
fn_ptr.fn_name()
|
||||
)
|
||||
},
|
||||
crate::Position::NONE,
|
||||
)
|
||||
.into());
|
||||
}
|
||||
lib: ast.shared_lib().clone(),
|
||||
imports: imports.into(),
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
constants,
|
||||
});
|
||||
|
||||
// Variables with an alias left in the scope become module variables
|
||||
for (_name, mut value, mut aliases) in scope {
|
||||
value.deep_scan(|v| {
|
||||
if let Some(fn_ptr) = v.downcast_mut::<crate::FnPtr>() {
|
||||
fn_ptr.set_encapsulated_environ(Some(environ.clone()));
|
||||
}
|
||||
});
|
||||
|
||||
match aliases.len() {
|
||||
0 => (),
|
||||
@ -2183,34 +2177,21 @@ impl Module {
|
||||
|
||||
// Non-private functions defined become module functions
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
{
|
||||
let environ = Shared::new(crate::ast::EncapsulatedEnviron {
|
||||
lib: ast.shared_lib().clone(),
|
||||
imports: imports.into_boxed_slice(),
|
||||
constants,
|
||||
});
|
||||
|
||||
ast.shared_lib()
|
||||
.iter_fn()
|
||||
.filter(|&f| match f.metadata.access {
|
||||
ast.iter_fn_def()
|
||||
.filter(|&f| match f.access {
|
||||
FnAccess::Public => true,
|
||||
FnAccess::Private => false,
|
||||
})
|
||||
.filter(|&f| f.func.is_script())
|
||||
.for_each(|f| {
|
||||
let mut func = f
|
||||
.func
|
||||
.get_script_fn_def()
|
||||
.expect("script-defined function")
|
||||
.as_ref()
|
||||
.clone();
|
||||
let hash = module.set_script_fn(f.clone());
|
||||
let f = module.functions.as_mut().unwrap().get_mut(&hash).unwrap();
|
||||
|
||||
// Encapsulate AST environment
|
||||
func.environ = Some(environ.clone());
|
||||
|
||||
module.set_script_fn(func);
|
||||
});
|
||||
match &mut f.func {
|
||||
CallableFunction::Script { environ: e, .. } => *e = Some(environ.clone()),
|
||||
_ => (),
|
||||
}
|
||||
});
|
||||
|
||||
module.id = ast.source_raw().cloned();
|
||||
|
||||
|
@ -54,7 +54,7 @@ struct OptimizerState<'a> {
|
||||
/// Has the [`AST`] been changed during this pass?
|
||||
changed: bool,
|
||||
/// Collection of constants to use for eager function evaluations.
|
||||
variables: StaticVec<(Identifier, AccessMode, Dynamic)>,
|
||||
variables: StaticVec<(Identifier, AccessMode, Option<Dynamic>)>,
|
||||
/// Activate constants propagation?
|
||||
propagate_constants: bool,
|
||||
/// An [`Engine`] instance for eager function evaluation.
|
||||
@ -115,7 +115,12 @@ impl<'a> OptimizerState<'a> {
|
||||
}
|
||||
/// Add a new variable to the list.
|
||||
#[inline(always)]
|
||||
pub fn push_var(&mut self, name: impl Into<Identifier>, access: AccessMode, value: Dynamic) {
|
||||
pub fn push_var(
|
||||
&mut self,
|
||||
name: impl Into<Identifier>,
|
||||
access: AccessMode,
|
||||
value: Option<Dynamic>,
|
||||
) {
|
||||
self.variables.push((name.into(), access, value));
|
||||
}
|
||||
/// Look up a constant from the list.
|
||||
@ -129,8 +134,7 @@ impl<'a> OptimizerState<'a> {
|
||||
if n == name {
|
||||
return match access {
|
||||
AccessMode::ReadWrite => None,
|
||||
AccessMode::ReadOnly if value.is_null() => None,
|
||||
AccessMode::ReadOnly => Some(value),
|
||||
AccessMode::ReadOnly => value.as_ref(),
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -144,7 +148,7 @@ impl<'a> OptimizerState<'a> {
|
||||
fn_name: &str,
|
||||
op_token: Token,
|
||||
arg_values: &mut [Dynamic],
|
||||
) -> Dynamic {
|
||||
) -> Option<Dynamic> {
|
||||
self.engine
|
||||
.exec_native_fn_call(
|
||||
&mut self.global,
|
||||
@ -156,7 +160,8 @@ impl<'a> OptimizerState<'a> {
|
||||
false,
|
||||
Position::NONE,
|
||||
)
|
||||
.map_or(Dynamic::NULL, |(v, ..)| v)
|
||||
.ok()
|
||||
.map(|(v, ..)| v)
|
||||
}
|
||||
}
|
||||
|
||||
@ -234,13 +239,13 @@ fn optimize_stmt_block(
|
||||
state.push_var(
|
||||
x.0.as_str(),
|
||||
AccessMode::ReadOnly,
|
||||
x.1.get_literal_value().unwrap_or(Dynamic::NULL),
|
||||
x.1.get_literal_value(),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// Add variables into the state
|
||||
optimize_expr(&mut x.1, state, false);
|
||||
state.push_var(x.0.as_str(), AccessMode::ReadWrite, Dynamic::NULL);
|
||||
state.push_var(x.0.as_str(), AccessMode::ReadWrite, None);
|
||||
}
|
||||
}
|
||||
// Optimize the statement
|
||||
@ -1190,15 +1195,15 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) {
|
||||
let arg_values = &mut x.args.iter().map(Expr::get_literal_value).collect::<Option<StaticVec<_>>>().unwrap();
|
||||
|
||||
let result = match x.name.as_str() {
|
||||
KEYWORD_TYPE_OF if arg_values.len() == 1 => state.engine.map_type_name(arg_values[0].type_name()).into(),
|
||||
KEYWORD_TYPE_OF if arg_values.len() == 1 => Some(state.engine.map_type_name(arg_values[0].type_name()).into()),
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
crate::engine::KEYWORD_IS_SHARED if arg_values.len() == 1 => Dynamic::FALSE,
|
||||
crate::engine::KEYWORD_IS_SHARED if arg_values.len() == 1 => Some(Dynamic::FALSE),
|
||||
_ => state.call_fn_with_constant_arguments(&x.name, x.op_token.clone(), arg_values)
|
||||
};
|
||||
|
||||
if !result.is_null() {
|
||||
if let Some(r) = result {
|
||||
state.set_dirty();
|
||||
*expr = Expr::from_dynamic(result, *pos);
|
||||
*expr = Expr::from_dynamic(r, *pos);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -1303,15 +1308,15 @@ impl Engine {
|
||||
|
||||
// Add constants from global modules
|
||||
for (name, value) in self.global_modules.iter().rev().flat_map(|m| m.iter_var()) {
|
||||
state.push_var(name, AccessMode::ReadOnly, value.clone());
|
||||
state.push_var(name, AccessMode::ReadOnly, Some(value.clone()));
|
||||
}
|
||||
|
||||
// Add constants and variables from the scope
|
||||
for (name, constant, value) in scope.iter() {
|
||||
if constant {
|
||||
state.push_var(name, AccessMode::ReadOnly, value);
|
||||
state.push_var(name, AccessMode::ReadOnly, Some(value));
|
||||
} else {
|
||||
state.push_var(name, AccessMode::ReadWrite, Dynamic::NULL);
|
||||
state.push_var(name, AccessMode::ReadWrite, None);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1344,8 +1349,6 @@ impl Engine {
|
||||
access: fn_def.access,
|
||||
body: crate::ast::StmtBlock::NONE,
|
||||
params: fn_def.params.clone(),
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
environ: None,
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
#[cfg(feature = "metadata")]
|
||||
comments: Box::default(),
|
||||
|
@ -72,9 +72,9 @@ macro_rules! gen_arithmetic_functions {
|
||||
pub fn power(x: $arg_type, y: INT) -> RhaiResultOf<$arg_type> {
|
||||
if cfg!(not(feature = "unchecked")) {
|
||||
if cfg!(not(feature = "only_i32")) && y > (u32::MAX as INT) {
|
||||
Err(make_err(format!("Integer raised to too large an index: {x} ** {y}")))
|
||||
Err(make_err(format!("Exponential overflow: {x} ** {y}")))
|
||||
} else if y < 0 {
|
||||
Err(make_err(format!("Integer raised to a negative index: {x} ** {y}")))
|
||||
Err(make_err(format!("Integer raised to a negative power: {x} ** {y}")))
|
||||
} else {
|
||||
x.checked_pow(y as u32).ok_or_else(|| make_err(format!("Exponential overflow: {x} ** {y}")))
|
||||
}
|
||||
@ -83,32 +83,36 @@ macro_rules! gen_arithmetic_functions {
|
||||
}
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "<<", return_raw)]
|
||||
pub fn shift_left(x: $arg_type, y: INT) -> RhaiResultOf<$arg_type> {
|
||||
#[rhai_fn(name = "<<")]
|
||||
pub fn shift_left(x: $arg_type, y: INT) -> $arg_type {
|
||||
if cfg!(not(feature = "unchecked")) {
|
||||
if cfg!(not(feature = "only_i32")) && y > (u32::MAX as INT) {
|
||||
Err(make_err(format!("Left-shift by too many bits: {x} << {y}")))
|
||||
0
|
||||
} else if y < 0 {
|
||||
Err(make_err(format!("Left-shift by a negative number: {x} << {y}")))
|
||||
shift_right(x, y.checked_abs().unwrap_or(INT::MAX))
|
||||
} else {
|
||||
x.checked_shl(y as u32).ok_or_else(|| make_err(format!("Left-shift by too many bits: {x} << {y}")))
|
||||
x.checked_shl(y as u32).unwrap_or_else(|| 0)
|
||||
}
|
||||
} else if y < 0 {
|
||||
x >> -y
|
||||
} else {
|
||||
Ok(x << y)
|
||||
x << y
|
||||
}
|
||||
}
|
||||
#[rhai_fn(name = ">>", return_raw)]
|
||||
pub fn shift_right(x: $arg_type, y: INT) -> RhaiResultOf<$arg_type> {
|
||||
#[rhai_fn(name = ">>")]
|
||||
pub fn shift_right(x: $arg_type, y: INT) -> $arg_type {
|
||||
if cfg!(not(feature = "unchecked")) {
|
||||
if cfg!(not(feature = "only_i32")) && y > (u32::MAX as INT) {
|
||||
Err(make_err(format!("Right-shift by too many bits: {x} >> {y}")))
|
||||
x.wrapping_shr(u32::MAX)
|
||||
} else if y < 0 {
|
||||
Err(make_err(format!("Right-shift by a negative number: {x} >> {y}")))
|
||||
shift_left(x, y.checked_abs().unwrap_or(INT::MAX))
|
||||
} else {
|
||||
x.checked_shr(y as u32).ok_or_else(|| make_err(format!("Right-shift by too many bits: {x} >> {y}")))
|
||||
x.checked_shr(y as u32).unwrap_or_else(|| x.wrapping_shr(u32::MAX))
|
||||
}
|
||||
} else if y < 0 {
|
||||
x << -y
|
||||
} else {
|
||||
Ok(x >> y)
|
||||
x >> y
|
||||
}
|
||||
}
|
||||
#[rhai_fn(name = "&")]
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -2890,8 +2890,7 @@ impl Engine {
|
||||
will_shadow,
|
||||
};
|
||||
let caches = &mut Caches::new();
|
||||
let mut this_ptr = Dynamic::NULL;
|
||||
let context = EvalContext::new(self, global, caches, stack, &mut this_ptr);
|
||||
let context = EvalContext::new(self, global, caches, stack, None);
|
||||
|
||||
match filter(false, info, context) {
|
||||
Ok(true) => (),
|
||||
@ -3629,8 +3628,6 @@ impl Engine {
|
||||
access,
|
||||
params,
|
||||
body,
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
environ: None,
|
||||
#[cfg(feature = "metadata")]
|
||||
comments: comments.into_iter().collect(),
|
||||
})
|
||||
@ -3791,8 +3788,6 @@ impl Engine {
|
||||
access: crate::FnAccess::Public,
|
||||
params,
|
||||
body: body.into(),
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
environ: None,
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
#[cfg(feature = "metadata")]
|
||||
comments: Box::default(),
|
||||
|
@ -34,7 +34,7 @@ macro_rules! auto_restore {
|
||||
auto_restore!($var = $var => $restore);
|
||||
};
|
||||
($var:ident = $value:expr => $restore:expr) => {
|
||||
let $var = &mut *crate::types::RestoreOnDrop::lock($value, $restore);
|
||||
let $var = &mut *crate::RestoreOnDrop::lock($value, $restore);
|
||||
};
|
||||
($var:ident if Some($guard:ident) => $restore:expr) => {
|
||||
auto_restore!($var = ($var) if Some($guard) => $restore);
|
||||
@ -42,7 +42,7 @@ macro_rules! auto_restore {
|
||||
($var:ident = ( $value:expr ) if Some($guard:ident) => $restore:expr) => {
|
||||
let mut __rx__;
|
||||
let $var = if let Some($guard) = $guard {
|
||||
__rx__ = crate::types::RestoreOnDrop::lock($value, $restore);
|
||||
__rx__ = crate::RestoreOnDrop::lock($value, $restore);
|
||||
&mut *__rx__
|
||||
} else {
|
||||
&mut *$value
|
||||
@ -54,7 +54,7 @@ macro_rules! auto_restore {
|
||||
($var:ident = ( $value:expr ) if $guard:expr => $restore:expr) => {
|
||||
let mut __rx__;
|
||||
let $var = if $guard {
|
||||
__rx__ = crate::types::RestoreOnDrop::lock($value, $restore);
|
||||
__rx__ = crate::RestoreOnDrop::lock($value, $restore);
|
||||
&mut *__rx__
|
||||
} else {
|
||||
&mut *$value
|
@ -125,8 +125,6 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> {
|
||||
|
||||
fn deserialize_any<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
|
||||
match self.0 .0 {
|
||||
Union::Null => unreachable!(),
|
||||
|
||||
Union::Unit(..) => self.deserialize_unit(visitor),
|
||||
Union::Bool(..) => self.deserialize_bool(visitor),
|
||||
Union::Str(..) => self.deserialize_str(visitor),
|
||||
|
@ -1,7 +1,7 @@
|
||||
//! Serialization of functions metadata.
|
||||
#![cfg(feature = "metadata")]
|
||||
|
||||
use crate::api::type_names::format_type;
|
||||
use crate::api::formatting::format_type;
|
||||
use crate::module::{calc_native_fn_hash, FuncInfo, ModuleFlags};
|
||||
use crate::{calc_fn_hash, Engine, FnAccess, SmartString, StaticVec, AST};
|
||||
use serde::Serialize;
|
||||
|
@ -15,8 +15,6 @@ use crate::types::dynamic::Variant;
|
||||
impl Serialize for Dynamic {
|
||||
fn serialize<S: Serializer>(&self, ser: S) -> Result<S::Ok, S::Error> {
|
||||
match self.0 {
|
||||
Union::Null => unreachable!(),
|
||||
|
||||
Union::Unit(..) => ser.serialize_unit(),
|
||||
Union::Bool(x, ..) => ser.serialize_bool(x),
|
||||
Union::Str(ref s, ..) => ser.serialize_str(s.as_str()),
|
||||
|
16
src/tests.rs
16
src/tests.rs
@ -32,15 +32,27 @@ fn check_struct_sizes() {
|
||||
if IS_32_BIT { 12 } else { 16 }
|
||||
);
|
||||
|
||||
#[cfg(feature = "internals")]
|
||||
{
|
||||
assert_eq!(
|
||||
size_of::<CallableFunction>(),
|
||||
if IS_32_BIT { 12 } else { 24 }
|
||||
);
|
||||
assert_eq!(
|
||||
size_of::<module::FuncInfo>(),
|
||||
if IS_32_BIT { 16 } else { 32 }
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
{
|
||||
assert_eq!(size_of::<Scope>(), 536);
|
||||
assert_eq!(
|
||||
size_of::<FnPtr>(),
|
||||
if cfg!(feature = "no_function") {
|
||||
64
|
||||
} else {
|
||||
72
|
||||
} else {
|
||||
80
|
||||
}
|
||||
);
|
||||
assert_eq!(size_of::<LexError>(), 56);
|
||||
|
@ -26,6 +26,8 @@ pub struct TokenizerControlBlock {
|
||||
/// Global comments.
|
||||
#[cfg(feature = "metadata")]
|
||||
pub global_comments: String,
|
||||
/// Whitespace-compressed version of the script (if any).
|
||||
pub compressed: Option<String>,
|
||||
}
|
||||
|
||||
impl TokenizerControlBlock {
|
||||
@ -37,6 +39,7 @@ impl TokenizerControlBlock {
|
||||
is_within_text: false,
|
||||
#[cfg(feature = "metadata")]
|
||||
global_comments: String::new(),
|
||||
compressed: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -879,6 +882,8 @@ pub struct TokenizeState {
|
||||
pub include_comments: bool,
|
||||
/// Is the current tokenizer position within the text stream of an interpolated string?
|
||||
pub is_within_text_terminated_by: Option<char>,
|
||||
/// Last token
|
||||
pub last_token: Option<SmartString>,
|
||||
}
|
||||
|
||||
/// _(internals)_ Trait that encapsulates a peekable character input stream.
|
||||
@ -956,6 +961,10 @@ pub fn parse_string_literal(
|
||||
let mut skip_whitespace_until = 0;
|
||||
|
||||
state.is_within_text_terminated_by = Some(termination_char);
|
||||
state.last_token.as_mut().map(|last| {
|
||||
last.clear();
|
||||
last.push(termination_char);
|
||||
});
|
||||
|
||||
loop {
|
||||
assert!(
|
||||
@ -985,6 +994,8 @@ pub fn parse_string_literal(
|
||||
}
|
||||
};
|
||||
|
||||
state.last_token.as_mut().map(|last| last.push(next_char));
|
||||
|
||||
// String interpolation?
|
||||
if allow_interpolation
|
||||
&& next_char == '$'
|
||||
@ -1004,6 +1015,10 @@ pub fn parse_string_literal(
|
||||
// Double wrapper
|
||||
if stream.peek_next().map_or(false, |c| c == termination_char) {
|
||||
eat_next(stream, pos);
|
||||
state
|
||||
.last_token
|
||||
.as_mut()
|
||||
.map(|last| last.push(termination_char));
|
||||
} else {
|
||||
state.is_within_text_terminated_by = None;
|
||||
break;
|
||||
@ -1060,6 +1075,7 @@ pub fn parse_string_literal(
|
||||
.get_next()
|
||||
.ok_or_else(|| (LERR::MalformedEscapeSequence(seq.to_string()), *pos))?;
|
||||
|
||||
state.last_token.as_mut().map(|last| last.push(c));
|
||||
seq.push(c);
|
||||
pos.advance();
|
||||
|
||||
@ -1240,6 +1256,8 @@ fn get_next_token_inner(
|
||||
state: &mut TokenizeState,
|
||||
pos: &mut Position,
|
||||
) -> Option<(Token, Position)> {
|
||||
state.last_token.as_mut().map(|last| last.clear());
|
||||
|
||||
// Still inside a comment?
|
||||
if state.comment_level > 0 {
|
||||
let start_pos = *pos;
|
||||
@ -1398,6 +1416,8 @@ fn get_next_token_inner(
|
||||
negated_pos
|
||||
});
|
||||
|
||||
state.last_token.as_mut().map(|last| *last = result.clone());
|
||||
|
||||
// Parse number
|
||||
let token = radix_base.map_or_else(
|
||||
|| {
|
||||
@ -1452,14 +1472,14 @@ fn get_next_token_inner(
|
||||
#[cfg(not(feature = "unicode-xid-ident"))]
|
||||
('a'..='z' | '_' | 'A'..='Z', ..) => {
|
||||
return Some(
|
||||
parse_identifier_token(stream, pos, start_pos, c)
|
||||
parse_identifier_token(stream, state, pos, start_pos, c)
|
||||
.unwrap_or_else(|err| (Token::LexError(err.into()), start_pos)),
|
||||
);
|
||||
}
|
||||
#[cfg(feature = "unicode-xid-ident")]
|
||||
(ch, ..) if unicode_xid::UnicodeXID::is_xid_start(ch) || ch == '_' => {
|
||||
return Some(
|
||||
parse_identifier_token(stream, pos, start_pos, c)
|
||||
parse_identifier_token(stream, state, pos, start_pos, c)
|
||||
.unwrap_or_else(|err| (Token::LexError(err.into()), start_pos)),
|
||||
);
|
||||
}
|
||||
@ -1942,18 +1962,24 @@ fn get_next_token_inner(
|
||||
/// Get the next token, parsing it as an identifier.
|
||||
fn parse_identifier_token(
|
||||
stream: &mut impl InputStream,
|
||||
state: &mut TokenizeState,
|
||||
pos: &mut Position,
|
||||
start_pos: Position,
|
||||
first_char: char,
|
||||
) -> Result<(Token, Position), LexError> {
|
||||
let mut identifier = SmartString::new_const();
|
||||
identifier.push(first_char);
|
||||
state.last_token.as_mut().map(|last| {
|
||||
last.clear();
|
||||
last.push(first_char);
|
||||
});
|
||||
|
||||
while let Some(next_char) = stream.peek_next() {
|
||||
match next_char {
|
||||
x if is_id_continue(x) => {
|
||||
identifier.push(x);
|
||||
eat_next(stream, pos);
|
||||
identifier.push(x);
|
||||
state.last_token.as_mut().map(|last| last.push(x));
|
||||
}
|
||||
_ => break,
|
||||
}
|
||||
@ -2129,7 +2155,7 @@ impl<'a> Iterator for TokenIterator<'a> {
|
||||
type Item = (Token, Position);
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
{
|
||||
let (within_interpolated, compress_script) = {
|
||||
let control = &mut *self.state.tokenizer_control.borrow_mut();
|
||||
|
||||
if control.is_within_text {
|
||||
@ -2138,7 +2164,12 @@ impl<'a> Iterator for TokenIterator<'a> {
|
||||
// Reset it
|
||||
control.is_within_text = false;
|
||||
}
|
||||
}
|
||||
|
||||
(
|
||||
self.state.is_within_text_terminated_by.is_some(),
|
||||
control.compressed.is_some(),
|
||||
)
|
||||
};
|
||||
|
||||
let (token, pos) = match get_next_token(&mut self.stream, &mut self.state, &mut self.pos) {
|
||||
// {EOF}
|
||||
@ -2230,6 +2261,49 @@ impl<'a> Iterator for TokenIterator<'a> {
|
||||
None => token,
|
||||
};
|
||||
|
||||
// Collect the compressed script, if needed
|
||||
if compress_script {
|
||||
let control = &mut *self.state.tokenizer_control.borrow_mut();
|
||||
|
||||
if let Some(ref mut compressed) = control.compressed {
|
||||
if !matches!(token, Token::EOF) {
|
||||
use std::fmt::Write;
|
||||
|
||||
let last_token = self.state.last_token.as_ref().unwrap();
|
||||
let mut buf = SmartString::new_const();
|
||||
|
||||
if last_token.is_empty() {
|
||||
write!(buf, "{token}").unwrap();
|
||||
} else if within_interpolated
|
||||
&& matches!(
|
||||
token,
|
||||
Token::StringConstant(..) | Token::InterpolatedString(..)
|
||||
)
|
||||
{
|
||||
compressed.push_str(&last_token[1..]);
|
||||
} else {
|
||||
buf = last_token.clone();
|
||||
}
|
||||
|
||||
if !buf.is_empty() {
|
||||
if !compressed.is_empty() {
|
||||
let prev = compressed.chars().last().unwrap();
|
||||
let cur = buf.chars().next().unwrap();
|
||||
if (prev == '_' || is_id_first_alphabetic(prev) || is_id_continue(prev))
|
||||
&& (cur == '_'
|
||||
|| is_id_first_alphabetic(cur)
|
||||
|| is_id_continue(cur))
|
||||
{
|
||||
compressed.push(' ');
|
||||
}
|
||||
}
|
||||
|
||||
compressed.push_str(&buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some((token, pos))
|
||||
}
|
||||
}
|
||||
@ -2281,6 +2355,7 @@ impl Engine {
|
||||
comment_level: 0,
|
||||
include_comments: false,
|
||||
is_within_text_terminated_by: None,
|
||||
last_token: None,
|
||||
},
|
||||
pos: Position::new(1, 0),
|
||||
stream: MultiInputsStream {
|
||||
|
@ -57,9 +57,6 @@ pub struct Dynamic(pub(crate) Union);
|
||||
/// Most variants are boxed to reduce the size.
|
||||
#[must_use]
|
||||
pub enum Union {
|
||||
/// An error value which should not exist.
|
||||
Null,
|
||||
|
||||
/// The Unit value - ().
|
||||
Unit((), Tag, AccessMode),
|
||||
/// A boolean value.
|
||||
@ -187,8 +184,6 @@ impl Dynamic {
|
||||
#[must_use]
|
||||
pub const fn tag(&self) -> Tag {
|
||||
match self.0 {
|
||||
Union::Null => unreachable!(),
|
||||
|
||||
Union::Unit(_, tag, _)
|
||||
| Union::Bool(_, tag, _)
|
||||
| Union::Str(_, tag, _)
|
||||
@ -214,8 +209,6 @@ impl Dynamic {
|
||||
/// Attach arbitrary data to this [`Dynamic`].
|
||||
pub fn set_tag(&mut self, value: Tag) -> &mut Self {
|
||||
match self.0 {
|
||||
Union::Null => unreachable!(),
|
||||
|
||||
Union::Unit(_, ref mut tag, _)
|
||||
| Union::Bool(_, ref mut tag, _)
|
||||
| Union::Str(_, ref mut tag, _)
|
||||
@ -239,12 +232,6 @@ impl Dynamic {
|
||||
}
|
||||
self
|
||||
}
|
||||
/// Is this [`Dynamic`] null?
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub(crate) const fn is_null(&self) -> bool {
|
||||
matches!(self.0, Union::Null)
|
||||
}
|
||||
/// Does this [`Dynamic`] hold a variant data type instead of one of the supported system
|
||||
/// primitive types?
|
||||
#[inline(always)]
|
||||
@ -334,8 +321,6 @@ impl Dynamic {
|
||||
#[must_use]
|
||||
pub fn type_id(&self) -> TypeId {
|
||||
match self.0 {
|
||||
Union::Null => unreachable!(),
|
||||
|
||||
Union::Unit(..) => TypeId::of::<()>(),
|
||||
Union::Bool(..) => TypeId::of::<bool>(),
|
||||
Union::Str(..) => TypeId::of::<ImmutableString>(),
|
||||
@ -370,8 +355,6 @@ impl Dynamic {
|
||||
#[must_use]
|
||||
pub fn type_name(&self) -> &'static str {
|
||||
match self.0 {
|
||||
Union::Null => unreachable!(),
|
||||
|
||||
Union::Unit(..) => "()",
|
||||
Union::Bool(..) => "bool",
|
||||
Union::Str(..) => "string",
|
||||
@ -416,8 +399,6 @@ impl Hash for Dynamic {
|
||||
mem::discriminant(&self.0).hash(state);
|
||||
|
||||
match self.0 {
|
||||
Union::Null => unreachable!(),
|
||||
|
||||
Union::Unit(..) => (),
|
||||
Union::Bool(ref b, ..) => b.hash(state),
|
||||
Union::Str(ref s, ..) => s.hash(state),
|
||||
@ -449,8 +430,6 @@ impl Hash for Dynamic {
|
||||
impl fmt::Display for Dynamic {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self.0 {
|
||||
Union::Null => unreachable!(),
|
||||
|
||||
Union::Unit(..) => Ok(()),
|
||||
Union::Bool(ref v, ..) => fmt::Display::fmt(v, f),
|
||||
Union::Str(ref v, ..) => fmt::Display::fmt(v, f),
|
||||
@ -544,8 +523,6 @@ impl fmt::Debug for Dynamic {
|
||||
#[inline(never)]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self.0 {
|
||||
Union::Null => unreachable!(),
|
||||
|
||||
Union::Unit(ref v, ..) => fmt::Debug::fmt(v, f),
|
||||
Union::Bool(ref v, ..) => fmt::Debug::fmt(v, f),
|
||||
Union::Str(ref v, ..) => fmt::Debug::fmt(v, f),
|
||||
@ -657,8 +634,6 @@ impl Clone for Dynamic {
|
||||
/// The cloned copy is marked read-write even if the original is read-only.
|
||||
fn clone(&self) -> Self {
|
||||
match self.0 {
|
||||
Union::Null => unreachable!(),
|
||||
|
||||
Union::Unit(v, tag, ..) => Self(Union::Unit(v, tag, ReadWrite)),
|
||||
Union::Bool(v, tag, ..) => Self(Union::Bool(v, tag, ReadWrite)),
|
||||
Union::Str(ref v, tag, ..) => Self(Union::Str(v.clone(), tag, ReadWrite)),
|
||||
@ -705,9 +680,6 @@ use std::f32::consts as FloatConstants;
|
||||
use std::f64::consts as FloatConstants;
|
||||
|
||||
impl Dynamic {
|
||||
/// A [`Dynamic`] containing a `null`.
|
||||
pub(crate) const NULL: Self = Self(Union::Null);
|
||||
|
||||
/// A [`Dynamic`] containing a `()`.
|
||||
pub const UNIT: Self = Self(Union::Unit((), DEFAULT_TAG_VALUE, ReadWrite));
|
||||
/// A [`Dynamic`] containing a `true`.
|
||||
@ -921,8 +893,6 @@ impl Dynamic {
|
||||
#[must_use]
|
||||
pub(crate) const fn access_mode(&self) -> AccessMode {
|
||||
match self.0 {
|
||||
Union::Null => unreachable!(),
|
||||
|
||||
Union::Unit(.., access)
|
||||
| Union::Bool(.., access)
|
||||
| Union::Str(.., access)
|
||||
@ -948,8 +918,6 @@ impl Dynamic {
|
||||
/// Set the [`AccessMode`] for this [`Dynamic`].
|
||||
pub(crate) fn set_access_mode(&mut self, typ: AccessMode) -> &mut Self {
|
||||
match self.0 {
|
||||
Union::Null => unreachable!(),
|
||||
|
||||
Union::Unit(.., ref mut access)
|
||||
| Union::Bool(.., ref mut access)
|
||||
| Union::Str(.., ref mut access)
|
||||
@ -1138,7 +1106,6 @@ impl Dynamic {
|
||||
let _access = self.access_mode();
|
||||
|
||||
match self.0 {
|
||||
Union::Null => unreachable!(),
|
||||
Union::Shared(..) => self,
|
||||
_ => Self(Union::Shared(
|
||||
crate::Locked::new(self).into(),
|
||||
@ -1584,7 +1551,6 @@ impl Dynamic {
|
||||
}
|
||||
|
||||
match self.0 {
|
||||
Union::Null => unreachable!(),
|
||||
Union::Variant(ref v, ..) => (***v).as_any().downcast_ref::<T>(),
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
Union::Shared(..) => None,
|
||||
@ -1683,7 +1649,6 @@ impl Dynamic {
|
||||
}
|
||||
|
||||
match self.0 {
|
||||
Union::Null => unreachable!(),
|
||||
Union::Variant(ref mut v, ..) => (***v).as_any_mut().downcast_mut::<T>(),
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
Union::Shared(..) => None,
|
||||
@ -2084,6 +2049,27 @@ impl Dynamic {
|
||||
_ => Err(self.type_name()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Recursively scan for [`Dynamic`] values within this [`Dynamic`] (e.g. items in an array or map),
|
||||
/// calling a filter function on each.
|
||||
///
|
||||
/// Shared values are _NOT_ scanned.
|
||||
#[inline]
|
||||
pub fn deep_scan(&mut self, mut filter: impl FnMut(&mut Self)) {
|
||||
fn scan_inner(value: &mut Dynamic, filter: &mut impl FnMut(&mut Dynamic)) {
|
||||
match &mut value.0 {
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Union::Array(a, ..) => a.iter_mut().for_each(|v| scan_inner(v, filter)),
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Union::Map(m, ..) => m.values_mut().for_each(|v| scan_inner(v, filter)),
|
||||
Union::FnPtr(f, ..) => f.iter_curry_mut().for_each(|v| scan_inner(v, filter)),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
filter(self);
|
||||
scan_inner(self, &mut filter);
|
||||
}
|
||||
}
|
||||
|
||||
impl From<()> for Dynamic {
|
||||
|
@ -1,11 +1,12 @@
|
||||
//! The `FnPtr` type.
|
||||
|
||||
use crate::eval::GlobalRuntimeState;
|
||||
use crate::func::EncapsulatedEnviron;
|
||||
use crate::tokenizer::is_valid_function_name;
|
||||
use crate::types::dynamic::Variant;
|
||||
use crate::{
|
||||
Dynamic, Engine, FnArgsVec, FuncArgs, ImmutableString, NativeCallContext, Position, RhaiError,
|
||||
RhaiResult, RhaiResultOf, StaticVec, AST, ERR,
|
||||
RhaiResult, RhaiResultOf, Shared, StaticVec, AST, ERR,
|
||||
};
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
@ -13,8 +14,9 @@ use std::{
|
||||
any::{type_name, TypeId},
|
||||
convert::{TryFrom, TryInto},
|
||||
fmt,
|
||||
hash::Hash,
|
||||
hash::{Hash, Hasher},
|
||||
mem,
|
||||
ops::{Index, IndexMut},
|
||||
};
|
||||
|
||||
/// A general function pointer, which may carry additional (i.e. curried) argument values
|
||||
@ -23,22 +25,23 @@ use std::{
|
||||
pub struct FnPtr {
|
||||
name: ImmutableString,
|
||||
curry: StaticVec<Dynamic>,
|
||||
environ: Option<Shared<EncapsulatedEnviron>>,
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
fn_def: Option<crate::Shared<crate::ast::ScriptFnDef>>,
|
||||
fn_def: Option<Shared<crate::ast::ScriptFnDef>>,
|
||||
}
|
||||
|
||||
impl Hash for FnPtr {
|
||||
#[inline(always)]
|
||||
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.name.hash(state);
|
||||
self.curry.hash(state);
|
||||
|
||||
// Hash the shared [`EncapsulatedEnviron`] by hashing its shared pointer.
|
||||
self.environ.as_ref().map(|e| Shared::as_ptr(e)).hash(state);
|
||||
|
||||
// Hash the linked [`ScriptFnDef`][crate::ast::ScriptFnDef] by hashing its shared pointer.
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
self.fn_def
|
||||
.as_ref()
|
||||
.map(|f| crate::Shared::as_ptr(f))
|
||||
.hash(state);
|
||||
self.fn_def.as_ref().map(|f| Shared::as_ptr(f)).hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
@ -75,6 +78,7 @@ impl FnPtr {
|
||||
Self {
|
||||
name: name.into(),
|
||||
curry,
|
||||
environ: None,
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
fn_def: None,
|
||||
}
|
||||
@ -100,16 +104,23 @@ impl FnPtr {
|
||||
) -> (
|
||||
ImmutableString,
|
||||
StaticVec<Dynamic>,
|
||||
Option<crate::Shared<crate::ast::ScriptFnDef>>,
|
||||
Option<Shared<EncapsulatedEnviron>>,
|
||||
Option<Shared<crate::ast::ScriptFnDef>>,
|
||||
) {
|
||||
(self.name, self.curry, self.fn_def)
|
||||
(self.name, self.curry, self.environ, self.fn_def)
|
||||
}
|
||||
/// Get the underlying data of the function pointer.
|
||||
#[cfg(feature = "no_function")]
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub(crate) fn take_data(self) -> (ImmutableString, StaticVec<Dynamic>) {
|
||||
(self.name, self.curry)
|
||||
pub(crate) fn take_data(
|
||||
self,
|
||||
) -> (
|
||||
ImmutableString,
|
||||
StaticVec<Dynamic>,
|
||||
Option<Shared<EncapsulatedEnviron>>,
|
||||
) {
|
||||
(self.name, self.curry, self.environ)
|
||||
}
|
||||
/// Get the curried arguments.
|
||||
#[inline(always)]
|
||||
@ -117,6 +128,16 @@ impl FnPtr {
|
||||
pub fn curry(&self) -> &[Dynamic] {
|
||||
self.curry.as_ref()
|
||||
}
|
||||
/// Iterate the curried arguments.
|
||||
#[inline(always)]
|
||||
pub fn iter_curry(&self) -> impl Iterator<Item = &Dynamic> {
|
||||
self.curry.iter()
|
||||
}
|
||||
/// Mutably-iterate the curried arguments.
|
||||
#[inline(always)]
|
||||
pub fn iter_curry_mut(&mut self) -> impl Iterator<Item = &mut Dynamic> {
|
||||
self.curry.iter_mut()
|
||||
}
|
||||
/// Add a new curried argument.
|
||||
#[inline(always)]
|
||||
pub fn add_curry(&mut self, value: Dynamic) -> &mut Self {
|
||||
@ -183,7 +204,7 @@ impl FnPtr {
|
||||
args: impl FuncArgs,
|
||||
) -> RhaiResultOf<T> {
|
||||
let _ast = ast;
|
||||
let mut arg_values = crate::StaticVec::new_const();
|
||||
let mut arg_values = StaticVec::new_const();
|
||||
args.parse(&mut arg_values);
|
||||
|
||||
let global = &mut GlobalRuntimeState::new(engine);
|
||||
@ -219,7 +240,7 @@ impl FnPtr {
|
||||
context: &NativeCallContext,
|
||||
args: impl FuncArgs,
|
||||
) -> RhaiResultOf<T> {
|
||||
let mut arg_values = crate::StaticVec::new_const();
|
||||
let mut arg_values = StaticVec::new_const();
|
||||
args.parse(&mut arg_values);
|
||||
|
||||
self.call_raw(context, None, arg_values).and_then(|result| {
|
||||
@ -277,19 +298,20 @@ impl FnPtr {
|
||||
|
||||
// Linked to scripted function?
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
if let Some(fn_def) = self.fn_def() {
|
||||
if let Some(ref fn_def) = self.fn_def {
|
||||
if fn_def.params.len() == args.len() {
|
||||
let global = &mut context.global_runtime_state().clone();
|
||||
global.level += 1;
|
||||
|
||||
let caches = &mut crate::eval::Caches::new();
|
||||
let mut null_ptr = Dynamic::NULL;
|
||||
let mut this_ptr = this_ptr;
|
||||
|
||||
return context.engine().call_script_fn(
|
||||
global,
|
||||
caches,
|
||||
&mut crate::Scope::new(),
|
||||
this_ptr.unwrap_or(&mut null_ptr),
|
||||
this_ptr.as_deref_mut(),
|
||||
self.encapsulated_environ(),
|
||||
&fn_def,
|
||||
args,
|
||||
true,
|
||||
@ -306,22 +328,119 @@ impl FnPtr {
|
||||
|
||||
context.call_fn_raw(self.fn_name(), is_method, is_method, args)
|
||||
}
|
||||
/// Get a reference to the [encapsulated environment][EncapsulatedEnviron].
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn encapsulated_environ(&self) -> Option<&EncapsulatedEnviron> {
|
||||
self.environ.as_deref()
|
||||
}
|
||||
/// Set a reference to the [encapsulated environment][EncapsulatedEnviron].
|
||||
#[inline(always)]
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn set_encapsulated_environ(
|
||||
&mut self,
|
||||
value: Option<impl Into<Shared<EncapsulatedEnviron>>>,
|
||||
) {
|
||||
self.environ = value.map(Into::into);
|
||||
}
|
||||
/// Get a reference to the linked [`ScriptFnDef`][crate::ast::ScriptFnDef].
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub(crate) fn fn_def(&self) -> Option<&crate::Shared<crate::ast::ScriptFnDef>> {
|
||||
self.fn_def.as_ref()
|
||||
pub(crate) fn fn_def(&self) -> Option<&crate::ast::ScriptFnDef> {
|
||||
self.fn_def.as_deref()
|
||||
}
|
||||
/// Set a reference to the linked [`ScriptFnDef`][crate::ast::ScriptFnDef].
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
#[inline(always)]
|
||||
pub(crate) fn set_fn_def(
|
||||
&mut self,
|
||||
value: Option<impl Into<crate::Shared<crate::ast::ScriptFnDef>>>,
|
||||
) {
|
||||
pub(crate) fn set_fn_def(&mut self, value: Option<impl Into<Shared<crate::ast::ScriptFnDef>>>) {
|
||||
self.fn_def = value.map(Into::into);
|
||||
}
|
||||
|
||||
/// Make a call to a function pointer with either a specified number of arguments, or with extra
|
||||
/// arguments attached.
|
||||
///
|
||||
/// This is useful for calling predicate closures within an iteration loop where the extra argument
|
||||
/// is the current element's index.
|
||||
///
|
||||
/// If the function pointer is linked to a scripted function definition, use the appropriate number
|
||||
/// of arguments to call it directly (one version attaches extra arguments).
|
||||
#[cfg(not(feature = "internals"))]
|
||||
#[inline(always)]
|
||||
pub(crate) fn call_raw_with_extra_args<const N: usize, const E: usize>(
|
||||
&self,
|
||||
fn_name: &str,
|
||||
ctx: &NativeCallContext,
|
||||
this_ptr: Option<&mut Dynamic>,
|
||||
items: [Dynamic; N],
|
||||
extras: [Dynamic; E],
|
||||
) -> RhaiResult {
|
||||
self._call_with_extra_args(fn_name, ctx, this_ptr, items, extras)
|
||||
}
|
||||
/// _(internals)_ Make a call to a function pointer with either a specified number of arguments,
|
||||
/// or with extra arguments attached.
|
||||
/// Exported under the `internals` feature only.
|
||||
///
|
||||
/// This is useful for calling predicate closures within an iteration loop where the extra
|
||||
/// argument is the current element's index.
|
||||
///
|
||||
/// If the function pointer is linked to a scripted function definition, use the appropriate
|
||||
/// number of arguments to call it directly (one version attaches extra arguments).
|
||||
#[cfg(feature = "internals")]
|
||||
#[inline(always)]
|
||||
pub fn call_raw_with_extra_args<const N: usize, const E: usize>(
|
||||
&self,
|
||||
fn_name: &str,
|
||||
ctx: &NativeCallContext,
|
||||
this_ptr: Option<&mut Dynamic>,
|
||||
items: [Dynamic; N],
|
||||
extras: [Dynamic; E],
|
||||
) -> RhaiResult {
|
||||
self._call_with_extra_args(fn_name, ctx, this_ptr, items, extras)
|
||||
}
|
||||
/// Make a call to a function pointer with either a specified number of arguments, or with extra
|
||||
/// arguments attached.
|
||||
fn _call_with_extra_args<const N: usize, const E: usize>(
|
||||
&self,
|
||||
fn_name: &str,
|
||||
ctx: &NativeCallContext,
|
||||
mut this_ptr: Option<&mut Dynamic>,
|
||||
items: [Dynamic; N],
|
||||
extras: [Dynamic; E],
|
||||
) -> RhaiResult {
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
if let Some(arity) = self.fn_def().map(|f| f.params.len()) {
|
||||
if arity == N {
|
||||
return self.call_raw(&ctx, None, items);
|
||||
}
|
||||
if arity == N + E {
|
||||
let mut items2 = FnArgsVec::with_capacity(items.len() + extras.len());
|
||||
items2.extend(IntoIterator::into_iter(items));
|
||||
items2.extend(IntoIterator::into_iter(extras));
|
||||
return self.call_raw(&ctx, this_ptr, items2);
|
||||
}
|
||||
}
|
||||
|
||||
self.call_raw(&ctx, this_ptr.as_deref_mut(), items.clone())
|
||||
.or_else(|err| match *err {
|
||||
ERR::ErrorFunctionNotFound(sig, ..) if sig.starts_with(self.fn_name()) => {
|
||||
let mut items2 = FnArgsVec::with_capacity(items.len() + extras.len());
|
||||
items2.extend(IntoIterator::into_iter(items));
|
||||
items2.extend(IntoIterator::into_iter(extras));
|
||||
self.call_raw(&ctx, this_ptr, items2)
|
||||
}
|
||||
_ => Err(err),
|
||||
})
|
||||
.map_err(|err| {
|
||||
Box::new(ERR::ErrorInFunctionCall(
|
||||
fn_name.to_string(),
|
||||
ctx.source().unwrap_or("").to_string(),
|
||||
err,
|
||||
Position::NONE,
|
||||
))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for FnPtr {
|
||||
@ -339,6 +458,7 @@ impl TryFrom<ImmutableString> for FnPtr {
|
||||
Ok(Self {
|
||||
name: value,
|
||||
curry: StaticVec::new_const(),
|
||||
environ: None,
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
fn_def: None,
|
||||
})
|
||||
@ -349,7 +469,7 @@ impl TryFrom<ImmutableString> for FnPtr {
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
impl<T: Into<crate::Shared<crate::ast::ScriptFnDef>>> From<T> for FnPtr {
|
||||
impl<T: Into<Shared<crate::ast::ScriptFnDef>>> From<T> for FnPtr {
|
||||
#[inline(always)]
|
||||
fn from(value: T) -> Self {
|
||||
let fn_def = value.into();
|
||||
@ -357,7 +477,24 @@ impl<T: Into<crate::Shared<crate::ast::ScriptFnDef>>> From<T> for FnPtr {
|
||||
Self {
|
||||
name: fn_def.name.clone(),
|
||||
curry: StaticVec::new_const(),
|
||||
environ: None,
|
||||
fn_def: Some(fn_def),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Index<usize> for FnPtr {
|
||||
type Output = Dynamic;
|
||||
|
||||
#[inline(always)]
|
||||
fn index(&self, index: usize) -> &Self::Output {
|
||||
self.curry.index(index)
|
||||
}
|
||||
}
|
||||
|
||||
impl IndexMut<usize> for FnPtr {
|
||||
#[inline(always)]
|
||||
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
|
||||
self.curry.index_mut(index)
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,5 @@
|
||||
//! Module defining Rhai data types.
|
||||
|
||||
#[macro_use]
|
||||
pub mod restore;
|
||||
|
||||
pub mod bloom_filter;
|
||||
pub mod custom_types;
|
||||
pub mod dynamic;
|
||||
@ -35,6 +32,5 @@ pub use position::{Position, Span};
|
||||
#[cfg(feature = "no_position")]
|
||||
pub use position_none::{Position, Span};
|
||||
|
||||
pub use restore::RestoreOnDrop;
|
||||
pub use scope::Scope;
|
||||
pub use variant::Variant;
|
||||
|
@ -30,7 +30,9 @@ fn test_binary_ops() -> Result<(), Box<EvalAltResult>> {
|
||||
assert_eq!(engine.eval::<INT>("let x = 10; x %= 4; x")?, 2);
|
||||
assert_eq!(engine.eval::<INT>("let x = 10; x **= 4; x")?, 10000);
|
||||
assert_eq!(engine.eval::<INT>("let x = 10; x <<= 4; x")?, 160);
|
||||
assert_eq!(engine.eval::<INT>("let x = 10; x <<= -1; x")?, 5);
|
||||
assert_eq!(engine.eval::<INT>("let x = 10; x >>= 4; x")?, 0);
|
||||
assert_eq!(engine.eval::<INT>("let x = 10; x >>= -2; x")?, 40);
|
||||
assert_eq!(engine.eval::<INT>("let x = 10; x &= 4; x")?, 0);
|
||||
assert_eq!(engine.eval::<INT>("let x = 10; x |= 4; x")?, 14);
|
||||
assert_eq!(engine.eval::<INT>("let x = 10; x ^= 4; x")?, 14);
|
||||
|
Loading…
Reference in New Issue
Block a user