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")?; let result = engine.eval::<i64>("40 + 2")?;
println!("Answer: {}", result); // prints 42 println!("Answer: {}", result); // prints 42
Ok(())
} }
``` ```
@ -153,7 +155,7 @@ use rhai::Engine;
let mut engine = Engine::new(); 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. 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(); let mut engine = Engine::new();
// Define a function in a script and compile to AST // 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 // 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) // 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))?; // 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 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 Fallible functions
------------------ ------------------

View File

@ -2,7 +2,7 @@
use crate::any::{Any, AnyExt, Dynamic}; use crate::any::{Any, AnyExt, Dynamic};
use crate::call::FuncArgs; 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::error::ParseError;
use crate::fn_register::RegisterFn; use crate::fn_register::RegisterFn;
use crate::parser::{lex, parse, Position, AST}; 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 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); /// assert_eq!(result, 126);
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
pub fn call_fn<'f, A: FuncArgs<'f>, T: Any + Clone>( pub fn call_fn<A: FuncArgs, T: Any + Clone>(
&mut self, &mut self,
name: &str, name: &str,
ast: &AST, ast: &AST,
args: A, 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> { ) -> Result<T, EvalAltResult> {
let pos = Default::default(); let pos = Default::default();
@ -297,16 +312,14 @@ impl<'e> Engine<'e> {
); );
}); });
let result = self let result = self.call_fn_raw(name, args, None, pos).and_then(|b| {
.call_fn_raw(name, args.into_vec(), None, pos) b.downcast().map(|b| *b).map_err(|a| {
.and_then(|b| { EvalAltResult::ErrorMismatchOutputType(
b.downcast().map(|b| *b).map_err(|a| { self.map_type_name((*a).type_name()).into(),
EvalAltResult::ErrorMismatchOutputType( pos,
self.map_type_name((*a).type_name()).into(), )
pos, })
) });
})
});
self.clear_functions(); self.clear_functions();

View File

@ -1,35 +1,29 @@
//! Helper module which defines `FnArgs` to make function calling easier. //! 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. /// Trait that represent arguments to a function call.
pub trait FuncArgs<'a> { pub trait FuncArgs {
/// Convert to a `Vec` of `Variant` arguments. /// Convert to a `Vec` of `Dynamic` arguments.
fn into_vec(self) -> Vec<&'a mut Variant>; fn into_vec(self) -> Vec<Dynamic>;
} }
impl<'a> FuncArgs<'a> for Vec<&'a mut Variant> { impl<T: Any> FuncArgs for &mut Vec<T> {
fn into_vec(self) -> Self { fn into_vec(self) -> Vec<Dynamic> {
self self.iter_mut().map(|x| x.into_dynamic()).collect()
}
}
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()
} }
} }
macro_rules! impl_args { macro_rules! impl_args {
($($p:ident),*) => { ($($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; let ($($p,)*) = self;
#[allow(unused_variables, unused_mut)] #[allow(unused_variables, unused_mut)]
let mut v = Vec::new(); let mut v = Vec::new();
$(v.push($p as &mut Variant);)* $(v.push($p.into_dynamic());)*
v v
} }
@ -45,4 +39,4 @@ macro_rules! impl_args {
} }
#[cfg_attr(rustfmt, rustfmt_skip)] #[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> { fn test_engine_call_fn() -> Result<(), EvalAltResult> {
let mut engine = Engine::new(); 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); assert_eq!(result, 126);