diff --git a/codegen/src/function.rs b/codegen/src/function.rs index ae50e45a..0c8391bb 100644 --- a/codegen/src/function.rs +++ b/codegen/src/function.rs @@ -229,8 +229,10 @@ impl ExportedFn { } } - pub fn generate_with_params(mut self, - mut params: ExportedFnParams) -> proc_macro2::TokenStream { + pub fn generate_with_params( + mut self, + mut params: ExportedFnParams, + ) -> proc_macro2::TokenStream { self.params = params; self.generate() } @@ -241,11 +243,12 @@ impl ExportedFn { } else { self.name().to_string() }; - let name: syn::Ident = syn::Ident::new(&format!("rhai_fn_{}", name_str), - self.name().span()); + let name: syn::Ident = + syn::Ident::new(&format!("rhai_fn_{}", name_str), self.name().span()); let impl_block = self.generate_impl("Token"); let callable_block = self.generate_callable("Token"); let input_types_block = self.generate_input_types("Token"); + let dyn_result_fn_block = self.generate_dynamic_fn(); quote! { #[allow(unused)] pub mod #name { @@ -254,6 +257,54 @@ impl ExportedFn { #impl_block #callable_block #input_types_block + #dyn_result_fn_block + } + } + } + + pub fn generate_dynamic_fn(&self) -> proc_macro2::TokenStream { + let name: syn::Ident = if let Some(ref name) = self.params.name { + syn::Ident::new(name, self.name().span()) + } else { + self.name().clone() + }; + + let mut dynamic_signature = self.signature.clone(); + dynamic_signature.ident = + syn::Ident::new("dynamic_result_fn", proc_macro2::Span::call_site()); + dynamic_signature.output = syn::parse2::(quote! { + -> Result + }) + .unwrap(); + let arguments: Vec = dynamic_signature + .inputs + .iter() + .filter_map(|fnarg| { + if let syn::FnArg::Typed(syn::PatType { ref pat, .. }) = fnarg { + if let syn::Pat::Ident(ref ident) = pat.as_ref() { + Some(ident.ident.clone()) + } else { + None + } + } else { + None + } + }) + .collect(); + + if !self.params.return_raw { + quote! { + type EvalBox = Box; + pub #dynamic_signature { + Ok(Dynamic::from(super::#name(#(#arguments),*))) + } + } + } else { + quote! { + type EvalBox = Box; + pub #dynamic_signature { + super::#name(#(#arguments),*) + } } } } @@ -740,6 +791,10 @@ mod generate_tests { pub fn token_input_types() -> Box<[std::any::TypeId]> { Token().input_types() } + type EvalBox = Box; + pub fn dynamic_result_fn() -> Result { + Ok(Dynamic::from(super::do_nothing())) + } } }; @@ -784,6 +839,10 @@ mod generate_tests { pub fn token_input_types() -> Box<[std::any::TypeId]> { Token().input_types() } + type EvalBox = Box; + pub fn dynamic_result_fn(x: usize) -> Result { + Ok(Dynamic::from(super::do_something(x))) + } } }; @@ -863,6 +922,10 @@ mod generate_tests { pub fn token_input_types() -> Box<[std::any::TypeId]> { Token().input_types() } + type EvalBox = Box; + pub fn dynamic_result_fn(x: usize, y: usize) -> Result { + Ok(Dynamic::from(super::add_together(x, y))) + } } }; @@ -909,6 +972,10 @@ mod generate_tests { pub fn token_input_types() -> Box<[std::any::TypeId]> { Token().input_types() } + type EvalBox = Box; + pub fn dynamic_result_fn(x: &mut usize, y: usize) -> Result { + Ok(Dynamic::from(super::increment(x, y))) + } } }; @@ -954,6 +1021,10 @@ mod generate_tests { pub fn token_input_types() -> Box<[std::any::TypeId]> { Token().input_types() } + type EvalBox = Box; + pub fn dynamic_result_fn(message: &str) -> Result { + Ok(Dynamic::from(super::special_print(message))) + } } }; diff --git a/codegen/src/lib.rs b/codegen/src/lib.rs index 8cc5bbd4..9a526883 100644 --- a/codegen/src/lib.rs +++ b/codegen/src/lib.rs @@ -35,7 +35,7 @@ //! } //! ``` //! -//! # Exporting a Function to Rhai +//! # Exporting a Function to a Rhai Module //! //! ``` //! use rhai::{EvalAltResult, FLOAT, Module, RegisterFn}; @@ -52,7 +52,7 @@ //! let mut engine = Engine::new(); //! engine.register_fn("get_mystic_number", || { 42 as FLOAT }); //! let mut m = Module::new(); -//! rhai::register_exported_fn!(m, "euclidean_distance", distance_function); +//! rhai::set_exported_fn!(m, "euclidean_distance", distance_function); //! let mut r = StaticModuleResolver::new(); //! r.insert("Math::Advanced".to_string(), m); //! engine.set_module_resolver(Some(r)); @@ -66,12 +66,39 @@ //! } //! ``` //! +//! # Exporting a Function to an Engine +//! +//! ``` +//! 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 { +//! ((y2 - y1).abs().powf(2.0) + (x2 -x1).abs().powf(2.0)).sqrt() +//! } +//! +//! fn main() -> Result<(), Box> { +//! +//! let mut engine = Engine::new(); +//! engine.register_fn("get_mystic_number", || { 42 as FLOAT }); +//! rhai::register_exported_fn!(engine, "euclidean_distance", distance_function); +//! +//! assert_eq!(engine.eval::( +//! r#"let m = get_mystic_number(); +//! let x = euclidean_distance(0.0, 1.0, 0.0, m); +//! x"#)?, 41.0); +//! Ok(()) +//! } +//! ``` +//! -use quote::{quote, quote_spanned, ToTokens}; -use syn::{parse::Parser, parse_macro_input, spanned::Spanned}; +use quote::{quote, ToTokens}; +use syn::parse_macro_input; mod function; mod module; +mod register; mod rhai_module; #[proc_macro_attribute] @@ -118,47 +145,30 @@ pub fn exported_module(module_path: proc_macro::TokenStream) -> proc_macro::Toke #[proc_macro] pub fn register_exported_fn(args: proc_macro::TokenStream) -> proc_macro::TokenStream { - let parser = syn::punctuated::Punctuated::::parse_separated_nonempty; - let args = parser.parse(args).unwrap(); - let arg_span = args.span(); - let items: Vec = args.into_iter().collect(); - if items.len() != 3 { - return proc_macro::TokenStream::from( - syn::Error::new(arg_span, "this macro requires three arguments").to_compile_error(), - ); - } - let rhai_module = &items[0]; - let export_name = match &items[1] { - syn::Expr::Lit(litstr) => quote_spanned!(items[1].span()=> - #litstr.to_string()), - expr => quote! { #expr }, - }; - let rust_modpath = if let syn::Expr::Path(ref path) = &items[2] { - &path.path - } else { - return proc_macro::TokenStream::from( - syn::Error::new(items[2].span(), "third argument must be a function name") - .to_compile_error(), - ); - }; - let gen_mod_path: syn::punctuated::Punctuated = { - let mut g = rust_modpath.clone().segments; - g.pop(); - let ident = syn::Ident::new( - &format!("rhai_fn_{}", rust_modpath.segments.last().unwrap().ident), - items[2].span(), - ); - g.push_value(syn::PathSegment { - ident, - arguments: syn::PathArguments::None, - }); - g + let (engine_expr, export_name, rust_modpath) = match crate::register::parse_register_macro(args) + { + Ok(triple) => triple, + Err(e) => return e.to_compile_error().into(), }; + let gen_mod_path = crate::register::generated_module_path(&rust_modpath); let tokens = quote! { - #rhai_module.set_fn(#export_name, rhai::FnAccess::Public, - #gen_mod_path::token_input_types().as_ref(), - #gen_mod_path::token_callable()); - + #engine_expr.register_result_fn(&(#export_name), #gen_mod_path::dynamic_result_fn); + }; + proc_macro::TokenStream::from(tokens) +} + +#[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) + { + Ok(triple) => triple, + Err(e) => return e.to_compile_error().into(), + }; + let gen_mod_path = crate::register::generated_module_path(&rust_modpath); + let tokens = quote! { + #module_expr.set_fn(#export_name, rhai::FnAccess::Public, + #gen_mod_path::token_input_types().as_ref(), + #gen_mod_path::token_callable()); }; proc_macro::TokenStream::from(tokens) } diff --git a/codegen/src/register.rs b/codegen/src/register.rs new file mode 100644 index 00000000..321769b7 --- /dev/null +++ b/codegen/src/register.rs @@ -0,0 +1,49 @@ +use quote::{quote, quote_spanned}; +use syn::{parse::Parser, spanned::Spanned}; + +pub(crate) fn generated_module_path( + fn_path: &syn::Path, +) -> syn::punctuated::Punctuated { + let mut g = fn_path.clone().segments; + g.pop(); + let ident = syn::Ident::new( + &format!("rhai_fn_{}", fn_path.segments.last().unwrap().ident), + fn_path.span(), + ); + g.push_value(syn::PathSegment { + ident, + arguments: syn::PathArguments::None, + }); + g +} + +type RegisterMacroInput = (syn::Expr, proc_macro2::TokenStream, syn::Path); +pub fn parse_register_macro( + args: proc_macro::TokenStream, +) -> Result { + let parser = syn::punctuated::Punctuated::::parse_separated_nonempty; + let args = parser.parse(args).unwrap(); + let arg_span = args.span(); + let mut items: Vec = args.into_iter().collect(); + if items.len() != 3 { + return Err(syn::Error::new( + arg_span, + "this macro requires three arguments", + )); + } + let export_name = match &items[1] { + syn::Expr::Lit(litstr) => quote_spanned!(items[1].span()=> + #litstr.to_string()), + expr => quote! { #expr }, + }; + let rust_modpath = if let syn::Expr::Path(ref path) = &items[2] { + path.path.clone() + } else { + return Err(syn::Error::new( + items[2].span(), + "third argument must be a function name", + )); + }; + let module = items.remove(0); + Ok((module, export_name, rust_modpath)) +} diff --git a/codegen/tests/test_functions.rs b/codegen/tests/test_functions.rs index 876fccf5..b61ae340 100644 --- a/codegen/tests/test_functions.rs +++ b/codegen/tests/test_functions.rs @@ -16,7 +16,7 @@ fn raw_fn_test() -> Result<(), Box> { let mut engine = Engine::new(); engine.register_fn("get_mystic_number", || 42 as FLOAT); let mut m = Module::new(); - rhai::register_exported_fn!( + rhai::set_exported_fn!( m, "euclidean_distance".to_string(), raw_fn::distance_function @@ -50,7 +50,7 @@ fn raw_fn_mut_test() -> Result<(), Box> { let mut engine = Engine::new(); engine.register_fn("get_mystic_number", || 42 as FLOAT); let mut m = Module::new(); - rhai::register_exported_fn!(m, "add_in_place", raw_fn_mut::add_in_place); + rhai::set_exported_fn!(m, "add_in_place", raw_fn_mut::add_in_place); let mut r = StaticModuleResolver::new(); r.insert("Math::Advanced".to_string(), m); engine.set_module_resolver(Some(r)); @@ -82,7 +82,7 @@ fn raw_fn_str_test() -> Result<(), Box> { let mut engine = Engine::new(); engine.register_fn("get_mystic_number", || 42 as FLOAT); let mut m = Module::new(); - rhai::register_exported_fn!(m, "write_out_str", raw_fn_str::write_out_str); + rhai::set_exported_fn!(m, "write_out_str", raw_fn_str::write_out_str); let mut r = StaticModuleResolver::new(); r.insert("Host::IO".to_string(), m); engine.set_module_resolver(Some(r)); @@ -138,9 +138,9 @@ mod mut_opaque_ref { fn mut_opaque_ref_test() -> Result<(), Box> { let mut engine = Engine::new(); let mut m = Module::new(); - rhai::register_exported_fn!(m, "new_message", mut_opaque_ref::new_message); - rhai::register_exported_fn!(m, "new_os_message", mut_opaque_ref::new_os_message); - rhai::register_exported_fn!(m, "write_out_message", mut_opaque_ref::write_out_message); + rhai::set_exported_fn!(m, "new_message", mut_opaque_ref::new_message); + rhai::set_exported_fn!(m, "new_os_message", mut_opaque_ref::new_os_message); + rhai::set_exported_fn!(m, "write_out_message", mut_opaque_ref::write_out_message); let mut r = StaticModuleResolver::new(); r.insert("Host::Msg".to_string(), m); engine.set_module_resolver(Some(r)); @@ -174,7 +174,7 @@ fn rename_fn_test() -> Result<(), Box> { let mut engine = Engine::new(); engine.register_fn("get_mystic_number", || 42 as FLOAT); let mut m = Module::new(); - rhai::register_exported_fn!(m, "add_two_floats", rename_fn::add_float); + rhai::set_exported_fn!(m, "add_two_floats", rename_fn::add_float); let mut r = StaticModuleResolver::new(); r.insert("Math::Advanced".to_string(), m); engine.set_module_resolver(Some(r)); @@ -211,8 +211,8 @@ fn duplicate_fn_rename_test() -> Result<(), Box> { let mut engine = Engine::new(); engine.register_fn("get_mystic_number", || 42 as FLOAT); let mut m = Module::new(); - rhai::register_exported_fn!(m, "add_two_floats", duplicate_fn_rename::add_float); - rhai::register_exported_fn!(m, "add_two_ints", duplicate_fn_rename::add_int); + rhai::set_exported_fn!(m, "add_two_floats", duplicate_fn_rename::add_float); + rhai::set_exported_fn!(m, "add_two_ints", duplicate_fn_rename::add_int); let mut r = StaticModuleResolver::new(); r.insert("Math::Advanced".to_string(), m); engine.set_module_resolver(Some(r)); @@ -224,7 +224,7 @@ fn duplicate_fn_rename_test() -> Result<(), Box> { let ix = 42; let iy = math::add_two_ints(ix, 1); [fy, iy] - "# + "#, )?; assert_eq!(&output_array[0].as_float().unwrap(), &43.0); assert_eq!(&output_array[1].as_int().unwrap(), &43); @@ -253,7 +253,7 @@ fn raw_returning_fn_test() -> Result<(), Box> { let mut engine = Engine::new(); engine.register_fn("get_mystic_number", || 42 as FLOAT); let mut m = Module::new(); - rhai::register_exported_fn!( + rhai::set_exported_fn!( m, "euclidean_distance".to_string(), raw_returning_fn::distance_function diff --git a/src/plugin.rs b/src/plugin.rs index 0de6e63f..c9716ea3 100644 --- a/src/plugin.rs +++ b/src/plugin.rs @@ -11,6 +11,7 @@ pub use crate::{ ImmutableString, Module, Position, + RegisterResultFn, }; pub use rhai_codegen::*;