diff --git a/RELEASES.md b/RELEASES.md index 3dcca24e..b3e0b141 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -22,7 +22,7 @@ Breaking changes ---------------- * `Module::set_fn`, `Module::set_raw_fn` and `Module::set_fn_XXX_mut` all take an additional parameter of `FnNamespace`. -* `Module::set_fn` takes a further parameter with a list of parameter names and types, if any. +* `Module::set_fn` takes a further parameter with a list of parameter names/types plus the function return type, if any. * `Module::get_sub_module_mut` is removed. * `begin`, `end`, `unless` are now reserved keywords. * `EvalPackage` is removed in favor of `Engine::disable_symbol`. @@ -37,8 +37,9 @@ New features * New `set_exported_global_fn!` macro to register a plugin function and expose it to the global namespace. * `Module::set_fn_XXX_mut` can expose a module function to the global namespace. This is convenient when registering an API for a custom type. * `Module::set_getter_fn`, `Module::set_setter_fn`, `Module::set_indexer_get_fn`, `Module::set_indexer_set_fn` all expose the function to the global namespace by default. This is convenient when registering an API for a custom type. -* New `Module::update_fn_param_names` to update a module function's parameter names and types. +* New `Module::update_fn_metadata` to update a module function's parameter names and types. * New `#[rhai_fn(global)]` and `#[rhai_fn(internal)]` attributes to determine whether a function defined in a plugin module should be exposed to the global namespace. This is convenient when defining an API for a custom type. +* New `get_fn_metadata_list` to get the metadata of all script-defined functions in scope. Enhancements ------------ diff --git a/codegen/src/function.rs b/codegen/src/function.rs index 5f976fd9..e84232c3 100644 --- a/codegen/src/function.rs +++ b/codegen/src/function.rs @@ -84,7 +84,15 @@ pub(crate) fn flatten_type_groups(ty: &syn::Type) -> &syn::Type { } pub(crate) fn print_type(ty: &syn::Type) -> String { - ty.to_token_stream().to_string().replace("& ", "&") + ty.to_token_stream() + .to_string() + .replace(" , ", ", ") + .replace("& ", "&") + .replace(" :: ", "::") + .replace(" ( ", "(") + .replace(" ) ", ")") + .replace(" < ", "<") + .replace(" > ", ">") } #[derive(Debug, Default)] @@ -582,6 +590,7 @@ impl ExportedFn { let callable_block = self.generate_callable("Token"); let input_names_block = self.generate_input_names("Token"); let input_types_block = self.generate_input_types("Token"); + let return_type_block = self.generate_return_type("Token"); let dyn_result_fn_block = self.generate_dynamic_fn(); quote! { #[allow(unused)] @@ -592,6 +601,7 @@ impl ExportedFn { #callable_block #input_names_block #input_types_block + #return_type_block #dyn_result_fn_block } } @@ -687,6 +697,19 @@ impl ExportedFn { } } + pub fn generate_return_type(&self, on_type_name: &str) -> proc_macro2::TokenStream { + let token_name: syn::Ident = syn::Ident::new(on_type_name, self.name().span()); + let return_type_fn_name: syn::Ident = syn::Ident::new( + format!("{}_return_type", on_type_name.to_lowercase()).as_str(), + self.name().span(), + ); + quote! { + pub fn #return_type_fn_name() -> &'static str { + #token_name().return_type() + } + } + } + pub fn generate_impl(&self, on_type_name: &str) -> proc_macro2::TokenStream { let sig_name = self.name().clone(); let name = self.params.name.as_ref().map_or_else( @@ -701,6 +724,12 @@ impl ExportedFn { let mut unpack_exprs: Vec = Vec::new(); let mut input_type_names: Vec = Vec::new(); let mut input_type_exprs: Vec = Vec::new(); + + let return_type = self + .return_type() + .map(print_type) + .unwrap_or_else(|| "()".to_string()); + let skip_first_arg; if self.pass_context { @@ -867,6 +896,9 @@ impl ExportedFn { fn input_types(&self) -> Box<[TypeId]> { new_vec![#(#input_type_exprs),*].into_boxed_slice() } + fn return_type(&self) -> &'static str { + #return_type + } } } } diff --git a/codegen/src/rhai_module.rs b/codegen/src/rhai_module.rs index 129089b0..caef2ba2 100644 --- a/codegen/src/rhai_module.rs +++ b/codegen/src/rhai_module.rs @@ -136,6 +136,11 @@ pub(crate) fn generate_body( }) .collect(); + let return_type = function + .return_type() + .map(print_type) + .unwrap_or_else(|| "()".to_string()); + if let Some(ns) = function.params().namespace { namespace = ns; } @@ -151,7 +156,7 @@ pub(crate) fn generate_body( set_fn_stmts.push( syn::parse2::(quote! { m.set_fn(#fn_literal, FnNamespace::#ns_str, FnAccess::Public, - Some(&[#(#fn_input_names),*]), &[#(#fn_input_types),*], + Some(&[#(#fn_input_names,)* #return_type]), &[#(#fn_input_types),*], #fn_token_name().into()); }) .unwrap(), @@ -166,6 +171,7 @@ pub(crate) fn generate_body( gen_fn_tokens.push(function.generate_callable(&fn_token_name.to_string())); gen_fn_tokens.push(function.generate_input_names(&fn_token_name.to_string())); gen_fn_tokens.push(function.generate_input_types(&fn_token_name.to_string())); + gen_fn_tokens.push(function.generate_return_type(&fn_token_name.to_string())); } let mut generate_fncall = syn::parse2::(quote! { diff --git a/codegen/src/test/function.rs b/codegen/src/test/function.rs index c0e74229..7a750123 100644 --- a/codegen/src/test/function.rs +++ b/codegen/src/test/function.rs @@ -292,6 +292,9 @@ mod generate_tests { fn input_types(&self) -> Box<[TypeId]> { new_vec![].into_boxed_slice() } + fn return_type(&self) -> &'static str { + "()" + } } pub fn token_callable() -> CallableFunction { Token().into() @@ -302,6 +305,9 @@ mod generate_tests { pub fn token_input_types() -> Box<[TypeId]> { Token().input_types() } + pub fn token_return_type() -> &'static str { + Token().return_type() + } pub fn dynamic_result_fn() -> Result > { Ok(Dynamic::from(do_nothing())) } @@ -340,6 +346,9 @@ mod generate_tests { fn input_types(&self) -> Box<[TypeId]> { new_vec![TypeId::of::()].into_boxed_slice() } + fn return_type(&self) -> &'static str { + "()" + } } pub fn token_callable() -> CallableFunction { Token().into() @@ -350,6 +359,9 @@ mod generate_tests { pub fn token_input_types() -> Box<[TypeId]> { Token().input_types() } + pub fn token_return_type() -> &'static str { + Token().return_type() + } pub fn dynamic_result_fn(x: usize) -> Result > { Ok(Dynamic::from(do_something(x))) } @@ -388,6 +400,9 @@ mod generate_tests { fn input_types(&self) -> Box<[TypeId]> { new_vec![TypeId::of::()].into_boxed_slice() } + fn return_type(&self) -> &'static str { + "()" + } } pub fn token_callable() -> CallableFunction { Token().into() @@ -398,6 +413,9 @@ mod generate_tests { pub fn token_input_types() -> Box<[TypeId]> { Token().input_types() } + pub fn token_return_type() -> &'static str { + Token().return_type() + } pub fn dynamic_result_fn(context: NativeCallContext, x: usize) -> Result > { Ok(Dynamic::from(do_something(context, x))) } @@ -438,6 +456,9 @@ mod generate_tests { fn input_types(&self) -> Box<[TypeId]> { new_vec![].into_boxed_slice() } + fn return_type(&self) -> &'static str { + "rhai::Dynamic" + } } pub fn token_callable() -> CallableFunction { Token().into() @@ -448,6 +469,9 @@ mod generate_tests { pub fn token_input_types() -> Box<[TypeId]> { Token().input_types() } + pub fn token_return_type() -> &'static str { + Token().return_type() + } pub fn dynamic_result_fn() -> Result > { Ok(return_dynamic()) } @@ -482,7 +506,10 @@ mod generate_tests { fn input_types(&self) -> Box<[TypeId]> { new_vec![TypeId::of::()].into_boxed_slice() } - } + fn return_type(&self) -> &'static str { + "()" + } + } }; let item_fn = syn::parse2::(input_tokens).unwrap(); @@ -519,6 +546,9 @@ mod generate_tests { new_vec![TypeId::of::(), TypeId::of::()].into_boxed_slice() } + fn return_type(&self) -> &'static str { + "usize" + } } pub fn token_callable() -> CallableFunction { Token().into() @@ -529,6 +559,9 @@ mod generate_tests { pub fn token_input_types() -> Box<[TypeId]> { Token().input_types() } + pub fn token_return_type() -> &'static str { + Token().return_type() + } pub fn dynamic_result_fn(x: usize, y: usize) -> Result > { Ok(Dynamic::from(add_together(x, y))) } @@ -569,6 +602,9 @@ mod generate_tests { new_vec![TypeId::of::(), TypeId::of::()].into_boxed_slice() } + fn return_type(&self) -> &'static str { + "()" + } } pub fn token_callable() -> CallableFunction { Token().into() @@ -579,6 +615,9 @@ mod generate_tests { pub fn token_input_types() -> Box<[TypeId]> { Token().input_types() } + pub fn token_return_type() -> &'static str { + Token().return_type() + } pub fn dynamic_result_fn(x: &mut usize, y: usize) -> Result > { Ok(Dynamic::from(increment(x, y))) } @@ -618,6 +657,9 @@ mod generate_tests { fn input_types(&self) -> Box<[TypeId]> { new_vec![TypeId::of::()].into_boxed_slice() } + fn return_type(&self) -> &'static str { + "()" + } } pub fn token_callable() -> CallableFunction { Token().into() @@ -628,6 +670,9 @@ mod generate_tests { pub fn token_input_types() -> Box<[TypeId]> { Token().input_types() } + pub fn token_return_type() -> &'static str { + Token().return_type() + } pub fn dynamic_result_fn(message: &str) -> Result > { Ok(Dynamic::from(special_print(message))) } diff --git a/codegen/src/test/module.rs b/codegen/src/test/module.rs index 8afdf134..8c6bdd50 100644 --- a/codegen/src/test/module.rs +++ b/codegen/src/test/module.rs @@ -297,7 +297,7 @@ mod generate_tests { } #[allow(unused_mut)] pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { - m.set_fn("get_mystic_number", FnNamespace::Internal, FnAccess::Public, Some(&[]), &[], + m.set_fn("get_mystic_number", FnNamespace::Internal, FnAccess::Public, Some(&["INT"]), &[], get_mystic_number_token().into()); if flatten {} else {} } @@ -321,6 +321,9 @@ mod generate_tests { fn input_types(&self) -> Box<[TypeId]> { new_vec![].into_boxed_slice() } + fn return_type(&self) -> &'static str { + "INT" + } } pub fn get_mystic_number_token_callable() -> CallableFunction { get_mystic_number_token().into() @@ -331,6 +334,9 @@ mod generate_tests { pub fn get_mystic_number_token_input_types() -> Box<[TypeId]> { get_mystic_number_token().input_types() } + pub fn get_mystic_number_token_return_type() -> &'static str { + get_mystic_number_token().return_type() + } } }; @@ -365,7 +371,7 @@ mod generate_tests { } #[allow(unused_mut)] pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { - m.set_fn("add_one_to", FnNamespace::Global, FnAccess::Public, Some(&["x: INT"]), &[core::any::TypeId::of::()], + m.set_fn("add_one_to", FnNamespace::Global, FnAccess::Public, Some(&["x: INT", "INT"]), &[core::any::TypeId::of::()], add_one_to_token().into()); if flatten {} else {} } @@ -390,6 +396,9 @@ mod generate_tests { fn input_types(&self) -> Box<[TypeId]> { new_vec![TypeId::of::()].into_boxed_slice() } + fn return_type(&self) -> &'static str { + "INT" + } } pub fn add_one_to_token_callable() -> CallableFunction { add_one_to_token().into() @@ -400,6 +409,9 @@ mod generate_tests { pub fn add_one_to_token_input_types() -> Box<[TypeId]> { add_one_to_token().input_types() } + pub fn add_one_to_token_return_type() -> &'static str { + add_one_to_token().return_type() + } } }; @@ -433,7 +445,7 @@ mod generate_tests { } #[allow(unused_mut)] pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { - m.set_fn("add_one_to", FnNamespace::Internal, FnAccess::Public, Some(&["x: INT"]), &[core::any::TypeId::of::()], + m.set_fn("add_one_to", FnNamespace::Internal, FnAccess::Public, Some(&["x: INT", "INT"]), &[core::any::TypeId::of::()], add_one_to_token().into()); if flatten {} else {} } @@ -458,6 +470,9 @@ mod generate_tests { fn input_types(&self) -> Box<[TypeId]> { new_vec![TypeId::of::()].into_boxed_slice() } + fn return_type(&self) -> &'static str { + "INT" + } } pub fn add_one_to_token_callable() -> CallableFunction { add_one_to_token().into() @@ -468,6 +483,9 @@ mod generate_tests { pub fn add_one_to_token_input_types() -> Box<[TypeId]> { add_one_to_token().input_types() } + pub fn add_one_to_token_return_type() -> &'static str { + add_one_to_token().return_type() + } } }; @@ -512,9 +530,9 @@ mod generate_tests { } #[allow(unused_mut)] pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { - m.set_fn("add_n", FnNamespace::Internal, FnAccess::Public, Some(&["x: INT"]), &[core::any::TypeId::of::()], + m.set_fn("add_n", FnNamespace::Internal, FnAccess::Public, Some(&["x: INT", "INT"]), &[core::any::TypeId::of::()], add_one_to_token().into()); - m.set_fn("add_n", FnNamespace::Internal, FnAccess::Public, Some(&["x: INT", "y: INT"]), + m.set_fn("add_n", FnNamespace::Internal, FnAccess::Public, Some(&["x: INT", "y: INT", "INT"]), &[core::any::TypeId::of::(), core::any::TypeId::of::()], add_n_to_token().into()); if flatten {} else {} @@ -540,6 +558,9 @@ mod generate_tests { fn input_types(&self) -> Box<[TypeId]> { new_vec![TypeId::of::()].into_boxed_slice() } + fn return_type(&self) -> &'static str { + "INT" + } } pub fn add_one_to_token_callable() -> CallableFunction { add_one_to_token().into() @@ -550,6 +571,9 @@ mod generate_tests { pub fn add_one_to_token_input_types() -> Box<[TypeId]> { add_one_to_token().input_types() } + pub fn add_one_to_token_return_type() -> &'static str { + add_one_to_token().return_type() + } #[allow(non_camel_case_types)] struct add_n_to_token(); @@ -574,6 +598,9 @@ mod generate_tests { new_vec![TypeId::of::(), TypeId::of::()].into_boxed_slice() } + fn return_type(&self) -> &'static str { + "INT" + } } pub fn add_n_to_token_callable() -> CallableFunction { add_n_to_token().into() @@ -584,6 +611,9 @@ mod generate_tests { pub fn add_n_to_token_input_types() -> Box<[TypeId]> { add_n_to_token().input_types() } + pub fn add_n_to_token_return_type() -> &'static str { + add_n_to_token().return_type() + } } }; @@ -617,7 +647,7 @@ mod generate_tests { } #[allow(unused_mut)] pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { - m.set_fn("add_together", FnNamespace::Internal, FnAccess::Public, Some(&["x: INT", "y: INT"]), + m.set_fn("add_together", FnNamespace::Internal, FnAccess::Public, Some(&["x: INT", "y: INT", "INT"]), &[core::any::TypeId::of::(), core::any::TypeId::of::()], add_together_token().into()); if flatten {} else {} @@ -645,6 +675,9 @@ mod generate_tests { new_vec![TypeId::of::(), TypeId::of::()].into_boxed_slice() } + fn return_type(&self) -> &'static str { + "INT" + } } pub fn add_together_token_callable() -> CallableFunction { add_together_token().into() @@ -655,6 +688,9 @@ mod generate_tests { pub fn add_together_token_input_types() -> Box<[TypeId]> { add_together_token().input_types() } + pub fn add_together_token_return_type() -> &'static str { + add_together_token().return_type() + } } }; @@ -689,13 +725,13 @@ mod generate_tests { } #[allow(unused_mut)] pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { - m.set_fn("add", FnNamespace::Internal, FnAccess::Public, Some(&["x: INT", "y: INT"]), + m.set_fn("add", FnNamespace::Internal, FnAccess::Public, Some(&["x: INT", "y: INT", "INT"]), &[core::any::TypeId::of::(), core::any::TypeId::of::()], add_together_token().into()); - m.set_fn("+", FnNamespace::Internal, FnAccess::Public, Some(&["x: INT", "y: INT"]), + m.set_fn("+", FnNamespace::Internal, FnAccess::Public, Some(&["x: INT", "y: INT", "INT"]), &[core::any::TypeId::of::(), core::any::TypeId::of::()], add_together_token().into()); - m.set_fn("add_together", FnNamespace::Internal, FnAccess::Public, Some(&["x: INT", "y: INT"]), + m.set_fn("add_together", FnNamespace::Internal, FnAccess::Public, Some(&["x: INT", "y: INT", "INT"]), &[core::any::TypeId::of::(), core::any::TypeId::of::()], add_together_token().into()); if flatten {} else {} @@ -723,6 +759,9 @@ mod generate_tests { new_vec![TypeId::of::(), TypeId::of::()].into_boxed_slice() } + fn return_type(&self) -> &'static str { + "INT" + } } pub fn add_together_token_callable() -> CallableFunction { add_together_token().into() @@ -733,6 +772,9 @@ mod generate_tests { pub fn add_together_token_input_types() -> Box<[TypeId]> { add_together_token().input_types() } + pub fn add_together_token_return_type() -> &'static str { + add_together_token().return_type() + } } }; @@ -948,7 +990,7 @@ mod generate_tests { } #[allow(unused_mut)] pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { - m.set_fn("get_mystic_number", FnNamespace::Internal, FnAccess::Public, Some(&[]), &[], + m.set_fn("get_mystic_number", FnNamespace::Internal, FnAccess::Public, Some(&["INT"]), &[], get_mystic_number_token().into()); if flatten {} else {} } @@ -972,6 +1014,9 @@ mod generate_tests { fn input_types(&self) -> Box<[TypeId]> { new_vec![].into_boxed_slice() } + fn return_type(&self) -> &'static str { + "INT" + } } pub fn get_mystic_number_token_callable() -> CallableFunction { get_mystic_number_token().into() @@ -982,6 +1027,9 @@ mod generate_tests { pub fn get_mystic_number_token_input_types() -> Box<[TypeId]> { get_mystic_number_token().input_types() } + pub fn get_mystic_number_token_return_type() -> &'static str { + get_mystic_number_token().return_type() + } } }; @@ -1047,7 +1095,7 @@ mod generate_tests { #[allow(unused_mut)] pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { m.set_fn("print_out_to", FnNamespace::Internal, FnAccess::Public, - Some(&["x: &str"]), &[core::any::TypeId::of::()], + Some(&["x: &str", "()"]), &[core::any::TypeId::of::()], print_out_to_token().into()); if flatten {} else {} } @@ -1072,6 +1120,9 @@ mod generate_tests { fn input_types(&self) -> Box<[TypeId]> { new_vec![TypeId::of::()].into_boxed_slice() } + fn return_type(&self) -> &'static str { + "()" + } } pub fn print_out_to_token_callable() -> CallableFunction { print_out_to_token().into() @@ -1082,6 +1133,9 @@ mod generate_tests { pub fn print_out_to_token_input_types() -> Box<[TypeId]> { print_out_to_token().input_types() } + pub fn print_out_to_token_return_type() -> &'static str { + print_out_to_token().return_type() + } } }; @@ -1116,7 +1170,7 @@ mod generate_tests { #[allow(unused_mut)] pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { m.set_fn("print_out_to", FnNamespace::Internal, FnAccess::Public, - Some(&["x: String"]), &[core::any::TypeId::of::()], + Some(&["x: String", "()"]), &[core::any::TypeId::of::()], print_out_to_token().into()); if flatten {} else {} } @@ -1141,6 +1195,9 @@ mod generate_tests { fn input_types(&self) -> Box<[TypeId]> { new_vec![TypeId::of::()].into_boxed_slice() } + fn return_type(&self) -> &'static str { + "()" + } } pub fn print_out_to_token_callable() -> CallableFunction { print_out_to_token().into() @@ -1151,6 +1208,9 @@ mod generate_tests { pub fn print_out_to_token_input_types() -> Box<[TypeId]> { print_out_to_token().input_types() } + pub fn print_out_to_token_return_type() -> &'static str { + print_out_to_token().return_type() + } } }; @@ -1185,7 +1245,7 @@ mod generate_tests { #[allow(unused_mut)] pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { m.set_fn("increment", FnNamespace::Internal, FnAccess::Public, - Some(&["x: &mut FLOAT"]), &[core::any::TypeId::of::()], + Some(&["x: &mut FLOAT", "()"]), &[core::any::TypeId::of::()], increment_token().into()); if flatten {} else {} } @@ -1210,6 +1270,9 @@ mod generate_tests { fn input_types(&self) -> Box<[TypeId]> { new_vec![TypeId::of::()].into_boxed_slice() } + fn return_type(&self) -> &'static str { + "()" + } } pub fn increment_token_callable() -> CallableFunction { increment_token().into() @@ -1220,6 +1283,9 @@ mod generate_tests { pub fn increment_token_input_types() -> Box<[TypeId]> { increment_token().input_types() } + pub fn increment_token_return_type() -> &'static str { + increment_token().return_type() + } } }; @@ -1257,7 +1323,7 @@ mod generate_tests { #[allow(unused_mut)] pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { m.set_fn("increment", FnNamespace::Internal, FnAccess::Public, - Some(&["x: &mut FLOAT"]), &[core::any::TypeId::of::()], + Some(&["x: &mut FLOAT", "()"]), &[core::any::TypeId::of::()], increment_token().into()); if flatten {} else {} } @@ -1282,6 +1348,9 @@ mod generate_tests { fn input_types(&self) -> Box<[TypeId]> { new_vec![TypeId::of::()].into_boxed_slice() } + fn return_type(&self) -> &'static str { + "()" + } } pub fn increment_token_callable() -> CallableFunction { increment_token().into() @@ -1292,6 +1361,9 @@ mod generate_tests { pub fn increment_token_input_types() -> Box<[TypeId]> { increment_token().input_types() } + pub fn increment_token_return_type() -> &'static str { + increment_token().return_type() + } } #[allow(unused_imports)] use super::*; @@ -1350,7 +1422,7 @@ mod generate_tests { #[allow(unused_mut)] pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { m.set_fn("increment", FnNamespace::Internal, FnAccess::Public, - Some(&["x: &mut FLOAT"]), &[core::any::TypeId::of::()], + Some(&["x: &mut FLOAT", "()"]), &[core::any::TypeId::of::()], increment_token().into()); if flatten {} else {} } @@ -1375,6 +1447,9 @@ mod generate_tests { fn input_types(&self) -> Box<[TypeId]> { new_vec![TypeId::of::()].into_boxed_slice() } + fn return_type(&self) -> &'static str { + "()" + } } pub fn increment_token_callable() -> CallableFunction { increment_token().into() @@ -1385,6 +1460,9 @@ mod generate_tests { pub fn increment_token_input_types() -> Box<[TypeId]> { increment_token().input_types() } + pub fn increment_token_return_type() -> &'static str { + increment_token().return_type() + } } #[allow(unused_imports)] use super::*; @@ -1441,7 +1519,8 @@ mod generate_tests { } #[allow(unused_mut)] pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { - m.set_fn("get$square", FnNamespace::Internal, FnAccess::Public, Some(&["x: &mut u64"]), &[core::any::TypeId::of::()], + m.set_fn("get$square", FnNamespace::Internal, FnAccess::Public, Some(&["x: &mut u64", "u64"]), + &[core::any::TypeId::of::()], int_foo_token().into()); if flatten {} else {} } @@ -1466,6 +1545,9 @@ mod generate_tests { fn input_types(&self) -> Box<[TypeId]> { new_vec![TypeId::of::()].into_boxed_slice() } + fn return_type(&self) -> &'static str { + "u64" + } } pub fn int_foo_token_callable() -> CallableFunction { int_foo_token().into() @@ -1476,6 +1558,9 @@ mod generate_tests { pub fn int_foo_token_input_types() -> Box<[TypeId]> { int_foo_token().input_types() } + pub fn int_foo_token_return_type() -> &'static str { + int_foo_token().return_type() + } } }; @@ -1510,9 +1595,9 @@ mod generate_tests { } #[allow(unused_mut)] pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { - m.set_fn("square", FnNamespace::Internal, FnAccess::Public, Some(&["x: &mut u64"]), &[core::any::TypeId::of::()], + m.set_fn("square", FnNamespace::Internal, FnAccess::Public, Some(&["x: &mut u64", "u64"]), &[core::any::TypeId::of::()], int_foo_token().into()); - m.set_fn("get$square", FnNamespace::Internal, FnAccess::Public, Some(&["x: &mut u64"]), &[core::any::TypeId::of::()], + m.set_fn("get$square", FnNamespace::Internal, FnAccess::Public, Some(&["x: &mut u64", "u64"]), &[core::any::TypeId::of::()], int_foo_token().into()); if flatten {} else {} } @@ -1537,6 +1622,9 @@ mod generate_tests { fn input_types(&self) -> Box<[TypeId]> { new_vec![TypeId::of::()].into_boxed_slice() } + fn return_type(&self) -> &'static str { + "u64" + } } pub fn int_foo_token_callable() -> CallableFunction { int_foo_token().into() @@ -1547,6 +1635,9 @@ mod generate_tests { pub fn int_foo_token_input_types() -> Box<[TypeId]> { int_foo_token().input_types() } + pub fn int_foo_token_return_type() -> &'static str { + int_foo_token().return_type() + } } }; @@ -1581,7 +1672,7 @@ mod generate_tests { } #[allow(unused_mut)] pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { - m.set_fn("set$squared", FnNamespace::Internal, FnAccess::Public, Some(&["x: &mut u64", "y: u64"]), + m.set_fn("set$squared", FnNamespace::Internal, FnAccess::Public, Some(&["x: &mut u64", "y: u64", "()"]), &[core::any::TypeId::of::(), core::any::TypeId::of::()], int_foo_token().into()); if flatten {} else {} @@ -1608,6 +1699,9 @@ mod generate_tests { fn input_types(&self) -> Box<[TypeId]> { new_vec![TypeId::of::(), TypeId::of::()].into_boxed_slice() } + fn return_type(&self) -> &'static str { + "()" + } } pub fn int_foo_token_callable() -> CallableFunction { int_foo_token().into() @@ -1618,6 +1712,9 @@ mod generate_tests { pub fn int_foo_token_input_types() -> Box<[TypeId]> { int_foo_token().input_types() } + pub fn int_foo_token_return_type() -> &'static str { + int_foo_token().return_type() + } } }; @@ -1652,10 +1749,10 @@ mod generate_tests { } #[allow(unused_mut)] pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { - m.set_fn("set_sq", FnNamespace::Internal, FnAccess::Public, Some(&["x: &mut u64", "y: u64"]), + m.set_fn("set_sq", FnNamespace::Internal, FnAccess::Public, Some(&["x: &mut u64", "y: u64", "()"]), &[core::any::TypeId::of::(), core::any::TypeId::of::()], int_foo_token().into()); - m.set_fn("set$squared", FnNamespace::Internal, FnAccess::Public, Some(&["x: &mut u64", "y: u64"]), + m.set_fn("set$squared", FnNamespace::Internal, FnAccess::Public, Some(&["x: &mut u64", "y: u64", "()"]), &[core::any::TypeId::of::(), core::any::TypeId::of::()], int_foo_token().into()); if flatten {} else {} @@ -1682,6 +1779,9 @@ mod generate_tests { fn input_types(&self) -> Box<[TypeId]> { new_vec![TypeId::of::(), TypeId::of::()].into_boxed_slice() } + fn return_type(&self) -> &'static str { + "()" + } } pub fn int_foo_token_callable() -> CallableFunction { int_foo_token().into() @@ -1692,6 +1792,9 @@ mod generate_tests { pub fn int_foo_token_input_types() -> Box<[TypeId]> { int_foo_token().input_types() } + pub fn int_foo_token_return_type() -> &'static str { + int_foo_token().return_type() + } } }; @@ -1727,7 +1830,7 @@ mod generate_tests { #[allow(unused_mut)] pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { m.set_fn("index$get$", FnNamespace::Internal, FnAccess::Public, - Some(&["x: &mut MyCollection", "i: u64"]), + Some(&["x: &mut MyCollection", "i: u64", "FLOAT"]), &[core::any::TypeId::of::(), core::any::TypeId::of::()], get_by_index_token().into()); @@ -1756,6 +1859,9 @@ mod generate_tests { new_vec![TypeId::of::(), TypeId::of::()].into_boxed_slice() } + fn return_type(&self) -> &'static str { + "FLOAT" + } } pub fn get_by_index_token_callable() -> CallableFunction { get_by_index_token().into() @@ -1766,6 +1872,9 @@ mod generate_tests { pub fn get_by_index_token_input_types() -> Box<[TypeId]> { get_by_index_token().input_types() } + pub fn get_by_index_token_return_type() -> &'static str { + get_by_index_token().return_type() + } } }; @@ -1801,12 +1910,12 @@ mod generate_tests { #[allow(unused_mut)] pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { m.set_fn("get", FnNamespace::Internal, FnAccess::Public, - Some(&["x: &mut MyCollection", "i: u64"]), + Some(&["x: &mut MyCollection", "i: u64", "FLOAT"]), &[core::any::TypeId::of::(), core::any::TypeId::of::()], get_by_index_token().into()); m.set_fn("index$get$", FnNamespace::Internal, FnAccess::Public, - Some(&["x: &mut MyCollection", "i: u64"]), + Some(&["x: &mut MyCollection", "i: u64", "FLOAT"]), &[core::any::TypeId::of::(), core::any::TypeId::of::()], get_by_index_token().into()); @@ -1835,6 +1944,9 @@ mod generate_tests { new_vec![TypeId::of::(), TypeId::of::()].into_boxed_slice() } + fn return_type(&self) -> &'static str { + "FLOAT" + } } pub fn get_by_index_token_callable() -> CallableFunction { get_by_index_token().into() @@ -1845,6 +1957,9 @@ mod generate_tests { pub fn get_by_index_token_input_types() -> Box<[TypeId]> { get_by_index_token().input_types() } + pub fn get_by_index_token_return_type() -> &'static str { + get_by_index_token().return_type() + } } }; @@ -1880,7 +1995,7 @@ mod generate_tests { #[allow(unused_mut)] pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { m.set_fn("index$set$", FnNamespace::Internal, FnAccess::Public, - Some(&["x: &mut MyCollection", "i: u64", "item: FLOAT"]), + Some(&["x: &mut MyCollection", "i: u64", "item: FLOAT", "()"]), &[core::any::TypeId::of::(), core::any::TypeId::of::(), core::any::TypeId::of::()], @@ -1912,6 +2027,9 @@ mod generate_tests { TypeId::of::(), TypeId::of::()].into_boxed_slice() } + fn return_type(&self) -> &'static str { + "()" + } } pub fn set_by_index_token_callable() -> CallableFunction { set_by_index_token().into() @@ -1922,6 +2040,9 @@ mod generate_tests { pub fn set_by_index_token_input_types() -> Box<[TypeId]> { set_by_index_token().input_types() } + pub fn set_by_index_token_return_type() -> &'static str { + set_by_index_token().return_type() + } } }; @@ -1957,13 +2078,13 @@ mod generate_tests { #[allow(unused_mut)] pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { m.set_fn("set", FnNamespace::Internal, FnAccess::Public, - Some(&["x: &mut MyCollection", "i: u64", "item: FLOAT"]), + Some(&["x: &mut MyCollection", "i: u64", "item: FLOAT", "()"]), &[core::any::TypeId::of::(), core::any::TypeId::of::(), core::any::TypeId::of::()], set_by_index_token().into()); m.set_fn("index$set$", FnNamespace::Internal, FnAccess::Public, - Some(&["x: &mut MyCollection", "i: u64", "item: FLOAT"]), + Some(&["x: &mut MyCollection", "i: u64", "item: FLOAT", "()"]), &[core::any::TypeId::of::(), core::any::TypeId::of::(), core::any::TypeId::of::()], @@ -1995,6 +2116,9 @@ mod generate_tests { TypeId::of::(), TypeId::of::()].into_boxed_slice() } + fn return_type(&self) -> &'static str { + "()" + } } pub fn set_by_index_token_callable() -> CallableFunction { set_by_index_token().into() @@ -2005,6 +2129,9 @@ mod generate_tests { pub fn set_by_index_token_input_types() -> Box<[TypeId]> { set_by_index_token().input_types() } + pub fn set_by_index_token_return_type() -> &'static str { + set_by_index_token().return_type() + } } }; diff --git a/doc/src/SUMMARY.md b/doc/src/SUMMARY.md index e6a4da84..891b760f 100644 --- a/doc/src/SUMMARY.md +++ b/doc/src/SUMMARY.md @@ -135,6 +135,7 @@ The Rhai Scripting Language 2. [Custom Operators](engine/custom-op.md) 3. [Extending with Custom Syntax](engine/custom-syntax.md) 5. [Multiple Instantiation](patterns/multiple.md) + 6. [Get Function Signatures](engine/get_fn_sig.md) 10. [Appendix](appendix/index.md) 1. [Keywords](appendix/keywords.md) 2. [Operators and Symbols](appendix/operators.md) diff --git a/doc/src/engine/get_fn_sig.md b/doc/src/engine/get_fn_sig.md new file mode 100644 index 00000000..69894633 --- /dev/null +++ b/doc/src/engine/get_fn_sig.md @@ -0,0 +1,87 @@ +Get Function Signatures +======================= + +{{#include ../links.md}} + + +`Engine::gen_fn_signatures` +-------------------------- + +As part of a _reflections_ API, `Engine::gen_fn_signatures` returns a list of function signatures +(`Vec`), each corresponding to a particular function available to that [`Engine`] instance. + +Functions from the following sources are included, in order: + +1) Functions registered into the global namespace via the `Engine::register_XXX` API, +2) Functions in global sub-modules registered via [`Engine::register_module`]({{rootUrl}}/rust/modules/create.md), +3) Functions in registered [packages] (optional) + +Included are both native Rust as well as script-defined functions (except [`private`] ones). + + +Function Metadata +----------------- + +Beware, however, that not all function signatures contain parameters and return value information. + +### `Engine::register_XXX` + +For instance, functions registered via `Engine::register_XXX` contain no information on +the names of parameter and their actual types because Rust simply does not make such metadata +available natively. The return type is also undetermined. + +A function registered under the name 'foo' with three parameters and unknown return type: + +> `foo(_, _, _)` + +An operator function - again, unknown parameters and return type. +Notice that function names do not need to be valid identifiers. + +> `+(_, _)` + +A [property setter][getters/setters] - again, unknown parameters and return type. +Notice that function names do not need to be valid identifiers. +In this case, the first parameter should be '&mut T' of the custom type and the return value is '()': + +> `set$prop(_, _, _)` + +### Script-defined functions + +Script-defined [function] signatures contain parameter names. Since all parameters, as well as +the return value, are [`Dynamic`] the types are simply not shown. + +A script-defined function always takes dynamic arguments, and the return type is also dynamic: + +> `foo(x, y, z)` + +probably defined as: + +```rust +fn foo(x, y, z) { + ... +} +``` + +is the same as: + +> `foo(x: Dynamic, y: Dynamic, z: Dynamic) -> Result>` + +### Plugin functions + +Functions defined in [plugin modules] are the best. They contain all the metadata +describing the functions. + +A plugin function `merge`: + +> `merge(list: &mut MyStruct, num: usize, name: &str) -> Option` + +Notice that function names do not need to be valid identifiers. + +An operator defined as a [fallible function] in a [plugin module] via `#[rhai_fn(name="+=", return_raw)]` +returns `Result>`: + +> `+=(list: &mut MyStruct, num: usize, name: &str) -> Result>` + +A [property getter][getters/setters] defined in a [plugin module]: + +> `get$prop(obj: &mut MyStruct) -> String` diff --git a/doc/src/language/functions.md b/doc/src/language/functions.md index acb3e33c..677deebc 100644 --- a/doc/src/language/functions.md +++ b/doc/src/language/functions.md @@ -101,21 +101,6 @@ 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. -`is_def_fn` ------------ - -Use `is_def_fn` to detect if a Rhai function is defined (and therefore callable), based on its name -and the number of parameters. - -```rust -fn foo(x) { x + 1 } - -is_def_fn("foo", 1) == true; - -is_def_fn("bar", 1) == false; -``` - - Arguments are Passed by Value ---------------------------- @@ -159,3 +144,43 @@ x == 42; // 'x' is changed! change(); // <- error: `this` is unbound ``` + + +`is_def_fn` +----------- + +Use `is_def_fn` to detect if a Rhai function is defined (and therefore callable), based on its name +and the number of parameters. + +```rust +fn foo(x) { x + 1 } + +is_def_fn("foo", 1) == true; + +is_def_fn("bar", 1) == false; +``` + + +Metadata +-------- + +The function `get_fn_metadata_list` is a _reflection_ API that returns an array of the metadata +of all script-defined functions in scope. + +Functions from the following sources are returned, in order: + +1) Encapsulated script environment (e.g. when loading a [module] from a script file), +2) Current script, +3) [Modules] imported via the [`import`] statement (latest imports first), +4) [Modules] added via [`Engine::register_module`]({{rootUrl}}/rust/modules/create.md) (latest registrations first) + +The return value is an [array] of [object maps] (so `get_fn_metadata_list` is not available under +[`no_index`] or [`no_object`]), containing the following fields: + +| Field | Type | Optional? | Description | +| -------------- | :------------------: | :-------: | ---------------------------------------------------------------------- | +| `namespace` | [string] | yes | the module _namespace_ if the function is defined within a module | +| `access` | [string] | no | `"public"` if the function is public,
`"private"` if it is private | +| `name` | [string] | no | function name | +| `params` | [array] of [strings] | no | parameter names | +| `is_anonymous` | `bool` | no | is this function an anonymous function? | diff --git a/doc/src/rust/modules/create.md b/doc/src/rust/modules/create.md index 937f85e9..6e1a1479 100644 --- a/doc/src/rust/modules/create.md +++ b/doc/src/rust/modules/create.md @@ -32,7 +32,13 @@ a module's functionalities to Rhai. use rhai::{Engine, Module}; let mut module = Module::new(); // new module -module.set_fn_1("inc", |x: i64| Ok(x+1)); // use the 'set_fn_XXX' API to add functions + +// Use the 'set_fn_XXX' API to add functions. +let hash = module.set_fn_1("inc", |x: i64| Ok(x+1)); + +// Remember to update the parameter names/types and return type metadata. +// 'set_fn_XXX' by default does not set function metadata. +module.update_fn_metadata(hash, ["x: i64", "i64"]); // Load the module into the Engine as a new package. let mut engine = Engine::new(); @@ -51,7 +57,13 @@ Make the `Module` a Global Module use rhai::{Engine, Module}; let mut module = Module::new(); // new module -module.set_fn_1("inc", |x: i64| Ok(x+1)); // use the 'set_fn_XXX' API to add functions + +// Use the 'set_fn_XXX' API to add functions. +let hash = module.set_fn_1("inc", |x: i64| Ok(x+1)); + +// Remember to update the parameter names/types and return type metadata. +// 'set_fn_XXX' by default does not set function metadata. +module.update_fn_metadata(hash, ["x: i64", "i64"]); // Load the module into the Engine as a sub-module named 'calc' let mut engine = Engine::new(); @@ -71,9 +83,11 @@ use rhai::{Engine, Module, FnNamespace}; let mut module = Module::new(); // new module // Expose method 'inc' to the global namespace (default is 'Internal') -module.set_fn_1_mut("inc", FnNamespace::Global, - |x: &mut i64| Ok(x+1) -); +let hash = module.set_fn_1_mut("inc", FnNamespace::Global, |x: &mut i64| Ok(x+1)); + +// Remember to update the parameter names/types and return type metadata. +// 'set_fn_XXX' by default does not set function metadata. +module.update_fn_metadata(hash, ["x: &mut i64", "i64"]); // Load the module into the Engine as a sub-module named 'calc' let mut engine = Engine::new(); diff --git a/doc/src/rust/packages/create.md b/doc/src/rust/packages/create.md index 04911dd4..ec573804 100644 --- a/doc/src/rust/packages/create.md +++ b/doc/src/rust/packages/create.md @@ -50,9 +50,13 @@ def_package!(rhai:MyPackage:"My own personal super package", module, { BasicMapPackage::init(module); // Register additional Rust functions using the standard 'set_fn_XXX' module API. - module.set_fn_1("foo", |s: ImmutableString| { + let hash = module.set_fn_1("foo", |s: ImmutableString| { Ok(foo(s.into_owned())) }); + + // Remember to update the parameter names/types and return type metadata. + // 'set_fn_XXX' by default does not set function metadata. + module.update_fn_metadata(hash, ["s: ImmutableString", "i64"]); }); ``` diff --git a/src/ast.rs b/src/ast.rs index e89fa446..cc5a79be 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -89,7 +89,7 @@ impl fmt::Display for ScriptFnDef { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, - "{}{}({})", + "{}{}({}) -> Dynamic", if self.access.is_private() { "private " } else { @@ -151,6 +151,7 @@ impl AST { &mut self.0 } /// Get the internal shared [`Module`] containing all script-defined functions. + #[cfg(not(feature = "no_function"))] #[inline(always)] pub(crate) fn shared_lib(&self) -> Shared { self.1.clone() diff --git a/src/engine.rs b/src/engine.rs index 1a8afa66..a6ad78d1 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -97,11 +97,11 @@ impl Imports { } /// Get an iterator to this stack of imported modules in reverse order. #[allow(dead_code)] - pub fn iter(&self) -> impl Iterator)> { + pub fn iter<'a>(&'a self) -> impl Iterator)> + 'a { self.0.iter().flat_map(|lib| { lib.iter() .rev() - .map(|(name, module)| (name.as_str(), module.clone())) + .map(|(name, module)| (name.clone(), module.clone())) }) } /// Get an iterator to this stack of imported modules in reverse order. diff --git a/src/engine_api.rs b/src/engine_api.rs index b801a33c..b3227c58 100644 --- a/src/engine_api.rs +++ b/src/engine_api.rs @@ -1650,10 +1650,10 @@ impl Engine { } /// Generate a list of all registered functions. /// - /// The ordering is: + /// Functions from the following sources are included, in order: /// 1) Functions registered into the global namespace /// 2) Functions in registered sub-modules - /// 3) Functions in packages + /// 3) Functions in packages (optional) pub fn gen_fn_signatures(&self, include_packages: bool) -> Vec { let mut signatures: Vec<_> = Default::default(); diff --git a/src/module/mod.rs b/src/module/mod.rs index c4d5147b..78e3628c 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -9,7 +9,7 @@ use crate::stdlib::{ boxed::Box, collections::HashMap, fmt, format, - iter::empty, + iter::{empty, once}, num::NonZeroUsize, ops::{Add, AddAssign, Deref, DerefMut}, string::{String, ToString}, @@ -90,8 +90,15 @@ impl FuncInfo { let mut sig = format!("{}(", self.name); if let Some(ref names) = self.param_names { - let params: Vec<_> = names.iter().map(ImmutableString::to_string).collect(); + let mut params: Vec<_> = names.iter().map(ImmutableString::to_string).collect(); + let return_type = params.pop().unwrap_or_else(|| "()".to_string()); sig.push_str(¶ms.join(", ")); + if return_type != "()" { + sig.push_str(") -> "); + sig.push_str(&return_type); + } else { + sig.push_str(")"); + } } else { for x in 0..self.params { sig.push_str("_"); @@ -99,9 +106,9 @@ impl FuncInfo { sig.push_str(", "); } } + sig.push_str(") -> Dynamic"); } - sig.push_str(")"); sig } } @@ -362,7 +369,14 @@ impl Module { access: fn_def.access, params: num_params, param_types: None, - param_names: Some(fn_def.params.clone()), + param_names: Some( + fn_def + .params + .iter() + .cloned() + .chain(once("Dynamic".into())) + .collect(), + ), func: fn_def.into(), }, ); @@ -483,13 +497,22 @@ impl Module { } } - /// Update the parameter names and types in a registered function. + /// Update the metadata (parameter names/types and return type) of a registered function. /// /// The [`u64`] hash is calculated either by the function [`crate::calc_native_fn_hash`] or /// the function [`crate::calc_script_fn_hash`]. - pub fn update_fn_param_names(&mut self, hash_fn: u64, arg_names: &[&str]) -> &mut Self { + /// + /// Each parameter name/type pair should be a single string of the format: `var_name: type`. + /// + /// The last entry in the list should be the return type of the function. + /// In other words, the number of entries should be one larger than the number of parameters. + pub fn update_fn_metadata<'a>( + &mut self, + hash_fn: u64, + arg_names: impl AsRef<[&'a str]>, + ) -> &mut Self { if let Some(f) = self.functions.get_mut(&hash_fn) { - f.param_names = Some(arg_names.iter().map(|&n| n.into()).collect()); + f.param_names = Some(arg_names.as_ref().iter().map(|&n| n.into()).collect()); } self } @@ -573,6 +596,10 @@ impl Module { /// /// To access the first mutable parameter, use `args.get_mut(0).unwrap()` /// + /// # Function Metadata + /// + /// No metadata for the function is registered. Use `update_fn_metadata` to add metadata. + /// /// # Example /// /// ``` @@ -632,6 +659,10 @@ impl Module { /// /// If there is a similar existing Rust function, it is replaced. /// + /// # Function Metadata + /// + /// No metadata for the function is registered. Use `update_fn_metadata` to add metadata. + /// /// # Example /// /// ``` @@ -663,6 +694,10 @@ impl Module { /// /// If there is a similar existing Rust function, it is replaced. /// + /// # Function Metadata + /// + /// No metadata for the function is registered. Use `update_fn_metadata` to add metadata. + /// /// # Example /// /// ``` @@ -696,6 +731,10 @@ impl Module { /// /// If there is a similar existing Rust function, it is replaced. /// + /// # Function Metadata + /// + /// No metadata for the function is registered. Use `update_fn_metadata` to add metadata. + /// /// # Example /// /// ``` @@ -733,6 +772,10 @@ impl Module { /// /// If there is a similar existing Rust getter function, it is replaced. /// + /// # Function Metadata + /// + /// No metadata for the function is registered. Use `update_fn_metadata` to add metadata. + /// /// # Example /// /// ``` @@ -760,6 +803,10 @@ impl Module { /// /// If there is a similar existing Rust function, it is replaced. /// + /// # Function Metadata + /// + /// No metadata for the function is registered. Use `update_fn_metadata` to add metadata. + /// /// # Example /// /// ``` @@ -799,6 +846,10 @@ impl Module { /// /// If there is a similar existing Rust function, it is replaced. /// + /// # Function Metadata + /// + /// No metadata for the function is registered. Use `update_fn_metadata` to add metadata. + /// /// # Example /// /// ``` @@ -843,6 +894,10 @@ impl Module { /// /// If there is a similar existing setter Rust function, it is replaced. /// + /// # Function Metadata + /// + /// No metadata for the function is registered. Use `update_fn_metadata` to add metadata. + /// /// # Example /// /// ``` @@ -880,6 +935,10 @@ impl Module { /// Panics if the type is [`Array`] or [`Map`]. /// Indexers for arrays, object maps and strings cannot be registered. /// + /// # Function Metadata + /// + /// No metadata for the function is registered. Use `update_fn_metadata` to add metadata. + /// /// # Example /// /// ``` @@ -918,6 +977,10 @@ impl Module { /// /// If there is a similar existing Rust function, it is replaced. /// + /// # Function Metadata + /// + /// No metadata for the function is registered. Use `update_fn_metadata` to add metadata. + /// /// # Example /// /// ``` @@ -963,6 +1026,10 @@ impl Module { /// /// If there is a similar existing Rust function, it is replaced. /// + /// # Function Metadata + /// + /// No metadata for the function is registered. Use `update_fn_metadata` to add metadata. + /// /// # Example /// /// ``` @@ -1018,6 +1085,10 @@ impl Module { /// Panics if the type is [`Array`] or [`Map`]. /// Indexers for arrays, object maps and strings cannot be registered. /// + /// # Function Metadata + /// + /// No metadata for the function is registered. Use `update_fn_metadata` to add metadata. + /// /// # Example /// /// ``` @@ -1079,6 +1150,10 @@ impl Module { /// Panics if the type is [`Array`] or [`Map`]. /// Indexers for arrays, object maps and strings cannot be registered. /// + /// # Function Metadata + /// + /// No metadata for the function is registered. Use `update_fn_metadata` to add metadata. + /// /// # Example /// /// ``` @@ -1114,6 +1189,10 @@ impl Module { /// /// If there is a similar existing Rust function, it is replaced. /// + /// # Function Metadata + /// + /// No metadata for the function is registered. Use `update_fn_metadata` to add metadata. + /// /// # Example /// /// ``` @@ -1166,6 +1245,10 @@ impl Module { /// /// If there is a similar existing Rust function, it is replaced. /// + /// # Function Metadata + /// + /// No metadata for the function is registered. Use `update_fn_metadata` to add metadata. + /// /// # Example /// /// ``` @@ -1417,10 +1500,16 @@ impl Module { ) } + /// Get an iterator to the sub-modules in the module. + #[inline(always)] + pub fn iter_sub_modules(&self) -> impl Iterator)> { + self.modules.iter().map(|(k, m)| (k.as_str(), m.clone())) + } + /// Get an iterator to the variables in the module. #[inline(always)] - pub fn iter_var(&self) -> impl Iterator { - self.variables.iter() + pub fn iter_var(&self) -> impl Iterator { + self.variables.iter().map(|(k, v)| (k.as_str(), v)) } /// Get an iterator to the functions in the module. diff --git a/src/packages/fn_basic.rs b/src/packages/fn_basic.rs index 630c58d9..9402413c 100644 --- a/src/packages/fn_basic.rs +++ b/src/packages/fn_basic.rs @@ -1,5 +1,11 @@ use crate::plugin::*; -use crate::{def_package, FnPtr}; +use crate::stdlib::iter::empty; +use crate::{calc_script_fn_hash, def_package, FnPtr, ImmutableString, NativeCallContext, INT}; + +#[cfg(not(feature = "no_function"))] +#[cfg(not(feature = "no_index"))] +#[cfg(not(feature = "no_object"))] +use crate::{module::SharedScriptFnDef, stdlib::collections::HashMap, Array, Map}; def_package!(crate:BasicFnPackage:"Basic Fn functions.", lib, { combine_with_exported_module!(lib, "FnPtr", fn_ptr_functions); @@ -13,10 +19,117 @@ mod fn_ptr_functions { } #[cfg(not(feature = "no_function"))] - pub mod anonymous { + pub mod functions { #[rhai_fn(name = "is_anonymous", get = "is_anonymous")] pub fn is_anonymous(f: &mut FnPtr) -> bool { f.is_anonymous() } + + pub fn is_def_fn(ctx: NativeCallContext, fn_name: &str, num_params: INT) -> bool { + if num_params < 0 { + false + } else { + let hash_script = calc_script_fn_hash(empty(), fn_name, num_params as usize); + ctx.engine() + .has_override(ctx.mods, ctx.lib, 0, hash_script, true) + } + } + } + + #[cfg(not(feature = "no_function"))] + #[cfg(not(feature = "no_index"))] + #[cfg(not(feature = "no_object"))] + pub mod functions_and_maps { + pub fn get_fn_metadata_list(ctx: NativeCallContext) -> Array { + collect_fn_metadata(ctx) + } } } + +#[cfg(not(feature = "no_function"))] +#[cfg(not(feature = "no_index"))] +#[cfg(not(feature = "no_object"))] +fn collect_fn_metadata(ctx: NativeCallContext) -> Array { + // Create a metadata record for a function. + fn make_metadata( + dict: &HashMap<&str, ImmutableString>, + namespace: Option, + f: SharedScriptFnDef, + ) -> Map { + let mut map = Map::with_capacity(6); + + if let Some(ns) = namespace { + map.insert(dict["namespace"].clone(), ns.into()); + } + map.insert(dict["name"].clone(), f.name.clone().into()); + map.insert( + dict["access"].clone(), + match f.access { + FnAccess::Public => dict["public"].clone(), + FnAccess::Private => dict["private"].clone(), + } + .into(), + ); + map.insert( + dict["is_anonymous"].clone(), + f.name.starts_with(crate::engine::FN_ANONYMOUS).into(), + ); + map.insert( + dict["params"].clone(), + f.params + .iter() + .cloned() + .map(Into::::into) + .collect::() + .into(), + ); + + map.into() + } + + // Recursively scan modules for script-defined functions. + fn scan_module( + list: &mut Array, + dict: &HashMap<&str, ImmutableString>, + namespace: ImmutableString, + module: &Module, + ) { + module.iter_script_fn().for_each(|(_, _, _, _, f)| { + list.push(make_metadata(dict, Some(namespace.clone()), f).into()) + }); + module.iter_sub_modules().for_each(|(ns, m)| { + let ns: ImmutableString = format!("{}::{}", namespace, ns).into(); + scan_module(list, dict, ns, m.as_ref()) + }); + } + + // Intern strings + let mut dict = HashMap::<&str, ImmutableString>::with_capacity(8); + [ + "namespace", + "name", + "access", + "public", + "private", + "is_anonymous", + "params", + ] + .iter() + .for_each(|&s| { + dict.insert(s, s.into()); + }); + + let mut list: Array = Default::default(); + + ctx.lib + .iter() + .flat_map(|m| m.iter_script_fn()) + .for_each(|(_, _, _, _, f)| list.push(make_metadata(&dict, None, f).into())); + + if let Some(mods) = ctx.mods { + mods.iter() + .for_each(|(ns, m)| scan_module(&mut list, &dict, ns, m.as_ref())); + } + + list +} diff --git a/src/packages/iter_basic.rs b/src/packages/iter_basic.rs index 97ff0f59..4ae548e2 100644 --- a/src/packages/iter_basic.rs +++ b/src/packages/iter_basic.rs @@ -49,7 +49,11 @@ macro_rules! reg_range { $( $lib.set_iterator::>(); let hash = $lib.set_fn_2($x, get_range::<$y>); - $lib.update_fn_param_names(hash, &[concat!("from: ", stringify!($y)), concat!("to: ", stringify!($y))]); + $lib.update_fn_metadata(hash, [ + concat!("from: ", stringify!($y)), + concat!("to: ", stringify!($y)), + concat!("Iterator") + ]); )* ) } @@ -61,7 +65,11 @@ macro_rules! reg_step { $( $lib.set_iterator::>(); let hash = $lib.set_fn_3($x, get_step_range::<$y>); - $lib.update_fn_param_names(hash, &[concat!("from: ", stringify!($y)), concat!("to: ", stringify!($y)), concat!("step: ", stringify!($y))]); + $lib.update_fn_metadata(hash, [ + concat!("from: ", stringify!($y)), + concat!("to: ", stringify!($y)), + concat!("step: ", stringify!($y)), concat!("Iterator") + ]); )* ) } @@ -69,7 +77,7 @@ macro_rules! reg_step { def_package!(crate:BasicIteratorPackage:"Basic range iterators.", lib, { lib.set_iterator::>(); let hash = lib.set_fn_2("range", get_range::); - lib.update_fn_param_names(hash, &["from: INT", "to: INT"]); + lib.update_fn_metadata(hash, ["from: INT", "to: INT", "Iterator"]); #[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i64"))] @@ -83,7 +91,7 @@ def_package!(crate:BasicIteratorPackage:"Basic range iterators.", lib, { lib.set_iterator::>(); let hash = lib.set_fn_3("range", get_step_range::); - lib.update_fn_param_names(hash, &["from: INT", "to: INT", "step: INT"]); + lib.update_fn_metadata(hash, ["from: INT", "to: INT", "step: INT", "Iterator"]); #[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i64"))] diff --git a/src/packages/pkg_core.rs b/src/packages/pkg_core.rs index 7480fdd1..284c5997 100644 --- a/src/packages/pkg_core.rs +++ b/src/packages/pkg_core.rs @@ -4,34 +4,9 @@ use super::iter_basic::BasicIteratorPackage; use super::logic::LogicPackage; use super::string_basic::BasicStringPackage; -use crate::fn_native::{CallableFunction, FnCallArgs}; -use crate::stdlib::{any::TypeId, boxed::Box, iter::empty}; -use crate::{ - calc_script_fn_hash, def_package, FnAccess, FnNamespace, ImmutableString, NativeCallContext, - INT, -}; +use crate::def_package; def_package!(crate:CorePackage:"_Core_ package containing basic facilities.", lib, { - #[cfg(not(feature = "no_function"))] - { - let f = |ctx: NativeCallContext, args: &mut FnCallArgs| { - let num_params = args[1].clone().cast::(); - let fn_name = args[0].as_str().unwrap(); - - Ok(if num_params < 0 { - false.into() - } else { - let hash_script = calc_script_fn_hash(empty(), fn_name, num_params as usize); - ctx.engine().has_override(ctx.mods, ctx.lib, 0, hash_script, true).into() - }) - }; - - lib.set_fn("is_def_fn", FnNamespace::Global, FnAccess::Public, - Some(&["fn_name: &str", "num_params: INT"]), - &[TypeId::of::(), TypeId::of::()], - CallableFunction::from_method(Box::new(f))); - } - ArithmeticPackage::init(lib); LogicPackage::init(lib); BasicStringPackage::init(lib); diff --git a/src/plugin.rs b/src/plugin.rs index 91385628..d07cc188 100644 --- a/src/plugin.rs +++ b/src/plugin.rs @@ -38,4 +38,7 @@ pub trait PluginFunction { /// Return a boxed slice of type ID's of the function's parameters. fn input_types(&self) -> Box<[TypeId]>; + + /// Return a string slice of the function's return type. + fn return_type(&self) -> &'static str; }