diff --git a/RELEASES.md b/RELEASES.md index 700a792f..3dcca24e 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -10,6 +10,9 @@ It also allows exposing selected module functions (usually methods) to the globa This is very convenient when encapsulating the API of a custom Rust type into a module while having methods and iterators registered on the custom type work normally. +A new `gen_fn_signatures` API enables enumerating the registered functions of an `Engine` for documentation purposes. +It also prepares the way for a future reflection API. + Bug fixes --------- @@ -19,6 +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::get_sub_module_mut` is removed. * `begin`, `end`, `unless` are now reserved keywords. * `EvalPackage` is removed in favor of `Engine::disable_symbol`. @@ -26,13 +30,15 @@ Breaking changes New features ------------ -* `switch` statement. -* `do ... while` and `do ... until` statement. -* `Engine::register_module` to register a module as a sub-module in the global namespace. -* `set_exported_global_fn!` macro to register a plugin function and expose it to the global namespace. +* New `switch` statement. +* New `do ... while` and `do ... until` statements. +* New `Engine::gen_fn_signatures`, `Module::gen_fn_signatures` and `PackagesCollection::gen_fn_signatures` to generate a list of signatures for functions registered. +* New `Engine::register_module` to register a module as a sub-module in the global namespace. +* 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. -* `#[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 `Module::update_fn_param_names` 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. Enhancements ------------ diff --git a/codegen/src/function.rs b/codegen/src/function.rs index f1d18fcd..5f976fd9 100644 --- a/codegen/src/function.rs +++ b/codegen/src/function.rs @@ -12,7 +12,7 @@ use std::format; use std::borrow::Cow; -use quote::{quote, quote_spanned}; +use quote::{quote, quote_spanned, ToTokens}; use syn::{ parse::{Parse, ParseStream, Parser}, spanned::Spanned, @@ -83,6 +83,10 @@ 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("& ", "&") +} + #[derive(Debug, Default)] pub(crate) struct ExportedFnParams { pub name: Option>, @@ -576,6 +580,7 @@ impl ExportedFn { syn::Ident::new(&format!("rhai_fn_{}", self.name()), self.name().span()); let impl_block = self.generate_impl("Token"); 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 dyn_result_fn_block = self.generate_dynamic_fn(); quote! { @@ -585,6 +590,7 @@ impl ExportedFn { struct Token(); #impl_block #callable_block + #input_names_block #input_types_block #dyn_result_fn_block } @@ -655,6 +661,19 @@ impl ExportedFn { } } + pub fn generate_input_names(&self, on_type_name: &str) -> proc_macro2::TokenStream { + let token_name: syn::Ident = syn::Ident::new(on_type_name, self.name().span()); + let input_names_fn_name: syn::Ident = syn::Ident::new( + format!("{}_input_names", on_type_name.to_lowercase()).as_str(), + self.name().span(), + ); + quote! { + pub fn #input_names_fn_name() -> Box<[&'static str]> { + #token_name().input_names() + } + } + } + pub fn generate_input_types(&self, on_type_name: &str) -> proc_macro2::TokenStream { let token_name: syn::Ident = syn::Ident::new(on_type_name, self.name().span()); let input_types_fn_name: syn::Ident = syn::Ident::new( @@ -680,6 +699,7 @@ impl ExportedFn { let mut unpack_stmts: Vec = Vec::new(); let mut unpack_exprs: Vec = Vec::new(); + let mut input_type_names: Vec = Vec::new(); let mut input_type_exprs: Vec = Vec::new(); let skip_first_arg; @@ -693,8 +713,9 @@ impl ExportedFn { let first_arg = self.arg_list().next().unwrap(); let var = syn::Ident::new("arg0", proc_macro2::Span::call_site()); match first_arg { - syn::FnArg::Typed(pattern) => { - let arg_type = match flatten_type_groups(pattern.ty.as_ref()) { + syn::FnArg::Typed(syn::PatType { pat, ty, .. }) => { + let arg_name = format!("{}: {}", pat.to_token_stream(), print_type(ty)); + let arg_type = match flatten_type_groups(ty.as_ref()) { syn::Type::Reference(syn::TypeReference { ref elem, .. }) => elem.as_ref(), p => p, }; @@ -706,6 +727,7 @@ impl ExportedFn { }) .unwrap(), ); + input_type_names.push(arg_name); input_type_exprs.push( syn::parse2::(quote_spanned!( arg_type.span()=> TypeId::of::<#arg_type>() @@ -731,9 +753,10 @@ impl ExportedFn { let is_string; let is_ref; match arg { - syn::FnArg::Typed(pattern) => { - let arg_type = pattern.ty.as_ref(); - let downcast_span = match flatten_type_groups(pattern.ty.as_ref()) { + syn::FnArg::Typed(syn::PatType { pat, ty, .. }) => { + let arg_name = format!("{}: {}", pat.to_token_stream(), print_type(ty)); + let arg_type = ty.as_ref(); + let downcast_span = match flatten_type_groups(arg_type) { syn::Type::Reference(syn::TypeReference { mutability: None, ref elem, @@ -767,6 +790,7 @@ impl ExportedFn { }) .unwrap(), ); + input_type_names.push(arg_name); if !is_string { input_type_exprs.push( syn::parse2::(quote_spanned!( @@ -837,6 +861,9 @@ impl ExportedFn { fn is_method_call(&self) -> bool { #is_method_call } fn is_variadic(&self) -> bool { false } fn clone_boxed(&self) -> Box { Box::new(#type_name()) } + fn input_names(&self) -> Box<[&'static str]> { + new_vec![#(#input_type_names),*].into_boxed_slice() + } 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 065a9461..38ee893a 100644 --- a/codegen/src/lib.rs +++ b/codegen/src/lib.rs @@ -340,6 +340,7 @@ pub fn set_exported_fn(args: proc_macro::TokenStream) -> proc_macro::TokenStream let gen_mod_path = crate::register::generated_module_path(&rust_modpath); let tokens = quote! { #module_expr.set_fn(#export_name, FnNamespace::Internal, FnAccess::Public, + Some(#gen_mod_path::token_input_names().as_ref()), #gen_mod_path::token_input_types().as_ref(), #gen_mod_path::token_callable()); }; @@ -381,6 +382,7 @@ pub fn set_exported_global_fn(args: proc_macro::TokenStream) -> proc_macro::Toke let gen_mod_path = crate::register::generated_module_path(&rust_modpath); let tokens = quote! { #module_expr.set_fn(#export_name, FnNamespace::Global, FnAccess::Public, + Some(#gen_mod_path::token_input_names().as_ref()), #gen_mod_path::token_input_types().as_ref(), #gen_mod_path::token_callable()); }; diff --git a/codegen/src/rhai_module.rs b/codegen/src/rhai_module.rs index 0d8cd0ba..129089b0 100644 --- a/codegen/src/rhai_module.rs +++ b/codegen/src/rhai_module.rs @@ -3,8 +3,9 @@ use std::collections::HashMap; use quote::{quote, ToTokens}; use crate::attrs::ExportScope; -use crate::function::flatten_type_groups; -use crate::function::{ExportedFn, FnNamespaceAccess, FnSpecialAccess}; +use crate::function::{ + flatten_type_groups, print_type, ExportedFn, FnNamespaceAccess, FnSpecialAccess, +}; use crate::module::Module; pub(crate) type ExportedConst = (String, Box, syn::Expr); @@ -82,6 +83,16 @@ pub(crate) fn generate_body( let reg_names = function.exported_names(); let mut namespace = FnNamespaceAccess::Internal; + let fn_input_names: Vec = function + .arg_list() + .map(|fnarg| match fnarg { + syn::FnArg::Receiver(_) => panic!("internal error: receiver fn outside impl!?"), + syn::FnArg::Typed(syn::PatType { pat, ty, .. }) => { + format!("{}: {}", pat.to_token_stream(), print_type(ty)) + } + }) + .collect(); + let fn_input_types: Vec = function .arg_list() .map(|fnarg| match fnarg { @@ -117,6 +128,7 @@ pub(crate) fn generate_body( }, t => t.clone(), }; + syn::parse2::(quote! { core::any::TypeId::of::<#arg_type>()}) .unwrap() @@ -138,7 +150,8 @@ pub(crate) fn generate_body( ); set_fn_stmts.push( syn::parse2::(quote! { - m.set_fn(#fn_literal, FnNamespace::#ns_str, FnAccess::Public, &[#(#fn_input_types),*], + m.set_fn(#fn_literal, FnNamespace::#ns_str, FnAccess::Public, + Some(&[#(#fn_input_names),*]), &[#(#fn_input_types),*], #fn_token_name().into()); }) .unwrap(), @@ -151,6 +164,7 @@ pub(crate) fn generate_body( }); gen_fn_tokens.push(function.generate_impl(&fn_token_name.to_string())); 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())); } @@ -195,9 +209,7 @@ pub(crate) fn check_rename_collisions(fns: &Vec) -> Result<(), syn:: .fold(name.to_string(), |mut argstr, fnarg| { let type_string: String = match fnarg { syn::FnArg::Receiver(_) => unimplemented!("receiver rhai_fns not implemented"), - syn::FnArg::Typed(syn::PatType { ref ty, .. }) => { - ty.as_ref().to_token_stream().to_string() - } + syn::FnArg::Typed(syn::PatType { ref ty, .. }) => print_type(ty), }; argstr.push('.'); argstr.push_str(&type_string); diff --git a/codegen/src/test/function.rs b/codegen/src/test/function.rs index 3cfdbf7b..c0e74229 100644 --- a/codegen/src/test/function.rs +++ b/codegen/src/test/function.rs @@ -286,6 +286,9 @@ mod generate_tests { fn is_method_call(&self) -> bool { false } fn is_variadic(&self) -> bool { false } fn clone_boxed(&self) -> Box { Box::new(Token()) } + fn input_names(&self) -> Box<[&'static str]> { + new_vec![].into_boxed_slice() + } fn input_types(&self) -> Box<[TypeId]> { new_vec![].into_boxed_slice() } @@ -293,6 +296,9 @@ mod generate_tests { pub fn token_callable() -> CallableFunction { Token().into() } + pub fn token_input_names() -> Box<[&'static str]> { + Token().input_names() + } pub fn token_input_types() -> Box<[TypeId]> { Token().input_types() } @@ -328,6 +334,9 @@ mod generate_tests { fn is_method_call(&self) -> bool { false } fn is_variadic(&self) -> bool { false } fn clone_boxed(&self) -> Box { Box::new(Token()) } + fn input_names(&self) -> Box<[&'static str]> { + new_vec!["x: usize"].into_boxed_slice() + } fn input_types(&self) -> Box<[TypeId]> { new_vec![TypeId::of::()].into_boxed_slice() } @@ -335,6 +344,9 @@ mod generate_tests { pub fn token_callable() -> CallableFunction { Token().into() } + pub fn token_input_names() -> Box<[&'static str]> { + Token().input_names() + } pub fn token_input_types() -> Box<[TypeId]> { Token().input_types() } @@ -370,6 +382,9 @@ mod generate_tests { fn is_method_call(&self) -> bool { false } fn is_variadic(&self) -> bool { false } fn clone_boxed(&self) -> Box { Box::new(Token()) } + fn input_names(&self) -> Box<[&'static str]> { + new_vec!["x: usize"].into_boxed_slice() + } fn input_types(&self) -> Box<[TypeId]> { new_vec![TypeId::of::()].into_boxed_slice() } @@ -377,6 +392,9 @@ mod generate_tests { pub fn token_callable() -> CallableFunction { Token().into() } + pub fn token_input_names() -> Box<[&'static str]> { + Token().input_names() + } pub fn token_input_types() -> Box<[TypeId]> { Token().input_types() } @@ -414,6 +432,9 @@ mod generate_tests { fn is_method_call(&self) -> bool { false } fn is_variadic(&self) -> bool { false } fn clone_boxed(&self) -> Box { Box::new(Token()) } + fn input_names(&self) -> Box<[&'static str]> { + new_vec![].into_boxed_slice() + } fn input_types(&self) -> Box<[TypeId]> { new_vec![].into_boxed_slice() } @@ -421,6 +442,9 @@ mod generate_tests { pub fn token_callable() -> CallableFunction { Token().into() } + pub fn token_input_names() -> Box<[&'static str]> { + Token().input_names() + } pub fn token_input_types() -> Box<[TypeId]> { Token().input_types() } @@ -452,6 +476,9 @@ mod generate_tests { fn is_method_call(&self) -> bool { false } fn is_variadic(&self) -> bool { false } fn clone_boxed(&self) -> Box { Box::new(MyType()) } + fn input_names(&self) -> Box<[&'static str]> { + new_vec!["x: usize"].into_boxed_slice() + } fn input_types(&self) -> Box<[TypeId]> { new_vec![TypeId::of::()].into_boxed_slice() } @@ -485,6 +512,9 @@ mod generate_tests { fn is_method_call(&self) -> bool { false } fn is_variadic(&self) -> bool { false } fn clone_boxed(&self) -> Box { Box::new(Token()) } + fn input_names(&self) -> Box<[&'static str]> { + new_vec!["x: usize", "y: usize"].into_boxed_slice() + } fn input_types(&self) -> Box<[TypeId]> { new_vec![TypeId::of::(), TypeId::of::()].into_boxed_slice() @@ -493,6 +523,9 @@ mod generate_tests { pub fn token_callable() -> CallableFunction { Token().into() } + pub fn token_input_names() -> Box<[&'static str]> { + Token().input_names() + } pub fn token_input_types() -> Box<[TypeId]> { Token().input_types() } @@ -529,6 +562,9 @@ mod generate_tests { fn is_method_call(&self) -> bool { true } fn is_variadic(&self) -> bool { false } fn clone_boxed(&self) -> Box { Box::new(Token()) } + fn input_names(&self) -> Box<[&'static str]> { + new_vec!["x: &mut usize", "y: usize"].into_boxed_slice() + } fn input_types(&self) -> Box<[TypeId]> { new_vec![TypeId::of::(), TypeId::of::()].into_boxed_slice() @@ -537,6 +573,9 @@ mod generate_tests { pub fn token_callable() -> CallableFunction { Token().into() } + pub fn token_input_names() -> Box<[&'static str]> { + Token().input_names() + } pub fn token_input_types() -> Box<[TypeId]> { Token().input_types() } @@ -573,6 +612,9 @@ mod generate_tests { fn is_method_call(&self) -> bool { false } fn is_variadic(&self) -> bool { false } fn clone_boxed(&self) -> Box { Box::new(Token()) } + fn input_names(&self) -> Box<[&'static str]> { + new_vec!["message: &str"].into_boxed_slice() + } fn input_types(&self) -> Box<[TypeId]> { new_vec![TypeId::of::()].into_boxed_slice() } @@ -580,6 +622,9 @@ mod generate_tests { pub fn token_callable() -> CallableFunction { Token().into() } + pub fn token_input_names() -> Box<[&'static str]> { + Token().input_names() + } 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 7e0f7eb0..8afdf134 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, &[], + m.set_fn("get_mystic_number", FnNamespace::Internal, FnAccess::Public, Some(&[]), &[], get_mystic_number_token().into()); if flatten {} else {} } @@ -315,6 +315,9 @@ mod generate_tests { fn clone_boxed(&self) -> Box { Box::new(get_mystic_number_token()) } + fn input_names(&self) -> Box<[&'static str]> { + new_vec![].into_boxed_slice() + } fn input_types(&self) -> Box<[TypeId]> { new_vec![].into_boxed_slice() } @@ -322,6 +325,9 @@ mod generate_tests { pub fn get_mystic_number_token_callable() -> CallableFunction { get_mystic_number_token().into() } + pub fn get_mystic_number_token_input_names() -> Box<[&'static str]> { + get_mystic_number_token().input_names() + } pub fn get_mystic_number_token_input_types() -> Box<[TypeId]> { get_mystic_number_token().input_types() } @@ -359,7 +365,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, &[core::any::TypeId::of::()], + m.set_fn("add_one_to", FnNamespace::Global, FnAccess::Public, Some(&["x: INT"]), &[core::any::TypeId::of::()], add_one_to_token().into()); if flatten {} else {} } @@ -378,6 +384,9 @@ mod generate_tests { fn clone_boxed(&self) -> Box { Box::new(add_one_to_token()) } + fn input_names(&self) -> Box<[&'static str]> { + new_vec!["x: INT"].into_boxed_slice() + } fn input_types(&self) -> Box<[TypeId]> { new_vec![TypeId::of::()].into_boxed_slice() } @@ -385,6 +394,9 @@ mod generate_tests { pub fn add_one_to_token_callable() -> CallableFunction { add_one_to_token().into() } + pub fn add_one_to_token_input_names() -> Box<[&'static str]> { + add_one_to_token().input_names() + } pub fn add_one_to_token_input_types() -> Box<[TypeId]> { add_one_to_token().input_types() } @@ -421,7 +433,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, &[core::any::TypeId::of::()], + m.set_fn("add_one_to", FnNamespace::Internal, FnAccess::Public, Some(&["x: INT"]), &[core::any::TypeId::of::()], add_one_to_token().into()); if flatten {} else {} } @@ -440,6 +452,9 @@ mod generate_tests { fn clone_boxed(&self) -> Box { Box::new(add_one_to_token()) } + fn input_names(&self) -> Box<[&'static str]> { + new_vec!["x: INT"].into_boxed_slice() + } fn input_types(&self) -> Box<[TypeId]> { new_vec![TypeId::of::()].into_boxed_slice() } @@ -447,6 +462,9 @@ mod generate_tests { pub fn add_one_to_token_callable() -> CallableFunction { add_one_to_token().into() } + pub fn add_one_to_token_input_names() -> Box<[&'static str]> { + add_one_to_token().input_names() + } pub fn add_one_to_token_input_types() -> Box<[TypeId]> { add_one_to_token().input_types() } @@ -494,10 +512,10 @@ 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, &[core::any::TypeId::of::()], + m.set_fn("add_n", FnNamespace::Internal, FnAccess::Public, Some(&["x: INT"]), &[core::any::TypeId::of::()], add_one_to_token().into()); - m.set_fn("add_n", FnNamespace::Internal, FnAccess::Public, &[core::any::TypeId::of::(), - core::any::TypeId::of::()], + m.set_fn("add_n", FnNamespace::Internal, FnAccess::Public, Some(&["x: INT", "y: INT"]), + &[core::any::TypeId::of::(), core::any::TypeId::of::()], add_n_to_token().into()); if flatten {} else {} } @@ -516,6 +534,9 @@ mod generate_tests { fn clone_boxed(&self) -> Box { Box::new(add_one_to_token()) } + fn input_names(&self) -> Box<[&'static str]> { + new_vec!["x: INT"].into_boxed_slice() + } fn input_types(&self) -> Box<[TypeId]> { new_vec![TypeId::of::()].into_boxed_slice() } @@ -523,6 +544,9 @@ mod generate_tests { pub fn add_one_to_token_callable() -> CallableFunction { add_one_to_token().into() } + pub fn add_one_to_token_input_names() -> Box<[&'static str]> { + add_one_to_token().input_names() + } pub fn add_one_to_token_input_types() -> Box<[TypeId]> { add_one_to_token().input_types() } @@ -543,6 +567,9 @@ mod generate_tests { fn clone_boxed(&self) -> Box { Box::new(add_n_to_token()) } + fn input_names(&self) -> Box<[&'static str]> { + new_vec!["x: INT", "y: INT"].into_boxed_slice() + } fn input_types(&self) -> Box<[TypeId]> { new_vec![TypeId::of::(), TypeId::of::()].into_boxed_slice() @@ -551,6 +578,9 @@ mod generate_tests { pub fn add_n_to_token_callable() -> CallableFunction { add_n_to_token().into() } + pub fn add_n_to_token_input_names() -> Box<[&'static str]> { + add_n_to_token().input_names() + } pub fn add_n_to_token_input_types() -> Box<[TypeId]> { add_n_to_token().input_types() } @@ -587,8 +617,8 @@ 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, &[core::any::TypeId::of::(), - core::any::TypeId::of::()], + m.set_fn("add_together", FnNamespace::Internal, FnAccess::Public, Some(&["x: INT", "y: INT"]), + &[core::any::TypeId::of::(), core::any::TypeId::of::()], add_together_token().into()); if flatten {} else {} } @@ -608,6 +638,9 @@ mod generate_tests { fn clone_boxed(&self) -> Box { Box::new(add_together_token()) } + fn input_names(&self) -> Box<[&'static str]> { + new_vec!["x: INT", "y: INT"].into_boxed_slice() + } fn input_types(&self) -> Box<[TypeId]> { new_vec![TypeId::of::(), TypeId::of::()].into_boxed_slice() @@ -616,6 +649,9 @@ mod generate_tests { pub fn add_together_token_callable() -> CallableFunction { add_together_token().into() } + pub fn add_together_token_input_names() -> Box<[&'static str]> { + add_together_token().input_names() + } pub fn add_together_token_input_types() -> Box<[TypeId]> { add_together_token().input_types() } @@ -653,14 +689,14 @@ 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, &[core::any::TypeId::of::(), - core::any::TypeId::of::()], + m.set_fn("add", FnNamespace::Internal, FnAccess::Public, Some(&["x: INT", "y: INT"]), + &[core::any::TypeId::of::(), core::any::TypeId::of::()], add_together_token().into()); - m.set_fn("+", FnNamespace::Internal, FnAccess::Public, &[core::any::TypeId::of::(), - core::any::TypeId::of::()], + m.set_fn("+", FnNamespace::Internal, FnAccess::Public, Some(&["x: INT", "y: INT"]), + &[core::any::TypeId::of::(), core::any::TypeId::of::()], add_together_token().into()); - m.set_fn("add_together", FnNamespace::Internal, FnAccess::Public, &[core::any::TypeId::of::(), - core::any::TypeId::of::()], + m.set_fn("add_together", FnNamespace::Internal, FnAccess::Public, Some(&["x: INT", "y: INT"]), + &[core::any::TypeId::of::(), core::any::TypeId::of::()], add_together_token().into()); if flatten {} else {} } @@ -680,6 +716,9 @@ mod generate_tests { fn clone_boxed(&self) -> Box { Box::new(add_together_token()) } + fn input_names(&self) -> Box<[&'static str]> { + new_vec!["x: INT", "y: INT"].into_boxed_slice() + } fn input_types(&self) -> Box<[TypeId]> { new_vec![TypeId::of::(), TypeId::of::()].into_boxed_slice() @@ -688,6 +727,9 @@ mod generate_tests { pub fn add_together_token_callable() -> CallableFunction { add_together_token().into() } + pub fn add_together_token_input_names() -> Box<[&'static str]> { + add_together_token().input_names() + } pub fn add_together_token_input_types() -> Box<[TypeId]> { add_together_token().input_types() } @@ -906,7 +948,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, &[], + m.set_fn("get_mystic_number", FnNamespace::Internal, FnAccess::Public, Some(&[]), &[], get_mystic_number_token().into()); if flatten {} else {} } @@ -924,6 +966,9 @@ mod generate_tests { fn clone_boxed(&self) -> Box { Box::new(get_mystic_number_token()) } + fn input_names(&self) -> Box<[&'static str]> { + new_vec![].into_boxed_slice() + } fn input_types(&self) -> Box<[TypeId]> { new_vec![].into_boxed_slice() } @@ -931,6 +976,9 @@ mod generate_tests { pub fn get_mystic_number_token_callable() -> CallableFunction { get_mystic_number_token().into() } + pub fn get_mystic_number_token_input_names() -> Box<[&'static str]> { + get_mystic_number_token().input_names() + } pub fn get_mystic_number_token_input_types() -> Box<[TypeId]> { get_mystic_number_token().input_types() } @@ -999,7 +1047,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, - &[core::any::TypeId::of::()], + Some(&["x: &str"]), &[core::any::TypeId::of::()], print_out_to_token().into()); if flatten {} else {} } @@ -1018,6 +1066,9 @@ mod generate_tests { fn clone_boxed(&self) -> Box { Box::new(print_out_to_token()) } + fn input_names(&self) -> Box<[&'static str]> { + new_vec!["x: &str"].into_boxed_slice() + } fn input_types(&self) -> Box<[TypeId]> { new_vec![TypeId::of::()].into_boxed_slice() } @@ -1025,6 +1076,9 @@ mod generate_tests { pub fn print_out_to_token_callable() -> CallableFunction { print_out_to_token().into() } + pub fn print_out_to_token_input_names() -> Box<[&'static str]> { + print_out_to_token().input_names() + } pub fn print_out_to_token_input_types() -> Box<[TypeId]> { print_out_to_token().input_types() } @@ -1062,7 +1116,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, - &[core::any::TypeId::of::()], + Some(&["x: String"]), &[core::any::TypeId::of::()], print_out_to_token().into()); if flatten {} else {} } @@ -1081,6 +1135,9 @@ mod generate_tests { fn clone_boxed(&self) -> Box { Box::new(print_out_to_token()) } + fn input_names(&self) -> Box<[&'static str]> { + new_vec!["x: String"].into_boxed_slice() + } fn input_types(&self) -> Box<[TypeId]> { new_vec![TypeId::of::()].into_boxed_slice() } @@ -1088,6 +1145,9 @@ mod generate_tests { pub fn print_out_to_token_callable() -> CallableFunction { print_out_to_token().into() } + pub fn print_out_to_token_input_names() -> Box<[&'static str]> { + print_out_to_token().input_names() + } pub fn print_out_to_token_input_types() -> Box<[TypeId]> { print_out_to_token().input_types() } @@ -1125,7 +1185,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, - &[core::any::TypeId::of::()], + Some(&["x: &mut FLOAT"]), &[core::any::TypeId::of::()], increment_token().into()); if flatten {} else {} } @@ -1144,6 +1204,9 @@ mod generate_tests { fn clone_boxed(&self) -> Box { Box::new(increment_token()) } + fn input_names(&self) -> Box<[&'static str]> { + new_vec!["x: &mut FLOAT"].into_boxed_slice() + } fn input_types(&self) -> Box<[TypeId]> { new_vec![TypeId::of::()].into_boxed_slice() } @@ -1151,6 +1214,9 @@ mod generate_tests { pub fn increment_token_callable() -> CallableFunction { increment_token().into() } + pub fn increment_token_input_names() -> Box<[&'static str]> { + increment_token().input_names() + } pub fn increment_token_input_types() -> Box<[TypeId]> { increment_token().input_types() } @@ -1191,7 +1257,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, - &[core::any::TypeId::of::()], + Some(&["x: &mut FLOAT"]), &[core::any::TypeId::of::()], increment_token().into()); if flatten {} else {} } @@ -1210,6 +1276,9 @@ mod generate_tests { fn clone_boxed(&self) -> Box { Box::new(increment_token()) } + fn input_names(&self) -> Box<[&'static str]> { + new_vec!["x: &mut FLOAT"].into_boxed_slice() + } fn input_types(&self) -> Box<[TypeId]> { new_vec![TypeId::of::()].into_boxed_slice() } @@ -1217,6 +1286,9 @@ mod generate_tests { pub fn increment_token_callable() -> CallableFunction { increment_token().into() } + pub fn increment_token_input_names() -> Box<[&'static str]> { + increment_token().input_names() + } pub fn increment_token_input_types() -> Box<[TypeId]> { increment_token().input_types() } @@ -1278,7 +1350,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, - &[core::any::TypeId::of::()], + Some(&["x: &mut FLOAT"]), &[core::any::TypeId::of::()], increment_token().into()); if flatten {} else {} } @@ -1297,6 +1369,9 @@ mod generate_tests { fn clone_boxed(&self) -> Box { Box::new(increment_token()) } + fn input_names(&self) -> Box<[&'static str]> { + new_vec!["x: &mut FLOAT"].into_boxed_slice() + } fn input_types(&self) -> Box<[TypeId]> { new_vec![TypeId::of::()].into_boxed_slice() } @@ -1304,6 +1379,9 @@ mod generate_tests { pub fn increment_token_callable() -> CallableFunction { increment_token().into() } + pub fn increment_token_input_names() -> Box<[&'static str]> { + increment_token().input_names() + } pub fn increment_token_input_types() -> Box<[TypeId]> { increment_token().input_types() } @@ -1363,7 +1441,7 @@ 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, &[core::any::TypeId::of::()], + m.set_fn("get$square", FnNamespace::Internal, FnAccess::Public, Some(&["x: &mut u64"]), &[core::any::TypeId::of::()], int_foo_token().into()); if flatten {} else {} } @@ -1382,6 +1460,9 @@ mod generate_tests { fn clone_boxed(&self) -> Box { Box::new(int_foo_token()) } + fn input_names(&self) -> Box<[&'static str]> { + new_vec!["x: &mut u64"].into_boxed_slice() + } fn input_types(&self) -> Box<[TypeId]> { new_vec![TypeId::of::()].into_boxed_slice() } @@ -1389,6 +1470,9 @@ mod generate_tests { pub fn int_foo_token_callable() -> CallableFunction { int_foo_token().into() } + pub fn int_foo_token_input_names() -> Box<[&'static str]> { + int_foo_token().input_names() + } pub fn int_foo_token_input_types() -> Box<[TypeId]> { int_foo_token().input_types() } @@ -1426,9 +1510,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, &[core::any::TypeId::of::()], + m.set_fn("square", FnNamespace::Internal, FnAccess::Public, Some(&["x: &mut u64"]), &[core::any::TypeId::of::()], int_foo_token().into()); - m.set_fn("get$square", FnNamespace::Internal, FnAccess::Public, &[core::any::TypeId::of::()], + m.set_fn("get$square", FnNamespace::Internal, FnAccess::Public, Some(&["x: &mut u64"]), &[core::any::TypeId::of::()], int_foo_token().into()); if flatten {} else {} } @@ -1447,6 +1531,9 @@ mod generate_tests { fn clone_boxed(&self) -> Box { Box::new(int_foo_token()) } + fn input_names(&self) -> Box<[&'static str]> { + new_vec!["x: &mut u64"].into_boxed_slice() + } fn input_types(&self) -> Box<[TypeId]> { new_vec![TypeId::of::()].into_boxed_slice() } @@ -1454,6 +1541,9 @@ mod generate_tests { pub fn int_foo_token_callable() -> CallableFunction { int_foo_token().into() } + pub fn int_foo_token_input_names() -> Box<[&'static str]> { + int_foo_token().input_names() + } pub fn int_foo_token_input_types() -> Box<[TypeId]> { int_foo_token().input_types() } @@ -1491,9 +1581,8 @@ 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, - &[core::any::TypeId::of::(), - core::any::TypeId::of::()], + 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 {} } @@ -1513,6 +1602,9 @@ mod generate_tests { fn clone_boxed(&self) -> Box { Box::new(int_foo_token()) } + fn input_names(&self) -> Box<[&'static str]> { + new_vec!["x: &mut u64", "y: u64"].into_boxed_slice() + } fn input_types(&self) -> Box<[TypeId]> { new_vec![TypeId::of::(), TypeId::of::()].into_boxed_slice() } @@ -1520,6 +1612,9 @@ mod generate_tests { pub fn int_foo_token_callable() -> CallableFunction { int_foo_token().into() } + pub fn int_foo_token_input_names() -> Box<[&'static str]> { + int_foo_token().input_names() + } pub fn int_foo_token_input_types() -> Box<[TypeId]> { int_foo_token().input_types() } @@ -1557,13 +1652,11 @@ 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, - &[core::any::TypeId::of::(), - core::any::TypeId::of::()], + 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, - &[core::any::TypeId::of::(), - core::any::TypeId::of::()], + 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 {} } @@ -1583,6 +1676,9 @@ mod generate_tests { fn clone_boxed(&self) -> Box { Box::new(int_foo_token()) } + fn input_names(&self) -> Box<[&'static str]> { + new_vec!["x: &mut u64", "y: u64"].into_boxed_slice() + } fn input_types(&self) -> Box<[TypeId]> { new_vec![TypeId::of::(), TypeId::of::()].into_boxed_slice() } @@ -1590,6 +1686,9 @@ mod generate_tests { pub fn int_foo_token_callable() -> CallableFunction { int_foo_token().into() } + pub fn int_foo_token_input_names() -> Box<[&'static str]> { + int_foo_token().input_names() + } pub fn int_foo_token_input_types() -> Box<[TypeId]> { int_foo_token().input_types() } @@ -1628,6 +1727,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"]), &[core::any::TypeId::of::(), core::any::TypeId::of::()], get_by_index_token().into()); @@ -1649,6 +1749,9 @@ mod generate_tests { fn clone_boxed(&self) -> Box { Box::new(get_by_index_token()) } + fn input_names(&self) -> Box<[&'static str]> { + new_vec!["x: &mut MyCollection", "i: u64"].into_boxed_slice() + } fn input_types(&self) -> Box<[TypeId]> { new_vec![TypeId::of::(), TypeId::of::()].into_boxed_slice() @@ -1657,6 +1760,9 @@ mod generate_tests { pub fn get_by_index_token_callable() -> CallableFunction { get_by_index_token().into() } + pub fn get_by_index_token_input_names() -> Box<[&'static str]> { + get_by_index_token().input_names() + } pub fn get_by_index_token_input_types() -> Box<[TypeId]> { get_by_index_token().input_types() } @@ -1695,10 +1801,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"]), &[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"]), &[core::any::TypeId::of::(), core::any::TypeId::of::()], get_by_index_token().into()); @@ -1720,6 +1828,9 @@ mod generate_tests { fn clone_boxed(&self) -> Box { Box::new(get_by_index_token()) } + fn input_names(&self) -> Box<[&'static str]> { + new_vec!["x: &mut MyCollection", "i: u64"].into_boxed_slice() + } fn input_types(&self) -> Box<[TypeId]> { new_vec![TypeId::of::(), TypeId::of::()].into_boxed_slice() @@ -1728,6 +1839,9 @@ mod generate_tests { pub fn get_by_index_token_callable() -> CallableFunction { get_by_index_token().into() } + pub fn get_by_index_token_input_names() -> Box<[&'static str]> { + get_by_index_token().input_names() + } pub fn get_by_index_token_input_types() -> Box<[TypeId]> { get_by_index_token().input_types() } @@ -1766,6 +1880,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"]), &[core::any::TypeId::of::(), core::any::TypeId::of::(), core::any::TypeId::of::()], @@ -1789,6 +1904,9 @@ mod generate_tests { fn clone_boxed(&self) -> Box { Box::new(set_by_index_token()) } + fn input_names(&self) -> Box<[&'static str]> { + new_vec!["x: &mut MyCollection", "i: u64", "item: FLOAT"].into_boxed_slice() + } fn input_types(&self) -> Box<[TypeId]> { new_vec![TypeId::of::(), TypeId::of::(), @@ -1798,6 +1916,9 @@ mod generate_tests { pub fn set_by_index_token_callable() -> CallableFunction { set_by_index_token().into() } + pub fn set_by_index_token_input_names() -> Box<[&'static str]> { + set_by_index_token().input_names() + } pub fn set_by_index_token_input_types() -> Box<[TypeId]> { set_by_index_token().input_types() } @@ -1836,11 +1957,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"]), &[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"]), &[core::any::TypeId::of::(), core::any::TypeId::of::(), core::any::TypeId::of::()], @@ -1864,6 +1987,9 @@ mod generate_tests { fn clone_boxed(&self) -> Box { Box::new(set_by_index_token()) } + fn input_names(&self) -> Box<[&'static str]> { + new_vec!["x: &mut MyCollection", "i: u64", "item: FLOAT"].into_boxed_slice() + } fn input_types(&self) -> Box<[TypeId]> { new_vec![TypeId::of::(), TypeId::of::(), @@ -1873,6 +1999,9 @@ mod generate_tests { pub fn set_by_index_token_callable() -> CallableFunction { set_by_index_token().into() } + pub fn set_by_index_token_input_names() -> Box<[&'static str]> { + set_by_index_token().input_names() + } pub fn set_by_index_token_input_types() -> Box<[TypeId]> { set_by_index_token().input_types() } diff --git a/doc/src/appendix/keywords.md b/doc/src/appendix/keywords.md index b11e8421..2866cb5d 100644 --- a/doc/src/appendix/keywords.md +++ b/doc/src/appendix/keywords.md @@ -9,8 +9,6 @@ Keywords List | `false` | boolean false literal | | no | | | `let` | variable declaration | | no | | | `const` | constant declaration | | no | | -| `is_def_var` | is a variable declared? | | yes | yes | -| `is_shared` | is a value shared? | [`no_closure`] | yes | no | | `if` | if statement | | no | | | `else` | else block of if statement | | no | | | `switch` | matching | | no | | @@ -35,7 +33,6 @@ Keywords List | `call` | call a [function pointer] | | yes | no | | `curry` | curry a [function pointer] | | yes | no | | `this` | reference to base object for method call | [`no_function`] | no | | -| `is_def_fn` | is a scripted function defined? | [`no_function`] | yes | yes | | `type_of` | get type name of value | | yes | yes | | `print` | print value | | yes | yes | | `debug` | print value in debug format | | yes | yes | diff --git a/examples/repl.rs b/examples/repl.rs index bd0691ef..24be5392 100644 --- a/examples/repl.rs +++ b/examples/repl.rs @@ -42,6 +42,7 @@ fn print_help() { println!("help => print this help"); println!("quit, exit => quit"); println!("scope => print all variables in the scope"); + println!("functions => print all functions defined"); println!("ast => print the last AST"); println!("astu => print the last raw, un-optimized AST"); println!(r"end a line with '\' to continue to the next line."); @@ -133,6 +134,18 @@ fn main() { println!("{:#?}\n", &ast); continue; } + "functions" => { + // print a list of all registered functions + engine + .gen_fn_signatures(false) + .into_iter() + .for_each(|f| println!("{}", f)); + main_ast + .iter_functions() + .for_each(|(_, _, _, _, f)| println!("{}", f)); + println!(); + continue; + } _ => (), } diff --git a/src/ast.rs b/src/ast.rs index 93e049a2..e89fa446 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -100,7 +100,7 @@ impl fmt::Display for ScriptFnDef { .iter() .map(|s| s.as_str()) .collect::>() - .join(",") + .join(", ") ) } } diff --git a/src/engine.rs b/src/engine.rs index 647050a8..1a8afa66 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -95,24 +95,25 @@ impl Imports { self.0.as_mut().unwrap().truncate(size); } } - /// Get an iterator to this stack of imported modules. + /// Get an iterator to this stack of imported modules in reverse order. #[allow(dead_code)] pub fn iter(&self) -> impl Iterator)> { self.0.iter().flat_map(|lib| { lib.iter() + .rev() .map(|(name, module)| (name.as_str(), module.clone())) }) } - /// Get an iterator to this stack of imported modules. + /// Get an iterator to this stack of imported modules in reverse order. #[allow(dead_code)] pub(crate) fn iter_raw<'a>( &'a self, ) -> impl Iterator)> + 'a { - self.0.iter().flat_map(|lib| lib.iter().cloned()) + self.0.iter().flat_map(|lib| lib.iter().rev().cloned()) } - /// Get a consuming iterator to this stack of imported modules. + /// Get a consuming iterator to this stack of imported modules in reverse order. pub fn into_iter(self) -> impl Iterator)> { - self.0.into_iter().flat_map(|lib| lib.into_iter()) + self.0.into_iter().flat_map(|lib| lib.into_iter().rev()) } /// Add a stream of imported modules. pub fn extend(&mut self, stream: impl Iterator)>) { diff --git a/src/engine_api.rs b/src/engine_api.rs index f005071e..0d7023f6 100644 --- a/src/engine_api.rs +++ b/src/engine_api.rs @@ -1515,7 +1515,7 @@ impl Engine { .into() }); } - /// Call a script function defined in an [`AST`] with multiple `Dynamic` arguments + /// Call a script function defined in an [`AST`] with multiple [`Dynamic`] arguments /// and optionally a value for binding to the `this` pointer. /// /// ## WARNING @@ -1578,7 +1578,7 @@ impl Engine { 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. + /// Call a script function defined in an [`AST`] with multiple [`Dynamic`] arguments. /// /// ## WARNING /// @@ -1646,6 +1646,27 @@ impl Engine { let stmt = crate::stdlib::mem::take(ast.statements_mut()); crate::optimize::optimize_into_ast(self, scope, stmt, lib, optimization_level) } + /// Generate a list of all registered functions. + /// + /// The ordering is: + /// 1) Functions registered into the global namespace + /// 2) Functions in registered sub-modules + /// 3) Functions in packages + pub fn gen_fn_signatures(&self, include_packages: bool) -> Vec { + let mut signatures: Vec<_> = Default::default(); + + signatures.extend(self.global_namespace.gen_fn_signatures()); + + self.global_sub_modules.iter().for_each(|(name, m)| { + signatures.extend(m.gen_fn_signatures().map(|f| format!("{}::{}", name, f))) + }); + + if include_packages { + signatures.extend(self.packages.gen_fn_signatures()); + } + + signatures + } /// Provide a callback that will be invoked before each variable access. /// /// ## Return Value of Callback diff --git a/src/fn_register.rs b/src/fn_register.rs index afc87194..3a6db5f8 100644 --- a/src/fn_register.rs +++ b/src/fn_register.rs @@ -187,7 +187,7 @@ macro_rules! def_register { { #[inline] fn register_fn(&mut self, name: &str, f: FN) -> &mut Self { - self.global_namespace.set_fn(name, FnNamespace::Global, FnAccess::Public, + self.global_namespace.set_fn(name, FnNamespace::Global, FnAccess::Public, None, &[$(map_type_id::<$par>()),*], CallableFunction::$abi(make_func!(f : map_dynamic ; $($par => $let => $clone => $arg),*)) ); @@ -202,7 +202,7 @@ macro_rules! def_register { { #[inline] fn register_result_fn(&mut self, name: &str, f: FN) -> &mut Self { - self.global_namespace.set_fn(name, FnNamespace::Global, FnAccess::Public, + self.global_namespace.set_fn(name, FnNamespace::Global, FnAccess::Public, None, &[$(map_type_id::<$par>()),*], CallableFunction::$abi(make_func!(f : map_result ; $($par => $let => $clone => $arg),*)) ); diff --git a/src/module/mod.rs b/src/module/mod.rs index 6b2dc51c..924f383f 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -79,7 +79,31 @@ pub struct FuncInfo { /// Number of parameters. pub params: usize, /// Parameter types (if applicable). - pub types: Option>, + pub param_types: Option>, + /// Parameter names (if available). + pub param_names: Option>, +} + +impl FuncInfo { + /// Generate a signature of the function. + pub fn gen_signature(&self) -> String { + let mut sig = format!("{}(", self.name); + + if let Some(ref names) = self.param_names { + let params: Vec<_> = names.iter().map(ImmutableString::to_string).collect(); + sig.push_str(¶ms.join(", ")); + } else { + for x in 0..self.params { + sig.push_str("_"); + if x < self.params - 1 { + sig.push_str(", "); + } + } + } + + sig.push_str(")"); + sig + } } /// A module which may contain variables, sub-modules, external Rust functions, @@ -230,6 +254,14 @@ impl Module { self.indexed } + /// Generate signatures for all the functions in the module. + pub fn gen_fn_signatures<'a>(&'a self) -> impl Iterator + 'a { + self.functions + .values() + .filter(|FuncInfo { access, .. }| !access.is_private()) + .map(FuncInfo::gen_signature) + } + /// Does a variable exist in the module? /// /// # Example @@ -329,7 +361,8 @@ impl Module { namespace: FnNamespace::Internal, access: fn_def.access, params: num_params, - types: None, + param_types: None, + param_names: Some(fn_def.params.clone()), func: fn_def.into(), }, ); @@ -450,6 +483,17 @@ impl Module { } } + /// Update the parameter names and types in 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 { + if let Some(f) = self.functions.get_mut(&hash_fn) { + f.param_names = Some(arg_names.iter().map(|&n| n.into()).collect()); + } + self + } + /// Set a Rust function into the module, returning a hash key. /// /// If there is an existing Rust function of the same hash, it is replaced. @@ -462,6 +506,7 @@ impl Module { name: impl Into, namespace: FnNamespace, access: FnAccess, + arg_names: Option<&[&str]>, arg_types: &[TypeId], func: CallableFunction, ) -> u64 { @@ -488,7 +533,8 @@ impl Module { namespace, access, params: params.len(), - types: Some(params), + param_types: Some(params), + param_names: arg_names.map(|p| p.iter().map(|&v| v.into()).collect()), func: func.into(), }, ); @@ -576,6 +622,7 @@ impl Module { name, namespace, access, + None, arg_types, CallableFunction::from_method(Box::new(f)), ) @@ -606,6 +653,7 @@ impl Module { name, FnNamespace::Internal, FnAccess::Public, + None, &arg_types, CallableFunction::from_pure(Box::new(f)), ) @@ -638,6 +686,7 @@ impl Module { name, FnNamespace::Internal, FnAccess::Public, + None, &arg_types, CallableFunction::from_pure(Box::new(f)), ) @@ -673,6 +722,7 @@ impl Module { name, namespace, FnAccess::Public, + None, &arg_types, CallableFunction::from_method(Box::new(f)), ) @@ -738,6 +788,7 @@ impl Module { name, FnNamespace::Internal, FnAccess::Public, + None, &arg_types, CallableFunction::from_pure(Box::new(f)), ) @@ -780,6 +831,7 @@ impl Module { name, namespace, FnAccess::Public, + None, &arg_types, CallableFunction::from_method(Box::new(f)), ) @@ -900,6 +952,7 @@ impl Module { name, FnNamespace::Internal, FnAccess::Public, + None, &arg_types, CallableFunction::from_pure(Box::new(f)), ) @@ -948,6 +1001,7 @@ impl Module { name, namespace, FnAccess::Public, + None, &arg_types, CallableFunction::from_method(Box::new(f)), ) @@ -1008,6 +1062,7 @@ impl Module { crate::engine::FN_IDX_SET, FnNamespace::Internal, FnAccess::Public, + None, &arg_types, CallableFunction::from_method(Box::new(f)), ) @@ -1100,6 +1155,7 @@ impl Module { name, FnNamespace::Internal, FnAccess::Public, + None, &arg_types, CallableFunction::from_pure(Box::new(f)), ) @@ -1155,6 +1211,7 @@ impl Module { name, namespace, FnAccess::Public, + None, &arg_types, CallableFunction::from_method(Box::new(f)), ) @@ -1569,7 +1626,7 @@ impl Module { name, namespace, params, - types, + param_types: types, func, .. }, diff --git a/src/packages/iter_basic.rs b/src/packages/iter_basic.rs index c336707f..97ff0f59 100644 --- a/src/packages/iter_basic.rs +++ b/src/packages/iter_basic.rs @@ -48,7 +48,8 @@ macro_rules! reg_range { ($lib:expr, $x:expr, $( $y:ty ),*) => ( $( $lib.set_iterator::>(); - $lib.set_fn_2($x, get_range::<$y>); + let hash = $lib.set_fn_2($x, get_range::<$y>); + $lib.update_fn_param_names(hash, &[concat!("from: ", stringify!($y)), concat!("to: ", stringify!($y))]); )* ) } @@ -59,14 +60,16 @@ macro_rules! reg_step { ($lib:expr, $x:expr, $( $y:ty ),*) => ( $( $lib.set_iterator::>(); - $lib.set_fn_3($x, get_step_range::<$y>); + 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))]); )* ) } def_package!(crate:BasicIteratorPackage:"Basic range iterators.", lib, { lib.set_iterator::>(); - lib.set_fn_2("range", get_range::); + let hash = lib.set_fn_2("range", get_range::); + lib.update_fn_param_names(hash, &["from: INT", "to: INT"]); #[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i64"))] @@ -79,7 +82,8 @@ def_package!(crate:BasicIteratorPackage:"Basic range iterators.", lib, { } lib.set_iterator::>(); - lib.set_fn_3("range", get_step_range::); + let hash = lib.set_fn_3("range", get_step_range::); + lib.update_fn_param_names(hash, &["from: INT", "to: INT", "step: INT"]); #[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i64"))] diff --git a/src/packages/mod.rs b/src/packages/mod.rs index e9731ec2..3b6669c7 100644 --- a/src/packages/mod.rs +++ b/src/packages/mod.rs @@ -87,6 +87,15 @@ impl PackagesCollection { .as_ref() .and_then(|x| x.iter().find_map(|p| p.get_iter(id))) } + /// Get an iterator over all the packages in the [`PackagesCollection`]. + pub(crate) fn iter(&self) -> impl Iterator { + self.0.iter().flat_map(|p| p.iter()) + } + + /// Generate signatures for all the functions in the [`PackagesCollection`]. + pub fn gen_fn_signatures<'a>(&'a self) -> impl Iterator + 'a { + self.iter().flat_map(|m| m.gen_fn_signatures()) + } } /// Macro that makes it easy to define a _package_ (which is basically a shared module) diff --git a/src/plugin.rs b/src/plugin.rs index 66783bbd..91385628 100644 --- a/src/plugin.rs +++ b/src/plugin.rs @@ -33,6 +33,9 @@ pub trait PluginFunction { /// Convert a plugin function into a boxed trait object. fn clone_boxed(&self) -> Box; + /// Return a boxed slice of the names of the function's parameters. + fn input_names(&self) -> Box<[&'static str]>; + /// Return a boxed slice of type ID's of the function's parameters. fn input_types(&self) -> Box<[TypeId]>; } diff --git a/src/token.rs b/src/token.rs index 6cea048f..aad19ef2 100644 --- a/src/token.rs +++ b/src/token.rs @@ -2,7 +2,7 @@ use crate::engine::{ KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_FN_PTR_CALL, KEYWORD_FN_PTR_CURRY, - KEYWORD_IS_DEF_FN, KEYWORD_IS_DEF_VAR, KEYWORD_PRINT, KEYWORD_THIS, KEYWORD_TYPE_OF, + KEYWORD_PRINT, KEYWORD_THIS, KEYWORD_TYPE_OF, }; use crate::stdlib::{ borrow::Cow, @@ -538,11 +538,7 @@ impl Token { | "async" | "await" | "yield" => Reserved(syntax.into()), KEYWORD_PRINT | KEYWORD_DEBUG | KEYWORD_TYPE_OF | KEYWORD_EVAL | KEYWORD_FN_PTR - | KEYWORD_FN_PTR_CALL | KEYWORD_FN_PTR_CURRY | KEYWORD_IS_DEF_VAR - | KEYWORD_IS_DEF_FN | KEYWORD_THIS => Reserved(syntax.into()), - - #[cfg(not(feature = "no_closure"))] - crate::engine::KEYWORD_IS_SHARED => Reserved(syntax.into()), + | KEYWORD_FN_PTR_CALL | KEYWORD_FN_PTR_CURRY | KEYWORD_THIS => Reserved(syntax.into()), _ => return None, }) @@ -1513,12 +1509,8 @@ fn get_identifier( #[inline(always)] pub fn is_keyword_function(name: &str) -> bool { match name { - #[cfg(not(feature = "no_closure"))] - crate::engine::KEYWORD_IS_SHARED => true, KEYWORD_PRINT | KEYWORD_DEBUG | KEYWORD_TYPE_OF | KEYWORD_EVAL | KEYWORD_FN_PTR - | KEYWORD_FN_PTR_CALL | KEYWORD_FN_PTR_CURRY | KEYWORD_IS_DEF_VAR | KEYWORD_IS_DEF_FN => { - true - } + | KEYWORD_FN_PTR_CALL | KEYWORD_FN_PTR_CURRY => true, _ => false, } } @@ -1528,8 +1520,7 @@ pub fn is_keyword_function(name: &str) -> bool { #[inline(always)] pub fn can_override_keyword(name: &str) -> bool { match name { - KEYWORD_PRINT | KEYWORD_DEBUG | KEYWORD_TYPE_OF | KEYWORD_EVAL | KEYWORD_FN_PTR - | KEYWORD_IS_DEF_VAR | KEYWORD_IS_DEF_FN => true, + KEYWORD_PRINT | KEYWORD_DEBUG | KEYWORD_TYPE_OF | KEYWORD_EVAL | KEYWORD_FN_PTR => true, _ => false, } }