Add new register_fn_raw API.
This commit is contained in:
parent
4052ad3df1
commit
a27f89b524
@ -14,6 +14,7 @@ Breaking changes
|
|||||||
----------------
|
----------------
|
||||||
|
|
||||||
* `EvalAltResult::ErrorMismatchOutputType` has an extra argument containing the name of the requested type.
|
* `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.
|
||||||
|
|
||||||
New features
|
New features
|
||||||
------------
|
------------
|
||||||
@ -22,6 +23,7 @@ New features
|
|||||||
This is particularly useful when converting a Rust `struct` to a `Dynamic` _object map_ and back.
|
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::disable_symbol` to surgically disable keywords and/or operators.
|
||||||
* `Engine::register_custom_operator` to define a custom operator.
|
* `Engine::register_custom_operator` to define a custom operator.
|
||||||
|
* New low-level API `Engine::register_raw_fn`.
|
||||||
|
|
||||||
|
|
||||||
Version 0.16.1
|
Version 0.16.1
|
||||||
|
@ -55,10 +55,35 @@ let result: i64 = engine.call_fn(&mut scope, &ast, "hello", () )?;
|
|||||||
let result: () = engine.call_fn(&mut scope, &ast, "hidden", ())?;
|
let result: () = engine.call_fn(&mut scope, &ast, "hidden", ())?;
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
`Engine::call_fn_dynamic`
|
||||||
|
------------------------
|
||||||
|
|
||||||
For more control, construct all arguments as `Dynamic` values and use `Engine::call_fn_dynamic`, passing it
|
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 `IntoIterator<Item = Dynamic>` (such as a simple `Vec<Dynamic>`):
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
let result: Dynamic = engine.call_fn_dynamic(&mut scope, &ast, "hello",
|
let result: Dynamic = engine.call_fn_dynamic(
|
||||||
vec![ String::from("abc").into(), 123_i64.into() ])?;
|
&mut scope, // scope to use
|
||||||
|
&ast, // AST to use
|
||||||
|
"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, "action", Some(&mut value), [ 41_i64.into() ])?;
|
||||||
|
// ^^^^^^^^^^^^^^^^ binding the 'this' pointer
|
||||||
|
|
||||||
|
assert_eq!(value.as_int().unwrap(), 42);
|
||||||
```
|
```
|
||||||
|
68
src/api.rs
68
src/api.rs
@ -32,6 +32,38 @@ use crate::stdlib::{fs::File, io::prelude::*, path::PathBuf};
|
|||||||
|
|
||||||
/// Engine public API
|
/// Engine public API
|
||||||
impl Engine {
|
impl Engine {
|
||||||
|
/// Register a function with 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,
|
||||||
|
args: &[TypeId],
|
||||||
|
|
||||||
|
#[cfg(not(feature = "sync"))] func: impl Fn(&Engine, &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>>
|
||||||
|
+ 'static,
|
||||||
|
|
||||||
|
#[cfg(feature = "sync")] func: impl Fn(&Engine, &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>>
|
||||||
|
+ Send
|
||||||
|
+ Sync
|
||||||
|
+ 'static,
|
||||||
|
) {
|
||||||
|
self.global_module.set_fn_var_args(name, args, func);
|
||||||
|
}
|
||||||
|
|
||||||
/// Register a custom type for use with the `Engine`.
|
/// Register a custom type for use with the `Engine`.
|
||||||
/// The type must implement `Clone`.
|
/// The type must implement `Clone`.
|
||||||
///
|
///
|
||||||
@ -1127,7 +1159,7 @@ impl Engine {
|
|||||||
args: A,
|
args: A,
|
||||||
) -> Result<T, Box<EvalAltResult>> {
|
) -> Result<T, Box<EvalAltResult>> {
|
||||||
let mut arg_values = args.into_vec();
|
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());
|
let typ = self.map_type_name(result.type_name());
|
||||||
|
|
||||||
@ -1140,7 +1172,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
|
/// # Example
|
||||||
///
|
///
|
||||||
@ -1148,7 +1188,7 @@ impl Engine {
|
|||||||
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
||||||
/// # #[cfg(not(feature = "no_function"))]
|
/// # #[cfg(not(feature = "no_function"))]
|
||||||
/// # {
|
/// # {
|
||||||
/// use rhai::{Engine, Scope};
|
/// use rhai::{Engine, Scope, Dynamic};
|
||||||
///
|
///
|
||||||
/// let engine = Engine::new();
|
/// let engine = Engine::new();
|
||||||
///
|
///
|
||||||
@ -1156,20 +1196,27 @@ impl Engine {
|
|||||||
/// fn add(x, y) { len(x) + y + foo }
|
/// fn add(x, y) { len(x) + y + foo }
|
||||||
/// fn add1(x) { len(x) + 1 + foo }
|
/// fn add1(x) { len(x) + 1 + foo }
|
||||||
/// fn bar() { foo/2 }
|
/// fn bar() { foo/2 }
|
||||||
|
/// fn action(x) { this += x; } // function using 'this' pointer
|
||||||
/// ")?;
|
/// ")?;
|
||||||
///
|
///
|
||||||
/// let mut scope = Scope::new();
|
/// let mut scope = Scope::new();
|
||||||
/// scope.push("foo", 42_i64);
|
/// scope.push("foo", 42_i64);
|
||||||
///
|
///
|
||||||
/// // Call the script-defined function
|
/// // 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);
|
/// 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);
|
/// 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);
|
/// 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(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
@ -1180,10 +1227,10 @@ impl Engine {
|
|||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
ast: &AST,
|
ast: &AST,
|
||||||
name: &str,
|
name: &str,
|
||||||
arg_values: impl IntoIterator<Item = Dynamic>,
|
mut this_ptr: Option<&mut Dynamic>,
|
||||||
|
mut arg_values: impl AsMut<[Dynamic]>,
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
let mut arg_values: StaticVec<_> = arg_values.into_iter().collect();
|
self.call_fn_dynamic_raw(scope, ast, name, &mut this_ptr, arg_values.as_mut())
|
||||||
self.call_fn_dynamic_raw(scope, ast, name, 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.
|
||||||
@ -1200,6 +1247,7 @@ impl Engine {
|
|||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
ast: &AST,
|
ast: &AST,
|
||||||
name: &str,
|
name: &str,
|
||||||
|
this_ptr: &mut Option<&mut Dynamic>,
|
||||||
arg_values: &mut [Dynamic],
|
arg_values: &mut [Dynamic],
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
let mut args: StaticVec<_> = arg_values.iter_mut().collect();
|
let mut args: StaticVec<_> = arg_values.iter_mut().collect();
|
||||||
@ -1220,7 +1268,7 @@ impl Engine {
|
|||||||
&mut mods,
|
&mut mods,
|
||||||
&mut state,
|
&mut state,
|
||||||
ast.lib(),
|
ast.lib(),
|
||||||
&mut None,
|
this_ptr,
|
||||||
name,
|
name,
|
||||||
fn_def,
|
fn_def,
|
||||||
args,
|
args,
|
||||||
|
@ -12,7 +12,7 @@ use crate::stdlib::{
|
|||||||
borrow::Cow,
|
borrow::Cow,
|
||||||
boxed::Box,
|
boxed::Box,
|
||||||
char,
|
char,
|
||||||
collections::{HashMap, HashSet},
|
collections::HashMap,
|
||||||
fmt,
|
fmt,
|
||||||
iter::Peekable,
|
iter::Peekable,
|
||||||
str::{Chars, FromStr},
|
str::{Chars, FromStr},
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
#![cfg(not(feature = "no_function"))]
|
#![cfg(not(feature = "no_function"))]
|
||||||
use rhai::{Engine, EvalAltResult, Func, ParseError, ParseErrorType, Scope, INT};
|
use rhai::{
|
||||||
|
Dynamic, Engine, EvalAltResult, Func, ImmutableString, ParseError, ParseErrorType, Scope, INT,
|
||||||
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_fn() -> Result<(), Box<EvalAltResult>> {
|
fn test_fn() -> Result<(), Box<EvalAltResult>> {
|
||||||
@ -110,3 +112,47 @@ fn test_anonymous_fn() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_fn_ptr() -> Result<(), Box<EvalAltResult>> {
|
||||||
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
|
let ast = engine.compile(
|
||||||
|
r#"
|
||||||
|
fn foo(x) { this += x; }
|
||||||
|
|
||||||
|
let x = 41;
|
||||||
|
x.bar("foo", 1);
|
||||||
|
x
|
||||||
|
"#,
|
||||||
|
)?;
|
||||||
|
let ast_clone = ast.clone();
|
||||||
|
|
||||||
|
engine.register_raw_fn(
|
||||||
|
"bar",
|
||||||
|
&[
|
||||||
|
std::any::TypeId::of::<INT>(),
|
||||||
|
std::any::TypeId::of::<ImmutableString>(),
|
||||||
|
std::any::TypeId::of::<INT>(),
|
||||||
|
],
|
||||||
|
move |engine: &Engine, 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(),
|
||||||
|
&ast_clone,
|
||||||
|
&callback,
|
||||||
|
Some(this_ptr),
|
||||||
|
[value],
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(().into())
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(engine.eval_ast::<INT>(&ast)?, 42);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user