Implement function pointers.

This commit is contained in:
Stephen Chung
2020-06-25 18:07:57 +08:00
parent aeb47efce8
commit fe640e0e13
18 changed files with 269 additions and 31 deletions

View File

@@ -1,6 +1,6 @@
//! Helper module which defines the `Any` trait to to allow dynamic value handling.
use crate::fn_native::SendSync;
use crate::fn_native::{FnPtr, SendSync};
use crate::module::Module;
use crate::parser::{ImmutableString, INT};
use crate::r#unsafe::{unsafe_cast_box, unsafe_try_cast};
@@ -139,6 +139,8 @@ pub enum Union {
#[cfg(not(feature = "no_object"))]
Map(Box<Map>),
Module(Box<Module>),
#[cfg(not(feature = "no_function"))]
FnPtr(FnPtr),
Variant(Box<Box<dyn Variant>>),
}
@@ -176,6 +178,8 @@ impl Dynamic {
#[cfg(not(feature = "no_object"))]
Union::Map(_) => TypeId::of::<Map>(),
Union::Module(_) => TypeId::of::<Module>(),
#[cfg(not(feature = "no_function"))]
Union::FnPtr(_) => TypeId::of::<FnPtr>(),
Union::Variant(value) => (***value).type_id(),
}
}
@@ -195,6 +199,8 @@ impl Dynamic {
#[cfg(not(feature = "no_object"))]
Union::Map(_) => "map",
Union::Module(_) => "sub-scope",
#[cfg(not(feature = "no_function"))]
Union::FnPtr(_) => "Fn",
#[cfg(not(feature = "no_std"))]
#[cfg(not(target_arch = "wasm32"))]
@@ -219,6 +225,8 @@ impl fmt::Display for Dynamic {
#[cfg(not(feature = "no_object"))]
Union::Map(value) => write!(f, "#{:?}", value),
Union::Module(value) => fmt::Debug::fmt(value, f),
#[cfg(not(feature = "no_function"))]
Union::FnPtr(value) => fmt::Display::fmt(value, f),
#[cfg(not(feature = "no_std"))]
#[cfg(not(target_arch = "wasm32"))]
@@ -243,6 +251,8 @@ impl fmt::Debug for Dynamic {
#[cfg(not(feature = "no_object"))]
Union::Map(value) => write!(f, "#{:?}", value),
Union::Module(value) => fmt::Debug::fmt(value, f),
#[cfg(not(feature = "no_function"))]
Union::FnPtr(value) => fmt::Display::fmt(value, f),
#[cfg(not(feature = "no_std"))]
#[cfg(not(target_arch = "wasm32"))]
@@ -267,6 +277,8 @@ impl Clone for Dynamic {
#[cfg(not(feature = "no_object"))]
Union::Map(ref value) => Self(Union::Map(value.clone())),
Union::Module(ref value) => Self(Union::Module(value.clone())),
#[cfg(not(feature = "no_function"))]
Union::FnPtr(ref value) => Self(Union::FnPtr(value.clone())),
Union::Variant(ref value) => (***value).clone_into_dynamic(),
}
}
@@ -396,6 +408,8 @@ impl Dynamic {
#[cfg(not(feature = "no_object"))]
Union::Map(value) => unsafe_cast_box::<_, T>(value).ok().map(|v| *v),
Union::Module(value) => unsafe_cast_box::<_, T>(value).ok().map(|v| *v),
#[cfg(not(feature = "no_function"))]
Union::FnPtr(value) => unsafe_try_cast(value),
Union::Variant(value) => (*value).as_box_any().downcast().map(|x| *x).ok(),
}
}
@@ -439,6 +453,8 @@ impl Dynamic {
#[cfg(not(feature = "no_object"))]
Union::Map(value) => *unsafe_cast_box::<_, T>(value).unwrap(),
Union::Module(value) => *unsafe_cast_box::<_, T>(value).unwrap(),
#[cfg(not(feature = "no_function"))]
Union::FnPtr(value) => unsafe_try_cast(value).unwrap(),
Union::Variant(value) => (*value).as_box_any().downcast().map(|x| *x).unwrap(),
}
}
@@ -454,8 +470,7 @@ impl Dynamic {
match &self.0 {
Union::Unit(value) => <dyn Any>::downcast_ref::<T>(value),
Union::Bool(value) => <dyn Any>::downcast_ref::<T>(value),
Union::Str(value) => (value as &dyn Any)
.downcast_ref::<T>()
Union::Str(value) => <dyn Any>::downcast_ref::<T>(value)
.or_else(|| <dyn Any>::downcast_ref::<T>(value.as_ref())),
Union::Char(value) => <dyn Any>::downcast_ref::<T>(value),
Union::Int(value) => <dyn Any>::downcast_ref::<T>(value),
@@ -466,6 +481,8 @@ impl Dynamic {
#[cfg(not(feature = "no_object"))]
Union::Map(value) => <dyn Any>::downcast_ref::<T>(value.as_ref()),
Union::Module(value) => <dyn Any>::downcast_ref::<T>(value.as_ref()),
#[cfg(not(feature = "no_function"))]
Union::FnPtr(value) => <dyn Any>::downcast_ref::<T>(value),
Union::Variant(value) => value.as_ref().as_ref().as_any().downcast_ref::<T>(),
}
}
@@ -491,6 +508,8 @@ impl Dynamic {
#[cfg(not(feature = "no_object"))]
Union::Map(value) => <dyn Any>::downcast_mut::<T>(value.as_mut()),
Union::Module(value) => <dyn Any>::downcast_mut::<T>(value.as_mut()),
#[cfg(not(feature = "no_function"))]
Union::FnPtr(value) => <dyn Any>::downcast_mut::<T>(value),
Union::Variant(value) => value.as_mut().as_mut_any().downcast_mut::<T>(),
}
}
@@ -556,6 +575,15 @@ impl Dynamic {
_ => Err(self.type_name()),
}
}
/// Cast the `Dynamic` as a `FnPtr` and return the function name.
/// Returns the name of the actual type if the cast fails.
pub(crate) fn as_fn_name(&self) -> Result<&str, &'static str> {
match &self.0 {
Union::FnPtr(f) => Ok(f.fn_name()),
_ => Err(self.type_name()),
}
}
}
impl From<()> for Dynamic {
@@ -616,6 +644,12 @@ impl<K: Into<ImmutableString>, T: Variant + Clone> From<HashMap<K, T>> for Dynam
)))
}
}
#[cfg(not(feature = "no_function"))]
impl From<FnPtr> for Dynamic {
fn from(value: FnPtr) -> Self {
Self(Union::FnPtr(value))
}
}
/// Private type which ensures that `rhai::Any` and `rhai::AnyExt` can only
/// be implemented by this crate.

View File

@@ -3,7 +3,8 @@
use crate::any::{Dynamic, Union, Variant};
use crate::calc_fn_hash;
use crate::error::ParseErrorType;
use crate::fn_native::{CallableFunction, Callback, FnCallArgs};
use crate::fn_native::{CallableFunction, Callback, FnCallArgs, FnPtr};
use crate::fn_register::RegisterResultFn;
use crate::module::{resolvers, Module, ModuleResolver};
use crate::optimize::OptimizationLevel;
use crate::packages::{Package, PackageLibrary, PackagesCollection, StandardPackage};
@@ -11,7 +12,7 @@ use crate::parser::{Expr, FnAccess, ImmutableString, ReturnType, ScriptFnDef, St
use crate::r#unsafe::{unsafe_cast_var_name_to_lifetime, unsafe_mut_cast_to_lifetime};
use crate::result::EvalAltResult;
use crate::scope::{EntryType as ScopeEntryType, Scope};
use crate::token::Position;
use crate::token::{Position, Token};
use crate::utils::StaticVec;
#[cfg(not(feature = "no_float"))]
@@ -71,11 +72,13 @@ pub const KEYWORD_PRINT: &str = "print";
pub const KEYWORD_DEBUG: &str = "debug";
pub const KEYWORD_TYPE_OF: &str = "type_of";
pub const KEYWORD_EVAL: &str = "eval";
pub const KEYWORD_FN_PTR_CALL: &str = "call";
pub const FN_TO_STRING: &str = "to_string";
pub const FN_GET: &str = "get$";
pub const FN_SET: &str = "set$";
pub const FN_IDX_GET: &str = "$index$get$";
pub const FN_IDX_SET: &str = "$index$set$";
pub const FN_FN_PTR: &str = "Fn";
/// A type specifying the method of chaining.
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
@@ -954,6 +957,14 @@ impl Engine {
false,
)),
// Fn
FN_FN_PTR if args.len() == 1 && !self.has_override(lib, hashes) => {
Err(Box::new(EvalAltResult::ErrorRuntime(
"'Fn' should not be called in method style. Try Fn(...);".into(),
Position::none(),
)))
}
// eval - reaching this point it must be a method-style call
KEYWORD_EVAL if args.len() == 1 && !self.has_override(lib, hashes) => {
Err(Box::new(EvalAltResult::ErrorRuntime(
@@ -1124,18 +1135,31 @@ impl Engine {
// Get a reference to the mutation target Dynamic
let (result, updated) = {
let obj = target.as_mut();
let mut arg_values: StaticVec<_> = once(obj)
.chain(
idx_val
.downcast_mut::<StaticVec<Dynamic>>()
.unwrap()
.iter_mut(),
)
.collect();
let idx = idx_val.downcast_mut::<StaticVec<Dynamic>>().unwrap();
// Check if it is a FnPtr call
let (fn_name, mut arg_values, hash_fn, is_native) =
if name == KEYWORD_FN_PTR_CALL && obj.is::<FnPtr>() {
// Redirect function name
let name = obj.as_fn_name().unwrap();
// Recalculate hash
let hash = calc_fn_hash(empty(), name, idx.len(), empty());
// Arguments are passed as-is
(name, idx.iter_mut().collect::<StaticVec<_>>(), hash, false)
} else {
// Attached object pointer in front of the arguments
(
name.as_ref(),
once(obj).chain(idx.iter_mut()).collect::<StaticVec<_>>(),
*hash,
*native,
)
};
let args = arg_values.as_mut();
self.exec_fn_call(
state, lib, name, *native, *hash, args, is_ref, def_val, 0,
state, lib, fn_name, is_native, hash_fn, args, is_ref, def_val, 0,
)
.map_err(|err| EvalAltResult::new_position(err, *pos))?
};
@@ -1697,6 +1721,27 @@ impl Engine {
let ((name, native, pos), _, hash, args_expr, def_val) = x.as_ref();
let def_val = def_val.as_ref();
// Handle Fn
if name == FN_FN_PTR && args_expr.len() == 1 {
let hash_fn =
calc_fn_hash(empty(), name, 1, once(TypeId::of::<ImmutableString>()));
if !self.has_override(lib, (hash_fn, *hash)) {
// Fn - only in function call style
let expr = args_expr.get(0);
let arg_value = self.eval_expr(scope, state, lib, expr, level)?;
return arg_value
.take_immutable_string()
.map(|s| FnPtr::from(s).into())
.map_err(|type_name| {
Box::new(EvalAltResult::ErrorMismatchOutputType(
type_name.into(),
Position::none(),
))
});
}
}
// Handle eval
if name == KEYWORD_EVAL && args_expr.len() == 1 {
let hash_fn =

View File

@@ -2,6 +2,7 @@ use crate::any::Dynamic;
use crate::engine::Engine;
use crate::parser::ScriptFnDef;
use crate::result::EvalAltResult;
use crate::utils::ImmutableString;
use crate::stdlib::{boxed::Box, fmt, rc::Rc, sync::Arc};
@@ -53,8 +54,37 @@ pub fn shared_take<T: Clone>(value: Shared<T>) -> T {
pub type FnCallArgs<'a> = [&'a mut Dynamic];
/// A general function pointer.
#[derive(Debug, Clone, Eq, PartialEq, Hash, Default)]
pub struct FnPtr(ImmutableString);
impl FnPtr {
/// Get the name of the function.
pub fn fn_name(&self) -> &str {
self.get_fn_name().as_ref()
}
/// Get the name of the function.
pub(crate) fn get_fn_name(&self) -> &ImmutableString {
&self.0
}
}
impl fmt::Display for FnPtr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Fn({})", self.0)
}
}
impl<S: Into<ImmutableString>> From<S> for FnPtr {
fn from(value: S) -> Self {
Self(value.into())
}
}
/// A general function trail object.
#[cfg(not(feature = "sync"))]
pub type FnAny = dyn Fn(&Engine, &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;

8
src/packages/fn_basic.rs Normal file
View File

@@ -0,0 +1,8 @@
use crate::def_package;
use crate::fn_native::FnPtr;
def_package!(crate:BasicFnPackage:"Basic Fn functions.", lib, {
lib.set_fn_1_mut("name", |f: &mut FnPtr| Ok(f.get_fn_name().clone()));
lib.set_getter_fn("name", |f: &mut FnPtr| Ok(f.get_fn_name().clone()));
});

View File

@@ -9,6 +9,7 @@ use crate::stdlib::any::TypeId;
pub(crate) mod arithmetic;
mod array_basic;
mod eval;
mod fn_basic;
mod iter_basic;
mod logic;
mod map_basic;
@@ -23,6 +24,8 @@ pub use arithmetic::ArithmeticPackage;
#[cfg(not(feature = "no_index"))]
pub use array_basic::BasicArrayPackage;
pub use eval::EvalPackage;
#[cfg(not(feature = "no_function"))]
pub use fn_basic::BasicFnPackage;
pub use iter_basic::BasicIteratorPackage;
pub use logic::LogicPackage;
#[cfg(not(feature = "no_object"))]

View File

@@ -1,4 +1,5 @@
use super::arithmetic::ArithmeticPackage;
use super::fn_basic::BasicFnPackage;
use super::iter_basic::BasicIteratorPackage;
use super::logic::LogicPackage;
use super::string_basic::BasicStringPackage;
@@ -10,4 +11,5 @@ def_package!(crate:CorePackage:"_Core_ package containing basic facilities.", li
LogicPackage::init(lib);
BasicStringPackage::init(lib);
BasicIteratorPackage::init(lib);
BasicFnPackage::init(lib);
});

View File

@@ -1,5 +1,6 @@
use crate::def_package;
use crate::engine::{FN_TO_STRING, KEYWORD_DEBUG, KEYWORD_PRINT};
use crate::fn_native::FnPtr;
use crate::module::FuncReturn;
use crate::parser::{ImmutableString, INT};
@@ -34,8 +35,9 @@ macro_rules! reg_op {
}
def_package!(crate:BasicStringPackage:"Basic string utilities, including printing.", lib, {
reg_op!(lib, KEYWORD_PRINT, to_string, INT, bool, char);
reg_op!(lib, FN_TO_STRING, to_string, INT, bool, char);
reg_op!(lib, KEYWORD_PRINT, to_string, INT, bool, char, FnPtr);
reg_op!(lib, FN_TO_STRING, to_string, INT, bool, char, FnPtr);
lib.set_fn_1_mut(KEYWORD_DEBUG, |f: &mut FnPtr| Ok(f.to_string()));
lib.set_fn_0(KEYWORD_PRINT, || Ok("".to_string()));
lib.set_fn_1(KEYWORD_PRINT, |_: ()| Ok("".to_string()));