Merge branch 'master' into plugins
This commit is contained in:
commit
fbe15d25b6
@ -14,6 +14,8 @@ Breaking changes
|
||||
----------------
|
||||
|
||||
* `EvalAltResult::ErrorMismatchOutputType` has an extra argument containing the name of the requested type.
|
||||
* `Engine::call_fn_dynamic` take an extra argument, allowing a `Dynamic` value to be bound to the `this` pointer.
|
||||
* Precedence of the `%` (modulo) operator is lowered to below `<<` ad `>>`. This is to handle the case of `x << 3 % 10`.
|
||||
|
||||
New features
|
||||
------------
|
||||
@ -22,6 +24,9 @@ New features
|
||||
This is particularly useful when converting a Rust `struct` to a `Dynamic` _object map_ and back.
|
||||
* `Engine::disable_symbol` to surgically disable keywords and/or operators.
|
||||
* `Engine::register_custom_operator` to define a custom operator.
|
||||
* New low-level API `Engine::register_raw_fn`.
|
||||
* `AST::clone_functions_only`, `AST::clone_functions_only_filtered` and `AST::clone_statements_only` to clone only part of an `AST`.
|
||||
* The boolean `^` (XOR) operator is added.
|
||||
|
||||
|
||||
Version 0.16.1
|
||||
|
@ -104,9 +104,10 @@ The Rhai Scripting Language
|
||||
4. [Side-Effect Considerations](engine/optimize/side-effects.md)
|
||||
5. [Volatility Considerations](engine/optimize/volatility.md)
|
||||
6. [Subtle Semantic Changes](engine/optimize/semantics.md)
|
||||
4. [Disable Keywords and/or Operators](engine/disable.md)
|
||||
5. [Custom Operators](engine/custom-op.md)
|
||||
6. [Eval Statement](language/eval.md)
|
||||
4. [Low-Level API](rust/register-raw.md)
|
||||
5. [Disable Keywords and/or Operators](engine/disable.md)
|
||||
6. [Custom Operators](engine/custom-op.md)
|
||||
7. [Eval Statement](language/eval.md)
|
||||
9. [Appendix](appendix/index.md)
|
||||
1. [Keywords](appendix/keywords.md)
|
||||
2. [Operators](appendix/operators.md)
|
||||
|
@ -15,7 +15,7 @@ Operators
|
||||
| `<<` | Left bit-shift | Yes | Left |
|
||||
| `&` | Bit-wise _And_, Boolean _And_ | Yes | Left |
|
||||
| <code>\|</code> | Bit-wise _Or_, Boolean _Or_ | Yes | Left |
|
||||
| `^` | Bit-wise _Xor_ | Yes | Left |
|
||||
| `^` | Bit-wise _Xor_, Boolean _Xor_ | Yes | Left |
|
||||
| `==` | Equals to | Yes | Left |
|
||||
| `~=` | Not equals to | Yes | Left |
|
||||
| `>` | Greater than | Yes | Left |
|
||||
|
@ -55,10 +55,41 @@ let result: i64 = engine.call_fn(&mut scope, &ast, "hello", () )?;
|
||||
let result: () = engine.call_fn(&mut scope, &ast, "hidden", ())?;
|
||||
```
|
||||
|
||||
|
||||
Low-Level API - `Engine::call_fn_dynamic`
|
||||
----------------------------------------
|
||||
|
||||
For more control, construct all arguments as `Dynamic` values and use `Engine::call_fn_dynamic`, passing it
|
||||
anything that implements `IntoIterator<Item = Dynamic>` (such as a simple `Vec<Dynamic>`):
|
||||
anything that implements `AsMut<Dynamic>` (such as a simple array or a `Vec<Dynamic>`):
|
||||
|
||||
```rust
|
||||
let result: Dynamic = engine.call_fn_dynamic(&mut scope, &ast, "hello",
|
||||
vec![ String::from("abc").into(), 123_i64.into() ])?;
|
||||
let result = engine.call_fn_dynamic(
|
||||
&mut scope, // scope to use
|
||||
ast.into(), // get 'Module' from 'AST'
|
||||
"hello", // function entry-point
|
||||
None, // 'this' pointer, if any
|
||||
[ String::from("abc").into(), 123_i64.into() ] // arguments
|
||||
)?;
|
||||
```
|
||||
|
||||
|
||||
Binding the `this` Pointer
|
||||
-------------------------
|
||||
|
||||
`Engine::call_fn_dynamic` can also bind a value to the `this` pointer of a script-defined function.
|
||||
|
||||
```rust
|
||||
let ast = engine.compile("fn action(x) { this += x; }")?;
|
||||
|
||||
let mut value: Dynamic = 1_i64.into();
|
||||
|
||||
let result = engine.call_fn_dynamic(
|
||||
&mut scope,
|
||||
ast.into(),
|
||||
"action",
|
||||
Some(&mut value), // binding the 'this' pointer
|
||||
[ 41_i64.into() ]
|
||||
)?;
|
||||
|
||||
assert_eq!(value.as_int().unwrap(), 42);
|
||||
```
|
||||
|
@ -18,8 +18,8 @@ use rhai::{Engine, RegisterFn};
|
||||
let mut engine = Engine::new();
|
||||
|
||||
// Register a custom operator called 'foo' and give it
|
||||
// a precedence of 140 (i.e. between +|- and *|/)
|
||||
engine.register_custom_operator("foo", 140).unwrap();
|
||||
// a precedence of 160 (i.e. between +|- and *|/)
|
||||
engine.register_custom_operator("foo", 160).unwrap();
|
||||
|
||||
// Register the implementation of the customer operator as a function
|
||||
engine.register_fn("foo", |x: i64, y: i64| (x * y) - (x + y));
|
||||
@ -49,7 +49,7 @@ Therefore, the following are equivalent (assuming `foo` has been registered as a
|
||||
A script using custom operators can always be pre-processed, via a pre-processor application,
|
||||
into a syntax that uses the corresponding function calls.
|
||||
|
||||
Using `Engine::register_custom_operator` merely enables a convenient short-cut.
|
||||
Using `Engine::register_custom_operator` merely enables a convenient shortcut.
|
||||
|
||||
|
||||
Must Follow Variable Naming
|
||||
@ -71,7 +71,7 @@ All custom operators must be _binary_ (i.e. they take two operands).
|
||||
_Unary_ custom operators are not supported.
|
||||
|
||||
```rust
|
||||
engine.register_custom_operator("foo", 140).unwrap();
|
||||
engine.register_custom_operator("foo", 160).unwrap();
|
||||
|
||||
engine.register_fn("foo", |x: i64| x * x);
|
||||
|
||||
@ -91,12 +91,12 @@ The following _precedence table_ show the built-in precedence of standard Rhai o
|
||||
| Assignments | `=`, `+=`, `-=`, `*=`, `/=`, `~=`, `%=`,<br/>`<<=`, `>>=`, `&=`, <code>\|=</code>, `^=` | 0 |
|
||||
| Logic and bit masks | <code>\|\|</code>, <code>\|</code>, `^` | 30 |
|
||||
| Logic and bit masks | `&`, `&&` | 60 |
|
||||
| Comparisons | `==`, `!=`, `>`, `>=`, `<`, `<=` | 90 |
|
||||
| | `in` | 110 |
|
||||
| Arithmetic | `+`, `-` | 130 |
|
||||
| Arithmetic | `*`, `/`, `~` | 160 |
|
||||
| Bit-shifts | `<<`, `>>` | 190 |
|
||||
| Arithmetic | `%` | 210 |
|
||||
| Comparisons | `==`, `!=` | 90 |
|
||||
| Comparisons | `>`, `>=`, `<`, `<=` | 110 |
|
||||
| | `in` | 130 |
|
||||
| Arithmetic | `+`, `-` | 150 |
|
||||
| Arithmetic | `*`, `/`, `~`, `%` | 180 |
|
||||
| Bit-shifts | `<<`, `>>` | 210 |
|
||||
| Object | `.` _(binds to right)_ | 240 |
|
||||
| _Others_ | | 0 |
|
||||
|
||||
|
@ -20,6 +20,7 @@
|
||||
[`Engine`]: {{rootUrl}}/engine/hello-world.md
|
||||
[traits]: {{rootUrl}}/rust/traits.md
|
||||
[`private`]: {{rootUrl}}/engine/call-fn.md
|
||||
[`call_fn`]: {{rootUrl}}/engine/call-fn.md
|
||||
[`Func`]: {{rootUrl}}/engine/func.md
|
||||
[`AST`]: {{rootUrl}}/engine/compile.md
|
||||
[`eval_expression`]: {{rootUrl}}/engine/expressions.md
|
||||
|
@ -16,7 +16,7 @@ use rhai::RegisterResultFn; // use 'RegisterResultFn' trait
|
||||
fn safe_divide(x: i64, y: i64) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
if y == 0 {
|
||||
// Return an error if y is zero
|
||||
Err("Division by zero!".into()) // short-cut to create Box<EvalAltResult::ErrorRuntime>
|
||||
Err("Division by zero!".into()) // shortcut to create Box<EvalAltResult::ErrorRuntime>
|
||||
} else {
|
||||
Ok((x / y).into()) // convert result into 'Dynamic'
|
||||
}
|
||||
|
127
doc/src/rust/register-raw.md
Normal file
127
doc/src/rust/register-raw.md
Normal file
@ -0,0 +1,127 @@
|
||||
Use the Low-Level API to Register a Rust Function
|
||||
================================================
|
||||
|
||||
{{#include ../links.md}}
|
||||
|
||||
When a native Rust function is registered with an `Engine` using the `Engine::register_XXX` API,
|
||||
Rhai transparently converts all function arguments from [`Dynamic`] into the correct types before
|
||||
calling the function.
|
||||
|
||||
For more power and flexibility, there is a _low-level_ API to work directly with [`Dynamic`] values
|
||||
without the conversions.
|
||||
|
||||
|
||||
Raw Function Registration
|
||||
-------------------------
|
||||
|
||||
The `Engine::register_raw_fn` method is marked _volatile_, meaning that it may be changed without warning.
|
||||
|
||||
If this is acceptable, then using this method to register a Rust function opens up more opportunities.
|
||||
|
||||
In particular, a reference to the current `Engine` instance is passed as an argument so the Rust function
|
||||
can also use `Engine` facilities (like evaluating a script).
|
||||
|
||||
```rust
|
||||
engine.register_raw_fn(
|
||||
"increment_by", // function name
|
||||
&[ // a slice containing parameter types
|
||||
std::any::TypeId::of::<i64>(), // type of first parameter
|
||||
std::any::TypeId::of::<i64>() // type of second parameter
|
||||
],
|
||||
|engine: &Engine, lib: &Module, args: &mut [&mut Dynamic]| { // fixed function signature
|
||||
// Arguments are guaranteed to be correct in number and of the correct types.
|
||||
|
||||
// But remember this is Rust, so you can keep only one mutable reference at any one time!
|
||||
// Therefore, get a '&mut' reference to the first argument _last_.
|
||||
// Alternatively, use `args.split_at_mut(1)` etc. to split the slice first.
|
||||
|
||||
let y: i64 = *args[1].downcast_ref::<i64>() // get a reference to the second argument
|
||||
.unwrap(); // then copying it because it is a primary type
|
||||
|
||||
let y: i64 = std::mem::take(args[1]).cast::<i64>(); // alternatively, directly 'consume' it
|
||||
|
||||
let x: &mut i64 = args[0].downcast_mut::<i64>() // get a '&mut' reference to the
|
||||
.unwrap(); // first argument
|
||||
|
||||
*x += y; // perform the action
|
||||
|
||||
Ok(().into()) // must be 'Result<Dynamic, Box<EvalAltResult>>'
|
||||
}
|
||||
);
|
||||
|
||||
// The above is the same as (in fact, internally they are equivalent):
|
||||
|
||||
engine.register_fn("increment_by", |x: &mut i64, y: i64| x += y);
|
||||
```
|
||||
|
||||
|
||||
Shortcuts
|
||||
---------
|
||||
|
||||
As usual with Rhai, there are shortcuts. For functions of zero to four parameters, which should be
|
||||
the majority, use one of the `Engine::register_raw_fn_n` (where `n = 0..4`) methods:
|
||||
|
||||
```rust
|
||||
// Specify parameter types as generics
|
||||
engine.register_raw_fn_2::<i64, i64>(
|
||||
"increment_by",
|
||||
|engine: &Engine, lib: &Module, args: &mut [&mut Dynamic]| { ... }
|
||||
);
|
||||
```
|
||||
|
||||
|
||||
Closure Signature
|
||||
-----------------
|
||||
|
||||
The closure passed to `Engine::register_raw_fn` takes the following form:
|
||||
|
||||
`Fn(engine: &Engine, lib: &Module, args: &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>> + 'static`
|
||||
|
||||
where:
|
||||
|
||||
* `engine` - a reference to the current [`Engine`], with all configurations and settings.
|
||||
|
||||
* `lib` - a reference to the current collection of script-defined functions, as a [`Module`].
|
||||
|
||||
* `args` - a reference to a slice containing `&mut` references to [`Dynamic`] values.
|
||||
The slice is guaranteed to contain enough arguments _of the correct types_.
|
||||
|
||||
Remember, in Rhai, all arguments _except_ the _first_ one are always passed by _value_ (i.e. cloned).
|
||||
Therefore, it is unnecessary to ever mutate any argument except the first one, as all mutations
|
||||
will be on the cloned copy.
|
||||
|
||||
|
||||
Extract Arguments
|
||||
-----------------
|
||||
|
||||
To extract an argument from the `args` parameter (`&mut [&mut Dynamic]`), use the following:
|
||||
|
||||
| Argument type | Access (`n` = argument position) | Result |
|
||||
| ------------------------------ | -------------------------------------- | ---------------------------------------------------------- |
|
||||
| [Primary type][standard types] | `args[n].clone().cast::<T>()` | Copy of value. |
|
||||
| Custom type | `args[n].downcast_ref::<T>().unwrap()` | Immutable reference to value. |
|
||||
| Custom type (consumed) | `mem::take(args[n]).cast::<T>()` | The _consumed_ value.<br/>The original value becomes `()`. |
|
||||
| `this` object | `args[0].downcast_mut::<T>().unwrap()` | Mutable reference to value. |
|
||||
|
||||
When there is a mutable reference to the `this` object (i.e. the first argument),
|
||||
there can be no other immutable references to `args`, otherwise the Rust borrow checker will complain.
|
||||
|
||||
|
||||
Hold Multiple References
|
||||
------------------------
|
||||
|
||||
In order to access a value argument that is expensive to clone _while_ holding a mutable reference
|
||||
to the first argument, either _consume_ that argument via `mem::take` as above, or use `args.split_at`
|
||||
to partition the slice:
|
||||
|
||||
```rust
|
||||
// Partition the slice
|
||||
let (first, rest) = args.split_at_mut(1);
|
||||
|
||||
// Mutable reference to the first parameter
|
||||
let this_ptr = first[0].downcast_mut::<A>().unwrap();
|
||||
|
||||
// Immutable reference to the second value parameter
|
||||
// This can be mutable but there is no point because the parameter is passed by value
|
||||
let value = rest[0].downcast_ref::<B>().unwrap();
|
||||
```
|
287
src/api.rs
287
src/api.rs
@ -6,6 +6,7 @@ use crate::error::ParseError;
|
||||
use crate::fn_call::FuncArgs;
|
||||
use crate::fn_native::{IteratorFn, SendSync};
|
||||
use crate::fn_register::RegisterFn;
|
||||
use crate::module::Module;
|
||||
use crate::optimize::{optimize_into_ast, OptimizationLevel};
|
||||
use crate::parser::AST;
|
||||
use crate::result::EvalAltResult;
|
||||
@ -32,6 +33,203 @@ use crate::stdlib::{fs::File, io::prelude::*, path::PathBuf};
|
||||
|
||||
/// Engine public API
|
||||
impl Engine {
|
||||
/// Register a function of the `Engine`.
|
||||
///
|
||||
/// ## WARNING - Low Level API
|
||||
///
|
||||
/// This function is very low level. It takes a list of `TypeId`'s indicating the actual types of the parameters.
|
||||
///
|
||||
/// Arguments are simply passed in as a mutable array of `&mut Dynamic`.
|
||||
/// The arguments are guaranteed to be of the correct types matching the `TypeId`'s.
|
||||
///
|
||||
/// To get access to a primary parameter value (i.e. cloning is cheap), use: `args[n].clone().cast::<T>()`
|
||||
///
|
||||
/// To get access to a parameter value and avoid cloning, use `std::mem::take(args[n]).cast::<T>()`.
|
||||
/// Notice that this will _consume_ the argument, replacing it with `()`.
|
||||
///
|
||||
/// To get access to the first mutable parameter, use `args.get_mut(0).unwrap()`
|
||||
#[deprecated(note = "this function is volatile and may change")]
|
||||
pub fn register_raw_fn(
|
||||
&mut self,
|
||||
name: &str,
|
||||
arg_types: &[TypeId],
|
||||
|
||||
#[cfg(not(feature = "sync"))] func: impl Fn(&Engine, &Module, &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>>
|
||||
+ 'static,
|
||||
|
||||
#[cfg(feature = "sync")] func: impl Fn(&Engine, &Module, &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>>
|
||||
+ Send
|
||||
+ Sync
|
||||
+ 'static,
|
||||
) {
|
||||
self.global_module.set_fn_var_args(name, arg_types, func);
|
||||
}
|
||||
|
||||
/// Register a function of no parameters with the `Engine`.
|
||||
///
|
||||
/// ## WARNING - Low Level API
|
||||
///
|
||||
/// This function is very low level.
|
||||
#[deprecated(note = "this function is volatile and may change")]
|
||||
pub fn register_raw_fn_0(
|
||||
&mut self,
|
||||
name: &str,
|
||||
|
||||
#[cfg(not(feature = "sync"))] func: impl Fn(&Engine, &Module, &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>>
|
||||
+ 'static,
|
||||
|
||||
#[cfg(feature = "sync")] func: impl Fn(&Engine, &Module, &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>>
|
||||
+ Send
|
||||
+ Sync
|
||||
+ 'static,
|
||||
) {
|
||||
self.global_module.set_fn_var_args(name, &[], func);
|
||||
}
|
||||
|
||||
/// Register a function of one parameter with the `Engine`.
|
||||
///
|
||||
/// ## WARNING - Low Level API
|
||||
///
|
||||
/// This function is very low level.
|
||||
///
|
||||
/// Arguments are simply passed in as a mutable array of `&mut Dynamic`.
|
||||
/// The argument is guaranteed to be of the correct type.
|
||||
///
|
||||
/// To get access to a primary parameter value (i.e. cloning is cheap), use: `args[n].clone().cast::<T>()`
|
||||
///
|
||||
/// To get access to a parameter value and avoid cloning, use `std::mem::take(args[n]).cast::<T>()`.
|
||||
/// Notice that this will _consume_ the argument, replacing it with `()`.
|
||||
///
|
||||
/// To get access to the first mutable parameter, use `args.get_mut(0).unwrap()`
|
||||
#[deprecated(note = "this function is volatile and may change")]
|
||||
pub fn register_raw_fn_1<A: Variant + Clone>(
|
||||
&mut self,
|
||||
name: &str,
|
||||
|
||||
#[cfg(not(feature = "sync"))] func: impl Fn(&Engine, &Module, &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>>
|
||||
+ 'static,
|
||||
|
||||
#[cfg(feature = "sync")] func: impl Fn(&Engine, &Module, &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>>
|
||||
+ Send
|
||||
+ Sync
|
||||
+ 'static,
|
||||
) {
|
||||
self.global_module
|
||||
.set_fn_var_args(name, &[TypeId::of::<A>()], func);
|
||||
}
|
||||
|
||||
/// Register a function of two parameters with the `Engine`.
|
||||
///
|
||||
/// ## WARNING - Low Level API
|
||||
///
|
||||
/// This function is very low level.
|
||||
///
|
||||
/// Arguments are simply passed in as a mutable array of `&mut Dynamic`.
|
||||
/// The arguments are guaranteed to be of the correct types.
|
||||
///
|
||||
/// To get access to a primary parameter value (i.e. cloning is cheap), use: `args[n].clone().cast::<T>()`
|
||||
///
|
||||
/// To get access to a parameter value and avoid cloning, use `std::mem::take(args[n]).cast::<T>()`.
|
||||
/// Notice that this will _consume_ the argument, replacing it with `()`.
|
||||
///
|
||||
/// To get access to the first mutable parameter, use `args.get_mut(0).unwrap()`
|
||||
#[deprecated(note = "this function is volatile and may change")]
|
||||
pub fn register_raw_fn_2<A: Variant + Clone, B: Variant + Clone>(
|
||||
&mut self,
|
||||
name: &str,
|
||||
|
||||
#[cfg(not(feature = "sync"))] func: impl Fn(&Engine, &Module, &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>>
|
||||
+ 'static,
|
||||
|
||||
#[cfg(feature = "sync")] func: impl Fn(&Engine, &Module, &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>>
|
||||
+ Send
|
||||
+ Sync
|
||||
+ 'static,
|
||||
) {
|
||||
self.global_module
|
||||
.set_fn_var_args(name, &[TypeId::of::<A>(), TypeId::of::<B>()], func);
|
||||
}
|
||||
|
||||
/// Register a function of three parameters with the `Engine`.
|
||||
///
|
||||
/// ## WARNING - Low Level API
|
||||
///
|
||||
/// This function is very low level.
|
||||
///
|
||||
/// Arguments are simply passed in as a mutable array of `&mut Dynamic`.
|
||||
/// The arguments are guaranteed to be of the correct types.
|
||||
///
|
||||
/// To get access to a primary parameter value (i.e. cloning is cheap), use: `args[n].clone().cast::<T>()`
|
||||
///
|
||||
/// To get access to a parameter value and avoid cloning, use `std::mem::take(args[n]).cast::<T>()`.
|
||||
/// Notice that this will _consume_ the argument, replacing it with `()`.
|
||||
///
|
||||
/// To get access to the first mutable parameter, use `args.get_mut(0).unwrap()`
|
||||
#[deprecated(note = "this function is volatile and may change")]
|
||||
pub fn register_raw_fn_3<A: Variant + Clone, B: Variant + Clone, C: Variant + Clone>(
|
||||
&mut self,
|
||||
name: &str,
|
||||
|
||||
#[cfg(not(feature = "sync"))] func: impl Fn(&Engine, &Module, &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>>
|
||||
+ 'static,
|
||||
|
||||
#[cfg(feature = "sync")] func: impl Fn(&Engine, &Module, &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>>
|
||||
+ Send
|
||||
+ Sync
|
||||
+ 'static,
|
||||
) {
|
||||
self.global_module.set_fn_var_args(
|
||||
name,
|
||||
&[TypeId::of::<A>(), TypeId::of::<B>(), TypeId::of::<C>()],
|
||||
func,
|
||||
);
|
||||
}
|
||||
|
||||
/// Register a function of four parameters with the `Engine`.
|
||||
///
|
||||
/// ## WARNING - Low Level API
|
||||
///
|
||||
/// This function is very low level.
|
||||
///
|
||||
/// Arguments are simply passed in as a mutable array of `&mut Dynamic`.
|
||||
/// The arguments are guaranteed to be of the correct types.
|
||||
///
|
||||
/// To get access to a primary parameter value (i.e. cloning is cheap), use: `args[n].clone().cast::<T>()`
|
||||
///
|
||||
/// To get access to a parameter value and avoid cloning, use `std::mem::take(args[n]).cast::<T>()`.
|
||||
/// Notice that this will _consume_ the argument, replacing it with `()`.
|
||||
///
|
||||
/// To get access to the first mutable parameter, use `args.get_mut(0).unwrap()`
|
||||
#[deprecated(note = "this function is volatile and may change")]
|
||||
pub fn register_raw_fn_4<
|
||||
A: Variant + Clone,
|
||||
B: Variant + Clone,
|
||||
C: Variant + Clone,
|
||||
D: Variant + Clone,
|
||||
>(
|
||||
&mut self,
|
||||
name: &str,
|
||||
|
||||
#[cfg(not(feature = "sync"))] func: impl Fn(&Engine, &Module, &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>>
|
||||
+ 'static,
|
||||
|
||||
#[cfg(feature = "sync")] func: impl Fn(&Engine, &Module, &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>>
|
||||
+ Send
|
||||
+ Sync
|
||||
+ 'static,
|
||||
) {
|
||||
self.global_module.set_fn_var_args(
|
||||
name,
|
||||
&[
|
||||
TypeId::of::<A>(),
|
||||
TypeId::of::<B>(),
|
||||
TypeId::of::<C>(),
|
||||
TypeId::of::<D>(),
|
||||
],
|
||||
func,
|
||||
);
|
||||
}
|
||||
|
||||
/// Register a custom type for use with the `Engine`.
|
||||
/// The type must implement `Clone`.
|
||||
///
|
||||
@ -427,7 +625,7 @@ impl Engine {
|
||||
self.register_indexer_set(setter);
|
||||
}
|
||||
|
||||
/// Compile a string into an [`AST`], which can be used later for evaluation.
|
||||
/// Compile a string into an `AST`, which can be used later for evaluation.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
@ -450,7 +648,7 @@ impl Engine {
|
||||
self.compile_with_scope(&Scope::new(), script)
|
||||
}
|
||||
|
||||
/// Compile a string into an [`AST`] using own scope, which can be used later for evaluation.
|
||||
/// Compile a string into an `AST` using own scope, which can be used later for evaluation.
|
||||
///
|
||||
/// The scope is useful for passing constants into the script for optimization
|
||||
/// when using `OptimizationLevel::Full`.
|
||||
@ -493,7 +691,7 @@ impl Engine {
|
||||
}
|
||||
|
||||
/// When passed a list of strings, first join the strings into one large script,
|
||||
/// and then compile them into an [`AST`] using own scope, which can be used later for evaluation.
|
||||
/// and then compile them into an `AST` using own scope, which can be used later for evaluation.
|
||||
///
|
||||
/// The scope is useful for passing constants into the script for optimization
|
||||
/// when using `OptimizationLevel::Full`.
|
||||
@ -546,7 +744,7 @@ impl Engine {
|
||||
self.compile_with_scope_and_optimization_level(scope, scripts, self.optimization_level)
|
||||
}
|
||||
|
||||
/// Join a list of strings and compile into an [`AST`] using own scope at a specific optimization level.
|
||||
/// Join a list of strings and compile into an `AST` using own scope at a specific optimization level.
|
||||
pub(crate) fn compile_with_scope_and_optimization_level(
|
||||
&self,
|
||||
scope: &Scope,
|
||||
@ -582,7 +780,7 @@ impl Engine {
|
||||
Ok(contents)
|
||||
}
|
||||
|
||||
/// Compile a script file into an [`AST`], which can be used later for evaluation.
|
||||
/// Compile a script file into an `AST`, which can be used later for evaluation.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
@ -608,7 +806,7 @@ impl Engine {
|
||||
self.compile_file_with_scope(&Scope::new(), path)
|
||||
}
|
||||
|
||||
/// Compile a script file into an [`AST`] using own scope, which can be used later for evaluation.
|
||||
/// Compile a script file into an `AST` using own scope, which can be used later for evaluation.
|
||||
///
|
||||
/// The scope is useful for passing constants into the script for optimization
|
||||
/// when using `OptimizationLevel::Full`.
|
||||
@ -690,7 +888,7 @@ impl Engine {
|
||||
self.eval_ast_with_scope(&mut scope, &ast)
|
||||
}
|
||||
|
||||
/// Compile a string containing an expression into an [`AST`],
|
||||
/// Compile a string containing an expression into an `AST`,
|
||||
/// which can be used later for evaluation.
|
||||
///
|
||||
/// # Example
|
||||
@ -714,7 +912,7 @@ impl Engine {
|
||||
self.compile_expression_with_scope(&Scope::new(), script)
|
||||
}
|
||||
|
||||
/// Compile a string containing an expression into an [`AST`] using own scope,
|
||||
/// Compile a string containing an expression into an `AST` using own scope,
|
||||
/// which can be used later for evaluation.
|
||||
///
|
||||
/// The scope is useful for passing constants into the script for optimization
|
||||
@ -922,7 +1120,7 @@ impl Engine {
|
||||
self.eval_ast_with_scope(scope, &ast)
|
||||
}
|
||||
|
||||
/// Evaluate an [`AST`].
|
||||
/// Evaluate an `AST`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
@ -944,7 +1142,7 @@ impl Engine {
|
||||
self.eval_ast_with_scope(&mut Scope::new(), ast)
|
||||
}
|
||||
|
||||
/// Evaluate an [`AST`] with own scope.
|
||||
/// Evaluate an `AST` with own scope.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
@ -992,7 +1190,7 @@ impl Engine {
|
||||
});
|
||||
}
|
||||
|
||||
/// Evaluate an [`AST`] with own scope.
|
||||
/// Evaluate an `AST` with own scope.
|
||||
pub(crate) fn eval_ast_with_scope_raw<'a>(
|
||||
&self,
|
||||
scope: &mut Scope,
|
||||
@ -1058,7 +1256,7 @@ impl Engine {
|
||||
self.consume_ast_with_scope(&mut Scope::new(), ast)
|
||||
}
|
||||
|
||||
/// Evaluate an [`AST`] with own scope, but throw away the result and only return error (if any).
|
||||
/// Evaluate an `AST` with own scope, but throw away the result and only return error (if any).
|
||||
/// Useful for when you don't need the result, but still need to keep track of possible errors.
|
||||
pub fn consume_ast_with_scope(
|
||||
&self,
|
||||
@ -1082,7 +1280,7 @@ impl Engine {
|
||||
)
|
||||
}
|
||||
|
||||
/// Call a script function defined in an [`AST`] with multiple arguments.
|
||||
/// Call a script function defined in an `AST` with multiple arguments.
|
||||
/// Arguments are passed as a tuple.
|
||||
///
|
||||
/// # Example
|
||||
@ -1127,7 +1325,7 @@ impl Engine {
|
||||
args: A,
|
||||
) -> Result<T, Box<EvalAltResult>> {
|
||||
let mut arg_values = args.into_vec();
|
||||
let result = self.call_fn_dynamic_raw(scope, ast, name, arg_values.as_mut())?;
|
||||
let result = self.call_fn_dynamic_raw(scope, ast, name, &mut None, arg_values.as_mut())?;
|
||||
|
||||
let typ = self.map_type_name(result.type_name());
|
||||
|
||||
@ -1140,7 +1338,15 @@ impl Engine {
|
||||
});
|
||||
}
|
||||
|
||||
/// Call a script function defined in an [`AST`] with multiple `Dynamic` arguments.
|
||||
/// Call a script function defined in an `AST` with multiple `Dynamic` arguments
|
||||
/// and optionally a value for binding to the 'this' pointer.
|
||||
///
|
||||
/// ## WARNING
|
||||
///
|
||||
/// All the arguments are _consumed_, meaning that they're replaced by `()`.
|
||||
/// This is to avoid unnecessarily cloning the arguments.
|
||||
/// Do not use the arguments after this call. If they are needed afterwards,
|
||||
/// clone them _before_ calling this function.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
@ -1148,7 +1354,7 @@ impl Engine {
|
||||
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
||||
/// # #[cfg(not(feature = "no_function"))]
|
||||
/// # {
|
||||
/// use rhai::{Engine, Scope};
|
||||
/// use rhai::{Engine, Scope, Dynamic};
|
||||
///
|
||||
/// let engine = Engine::new();
|
||||
///
|
||||
@ -1156,20 +1362,27 @@ impl Engine {
|
||||
/// fn add(x, y) { len(x) + y + foo }
|
||||
/// fn add1(x) { len(x) + 1 + foo }
|
||||
/// fn bar() { foo/2 }
|
||||
/// fn action(x) { this += x; } // function using 'this' pointer
|
||||
/// ")?;
|
||||
///
|
||||
/// let mut scope = Scope::new();
|
||||
/// scope.push("foo", 42_i64);
|
||||
///
|
||||
/// // Call the script-defined function
|
||||
/// let result = engine.call_fn_dynamic(&mut scope, &ast, "add", vec![ String::from("abc").into(), 123_i64.into() ])?;
|
||||
/// let result = engine.call_fn_dynamic(&mut scope, &ast, "add", None, [ String::from("abc").into(), 123_i64.into() ])?;
|
||||
/// // ^^^^ no 'this' pointer
|
||||
/// assert_eq!(result.cast::<i64>(), 168);
|
||||
///
|
||||
/// let result = engine.call_fn_dynamic(&mut scope, &ast, "add1", vec![ String::from("abc").into() ])?;
|
||||
/// let result = engine.call_fn_dynamic(&mut scope, &ast, "add1", None, [ String::from("abc").into() ])?;
|
||||
/// assert_eq!(result.cast::<i64>(), 46);
|
||||
///
|
||||
/// let result= engine.call_fn_dynamic(&mut scope, &ast, "bar", vec![])?;
|
||||
/// let result = engine.call_fn_dynamic(&mut scope, &ast, "bar", None, [])?;
|
||||
/// assert_eq!(result.cast::<i64>(), 21);
|
||||
///
|
||||
/// let mut value: Dynamic = 1_i64.into();
|
||||
/// let result = engine.call_fn_dynamic(&mut scope, &ast, "action", Some(&mut value), [ 41_i64.into() ])?;
|
||||
/// // ^^^^^^^^^^^^^^^^ binding the 'this' pointer
|
||||
/// assert_eq!(value.as_int().unwrap(), 42);
|
||||
/// # }
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
@ -1178,15 +1391,15 @@ impl Engine {
|
||||
pub fn call_fn_dynamic(
|
||||
&self,
|
||||
scope: &mut Scope,
|
||||
ast: &AST,
|
||||
lib: impl AsRef<Module>,
|
||||
name: &str,
|
||||
arg_values: impl IntoIterator<Item = Dynamic>,
|
||||
mut this_ptr: Option<&mut Dynamic>,
|
||||
mut arg_values: impl AsMut<[Dynamic]>,
|
||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
let mut arg_values: StaticVec<_> = arg_values.into_iter().collect();
|
||||
self.call_fn_dynamic_raw(scope, ast, name, arg_values.as_mut())
|
||||
self.call_fn_dynamic_raw(scope, lib, name, &mut this_ptr, arg_values.as_mut())
|
||||
}
|
||||
|
||||
/// Call a script function defined in an [`AST`] with multiple `Dynamic` arguments.
|
||||
/// Call a script function defined in an `AST` with multiple `Dynamic` arguments.
|
||||
///
|
||||
/// ## WARNING
|
||||
///
|
||||
@ -1198,13 +1411,15 @@ impl Engine {
|
||||
pub(crate) fn call_fn_dynamic_raw(
|
||||
&self,
|
||||
scope: &mut Scope,
|
||||
ast: &AST,
|
||||
lib: impl AsRef<Module>,
|
||||
name: &str,
|
||||
this_ptr: &mut Option<&mut Dynamic>,
|
||||
arg_values: &mut [Dynamic],
|
||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
let lib = lib.as_ref();
|
||||
let mut args: StaticVec<_> = arg_values.iter_mut().collect();
|
||||
let fn_def = get_script_function_by_signature(ast.lib(), name, args.len(), true)
|
||||
.ok_or_else(|| {
|
||||
let fn_def =
|
||||
get_script_function_by_signature(lib, name, args.len(), true).ok_or_else(|| {
|
||||
Box::new(EvalAltResult::ErrorFunctionNotFound(
|
||||
name.into(),
|
||||
Position::none(),
|
||||
@ -1216,27 +1431,19 @@ impl Engine {
|
||||
let args = args.as_mut();
|
||||
|
||||
self.call_script_fn(
|
||||
scope,
|
||||
&mut mods,
|
||||
&mut state,
|
||||
ast.lib(),
|
||||
&mut None,
|
||||
name,
|
||||
fn_def,
|
||||
args,
|
||||
0,
|
||||
scope, &mut mods, &mut state, lib, this_ptr, name, fn_def, args, 0,
|
||||
)
|
||||
}
|
||||
|
||||
/// Optimize the [`AST`] with constants defined in an external Scope.
|
||||
/// An optimized copy of the [`AST`] is returned while the original [`AST`] is consumed.
|
||||
/// Optimize the `AST` with constants defined in an external Scope.
|
||||
/// An optimized copy of the `AST` is returned while the original `AST` is consumed.
|
||||
///
|
||||
/// Although optimization is performed by default during compilation, sometimes it is necessary to
|
||||
/// _re_-optimize an AST. For example, when working with constants that are passed in via an
|
||||
/// external scope, it will be more efficient to optimize the [`AST`] once again to take advantage
|
||||
/// external scope, it will be more efficient to optimize the `AST` once again to take advantage
|
||||
/// of the new constants.
|
||||
///
|
||||
/// With this method, it is no longer necessary to recompile a large script. The script [`AST`] can be
|
||||
/// With this method, it is no longer necessary to recompile a large script. The script `AST` can be
|
||||
/// compiled just once. Before evaluation, constants are passed into the `Engine` via an external scope
|
||||
/// (i.e. with `scope.push_constant(...)`). Then, the `AST is cloned and the copy re-optimized before running.
|
||||
#[cfg(not(feature = "no_optimize"))]
|
||||
|
@ -661,7 +661,7 @@ impl Engine {
|
||||
}
|
||||
|
||||
// Run external function
|
||||
let result = func.get_native_fn()(self, args)?;
|
||||
let result = func.get_native_fn()(self, lib, args)?;
|
||||
|
||||
// Restore the original reference
|
||||
restore_first_arg(old_this_ptr, args);
|
||||
@ -1616,7 +1616,7 @@ impl Engine {
|
||||
.or_else(|| self.packages.get_fn(hash_fn))
|
||||
{
|
||||
// Overriding exact implementation
|
||||
func(self, &mut [lhs_ptr, &mut rhs_val])?;
|
||||
func(self, lib, &mut [lhs_ptr, &mut rhs_val])?;
|
||||
} else if run_builtin_op_assignment(op, lhs_ptr, &rhs_val)?.is_none() {
|
||||
// Not built in, map to `var = var op rhs`
|
||||
let op = &op[..op.len() - 1]; // extract operator without =
|
||||
@ -1663,14 +1663,20 @@ impl Engine {
|
||||
Expr::Variable(_) => unreachable!(),
|
||||
// idx_lhs[idx_expr] op= rhs
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Expr::Index(_) => self.eval_dot_index_chain(
|
||||
scope, mods, state, lib, this_ptr, lhs_expr, level, new_val,
|
||||
),
|
||||
Expr::Index(_) => {
|
||||
self.eval_dot_index_chain(
|
||||
scope, mods, state, lib, this_ptr, lhs_expr, level, new_val,
|
||||
)?;
|
||||
Ok(Default::default())
|
||||
}
|
||||
// dot_lhs.dot_rhs op= rhs
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Expr::Dot(_) => self.eval_dot_index_chain(
|
||||
scope, mods, state, lib, this_ptr, lhs_expr, level, new_val,
|
||||
),
|
||||
Expr::Dot(_) => {
|
||||
self.eval_dot_index_chain(
|
||||
scope, mods, state, lib, this_ptr, lhs_expr, level, new_val,
|
||||
)?;
|
||||
Ok(Default::default())
|
||||
}
|
||||
// Error assignment to constant
|
||||
expr if expr.is_constant() => {
|
||||
Err(Box::new(EvalAltResult::ErrorAssignmentToConstant(
|
||||
@ -1880,9 +1886,8 @@ impl Engine {
|
||||
.map_err(|err| err.new_position(*pos))
|
||||
}
|
||||
Ok(f) if f.is_plugin_fn() => f.get_plugin_fn().call(args.as_mut(), *pos),
|
||||
Ok(f) => {
|
||||
f.get_native_fn()(self, args.as_mut()).map_err(|err| err.new_position(*pos))
|
||||
}
|
||||
Ok(f) => f.get_native_fn()(self, lib, args.as_mut())
|
||||
.map_err(|err| err.new_position(*pos)),
|
||||
Err(err) => match *err {
|
||||
EvalAltResult::ErrorFunctionNotFound(_, _) if def_val.is_some() => {
|
||||
Ok(def_val.clone().unwrap())
|
||||
@ -1966,15 +1971,7 @@ impl Engine {
|
||||
Stmt::Noop(_) => Ok(Default::default()),
|
||||
|
||||
// Expression as statement
|
||||
Stmt::Expr(expr) => {
|
||||
let result = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?;
|
||||
|
||||
Ok(match expr.as_ref() {
|
||||
// If it is a simple assignment, erase the result at the root
|
||||
Expr::Assignment(_) => Default::default(),
|
||||
_ => result,
|
||||
})
|
||||
}
|
||||
Stmt::Expr(expr) => self.eval_expr(scope, mods, state, lib, this_ptr, expr, level),
|
||||
|
||||
// Block scope
|
||||
Stmt::Block(x) => {
|
||||
@ -2448,6 +2445,7 @@ fn run_builtin_binary_op(
|
||||
match op {
|
||||
"&" => return Ok(Some((x && y).into())),
|
||||
"|" => return Ok(Some((x || y).into())),
|
||||
"^" => return Ok(Some((x ^ y).into())),
|
||||
"==" => return Ok(Some((x == y).into())),
|
||||
"!=" => return Ok(Some((x != y).into())),
|
||||
_ => (),
|
||||
|
@ -15,8 +15,8 @@ use crate::stdlib::{boxed::Box, string::ToString};
|
||||
pub trait Func<ARGS, RET> {
|
||||
type Output;
|
||||
|
||||
/// Create a Rust anonymous function from an [`AST`].
|
||||
/// The `Engine` and [`AST`] are consumed and basically embedded into the closure.
|
||||
/// Create a Rust anonymous function from an `AST`.
|
||||
/// The `Engine` and `AST` are consumed and basically embedded into the closure.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
|
@ -1,5 +1,6 @@
|
||||
use crate::any::Dynamic;
|
||||
use crate::engine::Engine;
|
||||
use crate::module::Module;
|
||||
use crate::parser::ScriptFnDef;
|
||||
use crate::plugin::PluginFunction;
|
||||
use crate::result::EvalAltResult;
|
||||
@ -80,11 +81,11 @@ impl<S: Into<ImmutableString>> From<S> for FnPtr {
|
||||
|
||||
/// A general function trail object.
|
||||
#[cfg(not(feature = "sync"))]
|
||||
pub type FnAny = dyn Fn(&Engine, &mut FnCallArgs) -> Result<Dynamic, Box<EvalAltResult>>;
|
||||
pub type FnAny = dyn Fn(&Engine, &Module, &mut FnCallArgs) -> Result<Dynamic, Box<EvalAltResult>>;
|
||||
/// A general function trail object.
|
||||
#[cfg(feature = "sync")]
|
||||
pub type FnAny =
|
||||
dyn Fn(&Engine, &mut FnCallArgs) -> Result<Dynamic, Box<EvalAltResult>> + Send + Sync;
|
||||
dyn Fn(&Engine, &Module, &mut FnCallArgs) -> Result<Dynamic, Box<EvalAltResult>> + Send + Sync;
|
||||
|
||||
/// A standard function that gets an iterator from a type.
|
||||
pub type IteratorFn = fn(Dynamic) -> Box<dyn Iterator<Item = Dynamic>>;
|
||||
|
@ -4,6 +4,7 @@
|
||||
use crate::any::{Dynamic, Variant};
|
||||
use crate::engine::Engine;
|
||||
use crate::fn_native::{CallableFunction, FnAny, FnCallArgs, SendSync};
|
||||
use crate::module::Module;
|
||||
use crate::parser::FnAccess;
|
||||
use crate::plugin::Plugin;
|
||||
use crate::result::EvalAltResult;
|
||||
@ -205,7 +206,7 @@ macro_rules! make_func {
|
||||
// ^ function parameter generic type name (A, B, C etc.)
|
||||
// ^ dereferencing function
|
||||
|
||||
Box::new(move |_: &Engine, args: &mut FnCallArgs| {
|
||||
Box::new(move |_: &Engine, _: &Module, args: &mut FnCallArgs| {
|
||||
// The arguments are assumed to be of the correct number and types!
|
||||
|
||||
#[allow(unused_variables, unused_mut)]
|
||||
|
@ -62,7 +62,7 @@
|
||||
//! | `only_i32` | Set the system integer type to `i32` and disable all other integer types. `INT` is set to `i32`. |
|
||||
//! | `only_i64` | Set the system integer type to `i64` and disable all other integer types. `INT` is set to `i64`. |
|
||||
//! | `no_std` | Build for `no-std`. Notice that additional dependencies will be pulled in to replace `std` features. |
|
||||
//! | `sync` | Restrict all values types to those that are `Send + Sync`. Under this feature, `Engine`, `Scope` and [`AST`] are all `Send + Sync`. |
|
||||
//! | `sync` | Restrict all values types to those that are `Send + Sync`. Under this feature, `Engine`, `Scope` and `AST` are all `Send + Sync`. |
|
||||
//! | `serde` | Enable serialization/deserialization via `serde`. Notice that the [`serde`](https://crates.io/crates/serde) crate will be pulled in together with its dependencies. |
|
||||
//! | `internals` | Expose internal data structures (beware they may be volatile from version to version). |
|
||||
//!
|
||||
|
@ -102,6 +102,12 @@ impl Clone for Module {
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<Module> for Module {
|
||||
fn as_ref(&self) -> &Module {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Module {
|
||||
/// Create a new module.
|
||||
///
|
||||
@ -373,9 +379,11 @@ impl Module {
|
||||
&mut self,
|
||||
name: impl Into<String>,
|
||||
args: &[TypeId],
|
||||
func: impl Fn(&Engine, &mut [&mut Dynamic]) -> FuncReturn<T> + SendSync + 'static,
|
||||
func: impl Fn(&Engine, &Module, &mut [&mut Dynamic]) -> FuncReturn<T> + SendSync + 'static,
|
||||
) -> u64 {
|
||||
let f = move |engine: &Engine, args: &mut FnCallArgs| func(engine, args).map(Dynamic::from);
|
||||
let f = move |engine: &Engine, lib: &Module, args: &mut FnCallArgs| {
|
||||
func(engine, lib, args).map(Dynamic::from)
|
||||
};
|
||||
self.set_fn(
|
||||
name,
|
||||
Public,
|
||||
@ -402,7 +410,7 @@ impl Module {
|
||||
name: impl Into<String>,
|
||||
func: impl Fn() -> FuncReturn<T> + SendSync + 'static,
|
||||
) -> u64 {
|
||||
let f = move |_: &Engine, _: &mut FnCallArgs| func().map(Dynamic::from);
|
||||
let f = move |_: &Engine, _: &Module, _: &mut FnCallArgs| func().map(Dynamic::from);
|
||||
let args = [];
|
||||
self.set_fn(
|
||||
name,
|
||||
@ -430,7 +438,7 @@ impl Module {
|
||||
name: impl Into<String>,
|
||||
func: impl Fn(A) -> FuncReturn<T> + SendSync + 'static,
|
||||
) -> u64 {
|
||||
let f = move |_: &Engine, args: &mut FnCallArgs| {
|
||||
let f = move |_: &Engine, _: &Module, args: &mut FnCallArgs| {
|
||||
func(mem::take(args[0]).cast::<A>()).map(Dynamic::from)
|
||||
};
|
||||
let args = [TypeId::of::<A>()];
|
||||
@ -460,7 +468,7 @@ impl Module {
|
||||
name: impl Into<String>,
|
||||
func: impl Fn(&mut A) -> FuncReturn<T> + SendSync + 'static,
|
||||
) -> u64 {
|
||||
let f = move |_: &Engine, args: &mut FnCallArgs| {
|
||||
let f = move |_: &Engine, _: &Module, args: &mut FnCallArgs| {
|
||||
func(args[0].downcast_mut::<A>().unwrap()).map(Dynamic::from)
|
||||
};
|
||||
let args = [TypeId::of::<A>()];
|
||||
@ -514,7 +522,7 @@ impl Module {
|
||||
name: impl Into<String>,
|
||||
func: impl Fn(A, B) -> FuncReturn<T> + SendSync + 'static,
|
||||
) -> u64 {
|
||||
let f = move |_: &Engine, args: &mut FnCallArgs| {
|
||||
let f = move |_: &Engine, _: &Module, args: &mut FnCallArgs| {
|
||||
let a = mem::take(args[0]).cast::<A>();
|
||||
let b = mem::take(args[1]).cast::<B>();
|
||||
|
||||
@ -550,7 +558,7 @@ impl Module {
|
||||
name: impl Into<String>,
|
||||
func: impl Fn(&mut A, B) -> FuncReturn<T> + SendSync + 'static,
|
||||
) -> u64 {
|
||||
let f = move |_: &Engine, args: &mut FnCallArgs| {
|
||||
let f = move |_: &Engine, _: &Module, args: &mut FnCallArgs| {
|
||||
let b = mem::take(args[1]).cast::<B>();
|
||||
let a = args[0].downcast_mut::<A>().unwrap();
|
||||
|
||||
@ -641,7 +649,7 @@ impl Module {
|
||||
name: impl Into<String>,
|
||||
func: impl Fn(A, B, C) -> FuncReturn<T> + SendSync + 'static,
|
||||
) -> u64 {
|
||||
let f = move |_: &Engine, args: &mut FnCallArgs| {
|
||||
let f = move |_: &Engine, _: &Module, args: &mut FnCallArgs| {
|
||||
let a = mem::take(args[0]).cast::<A>();
|
||||
let b = mem::take(args[1]).cast::<B>();
|
||||
let c = mem::take(args[2]).cast::<C>();
|
||||
@ -683,7 +691,7 @@ impl Module {
|
||||
name: impl Into<String>,
|
||||
func: impl Fn(&mut A, B, C) -> FuncReturn<T> + SendSync + 'static,
|
||||
) -> u64 {
|
||||
let f = move |_: &Engine, args: &mut FnCallArgs| {
|
||||
let f = move |_: &Engine, _: &Module, args: &mut FnCallArgs| {
|
||||
let b = mem::take(args[1]).cast::<B>();
|
||||
let c = mem::take(args[2]).cast::<C>();
|
||||
let a = args[0].downcast_mut::<A>().unwrap();
|
||||
@ -720,7 +728,7 @@ impl Module {
|
||||
&mut self,
|
||||
func: impl Fn(&mut A, B, A) -> FuncReturn<()> + SendSync + 'static,
|
||||
) -> u64 {
|
||||
let f = move |_: &Engine, args: &mut FnCallArgs| {
|
||||
let f = move |_: &Engine, _: &Module, args: &mut FnCallArgs| {
|
||||
let b = mem::take(args[1]).cast::<B>();
|
||||
let c = mem::take(args[2]).cast::<A>();
|
||||
let a = args[0].downcast_mut::<A>().unwrap();
|
||||
@ -762,7 +770,7 @@ impl Module {
|
||||
name: impl Into<String>,
|
||||
func: impl Fn(A, B, C, D) -> FuncReturn<T> + SendSync + 'static,
|
||||
) -> u64 {
|
||||
let f = move |_: &Engine, args: &mut FnCallArgs| {
|
||||
let f = move |_: &Engine, _: &Module, args: &mut FnCallArgs| {
|
||||
let a = mem::take(args[0]).cast::<A>();
|
||||
let b = mem::take(args[1]).cast::<B>();
|
||||
let c = mem::take(args[2]).cast::<C>();
|
||||
@ -811,7 +819,7 @@ impl Module {
|
||||
name: impl Into<String>,
|
||||
func: impl Fn(&mut A, B, C, D) -> FuncReturn<T> + SendSync + 'static,
|
||||
) -> u64 {
|
||||
let f = move |_: &Engine, args: &mut FnCallArgs| {
|
||||
let f = move |_: &Engine, _: &Module, args: &mut FnCallArgs| {
|
||||
let b = mem::take(args[1]).cast::<B>();
|
||||
let c = mem::take(args[2]).cast::<C>();
|
||||
let d = mem::take(args[3]).cast::<D>();
|
||||
@ -942,7 +950,7 @@ impl Module {
|
||||
.map(|f| f.get_shared_fn_def())
|
||||
}
|
||||
|
||||
/// Create a new `Module` by evaluating an [`AST`].
|
||||
/// Create a new `Module` by evaluating an `AST`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
|
@ -382,23 +382,7 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr {
|
||||
stmt => Expr::Stmt(Box::new((stmt, x.1))),
|
||||
},
|
||||
// id op= expr
|
||||
Expr::Assignment(x) => match x.2 {
|
||||
//id = id2 op= rhs
|
||||
Expr::Assignment(x2) if x.1.is_empty() => match (x.0, &x2.0) {
|
||||
// var = var op= expr2 -> var op= expr2
|
||||
(Expr::Variable(a), Expr::Variable(b))
|
||||
if a.1.is_none() && b.1.is_none() && a.0 == b.0 && a.3 == b.3 =>
|
||||
{
|
||||
// Assignment to the same variable - fold
|
||||
state.set_dirty();
|
||||
Expr::Assignment(Box::new((Expr::Variable(a), x2.1, optimize_expr(x2.2, state), x.3)))
|
||||
}
|
||||
// expr1 = expr2 op= rhs
|
||||
(expr1, _) => Expr::Assignment(Box::new((expr1, x.1, optimize_expr(Expr::Assignment(x2), state), x.3))),
|
||||
},
|
||||
// expr = rhs
|
||||
expr => Expr::Assignment(Box::new((x.0, x.1, optimize_expr(expr, state), x.3))),
|
||||
},
|
||||
Expr::Assignment(x) => Expr::Assignment(Box::new((x.0, x.1, optimize_expr(x.2, state), x.3))),
|
||||
|
||||
// lhs.rhs
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
|
@ -3,7 +3,7 @@
|
||||
use crate::any::{Dynamic, Variant};
|
||||
use crate::def_package;
|
||||
use crate::engine::{Array, Engine};
|
||||
use crate::module::FuncReturn;
|
||||
use crate::module::{FuncReturn, Module};
|
||||
use crate::parser::{ImmutableString, INT};
|
||||
use crate::result::EvalAltResult;
|
||||
use crate::token::Position;
|
||||
@ -25,7 +25,11 @@ fn ins<T: Variant + Clone>(list: &mut Array, position: INT, item: T) -> FuncRetu
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
fn pad<T: Variant + Clone>(engine: &Engine, args: &mut [&mut Dynamic]) -> FuncReturn<()> {
|
||||
fn pad<T: Variant + Clone>(
|
||||
engine: &Engine,
|
||||
_: &Module,
|
||||
args: &mut [&mut Dynamic],
|
||||
) -> FuncReturn<()> {
|
||||
let len = *args[1].downcast_ref::<INT>().unwrap();
|
||||
|
||||
// Check if array will be over max size limit
|
||||
|
@ -1,7 +1,7 @@
|
||||
use crate::any::Dynamic;
|
||||
use crate::def_package;
|
||||
use crate::engine::Engine;
|
||||
use crate::module::FuncReturn;
|
||||
use crate::module::{FuncReturn, Module};
|
||||
use crate::parser::{ImmutableString, INT};
|
||||
use crate::result::EvalAltResult;
|
||||
use crate::token::Position;
|
||||
@ -226,7 +226,7 @@ def_package!(crate:MoreStringPackage:"Additional string utilities, including str
|
||||
lib.set_fn_var_args(
|
||||
"pad",
|
||||
&[TypeId::of::<ImmutableString>(), TypeId::of::<INT>(), TypeId::of::<char>()],
|
||||
|engine: &Engine, args: &mut [&mut Dynamic]| {
|
||||
|engine: &Engine, _: &Module, args: &mut [&mut Dynamic]| {
|
||||
let len = *args[1].downcast_ref::< INT>().unwrap();
|
||||
|
||||
// Check if string will be over max size limit
|
||||
|
@ -49,7 +49,7 @@ pub use crate::utils::ImmutableString;
|
||||
|
||||
/// Compiled AST (abstract syntax tree) of a Rhai script.
|
||||
///
|
||||
/// Currently, [`AST`] is neither `Send` nor `Sync`. Turn on the `sync` feature to make it `Send + Sync`.
|
||||
/// Currently, `AST` is neither `Send` nor `Sync`. Turn on the `sync` feature to make it `Send + Sync`.
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct AST(
|
||||
/// Global statements.
|
||||
@ -59,21 +59,21 @@ pub struct AST(
|
||||
);
|
||||
|
||||
impl AST {
|
||||
/// Create a new [`AST`].
|
||||
/// Create a new `AST`.
|
||||
pub fn new(statements: Vec<Stmt>, lib: Module) -> Self {
|
||||
Self(statements, lib)
|
||||
}
|
||||
|
||||
/// Get the statements.
|
||||
#[cfg(not(feature = "internals"))]
|
||||
pub(crate) fn statements(&self) -> &Vec<Stmt> {
|
||||
pub(crate) fn statements(&self) -> &[Stmt] {
|
||||
&self.0
|
||||
}
|
||||
|
||||
/// Get the statements.
|
||||
#[cfg(feature = "internals")]
|
||||
#[deprecated(note = "this method is volatile and may change")]
|
||||
pub fn statements(&self) -> &Vec<Stmt> {
|
||||
pub fn statements(&self) -> &[Stmt] {
|
||||
&self.0
|
||||
}
|
||||
|
||||
@ -95,16 +95,43 @@ impl AST {
|
||||
&self.1
|
||||
}
|
||||
|
||||
/// Merge two [`AST`] into one. Both [`AST`]'s are untouched and a new, merged, version
|
||||
/// Clone the `AST`'s functions into a new `AST`.
|
||||
/// No statements are cloned.
|
||||
///
|
||||
/// This operation is cheap because functions are shared.
|
||||
pub fn clone_functions_only(&self) -> Self {
|
||||
self.clone_functions_only_filtered(|_, _, _| true)
|
||||
}
|
||||
|
||||
/// Clone the `AST`'s functions into a new `AST` based on a filter predicate.
|
||||
/// No statements are cloned.
|
||||
///
|
||||
/// This operation is cheap because functions are shared.
|
||||
pub fn clone_functions_only_filtered(
|
||||
&self,
|
||||
filter: impl Fn(FnAccess, &str, usize) -> bool,
|
||||
) -> Self {
|
||||
let mut functions: Module = Default::default();
|
||||
functions.merge_filtered(&self.1, filter);
|
||||
Self(Default::default(), functions)
|
||||
}
|
||||
|
||||
/// Clone the `AST`'s script statements into a new `AST`.
|
||||
/// No functions are cloned.
|
||||
pub fn clone_statements_only(&self) -> Self {
|
||||
Self(self.0.clone(), Default::default())
|
||||
}
|
||||
|
||||
/// Merge two `AST` into one. Both `AST`'s are untouched and a new, merged, version
|
||||
/// is returned.
|
||||
///
|
||||
/// The second [`AST`] is simply appended to the end of the first _without any processing_.
|
||||
/// Thus, the return value of the first [`AST`] (if using expression-statement syntax) is buried.
|
||||
/// Of course, if the first [`AST`] uses a `return` statement at the end, then
|
||||
/// the second [`AST`] will essentially be dead code.
|
||||
/// The second `AST` is simply appended to the end of the first _without any processing_.
|
||||
/// Thus, the return value of the first `AST` (if using expression-statement syntax) is buried.
|
||||
/// Of course, if the first `AST` uses a `return` statement at the end, then
|
||||
/// the second `AST` will essentially be dead code.
|
||||
///
|
||||
/// All script-defined functions in the second [`AST`] overwrite similarly-named functions
|
||||
/// in the first [`AST`] with the same number of parameters.
|
||||
/// All script-defined functions in the second `AST` overwrite similarly-named functions
|
||||
/// in the first `AST` with the same number of parameters.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
@ -148,16 +175,16 @@ impl AST {
|
||||
self.merge_filtered(other, |_, _, _| true)
|
||||
}
|
||||
|
||||
/// Merge two [`AST`] into one. Both [`AST`]'s are untouched and a new, merged, version
|
||||
/// Merge two `AST` into one. Both `AST`'s are untouched and a new, merged, version
|
||||
/// is returned.
|
||||
///
|
||||
/// The second [`AST`] is simply appended to the end of the first _without any processing_.
|
||||
/// Thus, the return value of the first [`AST`] (if using expression-statement syntax) is buried.
|
||||
/// Of course, if the first [`AST`] uses a `return` statement at the end, then
|
||||
/// the second [`AST`] will essentially be dead code.
|
||||
/// The second `AST` is simply appended to the end of the first _without any processing_.
|
||||
/// Thus, the return value of the first `AST` (if using expression-statement syntax) is buried.
|
||||
/// Of course, if the first `AST` uses a `return` statement at the end, then
|
||||
/// the second `AST` will essentially be dead code.
|
||||
///
|
||||
/// All script-defined functions in the second [`AST`] are first selected based on a filter
|
||||
/// predicate, then overwrite similarly-named functions in the first [`AST`] with the
|
||||
/// All script-defined functions in the second `AST` are first selected based on a filter
|
||||
/// predicate, then overwrite similarly-named functions in the first `AST` with the
|
||||
/// same number of parameters.
|
||||
///
|
||||
/// # Example
|
||||
@ -251,13 +278,13 @@ impl AST {
|
||||
self.1.retain_functions(filter);
|
||||
}
|
||||
|
||||
/// Clear all function definitions in the [`AST`].
|
||||
/// Clear all function definitions in the `AST`.
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
pub fn clear_functions(&mut self) {
|
||||
self.1 = Default::default();
|
||||
}
|
||||
|
||||
/// Clear all statements in the [`AST`], leaving only function definitions.
|
||||
/// Clear all statements in the `AST`, leaving only function definitions.
|
||||
pub fn clear_statements(&mut self) {
|
||||
self.0 = vec![];
|
||||
}
|
||||
@ -271,6 +298,18 @@ impl Add<Self> for &AST {
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[Stmt]> for AST {
|
||||
fn as_ref(&self) -> &[Stmt] {
|
||||
self.statements()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<Module> for AST {
|
||||
fn as_ref(&self) -> &Module {
|
||||
self.lib()
|
||||
}
|
||||
}
|
||||
|
||||
/// A type representing the access mode of a scripted function.
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
|
||||
pub enum FnAccess {
|
||||
|
@ -218,8 +218,8 @@ impl Engine {
|
||||
/// let mut engine = Engine::new();
|
||||
///
|
||||
/// // Register a custom operator called 'foo' and give it
|
||||
/// // a precedence of 140 (i.e. between +|- and *|/).
|
||||
/// engine.register_custom_operator("foo", 140).unwrap();
|
||||
/// // a precedence of 160 (i.e. between +|- and *|/).
|
||||
/// engine.register_custom_operator("foo", 160).unwrap();
|
||||
///
|
||||
/// // Register a binary function named 'foo'
|
||||
/// engine.register_fn("foo", |x: i64, y: i64| (x * y) - (x + y));
|
||||
|
15
src/token.rs
15
src/token.rs
@ -12,7 +12,7 @@ use crate::stdlib::{
|
||||
borrow::Cow,
|
||||
boxed::Box,
|
||||
char,
|
||||
collections::{HashMap, HashSet},
|
||||
collections::HashMap,
|
||||
fmt,
|
||||
iter::Peekable,
|
||||
str::{Chars, FromStr},
|
||||
@ -394,18 +394,17 @@ impl Token {
|
||||
|
||||
And | Ampersand => 60,
|
||||
|
||||
LessThan | LessThanEqualsTo | GreaterThan | GreaterThanEqualsTo | EqualsTo
|
||||
| NotEqualsTo => 90,
|
||||
EqualsTo | NotEqualsTo => 90,
|
||||
|
||||
In => 110,
|
||||
LessThan | LessThanEqualsTo | GreaterThan | GreaterThanEqualsTo => 110,
|
||||
|
||||
Plus | Minus => 130,
|
||||
In => 130,
|
||||
|
||||
Divide | Multiply | PowerOf => 160,
|
||||
Plus | Minus => 150,
|
||||
|
||||
LeftShift | RightShift => 190,
|
||||
Divide | Multiply | PowerOf | Modulo => 180,
|
||||
|
||||
Modulo => 210,
|
||||
LeftShift | RightShift => 210,
|
||||
|
||||
Period => 240,
|
||||
|
||||
|
@ -1,5 +1,8 @@
|
||||
#![cfg(not(feature = "no_function"))]
|
||||
use rhai::{Engine, EvalAltResult, Func, ParseError, ParseErrorType, Scope, INT};
|
||||
use rhai::{
|
||||
Dynamic, Engine, EvalAltResult, Func, ImmutableString, Module, ParseError, ParseErrorType,
|
||||
Scope, INT,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn test_fn() -> Result<(), Box<EvalAltResult>> {
|
||||
@ -110,3 +113,41 @@ fn test_anonymous_fn() -> Result<(), Box<EvalAltResult>> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fn_ptr() -> Result<(), Box<EvalAltResult>> {
|
||||
let mut engine = Engine::new();
|
||||
|
||||
engine.register_raw_fn(
|
||||
"bar",
|
||||
&[
|
||||
std::any::TypeId::of::<INT>(),
|
||||
std::any::TypeId::of::<ImmutableString>(),
|
||||
std::any::TypeId::of::<INT>(),
|
||||
],
|
||||
move |engine: &Engine, lib: &Module, args: &mut [&mut Dynamic]| {
|
||||
let callback = args[1].clone().cast::<ImmutableString>();
|
||||
let value = args[2].clone();
|
||||
let this_ptr = args.get_mut(0).unwrap();
|
||||
|
||||
engine.call_fn_dynamic(&mut Scope::new(), lib, &callback, Some(this_ptr), [value])?;
|
||||
|
||||
Ok(().into())
|
||||
},
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
engine.eval::<INT>(
|
||||
r#"
|
||||
fn foo(x) { this += x; }
|
||||
|
||||
let x = 41;
|
||||
x.bar("foo", 1);
|
||||
x
|
||||
"#
|
||||
)?,
|
||||
42
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -24,8 +24,8 @@ fn test_tokens_custom_operator() -> Result<(), Box<EvalAltResult>> {
|
||||
let mut engine = Engine::new();
|
||||
|
||||
// Register a custom operator called `foo` and give it
|
||||
// a precedence of 140 (i.e. between +|- and *|/).
|
||||
engine.register_custom_operator("foo", 140).unwrap();
|
||||
// a precedence of 160 (i.e. between +|- and *|/).
|
||||
engine.register_custom_operator("foo", 160).unwrap();
|
||||
|
||||
// Register a binary function named `foo`
|
||||
engine.register_fn("foo", |x: INT, y: INT| (x * y) - (x + y));
|
||||
|
Loading…
Reference in New Issue
Block a user