From ea78fa2b27bd4bb03399d41501e5e5247579ec6a Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 29 Sep 2020 13:06:48 +0800 Subject: [PATCH 1/9] Change SharedPluginFunction to Shared --- codegen/src/function.rs | 2 +- codegen/src/rhai_module.rs | 2 +- codegen/src/test/function.rs | 10 ++-- codegen/src/test/module.rs | 92 ++++++++++++++++++------------------ src/fn_native.rs | 36 +++++++------- src/fn_register.rs | 2 +- src/syntax.rs | 10 +--- src/utils.rs | 3 +- 8 files changed, 77 insertions(+), 80 deletions(-) diff --git a/codegen/src/function.rs b/codegen/src/function.rs index 72ad2b64..47427ee8 100644 --- a/codegen/src/function.rs +++ b/codegen/src/function.rs @@ -580,7 +580,7 @@ impl ExportedFn { ); quote! { pub fn #callable_fn_name() -> CallableFunction { - CallableFunction::from_plugin(#token_name()) + #token_name().into() } } } diff --git a/codegen/src/rhai_module.rs b/codegen/src/rhai_module.rs index f6939ae7..94c0f720 100644 --- a/codegen/src/rhai_module.rs +++ b/codegen/src/rhai_module.rs @@ -126,7 +126,7 @@ pub(crate) fn generate_body( set_fn_stmts.push( syn::parse2::(quote! { m.set_fn(#fn_literal, FnAccess::Public, &[#(#fn_input_types),*], - CallableFunction::from_plugin(#fn_token_name())); + #fn_token_name().into()); }) .unwrap(), ); diff --git a/codegen/src/test/function.rs b/codegen/src/test/function.rs index a5b86ba1..2641417a 100644 --- a/codegen/src/test/function.rs +++ b/codegen/src/test/function.rs @@ -290,7 +290,7 @@ mod generate_tests { } } pub fn token_callable() -> CallableFunction { - CallableFunction::from_plugin(Token()) + Token().into() } pub fn token_input_types() -> Box<[TypeId]> { Token().input_types() @@ -335,7 +335,7 @@ mod generate_tests { } } pub fn token_callable() -> CallableFunction { - CallableFunction::from_plugin(Token()) + Token().into() } pub fn token_input_types() -> Box<[TypeId]> { Token().input_types() @@ -412,7 +412,7 @@ mod generate_tests { } } pub fn token_callable() -> CallableFunction { - CallableFunction::from_plugin(Token()) + Token().into() } pub fn token_input_types() -> Box<[TypeId]> { Token().input_types() @@ -459,7 +459,7 @@ mod generate_tests { } } pub fn token_callable() -> CallableFunction { - CallableFunction::from_plugin(Token()) + Token().into() } pub fn token_input_types() -> Box<[TypeId]> { Token().input_types() @@ -505,7 +505,7 @@ mod generate_tests { } } pub fn token_callable() -> CallableFunction { - CallableFunction::from_plugin(Token()) + Token().into() } pub fn token_input_types() -> Box<[TypeId]> { Token().input_types() diff --git a/codegen/src/test/module.rs b/codegen/src/test/module.rs index 472c949d..dbb2d023 100644 --- a/codegen/src/test/module.rs +++ b/codegen/src/test/module.rs @@ -296,7 +296,7 @@ mod generate_tests { #[allow(unused_mut)] pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { m.set_fn("get_mystic_number", FnAccess::Public, &[], - CallableFunction::from_plugin(get_mystic_number_token())); + get_mystic_number_token().into()); if flatten {} else {} } #[allow(non_camel_case_types)] @@ -320,7 +320,7 @@ mod generate_tests { } } pub fn get_mystic_number_token_callable() -> CallableFunction { - CallableFunction::from_plugin(get_mystic_number_token()) + get_mystic_number_token().into() } pub fn get_mystic_number_token_input_types() -> Box<[TypeId]> { get_mystic_number_token().input_types() @@ -358,7 +358,7 @@ mod generate_tests { #[allow(unused_mut)] pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { m.set_fn("add_one_to", FnAccess::Public, &[core::any::TypeId::of::()], - CallableFunction::from_plugin(add_one_to_token())); + add_one_to_token().into()); if flatten {} else {} } #[allow(non_camel_case_types)] @@ -383,7 +383,7 @@ mod generate_tests { } } pub fn add_one_to_token_callable() -> CallableFunction { - CallableFunction::from_plugin(add_one_to_token()) + add_one_to_token().into() } pub fn add_one_to_token_input_types() -> Box<[TypeId]> { add_one_to_token().input_types() @@ -432,10 +432,10 @@ mod generate_tests { #[allow(unused_mut)] pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { m.set_fn("add_n", FnAccess::Public, &[core::any::TypeId::of::()], - CallableFunction::from_plugin(add_one_to_token())); + add_one_to_token().into()); m.set_fn("add_n", FnAccess::Public, &[core::any::TypeId::of::(), core::any::TypeId::of::()], - CallableFunction::from_plugin(add_n_to_token())); + add_n_to_token().into()); if flatten {} else {} } #[allow(non_camel_case_types)] @@ -460,7 +460,7 @@ mod generate_tests { } } pub fn add_one_to_token_callable() -> CallableFunction { - CallableFunction::from_plugin(add_one_to_token()) + add_one_to_token().into() } pub fn add_one_to_token_input_types() -> Box<[TypeId]> { add_one_to_token().input_types() @@ -490,7 +490,7 @@ mod generate_tests { } } pub fn add_n_to_token_callable() -> CallableFunction { - CallableFunction::from_plugin(add_n_to_token()) + add_n_to_token().into() } pub fn add_n_to_token_input_types() -> Box<[TypeId]> { add_n_to_token().input_types() @@ -529,7 +529,7 @@ mod generate_tests { pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { m.set_fn("add_together", FnAccess::Public, &[core::any::TypeId::of::(), core::any::TypeId::of::()], - CallableFunction::from_plugin(add_together_token())); + add_together_token().into()); if flatten {} else {} } #[allow(non_camel_case_types)] @@ -556,7 +556,7 @@ mod generate_tests { } } pub fn add_together_token_callable() -> CallableFunction { - CallableFunction::from_plugin(add_together_token()) + add_together_token().into() } pub fn add_together_token_input_types() -> Box<[TypeId]> { add_together_token().input_types() @@ -596,13 +596,13 @@ mod generate_tests { pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { m.set_fn("add", FnAccess::Public, &[core::any::TypeId::of::(), core::any::TypeId::of::()], - CallableFunction::from_plugin(add_together_token())); + add_together_token().into()); m.set_fn("+", FnAccess::Public, &[core::any::TypeId::of::(), core::any::TypeId::of::()], - CallableFunction::from_plugin(add_together_token())); + add_together_token().into()); m.set_fn("add_together", FnAccess::Public, &[core::any::TypeId::of::(), core::any::TypeId::of::()], - CallableFunction::from_plugin(add_together_token())); + add_together_token().into()); if flatten {} else {} } #[allow(non_camel_case_types)] @@ -629,7 +629,7 @@ mod generate_tests { } } pub fn add_together_token_callable() -> CallableFunction { - CallableFunction::from_plugin(add_together_token()) + add_together_token().into() } pub fn add_together_token_input_types() -> Box<[TypeId]> { add_together_token().input_types() @@ -844,7 +844,7 @@ mod generate_tests { #[allow(unused_mut)] pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { m.set_fn("get_mystic_number", FnAccess::Public, &[], - CallableFunction::from_plugin(get_mystic_number_token())); + get_mystic_number_token().into()); if flatten {} else {} } #[allow(non_camel_case_types)] @@ -868,7 +868,7 @@ mod generate_tests { } } pub fn get_mystic_number_token_callable() -> CallableFunction { - CallableFunction::from_plugin(get_mystic_number_token()) + get_mystic_number_token().into() } pub fn get_mystic_number_token_input_types() -> Box<[TypeId]> { get_mystic_number_token().input_types() @@ -937,7 +937,7 @@ mod generate_tests { pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { m.set_fn("print_out_to", FnAccess::Public, &[core::any::TypeId::of::()], - CallableFunction::from_plugin(print_out_to_token())); + print_out_to_token().into()); if flatten {} else {} } #[allow(non_camel_case_types)] @@ -962,7 +962,7 @@ mod generate_tests { } } pub fn print_out_to_token_callable() -> CallableFunction { - CallableFunction::from_plugin(print_out_to_token()) + print_out_to_token().into() } pub fn print_out_to_token_input_types() -> Box<[TypeId]> { print_out_to_token().input_types() @@ -1001,7 +1001,7 @@ mod generate_tests { pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { m.set_fn("print_out_to", FnAccess::Public, &[core::any::TypeId::of::()], - CallableFunction::from_plugin(print_out_to_token())); + print_out_to_token().into()); if flatten {} else {} } #[allow(non_camel_case_types)] @@ -1026,7 +1026,7 @@ mod generate_tests { } } pub fn print_out_to_token_callable() -> CallableFunction { - CallableFunction::from_plugin(print_out_to_token()) + print_out_to_token().into() } pub fn print_out_to_token_input_types() -> Box<[TypeId]> { print_out_to_token().input_types() @@ -1065,7 +1065,7 @@ mod generate_tests { pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { m.set_fn("increment", FnAccess::Public, &[core::any::TypeId::of::()], - CallableFunction::from_plugin(increment_token())); + increment_token().into()); if flatten {} else {} } #[allow(non_camel_case_types)] @@ -1090,7 +1090,7 @@ mod generate_tests { } } pub fn increment_token_callable() -> CallableFunction { - CallableFunction::from_plugin(increment_token()) + increment_token().into() } pub fn increment_token_input_types() -> Box<[TypeId]> { increment_token().input_types() @@ -1132,7 +1132,7 @@ mod generate_tests { pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { m.set_fn("increment", FnAccess::Public, &[core::any::TypeId::of::()], - CallableFunction::from_plugin(increment_token())); + increment_token().into()); if flatten {} else {} } #[allow(non_camel_case_types)] @@ -1157,7 +1157,7 @@ mod generate_tests { } } pub fn increment_token_callable() -> CallableFunction { - CallableFunction::from_plugin(increment_token()) + increment_token().into() } pub fn increment_token_input_types() -> Box<[TypeId]> { increment_token().input_types() @@ -1219,7 +1219,7 @@ mod generate_tests { pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { m.set_fn("increment", FnAccess::Public, &[core::any::TypeId::of::()], - CallableFunction::from_plugin(increment_token())); + increment_token().into()); if flatten {} else {} } #[allow(non_camel_case_types)] @@ -1244,7 +1244,7 @@ mod generate_tests { } } pub fn increment_token_callable() -> CallableFunction { - CallableFunction::from_plugin(increment_token()) + increment_token().into() } pub fn increment_token_input_types() -> Box<[TypeId]> { increment_token().input_types() @@ -1304,7 +1304,7 @@ mod generate_tests { #[allow(unused_mut)] pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { m.set_fn("get$square", FnAccess::Public, &[core::any::TypeId::of::()], - CallableFunction::from_plugin(int_foo_token())); + int_foo_token().into()); if flatten {} else {} } #[allow(non_camel_case_types)] @@ -1329,7 +1329,7 @@ mod generate_tests { } } pub fn int_foo_token_callable() -> CallableFunction { - CallableFunction::from_plugin(int_foo_token()) + int_foo_token().into() } pub fn int_foo_token_input_types() -> Box<[TypeId]> { int_foo_token().input_types() @@ -1368,9 +1368,9 @@ mod generate_tests { #[allow(unused_mut)] pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { m.set_fn("square", FnAccess::Public, &[core::any::TypeId::of::()], - CallableFunction::from_plugin(int_foo_token())); + int_foo_token().into()); m.set_fn("get$square", FnAccess::Public, &[core::any::TypeId::of::()], - CallableFunction::from_plugin(int_foo_token())); + int_foo_token().into()); if flatten {} else {} } #[allow(non_camel_case_types)] @@ -1395,7 +1395,7 @@ mod generate_tests { } } pub fn int_foo_token_callable() -> CallableFunction { - CallableFunction::from_plugin(int_foo_token()) + int_foo_token().into() } pub fn int_foo_token_input_types() -> Box<[TypeId]> { int_foo_token().input_types() @@ -1436,7 +1436,7 @@ mod generate_tests { m.set_fn("set$squared", FnAccess::Public, &[core::any::TypeId::of::(), core::any::TypeId::of::()], - CallableFunction::from_plugin(int_foo_token())); + int_foo_token().into()); if flatten {} else {} } #[allow(non_camel_case_types)] @@ -1462,7 +1462,7 @@ mod generate_tests { } } pub fn int_foo_token_callable() -> CallableFunction { - CallableFunction::from_plugin(int_foo_token()) + int_foo_token().into() } pub fn int_foo_token_input_types() -> Box<[TypeId]> { int_foo_token().input_types() @@ -1503,11 +1503,11 @@ mod generate_tests { m.set_fn("set_sq", FnAccess::Public, &[core::any::TypeId::of::(), core::any::TypeId::of::()], - CallableFunction::from_plugin(int_foo_token())); + int_foo_token().into()); m.set_fn("set$squared", FnAccess::Public, &[core::any::TypeId::of::(), core::any::TypeId::of::()], - CallableFunction::from_plugin(int_foo_token())); + int_foo_token().into()); if flatten {} else {} } #[allow(non_camel_case_types)] @@ -1533,7 +1533,7 @@ mod generate_tests { } } pub fn int_foo_token_callable() -> CallableFunction { - CallableFunction::from_plugin(int_foo_token()) + int_foo_token().into() } pub fn int_foo_token_input_types() -> Box<[TypeId]> { int_foo_token().input_types() @@ -1574,7 +1574,7 @@ mod generate_tests { m.set_fn("index$get$", FnAccess::Public, &[core::any::TypeId::of::(), core::any::TypeId::of::()], - CallableFunction::from_plugin(get_by_index_token())); + get_by_index_token().into()); if flatten {} else {} } #[allow(non_camel_case_types)] @@ -1601,7 +1601,7 @@ mod generate_tests { } } pub fn get_by_index_token_callable() -> CallableFunction { - CallableFunction::from_plugin(get_by_index_token()) + get_by_index_token().into() } pub fn get_by_index_token_input_types() -> Box<[TypeId]> { get_by_index_token().input_types() @@ -1642,11 +1642,11 @@ mod generate_tests { m.set_fn("get", FnAccess::Public, &[core::any::TypeId::of::(), core::any::TypeId::of::()], - CallableFunction::from_plugin(get_by_index_token())); + get_by_index_token().into()); m.set_fn("index$get$", FnAccess::Public, &[core::any::TypeId::of::(), core::any::TypeId::of::()], - CallableFunction::from_plugin(get_by_index_token())); + get_by_index_token().into()); if flatten {} else {} } #[allow(non_camel_case_types)] @@ -1673,7 +1673,7 @@ mod generate_tests { } } pub fn get_by_index_token_callable() -> CallableFunction { - CallableFunction::from_plugin(get_by_index_token()) + get_by_index_token().into() } pub fn get_by_index_token_input_types() -> Box<[TypeId]> { get_by_index_token().input_types() @@ -1715,7 +1715,7 @@ mod generate_tests { &[core::any::TypeId::of::(), core::any::TypeId::of::(), core::any::TypeId::of::()], - CallableFunction::from_plugin(set_by_index_token())); + set_by_index_token().into()); if flatten {} else {} } #[allow(non_camel_case_types)] @@ -1744,7 +1744,7 @@ mod generate_tests { } } pub fn set_by_index_token_callable() -> CallableFunction { - CallableFunction::from_plugin(set_by_index_token()) + set_by_index_token().into() } pub fn set_by_index_token_input_types() -> Box<[TypeId]> { set_by_index_token().input_types() @@ -1786,12 +1786,12 @@ mod generate_tests { &[core::any::TypeId::of::(), core::any::TypeId::of::(), core::any::TypeId::of::()], - CallableFunction::from_plugin(set_by_index_token())); + set_by_index_token().into()); m.set_fn("index$set$", FnAccess::Public, &[core::any::TypeId::of::(), core::any::TypeId::of::(), core::any::TypeId::of::()], - CallableFunction::from_plugin(set_by_index_token())); + set_by_index_token().into()); if flatten {} else {} } #[allow(non_camel_case_types)] @@ -1820,7 +1820,7 @@ mod generate_tests { } } pub fn set_by_index_token_callable() -> CallableFunction { - CallableFunction::from_plugin(set_by_index_token()) + set_by_index_token().into() } pub fn set_by_index_token_input_types() -> Box<[TypeId]> { set_by_index_token().input_types() diff --git a/src/fn_native.rs b/src/fn_native.rs index 041ff224..390fa340 100644 --- a/src/fn_native.rs +++ b/src/fn_native.rs @@ -199,10 +199,10 @@ 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; +pub type FnPlugin = dyn PluginFunction; +#[cfg(feature = "sync")] +pub type FnPlugin = dyn PluginFunction + Send + Sync; /// A standard callback function. #[cfg(not(feature = "sync"))] @@ -222,7 +222,7 @@ pub enum CallableFunction { /// An iterator function. Iterator(IteratorFn), /// A plugin-defined function, - Plugin(SharedPluginFunction), + Plugin(Shared), /// A script-defined function. #[cfg(not(feature = "no_function"))] Script(Shared), @@ -389,9 +389,9 @@ impl CallableFunction { /// # Panics /// /// Panics if the `CallableFunction` is not `Plugin`. - pub fn get_plugin_fn<'s>(&'s self) -> SharedPluginFunction { + pub fn get_plugin_fn<'s>(&'s self) -> &FnPlugin { match self { - Self::Plugin(f) => f.clone(), + Self::Plugin(f) => f.as_ref(), Self::Pure(_) | Self::Method(_) | Self::Iterator(_) => unreachable!(), #[cfg(not(feature = "no_function"))] @@ -406,17 +406,9 @@ 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)) + pub fn from_plugin(func: impl PluginFunction + 'static + SendSync) -> Self { + Self::Plugin((Box::new(func) as Box).into()) } } @@ -445,3 +437,15 @@ impl From> for CallableFunction { Self::Script(_func) } } + +impl From for CallableFunction { + fn from(func: T) -> Self { + Self::from_plugin(func) + } +} + +impl From> for CallableFunction { + fn from(func: Shared) -> Self { + Self::Plugin(func.into()) + } +} diff --git a/src/fn_register.rs b/src/fn_register.rs index 12aedad2..b6b7b896 100644 --- a/src/fn_register.rs +++ b/src/fn_register.rs @@ -90,7 +90,7 @@ pub trait RegisterPlugin { /// std::any::TypeId::of::(), /// std::any::TypeId::of::(), /// std::any::TypeId::of::()], - /// CallableFunction::from_plugin(DistanceFunction())); + /// DistanceFunction().into()); /// let mut r = StaticModuleResolver::new(); /// r.insert("Math::Advanced".to_string(), m); /// engine.set_module_resolver(Some(r)); diff --git a/src/syntax.rs b/src/syntax.rs index 4cc937e6..cf9c9519 100644 --- a/src/syntax.rs +++ b/src/syntax.rs @@ -17,11 +17,6 @@ use crate::stdlib::{ string::{String, ToString}, }; -#[cfg(not(feature = "sync"))] -use crate::stdlib::rc::Rc; -#[cfg(feature = "sync")] -use crate::stdlib::sync::Arc; - /// A general expression evaluation trait object. #[cfg(not(feature = "sync"))] pub type FnCustomSyntaxEval = dyn Fn( @@ -175,10 +170,7 @@ impl Engine { let syntax = CustomSyntax { segments, - #[cfg(not(feature = "sync"))] - func: Rc::new(func), - #[cfg(feature = "sync")] - func: Arc::new(func), + func: (Box::new(func) as Box).into(), scope_delta, }; diff --git a/src/utils.rs b/src/utils.rs index 2587262b..a257d910 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -61,7 +61,8 @@ impl BuildHasher for StraightHasherBuilder { } } -/// Calculate a `u64` hash key from a module-qualified function name and parameter types. +/// [INTERNALS] Calculate a `u64` hash key from a module-qualified function name and parameter types. +/// Exported under the `internals` feature only. /// /// Module names are passed in via `&str` references from an iterator. /// Parameter types are passed in via `TypeId` values from an iterator. From 69054f1b697673323326e1e5dd6b23bf2bf9fd3d Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 29 Sep 2020 18:17:41 +0800 Subject: [PATCH 2/9] Change lib to &Module. --- src/api.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/api.rs b/src/api.rs index 15998614..f496f2cb 100644 --- a/src/api.rs +++ b/src/api.rs @@ -1504,7 +1504,7 @@ impl Engine { let mut arg_values = args.into_vec(); let mut args: StaticVec<_> = arg_values.as_mut().iter_mut().collect(); - let result = self.call_fn_dynamic_raw(scope, ast, name, &mut None, args.as_mut())?; + let result = self.call_fn_dynamic_raw(scope, ast.lib(), name, &mut None, args.as_mut())?; let typ = self.map_type_name(result.type_name()); @@ -1571,7 +1571,7 @@ impl Engine { pub fn call_fn_dynamic( &self, scope: &mut Scope, - lib: impl AsRef, + lib: &Module, name: &str, mut this_ptr: Option<&mut Dynamic>, mut arg_values: impl AsMut<[Dynamic]>, @@ -1593,12 +1593,11 @@ impl Engine { pub(crate) fn call_fn_dynamic_raw( &self, scope: &mut Scope, - lib: impl AsRef, + lib: &Module, name: &str, this_ptr: &mut Option<&mut Dynamic>, args: &mut [&mut Dynamic], ) -> FuncReturn { - let lib = lib.as_ref(); let fn_def = get_script_function_by_signature(lib, name, args.len(), true) .ok_or_else(|| EvalAltResult::ErrorFunctionNotFound(name.into(), Position::none()))?; From c7dfbd48a6793cdc0f04643223a16d448bd58b20 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 29 Sep 2020 18:37:00 +0800 Subject: [PATCH 3/9] Restore call_fn_dynamic. --- doc/src/engine/call-fn.md | 4 ++-- src/api.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/src/engine/call-fn.md b/doc/src/engine/call-fn.md index 058d6dab..a68925ac 100644 --- a/doc/src/engine/call-fn.md +++ b/doc/src/engine/call-fn.md @@ -65,7 +65,7 @@ anything that implements `AsMut` (such as a simple array or a `Vec, name: &str, mut this_ptr: Option<&mut Dynamic>, mut arg_values: impl AsMut<[Dynamic]>, ) -> FuncReturn { let mut args: StaticVec<_> = arg_values.as_mut().iter_mut().collect(); - self.call_fn_dynamic_raw(scope, lib, name, &mut this_ptr, args.as_mut()) + self.call_fn_dynamic_raw(scope, lib.as_ref(), name, &mut this_ptr, args.as_mut()) } /// Call a script function defined in an `AST` with multiple `Dynamic` arguments. From e526b53b4223174014fc4c86825fe842f55abcb2 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Wed, 30 Sep 2020 09:57:21 +0800 Subject: [PATCH 4/9] No need to merge module if empty. --- src/module/mod.rs | 132 +++++++++++++++++++++++++++++++--------------- tests/modules.rs | 7 +++ 2 files changed, 97 insertions(+), 42 deletions(-) diff --git a/src/module/mod.rs b/src/module/mod.rs index d44e5fbe..0a24de99 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -154,6 +154,25 @@ impl Module { } } + /// Is the module empty? + /// + /// # Examples + /// + /// ``` + /// use rhai::Module; + /// + /// let module = Module::new(); + /// assert!(module.is_empty()); + /// ``` + pub fn is_empty(&self) -> bool { + self.functions.is_empty() + && self.all_functions.is_empty() + && self.variables.is_empty() + && self.all_variables.is_empty() + && self.modules.is_empty() + && self.type_iterators.is_empty() + } + /// Clone the module, optionally skipping the index. fn do_clone(&self, clone_index: bool) -> Self { Self { @@ -1078,10 +1097,18 @@ impl Module { /// Combine another module into this module. /// The other module is consumed to merge into this module. pub fn combine(&mut self, other: Self) -> &mut Self { - self.modules.extend(other.modules.into_iter()); - self.variables.extend(other.variables.into_iter()); - self.functions.extend(other.functions.into_iter()); - self.type_iterators.extend(other.type_iterators.into_iter()); + if !other.modules.is_empty() { + self.modules.extend(other.modules.into_iter()); + } + if !other.variables.is_empty() { + self.variables.extend(other.variables.into_iter()); + } + if !other.functions.is_empty() { + self.functions.extend(other.functions.into_iter()); + } + if !other.type_iterators.is_empty() { + self.type_iterators.extend(other.type_iterators.into_iter()); + } self.all_functions.clear(); self.all_variables.clear(); self.indexed = false; @@ -1092,13 +1119,20 @@ impl Module { /// The other module is consumed to merge into this module. /// Sub-modules are flattened onto the root module, with higher level overriding lower level. pub fn combine_flatten(&mut self, other: Self) -> &mut Self { - other.modules.into_iter().for_each(|(_, m)| { - self.combine_flatten(m); - }); - - self.variables.extend(other.variables.into_iter()); - self.functions.extend(other.functions.into_iter()); - self.type_iterators.extend(other.type_iterators.into_iter()); + if !other.modules.is_empty() { + other.modules.into_iter().for_each(|(_, m)| { + self.combine_flatten(m); + }); + } + if !other.variables.is_empty() { + self.variables.extend(other.variables.into_iter()); + } + if !other.functions.is_empty() { + self.functions.extend(other.functions.into_iter()); + } + if !other.type_iterators.is_empty() { + self.type_iterators.extend(other.type_iterators.into_iter()); + } self.all_functions.clear(); self.all_variables.clear(); self.indexed = false; @@ -1117,36 +1151,42 @@ impl Module { mut _filter: &mut impl FnMut(FnAccess, &str, usize) -> bool, ) -> &mut Self { #[cfg(not(feature = "no_function"))] - for (k, v) in &other.modules { - let mut m = Self::new(); - m.merge_filtered(v, _filter); - self.modules.insert(k.clone(), m); + if !other.modules.is_empty() { + for (k, v) in &other.modules { + let mut m = Self::new(); + m.merge_filtered(v, _filter); + self.modules.insert(k.clone(), m); + } + } + #[cfg(feature = "no_function")] + if !other.modules.is_empty() { + self.modules + .extend(other.modules.iter().map(|(k, v)| (k.clone(), v.clone()))); + } + if !other.variables.is_empty() { + self.variables + .extend(other.variables.iter().map(|(k, v)| (k.clone(), v.clone()))); + } + if !other.functions.is_empty() { + self.functions.extend( + other + .functions + .iter() + .filter(|(_, (_, _, _, _, v))| match v { + #[cfg(not(feature = "no_function"))] + CallableFunction::Script(f) => { + _filter(f.access, f.name.as_str(), f.params.len()) + } + _ => true, + }) + .map(|(&k, v)| (k, v.clone())), + ); } - #[cfg(feature = "no_function")] - self.modules - .extend(other.modules.iter().map(|(k, v)| (k.clone(), v.clone()))); - - self.variables - .extend(other.variables.iter().map(|(k, v)| (k.clone(), v.clone()))); - - self.functions.extend( - other - .functions - .iter() - .filter(|(_, (_, _, _, _, v))| match v { - #[cfg(not(feature = "no_function"))] - CallableFunction::Script(f) => { - _filter(f.access, f.name.as_str(), f.params.len()) - } - _ => true, - }) - .map(|(&k, v)| (k, v.clone())), - ); - - self.type_iterators - .extend(other.type_iterators.iter().map(|(&k, v)| (k, v.clone()))); - + if !other.type_iterators.is_empty() { + self.type_iterators + .extend(other.type_iterators.iter().map(|(&k, v)| (k, v.clone()))); + } self.all_functions.clear(); self.all_variables.clear(); self.indexed = false; @@ -1280,13 +1320,21 @@ impl Module { name, num_args, move |engine: &Engine, lib: &Module, args: &mut [&mut Dynamic]| { - let mut lib_merged = lib.clone(); - lib_merged.merge(&ast_lib); + let mut lib_merged; + + let unified_lib = if lib.is_empty() { + // In the special case of the main script not defining any function + &ast_lib + } else { + lib_merged = lib.clone(); + lib_merged.merge(&ast_lib); + &lib_merged + }; engine .call_fn_dynamic_raw( &mut Scope::new(), - &lib_merged, + &unified_lib, &fn_name, &mut None, args, diff --git a/tests/modules.rs b/tests/modules.rs index b0134594..e1a8bae6 100644 --- a/tests/modules.rs +++ b/tests/modules.rs @@ -237,6 +237,9 @@ fn test_module_from_ast() -> Result<(), Box> { fn add_len(x, y) { x + len(y) } + fn cross_call(x) { + calc(x) + } private fn hidden() { throw "you shouldn't see me!"; } @@ -285,6 +288,10 @@ fn test_module_from_ast() -> Result<(), Box> { engine.eval::(r#"import "testing" as ttt; ttt::calc(999)"#)?, 1000 ); + assert_eq!( + engine.eval::(r#"import "testing" as ttt; ttt::cross_call(999)"#)?, + 1000 + ); assert_eq!( engine.eval::(r#"import "testing" as ttt; ttt::add_len(ttt::foo, ttt::hello)"#)?, 59 From a04ed02b547b0d90ed18d382bc7a8b1bc9d59b3c Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Wed, 30 Sep 2020 22:55:40 +0800 Subject: [PATCH 5/9] Simplify macro system and update doc comments. --- Cargo.toml | 5 +- codegen/Cargo.toml | 2 +- codegen/src/function.rs | 4 +- codegen/src/lib.rs | 115 ++++++++++++++++++++++++++++++++--- codegen/src/test/function.rs | 14 ++--- codegen/src/test/module.rs | 62 +++++++++---------- src/fn_native.rs | 4 +- src/fn_register.rs | 112 ---------------------------------- src/lib.rs | 2 +- src/plugin.rs | 50 +++++++-------- 10 files changed, 174 insertions(+), 196 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 0def9306..1103a7c4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,11 +6,11 @@ members = [ [package] name = "rhai" -version = "0.18.3" +version = "0.19.0" edition = "2018" authors = ["Jonathan Turner", "Lukáš Hozda", "Stephen Chung", "jhwgh1968"] description = "Embedded scripting for Rust" -homepage = "https://github.com/jonathandturner/rhai" +homepage = "https://schungx.github.io/rhai" repository = "https://github.com/jonathandturner/rhai" readme = "README.md" license = "MIT OR Apache-2.0" @@ -27,7 +27,6 @@ smallvec = { version = "1.4.2", default-features = false } rhai_codegen = { version = "0.1", path = "codegen" } [features] -#default = ["unchecked", "sync", "no_optimize", "no_float", "only_i32", "no_index", "no_object", "no_function", "no_module"] default = [] unchecked = [] # unchecked arithmetic sync = [] # restrict to only types that implement Send + Sync diff --git a/codegen/Cargo.toml b/codegen/Cargo.toml index 97a62e06..e8cc0ee5 100644 --- a/codegen/Cargo.toml +++ b/codegen/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2018" authors = ["jhwgh1968"] description = "Proceducral macro support package for Rhai, a scripting language for Rust" -homepage = "https://github.com/jonathandturner/rhai" +homepage = "https://schungx.github.io/rhai/plugins/index.html" repository = "https://github.com/jonathandturner/rhai" license = "MIT OR Apache-2.0" diff --git a/codegen/src/function.rs b/codegen/src/function.rs index 47427ee8..173bb0b2 100644 --- a/codegen/src/function.rs +++ b/codegen/src/function.rs @@ -628,7 +628,7 @@ impl ExportedFn { arg_type.span()=> &mut args[0usize].write_lock::<#arg_type>().unwrap()); unpack_stmts.push( syn::parse2::(quote! { - let #var: &mut _ = #downcast_span; + let #var = #downcast_span; }) .unwrap(), ); @@ -757,7 +757,7 @@ impl ExportedFn { } fn is_method_call(&self) -> bool { #is_method_call } - fn is_varadic(&self) -> bool { false } + fn is_variadic(&self) -> bool { false } fn clone_boxed(&self) -> Box { Box::new(#type_name()) } fn input_types(&self) -> Box<[TypeId]> { new_vec![#(#input_type_exprs),*].into_boxed_slice() diff --git a/codegen/src/lib.rs b/codegen/src/lib.rs index 5da8e1b4..abd92df5 100644 --- a/codegen/src/lib.rs +++ b/codegen/src/lib.rs @@ -1,17 +1,14 @@ +//! This crate contains procedural macros to make creating Rhai plugin-modules much easier. //! -//! This crate contains procedural macros to make creating Rhai modules much easier. -//! -//! # Exporting a Macro to Rhai +//! # Export an Entire Rust Module to a Rhai Module //! //! ``` //! use rhai::{EvalAltResult, FLOAT}; //! use rhai::plugin::*; //! use rhai::module_resolvers::*; //! -//! #[rhai::export_module] -//! pub mod advanced_math { -//! use rhai::FLOAT; -//! +//! #[export_module] +//! mod advanced_math { //! pub const MYSTIC_NUMBER: FLOAT = 42.0 as FLOAT; //! //! pub fn euclidean_distance(x1: FLOAT, y1: FLOAT, x2: FLOAT, y2: FLOAT) -> FLOAT { @@ -35,15 +32,15 @@ //! } //! ``` //! -//! # Exporting a Function to a Rhai Module +//! # Export a Rust Function to a Rhai Module //! //! ``` //! use rhai::{EvalAltResult, FLOAT, Module, RegisterFn}; //! use rhai::plugin::*; //! use rhai::module_resolvers::*; //! -//! #[rhai::export_fn] -//! pub fn distance_function(x1: FLOAT, y1: FLOAT, x2: FLOAT, y2: FLOAT) -> FLOAT { +//! #[export_fn] +//! fn distance_function(x1: FLOAT, y1: FLOAT, x2: FLOAT, y2: FLOAT) -> FLOAT { //! ((y2 - y1).abs().powf(2.0) + (x2 -x1).abs().powf(2.0)).sqrt() //! } //! @@ -105,6 +102,18 @@ mod rhai_module; #[cfg(test)] mod test; +/// Attribute, when put on a Rust function, turns it into a _plugin function_. +/// +/// # Usage +/// +/// ```no_run +/// use rhai::plugin::*; +/// +/// #[export_fn] +/// fn my_plugin_function(...) { +/// ... +/// } +/// ``` #[proc_macro_attribute] pub fn export_fn( args: proc_macro::TokenStream, @@ -125,6 +134,18 @@ pub fn export_fn( proc_macro::TokenStream::from(output) } +/// Attribute, when put on a Rust module, turns it into a _plugin module_. +/// +/// # Usage +/// +/// ```no_run +/// use rhai::plugin::*; +/// +/// #[export_module] +/// mod my_plugin_module { +/// ... +/// } +/// ``` #[proc_macro_attribute] pub fn export_module( args: proc_macro::TokenStream, @@ -143,6 +164,20 @@ pub fn export_module( proc_macro::TokenStream::from(tokens) } +/// Macro to generate a Rhai `Module` from a _plugin module_. +/// +/// # Usage +/// +/// ```no_run +/// use rhai::plugin::*; +/// +/// #[export_module] +/// mod my_plugin_module { +/// ... +/// } +/// +/// let module = exported_module!(my_plugin_module); +/// ``` #[proc_macro] pub fn exported_module(module_path: proc_macro::TokenStream) -> proc_macro::TokenStream { let module_path = parse_macro_input!(module_path as syn::Path); @@ -152,6 +187,34 @@ pub fn exported_module(module_path: proc_macro::TokenStream) -> proc_macro::Toke proc_macro::TokenStream::from(tokens) } +/// Macro to combine a _plugin module_ into an existing module. +/// +/// Functions and variables in the plugin module overrides any existing similarly-named +/// functions and variables in the target module. +/// +/// This call is intended to be used within the `def_package!` macro to define a custom +/// package based on a plugin module. +/// +/// All sub-modules, if any, in the plugin module are _flattened_ and their functions/variables +/// registered at the top level because packages require so. +/// +/// The text string name in the second parameter can be anything and is reserved for future use; +/// it is recommended to be an ID string that uniquely identifies the plugin module. +/// +/// # Usage +/// +/// ```no_run +/// use rhai::plugin::*; +/// +/// #[export_module] +/// mod my_plugin_module { +/// ... +/// } +/// +/// let mut module = Module::new(); +/// +/// combine_with_exported_module!(&mut module, "my_plugin_module_ID", my_plugin_module); +/// ``` #[proc_macro] pub fn combine_with_exported_module(args: proc_macro::TokenStream) -> proc_macro::TokenStream { let (module_expr, _export_name, module_path) = match crate::register::parse_register_macro(args) @@ -165,6 +228,22 @@ pub fn combine_with_exported_module(args: proc_macro::TokenStream) -> proc_macro proc_macro::TokenStream::from(tokens) } +/// Macro to register a _plugin function_ into an `Engine`. +/// +/// # Usage +/// +/// ```no_run +/// use rhai::plugin::*; +/// +/// #[export_fn] +/// fn my_plugin_function(...) { +/// ... +/// } +/// +/// let mut engine = Engine::new(); +/// +/// register_exported_fn!(engine, "calc", my_plugin_function); +/// ``` #[proc_macro] pub fn register_exported_fn(args: proc_macro::TokenStream) -> proc_macro::TokenStream { let (engine_expr, export_name, rust_modpath) = match crate::register::parse_register_macro(args) @@ -179,6 +258,22 @@ pub fn register_exported_fn(args: proc_macro::TokenStream) -> proc_macro::TokenS proc_macro::TokenStream::from(tokens) } +/// Macro to register a _plugin function_ into a Rhai `Module`. +/// +/// # Usage +/// +/// ```no_run +/// use rhai::plugin::*; +/// +/// #[export_fn] +/// fn my_plugin_function(...) { +/// ... +/// } +/// +/// let mut module = Module::new(); +/// +/// set_exported_fn!(module, "calc", my_plugin_function); +/// ``` #[proc_macro] pub fn set_exported_fn(args: proc_macro::TokenStream) -> proc_macro::TokenStream { let (module_expr, export_name, rust_modpath) = match crate::register::parse_register_macro(args) diff --git a/codegen/src/test/function.rs b/codegen/src/test/function.rs index 2641417a..f028c0b5 100644 --- a/codegen/src/test/function.rs +++ b/codegen/src/test/function.rs @@ -283,7 +283,7 @@ mod generate_tests { } fn is_method_call(&self) -> bool { false } - fn is_varadic(&self) -> bool { false } + fn is_variadic(&self) -> bool { false } fn clone_boxed(&self) -> Box { Box::new(Token()) } fn input_types(&self) -> Box<[TypeId]> { new_vec![].into_boxed_slice() @@ -328,7 +328,7 @@ mod generate_tests { } fn is_method_call(&self) -> bool { false } - fn is_varadic(&self) -> bool { false } + fn is_variadic(&self) -> bool { false } fn clone_boxed(&self) -> Box { Box::new(Token()) } fn input_types(&self) -> Box<[TypeId]> { new_vec![TypeId::of::()].into_boxed_slice() @@ -369,7 +369,7 @@ mod generate_tests { } fn is_method_call(&self) -> bool { false } - fn is_varadic(&self) -> bool { false } + fn is_variadic(&self) -> bool { false } fn clone_boxed(&self) -> Box { Box::new(MyType()) } fn input_types(&self) -> Box<[TypeId]> { new_vec![TypeId::of::()].into_boxed_slice() @@ -404,7 +404,7 @@ mod generate_tests { } fn is_method_call(&self) -> bool { false } - fn is_varadic(&self) -> bool { false } + fn is_variadic(&self) -> bool { false } fn clone_boxed(&self) -> Box { Box::new(Token()) } fn input_types(&self) -> Box<[TypeId]> { new_vec![TypeId::of::(), @@ -446,12 +446,12 @@ mod generate_tests { debug_assert_eq!(args.len(), 2usize, "wrong arg count: {} != {}", args.len(), 2usize); let arg1 = mem::take(args[1usize]).cast::(); - let arg0: &mut _ = &mut args[0usize].write_lock::().unwrap(); + let arg0 = &mut args[0usize].write_lock::().unwrap(); Ok(Dynamic::from(increment(arg0, arg1))) } fn is_method_call(&self) -> bool { true } - fn is_varadic(&self) -> bool { false } + fn is_variadic(&self) -> bool { false } fn clone_boxed(&self) -> Box { Box::new(Token()) } fn input_types(&self) -> Box<[TypeId]> { new_vec![TypeId::of::(), @@ -498,7 +498,7 @@ mod generate_tests { } fn is_method_call(&self) -> bool { false } - fn is_varadic(&self) -> bool { false } + fn is_variadic(&self) -> bool { false } fn clone_boxed(&self) -> Box { Box::new(Token()) } fn input_types(&self) -> Box<[TypeId]> { new_vec![TypeId::of::()].into_boxed_slice() diff --git a/codegen/src/test/module.rs b/codegen/src/test/module.rs index dbb2d023..fac3becd 100644 --- a/codegen/src/test/module.rs +++ b/codegen/src/test/module.rs @@ -311,7 +311,7 @@ mod generate_tests { } fn is_method_call(&self) -> bool { false } - fn is_varadic(&self) -> bool { false } + fn is_variadic(&self) -> bool { false } fn clone_boxed(&self) -> Box { Box::new(get_mystic_number_token()) } @@ -374,7 +374,7 @@ mod generate_tests { } fn is_method_call(&self) -> bool { false } - fn is_varadic(&self) -> bool { false } + fn is_variadic(&self) -> bool { false } fn clone_boxed(&self) -> Box { Box::new(add_one_to_token()) } @@ -451,7 +451,7 @@ mod generate_tests { } fn is_method_call(&self) -> bool { false } - fn is_varadic(&self) -> bool { false } + fn is_variadic(&self) -> bool { false } fn clone_boxed(&self) -> Box { Box::new(add_one_to_token()) } @@ -480,7 +480,7 @@ mod generate_tests { } fn is_method_call(&self) -> bool { false } - fn is_varadic(&self) -> bool { false } + fn is_variadic(&self) -> bool { false } fn clone_boxed(&self) -> Box { Box::new(add_n_to_token()) } @@ -546,7 +546,7 @@ mod generate_tests { } fn is_method_call(&self) -> bool { false } - fn is_varadic(&self) -> bool { false } + fn is_variadic(&self) -> bool { false } fn clone_boxed(&self) -> Box { Box::new(add_together_token()) } @@ -619,7 +619,7 @@ mod generate_tests { } fn is_method_call(&self) -> bool { false } - fn is_varadic(&self) -> bool { false } + fn is_variadic(&self) -> bool { false } fn clone_boxed(&self) -> Box { Box::new(add_together_token()) } @@ -859,7 +859,7 @@ mod generate_tests { } fn is_method_call(&self) -> bool { false } - fn is_varadic(&self) -> bool { false } + fn is_variadic(&self) -> bool { false } fn clone_boxed(&self) -> Box { Box::new(get_mystic_number_token()) } @@ -953,7 +953,7 @@ mod generate_tests { } fn is_method_call(&self) -> bool { false } - fn is_varadic(&self) -> bool { false } + fn is_variadic(&self) -> bool { false } fn clone_boxed(&self) -> Box { Box::new(print_out_to_token()) } @@ -1017,7 +1017,7 @@ mod generate_tests { } fn is_method_call(&self) -> bool { false } - fn is_varadic(&self) -> bool { false } + fn is_variadic(&self) -> bool { false } fn clone_boxed(&self) -> Box { Box::new(print_out_to_token()) } @@ -1076,12 +1076,12 @@ mod generate_tests { ) -> Result> { debug_assert_eq!(args.len(), 1usize, "wrong arg count: {} != {}", args.len(), 1usize); - let arg0: &mut _ = &mut args[0usize].write_lock::().unwrap(); + let arg0 = &mut args[0usize].write_lock::().unwrap(); Ok(Dynamic::from(increment(arg0))) } fn is_method_call(&self) -> bool { true } - fn is_varadic(&self) -> bool { false } + fn is_variadic(&self) -> bool { false } fn clone_boxed(&self) -> Box { Box::new(increment_token()) } @@ -1143,12 +1143,12 @@ mod generate_tests { ) -> Result> { debug_assert_eq!(args.len(), 1usize, "wrong arg count: {} != {}", args.len(), 1usize); - let arg0: &mut _ = &mut args[0usize].write_lock::().unwrap(); + let arg0 = &mut args[0usize].write_lock::().unwrap(); Ok(Dynamic::from(increment(arg0))) } fn is_method_call(&self) -> bool { true } - fn is_varadic(&self) -> bool { false } + fn is_variadic(&self) -> bool { false } fn clone_boxed(&self) -> Box { Box::new(increment_token()) } @@ -1230,12 +1230,12 @@ mod generate_tests { ) -> Result> { debug_assert_eq!(args.len(), 1usize, "wrong arg count: {} != {}", args.len(), 1usize); - let arg0: &mut _ = &mut args[0usize].write_lock::().unwrap(); + let arg0 = &mut args[0usize].write_lock::().unwrap(); Ok(Dynamic::from(increment(arg0))) } fn is_method_call(&self) -> bool { true } - fn is_varadic(&self) -> bool { false } + fn is_variadic(&self) -> bool { false } fn clone_boxed(&self) -> Box { Box::new(increment_token()) } @@ -1315,12 +1315,12 @@ mod generate_tests { ) -> Result> { debug_assert_eq!(args.len(), 1usize, "wrong arg count: {} != {}", args.len(), 1usize); - let arg0: &mut _ = &mut args[0usize].write_lock::().unwrap(); + let arg0 = &mut args[0usize].write_lock::().unwrap(); Ok(Dynamic::from(int_foo(arg0))) } fn is_method_call(&self) -> bool { true } - fn is_varadic(&self) -> bool { false } + fn is_variadic(&self) -> bool { false } fn clone_boxed(&self) -> Box { Box::new(int_foo_token()) } @@ -1381,12 +1381,12 @@ mod generate_tests { ) -> Result> { debug_assert_eq!(args.len(), 1usize, "wrong arg count: {} != {}", args.len(), 1usize); - let arg0: &mut _ = &mut args[0usize].write_lock::().unwrap(); + let arg0 = &mut args[0usize].write_lock::().unwrap(); Ok(Dynamic::from(int_foo(arg0))) } fn is_method_call(&self) -> bool { true } - fn is_varadic(&self) -> bool { false } + fn is_variadic(&self) -> bool { false } fn clone_boxed(&self) -> Box { Box::new(int_foo_token()) } @@ -1448,12 +1448,12 @@ mod generate_tests { debug_assert_eq!(args.len(), 2usize, "wrong arg count: {} != {}", args.len(), 2usize); let arg1 = mem::take(args[1usize]).cast::(); - let arg0: &mut _ = &mut args[0usize].write_lock::().unwrap(); + let arg0 = &mut args[0usize].write_lock::().unwrap(); Ok(Dynamic::from(int_foo(arg0, arg1))) } fn is_method_call(&self) -> bool { true } - fn is_varadic(&self) -> bool { false } + fn is_variadic(&self) -> bool { false } fn clone_boxed(&self) -> Box { Box::new(int_foo_token()) } @@ -1519,12 +1519,12 @@ mod generate_tests { debug_assert_eq!(args.len(), 2usize, "wrong arg count: {} != {}", args.len(), 2usize); let arg1 = mem::take(args[1usize]).cast::(); - let arg0: &mut _ = &mut args[0usize].write_lock::().unwrap(); + let arg0 = &mut args[0usize].write_lock::().unwrap(); Ok(Dynamic::from(int_foo(arg0, arg1))) } fn is_method_call(&self) -> bool { true } - fn is_varadic(&self) -> bool { false } + fn is_variadic(&self) -> bool { false } fn clone_boxed(&self) -> Box { Box::new(int_foo_token()) } @@ -1586,12 +1586,12 @@ mod generate_tests { debug_assert_eq!(args.len(), 2usize, "wrong arg count: {} != {}", args.len(), 2usize); let arg1 = mem::take(args[1usize]).cast::(); - let arg0: &mut _ = &mut args[0usize].write_lock::().unwrap(); + let arg0 = &mut args[0usize].write_lock::().unwrap(); Ok(Dynamic::from(get_by_index(arg0, arg1))) } fn is_method_call(&self) -> bool { true } - fn is_varadic(&self) -> bool { false } + fn is_variadic(&self) -> bool { false } fn clone_boxed(&self) -> Box { Box::new(get_by_index_token()) } @@ -1658,12 +1658,12 @@ mod generate_tests { debug_assert_eq!(args.len(), 2usize, "wrong arg count: {} != {}", args.len(), 2usize); let arg1 = mem::take(args[1usize]).cast::(); - let arg0: &mut _ = &mut args[0usize].write_lock::().unwrap(); + let arg0 = &mut args[0usize].write_lock::().unwrap(); Ok(Dynamic::from(get_by_index(arg0, arg1))) } fn is_method_call(&self) -> bool { true } - fn is_varadic(&self) -> bool { false } + fn is_variadic(&self) -> bool { false } fn clone_boxed(&self) -> Box { Box::new(get_by_index_token()) } @@ -1728,12 +1728,12 @@ mod generate_tests { "wrong arg count: {} != {}", args.len(), 3usize); let arg1 = mem::take(args[1usize]).cast::(); let arg2 = mem::take(args[2usize]).cast::(); - let arg0: &mut _ = &mut args[0usize].write_lock::().unwrap(); + let arg0 = &mut args[0usize].write_lock::().unwrap(); Ok(Dynamic::from(set_by_index(arg0, arg1, arg2))) } fn is_method_call(&self) -> bool { true } - fn is_varadic(&self) -> bool { false } + fn is_variadic(&self) -> bool { false } fn clone_boxed(&self) -> Box { Box::new(set_by_index_token()) } @@ -1804,12 +1804,12 @@ mod generate_tests { "wrong arg count: {} != {}", args.len(), 3usize); let arg1 = mem::take(args[1usize]).cast::(); let arg2 = mem::take(args[2usize]).cast::(); - let arg0: &mut _ = &mut args[0usize].write_lock::().unwrap(); + let arg0 = &mut args[0usize].write_lock::().unwrap(); Ok(Dynamic::from(set_by_index(arg0, arg1, arg2))) } fn is_method_call(&self) -> bool { true } - fn is_varadic(&self) -> bool { false } + fn is_variadic(&self) -> bool { false } fn clone_boxed(&self) -> Box { Box::new(set_by_index_token()) } diff --git a/src/fn_native.rs b/src/fn_native.rs index 390fa340..cf2ae263 100644 --- a/src/fn_native.rs +++ b/src/fn_native.rs @@ -221,7 +221,7 @@ pub enum CallableFunction { Method(Shared), /// An iterator function. Iterator(IteratorFn), - /// A plugin-defined function, + /// A plugin function, Plugin(Shared), /// A script-defined function. #[cfg(not(feature = "no_function"))] @@ -300,7 +300,7 @@ impl CallableFunction { Self::Pure(_) | Self::Method(_) | Self::Iterator(_) | Self::Plugin(_) => false, } } - /// Is this a plugin-defined function? + /// Is this a plugin function? pub fn is_plugin_fn(&self) -> bool { match self { Self::Plugin(_) => true, diff --git a/src/fn_register.rs b/src/fn_register.rs index b6b7b896..5c519d4c 100644 --- a/src/fn_register.rs +++ b/src/fn_register.rs @@ -7,118 +7,12 @@ use crate::engine::Engine; use crate::fn_native::{CallableFunction, FnAny, FnCallArgs, SendSync}; use crate::module::Module; use crate::parser::FnAccess; -use crate::plugin::Plugin; use crate::r#unsafe::unsafe_cast_box; use crate::result::EvalAltResult; use crate::utils::ImmutableString; use crate::stdlib::{any::TypeId, boxed::Box, mem, string::String}; -/// 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 - /// - /// ``` - /// # #[cfg(not(feature = "no_float"))] - /// use rhai::FLOAT as NUMBER; - /// # #[cfg(feature = "no_float")] - /// use rhai::INT as NUMBER; - /// # #[cfg(not(feature = "no_module"))] - /// use rhai::{Module, ModuleResolver, RegisterFn, RegisterPlugin}; - /// # #[cfg(not(feature = "no_module"))] - /// use rhai::plugin::*; - /// # #[cfg(not(feature = "no_module"))] - /// use rhai::module_resolvers::*; - /// - /// // A function we want to expose to Rhai. - /// #[derive(Copy, Clone)] - /// struct DistanceFunction(); - /// - /// # #[cfg(not(feature = "no_module"))] - /// impl PluginFunction for DistanceFunction { - /// fn is_method_call(&self) -> bool { false } - /// fn is_varadic(&self) -> bool { false } - /// - /// fn call(&self, args: &mut[&mut Dynamic]) -> Result> { - /// let x1: NUMBER = std::mem::take(args[0]).clone().cast::(); - /// let y1: NUMBER = std::mem::take(args[1]).clone().cast::(); - /// let x2: NUMBER = std::mem::take(args[2]).clone().cast::(); - /// let y2: NUMBER = std::mem::take(args[3]).clone().cast::(); - /// # #[cfg(not(feature = "no_float"))] - /// let square_sum = (y2 - y1).abs().powf(2.0) + (x2 -x1).abs().powf(2.0); - /// # #[cfg(feature = "no_float")] - /// let square_sum = (y2 - y1).abs().pow(2) + (x2 -x1).abs().pow(2); - /// Ok(Dynamic::from(square_sum)) - /// } - /// - /// fn clone_boxed(&self) -> Box { - /// Box::new(DistanceFunction()) - /// } - /// - /// fn input_types(&self) -> Box<[std::any::TypeId]> { - /// vec![std::any::TypeId::of::(), - /// std::any::TypeId::of::(), - /// std::any::TypeId::of::(), - /// std::any::TypeId::of::()].into_boxed_slice() - /// } - /// } - /// - /// // A simple custom plugin. This should not usually be done with hand-written code. - /// #[derive(Copy, Clone)] - /// pub struct AdvancedMathPlugin(); - /// - /// # #[cfg(not(feature = "no_module"))] - /// 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 NUMBER }); - /// - /// // 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::()], - /// DistanceFunction().into()); - /// let mut r = StaticModuleResolver::new(); - /// r.insert("Math::Advanced".to_string(), m); - /// engine.set_module_resolver(Some(r)); - /// } - /// } - /// - /// - /// # fn main() -> Result<(), Box> { - /// - /// # #[cfg(not(feature = "no_module"))] { - /// let mut engine = Engine::new(); - /// engine.register_plugin(AdvancedMathPlugin()); - /// - /// # #[cfg(feature = "no_float")] - /// assert_eq!(engine.eval::( - /// r#"import "Math::Advanced" as math; - /// let x = math::euclidean_distance(0, 1, 0, get_mystic_number()); x"#)?, 1681); - /// # #[cfg(not(feature = "no_float"))] - /// 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"#)?, 1681.0); - /// # } // end cfg - /// # 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`. @@ -223,12 +117,6 @@ 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 => $let:stmt => $convert:expr => $arg:expr),*) => { diff --git a/src/lib.rs b/src/lib.rs index e88cf7e2..13536be4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -86,7 +86,7 @@ pub use any::Dynamic; pub use engine::Engine; pub use error::{ParseError, ParseErrorType}; pub use fn_native::{FnPtr, IteratorFn}; -pub use fn_register::{RegisterFn, RegisterPlugin, RegisterResultFn}; +pub use fn_register::{RegisterFn, RegisterResultFn}; pub use module::Module; pub use parser::{ImmutableString, AST, INT}; pub use result::EvalAltResult; diff --git a/src/plugin.rs b/src/plugin.rs index e6e4e956..f82056ea 100644 --- a/src/plugin.rs +++ b/src/plugin.rs @@ -1,42 +1,38 @@ -//! Module defining plugins in Rhai for use by plugin authors. +//! Module defining macros for developing _plugins_. -pub use crate::{ - fn_native::CallableFunction, stdlib::any::TypeId, stdlib::boxed::Box, stdlib::format, - stdlib::mem, stdlib::string::ToString, stdlib::vec as new_vec, stdlib::vec::Vec, Dynamic, - Engine, EvalAltResult, FnAccess, ImmutableString, Module, RegisterResultFn, -}; +pub use crate::any::Dynamic; +pub use crate::engine::Engine; +pub use crate::fn_native::CallableFunction; +pub use crate::fn_register::{RegisterFn, RegisterResultFn}; +pub use crate::module::Module; +pub use crate::parser::FnAccess; +pub use crate::result::EvalAltResult; +pub use crate::utils::ImmutableString; + +pub use crate::stdlib::{any::TypeId, boxed::Box, format, mem, vec as new_vec}; #[cfg(not(features = "no_module"))] pub use rhai_codegen::*; #[cfg(features = "no_module")] pub use rhai_codegen::{export_fn, register_exported_fn}; -#[cfg(features = "sync")] -/// Represents an externally-written plugin for the Rhai interpreter. +/// Trait implemented by a _plugin function_. +/// This trait should not be used directly. /// -/// 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. +/// Use the `#[export_module]` and `#[export_fn]` procedural attributes instead. pub trait PluginFunction { - fn is_method_call(&self) -> bool; - fn is_varadic(&self) -> bool; - + /// Call the plugin function with the arguments provided. fn call(&self, args: &mut [&mut Dynamic]) -> Result>; + /// Is this plugin function a method? + fn is_method_call(&self) -> bool; + + /// Is this plugin function variadic? + fn is_variadic(&self) -> bool; + + /// Convert a plugin function into a boxed trait object. fn clone_boxed(&self) -> Box; + /// Return a boxed slice of type ID's of the function's parameters. fn input_types(&self) -> Box<[TypeId]>; } From cbc3d8764a2a698ce4c785064a61065284cb8276 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Wed, 30 Sep 2020 23:02:01 +0800 Subject: [PATCH 6/9] Revise docs for 0.19.0. --- doc/src/about/features.md | 4 +- doc/src/about/index.md | 26 ++++++++ doc/src/about/license.md | 6 +- doc/src/about/non-design.md | 31 ++++++--- doc/src/about/related.md | 4 +- doc/src/about/targets.md | 6 ++ doc/src/advanced.md | 4 +- rhai_logo.png => doc/src/images/rhai_logo.png | Bin doc/src/language/fn-capture.md | 4 +- doc/src/language/fn-namespaces.md | 8 +-- doc/src/language/fn-ptr.md | 11 ++-- doc/src/language/for.md | 41 ++++++++++-- doc/src/language/functions.md | 59 +++++++++++------- doc/src/language/method.md | 42 ++++++++++--- doc/src/language/strings-chars.md | 10 ++- doc/src/language/throw.md | 5 +- doc/src/patterns/config.md | 13 +++- doc/src/patterns/control.md | 15 +++-- doc/src/patterns/singleton.md | 25 ++++---- doc/src/plugins/function.md | 4 +- doc/src/plugins/index.md | 2 +- doc/src/plugins/module.md | 31 +++++---- doc/src/rust/disable-custom.md | 8 +++ doc/src/rust/getters-setters.md | 2 +- doc/src/rust/indexers.md | 2 +- doc/src/rust/operators.md | 13 +++- doc/src/rust/packages/create.md | 6 +- doc/src/rust/packages/plugin.md | 3 +- doc/src/rust/register-raw.md | 18 +++--- doc/src/rust/traits.md | 13 ++-- 30 files changed, 291 insertions(+), 125 deletions(-) rename rhai_logo.png => doc/src/images/rhai_logo.png (100%) diff --git a/doc/src/about/features.md b/doc/src/about/features.md index bcfe5a6e..2cef773b 100644 --- a/doc/src/about/features.md +++ b/doc/src/about/features.md @@ -14,8 +14,8 @@ Easy * Easily [call a script-defined function]({{rootUrl}}/engine/call-fn.md) from Rust. -* Very few additional dependencies (right now only [`smallvec`](https://crates.io/crates/smallvec/)); - for [`no-std`] builds, a number of additional dependencies are pulled in to provide for functionalities that used to be in `std`. +* Very few additional dependencies - right now only [`smallvec`](https://crates.io/crates/smallvec/) plus crates for procedural macros; + for [`no-std`] and `WASM` builds, a number of additional dependencies are pulled in to provide for missing functionalities. Fast ---- diff --git a/doc/src/about/index.md b/doc/src/about/index.md index cf021da5..2c6efcc8 100644 --- a/doc/src/about/index.md +++ b/doc/src/about/index.md @@ -3,6 +3,8 @@ What is Rhai {{#include ../links.md}} +![Rhai Logo](../images/rhai_logo.png) + Rhai is an embedded scripting language and evaluation engine for Rust that gives a safe and easy way to add scripting to any application. @@ -13,3 +15,27 @@ Versions This Book is for version **{{version}}** of Rhai. For the latest development version, see [here]({{rootUrl}}/vnext/). + + +Etymology of the name "Rhai" +--------------------------- + +### As per Rhai's author Johnathan Turner + +In the beginning there was [ChaiScript](http://chaiscript.com), +which is an embedded scripting language for C++. +Originally it was intended to be a scripting language similar to **JavaScript**. + +With java being a kind of hot beverage, the new language was named after +another hot beverage - **Chai**, which is the word for "tea" in many world languages +and, in particular, a popular kind of milk tea consumed in India. + +Later, when the novel implementation technique behind ChaiScript was ported from C++ to Rust, +logically the `C` was changed to an `R` to make it "RhaiScript", or just "Rhai". + +### On the origin of the temporary Rhai logo + +One of Rhai's maintainers, Stephen Chung, was thinking about a logo when he accidentally +came across a copy of _Catcher in the Rye_ in a restaurant. The rest was history. + +It is temporary until it becomes official, that is... diff --git a/doc/src/about/license.md b/doc/src/about/license.md index a1f24339..8465ec4c 100644 --- a/doc/src/about/license.md +++ b/doc/src/about/license.md @@ -3,13 +3,11 @@ Licensing {{#include ../links.md}} -Rhai is licensed under either: +Rhai is licensed under either of the following, at your choice: * [Apache License, Version 2.0]({{repoHome}}/LICENSE-APACHE.txt), or -* [MIT license]({{repoHome}}/LICENSE-MIT.txt) - -at your choice. +* [MIT license]({{repoHome}}/LICENSE-MIT.txt). Unless explicitly stated otherwise, any contribution intentionally submitted for inclusion in this crate, as defined in the Apache-2.0 license, shall be dual-licensed as above, diff --git a/doc/src/about/non-design.md b/doc/src/about/non-design.md index 7f40614e..720a7c12 100644 --- a/doc/src/about/non-design.md +++ b/doc/src/about/non-design.md @@ -14,7 +14,7 @@ It doesn't attempt to be a new language. For example: There is, however, a built-in [object map] type which is adequate for most uses. It is possible to simulate [object-oriented programming (OOP)][OOP] by storing [function pointers] - in [object map] properties, turning them into _methods_. + or [closures] in [object map] properties, turning them into _methods_. * No first-class functions - Code your functions in Rust instead, and register them with Rhai. @@ -22,22 +22,32 @@ It doesn't attempt to be a new language. For example: * No garbage collection - this should be expected, so... -* No closures - do your closure magic in Rust instead; [turn a Rhai scripted function into a Rust closure]({{rootUrl}}/engine/call-fn.md). +* No first-class closures - do your closure magic in Rust instead: [turn a Rhai scripted function into a Rust closure]({{rootUrl}}/engine/call-fn.md). - But you can [curry][currying] a [function pointer] with arguments to simulate it somewhat. + There is, however, support for simulated [closures] via [currying] a [function pointer] with + captured shared variables. -* No byte-codes/JIT - Rhai has an AST-walking interpreter which will not win any speed races. The purpose of Rhai is not - to be extremely _fast_, but to make it as easy as possible to integrate with native Rust applications. +* No byte-codes/JIT - Rhai has an AST-walking interpreter which will not win any speed races. + The purpose of Rhai is not to be extremely _fast_, but to make it as easy as possible to + integrate with native Rust applications. + +* No formal language grammar - Rhai uses a hand-coded lexer, a hand-coded top-down recursive-descent parser + for statements and a Pratt parser for expressions. + + This lack of formalism allows the parser itself to be exposed as a service in order to support + [disabling keywords/operators][disable keywords and operators], adding [custom operators], + and defining [custom syntax]. Do Not Write The Next 4D VR Game in Rhai --------------------------------------- -Due to this intended usage, Rhai deliberately keeps the language simple and small by omitting advanced language features -such as classes, inheritance, first-class functions, closures, concurrency, byte-codes, JIT etc. +Due to this intended usage, Rhai deliberately keeps the language simple and small by omitting +advanced language features such as classes, inheritance, first-class functions, closures, +concurrency, byte-codes VM, JIT etc. -Avoid the temptation to write full-fledge application logic entirely in Rhai - that use case is best fulfilled by -more complete languages such as JavaScript or Lua. +Avoid the temptation to write full-fledge application logic entirely in Rhai - +that use case is best fulfilled by more complete languages such as JavaScript or Lua. Thin Dynamic Wrapper Layer Over Rust Code @@ -47,7 +57,8 @@ In actual practice, it is usually best to expose a Rust API into Rhai for script All the core functionalities should be written in Rust, with Rhai being the dynamic _control_ layer. -This is similar to some dynamic languages where most of the core functionalities reside in a C/C++ standard library. +This is similar to some dynamic languages where most of the core functionalities reside in a C/C++ +standard library. Another similar scenario is a web front-end driving back-end services written in a systems language. In this case, JavaScript takes the role of Rhai while the back-end language, well... it can actually also be Rust. diff --git a/doc/src/about/related.md b/doc/src/about/related.md index defc29e4..8174676b 100644 --- a/doc/src/about/related.md +++ b/doc/src/about/related.md @@ -5,7 +5,7 @@ Related Resources Other online documentation resources for Rhai: -* [`crates.io`](https://crates.io/crates/rhai/) - Rhai crate +* [`crates.io`](https://crates.io/crates/rhai) - Rhai crate * [`DOCS.RS`](https://docs.rs/rhai) - Rhai API documentation @@ -15,6 +15,6 @@ Other online documentation resources for Rhai: Other cool projects to check out: -* [ChaiScript](http://chaiscript.com/) - A strong inspiration for Rhai. An embedded scripting language for C++ that I helped created many moons ago, now being led by my cousin. +* [ChaiScript](http://chaiscript.com) - A strong inspiration for Rhai. An embedded scripting language for C++. * Check out the list of [scripting languages for Rust](https://github.com/rust-unofficial/awesome-rust#scripting) on [awesome-rust](https://github.com/rust-unofficial/awesome-rust) diff --git a/doc/src/about/targets.md b/doc/src/about/targets.md index 70124e61..a68430a0 100644 --- a/doc/src/about/targets.md +++ b/doc/src/about/targets.md @@ -10,3 +10,9 @@ The following targets and builds are support by Rhai: * WebAssembly ([WASM]) * [`no-std`] + + +Minimum Rust Version +-------------------- + +The minimum version of Rust required to compile Rhai is `1.45.0`. diff --git a/doc/src/advanced.md b/doc/src/advanced.md index 0b9e170e..482ae621 100644 --- a/doc/src/advanced.md +++ b/doc/src/advanced.md @@ -13,8 +13,6 @@ This section covers advanced features such as: * [Script optimization]. -* [Domain-Specific Languages][DSL]. - * Low-level [function registration API]({{rootUrl}}/rust/register-raw.md) -* The dreaded (or beloved for those with twisted tastes) [`eval`] statement. +* [Domain-Specific Languages][DSL]. diff --git a/rhai_logo.png b/doc/src/images/rhai_logo.png similarity index 100% rename from rhai_logo.png rename to doc/src/images/rhai_logo.png diff --git a/doc/src/language/fn-capture.md b/doc/src/language/fn-capture.md index 08052826..58ce811a 100644 --- a/doc/src/language/fn-capture.md +++ b/doc/src/language/fn-capture.md @@ -49,8 +49,8 @@ f.call!(41); // <- syntax error: capturing is not allowed in method-c No Mutations ------------ -Variables in the calling scope are captured as copies. -Changes to them do not reflect back to the calling scope. +Variables in the calling scope are captured as cloned copies. +Changes to them do **not** reflect back to the calling scope. Rhai functions remain _pure_ in the sense that they can never mutate their environment. diff --git a/doc/src/language/fn-namespaces.md b/doc/src/language/fn-namespaces.md index e6844f9a..11ce1107 100644 --- a/doc/src/language/fn-namespaces.md +++ b/doc/src/language/fn-namespaces.md @@ -15,10 +15,10 @@ forming a new, combined, group of functions. In general, there are two types of _namespaces_ where functions are looked up: -| Namespace | Source | Lookup method | How Many | -| --------- | ---------------------------------------------------------------------- | --------------------------------- | :----------------------: | -| Global | `Engine::register_XXX` API, [`AST`] being evaluated, [packages] loaded | simple function name | one | -| Module | [`Module`] | namespace-qualified function name | as many as [`import`]-ed | +| Namespace | Source | Lookup method | Sub-modules? | Variables? | +| --------- | ------------------------------------------------------------------------------------- | ------------------------------ | :----------: | :--------: | +| Global | 1) `Engine::register_XXX` API
2) [`AST`] being evaluated
3) [packages] loaded | simple function name | ignored | ignored | +| Module | [`Module`] | module-qualified function name | yes | yes | Global Namespace diff --git a/doc/src/language/fn-ptr.md b/doc/src/language/fn-ptr.md index 8c294a83..e377e7e6 100644 --- a/doc/src/language/fn-ptr.md +++ b/doc/src/language/fn-ptr.md @@ -15,9 +15,10 @@ Built-in methods The following standard methods (mostly defined in the [`BasicFnPackage`][packages] but excluded if using a [raw `Engine`]) operate on [strings]: -| Function | Parameter(s) | Description | -| -------------------------- | ------------ | --------------------------------------------------------------------- | -| `name` method and property | _none_ | returns the name of the function encapsulated by the function pointer | +| Function | Parameter(s) | Description | +| -------------------------- | ------------ | ---------------------------------------------------------------------------- | +| `name` method and property | _none_ | returns the name of the function encapsulated by the function pointer | +| `call` | _arguments_ | calls the function matching the function pointer's name with the _arguments_ | Examples @@ -143,7 +144,9 @@ to a function call while binding the object in the method call to the `this` poi To achieve this, pass the `FnPtr` value as the _first_ argument to `call`: ```rust -fn add(x) { this += x; } // define function which uses 'this' +fn add(x) { // define function which uses 'this' + this += x; +} let func = Fn("add"); // function pointer to 'add' diff --git a/doc/src/language/for.md b/doc/src/language/for.md index 493d8759..c60372a8 100644 --- a/doc/src/language/for.md +++ b/doc/src/language/for.md @@ -12,8 +12,13 @@ Like C, `continue` can be used to skip to the next iteration, by-passing all fol To loop through a number sequence (with or without steps), use the `range` function to return a numeric iterator. + +Iterate Through Strings +----------------------- + +Iterating through a [string] yields characters. + ```rust -// Iterate through string, yielding characters let s = "hello, world!"; for ch in s { @@ -23,8 +28,15 @@ for ch in s { if x == '@' { break; } // break out of for loop } +``` -// Iterate through array + +Iterate Through Arrays +---------------------- + +Iterating through an [array] yields cloned _copies_ of each element. + +```rust let array = [1, 3, 5, 7, 9, 42]; for x in array { @@ -34,8 +46,17 @@ for x in array { if x == 42 { break; } // break out of for loop } +``` -// The 'range' function allows iterating from first to last-1 + +Iterate Through Numeric Ranges +----------------------------- + +The `range` function allows iterating through a range of numbers +(not including the last number). + +```rust +// Iterate starting from 0 and stopping at 49. for x in range(0, 50) { if x > 10 { continue; } // skip to the next iteration @@ -44,7 +65,7 @@ for x in range(0, 50) { if x == 42 { break; } // break out of for loop } -// The 'range' function also takes a step +// The 'range' function also takes a step. for x in range(0, 50, 3) { // step by 3 if x > 10 { continue; } // skip to the next iteration @@ -52,8 +73,18 @@ for x in range(0, 50, 3) { // step by 3 if x == 42 { break; } // break out of for loop } +``` -// Iterate through object map + +Iterate Through Object Maps +-------------------------- + +Two functions, `keys` and `values`, return [arrays] containing cloned _copies_ +of all property names and values of an [object map], respectively. + +These [arrays] can be iterated. + +```rust let map = #{a:1, b:3, c:5, d:7, e:9}; // Property names are returned in unsorted, random order diff --git a/doc/src/language/functions.md b/doc/src/language/functions.md index 3bd15cd0..5b9498a6 100644 --- a/doc/src/language/functions.md +++ b/doc/src/language/functions.md @@ -42,19 +42,6 @@ add2(42) == 44; ``` -No Access to External Scope --------------------------- - -Functions are not _closures_. They do not capture the calling environment and can only access their own parameters. -They cannot access variables external to the function itself. - -```rust -let x = 42; - -fn foo() { x } // <- syntax error: variable 'x' doesn't exist -``` - - Global Definitions Only ---------------------- @@ -77,24 +64,52 @@ fn do_addition(x) { ``` -Use Before Definition --------------------- +No Access to External Scope +-------------------------- + +Functions are not _closures_. They do not capture the calling environment +and can only access their own parameters. +They cannot access variables external to the function itself. + +```rust +let x = 42; + +fn foo() { x } // <- syntax error: variable 'x' doesn't exist +``` + + +But Can Call Other Functions +--------------------------- + +All functions in the same [`AST`] can call each other. + +```rust +fn foo(x) { x + 1 } // function defined in the global namespace + +fn bar(x) { foo(x) } // OK! function 'foo' can be called +``` + + +Use Before Definition Allowed +---------------------------- Unlike C/C++, functions in Rhai can be defined _anywhere_ at global level. + A function does not need to be defined prior to being used in a script; a statement in the script can freely call a function defined afterwards. This is similar to Rust and many other modern languages, such as JavaScript's `function` keyword. -Arguments Passed by Value ------------------------- +Arguments are Passed by Value +---------------------------- -Functions defined in script always take [`Dynamic`] parameters (i.e. the parameter can be of any type). +Functions defined in script always take [`Dynamic`] parameters (i.e. they can be of any types). Therefore, functions with the same name and same _number_ of parameters are equivalent. -It is important to remember that all arguments are passed by _value_, so all Rhai script-defined functions -are _pure_ (i.e. they never modify their arguments). +All arguments are passed by _value_, so all Rhai script-defined functions are _pure_ +(i.e. they never modify their arguments). + Any update to an argument will **not** be reflected back to the caller. ```rust @@ -113,8 +128,8 @@ x == 500; // 'x' is NOT changed! `this` - Simulating an Object Method ----------------------------------- -Functions can also be called in method-call style. When this is the case, the keyword '`this`' -binds to the object in the method call and can be changed. +Script-defined functions can also be called in method-call style. +When this happens, the keyword '`this`' binds to the object in the method call and can be changed. ```rust fn change() { // not that the object does not need a parameter diff --git a/doc/src/language/method.md b/doc/src/language/method.md index 01633350..6ca91a99 100644 --- a/doc/src/language/method.md +++ b/doc/src/language/method.md @@ -39,8 +39,8 @@ array[0].update(); // <- call in method-call style will update 'a' **IMPORTANT: Rhai does NOT support normal references (i.e. `&T`) as parameters.** -Number of Parameters --------------------- +Number of Parameters in Methods +------------------------------ Native Rust methods registered with an [`Engine`] take _one additional parameter_ more than an equivalent method coded in script, where the object is accessed via the `this` pointer instead. @@ -53,15 +53,43 @@ The following table illustrates the differences: | Rhai script | _N_ | `this` (of type `&mut T`) | `Fn(x: U, y: V)` | -`&mut` is Efficient, Except for `ImmutableString` ------------------------------------------------ +`&mut` is Efficient, Except for `&mut ImmutableString` +---------------------------------------------------- Using a `&mut` first parameter is highly encouraged when using types that are expensive to clone, even when the intention is not to mutate that argument, because it avoids cloning that argument value. -For example, the `len` method of an [array] has the signature: `Fn(&mut Array) -> INT`. -The array itself is not modified in any way, but using a `&mut` parameter avoids a cloning that would -otherwise have happened if the signature were `Fn(Array) -> INT`. +Even when a function is never intended to be a method - for example an operator, +it is still sometimes beneficial to make it method-like (i.e. with a first `&mut` parameter) +if the first parameter is not modified. + +For types that are expensive to clone (remember, all function calls are passed cloned +copies of argument values), this may result in a significant performance boost. For primary types that are cheap to clone (e.g. those that implement `Copy`), including `ImmutableString`, this is not necessary. + +```rust +// This is a type that is very expensive to clone. +#[derive(Debug, Clone)] +struct VeryComplexType { ... } + +// Calculate some value by adding 'VeryComplexType' with an integer number. +fn do_add(obj: &VeryComplexType, offset: i64) -> i64 { + ... +} + +engine + .register_type::() + .register_fn("+", add_pure /* or add_method*/); + +// Very expensive to call, as the 'VeryComplexType' is cloned before each call. +fn add_pure(obj: VeryComplexType, offset: i64) -> i64 { + do_add(obj, offset) +} + +// Efficient to call, as only a reference to the 'VeryComplexType' is passed. +fn add_method(obj: &mut VeryComplexType, offset: i64) -> i64 { + do_add(obj, offset) +} +``` diff --git a/doc/src/language/strings-chars.md b/doc/src/language/strings-chars.md index a402d485..e9f5e074 100644 --- a/doc/src/language/strings-chars.md +++ b/doc/src/language/strings-chars.md @@ -29,7 +29,15 @@ Modifying an `ImmutableString` causes it first to be cloned, and then the modifi `ImmutableString` should be used in place of `String` for function parameters because using `String` is very inefficient (the `String` argument is cloned during every call). -A alternative is to use `&str` which maps straight to `ImmutableString`. +A alternative is to use `&str` which de-sugars to `ImmutableString`. + +```rust +fn slow(s: String) -> i64 { ... } // string is cloned each call + +fn fast1(s: ImmutableString) -> i64 { ... } // cloning 'ImmutableString' is cheap + +fn fast2(s: &str) -> i64 { ... } // de-sugars to above +``` String and Character Literals diff --git a/doc/src/language/throw.md b/doc/src/language/throw.md index 97b74182..0b6c479e 100644 --- a/doc/src/language/throw.md +++ b/doc/src/language/throw.md @@ -16,8 +16,9 @@ if some_bad_condition_has_happened { throw; // defaults to empty exception text: "" ``` -Exceptions thrown via `throw` in the script can be captured by matching `Err(Box)` -with the exception text captured by the first parameter. +Exceptions thrown via `throw` in the script can be captured in Rust by matching +`Err(Box)` with the exception text +captured by `reason`. ```rust let result = engine.eval::(r#" diff --git a/doc/src/patterns/config.md b/doc/src/patterns/config.md index 3017bb1d..6af3ae10 100644 --- a/doc/src/patterns/config.md +++ b/doc/src/patterns/config.md @@ -46,15 +46,22 @@ struct Config { ### Make Shared Object ```rust -let config: Rc> = Rc::new(RefCell::new(Default::default())); +pub type SharedConfig = Rc>; +``` + +Note: Use `Arc>` or `Arc>` when using the [`sync`] feature because the function +must then be `Send + Sync`. + +```rust +let config: SharedConfig = Rc::new(RefCell::new(Default::default())); ``` ### Register Config API The trick to building a Config API is to clone the shared configuration object and -move it into each function registration as a closure. +move it into each function registration via a closure. -It is not possible to use a [plugin module] to achieve this, so each function must +Therefore, it is not possible to use a [plugin module] to achieve this, and each function must be registered one after another. ```rust diff --git a/doc/src/patterns/control.md b/doc/src/patterns/control.md index c3624442..8743d260 100644 --- a/doc/src/patterns/control.md +++ b/doc/src/patterns/control.md @@ -35,7 +35,7 @@ but only through exposing an abstract API primarily made up of functions. Use this when the API is relatively simple and clean, and the number of functions is small enough. -For a complex API involving lots of functions, or an API that is object-based, +For a complex API involving lots of functions, or an API that has a clear object structure, use the [Singleton Command Object]({{rootUrl}}/patterns/singleton.md) pattern instead. @@ -59,15 +59,22 @@ impl EnergizerBunny { ### Wrap API in Shared Object ```rust -let bunny: Rc> = Rc::new(RefCell::(EnergizerBunny::new())); +pub type SharedBunny = Rc>; +``` + +Note: Use `Arc>` or `Arc>` when using the [`sync`] feature because the function +must then be `Send + Sync`. + +```rust +let bunny: SharedBunny = Rc::new(RefCell::(EnergizerBunny::new())); ``` ### Register Control API The trick to building a Control API is to clone the shared API object and -move it into each function registration as a closure. +move it into each function registration via a closure. -It is not possible to use a [plugin module] to achieve this, so each function must +Therefore, it is not possible to use a [plugin module] to achieve this, and each function must be registered one after another. ```rust diff --git a/doc/src/patterns/singleton.md b/doc/src/patterns/singleton.md index fa7c9d75..877180df 100644 --- a/doc/src/patterns/singleton.md +++ b/doc/src/patterns/singleton.md @@ -41,7 +41,7 @@ wrapping the system in a shared, interior-mutated object. This is the other way which involves directly exposing the data structures of the external system as a name singleton object in the scripting space. -Use this when the API is complex and clearly object-based. +Use this when the API is complex but has a clear object structure. For a relatively simple API that is action-based and not object-based, use the [Control Layer]({{rootUrl}}/patterns/control.md) pattern instead. @@ -68,13 +68,16 @@ impl EnergizerBunny { ### Wrap Command Object Type as Shared ```rust -let SharedBunnyType = Rc>; +pub type SharedBunny = Rc>; ``` +Note: Use `Arc>` or `Arc>` when using the [`sync`] feature because the function +must then be `Send + Sync`. + ### Register the Custom Type ```rust -engine.register_type_with_name::("EnergizerBunny"); +engine.register_type_with_name::("EnergizerBunny"); ``` ### Develop a Plugin with Methods and Getters/Setters @@ -82,18 +85,18 @@ engine.register_type_with_name::("EnergizerBunny"); The easiest way to develop a complete set of API for a [custom type] is via a [plugin module]. ```rust -use rhai::plugins::*; +use rhai::plugin::*; #[export_module] pub mod bunny_api { pub const MAX_SPEED: i64 = 100; #[rhai_fn(get = "power")] - pub fn get_power(bunny: &mut SharedBunnyType) -> bool { + pub fn get_power(bunny: &mut SharedBunny) -> bool { bunny.borrow().is_going() } #[rhai_fn(set = "power")] - pub fn set_power(bunny: &mut SharedBunnyType, on: bool) { + pub fn set_power(bunny: &mut SharedBunny, on: bool) { if on { if bunny.borrow().is_going() { println!("Still going..."); @@ -109,7 +112,7 @@ pub mod bunny_api { } } #[rhai_fn(get = "speed")] - pub fn get_speed(bunny: &mut SharedBunnyType) -> i64 { + pub fn get_speed(bunny: &mut SharedBunny) -> i64 { if bunny.borrow().is_going() { bunny.borrow().get_speed() } else { @@ -117,7 +120,7 @@ pub mod bunny_api { } } #[rhai_fn(set = "speed", return_raw)] - pub fn set_speed(bunny: &mut SharedBunnyType, speed: i64) + pub fn set_speed(bunny: &mut SharedBunny, speed: i64) -> Result> { if speed <= 0 { @@ -131,12 +134,12 @@ pub mod bunny_api { Ok(().into()) } } - pub fn turn_left(bunny: &mut SharedBunnyType) { + pub fn turn_left(bunny: &mut SharedBunny) { if bunny.borrow().is_going() { bunny.borrow_mut().turn(true); } } - pub fn turn_right(bunny: &mut SharedBunnyType) { + pub fn turn_right(bunny: &mut SharedBunny) { if bunny.borrow().is_going() { bunny.borrow_mut().turn(false); } @@ -149,7 +152,7 @@ engine.load_package(exported_module!(bunny_api)); ### Push Constant Command Object into Custom Scope ```rust -let bunny: SharedBunnyType = Rc::new(RefCell::(EnergizerBunny::new())); +let bunny: SharedBunny = Rc::new(RefCell::(EnergizerBunny::new())); let mut scope = Scope::new(); diff --git a/doc/src/plugins/function.md b/doc/src/plugins/function.md index 388c9b84..396f53e1 100644 --- a/doc/src/plugins/function.md +++ b/doc/src/plugins/function.md @@ -29,7 +29,7 @@ To register the plugin function, simply call `register_exported_fn!`. The name any text string, so it is possible to register _overloaded_ functions as well as operators. ```rust -use rhai::plugins::*; // import macros +use rhai::plugin::*; // import macros #[export_fn] fn increment(num: &mut i64) { @@ -55,7 +55,7 @@ A syntax error is generated if the function with `#[rhai_fn(return_raw)]` does n have the appropriate return type. ```rust -use rhai::plugins::*; // import macros +use rhai::plugin::*; // a "prelude" import for macros #[export_fn] #[rhai_fn(return_raw)] diff --git a/doc/src/plugins/index.md b/doc/src/plugins/index.md index 04fb1d35..1e3f3cc9 100644 --- a/doc/src/plugins/index.md +++ b/doc/src/plugins/index.md @@ -9,5 +9,5 @@ functionality. Instead of using the large `Engine::register_XXX` API or the parallel `Module::set_fn_XXX` API, a _plugin_ simplifies the work of creating and registering new functionality in an [`Engine`]. -Plugins are processed via a set of procedural macros under the `rhai::plugins` module. These +Plugins are processed via a set of procedural macros under the `rhai::plugin` module. These allow registering Rust functions directly in the Engine, or adding Rust modules as packages. diff --git a/doc/src/plugins/module.md b/doc/src/plugins/module.md index 97e2ffa6..a083fb08 100644 --- a/doc/src/plugins/module.md +++ b/doc/src/plugins/module.md @@ -4,6 +4,20 @@ Export a Rust Module to Rhai {{#include ../links.md}} +Prelude +------- + +When using the plugins system, the entire `rhai::plugin` module must be imported as a prelude +because code generated will these imports. + +```rust +use rhai::plugin::*; +``` + + +`#[export_module]` and `exported_module!` +---------------------------------------- + When applied to a Rust module, the `#[export_module]` attribute generates the necessary code and metadata to allow Rhai access to its public (i.e. marked `pub`) functions, constants and sub-modules. @@ -14,18 +28,11 @@ and is custom fit to each exported item. This Rust module can then either be loaded into an [`Engine`] as a normal [module] or registered as a [custom package]. This is done by using the `exported_module!` macro. - -`#[export_module]` and `exported_module!` ----------------------------------------- - -Apply `#[export_module]` onto a Rust module to register automatically construct a Rhai [module], -which can then be loaded into an [`Engine`]. - All `pub` functions become registered functions, all `pub` constants become [module] constant variables, and all sub-modules become Rhai sub-modules. ```rust -use rhai::plugins::*; // a "prelude" import for macros +use rhai::plugin::*; // a "prelude" import for macros #[export_module] mod my_module { @@ -117,7 +124,7 @@ Operators (which require function names that are not valid for Rust) can also be Registering the same function name with the same parameter types will cause a parsing error. ```rust -use rhai::plugins::*; // a "prelude" import for macros +use rhai::plugin::*; // a "prelude" import for macros #[export_module] mod my_module { @@ -147,7 +154,7 @@ Functions can be marked as [getters/setters] and [indexers] for [custom types] v attribute, which is applied on a function level. ```rust -use rhai::plugins::*; // a "prelude" import for macros +use rhai::plugin::*; // a "prelude" import for macros #[export_module] mod my_module { @@ -188,7 +195,7 @@ This is especially useful for the `name = "..."`, `get = "..."` and `set = "..." to give multiple alternative names to the same function. ```rust -use rhai::plugins::*; // a "prelude" import for macros +use rhai::plugin::*; // a "prelude" import for macros #[export_module] mod my_module { @@ -221,7 +228,7 @@ A syntax error is generated if the function with `#[rhai_fn(return_raw)]` does n have the appropriate return type. ```rust -use rhai::plugins::*; // a "prelude" import for macros +use rhai::plugin::*; // a "prelude" import for macros #[export_module] mod my_module { diff --git a/doc/src/rust/disable-custom.md b/doc/src/rust/disable-custom.md index e5ad8a82..912482e5 100644 --- a/doc/src/rust/disable-custom.md +++ b/doc/src/rust/disable-custom.md @@ -3,8 +3,16 @@ Disable Custom Types {{#include ../links.md}} + +`no_object` Feature +------------------- + The custom types API `register_type`, `register_type_with_name`, `register_get`, `register_get_result`, `register_set`, `register_set_result` and `register_get_set` are not available under [`no_object`]. + +`no_index` Feature +------------------ + The indexers API `register_indexer_get`, `register_indexer_get_result`, `register_indexer_set`, `register_indexer_set_result`, and `register_indexer_get_set` are also not available under [`no_index`]. diff --git a/doc/src/rust/getters-setters.md b/doc/src/rust/getters-setters.md index bb07989d..586070af 100644 --- a/doc/src/rust/getters-setters.md +++ b/doc/src/rust/getters-setters.md @@ -11,7 +11,7 @@ Getters and setters are disabled when the [`no_object`] feature is used. | `Engine` API | Description | Return Value of Function | | --------------------- | ------------------------------------------------- | :-----------------------------------: | -| `register_get` | register a getter | _any_ | +| `register_get` | register a getter | _any_ `T: Clone` | | `register_set` | register a setter | _none_ | | `register_get_set` | short-hand to register both a getter and a setter | _none_ | | `register_get_result` | register a getter | `Result>` | diff --git a/doc/src/rust/indexers.md b/doc/src/rust/indexers.md index ca880cff..2f76277f 100644 --- a/doc/src/rust/indexers.md +++ b/doc/src/rust/indexers.md @@ -15,7 +15,7 @@ Indexers are disabled when the [`no_index`] feature is used. | `Engine` API | Description | Return Value of Function | | ----------------------------- | -------------------------------------------------------- | :-----------------------------------: | -| `register_indexer_get` | register an index getter | _any_ | +| `register_indexer_get` | register an index getter | _any_ `T: Clone` | | `register_indexer_set` | register an index setter | _none_ | | `register_indexer_get_set` | short-hand to register both an index getter and a setter | _none_ | | `register_indexer_get_result` | register an index getter | `Result>` | diff --git a/doc/src/rust/operators.md b/doc/src/rust/operators.md index e4ce9b65..83175bf8 100644 --- a/doc/src/rust/operators.md +++ b/doc/src/rust/operators.md @@ -6,16 +6,23 @@ Operator Overloading In Rhai, a lot of functionalities are actually implemented as functions, including basic operations such as arithmetic calculations. -For example, in the expression "`a + b`", the `+` operator is _not_ built in, but calls a function named "`+`" instead! +For example, in the expression "`a + b`", the `+` operator calls a function named "`+`"! ```rust let x = a + b; + let x = +(a, b); // <- the above is equivalent to this function call ``` Similarly, comparison operators including `==`, `!=` etc. are all implemented as functions, -with the stark exception of `&&` and `||`. Because they [_short-circuit_]({{rootUrl}}/language/logic.md#boolean-operators), -`&&` and `||` are handled specially and _not_ via a function; as a result, overriding them has no effect at all. +with the stark exception of `&&` and `||`. + + +`&&` and `||` Cannot Be Overloaded +--------------------------------- + +Because they [_short-circuit_]({{rootUrl}}/language/logic.md#boolean-operators), `&&` and `||` are +handled specially and _not_ via a function; as a result, overriding them has no effect at all. Overload Operator via Rust Function diff --git a/doc/src/rust/packages/create.md b/doc/src/rust/packages/create.md index 698be745..04911dd4 100644 --- a/doc/src/rust/packages/create.md +++ b/doc/src/rust/packages/create.md @@ -60,7 +60,7 @@ def_package!(rhai:MyPackage:"My own personal super package", module, { Create a Custom Package from a Plugin Module ------------------------------------------- -By far the easiest way to create a custom module is to call `rhai::plugins::combine_with_exported_module!` +By far the easiest way to create a custom module is to call `rhai::plugin::combine_with_exported_module!` from within `rhai::def_package!` which simply merges in all the functions defined within a [plugin module]. In fact, this exactly is how Rhai's built-in packages, such as `BasicMathPackage`, are implemented. @@ -113,8 +113,8 @@ def_package!(rhai:MyPackage:"My own personal super package", module, { // // The sub-module 'my_sub_module' is flattened and its functions registered at the top level. // - // The text string name in the middle parameter can be anything and is reserved for future use; - // it is recommended to be an ID string that uniquely identifies the module. + // The text string name in the second parameter can be anything and is reserved for future use; + // it is recommended to be an ID string that uniquely identifies the plugin module. // // The constant variable, 'MY_NUMBER', is ignored. // diff --git a/doc/src/rust/packages/plugin.md b/doc/src/rust/packages/plugin.md index 42bc9519..509aff32 100644 --- a/doc/src/rust/packages/plugin.md +++ b/doc/src/rust/packages/plugin.md @@ -3,7 +3,8 @@ Load a Plugin Module as a Package {{#include ../../links.md}} -[Plugin modules] can be loaded as a package just like a normal [module]. +[Plugin modules] can be loaded as a package just like a normal [module] +via the `exported_module!` macro. ```rust use rhai::Engine; diff --git a/doc/src/rust/register-raw.md b/doc/src/rust/register-raw.md index b77ebab5..2897138f 100644 --- a/doc/src/rust/register-raw.md +++ b/doc/src/rust/register-raw.md @@ -35,13 +35,12 @@ engine.register_raw_fn( // Therefore, get a '&mut' reference to the first argument _last_. // Alternatively, use `args.split_first_mut()` etc. to split the slice first. - let y: i64 = *args[1].read_lock::() // get a reference to the second argument - .unwrap(); // then copying it because it is a primary type + let y = *args[1].read_lock::().unwrap(); // get a reference to the second argument + // then copy it because it is a primary type - let y: i64 = std::mem::take(args[1]).cast::(); // alternatively, directly 'consume' it + let y = std::mem::take(args[1]).cast::(); // alternatively, directly 'consume' it - let x: &mut i64 = args[0].write_lock::() // get a '&mut' reference to the - .unwrap(); // first argument + let x = args[0].write_lock::().unwrap(); // get a '&mut' reference to the first argument *x += y; // perform the action @@ -51,7 +50,7 @@ engine.register_raw_fn( // The above is the same as (in fact, internally they are equivalent): -engine.register_fn("increment_by", |x: &mut i64, y: i64| x += y); +engine.register_fn("increment_by", |x: &mut i64, y: i64| *x += y); ``` @@ -153,7 +152,8 @@ is a _shared value_ created by [capturing][automatic currying] variables from [c Shared values are implemented as `Rc>` (`Arc>` under [`sync`]). If the value is _not_ a shared value, or if running under [`no_closure`] where there is -no [capturing][automatic currying], this API de-sugars to a simple `downcast_ref` and `downcast_mut`. +no [capturing][automatic currying], this API de-sugars to a simple `Dynamic::downcast_ref` and +`Dynamic::downcast_mut`. If the value is a shared value, then it is first locked and the returned lock guard then allows access to the underlying value in the specified type. @@ -171,9 +171,9 @@ to partition the slice: let (first, rest) = args.split_first_mut().unwrap(); // Mutable reference to the first parameter -let this_ptr: &mut A = &mut *first.write_lock::().unwrap(); +let this_ptr = &mut *first.write_lock::().unwrap(); // Immutable reference to the second value parameter // This can be mutable but there is no point because the parameter is passed by value -let value_ref: &B = &*rest[0].read_lock::().unwrap(); +let value_ref = &*rest[0].read_lock::().unwrap(); ``` diff --git a/doc/src/rust/traits.md b/doc/src/rust/traits.md index 4d0d8391..a9b1a297 100644 --- a/doc/src/rust/traits.md +++ b/doc/src/rust/traits.md @@ -5,9 +5,10 @@ Traits A number of traits, under the `rhai::` module namespace, provide additional functionalities. -| Trait | Description | Methods | -| ------------------ | ---------------------------------------------------------------------------------------- | --------------------------------------- | -| `RegisterFn` | trait for registering functions | `register_fn` | -| `RegisterResultFn` | trait for registering fallible functions returning `Result>` | `register_result_fn` | -| `Func` | trait for creating Rust closures from script | `create_from_ast`, `create_from_script` | -| `ModuleResolver` | trait implemented by module resolution services | `resolve` | +| Trait | Description | Methods | +| ------------------------ | ------------------------------------------------------------------ | --------------------------------------------------------------------- | +| `RegisterFn` | trait for registering functions | `register_fn` | +| `RegisterResultFn` | trait for registering [fallible functions] | `register_result_fn` | +| `Func` | trait for creating Rust closures from script | `create_from_ast`, `create_from_script` | +| `ModuleResolver` | trait implemented by [module resolution][module resolver] services | `resolve` | +| `plugin::PluginFunction` | trait implemented by [plugin] functions | `call`, `is_method_call`, `is_variadic`, `clone_boxed`, `input_types` | From 2290c72de1449716678993bdde6054bc2d401441 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Wed, 30 Sep 2020 23:27:41 +0800 Subject: [PATCH 7/9] Fix tests. --- codegen/src/lib.rs | 18 +++++++++--------- src/plugin.rs | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/codegen/src/lib.rs b/codegen/src/lib.rs index abd92df5..e5e10c53 100644 --- a/codegen/src/lib.rs +++ b/codegen/src/lib.rs @@ -1,6 +1,6 @@ //! This crate contains procedural macros to make creating Rhai plugin-modules much easier. //! -//! # Export an Entire Rust Module to a Rhai Module +//! # Export an Entire Rust Module to a Rhai `Module` //! //! ``` //! use rhai::{EvalAltResult, FLOAT}; @@ -32,7 +32,7 @@ //! } //! ``` //! -//! # Export a Rust Function to a Rhai Module +//! # Register a Rust Function with a Rhai `Module` //! //! ``` //! use rhai::{EvalAltResult, FLOAT, Module, RegisterFn}; @@ -63,7 +63,7 @@ //! } //! ``` //! -//! # Exporting a Function to an Engine +//! # Register a Plugin Function with an `Engine` //! //! ``` //! use rhai::{EvalAltResult, FLOAT, Module, RegisterFn}; @@ -106,7 +106,7 @@ mod test; /// /// # Usage /// -/// ```no_run +/// ```,no_run /// use rhai::plugin::*; /// /// #[export_fn] @@ -138,7 +138,7 @@ pub fn export_fn( /// /// # Usage /// -/// ```no_run +/// ```,no_run /// use rhai::plugin::*; /// /// #[export_module] @@ -168,7 +168,7 @@ pub fn export_module( /// /// # Usage /// -/// ```no_run +/// ```,no_run /// use rhai::plugin::*; /// /// #[export_module] @@ -203,7 +203,7 @@ pub fn exported_module(module_path: proc_macro::TokenStream) -> proc_macro::Toke /// /// # Usage /// -/// ```no_run +/// ```,no_run /// use rhai::plugin::*; /// /// #[export_module] @@ -232,7 +232,7 @@ pub fn combine_with_exported_module(args: proc_macro::TokenStream) -> proc_macro /// /// # Usage /// -/// ```no_run +/// ```,no_run /// use rhai::plugin::*; /// /// #[export_fn] @@ -262,7 +262,7 @@ pub fn register_exported_fn(args: proc_macro::TokenStream) -> proc_macro::TokenS /// /// # Usage /// -/// ```no_run +/// ```,no_run /// use rhai::plugin::*; /// /// #[export_fn] diff --git a/src/plugin.rs b/src/plugin.rs index f82056ea..d00ef28a 100644 --- a/src/plugin.rs +++ b/src/plugin.rs @@ -9,7 +9,7 @@ pub use crate::parser::FnAccess; pub use crate::result::EvalAltResult; pub use crate::utils::ImmutableString; -pub use crate::stdlib::{any::TypeId, boxed::Box, format, mem, vec as new_vec}; +pub use crate::stdlib::{any::TypeId, boxed::Box, format, mem, string::ToString, vec as new_vec}; #[cfg(not(features = "no_module"))] pub use rhai_codegen::*; From 45a290f0eaa113bc09b21a4a88494b3b1eb6b0bc Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Wed, 30 Sep 2020 23:39:46 +0800 Subject: [PATCH 8/9] Change doc test from no_run to ignore. --- codegen/src/lib.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/codegen/src/lib.rs b/codegen/src/lib.rs index e5e10c53..a3266d78 100644 --- a/codegen/src/lib.rs +++ b/codegen/src/lib.rs @@ -106,7 +106,7 @@ mod test; /// /// # Usage /// -/// ```,no_run +/// ```,ignore /// use rhai::plugin::*; /// /// #[export_fn] @@ -138,7 +138,7 @@ pub fn export_fn( /// /// # Usage /// -/// ```,no_run +/// ```,ignore /// use rhai::plugin::*; /// /// #[export_module] @@ -168,7 +168,7 @@ pub fn export_module( /// /// # Usage /// -/// ```,no_run +/// ```,ignore /// use rhai::plugin::*; /// /// #[export_module] @@ -203,7 +203,7 @@ pub fn exported_module(module_path: proc_macro::TokenStream) -> proc_macro::Toke /// /// # Usage /// -/// ```,no_run +/// ```,ignore /// use rhai::plugin::*; /// /// #[export_module] @@ -232,7 +232,7 @@ pub fn combine_with_exported_module(args: proc_macro::TokenStream) -> proc_macro /// /// # Usage /// -/// ```,no_run +/// ```,ignore /// use rhai::plugin::*; /// /// #[export_fn] @@ -262,7 +262,7 @@ pub fn register_exported_fn(args: proc_macro::TokenStream) -> proc_macro::TokenS /// /// # Usage /// -/// ```,no_run +/// ```,ignore /// use rhai::plugin::*; /// /// #[export_fn] From 07fdd1bef1258e86cfdfc8cc54eb55434635fa69 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Thu, 1 Oct 2020 00:11:45 +0800 Subject: [PATCH 9/9] Fix image url link. --- doc/src/about/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/src/about/index.md b/doc/src/about/index.md index 2c6efcc8..ce966635 100644 --- a/doc/src/about/index.md +++ b/doc/src/about/index.md @@ -3,7 +3,7 @@ What is Rhai {{#include ../links.md}} -![Rhai Logo](../images/rhai_logo.png) +![Rhai Logo]({{rootUrl}}/images/rhai_logo.png) Rhai is an embedded scripting language and evaluation engine for Rust that gives a safe and easy way to add scripting to any application.