From e14cfbd9d7193bf5540adbbe47beb808b46eec2d Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Thu, 2 Jul 2020 12:47:24 +0800 Subject: [PATCH] Restore plugins code. --- src/engine.rs | 1 + src/fn_native.rs | 56 +++++++++++++++++++++++++----- src/fn_register.rs | 86 ++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 6 +++- src/module.rs | 8 ++--- src/plugin.rs | 42 ++++++++++++++++++++++ 6 files changed, 184 insertions(+), 15 deletions(-) create mode 100644 src/plugin.rs diff --git a/src/engine.rs b/src/engine.rs index 65c91735..0a46bc1b 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -2019,6 +2019,7 @@ impl Engine { ) .map_err(|err| err.new_position(*pos)) } + Ok(f) if f.is_plugin_fn() => f.get_plugin_fn().call(args.as_mut(), *pos), Ok(f) => { f.get_native_fn()(self, args.as_mut()).map_err(|err| err.new_position(*pos)) } diff --git a/src/fn_native.rs b/src/fn_native.rs index e41ae7db..f3632d1e 100644 --- a/src/fn_native.rs +++ b/src/fn_native.rs @@ -1,6 +1,7 @@ use crate::any::Dynamic; use crate::engine::Engine; use crate::parser::ScriptFnDef; +use crate::plugin::PluginFunction; use crate::result::EvalAltResult; use crate::utils::ImmutableString; @@ -96,6 +97,11 @@ pub type FnAny = /// A standard function that gets an iterator from a type. pub type IteratorFn = fn(Dynamic) -> Box>; +#[cfg(feature = "sync")] +pub type SharedPluginFunction = Arc; +#[cfg(not(feature = "sync"))] +pub type SharedPluginFunction = Rc; + /// A standard callback function. #[cfg(not(feature = "sync"))] pub type Callback = Box R + 'static>; @@ -113,6 +119,8 @@ pub enum CallableFunction { Method(Shared), /// An iterator function. Iterator(IteratorFn), + /// A plugin-defined function, + Plugin(SharedPluginFunction), /// A script-defined function. Script(Shared), } @@ -123,6 +131,7 @@ impl fmt::Debug for CallableFunction { Self::Pure(_) => write!(f, "NativePureFunction"), Self::Method(_) => write!(f, "NativeMethod"), Self::Iterator(_) => write!(f, "NativeIterator"), + Self::Plugin(_) => write!(f, "PluginFunction"), Self::Script(fn_def) => fmt::Debug::fmt(fn_def, f), } } @@ -134,6 +143,7 @@ impl fmt::Display for CallableFunction { Self::Pure(_) => write!(f, "NativePureFunction"), Self::Method(_) => write!(f, "NativeMethod"), Self::Iterator(_) => write!(f, "NativeIterator"), + Self::Plugin(_) => write!(f, "PluginFunction"), CallableFunction::Script(s) => fmt::Display::fmt(s, f), } } @@ -144,28 +154,35 @@ impl CallableFunction { pub fn is_pure(&self) -> bool { match self { Self::Pure(_) => true, - Self::Method(_) | Self::Iterator(_) | Self::Script(_) => false, + Self::Method(_) | Self::Iterator(_) | Self::Script(_) | Self::Plugin(_) => false, } } /// Is this a native Rust method function? pub fn is_method(&self) -> bool { match self { Self::Method(_) => true, - Self::Pure(_) | Self::Iterator(_) | Self::Script(_) => false, + Self::Pure(_) | Self::Iterator(_) | Self::Script(_) | Self::Plugin(_) => false, } } /// Is this an iterator function? pub fn is_iter(&self) -> bool { match self { Self::Iterator(_) => true, - Self::Pure(_) | Self::Method(_) | Self::Script(_) => false, + Self::Pure(_) | Self::Method(_) | Self::Script(_) | Self::Plugin(_) => false, } } /// Is this a Rhai-scripted function? pub fn is_script(&self) -> bool { match self { Self::Script(_) => true, - Self::Pure(_) | Self::Method(_) | Self::Iterator(_) => false, + Self::Pure(_) | Self::Method(_) | Self::Iterator(_) | Self::Plugin(_) => false, + } + } + /// Is this a plugin-defined function? + pub fn is_plugin_fn(&self) -> bool { + match self { + Self::Plugin(_) => true, + Self::Pure(_) | Self::Method(_) | Self::Iterator(_) | Self::Script(_) => false, } } /// Get a reference to a native Rust function. @@ -176,7 +193,7 @@ impl CallableFunction { pub fn get_native_fn(&self) -> &FnAny { match self { Self::Pure(f) | Self::Method(f) => f.as_ref(), - Self::Iterator(_) | Self::Script(_) => unreachable!(), + Self::Iterator(_) | Self::Script(_) | Self::Plugin(_) => unreachable!(), } } /// Get a shared reference to a script-defined function definition. @@ -186,7 +203,7 @@ impl CallableFunction { /// Panics if the `CallableFunction` is not `Script`. pub fn get_shared_fn_def(&self) -> Shared { match self { - Self::Pure(_) | Self::Method(_) | Self::Iterator(_) => unreachable!(), + Self::Pure(_) | Self::Method(_) | Self::Iterator(_) | Self::Plugin(_) => unreachable!(), Self::Script(f) => f.clone(), } } @@ -197,7 +214,7 @@ impl CallableFunction { /// Panics if the `CallableFunction` is not `Script`. pub fn get_fn_def(&self) -> &ScriptFnDef { match self { - Self::Pure(_) | Self::Method(_) | Self::Iterator(_) => unreachable!(), + Self::Pure(_) | Self::Method(_) | Self::Iterator(_) | Self::Plugin(_) => unreachable!(), Self::Script(f) => f, } } @@ -209,7 +226,18 @@ impl CallableFunction { pub fn get_iter_fn(&self) -> IteratorFn { match self { Self::Iterator(f) => *f, - Self::Pure(_) | Self::Method(_) | Self::Script(_) => unreachable!(), + Self::Pure(_) | Self::Method(_) | Self::Script(_) | Self::Plugin(_) => unreachable!(), + } + } + /// Get a reference to a plugin function. + /// + /// # Panics + /// + /// Panics if the `CallableFunction` is not `Plugin`. + pub fn get_plugin_fn<'s>(&'s self) -> SharedPluginFunction { + match self { + Self::Plugin(f) => f.clone(), + Self::Pure(_) | Self::Method(_) | Self::Script(_) | Self::Iterator(_) => unreachable!(), } } /// Create a new `CallableFunction::Pure`. @@ -220,6 +248,18 @@ impl CallableFunction { pub fn from_method(func: Box) -> Self { Self::Method(func.into()) } + + #[cfg(feature = "sync")] + /// Create a new `CallableFunction::Plugin`. + pub fn from_plugin(plugin: impl PluginFunction + 'static + Send + Sync) -> Self { + Self::Plugin(Arc::new(plugin)) + } + + #[cfg(not(feature = "sync"))] + /// Create a new `CallableFunction::Plugin`. + pub fn from_plugin(plugin: impl PluginFunction + 'static) -> Self { + Self::Plugin(Rc::new(plugin)) + } } impl From for CallableFunction { diff --git a/src/fn_register.rs b/src/fn_register.rs index 1b07cbe8..cf83ae14 100644 --- a/src/fn_register.rs +++ b/src/fn_register.rs @@ -5,11 +5,91 @@ use crate::any::{Dynamic, Variant}; use crate::engine::Engine; use crate::fn_native::{CallableFunction, FnAny, FnCallArgs, SendSync}; use crate::parser::FnAccess; +use crate::plugin::Plugin; use crate::result::EvalAltResult; use crate::utils::ImmutableString; use crate::stdlib::{any::TypeId, boxed::Box, mem}; +/// A trait to register custom plugins with the `Engine`. +/// +/// A plugin consists of a number of functions. All functions will be registered with the engine. +pub trait RegisterPlugin { + /// Allow extensions of the engine's behavior. + /// + /// This can include importing modules, registering functions to the global name space, and + /// more. + /// + /// # Example + /// + /// ``` + /// use rhai::{FLOAT, INT, Module, ModuleResolver, RegisterFn, RegisterPlugin}; + /// use rhai::plugin::*; + /// use rhai::module_resolvers::*; + /// + /// // A function we want to expose to Rhai. + /// #[derive(Copy, Clone)] + /// struct DistanceFunction(); + /// + /// impl PluginFunction for DistanceFunction { + /// fn is_method_call(&self) -> bool { false } + /// fn is_varadic(&self) -> bool { false } + /// + /// fn call(&self, args: &[&mut Dynamic], pos: Position) -> Result> { + /// let x1: &FLOAT = args[0].downcast_ref::().unwrap(); + /// let y1: &FLOAT = args[1].downcast_ref::().unwrap(); + /// let x2: &FLOAT = args[2].downcast_ref::().unwrap(); + /// let y2: &FLOAT = args[3].downcast_ref::().unwrap(); + /// let square_sum = (y2 - y1).abs().powf(2.0) + (x2 -x1).abs().powf(2.0); + /// Ok(Dynamic::from(square_sum.sqrt())) + /// } + /// + /// fn clone_boxed(&self) -> Box { + /// Box::new(DistanceFunction()) + /// } + /// } + /// + /// // A simple custom plugin. This should not usually be done with hand-written code. + /// #[derive(Copy, Clone)] + /// pub struct AdvancedMathPlugin(); + /// + /// impl Plugin for AdvancedMathPlugin { + /// fn register_contents(self, engine: &mut Engine) { + /// // Plugins are allowed to have side-effects on the engine. + /// engine.register_fn("get_mystic_number", || { 42 as FLOAT }); + /// + /// // Main purpose: create a module to expose the functions to Rhai. + /// // + /// // This is currently a hack. There needs to be a better API here for "plugin" + /// // modules. + /// let mut m = Module::new(); + /// m.set_fn("euclidean_distance".to_string(), FnAccess::Public, + /// &[std::any::TypeId::of::(), + /// std::any::TypeId::of::(), + /// std::any::TypeId::of::(), + /// std::any::TypeId::of::()], + /// CallableFunction::from_plugin(DistanceFunction())); + /// let mut r = StaticModuleResolver::new(); + /// r.insert("Math::Advanced".to_string(), m); + /// engine.set_module_resolver(Some(r)); + /// } + /// } + /// + /// + /// # fn main() -> Result<(), Box> { + /// + /// let mut engine = Engine::new(); + /// engine.register_plugin(AdvancedMathPlugin()); + /// + /// assert_eq!(engine.eval::( + /// r#"import "Math::Advanced" as math; + /// let x = math::euclidean_distance(0.0, 1.0, 0.0, get_mystic_number()); x"#)?, 41.0); + /// # Ok(()) + /// # } + /// ``` + fn register_plugin(&mut self, plugin: PL); +} + /// Trait to register custom functions with the `Engine`. pub trait RegisterFn { /// Register a custom function with the `Engine`. @@ -111,6 +191,12 @@ pub fn by_value(data: &mut Dynamic) -> T { } } +impl RegisterPlugin for Engine { + fn register_plugin(&mut self, plugin: PL) { + plugin.register_contents(self); + } +} + /// This macro creates a closure wrapping a registered function. macro_rules! make_func { ($fn:ident : $map:expr ; $($par:ident => $convert:expr),*) => { diff --git a/src/lib.rs b/src/lib.rs index aa205dec..6e441424 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -84,6 +84,10 @@ mod module; mod optimize; pub mod packages; mod parser; +#[cfg(not(feature = "no_module"))] +pub mod plugin; +#[cfg(feature = "no_module")] +mod plugin; mod result; mod scope; mod stdlib; @@ -95,7 +99,7 @@ pub use any::Dynamic; pub use engine::Engine; pub use error::{ParseError, ParseErrorType}; pub use fn_native::IteratorFn; -pub use fn_register::{RegisterFn, RegisterResultFn}; +pub use fn_register::{RegisterFn, RegisterPlugin, RegisterResultFn}; pub use module::Module; pub use parser::{ImmutableString, AST, INT}; pub use result::EvalAltResult; diff --git a/src/module.rs b/src/module.rs index 3cdeb711..c7198d83 100644 --- a/src/module.rs +++ b/src/module.rs @@ -876,12 +876,10 @@ impl Module { .functions .iter() .filter(|(_, (_, _, _, v))| match v { - CallableFunction::Pure(_) - | CallableFunction::Method(_) - | CallableFunction::Iterator(_) => true, CallableFunction::Script(ref f) => { filter(f.access, f.name.as_str(), f.params.len()) } + _ => true, }) .map(|(&k, v)| (k, v.clone())), ); @@ -897,10 +895,8 @@ impl Module { /// Filter out the functions, retaining only some based on a filter predicate. pub(crate) fn retain_functions(&mut self, filter: impl Fn(FnAccess, &str, usize) -> bool) { self.functions.retain(|_, (_, _, _, v)| match v { - CallableFunction::Pure(_) - | CallableFunction::Method(_) - | CallableFunction::Iterator(_) => true, CallableFunction::Script(ref f) => filter(f.access, f.name.as_str(), f.params.len()), + _ => true, }); self.all_functions.clear(); diff --git a/src/plugin.rs b/src/plugin.rs new file mode 100644 index 00000000..e0d53507 --- /dev/null +++ b/src/plugin.rs @@ -0,0 +1,42 @@ +//! Module defining plugins in Rhai. Is exported for use by plugin authors. + +pub use crate::any::{Dynamic, Variant}; +pub use crate::fn_native::{CallableFunction, FnCallArgs, IteratorFn}; +pub use crate::parser::{ + FnAccess, + FnAccess::{Private, Public}, + AST, +}; +pub use crate::result::EvalAltResult; +pub use crate::scope::{Entry as ScopeEntry, EntryType as ScopeEntryType, Scope}; +pub use crate::token::{Position, Token}; +pub use crate::utils::StaticVec; +pub use crate::Engine; + +#[cfg(features = "sync")] +/// Represents an externally-written plugin for the Rhai interpreter. +/// +/// This trait should not be used directly. Use the `#[plugin]` procedural attribute instead. +pub trait Plugin: Send { + fn register_contents(self, engine: &mut Engine); +} + +#[cfg(not(features = "sync"))] +/// Represents an externally-written plugin for the Rhai interpreter. +/// +/// This trait should not be used directly. Use the `#[plugin]` procedural attribute instead. +pub trait Plugin: Send + Sync { + fn register_contents(self, engine: &mut Engine); +} + +/// Represents a function that is statically defined within a plugin. +/// +/// This trait should not be used directly. Use the `#[plugin]` procedural attribute instead. +pub trait PluginFunction { + fn is_method_call(&self) -> bool; + fn is_varadic(&self) -> bool; + + fn call(&self, args: &[&mut Dynamic], pos: Position) -> Result>; + + fn clone_boxed(&self) -> Box; +}