Implement function pointers.
This commit is contained in:
40
src/any.rs
40
src/any.rs
@@ -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.
|
||||
|
@@ -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 =
|
||||
|
@@ -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
8
src/packages/fn_basic.rs
Normal 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()));
|
||||
|
||||
});
|
@@ -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"))]
|
||||
|
@@ -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);
|
||||
});
|
||||
|
@@ -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()));
|
||||
|
Reference in New Issue
Block a user