OOP support.
This commit is contained in:
parent
259b6d0fcf
commit
175c3ccaec
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "rhai"
|
name = "rhai"
|
||||||
version = "0.15.2"
|
version = "0.16.0"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
authors = ["Jonathan Turner", "Lukáš Hozda", "Stephen Chung"]
|
authors = ["Jonathan Turner", "Lukáš Hozda", "Stephen Chung"]
|
||||||
description = "Embedded scripting for Rust"
|
description = "Embedded scripting for Rust"
|
||||||
|
@ -1,18 +1,24 @@
|
|||||||
Rhai Release Notes
|
Rhai Release Notes
|
||||||
==================
|
==================
|
||||||
|
|
||||||
Version 0.15.2
|
Version 0.16.0
|
||||||
==============
|
==============
|
||||||
|
|
||||||
|
The major new feature in this version is OOP - well, poor man's OOP, that is.
|
||||||
|
|
||||||
Breaking changes
|
Breaking changes
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
* The trait function `ModuleResolver::resolve` no longer takes a `Scope` as argument.
|
* The trait function `ModuleResolver::resolve` no longer takes a `Scope` as argument.
|
||||||
|
* Functions defined in script now differentiates between using method-call style and normal function-call style.
|
||||||
|
The method-call style will bind the object to the `this` parameter instead of consuming the first parameter.
|
||||||
|
|
||||||
New features
|
New features
|
||||||
------------
|
------------
|
||||||
|
|
||||||
* Support for _function pointers_ via `Fn(name)` and `Fn.call(...)` syntax - a poor man's first-class function.
|
* Support for _function pointers_ via `Fn(name)` and `Fn.call(...)` syntax - a poor man's first-class function.
|
||||||
|
* Support for calling script-defined functions in method-call style with `this` binding to the object.
|
||||||
|
* Special support in object maps for OOP.
|
||||||
|
|
||||||
Enhancements
|
Enhancements
|
||||||
------------
|
------------
|
||||||
|
@ -56,6 +56,7 @@ The Rhai Scripting Language
|
|||||||
5. [Arrays](language/arrays.md)
|
5. [Arrays](language/arrays.md)
|
||||||
6. [Object Maps](language/object-maps.md)
|
6. [Object Maps](language/object-maps.md)
|
||||||
1. [Parse from JSON](language/json.md)
|
1. [Parse from JSON](language/json.md)
|
||||||
|
2. [Special Support for OOP](language/object-maps-oop.md)
|
||||||
7. [Time-Stamps](language/timestamps.md)
|
7. [Time-Stamps](language/timestamps.md)
|
||||||
3. [Keywords](language/keywords.md)
|
3. [Keywords](language/keywords.md)
|
||||||
4. [Statements](language/statements.md)
|
4. [Statements](language/statements.md)
|
||||||
@ -92,14 +93,15 @@ The Rhai Scripting Language
|
|||||||
8. [Maximum Call Stack Depth](safety/max-call-stack.md)
|
8. [Maximum Call Stack Depth](safety/max-call-stack.md)
|
||||||
9. [Maximum Statement Depth](safety/max-stmt-depth.md)
|
9. [Maximum Statement Depth](safety/max-stmt-depth.md)
|
||||||
7. [Advanced Topics](advanced.md)
|
7. [Advanced Topics](advanced.md)
|
||||||
1. [Script Optimization](engine/optimize/index.md)
|
1. [Object-Oriented Programming (OOP)](language/oop.md)
|
||||||
|
2. [Script Optimization](engine/optimize/index.md)
|
||||||
1. [Optimization Levels](engine/optimize/optimize-levels.md)
|
1. [Optimization Levels](engine/optimize/optimize-levels.md)
|
||||||
2. [Re-Optimize an AST](engine/optimize/reoptimize.md)
|
2. [Re-Optimize an AST](engine/optimize/reoptimize.md)
|
||||||
3. [Eager Function Evaluation](engine/optimize/eager.md)
|
3. [Eager Function Evaluation](engine/optimize/eager.md)
|
||||||
4. [Side-Effect Considerations](engine/optimize/side-effects.md)
|
4. [Side-Effect Considerations](engine/optimize/side-effects.md)
|
||||||
5. [Volatility Considerations](engine/optimize/volatility.md)
|
5. [Volatility Considerations](engine/optimize/volatility.md)
|
||||||
6. [Subtle Semantic Changes](engine/optimize/semantics.md)
|
6. [Subtle Semantic Changes](engine/optimize/semantics.md)
|
||||||
2. [Eval Statement](language/eval.md)
|
3. [Eval Statement](language/eval.md)
|
||||||
8. [Appendix](appendix/index.md)
|
8. [Appendix](appendix/index.md)
|
||||||
1. [Keywords](appendix/keywords.md)
|
1. [Keywords](appendix/keywords.md)
|
||||||
2. [Operators](appendix/operators.md)
|
2. [Operators](appendix/operators.md)
|
||||||
|
@ -37,6 +37,8 @@ Dynamic
|
|||||||
|
|
||||||
* Dynamic dispatch via [function pointers].
|
* Dynamic dispatch via [function pointers].
|
||||||
|
|
||||||
|
* Some support for [OOP].
|
||||||
|
|
||||||
Safe
|
Safe
|
||||||
----
|
----
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@ It doesn't attempt to be a new language. For example:
|
|||||||
* No structures/records - define your types in Rust instead; Rhai can seamlessly work with _any Rust type_.
|
* No structures/records - define your types in Rust instead; Rhai can seamlessly work with _any Rust type_.
|
||||||
|
|
||||||
There is, however, a built-in [object map] type which is adequate for most uses.
|
There is, however, a built-in [object map] type which is adequate for most uses.
|
||||||
|
It is possible to simulate [OOP] by storing [function pointers] in [object map] properties, turning them into _methods_.
|
||||||
|
|
||||||
* No first-class functions - Code your functions in Rust instead, and register them with Rhai.
|
* No first-class functions - Code your functions in Rust instead, and register them with Rhai.
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ Keywords List
|
|||||||
{{#include ../links.md}}
|
{{#include ../links.md}}
|
||||||
|
|
||||||
| Keyword | Description | Not available under |
|
| Keyword | Description | Not available under |
|
||||||
| :-------------------: | --------------------------------------- | ------------------- |
|
| :-------------------: | ---------------------------------------- | :-----------------: |
|
||||||
| `true` | Boolean true literal | |
|
| `true` | Boolean true literal | |
|
||||||
| `false` | Boolean false literal | |
|
| `false` | Boolean false literal | |
|
||||||
| `let` | Variable declaration | |
|
| `let` | Variable declaration | |
|
||||||
@ -19,13 +19,14 @@ Keywords List
|
|||||||
| `break` | Loop breaking | |
|
| `break` | Loop breaking | |
|
||||||
| `return` | Return value | |
|
| `return` | Return value | |
|
||||||
| `throw` | Throw exception | |
|
| `throw` | Throw exception | |
|
||||||
| `private` | Mark function private | [`no_function`] |
|
|
||||||
| `import` | Import module | [`no_module`] |
|
| `import` | Import module | [`no_module`] |
|
||||||
| `export` | Export variable | [`no_module`] |
|
| `export` | Export variable | [`no_module`] |
|
||||||
| `as` | Alias for variable export | [`no_module`] |
|
| `as` | Alias for variable export | [`no_module`] |
|
||||||
|
| `private` | Mark function private | [`no_function`] |
|
||||||
| `fn` (lower-case `f`) | Function definition | [`no_function`] |
|
| `fn` (lower-case `f`) | Function definition | [`no_function`] |
|
||||||
| `Fn` (capital `F`) | Function to create a [function pointer] | [`no_function`] |
|
| `Fn` (capital `F`) | Function to create a [function pointer] | [`no_function`] |
|
||||||
| `call` | Call a [function pointer] | [`no_function`] |
|
| `call` | Call a [function pointer] | [`no_function`] |
|
||||||
|
| `this` | Reference to base object for method call | [`no_function`] |
|
||||||
| `type_of` | Get type name of value | |
|
| `type_of` | Get type name of value | |
|
||||||
| `print` | Print value | |
|
| `print` | Print value | |
|
||||||
| `debug` | Print value in debug format | |
|
| `debug` | Print value in debug format | |
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"version": "0.15.1",
|
"version": "0.16.0",
|
||||||
"rootUrl": "",
|
"rootUrl": "",
|
||||||
"rootUrlX": "/rhai"
|
"rootUrlX": "/rhai"
|
||||||
}
|
}
|
@ -1,6 +1,8 @@
|
|||||||
Function Pointers
|
Function Pointers
|
||||||
=================
|
=================
|
||||||
|
|
||||||
|
{{#include ../links.md}}
|
||||||
|
|
||||||
It is possible to store a _function pointer_ in a variable just like a normal value.
|
It is possible to store a _function pointer_ in a variable just like a normal value.
|
||||||
In fact, internally a function pointer simply stores the _name_ of the function as a string.
|
In fact, internally a function pointer simply stores the _name_ of the function as a string.
|
||||||
|
|
||||||
|
@ -53,29 +53,6 @@ fn foo() { x } // <- syntax error: variable 'x' doesn't exist
|
|||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
Arguments Passed by Value
|
|
||||||
------------------------
|
|
||||||
|
|
||||||
Functions defined in script always take [`Dynamic`] parameters (i.e. the parameter can be of any type).
|
|
||||||
Therefore, functions with the same name and same _number_ of parameters are equivalent.
|
|
||||||
|
|
||||||
It is important to remember that all arguments are passed by _value_, so all Rhai script-defined functions
|
|
||||||
are _pure_ (i.e. they never modify their arguments).
|
|
||||||
Any update to an argument will **not** be reflected back to the caller.
|
|
||||||
|
|
||||||
This can introduce subtle bugs, if not careful, especially when using the _method-call_ style.
|
|
||||||
|
|
||||||
```rust
|
|
||||||
fn change(s) { // 's' is passed by value
|
|
||||||
s = 42; // only a COPY of 's' is changed
|
|
||||||
}
|
|
||||||
|
|
||||||
let x = 500;
|
|
||||||
x.change(); // de-sugars to 'change(x)'
|
|
||||||
x == 500; // 'x' is NOT changed!
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
Global Definitions Only
|
Global Definitions Only
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
@ -106,3 +83,47 @@ A function does not need to be defined prior to being used in a script;
|
|||||||
a statement in the script can freely call a function defined afterwards.
|
a statement in the script can freely call a function defined afterwards.
|
||||||
|
|
||||||
This is similar to Rust and many other modern languages, such as JavaScript's `function` keyword.
|
This is similar to Rust and many other modern languages, such as JavaScript's `function` keyword.
|
||||||
|
|
||||||
|
|
||||||
|
Arguments Passed by Value
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
Functions defined in script always take [`Dynamic`] parameters (i.e. the parameter can be of any type).
|
||||||
|
Therefore, functions with the same name and same _number_ of parameters are equivalent.
|
||||||
|
|
||||||
|
It is important to remember that all arguments are passed by _value_, so all Rhai script-defined functions
|
||||||
|
are _pure_ (i.e. they never modify their arguments).
|
||||||
|
Any update to an argument will **not** be reflected back to the caller.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
fn change(s) { // 's' is passed by value
|
||||||
|
s = 42; // only a COPY of 's' is changed
|
||||||
|
}
|
||||||
|
|
||||||
|
let x = 500;
|
||||||
|
|
||||||
|
change(x);
|
||||||
|
|
||||||
|
x == 500; // 'x' is NOT changed!
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
`this` - Simulating an Object Method
|
||||||
|
-----------------------------------
|
||||||
|
|
||||||
|
Functions can also be called in method-call style. When this is the case, the keyword '`this`'
|
||||||
|
binds to the object in the method call and can be changed.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
fn change() { // not that the object does not need a parameter
|
||||||
|
this = 42; // 'this' binds to the object in method-call
|
||||||
|
}
|
||||||
|
|
||||||
|
let x = 500;
|
||||||
|
|
||||||
|
x.change(); // call 'change' in method-call style, 'this' binds to 'x'
|
||||||
|
|
||||||
|
x == 42; // 'x' is changed!
|
||||||
|
|
||||||
|
change(); // <- error: `this` is unbounded
|
||||||
|
```
|
||||||
|
@ -57,13 +57,13 @@ if the first one already proves the condition wrong.
|
|||||||
Single boolean operators `&` and `|` always evaluate both operands.
|
Single boolean operators `&` and `|` always evaluate both operands.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
this() || that(); // that() is not evaluated if this() is true
|
a() || b(); // b() is not evaluated if a() is true
|
||||||
|
|
||||||
this() && that(); // that() is not evaluated if this() is false
|
a() && b(); // b() is not evaluated if a() is false
|
||||||
|
|
||||||
this() | that(); // both this() and that() are evaluated
|
a() | b(); // both a() and b() are evaluated
|
||||||
|
|
||||||
this() & that(); // both this() and that() are evaluated
|
a() & b(); // both a() and b() are evaluated
|
||||||
```
|
```
|
||||||
|
|
||||||
Compound Assignment Operators
|
Compound Assignment Operators
|
||||||
|
23
doc/src/language/object-maps-oop.md
Normal file
23
doc/src/language/object-maps-oop.md
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
Special Support for OOP via Object Maps
|
||||||
|
======================================
|
||||||
|
|
||||||
|
{{#include ../links.md}}
|
||||||
|
|
||||||
|
[Object maps] can be used to simulate object-oriented programming ([OOP]) by storing data
|
||||||
|
as properties and methods as properties holding [function pointers].
|
||||||
|
|
||||||
|
If an [object map]'s property holding a [function pointer] is called in method-call style,
|
||||||
|
it calls the function referenced by the [function pointer].
|
||||||
|
|
||||||
|
```rust
|
||||||
|
fn do_action(x) { print(this + x); } // 'this' binds to the object when called
|
||||||
|
|
||||||
|
let obj = #{
|
||||||
|
data: 40,
|
||||||
|
action: Fn("do_action") // 'action' holds a function pointer to 'do_action'
|
||||||
|
};
|
||||||
|
|
||||||
|
obj.action(2); // prints 42
|
||||||
|
|
||||||
|
obj.action.call(2); // <- the above de-sugars to this
|
||||||
|
```
|
44
doc/src/language/oop.md
Normal file
44
doc/src/language/oop.md
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
Object-Oriented Programming (OOP)
|
||||||
|
================================
|
||||||
|
|
||||||
|
{{#include ../links.md}}
|
||||||
|
|
||||||
|
Rhai does not have _objects_ per se, but it is possible to _simulate_ object-oriented programming.
|
||||||
|
|
||||||
|
|
||||||
|
Use [Object Maps] to Simulate OOP
|
||||||
|
--------------------------------
|
||||||
|
|
||||||
|
Rhai's [object maps] has [special support for OOP]({{rootUrl}}/language/object-maps-oop.md).
|
||||||
|
|
||||||
|
| Rhai concept | Maps to OOP |
|
||||||
|
| ----------------------------------------------------- | :---------: |
|
||||||
|
| [Object maps] | objects |
|
||||||
|
| [Object map] properties holding values | properties |
|
||||||
|
| [Object map] properties that hold [function pointers] | methods |
|
||||||
|
|
||||||
|
|
||||||
|
Examples
|
||||||
|
--------
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// Define the object
|
||||||
|
let obj = #{
|
||||||
|
data: 0,
|
||||||
|
increment: Fn("add"), // when called, 'this' binds to 'obj'
|
||||||
|
update: Fn("update"), // when called, 'this' binds to 'obj'
|
||||||
|
action: Fn("action") // 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
|
||||||
|
obj.increment(1);
|
||||||
|
obj.action(); // prints 1
|
||||||
|
|
||||||
|
obj.update(42);
|
||||||
|
obj.action(); // prints 42
|
||||||
|
```
|
@ -77,6 +77,9 @@
|
|||||||
|
|
||||||
[`eval`]: {{rootUrl}}/language/eval.md
|
[`eval`]: {{rootUrl}}/language/eval.md
|
||||||
|
|
||||||
|
[OOP]: {{rootUrl}}/language/oop.md
|
||||||
|
|
||||||
|
|
||||||
[maximum statement depth]: {{rootUrl}}/safety/max-stmt-depth.md
|
[maximum statement depth]: {{rootUrl}}/safety/max-stmt-depth.md
|
||||||
[maximum call stack depth]: {{rootUrl}}/safety/max-call-stack.md
|
[maximum call stack depth]: {{rootUrl}}/safety/max-call-stack.md
|
||||||
[maximum number of operations]: {{rootUrl}}/safety/max-operations.md
|
[maximum number of operations]: {{rootUrl}}/safety/max-operations.md
|
||||||
|
@ -97,16 +97,17 @@ println!("result: {}", result.field); // prints 42
|
|||||||
Method-Call Style vs. Function-Call Style
|
Method-Call Style vs. Function-Call Style
|
||||||
----------------------------------------
|
----------------------------------------
|
||||||
|
|
||||||
In fact, any function with a first argument that is a `&mut` reference can be used
|
Any function with a first argument that is a `&mut` reference can be used
|
||||||
as method calls because internally they are the same thing: methods on a type is
|
as method calls because internally they are the same thing: methods on a type is
|
||||||
implemented as a functions taking a `&mut` first argument.
|
implemented as a functions taking a `&mut` first argument.
|
||||||
|
This design is similar to Rust.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
fn foo(ts: &mut TestStruct) -> i64 {
|
fn foo(ts: &mut TestStruct) -> i64 {
|
||||||
ts.field
|
ts.field
|
||||||
}
|
}
|
||||||
|
|
||||||
engine.register_fn("foo", foo); // register ad hoc function with correct signature
|
engine.register_fn("foo", foo); // register a Rust native function
|
||||||
|
|
||||||
let result = engine.eval::<i64>(
|
let result = engine.eval::<i64>(
|
||||||
"let x = new_ts(); x.foo()" // 'foo' can be called like a method on 'x'
|
"let x = new_ts(); x.foo()" // 'foo' can be called like a method on 'x'
|
||||||
|
15
src/api.rs
15
src/api.rs
@ -996,7 +996,7 @@ impl Engine {
|
|||||||
ast.statements()
|
ast.statements()
|
||||||
.iter()
|
.iter()
|
||||||
.try_fold(().into(), |_, stmt| {
|
.try_fold(().into(), |_, stmt| {
|
||||||
self.eval_stmt(scope, &mut state, ast.lib(), stmt, 0)
|
self.eval_stmt(scope, &mut state, ast.lib(), &mut None, stmt, 0)
|
||||||
})
|
})
|
||||||
.or_else(|err| match *err {
|
.or_else(|err| match *err {
|
||||||
EvalAltResult::Return(out, _) => Ok(out),
|
EvalAltResult::Return(out, _) => Ok(out),
|
||||||
@ -1062,7 +1062,7 @@ impl Engine {
|
|||||||
ast.statements()
|
ast.statements()
|
||||||
.iter()
|
.iter()
|
||||||
.try_fold(().into(), |_, stmt| {
|
.try_fold(().into(), |_, stmt| {
|
||||||
self.eval_stmt(scope, &mut state, ast.lib(), stmt, 0)
|
self.eval_stmt(scope, &mut state, ast.lib(), &mut None, stmt, 0)
|
||||||
})
|
})
|
||||||
.map_or_else(
|
.map_or_else(
|
||||||
|err| match *err {
|
|err| match *err {
|
||||||
@ -1203,7 +1203,16 @@ impl Engine {
|
|||||||
let mut state = State::new();
|
let mut state = State::new();
|
||||||
let args = args.as_mut();
|
let args = args.as_mut();
|
||||||
|
|
||||||
self.call_script_fn(scope, &mut state, ast.lib(), name, fn_def, args, 0)
|
self.call_script_fn(
|
||||||
|
scope,
|
||||||
|
&mut state,
|
||||||
|
ast.lib(),
|
||||||
|
&mut None,
|
||||||
|
name,
|
||||||
|
fn_def,
|
||||||
|
args,
|
||||||
|
0,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Optimize the `AST` with constants defined in an external Scope.
|
/// Optimize the `AST` with constants defined in an external Scope.
|
||||||
|
339
src/engine.rs
339
src/engine.rs
@ -4,7 +4,6 @@ use crate::any::{Dynamic, Union, Variant};
|
|||||||
use crate::calc_fn_hash;
|
use crate::calc_fn_hash;
|
||||||
use crate::error::ParseErrorType;
|
use crate::error::ParseErrorType;
|
||||||
use crate::fn_native::{CallableFunction, Callback, FnCallArgs, FnPtr};
|
use crate::fn_native::{CallableFunction, Callback, FnCallArgs, FnPtr};
|
||||||
use crate::fn_register::RegisterResultFn;
|
|
||||||
use crate::module::{resolvers, Module, ModuleResolver};
|
use crate::module::{resolvers, Module, ModuleResolver};
|
||||||
use crate::optimize::OptimizationLevel;
|
use crate::optimize::OptimizationLevel;
|
||||||
use crate::packages::{Package, PackageLibrary, PackagesCollection, StandardPackage};
|
use crate::packages::{Package, PackageLibrary, PackagesCollection, StandardPackage};
|
||||||
@ -12,7 +11,7 @@ use crate::parser::{Expr, FnAccess, ImmutableString, ReturnType, ScriptFnDef, St
|
|||||||
use crate::r#unsafe::{unsafe_cast_var_name_to_lifetime, unsafe_mut_cast_to_lifetime};
|
use crate::r#unsafe::{unsafe_cast_var_name_to_lifetime, unsafe_mut_cast_to_lifetime};
|
||||||
use crate::result::EvalAltResult;
|
use crate::result::EvalAltResult;
|
||||||
use crate::scope::{EntryType as ScopeEntryType, Scope};
|
use crate::scope::{EntryType as ScopeEntryType, Scope};
|
||||||
use crate::token::{Position, Token};
|
use crate::token::Position;
|
||||||
use crate::utils::StaticVec;
|
use crate::utils::StaticVec;
|
||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
@ -73,6 +72,7 @@ pub const KEYWORD_DEBUG: &str = "debug";
|
|||||||
pub const KEYWORD_TYPE_OF: &str = "type_of";
|
pub const KEYWORD_TYPE_OF: &str = "type_of";
|
||||||
pub const KEYWORD_EVAL: &str = "eval";
|
pub const KEYWORD_EVAL: &str = "eval";
|
||||||
pub const KEYWORD_FN_PTR_CALL: &str = "call";
|
pub const KEYWORD_FN_PTR_CALL: &str = "call";
|
||||||
|
pub const KEYWORD_THIS: &str = "this";
|
||||||
pub const FN_TO_STRING: &str = "to_string";
|
pub const FN_TO_STRING: &str = "to_string";
|
||||||
pub const FN_GET: &str = "get$";
|
pub const FN_GET: &str = "get$";
|
||||||
pub const FN_SET: &str = "set$";
|
pub const FN_SET: &str = "set$";
|
||||||
@ -189,7 +189,7 @@ impl<T: Into<Dynamic>> From<T> for Target<'_> {
|
|||||||
///
|
///
|
||||||
/// This type uses some unsafe code, mainly for avoiding cloning of local variable names via
|
/// This type uses some unsafe code, mainly for avoiding cloning of local variable names via
|
||||||
/// direct lifetime casting.
|
/// direct lifetime casting.
|
||||||
#[derive(Debug, Eq, PartialEq, Hash, Clone, Default)]
|
#[derive(Debug, Clone, Eq, PartialEq, Hash, Default)]
|
||||||
pub struct State {
|
pub struct State {
|
||||||
/// Normally, access to variables are parsed with a relative offset into the scope to avoid a lookup.
|
/// Normally, access to variables are parsed with a relative offset into the scope to avoid a lookup.
|
||||||
/// In some situation, e.g. after running an `eval` statement, subsequent offsets become mis-aligned.
|
/// In some situation, e.g. after running an `eval` statement, subsequent offsets become mis-aligned.
|
||||||
@ -393,13 +393,23 @@ fn default_print(s: &str) {
|
|||||||
fn search_scope<'s, 'a>(
|
fn search_scope<'s, 'a>(
|
||||||
scope: &'s mut Scope,
|
scope: &'s mut Scope,
|
||||||
state: &mut State,
|
state: &mut State,
|
||||||
|
this_ptr: &'s mut Option<&mut Dynamic>,
|
||||||
expr: &'a Expr,
|
expr: &'a Expr,
|
||||||
) -> Result<(&'s mut Dynamic, &'a str, ScopeEntryType, Position), Box<EvalAltResult>> {
|
) -> Result<(&'s mut Dynamic, &'a str, ScopeEntryType, Position), Box<EvalAltResult>> {
|
||||||
let ((name, pos), modules, hash_var, index) = match expr {
|
let ((name, pos), modules, hash_var, index) = match expr {
|
||||||
Expr::Variable(x) => x.as_ref(),
|
Expr::Variable(v) => v.as_ref(),
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Check if the variable is `this`
|
||||||
|
if name == KEYWORD_THIS {
|
||||||
|
if let Some(val) = this_ptr {
|
||||||
|
return Ok(((*val).into(), KEYWORD_THIS, ScopeEntryType::Normal, *pos));
|
||||||
|
} else {
|
||||||
|
return Err(Box::new(EvalAltResult::ErrorUnboundedThis(*pos)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Check if it is qualified
|
// Check if it is qualified
|
||||||
if let Some(modules) = modules.as_ref() {
|
if let Some(modules) = modules.as_ref() {
|
||||||
// Qualified - check if the root module is directly indexed
|
// Qualified - check if the root module is directly indexed
|
||||||
@ -654,6 +664,7 @@ impl Engine {
|
|||||||
(hash_fn, hash_script): (u64, u64),
|
(hash_fn, hash_script): (u64, u64),
|
||||||
args: &mut FnCallArgs,
|
args: &mut FnCallArgs,
|
||||||
is_ref: bool,
|
is_ref: bool,
|
||||||
|
is_method: bool,
|
||||||
def_val: Option<&Dynamic>,
|
def_val: Option<&Dynamic>,
|
||||||
level: usize,
|
level: usize,
|
||||||
) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
|
) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
|
||||||
@ -724,9 +735,9 @@ impl Engine {
|
|||||||
.or_else(|| self.packages.get_fn(hash_fn));
|
.or_else(|| self.packages.get_fn(hash_fn));
|
||||||
|
|
||||||
if let Some(func) = func {
|
if let Some(func) = func {
|
||||||
// Calling pure function in method-call?
|
// Calling pure function but the first argument is a reference?
|
||||||
normalize_first_arg(
|
normalize_first_arg(
|
||||||
(func.is_pure() || func.is_script()) && is_ref,
|
is_ref && (func.is_pure() || (func.is_script() && !is_method)),
|
||||||
&mut this_copy,
|
&mut this_copy,
|
||||||
&mut old_this_ptr,
|
&mut old_this_ptr,
|
||||||
args,
|
args,
|
||||||
@ -735,13 +746,33 @@ impl Engine {
|
|||||||
if func.is_script() {
|
if func.is_script() {
|
||||||
// Run scripted function
|
// Run scripted function
|
||||||
let fn_def = func.get_fn_def();
|
let fn_def = func.get_fn_def();
|
||||||
let result =
|
|
||||||
self.call_script_fn(scope, state, lib, fn_name, fn_def, args, level)?;
|
// Method call of script function - map first argument to `this`
|
||||||
|
if is_method {
|
||||||
|
let (first, rest) = args.split_at_mut(1);
|
||||||
|
return Ok((
|
||||||
|
self.call_script_fn(
|
||||||
|
scope,
|
||||||
|
state,
|
||||||
|
lib,
|
||||||
|
&mut Some(first[0]),
|
||||||
|
fn_name,
|
||||||
|
fn_def,
|
||||||
|
rest,
|
||||||
|
level,
|
||||||
|
)?,
|
||||||
|
false,
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
let result = self.call_script_fn(
|
||||||
|
scope, state, lib, &mut None, fn_name, fn_def, args, level,
|
||||||
|
)?;
|
||||||
|
|
||||||
// Restore the original reference
|
// Restore the original reference
|
||||||
restore_first_arg(old_this_ptr, args);
|
restore_first_arg(old_this_ptr, args);
|
||||||
|
|
||||||
return Ok((result, false));
|
return Ok((result, false));
|
||||||
|
};
|
||||||
} else {
|
} else {
|
||||||
// Run external function
|
// Run external function
|
||||||
let result = func.get_native_fn()(self, args)?;
|
let result = func.get_native_fn()(self, args)?;
|
||||||
@ -860,6 +891,7 @@ impl Engine {
|
|||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
state: &mut State,
|
state: &mut State,
|
||||||
lib: &Module,
|
lib: &Module,
|
||||||
|
this_ptr: &mut Option<&mut Dynamic>,
|
||||||
fn_name: &str,
|
fn_name: &str,
|
||||||
fn_def: &ScriptFnDef,
|
fn_def: &ScriptFnDef,
|
||||||
args: &mut FnCallArgs,
|
args: &mut FnCallArgs,
|
||||||
@ -885,7 +917,7 @@ impl Engine {
|
|||||||
|
|
||||||
// Evaluate the function at one higher level of call depth
|
// Evaluate the function at one higher level of call depth
|
||||||
let result = self
|
let result = self
|
||||||
.eval_stmt(scope, state, lib, &fn_def.body, level + 1)
|
.eval_stmt(scope, state, lib, this_ptr, &fn_def.body, level + 1)
|
||||||
.or_else(|err| match *err {
|
.or_else(|err| match *err {
|
||||||
// Convert return statement to return value
|
// Convert return statement to return value
|
||||||
EvalAltResult::Return(x, _) => Ok(x),
|
EvalAltResult::Return(x, _) => Ok(x),
|
||||||
@ -942,6 +974,7 @@ impl Engine {
|
|||||||
hash_script: u64,
|
hash_script: u64,
|
||||||
args: &mut FnCallArgs,
|
args: &mut FnCallArgs,
|
||||||
is_ref: bool,
|
is_ref: bool,
|
||||||
|
is_method: bool,
|
||||||
def_val: Option<&Dynamic>,
|
def_val: Option<&Dynamic>,
|
||||||
level: usize,
|
level: usize,
|
||||||
) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
|
) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
|
||||||
@ -977,7 +1010,8 @@ impl Engine {
|
|||||||
_ => {
|
_ => {
|
||||||
let mut scope = Scope::new();
|
let mut scope = Scope::new();
|
||||||
self.call_fn_raw(
|
self.call_fn_raw(
|
||||||
&mut scope, state, lib, fn_name, hashes, args, is_ref, def_val, level,
|
&mut scope, state, lib, fn_name, hashes, args, is_ref, is_method, def_val,
|
||||||
|
level,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1027,6 +1061,7 @@ impl Engine {
|
|||||||
&self,
|
&self,
|
||||||
state: &mut State,
|
state: &mut State,
|
||||||
lib: &Module,
|
lib: &Module,
|
||||||
|
this_ptr: &mut Option<&mut Dynamic>,
|
||||||
target: &mut Target,
|
target: &mut Target,
|
||||||
rhs: &Expr,
|
rhs: &Expr,
|
||||||
idx_values: &mut StaticVec<Dynamic>,
|
idx_values: &mut StaticVec<Dynamic>,
|
||||||
@ -1060,11 +1095,12 @@ impl Engine {
|
|||||||
Expr::Dot(x) | Expr::Index(x) => {
|
Expr::Dot(x) | Expr::Index(x) => {
|
||||||
let (idx, expr, pos) = x.as_ref();
|
let (idx, expr, pos) = x.as_ref();
|
||||||
let idx_pos = idx.position();
|
let idx_pos = idx.position();
|
||||||
let this_ptr = &mut self
|
let obj_ptr = &mut self
|
||||||
.get_indexed_mut(state, lib, target, idx_val, idx_pos, false)?;
|
.get_indexed_mut(state, lib, target, idx_val, idx_pos, false, level)?;
|
||||||
|
|
||||||
self.eval_dot_index_chain_helper(
|
self.eval_dot_index_chain_helper(
|
||||||
state, lib, this_ptr, expr, idx_values, next_chain, level, new_val,
|
state, lib, this_ptr, obj_ptr, expr, idx_values, next_chain, level,
|
||||||
|
new_val,
|
||||||
)
|
)
|
||||||
.map_err(|err| EvalAltResult::new_position(err, *pos))
|
.map_err(|err| EvalAltResult::new_position(err, *pos))
|
||||||
}
|
}
|
||||||
@ -1072,15 +1108,16 @@ impl Engine {
|
|||||||
_ if new_val.is_some() => {
|
_ if new_val.is_some() => {
|
||||||
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) {
|
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(this_ptr) if this_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.unwrap()];
|
&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, None, 0,
|
state, lib, FN_IDX_SET, true, 0, args, is_ref, true, None,
|
||||||
|
level,
|
||||||
)
|
)
|
||||||
.or_else(|err| match *err {
|
.or_else(|err| match *err {
|
||||||
// If there is no index setter, no need to set it back because the indexer is read-only
|
// If there is no index setter, no need to set it back because the indexer is read-only
|
||||||
@ -1093,8 +1130,8 @@ impl Engine {
|
|||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
// Indexed value is a reference - update directly
|
// Indexed value is a reference - update directly
|
||||||
Ok(ref mut this_ptr) => {
|
Ok(ref mut obj_ptr) => {
|
||||||
this_ptr.set_value(new_val.unwrap()).map_err(|err| {
|
obj_ptr.set_value(new_val.unwrap()).map_err(|err| {
|
||||||
EvalAltResult::new_position(err, rhs.position())
|
EvalAltResult::new_position(err, rhs.position())
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
@ -1108,7 +1145,8 @@ impl Engine {
|
|||||||
];
|
];
|
||||||
|
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
state, lib, FN_IDX_SET, true, 0, args, is_ref, None, 0,
|
state, lib, FN_IDX_SET, true, 0, args, is_ref, true, None,
|
||||||
|
level,
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
// Error
|
// Error
|
||||||
@ -1119,7 +1157,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
// xxx[rhs]
|
// xxx[rhs]
|
||||||
_ => self
|
_ => self
|
||||||
.get_indexed_mut(state, lib, target, idx_val, pos, false)
|
.get_indexed_mut(state, lib, target, idx_val, pos, false, level)
|
||||||
.map(|v| (v.clone_into_dynamic(), false)),
|
.map(|v| (v.clone_into_dynamic(), false)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1138,29 +1176,55 @@ impl Engine {
|
|||||||
let idx = idx_val.downcast_mut::<StaticVec<Dynamic>>().unwrap();
|
let idx = idx_val.downcast_mut::<StaticVec<Dynamic>>().unwrap();
|
||||||
|
|
||||||
// Check if it is a FnPtr call
|
// Check if it is a FnPtr call
|
||||||
let (fn_name, mut arg_values, hash_fn, is_native) =
|
|
||||||
if name == KEYWORD_FN_PTR_CALL && obj.is::<FnPtr>() {
|
if name == KEYWORD_FN_PTR_CALL && obj.is::<FnPtr>() {
|
||||||
// Redirect function name
|
// Redirect function name
|
||||||
let name = obj.as_fn_name().unwrap();
|
let fn_name = obj.as_fn_name().unwrap();
|
||||||
// Recalculate hash
|
// Recalculate hash
|
||||||
let hash = calc_fn_hash(empty(), name, idx.len(), empty());
|
let hash = calc_fn_hash(empty(), fn_name, idx.len(), empty());
|
||||||
// Arguments are passed as-is
|
// Arguments are passed as-is
|
||||||
(name, idx.iter_mut().collect::<StaticVec<_>>(), hash, false)
|
let mut arg_values = idx.iter_mut().collect::<StaticVec<_>>();
|
||||||
} else {
|
|
||||||
// Attached object pointer in front of the arguments
|
|
||||||
(
|
|
||||||
name.as_ref(),
|
|
||||||
once(obj).chain(idx.iter_mut()).collect::<StaticVec<_>>(),
|
|
||||||
*hash,
|
|
||||||
*native,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
let args = arg_values.as_mut();
|
let args = arg_values.as_mut();
|
||||||
|
|
||||||
|
// Map it to name(args) in function-call style
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
state, lib, fn_name, is_native, hash_fn, args, is_ref, def_val, 0,
|
state, lib, fn_name, *native, hash, args, false, false,
|
||||||
|
def_val, level,
|
||||||
)
|
)
|
||||||
|
} else {
|
||||||
|
let mut fn_name = name.clone();
|
||||||
|
let mut redirected = None;
|
||||||
|
let mut hash = *hash;
|
||||||
|
|
||||||
|
// Check if it is a map method call in OOP style
|
||||||
|
if let Some(map) = obj.downcast_ref::<Map>() {
|
||||||
|
if let Some(val) = map.get(name.as_ref()) {
|
||||||
|
if let Some(f) = val.downcast_ref::<FnPtr>() {
|
||||||
|
// Remap the function name
|
||||||
|
redirected = Some(f.get_fn_name().clone());
|
||||||
|
fn_name = redirected.as_ref().unwrap().as_str().into();
|
||||||
|
|
||||||
|
// Recalculate the hash based on the new function name
|
||||||
|
hash = calc_fn_hash(
|
||||||
|
empty(),
|
||||||
|
fn_name.as_ref(),
|
||||||
|
idx.len(),
|
||||||
|
empty(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Attached object pointer in front of the arguments
|
||||||
|
let mut arg_values =
|
||||||
|
once(obj).chain(idx.iter_mut()).collect::<StaticVec<_>>();
|
||||||
|
let args = arg_values.as_mut();
|
||||||
|
let fn_name = fn_name.as_ref();
|
||||||
|
|
||||||
|
self.exec_fn_call(
|
||||||
|
state, lib, fn_name, *native, hash, args, is_ref, true,
|
||||||
|
def_val, level,
|
||||||
|
)
|
||||||
|
}
|
||||||
.map_err(|err| EvalAltResult::new_position(err, *pos))?
|
.map_err(|err| EvalAltResult::new_position(err, *pos))?
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1179,7 +1243,7 @@ impl Engine {
|
|||||||
let ((prop, _, _), pos) = x.as_ref();
|
let ((prop, _, _), pos) = x.as_ref();
|
||||||
let index = prop.clone().into();
|
let index = prop.clone().into();
|
||||||
let mut val =
|
let mut val =
|
||||||
self.get_indexed_mut(state, lib, target, index, *pos, true)?;
|
self.get_indexed_mut(state, lib, target, index, *pos, true, level)?;
|
||||||
|
|
||||||
val.set_value(new_val.unwrap())
|
val.set_value(new_val.unwrap())
|
||||||
.map_err(|err| EvalAltResult::new_position(err, rhs.position()))?;
|
.map_err(|err| EvalAltResult::new_position(err, rhs.position()))?;
|
||||||
@ -1189,7 +1253,8 @@ impl Engine {
|
|||||||
Expr::Property(x) if target.is::<Map>() => {
|
Expr::Property(x) if target.is::<Map>() => {
|
||||||
let ((prop, _, _), pos) = x.as_ref();
|
let ((prop, _, _), pos) = x.as_ref();
|
||||||
let index = prop.clone().into();
|
let index = prop.clone().into();
|
||||||
let val = self.get_indexed_mut(state, lib, target, index, *pos, false)?;
|
let val =
|
||||||
|
self.get_indexed_mut(state, lib, target, index, *pos, false, level)?;
|
||||||
|
|
||||||
Ok((val.clone_into_dynamic(), false))
|
Ok((val.clone_into_dynamic(), false))
|
||||||
}
|
}
|
||||||
@ -1197,7 +1262,9 @@ impl Engine {
|
|||||||
Expr::Property(x) if new_val.is_some() => {
|
Expr::Property(x) if new_val.is_some() => {
|
||||||
let ((_, _, setter), pos) = x.as_ref();
|
let ((_, _, setter), pos) = x.as_ref();
|
||||||
let mut args = [target.as_mut(), new_val.as_mut().unwrap()];
|
let mut args = [target.as_mut(), new_val.as_mut().unwrap()];
|
||||||
self.exec_fn_call(state, lib, setter, true, 0, &mut args, is_ref, None, 0)
|
self.exec_fn_call(
|
||||||
|
state, lib, setter, true, 0, &mut args, is_ref, true, None, level,
|
||||||
|
)
|
||||||
.map(|(v, _)| (v, true))
|
.map(|(v, _)| (v, true))
|
||||||
.map_err(|err| EvalAltResult::new_position(err, *pos))
|
.map_err(|err| EvalAltResult::new_position(err, *pos))
|
||||||
}
|
}
|
||||||
@ -1205,7 +1272,9 @@ impl Engine {
|
|||||||
Expr::Property(x) => {
|
Expr::Property(x) => {
|
||||||
let ((_, getter, _), pos) = x.as_ref();
|
let ((_, getter, _), pos) = x.as_ref();
|
||||||
let mut args = [target.as_mut()];
|
let mut args = [target.as_mut()];
|
||||||
self.exec_fn_call(state, lib, getter, true, 0, &mut args, is_ref, None, 0)
|
self.exec_fn_call(
|
||||||
|
state, lib, getter, true, 0, &mut args, is_ref, true, None, level,
|
||||||
|
)
|
||||||
.map(|(v, _)| (v, false))
|
.map(|(v, _)| (v, false))
|
||||||
.map_err(|err| EvalAltResult::new_position(err, *pos))
|
.map_err(|err| EvalAltResult::new_position(err, *pos))
|
||||||
}
|
}
|
||||||
@ -1216,13 +1285,14 @@ impl Engine {
|
|||||||
let mut val = if let Expr::Property(p) = prop {
|
let mut val = if let Expr::Property(p) = prop {
|
||||||
let ((prop, _, _), _) = p.as_ref();
|
let ((prop, _, _), _) = p.as_ref();
|
||||||
let index = prop.clone().into();
|
let index = prop.clone().into();
|
||||||
self.get_indexed_mut(state, lib, target, index, *pos, false)?
|
self.get_indexed_mut(state, lib, target, index, *pos, false, level)?
|
||||||
} else {
|
} else {
|
||||||
unreachable!();
|
unreachable!();
|
||||||
};
|
};
|
||||||
|
|
||||||
self.eval_dot_index_chain_helper(
|
self.eval_dot_index_chain_helper(
|
||||||
state, lib, &mut val, expr, idx_values, next_chain, level, new_val,
|
state, lib, this_ptr, &mut val, expr, idx_values, next_chain, level,
|
||||||
|
new_val,
|
||||||
)
|
)
|
||||||
.map_err(|err| EvalAltResult::new_position(err, *pos))
|
.map_err(|err| EvalAltResult::new_position(err, *pos))
|
||||||
}
|
}
|
||||||
@ -1234,7 +1304,9 @@ impl Engine {
|
|||||||
let (mut val, updated) = if let Expr::Property(p) = prop {
|
let (mut val, updated) = if let Expr::Property(p) = prop {
|
||||||
let ((_, getter, _), _) = p.as_ref();
|
let ((_, getter, _), _) = p.as_ref();
|
||||||
let args = &mut args[..1];
|
let args = &mut args[..1];
|
||||||
self.exec_fn_call(state, lib, getter, true, 0, args, is_ref, None, 0)
|
self.exec_fn_call(
|
||||||
|
state, lib, getter, true, 0, args, is_ref, true, None, level,
|
||||||
|
)
|
||||||
.map_err(|err| EvalAltResult::new_position(err, *pos))?
|
.map_err(|err| EvalAltResult::new_position(err, *pos))?
|
||||||
} else {
|
} else {
|
||||||
unreachable!();
|
unreachable!();
|
||||||
@ -1244,7 +1316,8 @@ impl Engine {
|
|||||||
|
|
||||||
let (result, may_be_changed) = self
|
let (result, may_be_changed) = self
|
||||||
.eval_dot_index_chain_helper(
|
.eval_dot_index_chain_helper(
|
||||||
state, lib, target, expr, idx_values, next_chain, level, new_val,
|
state, lib, this_ptr, target, expr, idx_values, next_chain, level,
|
||||||
|
new_val,
|
||||||
)
|
)
|
||||||
.map_err(|err| EvalAltResult::new_position(err, *pos))?;
|
.map_err(|err| EvalAltResult::new_position(err, *pos))?;
|
||||||
|
|
||||||
@ -1255,7 +1328,7 @@ impl Engine {
|
|||||||
// Re-use args because the first &mut parameter will not be consumed
|
// Re-use args because the first &mut parameter will not be consumed
|
||||||
args[1] = val;
|
args[1] = val;
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
state, lib, setter, true, 0, args, is_ref, None, 0,
|
state, lib, setter, true, 0, args, is_ref, true, None, level,
|
||||||
)
|
)
|
||||||
.or_else(|err| match *err {
|
.or_else(|err| match *err {
|
||||||
// If there is no setter, no need to feed it back because the property is read-only
|
// If there is no setter, no need to feed it back because the property is read-only
|
||||||
@ -1285,6 +1358,7 @@ impl Engine {
|
|||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
state: &mut State,
|
state: &mut State,
|
||||||
lib: &Module,
|
lib: &Module,
|
||||||
|
this_ptr: &mut Option<&mut Dynamic>,
|
||||||
expr: &Expr,
|
expr: &Expr,
|
||||||
level: usize,
|
level: usize,
|
||||||
new_val: Option<Dynamic>,
|
new_val: Option<Dynamic>,
|
||||||
@ -1297,30 +1371,33 @@ impl Engine {
|
|||||||
|
|
||||||
let idx_values = &mut StaticVec::new();
|
let idx_values = &mut StaticVec::new();
|
||||||
|
|
||||||
self.eval_indexed_chain(scope, state, lib, dot_rhs, idx_values, 0, level)?;
|
self.eval_indexed_chain(scope, state, lib, this_ptr, dot_rhs, idx_values, 0, level)?;
|
||||||
|
|
||||||
match dot_lhs {
|
match dot_lhs {
|
||||||
// id.??? or id[???]
|
// id.??? or id[???]
|
||||||
Expr::Variable(_) => {
|
Expr::Variable(x) => {
|
||||||
let (target, name, typ, pos) = search_scope(scope, state, dot_lhs)?;
|
let (var_name, var_pos) = &x.0;
|
||||||
|
|
||||||
self.inc_operations(state)
|
self.inc_operations(state)
|
||||||
.map_err(|err| EvalAltResult::new_position(err, pos))?;
|
.map_err(|err| EvalAltResult::new_position(err, *var_pos))?;
|
||||||
|
|
||||||
|
let (target, _, typ, pos) = search_scope(scope, state, this_ptr, dot_lhs)?;
|
||||||
|
|
||||||
// Constants cannot be modified
|
// Constants cannot be modified
|
||||||
match typ {
|
match typ {
|
||||||
ScopeEntryType::Module => unreachable!(),
|
ScopeEntryType::Module => unreachable!(),
|
||||||
ScopeEntryType::Constant if new_val.is_some() => {
|
ScopeEntryType::Constant if new_val.is_some() => {
|
||||||
return Err(Box::new(EvalAltResult::ErrorAssignmentToConstant(
|
return Err(Box::new(EvalAltResult::ErrorAssignmentToConstant(
|
||||||
name.to_string(),
|
var_name.to_string(),
|
||||||
pos,
|
pos,
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
ScopeEntryType::Constant | ScopeEntryType::Normal => (),
|
ScopeEntryType::Constant | ScopeEntryType::Normal => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
let this_ptr = &mut target.into();
|
let obj_ptr = &mut target.into();
|
||||||
self.eval_dot_index_chain_helper(
|
self.eval_dot_index_chain_helper(
|
||||||
state, lib, this_ptr, dot_rhs, idx_values, chain_type, level, new_val,
|
state, lib, &mut None, obj_ptr, dot_rhs, idx_values, chain_type, level, new_val,
|
||||||
)
|
)
|
||||||
.map(|(v, _)| v)
|
.map(|(v, _)| v)
|
||||||
.map_err(|err| EvalAltResult::new_position(err, *op_pos))
|
.map_err(|err| EvalAltResult::new_position(err, *op_pos))
|
||||||
@ -1333,10 +1410,10 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
// {expr}.??? or {expr}[???]
|
// {expr}.??? or {expr}[???]
|
||||||
expr => {
|
expr => {
|
||||||
let val = self.eval_expr(scope, state, lib, expr, level)?;
|
let val = self.eval_expr(scope, state, lib, this_ptr, expr, level)?;
|
||||||
let this_ptr = &mut val.into();
|
let obj_ptr = &mut val.into();
|
||||||
self.eval_dot_index_chain_helper(
|
self.eval_dot_index_chain_helper(
|
||||||
state, lib, this_ptr, dot_rhs, idx_values, chain_type, level, new_val,
|
state, lib, this_ptr, obj_ptr, dot_rhs, idx_values, chain_type, level, new_val,
|
||||||
)
|
)
|
||||||
.map(|(v, _)| v)
|
.map(|(v, _)| v)
|
||||||
.map_err(|err| EvalAltResult::new_position(err, *op_pos))
|
.map_err(|err| EvalAltResult::new_position(err, *op_pos))
|
||||||
@ -1354,6 +1431,7 @@ impl Engine {
|
|||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
state: &mut State,
|
state: &mut State,
|
||||||
lib: &Module,
|
lib: &Module,
|
||||||
|
this_ptr: &mut Option<&mut Dynamic>,
|
||||||
expr: &Expr,
|
expr: &Expr,
|
||||||
idx_values: &mut StaticVec<Dynamic>,
|
idx_values: &mut StaticVec<Dynamic>,
|
||||||
size: usize,
|
size: usize,
|
||||||
@ -1364,9 +1442,10 @@ impl Engine {
|
|||||||
|
|
||||||
match expr {
|
match expr {
|
||||||
Expr::FnCall(x) if x.1.is_none() => {
|
Expr::FnCall(x) if x.1.is_none() => {
|
||||||
let arg_values =
|
let arg_values = x
|
||||||
x.3.iter()
|
.3
|
||||||
.map(|arg_expr| self.eval_expr(scope, state, lib, arg_expr, level))
|
.iter()
|
||||||
|
.map(|arg_expr| self.eval_expr(scope, state, lib, this_ptr, arg_expr, level))
|
||||||
.collect::<Result<StaticVec<Dynamic>, _>>()?;
|
.collect::<Result<StaticVec<Dynamic>, _>>()?;
|
||||||
|
|
||||||
idx_values.push(Dynamic::from(arg_values));
|
idx_values.push(Dynamic::from(arg_values));
|
||||||
@ -1379,15 +1458,15 @@ impl Engine {
|
|||||||
// Evaluate in left-to-right order
|
// Evaluate in left-to-right order
|
||||||
let lhs_val = match lhs {
|
let lhs_val = match lhs {
|
||||||
Expr::Property(_) => Default::default(), // Store a placeholder in case of a property
|
Expr::Property(_) => Default::default(), // Store a placeholder in case of a property
|
||||||
_ => self.eval_expr(scope, state, lib, lhs, level)?,
|
_ => self.eval_expr(scope, state, lib, this_ptr, lhs, level)?,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Push in reverse order
|
// Push in reverse order
|
||||||
self.eval_indexed_chain(scope, state, lib, rhs, idx_values, size, level)?;
|
self.eval_indexed_chain(scope, state, lib, this_ptr, rhs, idx_values, size, level)?;
|
||||||
|
|
||||||
idx_values.push(lhs_val);
|
idx_values.push(lhs_val);
|
||||||
}
|
}
|
||||||
_ => idx_values.push(self.eval_expr(scope, state, lib, expr, level)?),
|
_ => idx_values.push(self.eval_expr(scope, state, lib, this_ptr, expr, level)?),
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -1403,6 +1482,7 @@ impl Engine {
|
|||||||
mut idx: Dynamic,
|
mut idx: Dynamic,
|
||||||
idx_pos: Position,
|
idx_pos: Position,
|
||||||
create: bool,
|
create: bool,
|
||||||
|
level: usize,
|
||||||
) -> Result<Target<'a>, Box<EvalAltResult>> {
|
) -> Result<Target<'a>, Box<EvalAltResult>> {
|
||||||
self.inc_operations(state)?;
|
self.inc_operations(state)?;
|
||||||
|
|
||||||
@ -1477,7 +1557,9 @@ impl Engine {
|
|||||||
_ => {
|
_ => {
|
||||||
let type_name = self.map_type_name(val.type_name());
|
let type_name = self.map_type_name(val.type_name());
|
||||||
let args = &mut [val, &mut idx];
|
let args = &mut [val, &mut idx];
|
||||||
self.exec_fn_call(state, lib, FN_IDX_GET, true, 0, args, is_ref, None, 0)
|
self.exec_fn_call(
|
||||||
|
state, lib, FN_IDX_GET, true, 0, args, is_ref, true, None, level,
|
||||||
|
)
|
||||||
.map(|(v, _)| v.into())
|
.map(|(v, _)| v.into())
|
||||||
.map_err(|_| {
|
.map_err(|_| {
|
||||||
Box::new(EvalAltResult::ErrorIndexingType(
|
Box::new(EvalAltResult::ErrorIndexingType(
|
||||||
@ -1501,6 +1583,7 @@ impl Engine {
|
|||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
state: &mut State,
|
state: &mut State,
|
||||||
lib: &Module,
|
lib: &Module,
|
||||||
|
this_ptr: &mut Option<&mut Dynamic>,
|
||||||
lhs: &Expr,
|
lhs: &Expr,
|
||||||
rhs: &Expr,
|
rhs: &Expr,
|
||||||
level: usize,
|
level: usize,
|
||||||
@ -1508,8 +1591,8 @@ impl Engine {
|
|||||||
self.inc_operations(state)
|
self.inc_operations(state)
|
||||||
.map_err(|err| EvalAltResult::new_position(err, rhs.position()))?;
|
.map_err(|err| EvalAltResult::new_position(err, rhs.position()))?;
|
||||||
|
|
||||||
let lhs_value = self.eval_expr(scope, state, lib, lhs, level)?;
|
let lhs_value = self.eval_expr(scope, state, lib, this_ptr, lhs, level)?;
|
||||||
let rhs_value = self.eval_expr(scope, state, lib, rhs, level)?;
|
let rhs_value = self.eval_expr(scope, state, lib, this_ptr, rhs, level)?;
|
||||||
|
|
||||||
match rhs_value {
|
match rhs_value {
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
@ -1531,7 +1614,8 @@ impl Engine {
|
|||||||
|
|
||||||
let (r, _) = self
|
let (r, _) = self
|
||||||
.call_fn_raw(
|
.call_fn_raw(
|
||||||
&mut scope, state, lib, op, hashes, args, false, def_value, level,
|
&mut scope, state, lib, op, hashes, args, false, false, def_value,
|
||||||
|
level,
|
||||||
)
|
)
|
||||||
.map_err(|err| EvalAltResult::new_position(err, rhs.position()))?;
|
.map_err(|err| EvalAltResult::new_position(err, rhs.position()))?;
|
||||||
if r.as_bool().unwrap_or(false) {
|
if r.as_bool().unwrap_or(false) {
|
||||||
@ -1566,6 +1650,7 @@ impl Engine {
|
|||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
state: &mut State,
|
state: &mut State,
|
||||||
lib: &Module,
|
lib: &Module,
|
||||||
|
this_ptr: &mut Option<&mut Dynamic>,
|
||||||
expr: &Expr,
|
expr: &Expr,
|
||||||
level: usize,
|
level: usize,
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
@ -1573,27 +1658,34 @@ impl Engine {
|
|||||||
.map_err(|err| EvalAltResult::new_position(err, expr.position()))?;
|
.map_err(|err| EvalAltResult::new_position(err, expr.position()))?;
|
||||||
|
|
||||||
let result = match expr {
|
let result = match expr {
|
||||||
Expr::Expr(x) => self.eval_expr(scope, state, lib, x.as_ref(), level),
|
Expr::Expr(x) => self.eval_expr(scope, state, lib, this_ptr, x.as_ref(), level),
|
||||||
|
|
||||||
Expr::IntegerConstant(x) => Ok(x.0.into()),
|
Expr::IntegerConstant(x) => Ok(x.0.into()),
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
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::Variable(x) if (x.0).0 == KEYWORD_THIS => {
|
||||||
|
if let Some(ref val) = this_ptr {
|
||||||
|
Ok((*val).clone())
|
||||||
|
} else {
|
||||||
|
Err(Box::new(EvalAltResult::ErrorUnboundedThis((x.0).1)))
|
||||||
|
}
|
||||||
|
}
|
||||||
Expr::Variable(_) => {
|
Expr::Variable(_) => {
|
||||||
let (val, _, _, _) = search_scope(scope, state, expr)?;
|
let (val, _, _, _) = search_scope(scope, state, this_ptr, expr)?;
|
||||||
Ok(val.clone())
|
Ok(val.clone())
|
||||||
}
|
}
|
||||||
Expr::Property(_) => unreachable!(),
|
Expr::Property(_) => unreachable!(),
|
||||||
|
|
||||||
// Statement block
|
// Statement block
|
||||||
Expr::Stmt(x) => self.eval_stmt(scope, state, lib, &x.0, level),
|
Expr::Stmt(x) => self.eval_stmt(scope, state, lib, this_ptr, &x.0, level),
|
||||||
|
|
||||||
// var op= rhs
|
// var op= rhs
|
||||||
Expr::Assignment(x) if matches!(x.0, Expr::Variable(_)) => {
|
Expr::Assignment(x) if matches!(x.0, Expr::Variable(_)) => {
|
||||||
let (lhs_expr, op, rhs_expr, op_pos) = x.as_ref();
|
let (lhs_expr, op, rhs_expr, op_pos) = x.as_ref();
|
||||||
let mut rhs_val = self.eval_expr(scope, state, lib, rhs_expr, level)?;
|
let mut rhs_val = self.eval_expr(scope, state, lib, this_ptr, rhs_expr, level)?;
|
||||||
let (lhs_ptr, name, typ, pos) = search_scope(scope, state, lhs_expr)?;
|
let (lhs_ptr, name, typ, pos) = search_scope(scope, state, this_ptr, lhs_expr)?;
|
||||||
self.inc_operations(state)
|
self.inc_operations(state)
|
||||||
.map_err(|err| EvalAltResult::new_position(err, pos))?;
|
.map_err(|err| EvalAltResult::new_position(err, pos))?;
|
||||||
|
|
||||||
@ -1632,7 +1724,9 @@ impl Engine {
|
|||||||
|
|
||||||
// Set variable value
|
// Set variable value
|
||||||
*lhs_ptr = self
|
*lhs_ptr = self
|
||||||
.exec_fn_call(state, lib, op, true, hash, args, false, None, level)
|
.exec_fn_call(
|
||||||
|
state, lib, op, true, hash, args, false, false, None, level,
|
||||||
|
)
|
||||||
.map(|(v, _)| v)
|
.map(|(v, _)| v)
|
||||||
.map_err(|err| EvalAltResult::new_position(err, *op_pos))?;
|
.map_err(|err| EvalAltResult::new_position(err, *op_pos))?;
|
||||||
}
|
}
|
||||||
@ -1646,7 +1740,7 @@ impl Engine {
|
|||||||
// lhs op= rhs
|
// lhs op= rhs
|
||||||
Expr::Assignment(x) => {
|
Expr::Assignment(x) => {
|
||||||
let (lhs_expr, op, rhs_expr, op_pos) = x.as_ref();
|
let (lhs_expr, op, rhs_expr, op_pos) = x.as_ref();
|
||||||
let mut rhs_val = self.eval_expr(scope, state, lib, rhs_expr, level)?;
|
let mut rhs_val = self.eval_expr(scope, state, lib, this_ptr, rhs_expr, level)?;
|
||||||
|
|
||||||
let new_val = Some(if op.is_empty() {
|
let new_val = Some(if op.is_empty() {
|
||||||
// Normal assignment
|
// Normal assignment
|
||||||
@ -1656,10 +1750,10 @@ impl Engine {
|
|||||||
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());
|
||||||
let args = &mut [
|
let args = &mut [
|
||||||
&mut self.eval_expr(scope, state, lib, lhs_expr, level)?,
|
&mut self.eval_expr(scope, state, lib, this_ptr, lhs_expr, level)?,
|
||||||
&mut rhs_val,
|
&mut rhs_val,
|
||||||
];
|
];
|
||||||
self.exec_fn_call(state, lib, op, true, hash, args, false, None, level)
|
self.exec_fn_call(state, lib, op, true, hash, args, false, false, None, level)
|
||||||
.map(|(v, _)| v)
|
.map(|(v, _)| v)
|
||||||
.map_err(|err| EvalAltResult::new_position(err, *op_pos))?
|
.map_err(|err| EvalAltResult::new_position(err, *op_pos))?
|
||||||
});
|
});
|
||||||
@ -1669,14 +1763,14 @@ impl Engine {
|
|||||||
Expr::Variable(_) => unreachable!(),
|
Expr::Variable(_) => unreachable!(),
|
||||||
// idx_lhs[idx_expr] op= rhs
|
// idx_lhs[idx_expr] op= rhs
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Expr::Index(_) => {
|
Expr::Index(_) => self.eval_dot_index_chain(
|
||||||
self.eval_dot_index_chain(scope, state, lib, lhs_expr, level, new_val)
|
scope, state, lib, this_ptr, lhs_expr, level, new_val,
|
||||||
}
|
),
|
||||||
// dot_lhs.dot_rhs op= rhs
|
// dot_lhs.dot_rhs op= rhs
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Expr::Dot(_) => {
|
Expr::Dot(_) => self.eval_dot_index_chain(
|
||||||
self.eval_dot_index_chain(scope, state, lib, lhs_expr, level, new_val)
|
scope, state, lib, this_ptr, lhs_expr, level, new_val,
|
||||||
}
|
),
|
||||||
// Error assignment to constant
|
// Error assignment to constant
|
||||||
expr if expr.is_constant() => {
|
expr if expr.is_constant() => {
|
||||||
Err(Box::new(EvalAltResult::ErrorAssignmentToConstant(
|
Err(Box::new(EvalAltResult::ErrorAssignmentToConstant(
|
||||||
@ -1693,16 +1787,20 @@ impl Engine {
|
|||||||
|
|
||||||
// lhs[idx_expr]
|
// lhs[idx_expr]
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Expr::Index(_) => self.eval_dot_index_chain(scope, state, lib, expr, level, None),
|
Expr::Index(_) => {
|
||||||
|
self.eval_dot_index_chain(scope, state, lib, this_ptr, expr, level, None)
|
||||||
|
}
|
||||||
|
|
||||||
// lhs.dot_rhs
|
// lhs.dot_rhs
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Expr::Dot(_) => self.eval_dot_index_chain(scope, state, lib, expr, level, None),
|
Expr::Dot(_) => {
|
||||||
|
self.eval_dot_index_chain(scope, state, lib, this_ptr, expr, level, None)
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Expr::Array(x) => Ok(Dynamic(Union::Array(Box::new(
|
Expr::Array(x) => Ok(Dynamic(Union::Array(Box::new(
|
||||||
x.0.iter()
|
x.0.iter()
|
||||||
.map(|item| self.eval_expr(scope, state, lib, item, level))
|
.map(|item| self.eval_expr(scope, state, lib, this_ptr, item, level))
|
||||||
.collect::<Result<Vec<_>, _>>()?,
|
.collect::<Result<Vec<_>, _>>()?,
|
||||||
)))),
|
)))),
|
||||||
|
|
||||||
@ -1710,7 +1808,7 @@ impl Engine {
|
|||||||
Expr::Map(x) => Ok(Dynamic(Union::Map(Box::new(
|
Expr::Map(x) => Ok(Dynamic(Union::Map(Box::new(
|
||||||
x.0.iter()
|
x.0.iter()
|
||||||
.map(|((key, _), expr)| {
|
.map(|((key, _), expr)| {
|
||||||
self.eval_expr(scope, state, lib, expr, level)
|
self.eval_expr(scope, state, lib, this_ptr, expr, level)
|
||||||
.map(|val| (key.clone(), val))
|
.map(|val| (key.clone(), val))
|
||||||
})
|
})
|
||||||
.collect::<Result<HashMap<_, _>, _>>()?,
|
.collect::<Result<HashMap<_, _>, _>>()?,
|
||||||
@ -1729,7 +1827,7 @@ impl Engine {
|
|||||||
if !self.has_override(lib, (hash_fn, *hash)) {
|
if !self.has_override(lib, (hash_fn, *hash)) {
|
||||||
// Fn - only in function call style
|
// Fn - only in function call style
|
||||||
let expr = args_expr.get(0);
|
let expr = args_expr.get(0);
|
||||||
let arg_value = self.eval_expr(scope, state, lib, expr, level)?;
|
let arg_value = self.eval_expr(scope, state, lib, this_ptr, expr, level)?;
|
||||||
return arg_value
|
return arg_value
|
||||||
.take_immutable_string()
|
.take_immutable_string()
|
||||||
.map(|s| FnPtr::from(s).into())
|
.map(|s| FnPtr::from(s).into())
|
||||||
@ -1751,7 +1849,7 @@ impl Engine {
|
|||||||
// eval - only in function call style
|
// eval - only in function call style
|
||||||
let prev_len = scope.len();
|
let prev_len = scope.len();
|
||||||
let expr = args_expr.get(0);
|
let expr = args_expr.get(0);
|
||||||
let script = self.eval_expr(scope, state, lib, expr, level)?;
|
let script = self.eval_expr(scope, state, lib, this_ptr, expr, level)?;
|
||||||
let result = self
|
let result = self
|
||||||
.eval_script_expr(scope, state, lib, &script)
|
.eval_script_expr(scope, state, lib, &script)
|
||||||
.map_err(|err| EvalAltResult::new_position(err, expr.position()));
|
.map_err(|err| EvalAltResult::new_position(err, expr.position()));
|
||||||
@ -1766,7 +1864,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Normal function call - except for eval (handled above)
|
// Normal function call - except for Fn and eval (handled above)
|
||||||
let mut arg_values: StaticVec<Dynamic>;
|
let mut arg_values: StaticVec<Dynamic>;
|
||||||
let mut args: StaticVec<_>;
|
let mut args: StaticVec<_>;
|
||||||
let mut is_ref = false;
|
let mut is_ref = false;
|
||||||
@ -1783,18 +1881,21 @@ impl Engine {
|
|||||||
arg_values = args_expr
|
arg_values = args_expr
|
||||||
.iter()
|
.iter()
|
||||||
.skip(1)
|
.skip(1)
|
||||||
.map(|expr| self.eval_expr(scope, state, lib, expr, level))
|
.map(|expr| {
|
||||||
|
self.eval_expr(scope, state, lib, this_ptr, expr, level)
|
||||||
|
})
|
||||||
.collect::<Result<_, _>>()?;
|
.collect::<Result<_, _>>()?;
|
||||||
|
|
||||||
let (target, _, typ, pos) = search_scope(scope, state, lhs)?;
|
let (target, _, typ, pos) = search_scope(scope, state, this_ptr, lhs)?;
|
||||||
self.inc_operations(state)
|
|
||||||
.map_err(|err| EvalAltResult::new_position(err, pos))?;
|
|
||||||
|
|
||||||
match typ {
|
match typ {
|
||||||
ScopeEntryType::Module => unreachable!(),
|
ScopeEntryType::Module => unreachable!(),
|
||||||
ScopeEntryType::Constant | ScopeEntryType::Normal => (),
|
ScopeEntryType::Constant | ScopeEntryType::Normal => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.inc_operations(state)
|
||||||
|
.map_err(|err| EvalAltResult::new_position(err, pos))?;
|
||||||
|
|
||||||
args = once(target).chain(arg_values.iter_mut()).collect();
|
args = once(target).chain(arg_values.iter_mut()).collect();
|
||||||
|
|
||||||
is_ref = true;
|
is_ref = true;
|
||||||
@ -1803,7 +1904,9 @@ impl Engine {
|
|||||||
_ => {
|
_ => {
|
||||||
arg_values = args_expr
|
arg_values = args_expr
|
||||||
.iter()
|
.iter()
|
||||||
.map(|expr| self.eval_expr(scope, state, lib, expr, level))
|
.map(|expr| {
|
||||||
|
self.eval_expr(scope, state, lib, this_ptr, expr, level)
|
||||||
|
})
|
||||||
.collect::<Result<_, _>>()?;
|
.collect::<Result<_, _>>()?;
|
||||||
|
|
||||||
args = arg_values.iter_mut().collect();
|
args = arg_values.iter_mut().collect();
|
||||||
@ -1813,7 +1916,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, def_val, level,
|
state, lib, name, *native, *hash, args, is_ref, false, def_val, level,
|
||||||
)
|
)
|
||||||
.map(|(v, _)| v)
|
.map(|(v, _)| v)
|
||||||
.map_err(|err| EvalAltResult::new_position(err, *pos))
|
.map_err(|err| EvalAltResult::new_position(err, *pos))
|
||||||
@ -1826,7 +1929,7 @@ impl Engine {
|
|||||||
|
|
||||||
let mut arg_values = args_expr
|
let mut arg_values = args_expr
|
||||||
.iter()
|
.iter()
|
||||||
.map(|expr| self.eval_expr(scope, state, lib, expr, level))
|
.map(|expr| self.eval_expr(scope, state, lib, this_ptr, expr, level))
|
||||||
.collect::<Result<StaticVec<_>, _>>()?;
|
.collect::<Result<StaticVec<_>, _>>()?;
|
||||||
|
|
||||||
let mut args: StaticVec<_> = arg_values.iter_mut().collect();
|
let mut args: StaticVec<_> = arg_values.iter_mut().collect();
|
||||||
@ -1872,7 +1975,9 @@ impl Engine {
|
|||||||
let args = args.as_mut();
|
let args = args.as_mut();
|
||||||
let fn_def = f.get_fn_def();
|
let fn_def = f.get_fn_def();
|
||||||
let mut scope = Scope::new();
|
let mut scope = Scope::new();
|
||||||
self.call_script_fn(&mut scope, state, lib, name, fn_def, args, level)
|
self.call_script_fn(
|
||||||
|
&mut scope, state, lib, &mut None, name, fn_def, args, level,
|
||||||
|
)
|
||||||
.map_err(|err| EvalAltResult::new_position(err, *pos))
|
.map_err(|err| EvalAltResult::new_position(err, *pos))
|
||||||
}
|
}
|
||||||
Ok(f) => {
|
Ok(f) => {
|
||||||
@ -1888,19 +1993,19 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Expr::In(x) => self.eval_in_expr(scope, state, lib, &x.0, &x.1, level),
|
Expr::In(x) => self.eval_in_expr(scope, state, lib, this_ptr, &x.0, &x.1, level),
|
||||||
|
|
||||||
Expr::And(x) => {
|
Expr::And(x) => {
|
||||||
let (lhs, rhs, _) = x.as_ref();
|
let (lhs, rhs, _) = x.as_ref();
|
||||||
Ok((self
|
Ok((self
|
||||||
.eval_expr(scope, state, lib, lhs, level)?
|
.eval_expr(scope, state, lib, this_ptr, lhs, level)?
|
||||||
.as_bool()
|
.as_bool()
|
||||||
.map_err(|_| {
|
.map_err(|_| {
|
||||||
EvalAltResult::ErrorBooleanArgMismatch("AND".into(), lhs.position())
|
EvalAltResult::ErrorBooleanArgMismatch("AND".into(), lhs.position())
|
||||||
})?
|
})?
|
||||||
&& // Short-circuit using &&
|
&& // Short-circuit using &&
|
||||||
self
|
self
|
||||||
.eval_expr(scope, state, lib, rhs, level)?
|
.eval_expr(scope, state, lib, this_ptr, rhs, level)?
|
||||||
.as_bool()
|
.as_bool()
|
||||||
.map_err(|_| {
|
.map_err(|_| {
|
||||||
EvalAltResult::ErrorBooleanArgMismatch("AND".into(), rhs.position())
|
EvalAltResult::ErrorBooleanArgMismatch("AND".into(), rhs.position())
|
||||||
@ -1911,14 +2016,14 @@ impl Engine {
|
|||||||
Expr::Or(x) => {
|
Expr::Or(x) => {
|
||||||
let (lhs, rhs, _) = x.as_ref();
|
let (lhs, rhs, _) = x.as_ref();
|
||||||
Ok((self
|
Ok((self
|
||||||
.eval_expr(scope, state, lib, lhs, level)?
|
.eval_expr(scope, state, lib, this_ptr, lhs, level)?
|
||||||
.as_bool()
|
.as_bool()
|
||||||
.map_err(|_| {
|
.map_err(|_| {
|
||||||
EvalAltResult::ErrorBooleanArgMismatch("OR".into(), lhs.position())
|
EvalAltResult::ErrorBooleanArgMismatch("OR".into(), lhs.position())
|
||||||
})?
|
})?
|
||||||
|| // Short-circuit using ||
|
|| // Short-circuit using ||
|
||||||
self
|
self
|
||||||
.eval_expr(scope, state, lib, rhs, level)?
|
.eval_expr(scope, state, lib, this_ptr, rhs, level)?
|
||||||
.as_bool()
|
.as_bool()
|
||||||
.map_err(|_| {
|
.map_err(|_| {
|
||||||
EvalAltResult::ErrorBooleanArgMismatch("OR".into(), rhs.position())
|
EvalAltResult::ErrorBooleanArgMismatch("OR".into(), rhs.position())
|
||||||
@ -1942,6 +2047,7 @@ impl Engine {
|
|||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
state: &mut State,
|
state: &mut State,
|
||||||
lib: &Module,
|
lib: &Module,
|
||||||
|
this_ptr: &mut Option<&mut Dynamic>,
|
||||||
stmt: &Stmt,
|
stmt: &Stmt,
|
||||||
level: usize,
|
level: usize,
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
@ -1954,7 +2060,7 @@ impl Engine {
|
|||||||
|
|
||||||
// Expression as statement
|
// Expression as statement
|
||||||
Stmt::Expr(expr) => {
|
Stmt::Expr(expr) => {
|
||||||
let result = self.eval_expr(scope, state, lib, expr, level)?;
|
let result = self.eval_expr(scope, state, lib, this_ptr, expr, level)?;
|
||||||
|
|
||||||
Ok(match expr.as_ref() {
|
Ok(match expr.as_ref() {
|
||||||
// If it is a simple assignment, erase the result at the root
|
// If it is a simple assignment, erase the result at the root
|
||||||
@ -1969,7 +2075,7 @@ impl Engine {
|
|||||||
state.scope_level += 1;
|
state.scope_level += 1;
|
||||||
|
|
||||||
let result = x.0.iter().try_fold(Default::default(), |_, stmt| {
|
let result = x.0.iter().try_fold(Default::default(), |_, stmt| {
|
||||||
self.eval_stmt(scope, state, lib, stmt, level)
|
self.eval_stmt(scope, state, lib, this_ptr, stmt, level)
|
||||||
});
|
});
|
||||||
|
|
||||||
scope.rewind(prev_len);
|
scope.rewind(prev_len);
|
||||||
@ -1986,14 +2092,14 @@ impl Engine {
|
|||||||
Stmt::IfThenElse(x) => {
|
Stmt::IfThenElse(x) => {
|
||||||
let (expr, if_block, else_block) = x.as_ref();
|
let (expr, if_block, else_block) = x.as_ref();
|
||||||
|
|
||||||
self.eval_expr(scope, state, lib, expr, level)?
|
self.eval_expr(scope, state, lib, this_ptr, expr, level)?
|
||||||
.as_bool()
|
.as_bool()
|
||||||
.map_err(|_| Box::new(EvalAltResult::ErrorLogicGuard(expr.position())))
|
.map_err(|_| Box::new(EvalAltResult::ErrorLogicGuard(expr.position())))
|
||||||
.and_then(|guard_val| {
|
.and_then(|guard_val| {
|
||||||
if guard_val {
|
if guard_val {
|
||||||
self.eval_stmt(scope, state, lib, if_block, level)
|
self.eval_stmt(scope, state, lib, this_ptr, if_block, level)
|
||||||
} else if let Some(stmt) = else_block {
|
} else if let Some(stmt) = else_block {
|
||||||
self.eval_stmt(scope, state, lib, stmt, level)
|
self.eval_stmt(scope, state, lib, this_ptr, stmt, level)
|
||||||
} else {
|
} else {
|
||||||
Ok(Default::default())
|
Ok(Default::default())
|
||||||
}
|
}
|
||||||
@ -2004,8 +2110,11 @@ impl Engine {
|
|||||||
Stmt::While(x) => loop {
|
Stmt::While(x) => loop {
|
||||||
let (expr, body) = x.as_ref();
|
let (expr, body) = x.as_ref();
|
||||||
|
|
||||||
match self.eval_expr(scope, state, lib, expr, level)?.as_bool() {
|
match self
|
||||||
Ok(true) => match self.eval_stmt(scope, state, lib, body, level) {
|
.eval_expr(scope, state, lib, this_ptr, expr, level)?
|
||||||
|
.as_bool()
|
||||||
|
{
|
||||||
|
Ok(true) => match self.eval_stmt(scope, state, lib, this_ptr, body, level) {
|
||||||
Ok(_) => (),
|
Ok(_) => (),
|
||||||
Err(err) => match *err {
|
Err(err) => match *err {
|
||||||
EvalAltResult::ErrorLoopBreak(false, _) => (),
|
EvalAltResult::ErrorLoopBreak(false, _) => (),
|
||||||
@ -2022,7 +2131,7 @@ impl Engine {
|
|||||||
|
|
||||||
// Loop statement
|
// Loop statement
|
||||||
Stmt::Loop(body) => loop {
|
Stmt::Loop(body) => loop {
|
||||||
match self.eval_stmt(scope, state, lib, body, level) {
|
match self.eval_stmt(scope, state, lib, this_ptr, body, level) {
|
||||||
Ok(_) => (),
|
Ok(_) => (),
|
||||||
Err(err) => match *err {
|
Err(err) => match *err {
|
||||||
EvalAltResult::ErrorLoopBreak(false, _) => (),
|
EvalAltResult::ErrorLoopBreak(false, _) => (),
|
||||||
@ -2035,7 +2144,7 @@ impl Engine {
|
|||||||
// For loop
|
// For loop
|
||||||
Stmt::For(x) => {
|
Stmt::For(x) => {
|
||||||
let (name, expr, stmt) = x.as_ref();
|
let (name, expr, stmt) = x.as_ref();
|
||||||
let iter_type = self.eval_expr(scope, state, lib, expr, level)?;
|
let iter_type = self.eval_expr(scope, state, lib, this_ptr, expr, level)?;
|
||||||
let tid = iter_type.type_id();
|
let tid = iter_type.type_id();
|
||||||
|
|
||||||
if let Some(func) = self
|
if let Some(func) = self
|
||||||
@ -2054,7 +2163,7 @@ impl Engine {
|
|||||||
self.inc_operations(state)
|
self.inc_operations(state)
|
||||||
.map_err(|err| EvalAltResult::new_position(err, stmt.position()))?;
|
.map_err(|err| EvalAltResult::new_position(err, stmt.position()))?;
|
||||||
|
|
||||||
match self.eval_stmt(scope, state, lib, stmt, level) {
|
match self.eval_stmt(scope, state, lib, this_ptr, stmt, level) {
|
||||||
Ok(_) => (),
|
Ok(_) => (),
|
||||||
Err(err) => match *err {
|
Err(err) => match *err {
|
||||||
EvalAltResult::ErrorLoopBreak(false, _) => (),
|
EvalAltResult::ErrorLoopBreak(false, _) => (),
|
||||||
@ -2081,7 +2190,7 @@ impl Engine {
|
|||||||
// Return value
|
// Return value
|
||||||
Stmt::ReturnWithVal(x) if x.1.is_some() && (x.0).0 == ReturnType::Return => {
|
Stmt::ReturnWithVal(x) if x.1.is_some() && (x.0).0 == ReturnType::Return => {
|
||||||
Err(Box::new(EvalAltResult::Return(
|
Err(Box::new(EvalAltResult::Return(
|
||||||
self.eval_expr(scope, state, lib, x.1.as_ref().unwrap(), level)?,
|
self.eval_expr(scope, state, lib, this_ptr, x.1.as_ref().unwrap(), level)?,
|
||||||
(x.0).1,
|
(x.0).1,
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
@ -2093,7 +2202,8 @@ impl Engine {
|
|||||||
|
|
||||||
// Throw value
|
// Throw value
|
||||||
Stmt::ReturnWithVal(x) if x.1.is_some() && (x.0).0 == ReturnType::Exception => {
|
Stmt::ReturnWithVal(x) if x.1.is_some() && (x.0).0 == ReturnType::Exception => {
|
||||||
let val = self.eval_expr(scope, state, lib, x.1.as_ref().unwrap(), level)?;
|
let val =
|
||||||
|
self.eval_expr(scope, state, lib, this_ptr, x.1.as_ref().unwrap(), level)?;
|
||||||
Err(Box::new(EvalAltResult::ErrorRuntime(
|
Err(Box::new(EvalAltResult::ErrorRuntime(
|
||||||
val.take_string().unwrap_or_else(|_| "".into()),
|
val.take_string().unwrap_or_else(|_| "".into()),
|
||||||
(x.0).1,
|
(x.0).1,
|
||||||
@ -2110,7 +2220,8 @@ impl Engine {
|
|||||||
// Let statement
|
// Let statement
|
||||||
Stmt::Let(x) if x.1.is_some() => {
|
Stmt::Let(x) if x.1.is_some() => {
|
||||||
let ((var_name, _), expr) = x.as_ref();
|
let ((var_name, _), expr) = x.as_ref();
|
||||||
let val = self.eval_expr(scope, state, lib, expr.as_ref().unwrap(), level)?;
|
let val =
|
||||||
|
self.eval_expr(scope, state, lib, this_ptr, expr.as_ref().unwrap(), level)?;
|
||||||
let var_name = unsafe_cast_var_name_to_lifetime(var_name, &state);
|
let var_name = unsafe_cast_var_name_to_lifetime(var_name, &state);
|
||||||
scope.push_dynamic_value(var_name, ScopeEntryType::Normal, val, false);
|
scope.push_dynamic_value(var_name, ScopeEntryType::Normal, val, false);
|
||||||
Ok(Default::default())
|
Ok(Default::default())
|
||||||
@ -2126,7 +2237,7 @@ impl Engine {
|
|||||||
// Const statement
|
// Const statement
|
||||||
Stmt::Const(x) if x.1.is_constant() => {
|
Stmt::Const(x) if x.1.is_constant() => {
|
||||||
let ((var_name, _), expr) = x.as_ref();
|
let ((var_name, _), expr) = x.as_ref();
|
||||||
let val = self.eval_expr(scope, state, lib, &expr, level)?;
|
let val = self.eval_expr(scope, state, lib, this_ptr, &expr, level)?;
|
||||||
let var_name = unsafe_cast_var_name_to_lifetime(var_name, &state);
|
let var_name = unsafe_cast_var_name_to_lifetime(var_name, &state);
|
||||||
scope.push_dynamic_value(var_name, ScopeEntryType::Constant, val, true);
|
scope.push_dynamic_value(var_name, ScopeEntryType::Constant, val, true);
|
||||||
Ok(Default::default())
|
Ok(Default::default())
|
||||||
@ -2145,7 +2256,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(path) = self
|
if let Some(path) = self
|
||||||
.eval_expr(scope, state, lib, &expr, level)?
|
.eval_expr(scope, state, lib, this_ptr, &expr, level)?
|
||||||
.try_cast::<ImmutableString>()
|
.try_cast::<ImmutableString>()
|
||||||
{
|
{
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
|
@ -3,7 +3,13 @@
|
|||||||
use crate::result::EvalAltResult;
|
use crate::result::EvalAltResult;
|
||||||
use crate::token::Position;
|
use crate::token::Position;
|
||||||
|
|
||||||
use crate::stdlib::{boxed::Box, char, error::Error, fmt, string::String};
|
use crate::stdlib::{
|
||||||
|
boxed::Box,
|
||||||
|
char,
|
||||||
|
error::Error,
|
||||||
|
fmt,
|
||||||
|
string::{String, ToString},
|
||||||
|
};
|
||||||
|
|
||||||
/// Error when tokenizing the script text.
|
/// Error when tokenizing the script text.
|
||||||
#[derive(Debug, Eq, PartialEq, Clone, Hash)]
|
#[derive(Debug, Eq, PartialEq, Clone, Hash)]
|
||||||
|
@ -132,7 +132,7 @@ impl CallableFunction {
|
|||||||
Self::Method(_) | Self::Iterator(_) | Self::Script(_) => false,
|
Self::Method(_) | Self::Iterator(_) | Self::Script(_) => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Is this a pure native Rust method-call?
|
/// Is this a native Rust method function?
|
||||||
pub fn is_method(&self) -> bool {
|
pub fn is_method(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Self::Method(_) => true,
|
Self::Method(_) => true,
|
||||||
|
@ -128,6 +128,7 @@ fn call_fn_with_constant_arguments(
|
|||||||
(hash_fn, 0),
|
(hash_fn, 0),
|
||||||
arg_values.iter_mut().collect::<StaticVec<_>>().as_mut(),
|
arg_values.iter_mut().collect::<StaticVec<_>>().as_mut(),
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
None,
|
None,
|
||||||
0,
|
0,
|
||||||
)
|
)
|
||||||
|
@ -8,7 +8,7 @@ use crate::parser::{ImmutableString, INT};
|
|||||||
use crate::result::EvalAltResult;
|
use crate::result::EvalAltResult;
|
||||||
use crate::token::Position;
|
use crate::token::Position;
|
||||||
|
|
||||||
use crate::stdlib::{any::TypeId, boxed::Box};
|
use crate::stdlib::{any::TypeId, boxed::Box, string::ToString};
|
||||||
|
|
||||||
// Register array utility functions
|
// Register array utility functions
|
||||||
fn push<T: Variant + Clone>(list: &mut Array, item: T) -> FuncReturn<()> {
|
fn push<T: Variant + Clone>(list: &mut Array, item: T) -> FuncReturn<()> {
|
||||||
|
@ -12,6 +12,7 @@ use crate::engine::Array;
|
|||||||
|
|
||||||
use crate::stdlib::{
|
use crate::stdlib::{
|
||||||
any::TypeId,
|
any::TypeId,
|
||||||
|
boxed::Box,
|
||||||
fmt::Display,
|
fmt::Display,
|
||||||
format,
|
format,
|
||||||
string::{String, ToString},
|
string::{String, ToString},
|
||||||
|
@ -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};
|
use crate::engine::{make_getter, make_setter, Engine, 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};
|
||||||
@ -1847,19 +1847,8 @@ fn parse_binary_op(
|
|||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Token::Period => {
|
Token::Period => {
|
||||||
let mut rhs = args.pop();
|
let rhs = args.pop();
|
||||||
let current_lhs = args.pop();
|
let current_lhs = args.pop();
|
||||||
|
|
||||||
match &mut rhs {
|
|
||||||
// current_lhs.rhs(...) - method call
|
|
||||||
Expr::FnCall(x) => {
|
|
||||||
let ((id, _, _), _, hash, args, _) = x.as_mut();
|
|
||||||
// Recalculate function call hash because there is an additional argument
|
|
||||||
*hash = calc_fn_hash(empty(), id, args.len() + 1, empty());
|
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
|
|
||||||
make_dot_expr(current_lhs, rhs, pos)?
|
make_dot_expr(current_lhs, rhs, pos)?
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2057,6 +2046,16 @@ fn parse_let(
|
|||||||
(_, pos) => return Err(PERR::VariableExpected.into_err(pos)),
|
(_, pos) => return Err(PERR::VariableExpected.into_err(pos)),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Check if the name is allowed
|
||||||
|
match name.as_str() {
|
||||||
|
KEYWORD_THIS => {
|
||||||
|
return Err(
|
||||||
|
PERR::BadInput(LexError::MalformedIdentifier(name).to_string()).into_err(pos),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
|
||||||
// let name = ...
|
// let name = ...
|
||||||
if match_token(input, Token::Equals)? {
|
if match_token(input, Token::Equals)? {
|
||||||
// let name = expr
|
// let name = expr
|
||||||
@ -2073,7 +2072,7 @@ fn parse_let(
|
|||||||
state.push((name.clone(), ScopeEntryType::Constant));
|
state.push((name.clone(), ScopeEntryType::Constant));
|
||||||
Ok(Stmt::Const(Box::new(((name, pos), init_value))))
|
Ok(Stmt::Const(Box::new(((name, pos), init_value))))
|
||||||
}
|
}
|
||||||
// const name = expr - error
|
// const name = expr: error
|
||||||
ScopeEntryType::Constant => {
|
ScopeEntryType::Constant => {
|
||||||
Err(PERR::ForbiddenConstantExpr(name).into_err(init_value.position()))
|
Err(PERR::ForbiddenConstantExpr(name).into_err(init_value.position()))
|
||||||
}
|
}
|
||||||
|
@ -39,6 +39,8 @@ pub enum EvalAltResult {
|
|||||||
/// An error has occurred inside a called function.
|
/// An error has occurred inside a called function.
|
||||||
/// Wrapped values re the name of the function and the interior error.
|
/// Wrapped values re the name of the function and the interior error.
|
||||||
ErrorInFunctionCall(String, Box<EvalAltResult>, Position),
|
ErrorInFunctionCall(String, Box<EvalAltResult>, Position),
|
||||||
|
/// Access to `this` that is not bounded.
|
||||||
|
ErrorUnboundedThis(Position),
|
||||||
/// Non-boolean operand encountered for boolean operator. Wrapped value is the operator.
|
/// Non-boolean operand encountered for boolean operator. Wrapped value is the operator.
|
||||||
ErrorBooleanArgMismatch(String, Position),
|
ErrorBooleanArgMismatch(String, Position),
|
||||||
/// Non-character value encountered where a character is required.
|
/// Non-character value encountered where a character is required.
|
||||||
@ -110,6 +112,7 @@ impl EvalAltResult {
|
|||||||
Self::ErrorParsing(p, _) => p.desc(),
|
Self::ErrorParsing(p, _) => p.desc(),
|
||||||
Self::ErrorInFunctionCall(_, _, _) => "Error in called function",
|
Self::ErrorInFunctionCall(_, _, _) => "Error in called function",
|
||||||
Self::ErrorFunctionNotFound(_, _) => "Function not found",
|
Self::ErrorFunctionNotFound(_, _) => "Function not found",
|
||||||
|
Self::ErrorUnboundedThis(_) => "'this' is not bounded",
|
||||||
Self::ErrorBooleanArgMismatch(_, _) => "Boolean operator expects boolean operands",
|
Self::ErrorBooleanArgMismatch(_, _) => "Boolean operator expects boolean operands",
|
||||||
Self::ErrorCharMismatch(_) => "Character expected",
|
Self::ErrorCharMismatch(_) => "Character expected",
|
||||||
Self::ErrorNumericIndexExpr(_) => {
|
Self::ErrorNumericIndexExpr(_) => {
|
||||||
@ -184,6 +187,7 @@ impl fmt::Display for EvalAltResult {
|
|||||||
Self::ErrorIndexingType(_, _)
|
Self::ErrorIndexingType(_, _)
|
||||||
| Self::ErrorNumericIndexExpr(_)
|
| Self::ErrorNumericIndexExpr(_)
|
||||||
| Self::ErrorStringIndexExpr(_)
|
| Self::ErrorStringIndexExpr(_)
|
||||||
|
| Self::ErrorUnboundedThis(_)
|
||||||
| Self::ErrorImportExpr(_)
|
| Self::ErrorImportExpr(_)
|
||||||
| Self::ErrorLogicGuard(_)
|
| Self::ErrorLogicGuard(_)
|
||||||
| Self::ErrorFor(_)
|
| Self::ErrorFor(_)
|
||||||
@ -270,6 +274,7 @@ impl EvalAltResult {
|
|||||||
Self::ErrorParsing(_, pos)
|
Self::ErrorParsing(_, pos)
|
||||||
| Self::ErrorFunctionNotFound(_, pos)
|
| Self::ErrorFunctionNotFound(_, pos)
|
||||||
| Self::ErrorInFunctionCall(_, _, pos)
|
| Self::ErrorInFunctionCall(_, _, pos)
|
||||||
|
| Self::ErrorUnboundedThis(pos)
|
||||||
| Self::ErrorBooleanArgMismatch(_, pos)
|
| Self::ErrorBooleanArgMismatch(_, pos)
|
||||||
| Self::ErrorCharMismatch(pos)
|
| Self::ErrorCharMismatch(pos)
|
||||||
| Self::ErrorArrayBounds(_, _, pos)
|
| Self::ErrorArrayBounds(_, _, pos)
|
||||||
@ -309,6 +314,7 @@ impl EvalAltResult {
|
|||||||
Self::ErrorParsing(_, pos)
|
Self::ErrorParsing(_, pos)
|
||||||
| Self::ErrorFunctionNotFound(_, pos)
|
| Self::ErrorFunctionNotFound(_, pos)
|
||||||
| Self::ErrorInFunctionCall(_, _, pos)
|
| Self::ErrorInFunctionCall(_, _, pos)
|
||||||
|
| Self::ErrorUnboundedThis(pos)
|
||||||
| Self::ErrorBooleanArgMismatch(_, pos)
|
| Self::ErrorBooleanArgMismatch(_, pos)
|
||||||
| Self::ErrorCharMismatch(pos)
|
| Self::ErrorCharMismatch(pos)
|
||||||
| Self::ErrorArrayBounds(_, _, pos)
|
| Self::ErrorArrayBounds(_, _, pos)
|
||||||
|
@ -39,9 +39,9 @@ fn test_bool_op_short_circuit() -> Result<(), Box<EvalAltResult>> {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<bool>(
|
engine.eval::<bool>(
|
||||||
r"
|
r"
|
||||||
let this = true;
|
let x = true;
|
||||||
|
|
||||||
this || { throw; };
|
x || { throw; };
|
||||||
"
|
"
|
||||||
)?,
|
)?,
|
||||||
true
|
true
|
||||||
@ -50,9 +50,9 @@ fn test_bool_op_short_circuit() -> Result<(), Box<EvalAltResult>> {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<bool>(
|
engine.eval::<bool>(
|
||||||
r"
|
r"
|
||||||
let this = false;
|
let x = false;
|
||||||
|
|
||||||
this && { throw; };
|
x && { throw; };
|
||||||
"
|
"
|
||||||
)?,
|
)?,
|
||||||
false
|
false
|
||||||
@ -68,9 +68,9 @@ fn test_bool_op_no_short_circuit1() {
|
|||||||
assert!(engine
|
assert!(engine
|
||||||
.eval::<bool>(
|
.eval::<bool>(
|
||||||
r"
|
r"
|
||||||
let this = true;
|
let x = true;
|
||||||
|
|
||||||
this | { throw; }
|
x | { throw; }
|
||||||
"
|
"
|
||||||
)
|
)
|
||||||
.is_err());
|
.is_err());
|
||||||
@ -83,9 +83,9 @@ fn test_bool_op_no_short_circuit2() {
|
|||||||
assert!(engine
|
assert!(engine
|
||||||
.eval::<bool>(
|
.eval::<bool>(
|
||||||
r"
|
r"
|
||||||
let this = false;
|
let x = false;
|
||||||
|
|
||||||
this & { throw; }
|
x & { throw; }
|
||||||
"
|
"
|
||||||
)
|
)
|
||||||
.is_err());
|
.is_err());
|
||||||
|
@ -19,28 +19,33 @@ fn test_functions() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<INT>("fn add(x, n) { x + n } let x = 40; x.add(2)")?,
|
engine.eval::<INT>("fn add(n) { this + n } let x = 40; x.add(2)")?,
|
||||||
42
|
42
|
||||||
);
|
);
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<INT>("fn add(x, n) { x += n; } let x = 40; x.add(2); x")?,
|
engine.eval::<INT>("fn add(n) { this += n; } let x = 40; x.add(2); x")?,
|
||||||
40
|
42
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(engine.eval::<INT>("fn mul2(x) { x * 2 } mul2(21)")?, 42);
|
assert_eq!(engine.eval::<INT>("fn mul2(x) { x * 2 } mul2(21)")?, 42);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<INT>("fn mul2(x) { x *= 2 } let a = 21; mul2(a); a")?,
|
||||||
|
21
|
||||||
|
);
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<INT>("fn mul2(x) { x * 2 } let x = 21; x.mul2()")?,
|
engine.eval::<INT>("fn mul2() { this * 2 } let x = 21; x.mul2()")?,
|
||||||
42
|
42
|
||||||
);
|
);
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<INT>("fn mul2(x) { x *= 2; } let x = 21; x.mul2(); x")?,
|
engine.eval::<INT>("fn mul2() { this *= 2; } let x = 21; x.mul2(); x")?,
|
||||||
21
|
42
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -250,3 +250,25 @@ fn test_map_json() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
fn test_map_oop() -> Result<(), Box<EvalAltResult>> {
|
||||||
|
let engine = Engine::new();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<INT>(
|
||||||
|
r#"
|
||||||
|
let obj = #{ data: 40, action: Fn("abc") };
|
||||||
|
|
||||||
|
fn abc(x) { this.data += x; }
|
||||||
|
|
||||||
|
obj.action(2);
|
||||||
|
obj.data
|
||||||
|
"#,
|
||||||
|
)?,
|
||||||
|
42
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
#![cfg(not(feature = "no_module"))]
|
#![cfg(not(feature = "no_module"))]
|
||||||
use rhai::{
|
use rhai::{
|
||||||
module_resolvers, Dynamic, Engine, EvalAltResult, Module, ParseError, ParseErrorType, Scope,
|
module_resolvers, Engine, EvalAltResult, Module, ParseError, ParseErrorType, Scope, INT,
|
||||||
INT,
|
|
||||||
};
|
};
|
||||||
use std::any::TypeId;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_module() {
|
fn test_module() {
|
||||||
|
Loading…
Reference in New Issue
Block a user