Add fallible functions support and replace most arithmetic operations with checked versions.

This commit is contained in:
Stephen Chung
2020-03-08 22:47:13 +08:00
parent 3e7adc2e51
commit b1b25d3043
9 changed files with 387 additions and 40 deletions

View File

@@ -58,6 +58,32 @@ pub trait RegisterDynamicFn<FN, ARGS> {
fn register_dynamic_fn(&mut self, name: &str, f: FN);
}
/// A trait to register fallible custom functions returning Result<_, EvalAltResult> with the `Engine`.
///
/// # Example
///
/// ```rust
/// use rhai::{Engine, RegisterFn};
///
/// // Normal function
/// fn add(x: i64, y: i64) -> i64 {
/// x + y
/// }
///
/// let mut engine = Engine::new();
///
/// // You must use the trait rhai::RegisterFn to get this method.
/// engine.register_fn("add", add);
///
/// if let Ok(result) = engine.eval::<i64>("add(40, 2)") {
/// println!("Answer: {}", result); // prints 42
/// }
/// ```
pub trait RegisterResultFn<FN, ARGS, RET> {
/// Register a custom function with the `Engine`.
fn register_result_fn(&mut self, name: &str, f: FN);
}
pub struct Ref<A>(A);
pub struct Mut<A>(A);
@@ -91,7 +117,7 @@ macro_rules! def_register {
let mut drain = args.drain(..);
$(
// Downcast every element, return in case of a type mismatch
let $par = (drain.next().unwrap().downcast_mut() as Option<&mut $par>).unwrap();
let $par = drain.next().unwrap().downcast_mut::<$par>().unwrap();
)*
// Call the user-supplied function using ($clone) to
@@ -123,7 +149,7 @@ macro_rules! def_register {
let mut drain = args.drain(..);
$(
// Downcast every element, return in case of a type mismatch
let $par = (drain.next().unwrap().downcast_mut() as Option<&mut $par>).unwrap();
let $par = drain.next().unwrap().downcast_mut::<$par>().unwrap();
)*
// Call the user-supplied function using ($clone) to
@@ -135,6 +161,44 @@ macro_rules! def_register {
}
}
impl<
$($par: Any + Clone,)*
FN: Fn($($param),*) -> Result<RET, EvalAltResult> + 'static,
RET: Any
> RegisterResultFn<FN, ($($mark,)*), RET> for Engine<'_>
{
fn register_result_fn(&mut self, name: &str, f: FN) {
let fn_name = name.to_string();
let fun = move |mut args: FnCallArgs, pos: Position| {
// Check for length at the beginning to avoid per-element bound checks.
const NUM_ARGS: usize = count_args!($($par)*);
if args.len() != NUM_ARGS {
Err(EvalAltResult::ErrorFunctionArgsMismatch(fn_name.clone(), NUM_ARGS, args.len(), pos))
} else {
#[allow(unused_variables, unused_mut)]
let mut drain = args.drain(..);
$(
// Downcast every element, return in case of a type mismatch
let $par = drain.next().unwrap().downcast_mut::<$par>().unwrap();
)*
// Call the user-supplied function using ($clone) to
// potentially clone the value, otherwise pass the reference.
match f($(($clone)($par)),*) {
Ok(r) => Ok(Box::new(r) as Dynamic),
Err(mut err) => {
err.set_position(pos);
Err(err)
}
}
}
};
self.register_fn_raw(name, Some(vec![$(TypeId::of::<$par>()),*]), Box::new(fun));
}
}
//def_register!(imp_pop $($par => $mark => $param),*);
};
($p0:ident $(, $p:ident)*) => {