Simplify call_fn API, no need to pass &mut references.

This commit is contained in:
Stephen Chung 2020-03-10 14:09:05 +08:00
parent 711cd9bb1c
commit f3bcb2a10d
4 changed files with 61 additions and 38 deletions

View File

@ -120,6 +120,8 @@ fn main() -> Result<(), EvalAltResult>
let result = engine.eval::<i64>("40 + 2")?;
println!("Answer: {}", result); // prints 42
Ok(())
}
```
@ -153,7 +155,7 @@ use rhai::Engine;
let mut engine = Engine::new();
let ast = engine.compile_file("hello_world.rhai").unwrap();
let ast = engine.compile_file("hello_world.rhai")?;
```
Rhai also allows you to work _backwards_ from the other direction - i.e. calling a Rhai-scripted function from Rust.
@ -166,11 +168,19 @@ use rhai::Engine;
let mut engine = Engine::new();
// Define a function in a script and compile to AST
let ast = engine.compile("fn hello(x, y) { x.len() + y }")?;
let ast = engine.compile(
r"
fn hello(x, y) { // a function with two arguments: String and i64
x.len() + y // returning i64
}
")?;
// Evaluate the function in the AST, passing arguments into the script as a tuple
// (beware, arguments must be of the correct types because Rhai does not have built-in type conversions)
let result: i64 = engine.call_fn("hello", &ast, (&mut String::from("abc"), &mut 123_i64))?;
// Evaluate the function in the AST, passing arguments into the script as a tuple.
// Beware, arguments must be of the correct types because Rhai does not have built-in type conversions.
// If you pass in arguments of the wrong type, the Engine will not find the function.
let result: i64 = engine.call_fn("hello", &ast, ( String::from("abc"), 123_i64 ) )?;
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ put arguments in a tuple
```
Values and types
@ -292,7 +302,7 @@ fn main()
}
```
You can also see in this example how you can register multiple functions (or in this case multiple instances of the same function) to the same name in script. This gives you a way to overload functions and call the correct one, based on the types of the arguments, from your script.
You can also see in this example how you can register multiple functions (or in this case multiple instances of the same function) to the same name in script. This gives you a way to overload functions the correct one, based on the types of the arguments, from your script.
Fallible functions
------------------

View File

@ -2,7 +2,7 @@
use crate::any::{Any, AnyExt, Dynamic};
use crate::call::FuncArgs;
use crate::engine::{Engine, FnAny, FnIntExt, FnSpec};
use crate::engine::{Engine, FnAny, FnCallArgs, FnIntExt, FnSpec};
use crate::error::ParseError;
use crate::fn_register::RegisterFn;
use crate::parser::{lex, parse, Position, AST};
@ -273,17 +273,32 @@ impl<'e> Engine<'e> {
///
/// let ast = engine.compile("fn add(x, y) { x.len() + y }")?;
///
/// let result: i64 = engine.call_fn("add", &ast, (&mut String::from("abc"), &mut 123_i64))?;
/// let result: i64 = engine.call_fn("add", &ast, (String::from("abc"), 123_i64))?;
///
/// assert_eq!(result, 126);
/// # Ok(())
/// # }
/// ```
pub fn call_fn<'f, A: FuncArgs<'f>, T: Any + Clone>(
pub fn call_fn<A: FuncArgs, T: Any + Clone>(
&mut self,
name: &str,
ast: &AST,
args: A,
) -> Result<T, EvalAltResult> {
let mut arg_values = args.into_vec();
self.call_fn_internal(
name,
ast,
arg_values.iter_mut().map(|v| v.as_mut()).collect(),
)
}
pub(crate) fn call_fn_internal<T: Any + Clone>(
&mut self,
name: &str,
ast: &AST,
args: FnCallArgs,
) -> Result<T, EvalAltResult> {
let pos = Default::default();
@ -297,16 +312,14 @@ impl<'e> Engine<'e> {
);
});
let result = self
.call_fn_raw(name, args.into_vec(), None, pos)
.and_then(|b| {
b.downcast().map(|b| *b).map_err(|a| {
EvalAltResult::ErrorMismatchOutputType(
self.map_type_name((*a).type_name()).into(),
pos,
)
})
});
let result = self.call_fn_raw(name, args, None, pos).and_then(|b| {
b.downcast().map(|b| *b).map_err(|a| {
EvalAltResult::ErrorMismatchOutputType(
self.map_type_name((*a).type_name()).into(),
pos,
)
})
});
self.clear_functions();

View File

@ -1,35 +1,29 @@
//! Helper module which defines `FnArgs` to make function calling easier.
use crate::any::{Any, Variant};
use crate::any::{Any, Dynamic};
/// Trait that represent arguments to a function call.
pub trait FuncArgs<'a> {
/// Convert to a `Vec` of `Variant` arguments.
fn into_vec(self) -> Vec<&'a mut Variant>;
pub trait FuncArgs {
/// Convert to a `Vec` of `Dynamic` arguments.
fn into_vec(self) -> Vec<Dynamic>;
}
impl<'a> FuncArgs<'a> for Vec<&'a mut Variant> {
fn into_vec(self) -> Self {
self
}
}
impl<'a, T: Any> FuncArgs<'a> for &'a mut Vec<T> {
fn into_vec(self) -> Vec<&'a mut Variant> {
self.iter_mut().map(|x| x as &mut Variant).collect()
impl<T: Any> FuncArgs for &mut Vec<T> {
fn into_vec(self) -> Vec<Dynamic> {
self.iter_mut().map(|x| x.into_dynamic()).collect()
}
}
macro_rules! impl_args {
($($p:ident),*) => {
impl<'a, $($p: Any + Clone),*> FuncArgs<'a> for ($(&'a mut $p,)*)
impl<$($p: Any + Clone),*> FuncArgs for ($($p,)*)
{
fn into_vec(self) -> Vec<&'a mut Variant> {
fn into_vec(self) -> Vec<Dynamic> {
let ($($p,)*) = self;
#[allow(unused_variables, unused_mut)]
let mut v = Vec::new();
$(v.push($p as &mut Variant);)*
$(v.push($p.into_dynamic());)*
v
}
@ -45,4 +39,4 @@ macro_rules! impl_args {
}
#[cfg_attr(rustfmt, rustfmt_skip)]
impl_args!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S);
impl_args!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T);

View File

@ -4,9 +4,15 @@ use rhai::{Engine, EvalAltResult};
fn test_engine_call_fn() -> Result<(), EvalAltResult> {
let mut engine = Engine::new();
let ast = engine.compile("fn hello(x, y) { x.len() + y }")?;
let ast = engine.compile(
r"
fn hello(x, y) {
x.len() + y
}
",
)?;
let result: i64 = engine.call_fn("hello", &ast, (&mut String::from("abc"), &mut 123_i64))?;
let result: i64 = engine.call_fn("hello", &ast, (String::from("abc"), 123_i64))?;
assert_eq!(result, 126);