Merge pull request #188 from schungx/master
Anonymous functions, no-std fix, simplify custom syntax, minor speed improvements.
This commit is contained in:
commit
9231dbf7b1
@ -46,10 +46,12 @@ codegen-units = 1
|
|||||||
|
|
||||||
[dependencies.libm]
|
[dependencies.libm]
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
|
default_features = false
|
||||||
optional = true
|
optional = true
|
||||||
|
|
||||||
[dependencies.core-error]
|
[dependencies.core-error]
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
|
default_features = false
|
||||||
features = ["alloc"]
|
features = ["alloc"]
|
||||||
optional = true
|
optional = true
|
||||||
|
|
||||||
@ -66,9 +68,9 @@ features = ["compile-time-rng"]
|
|||||||
optional = true
|
optional = true
|
||||||
|
|
||||||
[dependencies.serde]
|
[dependencies.serde]
|
||||||
package = "serde"
|
|
||||||
version = "1.0.111"
|
version = "1.0.111"
|
||||||
features = ["derive"]
|
default_features = false
|
||||||
|
features = ["derive", "alloc"]
|
||||||
optional = true
|
optional = true
|
||||||
|
|
||||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||||
|
10
RELEASES.md
10
RELEASES.md
@ -4,12 +4,22 @@ Rhai Release Notes
|
|||||||
Version 0.18.0
|
Version 0.18.0
|
||||||
==============
|
==============
|
||||||
|
|
||||||
|
This version adds:
|
||||||
|
|
||||||
|
* Anonymous functions (in closure syntax). Simplifies creation of ad hoc functions.
|
||||||
|
|
||||||
New features
|
New features
|
||||||
------------
|
------------
|
||||||
|
|
||||||
* `call` can now be called function-call style for function pointers - this is to handle builds with `no_object`.
|
* `call` can now be called function-call style for function pointers - this is to handle builds with `no_object`.
|
||||||
* Disallow many keywords as variables, such as `print`, `eval`, `call`, `this` etc.
|
* Disallow many keywords as variables, such as `print`, `eval`, `call`, `this` etc.
|
||||||
* `x.call(f, ...)` allows binding `x` to `this` for the function referenced by the function pointer `f`.
|
* `x.call(f, ...)` allows binding `x` to `this` for the function referenced by the function pointer `f`.
|
||||||
|
* Anonymous functions in the syntax of a closure, e.g. `|x, y, z| x + y - z`.
|
||||||
|
|
||||||
|
Breaking changes
|
||||||
|
----------------
|
||||||
|
|
||||||
|
* Function signature for defining custom syntax is simplified.
|
||||||
|
|
||||||
|
|
||||||
Version 0.17.0
|
Version 0.17.0
|
||||||
|
@ -75,6 +75,7 @@ The Rhai Scripting Language
|
|||||||
2. [Overloading](language/overload.md)
|
2. [Overloading](language/overload.md)
|
||||||
3. [Namespaces](language/fn-namespaces.md)
|
3. [Namespaces](language/fn-namespaces.md)
|
||||||
4. [Function Pointers](language/fn-ptr.md)
|
4. [Function Pointers](language/fn-ptr.md)
|
||||||
|
5. [Anonymous Functions](language/fn-anon.md)
|
||||||
15. [Print and Debug](language/print-debug.md)
|
15. [Print and Debug](language/print-debug.md)
|
||||||
16. [Modules](language/modules/index.md)
|
16. [Modules](language/modules/index.md)
|
||||||
1. [Export Variables, Functions and Sub-Modules](language/modules/export.md)
|
1. [Export Variables, Functions and Sub-Modules](language/modules/export.md)
|
||||||
|
@ -25,12 +25,12 @@ It doesn't attempt to be a new language. For example:
|
|||||||
* No closures - do your closure magic in Rust instead; [turn a Rhai scripted function into a Rust closure]({{rootUrl}}/engine/call-fn.md).
|
* No closures - do your closure magic in Rust instead; [turn a Rhai scripted function into a Rust closure]({{rootUrl}}/engine/call-fn.md).
|
||||||
|
|
||||||
* No byte-codes/JIT - Rhai has an AST-walking interpreter which will not win any speed races. The purpose of Rhai is not
|
* No byte-codes/JIT - Rhai has an AST-walking interpreter which will not win any speed races. The purpose of Rhai is not
|
||||||
to be extremely _fast_, but to make it as easy as possible to integrate with native Rust programs.
|
to be extremely _fast_, but to make it as easy as possible to integrate with native Rust applications.
|
||||||
|
|
||||||
Due to this intended usage, Rhai deliberately keeps the language simple and small by omitting advanced language features
|
Due to this intended usage, Rhai deliberately keeps the language simple and small by omitting advanced language features
|
||||||
such as classes, inheritance, first-class functions, closures, concurrency, byte-codes, JIT etc.
|
such as classes, inheritance, first-class functions, closures, concurrency, byte-codes, JIT etc.
|
||||||
|
|
||||||
Avoid the temptation to write full-fledge program logic entirely in Rhai - that use case is best fulfilled by
|
Avoid the temptation to write full-fledge application logic entirely in Rhai - that use case is best fulfilled by
|
||||||
more complete languages such as JavaScript or Lua.
|
more complete languages such as JavaScript or Lua.
|
||||||
|
|
||||||
Therefore, in actual practice, it is usually best to expose a Rust API into Rhai for scripts to call.
|
Therefore, in actual practice, it is usually best to expose a Rust API into Rhai for scripts to call.
|
||||||
|
@ -126,32 +126,21 @@ Any custom syntax must include an _implementation_ of it.
|
|||||||
The function signature of an implementation is:
|
The function signature of an implementation is:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
Fn(
|
Fn(engine: &Engine, context: &mut EvalContext, scope: &mut Scope, inputs: &[Expression])
|
||||||
engine: &Engine,
|
-> Result<Dynamic, Box<EvalAltResult>>
|
||||||
scope: &mut Scope,
|
|
||||||
mods: &mut Imports,
|
|
||||||
state: &mut State,
|
|
||||||
lib: &Module,
|
|
||||||
this_ptr: &mut Option<&mut Dynamic>,
|
|
||||||
inputs: &[Expression],
|
|
||||||
level: usize
|
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>>
|
|
||||||
```
|
```
|
||||||
|
|
||||||
where:
|
where:
|
||||||
|
|
||||||
* `engine : &Engine` - reference to the current [`Engine`].
|
* `engine: &Engine` - reference to the current [`Engine`].
|
||||||
* `scope : &mut Scope` - mutable reference to the current [`Scope`]; variables can be added to it.
|
* `context: &mut EvalContext` - mutable reference to the current evaluation _context_; **do not touch**.
|
||||||
* `mods : &mut Imports` - mutable reference to the current collection of imported [`Module`]'s; **do not touch**.
|
* `scope: &mut Scope` - mutable reference to the current [`Scope`]; variables can be added to it.
|
||||||
* `state : &mut State` - mutable reference to the current evaluation state; **do not touch**.
|
* `inputs: &[Expression]` - a list of input expression trees.
|
||||||
* `lib : &Module` - reference to the current collection of script-defined functions.
|
|
||||||
* `this_ptr : &mut Option<&mut Dynamic>` - mutable reference to the current binding of the `this` pointer; **do not touch**.
|
|
||||||
* `inputs : &[Expression]` - a list of input expression trees.
|
|
||||||
* `level : usize` - the current function call level.
|
|
||||||
|
|
||||||
There are a lot of parameters, most of which should not be touched or Bad Things Happen™.
|
#### WARNING - Lark's Vomit
|
||||||
They represent the running _content_ of a script evaluation and should simply be passed
|
|
||||||
straight-through the the [`Engine`].
|
The `context` parameter contains the evaluation _context_ and should not be touched or Bad Things Happen™.
|
||||||
|
It should simply be passed straight-through the the [`Engine`].
|
||||||
|
|
||||||
### Access Arguments
|
### Access Arguments
|
||||||
|
|
||||||
@ -172,7 +161,7 @@ Use the `engine::eval_expression_tree` method to evaluate an expression tree.
|
|||||||
|
|
||||||
```rust
|
```rust
|
||||||
let expr = inputs.get(0).unwrap();
|
let expr = inputs.get(0).unwrap();
|
||||||
let result = engine.eval_expression_tree(scope, mods, state, lib, this_ptr, expr, level)?;
|
let result = engine.eval_expression_tree(context, scope, expr)?;
|
||||||
```
|
```
|
||||||
|
|
||||||
As can be seem above, most arguments are simply passed straight-through to `engine::eval_expression_tree`.
|
As can be seem above, most arguments are simply passed straight-through to `engine::eval_expression_tree`.
|
||||||
@ -210,13 +199,9 @@ The syntax is passed simply as a slice of `&str`.
|
|||||||
// Custom syntax implementation
|
// Custom syntax implementation
|
||||||
fn implementation_func(
|
fn implementation_func(
|
||||||
engine: &Engine,
|
engine: &Engine,
|
||||||
|
context: &mut EvalContext,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
mods: &mut Imports,
|
inputs: &[Expression]
|
||||||
state: &mut State,
|
|
||||||
lib: &Module,
|
|
||||||
this_ptr: &mut Option<&mut Dynamic>,
|
|
||||||
inputs: &[Expression],
|
|
||||||
level: usize
|
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
let var_name = inputs[0].get_variable_name().unwrap().to_string();
|
let var_name = inputs[0].get_variable_name().unwrap().to_string();
|
||||||
let stmt = inputs.get(1).unwrap();
|
let stmt = inputs.get(1).unwrap();
|
||||||
@ -227,15 +212,12 @@ fn implementation_func(
|
|||||||
|
|
||||||
loop {
|
loop {
|
||||||
// Evaluate the statement block
|
// Evaluate the statement block
|
||||||
engine.eval_expression_tree(scope, mods, state, lib, this_ptr, stmt, level)?;
|
engine.eval_expression_tree(context, scope, stmt)?;
|
||||||
|
|
||||||
// Evaluate the condition expression
|
// Evaluate the condition expression
|
||||||
let stop = !engine
|
let stop = !engine.eval_expression_tree(context, scope, condition)?
|
||||||
.eval_expression_tree(scope, mods, state, lib, this_ptr, condition, level)?
|
.as_bool().map_err(|_| EvalAltResult::ErrorBooleanArgMismatch(
|
||||||
.as_bool()
|
"do-while".into(), expr.position()))?;
|
||||||
.map_err(|_| EvalAltResult::ErrorBooleanArgMismatch(
|
|
||||||
"do-while".into(), expr.position()
|
|
||||||
))?;
|
|
||||||
|
|
||||||
if stop {
|
if stop {
|
||||||
break;
|
break;
|
||||||
|
@ -7,7 +7,7 @@ Raw `Engine`
|
|||||||
`Engine::new` creates a scripting [`Engine`] with common functionalities (e.g. printing to the console via `print`).
|
`Engine::new` creates a scripting [`Engine`] with common functionalities (e.g. printing to the console via `print`).
|
||||||
|
|
||||||
In many controlled embedded environments, however, these may not be needed and unnecessarily occupy
|
In many controlled embedded environments, however, these may not be needed and unnecessarily occupy
|
||||||
program code storage space.
|
application code storage space.
|
||||||
|
|
||||||
Use `Engine::new_raw` to create a _raw_ `Engine`, in which only a minimal set of
|
Use `Engine::new_raw` to create a _raw_ `Engine`, in which only a minimal set of
|
||||||
basic arithmetic and logical operators are supported.
|
basic arithmetic and logical operators are supported.
|
||||||
|
57
doc/src/language/fn-anon.md
Normal file
57
doc/src/language/fn-anon.md
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
Anonymous Functions
|
||||||
|
===================
|
||||||
|
|
||||||
|
{{#include ../links.md}}
|
||||||
|
|
||||||
|
Sometimes it gets tedious to define separate functions only to dispatch them via single [function pointers].
|
||||||
|
This scenario is especially common when simulating object-oriented programming ([OOP]).
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// Define object
|
||||||
|
let obj = #{
|
||||||
|
data: 42,
|
||||||
|
increment: Fn("inc_obj"), // use function pointers to
|
||||||
|
decrement: Fn("dec_obj"), // refer to method functions
|
||||||
|
print: Fn("print_obj")
|
||||||
|
};
|
||||||
|
|
||||||
|
// Define method functions one-by-one
|
||||||
|
fn inc_obj(x) { this.data += x; }
|
||||||
|
fn dec_obj(x) { this.data -= x; }
|
||||||
|
fn print_obj() { print(this.data); }
|
||||||
|
```
|
||||||
|
|
||||||
|
The above can be replaced by using _anonymous functions_ which have the same syntax as Rust's closures
|
||||||
|
(but they are **NOT** closures, merely syntactic sugar):
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let obj = #{
|
||||||
|
data: 42,
|
||||||
|
increment: |x| this.data += x, // one-liner
|
||||||
|
decrement: |x| this.data -= x,
|
||||||
|
print_obj: || { print(this.data); } // full function body
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
The anonymous functions will be hoisted into separate functions in the global namespace.
|
||||||
|
The above is equivalent to:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let obj = #{
|
||||||
|
data: 42,
|
||||||
|
increment: Fn("anon_fn_1000"),
|
||||||
|
decrement: Fn("anon_fn_1001"),
|
||||||
|
print: Fn("anon_fn_1002")
|
||||||
|
};
|
||||||
|
|
||||||
|
fn anon_fn_1000(x) { this.data += x; }
|
||||||
|
fn anon_fn_1001(x) { this.data -= x; }
|
||||||
|
fn anon_fn_1002() { print this.data; }
|
||||||
|
```
|
||||||
|
|
||||||
|
WARNING - NOT Closures
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
Remember: anonymous functions, though having the same syntax as Rust _closures_, are themselves
|
||||||
|
**not** closures. In particular, they do not capture their running environment. They are more like
|
||||||
|
Rust's function pointers.
|
@ -163,4 +163,4 @@ x == 42;
|
|||||||
Beware that this only works for _method-call_ style. Normal function-call style cannot bind
|
Beware that this only works for _method-call_ style. Normal function-call style cannot bind
|
||||||
the `this` pointer (for syntactic reasons).
|
the `this` pointer (for syntactic reasons).
|
||||||
|
|
||||||
Therefore, obviously, binding the `this` pointer is unsupported under [`no_function`].
|
Therefore, obviously, binding the `this` pointer is unsupported under [`no_object`].
|
||||||
|
@ -20,17 +20,21 @@ to the [object map] before the function is called. There is no way to simulate
|
|||||||
via a normal function-call syntax because all scripted function arguments are passed by value.
|
via a normal function-call syntax because all scripted function arguments are passed by value.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
fn do_action(x) { print(this.data + x); } // 'this' binds to the object when called
|
fn do_action(x) { this.data += x; } // 'this' binds to the object when called
|
||||||
|
|
||||||
let obj = #{
|
let obj = #{
|
||||||
data: 40,
|
data: 40,
|
||||||
action: Fn("do_action") // 'action' holds a function pointer to 'do_action'
|
action: Fn("do_action") // 'action' holds a function pointer to 'do_action'
|
||||||
};
|
};
|
||||||
|
|
||||||
obj.action(2); // Short-hand syntax: prints 42
|
obj.action(2); // Calls 'do_action' with `this` bound to 'obj'
|
||||||
|
|
||||||
// To achieve the above with normal function pointer calls:
|
obj.call(obj.action, 2); // The above de-sugars to this
|
||||||
fn do_action(map, x) { print(map.data + x); }
|
|
||||||
|
|
||||||
obj.action.call(obj, 2); // this call cannot mutate 'obj'
|
obj.data == 42;
|
||||||
|
|
||||||
|
// To achieve the above with normal function pointer call will fail.
|
||||||
|
fn do_action(map, x) { map.data += x; } // 'map' is a copy
|
||||||
|
|
||||||
|
obj.action.call(obj, 2); // 'obj' is passed as a copy by value
|
||||||
```
|
```
|
||||||
|
@ -17,23 +17,22 @@ Rhai's [object maps] has [special support for OOP]({{rootUrl}}/language/object-m
|
|||||||
| [Object map] properties holding values | properties |
|
| [Object map] properties holding values | properties |
|
||||||
| [Object map] properties that hold [function pointers] | methods |
|
| [Object map] properties that hold [function pointers] | methods |
|
||||||
|
|
||||||
|
When a property of an [object map] is called like a method function, and if it happens to hold
|
||||||
|
a valid [function pointer] (perhaps defined via an [anonymous function]), then the call will be
|
||||||
|
dispatched to the actual function with `this` binding to the [object map] itself.
|
||||||
|
|
||||||
Examples
|
Examples
|
||||||
--------
|
--------
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
// Define the object
|
// Define the object
|
||||||
let obj = #{
|
let obj =
|
||||||
data: 0,
|
#{
|
||||||
increment: Fn("add"), // when called, 'this' binds to 'obj'
|
data: 0,
|
||||||
update: Fn("update"), // when called, 'this' binds to 'obj'
|
increment: |x| this.data += x, // when called, 'this' binds to 'obj'
|
||||||
action: Fn("action") // when called, 'this' binds to 'obj'
|
update: |x| this.data = x, // when called, 'this' binds to 'obj'
|
||||||
};
|
action: || print(this.data) // when called, 'this' binds to 'obj'
|
||||||
|
};
|
||||||
// Define functions
|
|
||||||
fn add(x) { this.data += x; } // update using 'this'
|
|
||||||
fn update(x) { this.data = x; } // update using 'this'
|
|
||||||
fn action() { print(this.data); } // access properties of 'this'
|
|
||||||
|
|
||||||
// Use the object
|
// Use the object
|
||||||
obj.increment(1);
|
obj.increment(1);
|
||||||
|
@ -78,6 +78,8 @@
|
|||||||
[function pointers]: {{rootUrl}}/language/fn-ptr.md
|
[function pointers]: {{rootUrl}}/language/fn-ptr.md
|
||||||
[function namespace]: {{rootUrl}}/language/fn-namespaces.md
|
[function namespace]: {{rootUrl}}/language/fn-namespaces.md
|
||||||
[function namespaces]: {{rootUrl}}/language/fn-namespaces.md
|
[function namespaces]: {{rootUrl}}/language/fn-namespaces.md
|
||||||
|
[anonymous function]: {{rootUrl}}/language/fn-anon.md
|
||||||
|
[anonymous functions]: {{rootUrl}}/language/fn-anon.md
|
||||||
|
|
||||||
[`Module`]: {{rootUrl}}/language/modules/index.md
|
[`Module`]: {{rootUrl}}/language/modules/index.md
|
||||||
[module]: {{rootUrl}}/language/modules/index.md
|
[module]: {{rootUrl}}/language/modules/index.md
|
||||||
|
@ -13,3 +13,9 @@ Nightly Required
|
|||||||
----------------
|
----------------
|
||||||
|
|
||||||
Currently, [`no_std`] requires the nightly compiler due to the crates that it uses.
|
Currently, [`no_std`] requires the nightly compiler due to the crates that it uses.
|
||||||
|
|
||||||
|
|
||||||
|
Samples
|
||||||
|
-------
|
||||||
|
|
||||||
|
Check out the [`no-std` sample applications](../examples/rust.md#no-std-samples) for different operating environments.
|
||||||
|
@ -3,14 +3,13 @@ Rust Examples
|
|||||||
|
|
||||||
{{#include ../../links.md}}
|
{{#include ../../links.md}}
|
||||||
|
|
||||||
A number of examples can be found in the `examples` folder:
|
A number of examples can be found in the `examples` directory:
|
||||||
|
|
||||||
| Example | Description |
|
| Example | Description |
|
||||||
| ---------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
|
| ---------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| [`arrays_and_structs`](https://github.com/jonathandturner/rhai/tree/master/examples/arrays_and_structs.rs) | Shows how to register a custom Rust type and using [arrays] on it. |
|
| [`arrays_and_structs`](https://github.com/jonathandturner/rhai/tree/master/examples/arrays_and_structs.rs) | Shows how to register a custom Rust type and using [arrays] on it. |
|
||||||
| [`custom_types_and_methods`](https://github.com/jonathandturner/rhai/tree/master/examples/custom_types_and_methods.rs) | Shows how to register a custom Rust type and methods for it. |
|
| [`custom_types_and_methods`](https://github.com/jonathandturner/rhai/tree/master/examples/custom_types_and_methods.rs) | Shows how to register a custom Rust type and methods for it. |
|
||||||
| [`hello`](https://github.com/jonathandturner/rhai/tree/master/examples/hello.rs) | Simple example that evaluates an expression and prints the result. |
|
| [`hello`](https://github.com/jonathandturner/rhai/tree/master/examples/hello.rs) | Simple example that evaluates an expression and prints the result. |
|
||||||
| [`no_std`](https://github.com/jonathandturner/rhai/tree/master/examples/no_std.rs) | Example to test out `no-std` builds.</br>The [`no_std`] feature is required to build in `no-std`. |
|
|
||||||
| [`reuse_scope`](https://github.com/jonathandturner/rhai/tree/master/examples/reuse_scope.rs) | Evaluates two pieces of code in separate runs, but using a common [`Scope`]. |
|
| [`reuse_scope`](https://github.com/jonathandturner/rhai/tree/master/examples/reuse_scope.rs) | Evaluates two pieces of code in separate runs, but using a common [`Scope`]. |
|
||||||
| [`rhai_runner`](https://github.com/jonathandturner/rhai/tree/master/examples/rhai_runner.rs) | Runs each filename passed to it as a Rhai script. |
|
| [`rhai_runner`](https://github.com/jonathandturner/rhai/tree/master/examples/rhai_runner.rs) | Runs each filename passed to it as a Rhai script. |
|
||||||
| [`serde`](https://github.com/jonathandturner/rhai/tree/master/examples/serde.rs) | Example to serialize and deserialize Rust types with [`serde`](https://crates.io/crates/serde).<br/>The [`serde`] feature is required to run. |
|
| [`serde`](https://github.com/jonathandturner/rhai/tree/master/examples/serde.rs) | Example to serialize and deserialize Rust types with [`serde`](https://crates.io/crates/serde).<br/>The [`serde`] feature is required to run. |
|
||||||
@ -30,3 +29,22 @@ Examples can be run with the following command:
|
|||||||
```bash
|
```bash
|
||||||
cargo run --example {example_name}
|
cargo run --example {example_name}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
`no-std` Samples
|
||||||
|
----------------
|
||||||
|
|
||||||
|
To illustrate `no-std` builds, a number of sample applications are available under the `no_std` directory:
|
||||||
|
|
||||||
|
| Sample | Description | Optimization | Allocator | Panics |
|
||||||
|
| --------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- | :----------: | :-----------------------------------------------: | :----: |
|
||||||
|
| [`no_std_test`](https://github.com/jonathandturner/rhai/tree/master/no_std/no_std_test) | Bare-bones test application that evaluates a Rhai expression and sets the result as the return value. | Size | [`wee_alloc`](https://crates.io/crates/wee_alloc) | Abort |
|
||||||
|
|
||||||
|
`cargo run` cannot be used to run a `no-std` sample. It must first be built:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd no_std/no_std_test
|
||||||
|
|
||||||
|
cargo +nightly build --release
|
||||||
|
|
||||||
|
./target/release/no_std_test
|
||||||
|
```
|
||||||
|
@ -6,27 +6,27 @@ Example Scripts
|
|||||||
Language Feature Scripts
|
Language Feature Scripts
|
||||||
-----------------------
|
-----------------------
|
||||||
|
|
||||||
There are also a number of examples scripts that showcase Rhai's features, all in the `scripts` folder:
|
There are also a number of examples scripts that showcase Rhai's features, all in the `scripts` directory:
|
||||||
|
|
||||||
| Script | Description |
|
| Script | Description |
|
||||||
| -------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- |
|
| -------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- |
|
||||||
| [`array.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/array.rhai) | [Arrays] |
|
| [`array.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/array.rhai) | [Arrays] |
|
||||||
| [`assignment.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/assignment.rhai) | Variable declarations |
|
| [`assignment.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/assignment.rhai) | Variable declarations |
|
||||||
| [`comments.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/comments.rhai) | Just comments |
|
| [`comments.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/comments.rhai) | Just comments |
|
||||||
| [`for1.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/for1.rhai) | [`for`](#for-loop) loops |
|
| [`for1.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/for1.rhai) | [`for`]({{rootUrl}}/language/for.md) loops |
|
||||||
| [`for2.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/for2.rhai) | [`for`](#for-loop) loops on [arrays] |
|
| [`for2.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/for2.rhai) | [`for`]({{rootUrl}}/language/for.md) loops on [arrays] |
|
||||||
| [`function_decl1.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/function_decl1.rhai) | A [function] without parameters |
|
| [`function_decl1.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/function_decl1.rhai) | A [function] without parameters |
|
||||||
| [`function_decl2.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/function_decl2.rhai) | A [function] with two parameters |
|
| [`function_decl2.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/function_decl2.rhai) | A [function] with two parameters |
|
||||||
| [`function_decl3.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/function_decl3.rhai) | A [function] with many parameters |
|
| [`function_decl3.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/function_decl3.rhai) | A [function] with many parameters |
|
||||||
| [`if1.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/if1.rhai) | [`if`](#if-statement) example |
|
| [`if1.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/if1.rhai) | [`if`]({{rootUrl}}/language/if.md) example |
|
||||||
| [`loop.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/loop.rhai) | Count-down [`loop`](#infinite-loop) in Rhai, emulating a `do` .. `while` loop |
|
| [`loop.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/loop.rhai) | Count-down [`loop`]({{rootUrl}}/language/loop.md) in Rhai, emulating a `do` .. `while` loop |
|
||||||
| [`oop.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/oop.rhai) | Simulate [object-oriented programming (OOP)][OOP] |
|
| [`oop.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/oop.rhai) | Simulate [object-oriented programming (OOP)][OOP] |
|
||||||
| [`op1.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/op1.rhai) | Just simple addition |
|
| [`op1.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/op1.rhai) | Just simple addition |
|
||||||
| [`op2.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/op2.rhai) | Simple addition and multiplication |
|
| [`op2.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/op2.rhai) | Simple addition and multiplication |
|
||||||
| [`op3.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/op3.rhai) | Change evaluation order with parenthesis |
|
| [`op3.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/op3.rhai) | Change evaluation order with parenthesis |
|
||||||
| [`string.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/string.rhai) | [String] operations |
|
| [`string.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/string.rhai) | [String] operations |
|
||||||
| [`strings_map.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/strings_map.rhai) | [String] and [object map] operations |
|
| [`strings_map.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/strings_map.rhai) | [String] and [object map] operations |
|
||||||
| [`while.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/while.rhai) | [`while`](#while-loop) loop |
|
| [`while.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/while.rhai) | [`while`]({{rootUrl}}/language/while.md) loop |
|
||||||
|
|
||||||
|
|
||||||
Benchmark Scripts
|
Benchmark Scripts
|
||||||
@ -34,18 +34,18 @@ Benchmark Scripts
|
|||||||
|
|
||||||
The following scripts are for benchmarking the speed of Rhai:
|
The following scripts are for benchmarking the speed of Rhai:
|
||||||
|
|
||||||
| Scripts | Description |
|
| Scripts | Description |
|
||||||
| ------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------- |
|
| ------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------- |
|
||||||
| [`speed_test.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/speed_test.rhai) | A simple program to measure the speed of Rhai's interpreter (1 million iterations). |
|
| [`speed_test.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/speed_test.rhai) | A simple application to measure the speed of Rhai's interpreter (1 million iterations). |
|
||||||
| [`primes.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/primes.rhai) | Use Sieve of Eratosthenes to find all primes smaller than a limit. |
|
| [`primes.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/primes.rhai) | Use Sieve of Eratosthenes to find all primes smaller than a limit. |
|
||||||
| [`fibonacci.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/fibonacci.rhai) | Calculate the n-th Fibonacci number using a really dumb algorithm. |
|
| [`fibonacci.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/fibonacci.rhai) | Calculate the n-th Fibonacci number using a really dumb algorithm. |
|
||||||
| [`mat_mul.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/mat_mul.rhai) | Matrix multiplication test to measure the speed of multi-dimensional array access. |
|
| [`mat_mul.rhai`](https://github.com/jonathandturner/rhai/tree/master/scripts/mat_mul.rhai) | Matrix multiplication test to measure the speed of multi-dimensional array access. |
|
||||||
|
|
||||||
|
|
||||||
Running Example Scripts
|
Running Example Scripts
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
To run the scripts, either make a tiny program or use of the `rhai_runner` example:
|
The [`rhai_runner`](../examples/rust.md) example can be used to run the scripts:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cargo run --example rhai_runner scripts/any_script.rhai
|
cargo run --example rhai_runner scripts/any_script.rhai
|
||||||
|
@ -1,23 +0,0 @@
|
|||||||
#![cfg_attr(feature = "no_std", no_std)]
|
|
||||||
|
|
||||||
use rhai::{Engine, EvalAltResult, INT};
|
|
||||||
|
|
||||||
#[cfg(feature = "no_std")]
|
|
||||||
extern crate alloc;
|
|
||||||
|
|
||||||
#[cfg(feature = "no_std")]
|
|
||||||
use alloc::boxed::Box;
|
|
||||||
|
|
||||||
fn main() -> Result<(), Box<EvalAltResult>> {
|
|
||||||
let engine = Engine::new();
|
|
||||||
|
|
||||||
let result = engine.eval::<INT>("40 + 2")?;
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_std"))]
|
|
||||||
println!("Answer: {}", result);
|
|
||||||
|
|
||||||
#[cfg(feature = "no_std")]
|
|
||||||
assert_eq!(result, 42);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
24
no_std/no_std_test/Cargo.toml
Normal file
24
no_std/no_std_test/Cargo.toml
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
[package]
|
||||||
|
name = "no_std_test"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2018"
|
||||||
|
authors = ["Stephen Chung"]
|
||||||
|
description = "no-std test application"
|
||||||
|
homepage = "https://github.com/jonathandturner/rhai/tree/master/no_std/no_std_test"
|
||||||
|
repository = "https://github.com/jonathandturner/rhai"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
rhai = { path = "../../", features = [ "no_std", "unchecked", "only_i32", "no_module" ], default_features = false }
|
||||||
|
wee_alloc = { version = "0.4.5", default_features = false }
|
||||||
|
|
||||||
|
[profile.dev]
|
||||||
|
panic = "abort"
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
opt-level = "z" # optimize for size
|
||||||
|
debug = false
|
||||||
|
rpath = false
|
||||||
|
lto = "fat"
|
||||||
|
debug-assertions = false
|
||||||
|
codegen-units = 1
|
||||||
|
panic = "abort"
|
18
no_std/no_std_test/README.md
Normal file
18
no_std/no_std_test/README.md
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
`no-std` Test Sample
|
||||||
|
====================
|
||||||
|
|
||||||
|
This sample application is a bare-bones `no-std` build for testing.
|
||||||
|
|
||||||
|
[`wee_alloc`](https://crates.io/crates/wee_alloc) is used as the allocator.
|
||||||
|
|
||||||
|
|
||||||
|
To Compile
|
||||||
|
----------
|
||||||
|
|
||||||
|
The nightly compiler is required:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo +nightly build --release
|
||||||
|
```
|
||||||
|
|
||||||
|
The release build is optimized for size. It can be changed to optimize on speed instead.
|
43
no_std/no_std_test/src/main.rs
Normal file
43
no_std/no_std_test/src/main.rs
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
//! This is a bare-bones `no-std` application that evaluates
|
||||||
|
//! a simple expression and uses the result as the return value.
|
||||||
|
|
||||||
|
#![no_std]
|
||||||
|
#![feature(alloc_error_handler, start, core_intrinsics, lang_items)]
|
||||||
|
|
||||||
|
extern crate alloc;
|
||||||
|
extern crate wee_alloc;
|
||||||
|
|
||||||
|
#[global_allocator]
|
||||||
|
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
|
||||||
|
|
||||||
|
use rhai::{Engine, INT};
|
||||||
|
|
||||||
|
#[start]
|
||||||
|
fn main(_argc: isize, _argv: *const *const u8) -> isize {
|
||||||
|
// Notice that this is a _raw_ engine.
|
||||||
|
// To do anything useful, load a few packages from `rhai::packages`.
|
||||||
|
let engine = Engine::new_raw();
|
||||||
|
|
||||||
|
// Evaluate a simple expression: 40 + 2
|
||||||
|
engine.eval_expression::<INT>("40 + 2").unwrap() as isize
|
||||||
|
}
|
||||||
|
|
||||||
|
#[alloc_error_handler]
|
||||||
|
fn foo(_: core::alloc::Layout) -> ! {
|
||||||
|
core::intrinsics::abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[panic_handler]
|
||||||
|
#[lang = "panic_impl"]
|
||||||
|
extern "C" fn rust_begin_panic(_: &core::panic::PanicInfo) -> ! {
|
||||||
|
core::intrinsics::abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[lang = "eh_personality"]
|
||||||
|
extern "C" fn eh_personality() {}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
extern "C" fn rust_eh_register_frames() {}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
extern "C" fn rust_eh_unregister_frames() {}
|
334
src/any.rs
334
src/any.rs
@ -203,6 +203,7 @@ impl Dynamic {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Map the name of a standard type into a friendly form.
|
/// Map the name of a standard type into a friendly form.
|
||||||
|
#[inline]
|
||||||
pub(crate) fn map_std_type_name(name: &str) -> &str {
|
pub(crate) fn map_std_type_name(name: &str) -> &str {
|
||||||
if name == type_name::<String>() {
|
if name == type_name::<String>() {
|
||||||
"string"
|
"string"
|
||||||
@ -243,12 +244,15 @@ impl fmt::Display for Dynamic {
|
|||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Union::Array(value) => fmt::Debug::fmt(value, f),
|
Union::Array(value) => fmt::Debug::fmt(value, f),
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Union::Map(value) => write!(f, "#{:?}", value),
|
Union::Map(value) => {
|
||||||
|
f.write_str("#")?;
|
||||||
|
fmt::Debug::fmt(value, f)
|
||||||
|
}
|
||||||
Union::FnPtr(value) => fmt::Display::fmt(value, f),
|
Union::FnPtr(value) => fmt::Display::fmt(value, f),
|
||||||
|
|
||||||
#[cfg(not(feature = "no_std"))]
|
#[cfg(not(feature = "no_std"))]
|
||||||
Union::Variant(value) if value.is::<Instant>() => write!(f, "<timestamp>"),
|
Union::Variant(value) if value.is::<Instant>() => write!(f, "<timestamp>"),
|
||||||
Union::Variant(_) => write!(f, "?"),
|
Union::Variant(value) => write!(f, "{}", (*value).type_name()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -266,12 +270,15 @@ impl fmt::Debug for Dynamic {
|
|||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Union::Array(value) => fmt::Debug::fmt(value, f),
|
Union::Array(value) => fmt::Debug::fmt(value, f),
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Union::Map(value) => write!(f, "#{:?}", value),
|
Union::Map(value) => {
|
||||||
|
f.write_str("#")?;
|
||||||
|
fmt::Debug::fmt(value, f)
|
||||||
|
}
|
||||||
Union::FnPtr(value) => fmt::Display::fmt(value, f),
|
Union::FnPtr(value) => fmt::Display::fmt(value, f),
|
||||||
|
|
||||||
#[cfg(not(feature = "no_std"))]
|
#[cfg(not(feature = "no_std"))]
|
||||||
Union::Variant(value) if value.is::<Instant>() => write!(f, "<timestamp>"),
|
Union::Variant(value) if value.is::<Instant>() => write!(f, "<timestamp>"),
|
||||||
Union::Variant(_) => write!(f, "<dynamic>"),
|
Union::Variant(value) => write!(f, "{}", (*value).type_name()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -334,30 +341,47 @@ impl Dynamic {
|
|||||||
/// assert_eq!(new_result.type_name(), "string");
|
/// assert_eq!(new_result.type_name(), "string");
|
||||||
/// assert_eq!(new_result.to_string(), "hello");
|
/// assert_eq!(new_result.to_string(), "hello");
|
||||||
/// ```
|
/// ```
|
||||||
|
#[inline(always)]
|
||||||
pub fn from<T: Variant + Clone>(value: T) -> Self {
|
pub fn from<T: Variant + Clone>(value: T) -> Self {
|
||||||
if let Some(result) = <dyn Any>::downcast_ref::<()>(&value) {
|
let type_id = TypeId::of::<T>();
|
||||||
return result.clone().into();
|
|
||||||
} else if let Some(result) = <dyn Any>::downcast_ref::<bool>(&value) {
|
|
||||||
return result.clone().into();
|
|
||||||
} else if let Some(result) = <dyn Any>::downcast_ref::<INT>(&value) {
|
|
||||||
return result.clone().into();
|
|
||||||
} else if let Some(result) = <dyn Any>::downcast_ref::<char>(&value) {
|
|
||||||
return result.clone().into();
|
|
||||||
} else if let Some(result) = <dyn Any>::downcast_ref::<ImmutableString>(&value) {
|
|
||||||
return result.clone().into();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if type_id == TypeId::of::<INT>() {
|
||||||
|
return <dyn Any>::downcast_ref::<INT>(&value)
|
||||||
|
.unwrap()
|
||||||
|
.clone()
|
||||||
|
.into();
|
||||||
|
}
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
if let Some(result) = <dyn Any>::downcast_ref::<FLOAT>(&value) {
|
if type_id == TypeId::of::<FLOAT>() {
|
||||||
return result.clone().into();
|
return <dyn Any>::downcast_ref::<FLOAT>(&value)
|
||||||
|
.unwrap()
|
||||||
|
.clone()
|
||||||
|
.into();
|
||||||
|
}
|
||||||
|
if type_id == TypeId::of::<bool>() {
|
||||||
|
return <dyn Any>::downcast_ref::<bool>(&value)
|
||||||
|
.unwrap()
|
||||||
|
.clone()
|
||||||
|
.into();
|
||||||
|
}
|
||||||
|
if type_id == TypeId::of::<char>() {
|
||||||
|
return <dyn Any>::downcast_ref::<char>(&value)
|
||||||
|
.unwrap()
|
||||||
|
.clone()
|
||||||
|
.into();
|
||||||
|
}
|
||||||
|
if type_id == TypeId::of::<ImmutableString>() {
|
||||||
|
return <dyn Any>::downcast_ref::<ImmutableString>(&value)
|
||||||
|
.unwrap()
|
||||||
|
.clone()
|
||||||
|
.into();
|
||||||
|
}
|
||||||
|
if type_id == TypeId::of::<()>() {
|
||||||
|
return ().into();
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut boxed = Box::new(value);
|
let mut boxed = Box::new(value);
|
||||||
|
|
||||||
boxed = match unsafe_cast_box::<_, Dynamic>(boxed) {
|
|
||||||
Ok(d) => return *d,
|
|
||||||
Err(val) => val,
|
|
||||||
};
|
|
||||||
boxed = match unsafe_cast_box::<_, String>(boxed) {
|
boxed = match unsafe_cast_box::<_, String>(boxed) {
|
||||||
Ok(s) => return (*s).into(),
|
Ok(s) => return (*s).into(),
|
||||||
Err(val) => val,
|
Err(val) => val,
|
||||||
@ -378,6 +402,11 @@ impl Dynamic {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boxed = match unsafe_cast_box::<_, Dynamic>(boxed) {
|
||||||
|
Ok(d) => return *d,
|
||||||
|
Err(val) => val,
|
||||||
|
};
|
||||||
|
|
||||||
Self(Union::Variant(Box::new(boxed)))
|
Self(Union::Variant(Box::new(boxed)))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -395,30 +424,80 @@ impl Dynamic {
|
|||||||
///
|
///
|
||||||
/// assert_eq!(x.try_cast::<u32>().unwrap(), 42);
|
/// assert_eq!(x.try_cast::<u32>().unwrap(), 42);
|
||||||
/// ```
|
/// ```
|
||||||
|
#[inline(always)]
|
||||||
pub fn try_cast<T: Variant>(self) -> Option<T> {
|
pub fn try_cast<T: Variant>(self) -> Option<T> {
|
||||||
let type_id = TypeId::of::<T>();
|
let type_id = TypeId::of::<T>();
|
||||||
|
|
||||||
|
if type_id == TypeId::of::<INT>() {
|
||||||
|
return match self.0 {
|
||||||
|
Union::Int(value) => unsafe_try_cast(value),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
if type_id == TypeId::of::<FLOAT>() {
|
||||||
|
return match self.0 {
|
||||||
|
Union::Float(value) => unsafe_try_cast(value),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if type_id == TypeId::of::<bool>() {
|
||||||
|
return match self.0 {
|
||||||
|
Union::Bool(value) => unsafe_try_cast(value),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if type_id == TypeId::of::<ImmutableString>() {
|
||||||
|
return match self.0 {
|
||||||
|
Union::Str(value) => unsafe_try_cast(value),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if type_id == TypeId::of::<String>() {
|
||||||
|
return match self.0 {
|
||||||
|
Union::Str(value) => unsafe_try_cast(value.into_owned()),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if type_id == TypeId::of::<char>() {
|
||||||
|
return match self.0 {
|
||||||
|
Union::Char(value) => unsafe_try_cast(value),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
if type_id == TypeId::of::<Array>() {
|
||||||
|
return match self.0 {
|
||||||
|
Union::Array(value) => unsafe_cast_box::<_, T>(value).ok().map(|v| *v),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
if type_id == TypeId::of::<Map>() {
|
||||||
|
return match self.0 {
|
||||||
|
Union::Map(value) => unsafe_cast_box::<_, T>(value).ok().map(|v| *v),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if type_id == TypeId::of::<FnPtr>() {
|
||||||
|
return match self.0 {
|
||||||
|
Union::FnPtr(value) => unsafe_try_cast(value),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if type_id == TypeId::of::<()>() {
|
||||||
|
return match self.0 {
|
||||||
|
Union::Unit(value) => unsafe_try_cast(value),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
}
|
||||||
if type_id == TypeId::of::<Dynamic>() {
|
if type_id == TypeId::of::<Dynamic>() {
|
||||||
return unsafe_cast_box::<_, T>(Box::new(self)).ok().map(|v| *v);
|
return unsafe_cast_box::<_, T>(Box::new(self)).ok().map(|v| *v);
|
||||||
}
|
}
|
||||||
|
|
||||||
match self.0 {
|
match self.0 {
|
||||||
Union::Unit(value) => unsafe_try_cast(value),
|
|
||||||
Union::Bool(value) => unsafe_try_cast(value),
|
|
||||||
Union::Str(value) if type_id == TypeId::of::<ImmutableString>() => {
|
|
||||||
unsafe_try_cast(value)
|
|
||||||
}
|
|
||||||
Union::Str(value) => unsafe_try_cast(value.into_owned()),
|
|
||||||
Union::Char(value) => unsafe_try_cast(value),
|
|
||||||
Union::Int(value) => unsafe_try_cast(value),
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
|
||||||
Union::Float(value) => unsafe_try_cast(value),
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
Union::Array(value) => unsafe_cast_box::<_, T>(value).ok().map(|v| *v),
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
Union::Map(value) => unsafe_cast_box::<_, T>(value).ok().map(|v| *v),
|
|
||||||
Union::FnPtr(value) => unsafe_try_cast(value),
|
|
||||||
Union::Variant(value) => (*value).as_box_any().downcast().map(|x| *x).ok(),
|
Union::Variant(value) => (*value).as_box_any().downcast().map(|x| *x).ok(),
|
||||||
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -438,81 +517,162 @@ impl Dynamic {
|
|||||||
///
|
///
|
||||||
/// assert_eq!(x.cast::<u32>(), 42);
|
/// assert_eq!(x.cast::<u32>(), 42);
|
||||||
/// ```
|
/// ```
|
||||||
|
#[inline(always)]
|
||||||
pub fn cast<T: Variant + Clone>(self) -> T {
|
pub fn cast<T: Variant + Clone>(self) -> T {
|
||||||
let type_id = TypeId::of::<T>();
|
self.try_cast::<T>().unwrap()
|
||||||
|
|
||||||
if type_id == TypeId::of::<Dynamic>() {
|
|
||||||
return *unsafe_cast_box::<_, T>(Box::new(self)).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
match self.0 {
|
|
||||||
Union::Unit(value) => unsafe_try_cast(value).unwrap(),
|
|
||||||
Union::Bool(value) => unsafe_try_cast(value).unwrap(),
|
|
||||||
Union::Str(value) if type_id == TypeId::of::<ImmutableString>() => {
|
|
||||||
unsafe_try_cast(value).unwrap()
|
|
||||||
}
|
|
||||||
Union::Str(value) => unsafe_try_cast(value.into_owned()).unwrap(),
|
|
||||||
Union::Char(value) => unsafe_try_cast(value).unwrap(),
|
|
||||||
Union::Int(value) => unsafe_try_cast(value).unwrap(),
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
|
||||||
Union::Float(value) => unsafe_try_cast(value).unwrap(),
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
Union::Array(value) => *unsafe_cast_box::<_, T>(value).unwrap(),
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
Union::Map(value) => *unsafe_cast_box::<_, T>(value).unwrap(),
|
|
||||||
Union::FnPtr(value) => unsafe_try_cast(value).unwrap(),
|
|
||||||
Union::Variant(value) => (*value).as_box_any().downcast().map(|x| *x).unwrap(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a reference of a specific type to the `Dynamic`.
|
/// Get a reference of a specific type to the `Dynamic`.
|
||||||
/// Casting to `Dynamic` just returns a reference to it.
|
/// Casting to `Dynamic` just returns a reference to it.
|
||||||
/// Returns `None` if the cast fails.
|
/// Returns `None` if the cast fails.
|
||||||
|
#[inline(always)]
|
||||||
pub fn downcast_ref<T: Variant + Clone>(&self) -> Option<&T> {
|
pub fn downcast_ref<T: Variant + Clone>(&self) -> Option<&T> {
|
||||||
if TypeId::of::<T>() == TypeId::of::<Dynamic>() {
|
let type_id = TypeId::of::<T>();
|
||||||
|
|
||||||
|
if type_id == TypeId::of::<INT>() {
|
||||||
|
return match &self.0 {
|
||||||
|
Union::Int(value) => <dyn Any>::downcast_ref::<T>(value),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
if type_id == TypeId::of::<FLOAT>() {
|
||||||
|
return match &self.0 {
|
||||||
|
Union::Float(value) => <dyn Any>::downcast_ref::<T>(value),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if type_id == TypeId::of::<bool>() {
|
||||||
|
return match &self.0 {
|
||||||
|
Union::Bool(value) => <dyn Any>::downcast_ref::<T>(value),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if type_id == TypeId::of::<ImmutableString>() {
|
||||||
|
return match &self.0 {
|
||||||
|
Union::Str(value) => <dyn Any>::downcast_ref::<T>(value),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if type_id == TypeId::of::<String>() {
|
||||||
|
return match &self.0 {
|
||||||
|
Union::Str(value) => <dyn Any>::downcast_ref::<T>(value.as_ref()),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if type_id == TypeId::of::<char>() {
|
||||||
|
return match &self.0 {
|
||||||
|
Union::Char(value) => <dyn Any>::downcast_ref::<T>(value),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
if type_id == TypeId::of::<Array>() {
|
||||||
|
return match &self.0 {
|
||||||
|
Union::Array(value) => <dyn Any>::downcast_ref::<T>(value.as_ref()),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
if type_id == TypeId::of::<Map>() {
|
||||||
|
return match &self.0 {
|
||||||
|
Union::Map(value) => <dyn Any>::downcast_ref::<T>(value.as_ref()),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if type_id == TypeId::of::<FnPtr>() {
|
||||||
|
return match &self.0 {
|
||||||
|
Union::FnPtr(value) => <dyn Any>::downcast_ref::<T>(value),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if type_id == TypeId::of::<()>() {
|
||||||
|
return match &self.0 {
|
||||||
|
Union::Unit(value) => <dyn Any>::downcast_ref::<T>(value),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if type_id == TypeId::of::<Dynamic>() {
|
||||||
return <dyn Any>::downcast_ref::<T>(self);
|
return <dyn Any>::downcast_ref::<T>(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
match &self.0 {
|
match &self.0 {
|
||||||
Union::Unit(value) => <dyn Any>::downcast_ref::<T>(value),
|
|
||||||
Union::Bool(value) => <dyn Any>::downcast_ref::<T>(value),
|
|
||||||
Union::Str(value) => <dyn Any>::downcast_ref::<T>(value)
|
|
||||||
.or_else(|| <dyn Any>::downcast_ref::<T>(value.as_ref())),
|
|
||||||
Union::Char(value) => <dyn Any>::downcast_ref::<T>(value),
|
|
||||||
Union::Int(value) => <dyn Any>::downcast_ref::<T>(value),
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
|
||||||
Union::Float(value) => <dyn Any>::downcast_ref::<T>(value),
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
Union::Array(value) => <dyn Any>::downcast_ref::<T>(value.as_ref()),
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
Union::Map(value) => <dyn Any>::downcast_ref::<T>(value.as_ref()),
|
|
||||||
Union::FnPtr(value) => <dyn Any>::downcast_ref::<T>(value),
|
|
||||||
Union::Variant(value) => value.as_ref().as_ref().as_any().downcast_ref::<T>(),
|
Union::Variant(value) => value.as_ref().as_ref().as_any().downcast_ref::<T>(),
|
||||||
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a mutable reference of a specific type to the `Dynamic`.
|
/// Get a mutable reference of a specific type to the `Dynamic`.
|
||||||
/// Casting to `Dynamic` just returns a mutable reference to it.
|
/// Casting to `Dynamic` just returns a mutable reference to it.
|
||||||
/// Returns `None` if the cast fails.
|
/// Returns `None` if the cast fails.
|
||||||
|
#[inline(always)]
|
||||||
pub fn downcast_mut<T: Variant + Clone>(&mut self) -> Option<&mut T> {
|
pub fn downcast_mut<T: Variant + Clone>(&mut self) -> Option<&mut T> {
|
||||||
if TypeId::of::<T>() == TypeId::of::<Dynamic>() {
|
let type_id = TypeId::of::<T>();
|
||||||
|
|
||||||
|
if type_id == TypeId::of::<INT>() {
|
||||||
|
return match &mut self.0 {
|
||||||
|
Union::Int(value) => <dyn Any>::downcast_mut::<T>(value),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
if type_id == TypeId::of::<FLOAT>() {
|
||||||
|
return match &mut self.0 {
|
||||||
|
Union::Float(value) => <dyn Any>::downcast_mut::<T>(value),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if type_id == TypeId::of::<bool>() {
|
||||||
|
return match &mut self.0 {
|
||||||
|
Union::Bool(value) => <dyn Any>::downcast_mut::<T>(value),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if type_id == TypeId::of::<ImmutableString>() {
|
||||||
|
return match &mut self.0 {
|
||||||
|
Union::Str(value) => <dyn Any>::downcast_mut::<T>(value),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if type_id == TypeId::of::<char>() {
|
||||||
|
return match &mut self.0 {
|
||||||
|
Union::Char(value) => <dyn Any>::downcast_mut::<T>(value),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
if type_id == TypeId::of::<Array>() {
|
||||||
|
return match &mut self.0 {
|
||||||
|
Union::Array(value) => <dyn Any>::downcast_mut::<T>(value.as_mut()),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
if type_id == TypeId::of::<Map>() {
|
||||||
|
return match &mut self.0 {
|
||||||
|
Union::Map(value) => <dyn Any>::downcast_mut::<T>(value.as_mut()),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if type_id == TypeId::of::<FnPtr>() {
|
||||||
|
return match &mut self.0 {
|
||||||
|
Union::FnPtr(value) => <dyn Any>::downcast_mut::<T>(value),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if type_id == TypeId::of::<()>() {
|
||||||
|
return match &mut self.0 {
|
||||||
|
Union::Unit(value) => <dyn Any>::downcast_mut::<T>(value),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if type_id == TypeId::of::<Dynamic>() {
|
||||||
return <dyn Any>::downcast_mut::<T>(self);
|
return <dyn Any>::downcast_mut::<T>(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
match &mut self.0 {
|
match &mut self.0 {
|
||||||
Union::Unit(value) => <dyn Any>::downcast_mut::<T>(value),
|
|
||||||
Union::Bool(value) => <dyn Any>::downcast_mut::<T>(value),
|
|
||||||
Union::Str(value) => <dyn Any>::downcast_mut::<T>(value),
|
|
||||||
Union::Char(value) => <dyn Any>::downcast_mut::<T>(value),
|
|
||||||
Union::Int(value) => <dyn Any>::downcast_mut::<T>(value),
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
|
||||||
Union::Float(value) => <dyn Any>::downcast_mut::<T>(value),
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
Union::Array(value) => <dyn Any>::downcast_mut::<T>(value.as_mut()),
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
Union::Map(value) => <dyn Any>::downcast_mut::<T>(value.as_mut()),
|
|
||||||
Union::FnPtr(value) => <dyn Any>::downcast_mut::<T>(value),
|
|
||||||
Union::Variant(value) => value.as_mut().as_mut_any().downcast_mut::<T>(),
|
Union::Variant(value) => value.as_mut().as_mut_any().downcast_mut::<T>(),
|
||||||
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
113
src/engine.rs
113
src/engine.rs
@ -18,7 +18,7 @@ use crate::utils::StaticVec;
|
|||||||
use crate::parser::FLOAT;
|
use crate::parser::FLOAT;
|
||||||
|
|
||||||
#[cfg(feature = "internals")]
|
#[cfg(feature = "internals")]
|
||||||
use crate::syntax::CustomSyntax;
|
use crate::syntax::{CustomSyntax, EvalContext};
|
||||||
|
|
||||||
use crate::stdlib::{
|
use crate::stdlib::{
|
||||||
any::{type_name, TypeId},
|
any::{type_name, TypeId},
|
||||||
@ -87,6 +87,7 @@ pub const FN_GET: &str = "get$";
|
|||||||
pub const FN_SET: &str = "set$";
|
pub const FN_SET: &str = "set$";
|
||||||
pub const FN_IDX_GET: &str = "index$get$";
|
pub const FN_IDX_GET: &str = "index$get$";
|
||||||
pub const FN_IDX_SET: &str = "index$set$";
|
pub const FN_IDX_SET: &str = "index$set$";
|
||||||
|
pub const FN_ANONYMOUS: &str = "anon$";
|
||||||
|
|
||||||
#[cfg(feature = "internals")]
|
#[cfg(feature = "internals")]
|
||||||
pub const MARKER_EXPR: &str = "$expr$";
|
pub const MARKER_EXPR: &str = "$expr$";
|
||||||
@ -187,7 +188,7 @@ impl Target<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Update the value of the `Target`.
|
/// Update the value of the `Target`.
|
||||||
/// Position in `EvalAltResult` is None and must be set afterwards.
|
/// Position in `EvalAltResult` is `None` and must be set afterwards.
|
||||||
pub fn set_value(&mut self, new_val: Dynamic) -> Result<(), Box<EvalAltResult>> {
|
pub fn set_value(&mut self, new_val: Dynamic) -> Result<(), Box<EvalAltResult>> {
|
||||||
match self {
|
match self {
|
||||||
Self::Ref(r) => **r = new_val,
|
Self::Ref(r) => **r = new_val,
|
||||||
@ -418,12 +419,9 @@ pub fn make_getter(id: &str) -> String {
|
|||||||
fn extract_prop_from_getter(fn_name: &str) -> Option<&str> {
|
fn extract_prop_from_getter(fn_name: &str) -> Option<&str> {
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
if fn_name.starts_with(FN_GET) {
|
if fn_name.starts_with(FN_GET) {
|
||||||
Some(&fn_name[FN_GET.len()..])
|
return Some(&fn_name[FN_GET.len()..]);
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "no_object")]
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -436,12 +434,9 @@ pub fn make_setter(id: &str) -> String {
|
|||||||
fn extract_prop_from_setter(fn_name: &str) -> Option<&str> {
|
fn extract_prop_from_setter(fn_name: &str) -> Option<&str> {
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
if fn_name.starts_with(FN_SET) {
|
if fn_name.starts_with(FN_SET) {
|
||||||
Some(&fn_name[FN_SET.len()..])
|
return Some(&fn_name[FN_SET.len()..]);
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "no_object")]
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -453,7 +448,7 @@ fn default_print(s: &str) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Search for a module within an imports stack.
|
/// Search for a module within an imports stack.
|
||||||
/// Position in `EvalAltResult` is None and must be set afterwards.
|
/// Position in `EvalAltResult` is `None` and must be set afterwards.
|
||||||
fn search_imports<'s>(
|
fn search_imports<'s>(
|
||||||
mods: &'s Imports,
|
mods: &'s Imports,
|
||||||
state: &mut State,
|
state: &mut State,
|
||||||
@ -486,7 +481,7 @@ fn search_imports<'s>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Search for a module within an imports stack.
|
/// Search for a module within an imports stack.
|
||||||
/// Position in `EvalAltResult` is None and must be set afterwards.
|
/// Position in `EvalAltResult` is `None` and must be set afterwards.
|
||||||
fn search_imports_mut<'s>(
|
fn search_imports_mut<'s>(
|
||||||
mods: &'s mut Imports,
|
mods: &'s mut Imports,
|
||||||
state: &mut State,
|
state: &mut State,
|
||||||
@ -636,7 +631,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Universal method for calling functions either registered with the `Engine` or written in Rhai.
|
/// Universal method for calling functions either registered with the `Engine` or written in Rhai.
|
||||||
/// Position in `EvalAltResult` is None and must be set afterwards.
|
/// Position in `EvalAltResult` is `None` and must be set afterwards.
|
||||||
///
|
///
|
||||||
/// ## WARNING
|
/// ## WARNING
|
||||||
///
|
///
|
||||||
@ -654,7 +649,7 @@ impl Engine {
|
|||||||
args: &mut FnCallArgs,
|
args: &mut FnCallArgs,
|
||||||
is_ref: bool,
|
is_ref: bool,
|
||||||
is_method: bool,
|
is_method: bool,
|
||||||
def_val: Option<&Dynamic>,
|
def_val: Option<bool>,
|
||||||
level: usize,
|
level: usize,
|
||||||
) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
|
) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
|
||||||
self.inc_operations(state)?;
|
self.inc_operations(state)?;
|
||||||
@ -709,7 +704,7 @@ impl Engine {
|
|||||||
/// This function restores the first argument that was replaced by `normalize_first_arg_of_method_call`.
|
/// This function restores the first argument that was replaced by `normalize_first_arg_of_method_call`.
|
||||||
fn restore_first_arg<'a>(old_this_ptr: Option<&'a mut Dynamic>, args: &mut FnCallArgs<'a>) {
|
fn restore_first_arg<'a>(old_this_ptr: Option<&'a mut Dynamic>, args: &mut FnCallArgs<'a>) {
|
||||||
if let Some(this_pointer) = old_this_ptr {
|
if let Some(this_pointer) = old_this_ptr {
|
||||||
mem::replace(args.get_mut(0).unwrap(), this_pointer);
|
args[0] = this_pointer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -815,7 +810,7 @@ impl Engine {
|
|||||||
|
|
||||||
// Return default value (if any)
|
// Return default value (if any)
|
||||||
if let Some(val) = def_val {
|
if let Some(val) = def_val {
|
||||||
return Ok((val.clone(), false));
|
return Ok((val.into(), false));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Getter function not found?
|
// Getter function not found?
|
||||||
@ -877,7 +872,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Call a script-defined function.
|
/// Call a script-defined function.
|
||||||
/// Position in `EvalAltResult` is None and must be set afterwards.
|
/// Position in `EvalAltResult` is `None` and must be set afterwards.
|
||||||
///
|
///
|
||||||
/// ## WARNING
|
/// ## WARNING
|
||||||
///
|
///
|
||||||
@ -959,7 +954,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Perform an actual function call, taking care of special functions
|
/// Perform an actual function call, taking care of special functions
|
||||||
/// Position in `EvalAltResult` is None and must be set afterwards.
|
/// Position in `EvalAltResult` is `None` and must be set afterwards.
|
||||||
///
|
///
|
||||||
/// ## WARNING
|
/// ## WARNING
|
||||||
///
|
///
|
||||||
@ -976,7 +971,7 @@ impl Engine {
|
|||||||
args: &mut FnCallArgs,
|
args: &mut FnCallArgs,
|
||||||
is_ref: bool,
|
is_ref: bool,
|
||||||
is_method: bool,
|
is_method: bool,
|
||||||
def_val: Option<&Dynamic>,
|
def_val: Option<bool>,
|
||||||
level: usize,
|
level: usize,
|
||||||
) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
|
) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
|
||||||
// Qualifiers (none) + function name + number of arguments + argument `TypeId`'s.
|
// Qualifiers (none) + function name + number of arguments + argument `TypeId`'s.
|
||||||
@ -1022,7 +1017,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Evaluate a text string as a script - used primarily for 'eval'.
|
/// Evaluate a text string as a script - used primarily for 'eval'.
|
||||||
/// Position in `EvalAltResult` is None and must be set afterwards.
|
/// Position in `EvalAltResult` is `None` and must be set afterwards.
|
||||||
fn eval_script_expr(
|
fn eval_script_expr(
|
||||||
&self,
|
&self,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
@ -1081,7 +1076,6 @@ impl Engine {
|
|||||||
|
|
||||||
let is_ref = target.is_ref();
|
let is_ref = target.is_ref();
|
||||||
let is_value = target.is_value();
|
let is_value = target.is_value();
|
||||||
let def_val = def_val.as_ref();
|
|
||||||
|
|
||||||
// Get a reference to the mutation target Dynamic
|
// Get a reference to the mutation target Dynamic
|
||||||
let obj = target.as_mut();
|
let obj = target.as_mut();
|
||||||
@ -1100,7 +1094,7 @@ impl Engine {
|
|||||||
|
|
||||||
// Map it to name(args) in function-call style
|
// Map it to name(args) in function-call style
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
state, lib, fn_name, *native, hash, args, false, false, def_val, level,
|
state, lib, fn_name, *native, hash, args, false, false, *def_val, level,
|
||||||
)
|
)
|
||||||
} else if fn_name == KEYWORD_FN_PTR_CALL && idx.len() > 0 && idx[0].is::<FnPtr>() {
|
} else if fn_name == KEYWORD_FN_PTR_CALL && idx.len() > 0 && idx[0].is::<FnPtr>() {
|
||||||
// FnPtr call on object
|
// FnPtr call on object
|
||||||
@ -1120,7 +1114,7 @@ impl Engine {
|
|||||||
|
|
||||||
// Map it to name(args) in function-call style
|
// Map it to name(args) in function-call style
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
state, lib, &fn_name, *native, hash, args, is_ref, true, def_val, level,
|
state, lib, &fn_name, *native, hash, args, is_ref, true, *def_val, level,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
let redirected: Option<ImmutableString>;
|
let redirected: Option<ImmutableString>;
|
||||||
@ -1146,7 +1140,7 @@ impl Engine {
|
|||||||
let args = arg_values.as_mut();
|
let args = arg_values.as_mut();
|
||||||
|
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
state, lib, fn_name, *native, hash, args, is_ref, true, def_val, level,
|
state, lib, fn_name, *native, hash, args, is_ref, true, *def_val, level,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
.map_err(|err| err.new_position(*pos))?;
|
.map_err(|err| err.new_position(*pos))?;
|
||||||
@ -1161,7 +1155,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Chain-evaluate a dot/index chain.
|
/// Chain-evaluate a dot/index chain.
|
||||||
/// Position in `EvalAltResult` is None and must be set afterwards.
|
/// Position in `EvalAltResult` is `None` and must be set afterwards.
|
||||||
fn eval_dot_index_chain_helper(
|
fn eval_dot_index_chain_helper(
|
||||||
&self,
|
&self,
|
||||||
state: &mut State,
|
state: &mut State,
|
||||||
@ -1210,14 +1204,14 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
// xxx[rhs] = new_val
|
// xxx[rhs] = new_val
|
||||||
_ if new_val.is_some() => {
|
_ if new_val.is_some() => {
|
||||||
|
let mut new_val = new_val.unwrap();
|
||||||
let mut idx_val2 = idx_val.clone();
|
let mut idx_val2 = idx_val.clone();
|
||||||
|
|
||||||
match self.get_indexed_mut(state, lib, target, idx_val, pos, true, level) {
|
match self.get_indexed_mut(state, lib, target, idx_val, pos, true, level) {
|
||||||
// Indexed value is an owned value - the only possibility is an indexer
|
// Indexed value is an owned value - the only possibility is an indexer
|
||||||
// Try to call an index setter
|
// Try to call an index setter
|
||||||
Ok(obj_ptr) if obj_ptr.is_value() => {
|
Ok(obj_ptr) if obj_ptr.is_value() => {
|
||||||
let args =
|
let args = &mut [target.as_mut(), &mut idx_val2, &mut new_val];
|
||||||
&mut [target.as_mut(), &mut idx_val2, &mut new_val.unwrap()];
|
|
||||||
|
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
state, lib, FN_IDX_SET, true, 0, args, is_ref, true, None,
|
state, lib, FN_IDX_SET, true, 0, args, is_ref, true, None,
|
||||||
@ -1236,17 +1230,13 @@ impl Engine {
|
|||||||
// Indexed value is a reference - update directly
|
// Indexed value is a reference - update directly
|
||||||
Ok(ref mut obj_ptr) => {
|
Ok(ref mut obj_ptr) => {
|
||||||
obj_ptr
|
obj_ptr
|
||||||
.set_value(new_val.unwrap())
|
.set_value(new_val)
|
||||||
.map_err(|err| err.new_position(rhs.position()))?;
|
.map_err(|err| err.new_position(rhs.position()))?;
|
||||||
}
|
}
|
||||||
Err(err) => match *err {
|
Err(err) => match *err {
|
||||||
// No index getter - try to call an index setter
|
// No index getter - try to call an index setter
|
||||||
EvalAltResult::ErrorIndexingType(_, _) => {
|
EvalAltResult::ErrorIndexingType(_, _) => {
|
||||||
let args = &mut [
|
let args = &mut [target.as_mut(), &mut idx_val2, &mut new_val];
|
||||||
target.as_mut(),
|
|
||||||
&mut idx_val2,
|
|
||||||
&mut new_val.unwrap(),
|
|
||||||
];
|
|
||||||
|
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
state, lib, FN_IDX_SET, true, 0, args, is_ref, true, None,
|
state, lib, FN_IDX_SET, true, 0, args, is_ref, true, None,
|
||||||
@ -1694,13 +1684,12 @@ impl Engine {
|
|||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Dynamic(Union::Array(mut rhs_value)) => {
|
Dynamic(Union::Array(mut rhs_value)) => {
|
||||||
let op = "==";
|
let op = "==";
|
||||||
let def_value = false.into();
|
|
||||||
let mut scope = Scope::new();
|
let mut scope = Scope::new();
|
||||||
|
|
||||||
// Call the `==` operator to compare each value
|
// Call the `==` operator to compare each value
|
||||||
for value in rhs_value.iter_mut() {
|
for value in rhs_value.iter_mut() {
|
||||||
|
let def_value = Some(false);
|
||||||
let args = &mut [&mut lhs_value.clone(), value];
|
let args = &mut [&mut lhs_value.clone(), value];
|
||||||
let def_value = Some(&def_value);
|
|
||||||
|
|
||||||
let hashes = (
|
let hashes = (
|
||||||
// Qualifiers (none) + function name + number of arguments + argument `TypeId`'s.
|
// Qualifiers (none) + function name + number of arguments + argument `TypeId`'s.
|
||||||
@ -1749,15 +1738,19 @@ impl Engine {
|
|||||||
#[deprecated(note = "this method is volatile and may change")]
|
#[deprecated(note = "this method is volatile and may change")]
|
||||||
pub fn eval_expression_tree(
|
pub fn eval_expression_tree(
|
||||||
&self,
|
&self,
|
||||||
|
context: &mut EvalContext,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
mods: &mut Imports,
|
|
||||||
state: &mut State,
|
|
||||||
lib: &Module,
|
|
||||||
this_ptr: &mut Option<&mut Dynamic>,
|
|
||||||
expr: &Expression,
|
expr: &Expression,
|
||||||
level: usize,
|
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
self.eval_expr(scope, mods, state, lib, this_ptr, expr.expr(), level)
|
self.eval_expr(
|
||||||
|
scope,
|
||||||
|
context.mods,
|
||||||
|
context.state,
|
||||||
|
context.lib,
|
||||||
|
context.this_ptr,
|
||||||
|
expr.expr(),
|
||||||
|
context.level,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Evaluate an expression
|
/// Evaluate an expression
|
||||||
@ -1782,9 +1775,10 @@ impl Engine {
|
|||||||
Expr::FloatConstant(x) => Ok(x.0.into()),
|
Expr::FloatConstant(x) => Ok(x.0.into()),
|
||||||
Expr::StringConstant(x) => Ok(x.0.to_string().into()),
|
Expr::StringConstant(x) => Ok(x.0.to_string().into()),
|
||||||
Expr::CharConstant(x) => Ok(x.0.into()),
|
Expr::CharConstant(x) => Ok(x.0.into()),
|
||||||
|
Expr::FnPointer(x) => Ok(FnPtr::new_unchecked(x.0.clone()).into()),
|
||||||
Expr::Variable(x) if (x.0).0 == KEYWORD_THIS => {
|
Expr::Variable(x) if (x.0).0 == KEYWORD_THIS => {
|
||||||
if let Some(ref val) = this_ptr {
|
if let Some(val) = this_ptr {
|
||||||
Ok((*val).clone())
|
Ok(val.clone())
|
||||||
} else {
|
} else {
|
||||||
Err(Box::new(EvalAltResult::ErrorUnboundedThis((x.0).1)))
|
Err(Box::new(EvalAltResult::ErrorUnboundedThis((x.0).1)))
|
||||||
}
|
}
|
||||||
@ -1839,15 +1833,16 @@ impl Engine {
|
|||||||
// Not built in, map to `var = var op rhs`
|
// Not built in, map to `var = var op rhs`
|
||||||
let op = &op[..op.len() - 1]; // extract operator without =
|
let op = &op[..op.len() - 1]; // extract operator without =
|
||||||
let hash = calc_fn_hash(empty(), op, 2, empty());
|
let hash = calc_fn_hash(empty(), op, 2, empty());
|
||||||
|
// Clone the LHS value
|
||||||
let args = &mut [&mut lhs_ptr.clone(), &mut rhs_val];
|
let args = &mut [&mut lhs_ptr.clone(), &mut rhs_val];
|
||||||
|
// Run function
|
||||||
// Set variable value
|
let (value, _) = self
|
||||||
*lhs_ptr = self
|
|
||||||
.exec_fn_call(
|
.exec_fn_call(
|
||||||
state, lib, op, true, hash, args, false, false, None, level,
|
state, lib, op, true, hash, args, false, false, None, level,
|
||||||
)
|
)
|
||||||
.map(|(v, _)| v)
|
|
||||||
.map_err(|err| err.new_position(*op_pos))?;
|
.map_err(|err| err.new_position(*op_pos))?;
|
||||||
|
// Set value to LHS
|
||||||
|
*lhs_ptr = value;
|
||||||
}
|
}
|
||||||
Ok(Default::default())
|
Ok(Default::default())
|
||||||
}
|
}
|
||||||
@ -1941,7 +1936,6 @@ impl Engine {
|
|||||||
// Normal function call
|
// Normal function call
|
||||||
Expr::FnCall(x) if x.1.is_none() => {
|
Expr::FnCall(x) if x.1.is_none() => {
|
||||||
let ((name, native, pos), _, hash, args_expr, def_val) = x.as_ref();
|
let ((name, native, pos), _, hash, args_expr, def_val) = x.as_ref();
|
||||||
let def_val = def_val.as_ref();
|
|
||||||
|
|
||||||
// Handle Fn()
|
// Handle Fn()
|
||||||
if name == KEYWORD_FN_PTR && args_expr.len() == 1 {
|
if name == KEYWORD_FN_PTR && args_expr.len() == 1 {
|
||||||
@ -2072,7 +2066,7 @@ impl Engine {
|
|||||||
|
|
||||||
let args = args.as_mut();
|
let args = args.as_mut();
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
state, lib, name, *native, hash, args, is_ref, false, def_val, level,
|
state, lib, name, *native, hash, args, is_ref, false, *def_val, level,
|
||||||
)
|
)
|
||||||
.map(|(v, _)| v)
|
.map(|(v, _)| v)
|
||||||
.map_err(|err| err.new_position(*pos))
|
.map_err(|err| err.new_position(*pos))
|
||||||
@ -2166,7 +2160,7 @@ impl Engine {
|
|||||||
.map_err(|err| err.new_position(*pos)),
|
.map_err(|err| err.new_position(*pos)),
|
||||||
Err(err) => match *err {
|
Err(err) => match *err {
|
||||||
EvalAltResult::ErrorFunctionNotFound(_, _) if def_val.is_some() => {
|
EvalAltResult::ErrorFunctionNotFound(_, _) if def_val.is_some() => {
|
||||||
Ok(def_val.clone().unwrap())
|
Ok(def_val.unwrap().into())
|
||||||
}
|
}
|
||||||
EvalAltResult::ErrorFunctionNotFound(_, _) => {
|
EvalAltResult::ErrorFunctionNotFound(_, _) => {
|
||||||
Err(Box::new(EvalAltResult::ErrorFunctionNotFound(
|
Err(Box::new(EvalAltResult::ErrorFunctionNotFound(
|
||||||
@ -2225,7 +2219,14 @@ impl Engine {
|
|||||||
Expr::Custom(x) => {
|
Expr::Custom(x) => {
|
||||||
let func = (x.0).1.as_ref();
|
let func = (x.0).1.as_ref();
|
||||||
let ep: StaticVec<_> = (x.0).0.iter().map(|e| e.into()).collect();
|
let ep: StaticVec<_> = (x.0).0.iter().map(|e| e.into()).collect();
|
||||||
func(self, scope, mods, state, lib, this_ptr, ep.as_ref(), level)
|
let mut context = EvalContext {
|
||||||
|
mods,
|
||||||
|
state,
|
||||||
|
lib,
|
||||||
|
this_ptr,
|
||||||
|
level,
|
||||||
|
};
|
||||||
|
func(self, &mut context, scope, ep.as_ref())
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
@ -2502,12 +2503,8 @@ impl Engine {
|
|||||||
for ((id, id_pos), rename) in list.iter() {
|
for ((id, id_pos), rename) in list.iter() {
|
||||||
// Mark scope variables as public
|
// Mark scope variables as public
|
||||||
if let Some(index) = scope.get_index(id).map(|(i, _)| i) {
|
if let Some(index) = scope.get_index(id).map(|(i, _)| i) {
|
||||||
let alias = rename
|
let alias = rename.as_ref().map(|(n, _)| n).unwrap_or_else(|| id);
|
||||||
.as_ref()
|
scope.set_entry_alias(index, alias.clone());
|
||||||
.map(|(n, _)| n.clone())
|
|
||||||
.unwrap_or_else(|| id.clone());
|
|
||||||
|
|
||||||
scope.set_entry_alias(index, alias);
|
|
||||||
} else {
|
} else {
|
||||||
return Err(Box::new(EvalAltResult::ErrorVariableNotFound(
|
return Err(Box::new(EvalAltResult::ErrorVariableNotFound(
|
||||||
id.into(),
|
id.into(),
|
||||||
@ -2633,7 +2630,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Check if the number of operations stay within limit.
|
/// Check if the number of operations stay within limit.
|
||||||
/// Position in `EvalAltResult` is None and must be set afterwards.
|
/// Position in `EvalAltResult` is `None` and must be set afterwards.
|
||||||
fn inc_operations(&self, state: &mut State) -> Result<(), Box<EvalAltResult>> {
|
fn inc_operations(&self, state: &mut State) -> Result<(), Box<EvalAltResult>> {
|
||||||
state.operations += 1;
|
state.operations += 1;
|
||||||
|
|
||||||
|
@ -206,6 +206,9 @@ impl fmt::Display for ParseErrorType {
|
|||||||
|
|
||||||
Self::FnMissingParams(s) => write!(f, "Expecting parameters for function '{}'", s),
|
Self::FnMissingParams(s) => write!(f, "Expecting parameters for function '{}'", s),
|
||||||
|
|
||||||
|
Self::FnMissingBody(s) if s.is_empty() => {
|
||||||
|
f.write_str("Expecting body statement block for anonymous function")
|
||||||
|
}
|
||||||
Self::FnMissingBody(s) => {
|
Self::FnMissingBody(s) => {
|
||||||
write!(f, "Expecting body statement block for function '{}'", s)
|
write!(f, "Expecting body statement block for function '{}'", s)
|
||||||
}
|
}
|
||||||
|
@ -171,6 +171,10 @@ pub use parser::{CustomExpr, Expr, ReturnType, ScriptFnDef, Stmt};
|
|||||||
#[deprecated(note = "this type is volatile and may change")]
|
#[deprecated(note = "this type is volatile and may change")]
|
||||||
pub use engine::{Expression, Imports, State as EvalState};
|
pub use engine::{Expression, Imports, State as EvalState};
|
||||||
|
|
||||||
|
#[cfg(feature = "internals")]
|
||||||
|
#[deprecated(note = "this type is volatile and may change")]
|
||||||
|
pub use syntax::EvalContext;
|
||||||
|
|
||||||
#[cfg(feature = "internals")]
|
#[cfg(feature = "internals")]
|
||||||
#[deprecated(note = "this type is volatile and may change")]
|
#[deprecated(note = "this type is volatile and may change")]
|
||||||
pub use module::ModuleRef;
|
pub use module::ModuleRef;
|
||||||
|
@ -30,6 +30,7 @@ use crate::stdlib::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_std"))]
|
#[cfg(not(feature = "no_std"))]
|
||||||
|
#[cfg(feature = "sync")]
|
||||||
use crate::stdlib::sync::RwLock;
|
use crate::stdlib::sync::RwLock;
|
||||||
|
|
||||||
/// Return type of module-level Rust function.
|
/// Return type of module-level Rust function.
|
||||||
@ -1098,7 +1099,7 @@ impl Module {
|
|||||||
///
|
///
|
||||||
/// A `StaticVec` is used because most module-level access contains only one level,
|
/// A `StaticVec` is used because most module-level access contains only one level,
|
||||||
/// and it is wasteful to always allocate a `Vec` with one element.
|
/// and it is wasteful to always allocate a `Vec` with one element.
|
||||||
#[derive(Clone, Eq, PartialEq, Default)]
|
#[derive(Clone, Eq, PartialEq, Default, Hash)]
|
||||||
pub struct ModuleRef(StaticVec<(String, Position)>, Option<NonZeroUsize>);
|
pub struct ModuleRef(StaticVec<(String, Position)>, Option<NonZeroUsize>);
|
||||||
|
|
||||||
impl fmt::Debug for ModuleRef {
|
impl fmt::Debug for ModuleRef {
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
use crate::any::Dynamic;
|
use crate::any::Dynamic;
|
||||||
use crate::calc_fn_hash;
|
use crate::calc_fn_hash;
|
||||||
use crate::engine::{Engine, Imports, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_PRINT, KEYWORD_TYPE_OF};
|
use crate::engine::{
|
||||||
|
Engine, Imports, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_PRINT, KEYWORD_TYPE_OF,
|
||||||
|
};
|
||||||
use crate::module::Module;
|
use crate::module::Module;
|
||||||
use crate::parser::{map_dynamic_to_expr, Expr, ReturnType, ScriptFnDef, Stmt, AST};
|
use crate::parser::{map_dynamic_to_expr, Expr, ReturnType, ScriptFnDef, Stmt, AST};
|
||||||
use crate::scope::{Entry as ScopeEntry, EntryType as ScopeEntryType, Scope};
|
use crate::scope::{Entry as ScopeEntry, EntryType as ScopeEntryType, Scope};
|
||||||
|
use crate::token::is_valid_identifier;
|
||||||
use crate::utils::StaticVec;
|
use crate::utils::StaticVec;
|
||||||
|
|
||||||
#[cfg(feature = "internals")]
|
#[cfg(feature = "internals")]
|
||||||
@ -397,7 +400,7 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr {
|
|||||||
// All other items can be thrown away.
|
// All other items can be thrown away.
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
let pos = m.1;
|
let pos = m.1;
|
||||||
m.0.into_iter().find(|((name, _), _)| name.as_str() == prop)
|
m.0.into_iter().find(|((name, _), _)| name.as_str() == prop.as_str())
|
||||||
.map(|(_, expr)| expr.set_position(pos))
|
.map(|(_, expr)| expr.set_position(pos))
|
||||||
.unwrap_or_else(|| Expr::Unit(pos))
|
.unwrap_or_else(|| Expr::Unit(pos))
|
||||||
}
|
}
|
||||||
@ -431,7 +434,7 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr {
|
|||||||
(Expr::StringConstant(s), Expr::IntegerConstant(i)) if i.0 >= 0 && (i.0 as usize) < s.0.chars().count() => {
|
(Expr::StringConstant(s), Expr::IntegerConstant(i)) if i.0 >= 0 && (i.0 as usize) < s.0.chars().count() => {
|
||||||
// String literal indexing - get the character
|
// String literal indexing - get the character
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
Expr::CharConstant(Box::new((s.0.chars().nth(i.0 as usize).expect("should get char"), s.1)))
|
Expr::CharConstant(Box::new((s.0.chars().nth(i.0 as usize).unwrap(), s.1)))
|
||||||
}
|
}
|
||||||
// lhs[rhs]
|
// lhs[rhs]
|
||||||
(lhs, rhs) => Expr::Index(Box::new((optimize_expr(lhs, state), optimize_expr(rhs, state), x.2))),
|
(lhs, rhs) => Expr::Index(Box::new((optimize_expr(lhs, state), optimize_expr(rhs, state), x.2))),
|
||||||
@ -528,6 +531,19 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr {
|
|||||||
Expr::FnCall(x)
|
Expr::FnCall(x)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fn("...")
|
||||||
|
Expr::FnCall(x)
|
||||||
|
if x.1.is_none()
|
||||||
|
&& (x.0).0 == KEYWORD_FN_PTR
|
||||||
|
&& x.3.len() == 1
|
||||||
|
&& matches!(x.3[0], Expr::StringConstant(_))
|
||||||
|
=> {
|
||||||
|
match &x.3[0] {
|
||||||
|
Expr::StringConstant(s) if is_valid_identifier(s.0.chars()) => Expr::FnPointer(s.clone()),
|
||||||
|
_ => Expr::FnCall(x)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Eagerly call functions
|
// Eagerly call functions
|
||||||
Expr::FnCall(mut x)
|
Expr::FnCall(mut x)
|
||||||
if x.1.is_none() // Non-qualified
|
if x.1.is_none() // Non-qualified
|
||||||
@ -571,7 +587,7 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr {
|
|||||||
Some(arg_for_type_of.to_string().into())
|
Some(arg_for_type_of.to_string().into())
|
||||||
} else {
|
} else {
|
||||||
// Otherwise use the default value, if any
|
// Otherwise use the default value, if any
|
||||||
def_value.clone()
|
def_value.map(|v| v.into())
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.and_then(|result| map_dynamic_to_expr(result, *pos))
|
.and_then(|result| map_dynamic_to_expr(result, *pos))
|
||||||
@ -598,7 +614,7 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr {
|
|||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
|
|
||||||
// Replace constant with value
|
// Replace constant with value
|
||||||
state.find_constant(&name).expect("should find constant in scope!").clone().set_position(pos)
|
state.find_constant(&name).unwrap().clone().set_position(pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Custom syntax
|
// Custom syntax
|
||||||
@ -639,10 +655,7 @@ fn optimize(
|
|||||||
&& expr.as_ref().map(|v| v.is_constant()).unwrap_or(false)
|
&& expr.as_ref().map(|v| v.is_constant()).unwrap_or(false)
|
||||||
})
|
})
|
||||||
.for_each(|ScopeEntry { name, expr, .. }| {
|
.for_each(|ScopeEntry { name, expr, .. }| {
|
||||||
state.push_constant(
|
state.push_constant(name.as_ref(), expr.as_ref().unwrap().as_ref().clone())
|
||||||
name.as_ref(),
|
|
||||||
(**expr.as_ref().expect("should be Some(expr)")).clone(),
|
|
||||||
)
|
|
||||||
});
|
});
|
||||||
|
|
||||||
let orig_constants_len = state.constants.len();
|
let orig_constants_len = state.constants.len();
|
||||||
|
@ -7,6 +7,10 @@ use crate::token::Position;
|
|||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
use crate::parser::FLOAT;
|
use crate::parser::FLOAT;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
#[cfg(feature = "no_std")]
|
||||||
|
use num_traits::*;
|
||||||
|
|
||||||
use num_traits::{
|
use num_traits::{
|
||||||
identities::Zero, CheckedAdd, CheckedDiv, CheckedMul, CheckedNeg, CheckedRem, CheckedShl,
|
identities::Zero, CheckedAdd, CheckedDiv, CheckedMul, CheckedNeg, CheckedRem, CheckedShl,
|
||||||
CheckedShr, CheckedSub,
|
CheckedShr, CheckedSub,
|
||||||
|
@ -6,6 +6,10 @@ use crate::token::Position;
|
|||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
use crate::parser::FLOAT;
|
use crate::parser::FLOAT;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
#[cfg(feature = "no_std")]
|
||||||
|
use num_traits::*;
|
||||||
|
|
||||||
use crate::stdlib::{boxed::Box, format, i32, i64};
|
use crate::stdlib::{boxed::Box, format, i32, i64};
|
||||||
|
|
||||||
#[cfg(feature = "only_i32")]
|
#[cfg(feature = "only_i32")]
|
||||||
|
225
src/parser.rs
225
src/parser.rs
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
use crate::any::{Dynamic, Union};
|
use crate::any::{Dynamic, Union};
|
||||||
use crate::calc_fn_hash;
|
use crate::calc_fn_hash;
|
||||||
use crate::engine::{make_getter, make_setter, Engine, KEYWORD_THIS};
|
use crate::engine::{make_getter, make_setter, Engine, FN_ANONYMOUS, KEYWORD_THIS};
|
||||||
use crate::error::{LexError, ParseError, ParseErrorType};
|
use crate::error::{LexError, ParseError, ParseErrorType};
|
||||||
use crate::module::{Module, ModuleRef};
|
use crate::module::{Module, ModuleRef};
|
||||||
use crate::optimize::{optimize_into_ast, OptimizationLevel};
|
use crate::optimize::{optimize_into_ast, OptimizationLevel};
|
||||||
@ -35,6 +35,12 @@ use crate::stdlib::{
|
|||||||
vec::Vec,
|
vec::Vec,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_std"))]
|
||||||
|
use crate::stdlib::collections::hash_map::DefaultHasher;
|
||||||
|
|
||||||
|
#[cfg(feature = "no_std")]
|
||||||
|
use ahash::AHasher;
|
||||||
|
|
||||||
/// The system integer type.
|
/// The system integer type.
|
||||||
///
|
///
|
||||||
/// If the `only_i32` feature is enabled, this will be `i32` instead.
|
/// If the `only_i32` feature is enabled, this will be `i32` instead.
|
||||||
@ -598,21 +604,35 @@ impl Hash for CustomExpr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
#[derive(Debug, PartialEq, PartialOrd, Clone)]
|
||||||
|
pub struct FloatWrapper(pub FLOAT, pub Position);
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
impl Hash for FloatWrapper {
|
||||||
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
|
state.write(&self.0.to_le_bytes());
|
||||||
|
self.1.hash(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// An expression.
|
/// An expression.
|
||||||
///
|
///
|
||||||
/// Each variant is at most one pointer in size (for speed),
|
/// Each variant is at most one pointer in size (for speed),
|
||||||
/// with everything being allocated together in one single tuple.
|
/// with everything being allocated together in one single tuple.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, Hash)]
|
||||||
pub enum Expr {
|
pub enum Expr {
|
||||||
/// Integer constant.
|
/// Integer constant.
|
||||||
IntegerConstant(Box<(INT, Position)>),
|
IntegerConstant(Box<(INT, Position)>),
|
||||||
/// Floating-point constant.
|
/// Floating-point constant.
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
FloatConstant(Box<(FLOAT, Position)>),
|
FloatConstant(Box<FloatWrapper>),
|
||||||
/// Character constant.
|
/// Character constant.
|
||||||
CharConstant(Box<(char, Position)>),
|
CharConstant(Box<(char, Position)>),
|
||||||
/// String constant.
|
/// String constant.
|
||||||
StringConstant(Box<(ImmutableString, Position)>),
|
StringConstant(Box<(ImmutableString, Position)>),
|
||||||
|
/// FnPtr constant.
|
||||||
|
FnPointer(Box<(ImmutableString, Position)>),
|
||||||
/// Variable access - ((variable name, position), optional modules, hash, optional index)
|
/// Variable access - ((variable name, position), optional modules, hash, optional index)
|
||||||
Variable(
|
Variable(
|
||||||
Box<(
|
Box<(
|
||||||
@ -623,7 +643,7 @@ pub enum Expr {
|
|||||||
)>,
|
)>,
|
||||||
),
|
),
|
||||||
/// Property access.
|
/// Property access.
|
||||||
Property(Box<((String, String, String), Position)>),
|
Property(Box<((ImmutableString, String, String), Position)>),
|
||||||
/// { stmt }
|
/// { stmt }
|
||||||
Stmt(Box<(Stmt, Position)>),
|
Stmt(Box<(Stmt, Position)>),
|
||||||
/// Wrapped expression - should not be optimized away.
|
/// Wrapped expression - should not be optimized away.
|
||||||
@ -637,7 +657,7 @@ pub enum Expr {
|
|||||||
Option<Box<ModuleRef>>,
|
Option<Box<ModuleRef>>,
|
||||||
u64,
|
u64,
|
||||||
StaticVec<Expr>,
|
StaticVec<Expr>,
|
||||||
Option<Dynamic>,
|
Option<bool>,
|
||||||
)>,
|
)>,
|
||||||
),
|
),
|
||||||
/// expr op= expr
|
/// expr op= expr
|
||||||
@ -673,18 +693,6 @@ impl Default for Expr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Hash for Expr {
|
|
||||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
|
||||||
match self {
|
|
||||||
Self::FloatConstant(x) => {
|
|
||||||
state.write(&x.0.to_le_bytes());
|
|
||||||
x.1.hash(state);
|
|
||||||
}
|
|
||||||
_ => self.hash(state),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Expr {
|
impl Expr {
|
||||||
/// Get the `Dynamic` value of a constant expression.
|
/// Get the `Dynamic` value of a constant expression.
|
||||||
///
|
///
|
||||||
@ -758,6 +766,7 @@ impl Expr {
|
|||||||
Self::IntegerConstant(x) => x.1,
|
Self::IntegerConstant(x) => x.1,
|
||||||
Self::CharConstant(x) => x.1,
|
Self::CharConstant(x) => x.1,
|
||||||
Self::StringConstant(x) => x.1,
|
Self::StringConstant(x) => x.1,
|
||||||
|
Self::FnPointer(x) => x.1,
|
||||||
Self::Array(x) => x.1,
|
Self::Array(x) => x.1,
|
||||||
Self::Map(x) => x.1,
|
Self::Map(x) => x.1,
|
||||||
Self::Property(x) => x.1,
|
Self::Property(x) => x.1,
|
||||||
@ -791,6 +800,7 @@ impl Expr {
|
|||||||
Self::IntegerConstant(x) => x.1 = new_pos,
|
Self::IntegerConstant(x) => x.1 = new_pos,
|
||||||
Self::CharConstant(x) => x.1 = new_pos,
|
Self::CharConstant(x) => x.1 = new_pos,
|
||||||
Self::StringConstant(x) => x.1 = new_pos,
|
Self::StringConstant(x) => x.1 = new_pos,
|
||||||
|
Self::FnPointer(x) => x.1 = new_pos,
|
||||||
Self::Array(x) => x.1 = new_pos,
|
Self::Array(x) => x.1 = new_pos,
|
||||||
Self::Map(x) => x.1 = new_pos,
|
Self::Map(x) => x.1 = new_pos,
|
||||||
Self::Variable(x) => (x.0).1 = new_pos,
|
Self::Variable(x) => (x.0).1 = new_pos,
|
||||||
@ -847,6 +857,7 @@ impl Expr {
|
|||||||
Self::IntegerConstant(_)
|
Self::IntegerConstant(_)
|
||||||
| Self::CharConstant(_)
|
| Self::CharConstant(_)
|
||||||
| Self::StringConstant(_)
|
| Self::StringConstant(_)
|
||||||
|
| Self::FnPointer(_)
|
||||||
| Self::True(_)
|
| Self::True(_)
|
||||||
| Self::False(_)
|
| Self::False(_)
|
||||||
| Self::Unit(_) => true,
|
| Self::Unit(_) => true,
|
||||||
@ -878,6 +889,7 @@ impl Expr {
|
|||||||
|
|
||||||
Self::IntegerConstant(_)
|
Self::IntegerConstant(_)
|
||||||
| Self::CharConstant(_)
|
| Self::CharConstant(_)
|
||||||
|
| Self::FnPointer(_)
|
||||||
| Self::In(_)
|
| Self::In(_)
|
||||||
| Self::And(_)
|
| Self::And(_)
|
||||||
| Self::Or(_)
|
| Self::Or(_)
|
||||||
@ -925,7 +937,7 @@ impl Expr {
|
|||||||
let (name, pos) = x.0;
|
let (name, pos) = x.0;
|
||||||
let getter = make_getter(&name);
|
let getter = make_getter(&name);
|
||||||
let setter = make_setter(&name);
|
let setter = make_setter(&name);
|
||||||
Self::Property(Box::new(((name.clone(), getter, setter), pos)))
|
Self::Property(Box::new(((name.into(), getter, setter), pos)))
|
||||||
}
|
}
|
||||||
_ => self,
|
_ => self,
|
||||||
}
|
}
|
||||||
@ -1476,7 +1488,7 @@ fn parse_primary(
|
|||||||
let mut root_expr = match token {
|
let mut root_expr = match token {
|
||||||
Token::IntegerConstant(x) => Expr::IntegerConstant(Box::new((x, settings.pos))),
|
Token::IntegerConstant(x) => Expr::IntegerConstant(Box::new((x, settings.pos))),
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
Token::FloatConstant(x) => Expr::FloatConstant(Box::new((x, settings.pos))),
|
Token::FloatConstant(x) => Expr::FloatConstant(Box::new(FloatWrapper(x, settings.pos))),
|
||||||
Token::CharConstant(c) => Expr::CharConstant(Box::new((c, settings.pos))),
|
Token::CharConstant(c) => Expr::CharConstant(Box::new((c, settings.pos))),
|
||||||
Token::StringConstant(s) => Expr::StringConstant(Box::new((s.into(), settings.pos))),
|
Token::StringConstant(s) => Expr::StringConstant(Box::new((s.into(), settings.pos))),
|
||||||
Token::Identifier(s) => {
|
Token::Identifier(s) => {
|
||||||
@ -1616,7 +1628,10 @@ fn parse_unary(
|
|||||||
.map(|i| Expr::IntegerConstant(Box::new((i, pos))))
|
.map(|i| Expr::IntegerConstant(Box::new((i, pos))))
|
||||||
.or_else(|| {
|
.or_else(|| {
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
return Some(Expr::FloatConstant(Box::new((-(x.0 as FLOAT), pos))));
|
return Some(Expr::FloatConstant(Box::new(FloatWrapper(
|
||||||
|
-(x.0 as FLOAT),
|
||||||
|
pos,
|
||||||
|
))));
|
||||||
#[cfg(feature = "no_float")]
|
#[cfg(feature = "no_float")]
|
||||||
return None;
|
return None;
|
||||||
})
|
})
|
||||||
@ -1625,7 +1640,9 @@ fn parse_unary(
|
|||||||
|
|
||||||
// Negative float
|
// Negative float
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
Expr::FloatConstant(x) => Ok(Expr::FloatConstant(Box::new((-x.0, x.1)))),
|
Expr::FloatConstant(x) => {
|
||||||
|
Ok(Expr::FloatConstant(Box::new(FloatWrapper(-x.0, x.1))))
|
||||||
|
}
|
||||||
|
|
||||||
// Call negative function
|
// Call negative function
|
||||||
expr => {
|
expr => {
|
||||||
@ -1664,9 +1681,38 @@ fn parse_unary(
|
|||||||
None,
|
None,
|
||||||
hash,
|
hash,
|
||||||
args,
|
args,
|
||||||
Some(false.into()), // NOT operator, when operating on invalid operand, defaults to false
|
Some(false), // NOT operator, when operating on invalid operand, defaults to false
|
||||||
))))
|
))))
|
||||||
}
|
}
|
||||||
|
// | ...
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
Token::Pipe | Token::Or => {
|
||||||
|
let mut state = ParseState::new(
|
||||||
|
state.engine,
|
||||||
|
state.max_function_expr_depth,
|
||||||
|
state.max_function_expr_depth,
|
||||||
|
);
|
||||||
|
|
||||||
|
let settings = ParseSettings {
|
||||||
|
allow_if_expr: true,
|
||||||
|
allow_stmt_expr: true,
|
||||||
|
allow_anonymous_fn: true,
|
||||||
|
is_global: false,
|
||||||
|
is_function_scope: true,
|
||||||
|
is_breakable: false,
|
||||||
|
level: 0,
|
||||||
|
pos: *token_pos,
|
||||||
|
};
|
||||||
|
|
||||||
|
let (expr, func) = parse_anon_fn(input, &mut state, lib, settings)?;
|
||||||
|
|
||||||
|
// Qualifiers (none) + function name + number of arguments.
|
||||||
|
let hash = calc_fn_hash(empty(), &func.name, func.params.len(), empty());
|
||||||
|
|
||||||
|
lib.insert(hash, func);
|
||||||
|
|
||||||
|
Ok(expr)
|
||||||
|
}
|
||||||
// <EOF>
|
// <EOF>
|
||||||
Token::EOF => Err(PERR::UnexpectedEOF.into_err(settings.pos)),
|
Token::EOF => Err(PERR::UnexpectedEOF.into_err(settings.pos)),
|
||||||
// All other tokens
|
// All other tokens
|
||||||
@ -1788,7 +1834,7 @@ fn make_dot_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result<Expr, ParseEr
|
|||||||
|
|
||||||
let getter = make_getter(&name);
|
let getter = make_getter(&name);
|
||||||
let setter = make_setter(&name);
|
let setter = make_setter(&name);
|
||||||
let rhs = Expr::Property(Box::new(((name, getter, setter), pos)));
|
let rhs = Expr::Property(Box::new(((name.into(), getter, setter), pos)));
|
||||||
|
|
||||||
Expr::Dot(Box::new((lhs, rhs, op_pos)))
|
Expr::Dot(Box::new((lhs, rhs, op_pos)))
|
||||||
}
|
}
|
||||||
@ -2018,7 +2064,7 @@ fn parse_binary_op(
|
|||||||
settings.pos = pos;
|
settings.pos = pos;
|
||||||
settings.ensure_level_within_max_limit(state.max_expr_depth)?;
|
settings.ensure_level_within_max_limit(state.max_expr_depth)?;
|
||||||
|
|
||||||
let cmp_def = Some(false.into());
|
let cmp_def = Some(false);
|
||||||
let op = op_token.syntax();
|
let op = op_token.syntax();
|
||||||
let hash = calc_fn_hash(empty(), &op, 2, empty());
|
let hash = calc_fn_hash(empty(), &op, 2, empty());
|
||||||
let op = (op, true, pos);
|
let op = (op, true, pos);
|
||||||
@ -2041,7 +2087,7 @@ fn parse_binary_op(
|
|||||||
| Token::XOr => Expr::FnCall(Box::new((op, None, hash, args, None))),
|
| Token::XOr => Expr::FnCall(Box::new((op, None, hash, args, None))),
|
||||||
|
|
||||||
// '!=' defaults to true when passed invalid operands
|
// '!=' defaults to true when passed invalid operands
|
||||||
Token::NotEqualsTo => Expr::FnCall(Box::new((op, None, hash, args, Some(true.into())))),
|
Token::NotEqualsTo => Expr::FnCall(Box::new((op, None, hash, args, Some(true)))),
|
||||||
|
|
||||||
// Comparison operators default to false when passed invalid operands
|
// Comparison operators default to false when passed invalid operands
|
||||||
Token::EqualsTo
|
Token::EqualsTo
|
||||||
@ -2762,32 +2808,28 @@ fn parse_fn(
|
|||||||
let mut params = Vec::new();
|
let mut params = Vec::new();
|
||||||
|
|
||||||
if !match_token(input, Token::RightParen)? {
|
if !match_token(input, Token::RightParen)? {
|
||||||
let end_err = format!("to close the parameters list of function '{}'", name);
|
|
||||||
let sep_err = format!("to separate the parameters of function '{}'", name);
|
let sep_err = format!("to separate the parameters of function '{}'", name);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
match input.peek().unwrap() {
|
match input.next().unwrap() {
|
||||||
(Token::RightParen, _) => (),
|
(Token::RightParen, _) => break,
|
||||||
_ => match input.next().unwrap() {
|
(Token::Identifier(s), pos) => {
|
||||||
(Token::Identifier(s), pos) => {
|
state.stack.push((s.clone(), ScopeEntryType::Normal));
|
||||||
state.stack.push((s.clone(), ScopeEntryType::Normal));
|
params.push((s, pos))
|
||||||
params.push((s, pos))
|
}
|
||||||
}
|
(Token::LexError(err), pos) => return Err(err.into_err(pos)),
|
||||||
(Token::LexError(err), pos) => return Err(err.into_err(pos)),
|
(_, pos) => {
|
||||||
(_, pos) => {
|
return Err(PERR::MissingToken(
|
||||||
return Err(
|
Token::RightParen.into(),
|
||||||
PERR::MissingToken(Token::RightParen.into(), end_err).into_err(pos)
|
format!("to close the parameters list of function '{}'", name),
|
||||||
)
|
)
|
||||||
}
|
.into_err(pos))
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
match input.next().unwrap() {
|
match input.next().unwrap() {
|
||||||
(Token::RightParen, _) => break,
|
(Token::RightParen, _) => break,
|
||||||
(Token::Comma, _) => (),
|
(Token::Comma, _) => (),
|
||||||
(Token::Identifier(_), pos) => {
|
|
||||||
return Err(PERR::MissingToken(Token::Comma.into(), sep_err).into_err(pos))
|
|
||||||
}
|
|
||||||
(Token::LexError(err), pos) => return Err(err.into_err(pos)),
|
(Token::LexError(err), pos) => return Err(err.into_err(pos)),
|
||||||
(_, pos) => {
|
(_, pos) => {
|
||||||
return Err(PERR::MissingToken(Token::Comma.into(), sep_err).into_err(pos))
|
return Err(PERR::MissingToken(Token::Comma.into(), sep_err).into_err(pos))
|
||||||
@ -2831,6 +2873,101 @@ fn parse_fn(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parse an anonymous function definition.
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
fn parse_anon_fn(
|
||||||
|
input: &mut TokenStream,
|
||||||
|
state: &mut ParseState,
|
||||||
|
lib: &mut FunctionsLib,
|
||||||
|
mut settings: ParseSettings,
|
||||||
|
) -> Result<(Expr, ScriptFnDef), ParseError> {
|
||||||
|
settings.ensure_level_within_max_limit(state.max_expr_depth)?;
|
||||||
|
|
||||||
|
let mut params = Vec::new();
|
||||||
|
|
||||||
|
if input.next().unwrap().0 != Token::Or {
|
||||||
|
if !match_token(input, Token::Pipe)? {
|
||||||
|
loop {
|
||||||
|
match input.next().unwrap() {
|
||||||
|
(Token::Pipe, _) => break,
|
||||||
|
(Token::Identifier(s), pos) => {
|
||||||
|
state.stack.push((s.clone(), ScopeEntryType::Normal));
|
||||||
|
params.push((s, pos))
|
||||||
|
}
|
||||||
|
(Token::LexError(err), pos) => return Err(err.into_err(pos)),
|
||||||
|
(_, pos) => {
|
||||||
|
return Err(PERR::MissingToken(
|
||||||
|
Token::Pipe.into(),
|
||||||
|
"to close the parameters list of anonymous function".into(),
|
||||||
|
)
|
||||||
|
.into_err(pos))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match input.next().unwrap() {
|
||||||
|
(Token::Pipe, _) => break,
|
||||||
|
(Token::Comma, _) => (),
|
||||||
|
(Token::LexError(err), pos) => return Err(err.into_err(pos)),
|
||||||
|
(_, pos) => {
|
||||||
|
return Err(PERR::MissingToken(
|
||||||
|
Token::Comma.into(),
|
||||||
|
"to separate the parameters of anonymous function".into(),
|
||||||
|
)
|
||||||
|
.into_err(pos))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for duplicating parameters
|
||||||
|
params
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.try_for_each(|(i, (p1, _))| {
|
||||||
|
params
|
||||||
|
.iter()
|
||||||
|
.skip(i + 1)
|
||||||
|
.find(|(p2, _)| p2 == p1)
|
||||||
|
.map_or_else(|| Ok(()), |(p2, pos)| Err((p2, *pos)))
|
||||||
|
})
|
||||||
|
.map_err(|(p, pos)| PERR::FnDuplicatedParam("".to_string(), p.to_string()).into_err(pos))?;
|
||||||
|
|
||||||
|
// Parse function body
|
||||||
|
settings.is_breakable = false;
|
||||||
|
let pos = input.peek().unwrap().1;
|
||||||
|
let body = parse_stmt(input, state, lib, settings.level_up())
|
||||||
|
.map(|stmt| stmt.unwrap_or_else(|| Stmt::Noop(pos)))?;
|
||||||
|
|
||||||
|
let params: StaticVec<_> = params.into_iter().map(|(p, _)| p).collect();
|
||||||
|
|
||||||
|
// Calculate hash
|
||||||
|
#[cfg(feature = "no_std")]
|
||||||
|
let mut s: AHasher = Default::default();
|
||||||
|
#[cfg(not(feature = "no_std"))]
|
||||||
|
let mut s = DefaultHasher::new();
|
||||||
|
|
||||||
|
s.write_usize(params.len());
|
||||||
|
params.iter().for_each(|a| a.hash(&mut s));
|
||||||
|
body.hash(&mut s);
|
||||||
|
let hash = s.finish();
|
||||||
|
|
||||||
|
// Create unique function name
|
||||||
|
let fn_name = format!("{}{}", FN_ANONYMOUS, hash);
|
||||||
|
|
||||||
|
let script = ScriptFnDef {
|
||||||
|
name: fn_name.clone(),
|
||||||
|
access: FnAccess::Public,
|
||||||
|
params,
|
||||||
|
body,
|
||||||
|
pos: settings.pos,
|
||||||
|
};
|
||||||
|
|
||||||
|
let expr = Expr::FnPointer(Box::new((fn_name.into(), settings.pos)));
|
||||||
|
|
||||||
|
Ok((expr, script))
|
||||||
|
}
|
||||||
|
|
||||||
impl Engine {
|
impl Engine {
|
||||||
pub(crate) fn parse_global_expr(
|
pub(crate) fn parse_global_expr(
|
||||||
&self,
|
&self,
|
||||||
@ -2951,7 +3088,7 @@ impl Engine {
|
|||||||
pub fn map_dynamic_to_expr(value: Dynamic, pos: Position) -> Option<Expr> {
|
pub fn map_dynamic_to_expr(value: Dynamic, pos: Position) -> Option<Expr> {
|
||||||
match value.0 {
|
match value.0 {
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
Union::Float(value) => Some(Expr::FloatConstant(Box::new((value, pos)))),
|
Union::Float(value) => Some(Expr::FloatConstant(Box::new(FloatWrapper(value, pos)))),
|
||||||
|
|
||||||
Union::Unit(_) => Some(Expr::Unit(pos)),
|
Union::Unit(_) => Some(Expr::Unit(pos)),
|
||||||
Union::Int(value) => Some(Expr::IntegerConstant(Box::new((value, pos)))),
|
Union::Int(value) => Some(Expr::IntegerConstant(Box::new((value, pos)))),
|
||||||
|
@ -22,26 +22,13 @@ use crate::stdlib::{
|
|||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
pub type FnCustomSyntaxEval = dyn Fn(
|
pub type FnCustomSyntaxEval = dyn Fn(
|
||||||
&Engine,
|
&Engine,
|
||||||
|
&mut EvalContext,
|
||||||
&mut Scope,
|
&mut Scope,
|
||||||
&mut Imports,
|
|
||||||
&mut State,
|
|
||||||
&Module,
|
|
||||||
&mut Option<&mut Dynamic>,
|
|
||||||
&[Expression],
|
&[Expression],
|
||||||
usize,
|
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>>;
|
) -> Result<Dynamic, Box<EvalAltResult>>;
|
||||||
/// A general function trail object.
|
/// A general function trail object.
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
pub type FnCustomSyntaxEval = dyn Fn(
|
pub type FnCustomSyntaxEval = dyn Fn(&Engine, &mut EvalContext, &mut Scope, &[Expression]) -> Result<Dynamic, Box<EvalAltResult>>
|
||||||
&Engine,
|
|
||||||
&mut Scope,
|
|
||||||
&mut Imports,
|
|
||||||
&mut State,
|
|
||||||
&Module,
|
|
||||||
&mut Option<&mut Dynamic>,
|
|
||||||
&[Expression],
|
|
||||||
usize,
|
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>>
|
|
||||||
+ Send
|
+ Send
|
||||||
+ Sync;
|
+ Sync;
|
||||||
|
|
||||||
@ -58,6 +45,14 @@ impl fmt::Debug for CustomSyntax {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct EvalContext<'a, 'b: 'a, 's, 'm, 't, 'd: 't> {
|
||||||
|
pub(crate) mods: &'a mut Imports<'b>,
|
||||||
|
pub(crate) state: &'s mut State,
|
||||||
|
pub(crate) lib: &'m Module,
|
||||||
|
pub(crate) this_ptr: &'t mut Option<&'d mut Dynamic>,
|
||||||
|
pub(crate) level: usize,
|
||||||
|
}
|
||||||
|
|
||||||
impl Engine {
|
impl Engine {
|
||||||
pub fn register_custom_syntax<S: AsRef<str> + ToString>(
|
pub fn register_custom_syntax<S: AsRef<str> + ToString>(
|
||||||
&mut self,
|
&mut self,
|
||||||
@ -65,13 +60,9 @@ impl Engine {
|
|||||||
scope_delta: isize,
|
scope_delta: isize,
|
||||||
func: impl Fn(
|
func: impl Fn(
|
||||||
&Engine,
|
&Engine,
|
||||||
|
&mut EvalContext,
|
||||||
&mut Scope,
|
&mut Scope,
|
||||||
&mut Imports,
|
|
||||||
&mut State,
|
|
||||||
&Module,
|
|
||||||
&mut Option<&mut Dynamic>,
|
|
||||||
&[Expression],
|
&[Expression],
|
||||||
usize,
|
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>>
|
) -> Result<Dynamic, Box<EvalAltResult>>
|
||||||
+ SendSync
|
+ SendSync
|
||||||
+ 'static,
|
+ 'static,
|
||||||
|
@ -12,6 +12,7 @@ use crate::stdlib::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
/// Cast a type into another type.
|
/// Cast a type into another type.
|
||||||
|
#[inline(always)]
|
||||||
pub fn unsafe_try_cast<A: Any, B: Any>(a: A) -> Option<B> {
|
pub fn unsafe_try_cast<A: Any, B: Any>(a: A) -> Option<B> {
|
||||||
if TypeId::of::<B>() == a.type_id() {
|
if TypeId::of::<B>() == a.type_id() {
|
||||||
// SAFETY: Just checked we have the right type. We explicitly forget the
|
// SAFETY: Just checked we have the right type. We explicitly forget the
|
||||||
@ -28,6 +29,7 @@ pub fn unsafe_try_cast<A: Any, B: Any>(a: A) -> Option<B> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Cast a Boxed type into another type.
|
/// Cast a Boxed type into another type.
|
||||||
|
#[inline(always)]
|
||||||
pub fn unsafe_cast_box<X: Variant, T: Variant>(item: Box<X>) -> Result<Box<T>, Box<X>> {
|
pub fn unsafe_cast_box<X: Variant, T: Variant>(item: Box<X>) -> Result<Box<T>, Box<X>> {
|
||||||
// Only allow casting to the exact same type
|
// Only allow casting to the exact same type
|
||||||
if TypeId::of::<X>() == TypeId::of::<T>() {
|
if TypeId::of::<X>() == TypeId::of::<T>() {
|
||||||
@ -51,6 +53,7 @@ pub fn unsafe_cast_box<X: Variant, T: Variant>(item: Box<X>) -> Result<Box<T>, B
|
|||||||
///
|
///
|
||||||
/// Force-casting a local variable's lifetime to the current `Scope`'s larger lifetime saves
|
/// Force-casting a local variable's lifetime to the current `Scope`'s larger lifetime saves
|
||||||
/// on allocations and string cloning, thus avoids us having to maintain a chain of `Scope`'s.
|
/// on allocations and string cloning, thus avoids us having to maintain a chain of `Scope`'s.
|
||||||
|
#[inline]
|
||||||
pub fn unsafe_cast_var_name_to_lifetime<'s>(name: &str, state: &State) -> Cow<'s, str> {
|
pub fn unsafe_cast_var_name_to_lifetime<'s>(name: &str, state: &State) -> Cow<'s, str> {
|
||||||
// If not at global level, we can force-cast
|
// If not at global level, we can force-cast
|
||||||
if state.scope_level > 0 {
|
if state.scope_level > 0 {
|
||||||
|
26
src/utils.rs
26
src/utils.rs
@ -36,11 +36,9 @@ use ahash::AHasher;
|
|||||||
pub struct StraightHasher(u64);
|
pub struct StraightHasher(u64);
|
||||||
|
|
||||||
impl Hasher for StraightHasher {
|
impl Hasher for StraightHasher {
|
||||||
#[inline(always)]
|
|
||||||
fn finish(&self) -> u64 {
|
fn finish(&self) -> u64 {
|
||||||
self.0
|
self.0
|
||||||
}
|
}
|
||||||
#[inline]
|
|
||||||
fn write(&mut self, bytes: &[u8]) {
|
fn write(&mut self, bytes: &[u8]) {
|
||||||
let mut key = [0_u8; 8];
|
let mut key = [0_u8; 8];
|
||||||
key.copy_from_slice(&bytes[..8]); // Panics if fewer than 8 bytes
|
key.copy_from_slice(&bytes[..8]); // Panics if fewer than 8 bytes
|
||||||
@ -50,7 +48,6 @@ impl Hasher for StraightHasher {
|
|||||||
|
|
||||||
impl StraightHasher {
|
impl StraightHasher {
|
||||||
/// Create a `StraightHasher`.
|
/// Create a `StraightHasher`.
|
||||||
#[inline(always)]
|
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self(0)
|
Self(0)
|
||||||
}
|
}
|
||||||
@ -63,7 +60,6 @@ pub struct StraightHasherBuilder;
|
|||||||
impl BuildHasher for StraightHasherBuilder {
|
impl BuildHasher for StraightHasherBuilder {
|
||||||
type Hasher = StraightHasher;
|
type Hasher = StraightHasher;
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn build_hasher(&self) -> Self::Hasher {
|
fn build_hasher(&self) -> Self::Hasher {
|
||||||
StraightHasher::new()
|
StraightHasher::new()
|
||||||
}
|
}
|
||||||
@ -150,7 +146,6 @@ pub struct StaticVec<T> {
|
|||||||
const MAX_STATIC_VEC: usize = 4;
|
const MAX_STATIC_VEC: usize = 4;
|
||||||
|
|
||||||
impl<T> Drop for StaticVec<T> {
|
impl<T> Drop for StaticVec<T> {
|
||||||
#[inline(always)]
|
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
self.clear();
|
self.clear();
|
||||||
}
|
}
|
||||||
@ -233,7 +228,6 @@ impl<T: 'static> IntoIterator for StaticVec<T> {
|
|||||||
|
|
||||||
impl<T> StaticVec<T> {
|
impl<T> StaticVec<T> {
|
||||||
/// Create a new `StaticVec`.
|
/// Create a new `StaticVec`.
|
||||||
#[inline(always)]
|
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Default::default()
|
Default::default()
|
||||||
}
|
}
|
||||||
@ -249,7 +243,6 @@ impl<T> StaticVec<T> {
|
|||||||
self.len = 0;
|
self.len = 0;
|
||||||
}
|
}
|
||||||
/// Extract a `MaybeUninit` into a concrete initialized type.
|
/// Extract a `MaybeUninit` into a concrete initialized type.
|
||||||
#[inline(always)]
|
|
||||||
fn extract(value: MaybeUninit<T>) -> T {
|
fn extract(value: MaybeUninit<T>) -> T {
|
||||||
unsafe { value.assume_init() }
|
unsafe { value.assume_init() }
|
||||||
}
|
}
|
||||||
@ -311,7 +304,6 @@ impl<T> StaticVec<T> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
/// Is data stored in fixed-size storage?
|
/// Is data stored in fixed-size storage?
|
||||||
#[inline(always)]
|
|
||||||
fn is_fixed_storage(&self) -> bool {
|
fn is_fixed_storage(&self) -> bool {
|
||||||
self.len <= MAX_STATIC_VEC
|
self.len <= MAX_STATIC_VEC
|
||||||
}
|
}
|
||||||
@ -418,12 +410,10 @@ impl<T> StaticVec<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Get the number of items in this `StaticVec`.
|
/// Get the number of items in this `StaticVec`.
|
||||||
#[inline(always)]
|
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
self.len
|
self.len
|
||||||
}
|
}
|
||||||
/// Is this `StaticVec` empty?
|
/// Is this `StaticVec` empty?
|
||||||
#[inline(always)]
|
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.len == 0
|
self.len == 0
|
||||||
}
|
}
|
||||||
@ -664,48 +654,41 @@ pub struct ImmutableString(Shared<String>);
|
|||||||
impl Deref for ImmutableString {
|
impl Deref for ImmutableString {
|
||||||
type Target = String;
|
type Target = String;
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
&self.0
|
&self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsRef<String> for ImmutableString {
|
impl AsRef<String> for ImmutableString {
|
||||||
#[inline(always)]
|
|
||||||
fn as_ref(&self) -> &String {
|
fn as_ref(&self) -> &String {
|
||||||
&self.0
|
&self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Borrow<str> for ImmutableString {
|
impl Borrow<str> for ImmutableString {
|
||||||
#[inline(always)]
|
|
||||||
fn borrow(&self) -> &str {
|
fn borrow(&self) -> &str {
|
||||||
self.0.as_str()
|
self.0.as_str()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&str> for ImmutableString {
|
impl From<&str> for ImmutableString {
|
||||||
#[inline(always)]
|
|
||||||
fn from(value: &str) -> Self {
|
fn from(value: &str) -> Self {
|
||||||
Self(value.to_string().into())
|
Self(value.to_string().into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl From<String> for ImmutableString {
|
impl From<String> for ImmutableString {
|
||||||
#[inline(always)]
|
|
||||||
fn from(value: String) -> Self {
|
fn from(value: String) -> Self {
|
||||||
Self(value.into())
|
Self(value.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Box<String>> for ImmutableString {
|
impl From<Box<String>> for ImmutableString {
|
||||||
#[inline(always)]
|
|
||||||
fn from(value: Box<String>) -> Self {
|
fn from(value: Box<String>) -> Self {
|
||||||
Self(value.into())
|
Self(value.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ImmutableString> for String {
|
impl From<ImmutableString> for String {
|
||||||
#[inline(always)]
|
|
||||||
fn from(value: ImmutableString) -> Self {
|
fn from(value: ImmutableString) -> Self {
|
||||||
value.into_owned()
|
value.into_owned()
|
||||||
}
|
}
|
||||||
@ -714,49 +697,42 @@ impl From<ImmutableString> for String {
|
|||||||
impl FromStr for ImmutableString {
|
impl FromStr for ImmutableString {
|
||||||
type Err = ();
|
type Err = ();
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
Ok(Self(s.to_string().into()))
|
Ok(Self(s.to_string().into()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromIterator<char> for ImmutableString {
|
impl FromIterator<char> for ImmutableString {
|
||||||
#[inline(always)]
|
|
||||||
fn from_iter<T: IntoIterator<Item = char>>(iter: T) -> Self {
|
fn from_iter<T: IntoIterator<Item = char>>(iter: T) -> Self {
|
||||||
Self(iter.into_iter().collect::<String>().into())
|
Self(iter.into_iter().collect::<String>().into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> FromIterator<&'a char> for ImmutableString {
|
impl<'a> FromIterator<&'a char> for ImmutableString {
|
||||||
#[inline(always)]
|
|
||||||
fn from_iter<T: IntoIterator<Item = &'a char>>(iter: T) -> Self {
|
fn from_iter<T: IntoIterator<Item = &'a char>>(iter: T) -> Self {
|
||||||
Self(iter.into_iter().cloned().collect::<String>().into())
|
Self(iter.into_iter().cloned().collect::<String>().into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> FromIterator<&'a str> for ImmutableString {
|
impl<'a> FromIterator<&'a str> for ImmutableString {
|
||||||
#[inline(always)]
|
|
||||||
fn from_iter<T: IntoIterator<Item = &'a str>>(iter: T) -> Self {
|
fn from_iter<T: IntoIterator<Item = &'a str>>(iter: T) -> Self {
|
||||||
Self(iter.into_iter().collect::<String>().into())
|
Self(iter.into_iter().collect::<String>().into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> FromIterator<String> for ImmutableString {
|
impl<'a> FromIterator<String> for ImmutableString {
|
||||||
#[inline(always)]
|
|
||||||
fn from_iter<T: IntoIterator<Item = String>>(iter: T) -> Self {
|
fn from_iter<T: IntoIterator<Item = String>>(iter: T) -> Self {
|
||||||
Self(iter.into_iter().collect::<String>().into())
|
Self(iter.into_iter().collect::<String>().into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for ImmutableString {
|
impl fmt::Display for ImmutableString {
|
||||||
#[inline(always)]
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
fmt::Display::fmt(self.0.as_str(), f)
|
fmt::Display::fmt(self.0.as_str(), f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for ImmutableString {
|
impl fmt::Debug for ImmutableString {
|
||||||
#[inline(always)]
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
fmt::Debug::fmt(self.0.as_str(), f)
|
fmt::Debug::fmt(self.0.as_str(), f)
|
||||||
}
|
}
|
||||||
@ -891,7 +867,6 @@ impl Add<char> for &ImmutableString {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl AddAssign<char> for ImmutableString {
|
impl AddAssign<char> for ImmutableString {
|
||||||
#[inline(always)]
|
|
||||||
fn add_assign(&mut self, rhs: char) {
|
fn add_assign(&mut self, rhs: char) {
|
||||||
self.make_mut().push(rhs);
|
self.make_mut().push(rhs);
|
||||||
}
|
}
|
||||||
@ -906,7 +881,6 @@ impl ImmutableString {
|
|||||||
}
|
}
|
||||||
/// Make sure that the `ImmutableString` is unique (i.e. no other outstanding references).
|
/// Make sure that the `ImmutableString` is unique (i.e. no other outstanding references).
|
||||||
/// Then return a mutable reference to the `String`.
|
/// Then return a mutable reference to the `String`.
|
||||||
#[inline(always)]
|
|
||||||
pub fn make_mut(&mut self) -> &mut String {
|
pub fn make_mut(&mut self) -> &mut String {
|
||||||
shared_make_mut(&mut self.0)
|
shared_make_mut(&mut self.0)
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
#![cfg(not(feature = "no_function"))]
|
#![cfg(not(feature = "no_function"))]
|
||||||
use rhai::{
|
use rhai::{
|
||||||
Dynamic, Engine, EvalAltResult, FnPtr, Func, ImmutableString, Module, ParseError,
|
Dynamic, Engine, EvalAltResult, FnPtr, Func, Module, ParseError, ParseErrorType, Scope, INT,
|
||||||
ParseErrorType, Scope, INT,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
#![cfg(feature = "internals")]
|
#![cfg(feature = "internals")]
|
||||||
use rhai::{
|
use rhai::{Engine, EvalAltResult, EvalContext, Expression, LexError, ParseErrorType, Scope, INT};
|
||||||
Dynamic, Engine, EvalAltResult, EvalState, Expression, Imports, LexError, Module, ParseError,
|
|
||||||
ParseErrorType, Scope, INT,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_custom_syntax() -> Result<(), Box<EvalAltResult>> {
|
fn test_custom_syntax() -> Result<(), Box<EvalAltResult>> {
|
||||||
@ -28,13 +25,9 @@ fn test_custom_syntax() -> Result<(), Box<EvalAltResult>> {
|
|||||||
],
|
],
|
||||||
1,
|
1,
|
||||||
|engine: &Engine,
|
|engine: &Engine,
|
||||||
|
context: &mut EvalContext,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
mods: &mut Imports,
|
inputs: &[Expression]| {
|
||||||
state: &mut EvalState,
|
|
||||||
lib: &Module,
|
|
||||||
this_ptr: &mut Option<&mut Dynamic>,
|
|
||||||
inputs: &[Expression],
|
|
||||||
level: usize| {
|
|
||||||
let var_name = inputs[0].get_variable_name().unwrap().to_string();
|
let var_name = inputs[0].get_variable_name().unwrap().to_string();
|
||||||
let stmt = inputs.get(1).unwrap();
|
let stmt = inputs.get(1).unwrap();
|
||||||
let expr = inputs.get(2).unwrap();
|
let expr = inputs.get(2).unwrap();
|
||||||
@ -42,10 +35,10 @@ fn test_custom_syntax() -> Result<(), Box<EvalAltResult>> {
|
|||||||
scope.push(var_name, 0 as INT);
|
scope.push(var_name, 0 as INT);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
engine.eval_expression_tree(scope, mods, state, lib, this_ptr, stmt, level)?;
|
engine.eval_expression_tree(context, scope, stmt)?;
|
||||||
|
|
||||||
if !engine
|
if !engine
|
||||||
.eval_expression_tree(scope, mods, state, lib, this_ptr, expr, level)?
|
.eval_expression_tree(context, scope, expr)?
|
||||||
.as_bool()
|
.as_bool()
|
||||||
.map_err(|_| {
|
.map_err(|_| {
|
||||||
EvalAltResult::ErrorBooleanArgMismatch(
|
EvalAltResult::ErrorBooleanArgMismatch(
|
||||||
@ -78,7 +71,7 @@ fn test_custom_syntax() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
// The first symbol must be an identifier
|
// The first symbol must be an identifier
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
*engine.register_custom_syntax(&["!"], 0, |_, _, _, _, _, _, _, _| Ok(().into())).expect_err("should error"),
|
*engine.register_custom_syntax(&["!"], 0, |_, _, _, _| Ok(().into())).expect_err("should error"),
|
||||||
LexError::ImproperSymbol(s) if s == "!"
|
LexError::ImproperSymbol(s) if s == "!"
|
||||||
));
|
));
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user