diff --git a/RELEASES.md b/RELEASES.md index 1f0a1fed..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,18 +22,23 @@ Breaking changes ---------------- * `Module::set_fn`, `Module::set_raw_fn` and `Module::set_fn_XXX_mut` all take an additional parameter of `FnNamespace`. -* `unless` is now a reserved keyword. +* `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`. 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 76d7648a..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())); } @@ -162,6 +176,7 @@ pub(crate) fn generate_body( pub fn rhai_module_generate() -> Module { let mut m = Module::new(); rhai_generate_into_module(&mut m, false); + m.build_index(); m } #[allow(unused_mut)] @@ -194,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 56d3290c..8afdf134 100644 --- a/codegen/src/test/module.rs +++ b/codegen/src/test/module.rs @@ -257,6 +257,7 @@ mod generate_tests { pub fn rhai_module_generate() -> Module { let mut m = Module::new(); rhai_generate_into_module(&mut m, false); + m.build_index(); m } #[allow(unused_mut)] @@ -291,11 +292,12 @@ mod generate_tests { pub fn rhai_module_generate() -> Module { let mut m = Module::new(); rhai_generate_into_module(&mut m, false); + m.build_index(); m } #[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 {} } @@ -313,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() } @@ -320,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() } @@ -352,11 +360,12 @@ mod generate_tests { pub fn rhai_module_generate() -> Module { let mut m = Module::new(); rhai_generate_into_module(&mut m, false); + m.build_index(); m } #[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 {} } @@ -375,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() } @@ -382,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() } @@ -413,11 +428,12 @@ mod generate_tests { pub fn rhai_module_generate() -> Module { let mut m = Module::new(); rhai_generate_into_module(&mut m, false); + m.build_index(); m } #[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 {} } @@ -436,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() } @@ -443,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() } @@ -485,14 +507,15 @@ mod generate_tests { pub fn rhai_module_generate() -> Module { let mut m = Module::new(); rhai_generate_into_module(&mut m, false); + m.build_index(); m } #[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 {} } @@ -511,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() } @@ -518,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() } @@ -538,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() @@ -546,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() } @@ -577,12 +612,13 @@ mod generate_tests { pub fn rhai_module_generate() -> Module { let mut m = Module::new(); rhai_generate_into_module(&mut m, false); + m.build_index(); m } #[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 {} } @@ -602,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() @@ -610,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() } @@ -642,18 +684,19 @@ mod generate_tests { pub fn rhai_module_generate() -> Module { let mut m = Module::new(); rhai_generate_into_module(&mut m, false); + m.build_index(); m } #[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 {} } @@ -673,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() @@ -681,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() } @@ -714,6 +763,7 @@ mod generate_tests { pub fn rhai_module_generate() -> Module { let mut m = Module::new(); rhai_generate_into_module(&mut m, false); + m.build_index(); m } #[allow(unused_mut)] @@ -745,6 +795,7 @@ mod generate_tests { pub fn rhai_module_generate() -> Module { let mut m = Module::new(); rhai_generate_into_module(&mut m, false); + m.build_index(); m } #[allow(unused_mut)] @@ -778,6 +829,7 @@ mod generate_tests { pub fn rhai_module_generate() -> Module { let mut m = Module::new(); rhai_generate_into_module(&mut m, false); + m.build_index(); m } #[allow(unused_mut)] @@ -813,6 +865,7 @@ mod generate_tests { pub fn rhai_module_generate() -> Module { let mut m = Module::new(); rhai_generate_into_module(&mut m, false); + m.build_index(); m } #[allow(unused_mut)] @@ -848,6 +901,7 @@ mod generate_tests { pub fn rhai_module_generate() -> Module { let mut m = Module::new(); rhai_generate_into_module(&mut m, false); + m.build_index(); m } #[allow(unused_mut)] @@ -889,11 +943,12 @@ mod generate_tests { pub fn rhai_module_generate() -> Module { let mut m = Module::new(); rhai_generate_into_module(&mut m, false); + m.build_index(); m } #[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 {} } @@ -911,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() } @@ -918,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() } @@ -945,6 +1006,7 @@ mod generate_tests { pub fn rhai_module_generate() -> Module { let mut m = Module::new(); rhai_generate_into_module(&mut m, false); + m.build_index(); m } #[allow(unused_mut)] @@ -979,12 +1041,13 @@ mod generate_tests { pub fn rhai_module_generate() -> Module { let mut m = Module::new(); rhai_generate_into_module(&mut m, false); + m.build_index(); m } #[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 {} } @@ -1003,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() } @@ -1010,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() } @@ -1041,12 +1110,13 @@ mod generate_tests { pub fn rhai_module_generate() -> Module { let mut m = Module::new(); rhai_generate_into_module(&mut m, false); + m.build_index(); m } #[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 {} } @@ -1065,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() } @@ -1072,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() } @@ -1103,12 +1179,13 @@ mod generate_tests { pub fn rhai_module_generate() -> Module { let mut m = Module::new(); rhai_generate_into_module(&mut m, false); + m.build_index(); m } #[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 {} } @@ -1127,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() } @@ -1134,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() } @@ -1168,12 +1251,13 @@ mod generate_tests { pub fn rhai_module_generate() -> Module { let mut m = Module::new(); rhai_generate_into_module(&mut m, false); + m.build_index(); m } #[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 {} } @@ -1192,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() } @@ -1199,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() } @@ -1209,6 +1299,7 @@ mod generate_tests { pub fn rhai_module_generate() -> Module { let mut m = Module::new(); rhai_generate_into_module(&mut m, false); + m.build_index(); m } #[allow(unused_mut)] @@ -1253,12 +1344,13 @@ mod generate_tests { pub fn rhai_module_generate() -> Module { let mut m = Module::new(); rhai_generate_into_module(&mut m, false); + m.build_index(); m } #[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 {} } @@ -1277,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() } @@ -1284,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() } @@ -1294,6 +1392,7 @@ mod generate_tests { pub fn rhai_module_generate() -> Module { let mut m = Module::new(); rhai_generate_into_module(&mut m, false); + m.build_index(); m } #[allow(unused_mut)] @@ -1337,11 +1436,12 @@ mod generate_tests { pub fn rhai_module_generate() -> Module { let mut m = Module::new(); rhai_generate_into_module(&mut m, false); + m.build_index(); m } #[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 {} } @@ -1360,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() } @@ -1367,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() } @@ -1399,13 +1505,14 @@ mod generate_tests { pub fn rhai_module_generate() -> Module { let mut m = Module::new(); rhai_generate_into_module(&mut m, false); + m.build_index(); m } #[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 {} } @@ -1424,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() } @@ -1431,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() } @@ -1463,13 +1576,13 @@ mod generate_tests { pub fn rhai_module_generate() -> Module { let mut m = Module::new(); rhai_generate_into_module(&mut m, false); + m.build_index(); m } #[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 {} } @@ -1489,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() } @@ -1496,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() } @@ -1528,17 +1647,16 @@ mod generate_tests { pub fn rhai_module_generate() -> Module { let mut m = Module::new(); rhai_generate_into_module(&mut m, false); + m.build_index(); m } #[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 {} } @@ -1558,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() } @@ -1565,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() } @@ -1597,11 +1721,13 @@ mod generate_tests { pub fn rhai_module_generate() -> Module { let mut m = Module::new(); rhai_generate_into_module(&mut m, false); + m.build_index(); m } #[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()); @@ -1623,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() @@ -1631,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() } @@ -1663,15 +1795,18 @@ mod generate_tests { pub fn rhai_module_generate() -> Module { let mut m = Module::new(); rhai_generate_into_module(&mut m, false); + m.build_index(); m } #[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()); @@ -1693,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() @@ -1701,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() } @@ -1733,11 +1874,13 @@ mod generate_tests { pub fn rhai_module_generate() -> Module { let mut m = Module::new(); rhai_generate_into_module(&mut m, false); + m.build_index(); m } #[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::()], @@ -1761,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::(), @@ -1770,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() } @@ -1802,16 +1951,19 @@ mod generate_tests { pub fn rhai_module_generate() -> Module { let mut m = Module::new(); rhai_generate_into_module(&mut m, false); + m.build_index(); m } #[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::()], @@ -1835,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::(), @@ -1844,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() } @@ -1874,6 +2032,7 @@ mod generate_tests { pub fn rhai_module_generate() -> Module { let mut m = Module::new(); rhai_generate_into_module(&mut m, false); + m.build_index(); m } #[allow(unused_mut)] @@ -1888,6 +2047,7 @@ mod generate_tests { pub fn rhai_module_generate() -> Module { let mut m = Module::new(); rhai_generate_into_module(&mut m, false); + m.build_index(); m } #[allow(unused_mut)] @@ -1928,6 +2088,7 @@ mod generate_tests { pub fn rhai_module_generate() -> Module { let mut m = Module::new(); rhai_generate_into_module(&mut m, false); + m.build_index(); m } #[allow(unused_mut)] @@ -1944,6 +2105,7 @@ mod generate_tests { pub fn rhai_module_generate() -> Module { let mut m = Module::new(); rhai_generate_into_module(&mut m, false); + m.build_index(); m } #[allow(unused_mut)] @@ -1958,6 +2120,7 @@ mod generate_tests { pub fn rhai_module_generate() -> Module { let mut m = Module::new(); rhai_generate_into_module(&mut m, false); + m.build_index(); m } #[allow(unused_mut)] @@ -2024,6 +2187,7 @@ mod generate_tests { pub fn rhai_module_generate() -> Module { let mut m = Module::new(); rhai_generate_into_module(&mut m, false); + m.build_index(); m } #[allow(unused_mut)] @@ -2040,6 +2204,7 @@ mod generate_tests { pub fn rhai_module_generate() -> Module { let mut m = Module::new(); rhai_generate_into_module(&mut m, false); + m.build_index(); m } #[allow(unused_mut)] @@ -2054,6 +2219,7 @@ mod generate_tests { pub fn rhai_module_generate() -> Module { let mut m = Module::new(); rhai_generate_into_module(&mut m, false); + m.build_index(); m } #[allow(unused_mut)] @@ -2077,6 +2243,7 @@ mod generate_tests { pub fn rhai_module_generate() -> Module { let mut m = Module::new(); rhai_generate_into_module(&mut m, false); + m.build_index(); m } #[allow(unused_mut)] @@ -2091,6 +2258,7 @@ mod generate_tests { pub fn rhai_module_generate() -> Module { let mut m = Module::new(); rhai_generate_into_module(&mut m, false); + m.build_index(); m } #[allow(unused_mut)] @@ -2116,6 +2284,7 @@ mod generate_tests { pub fn rhai_module_generate() -> Module { let mut m = Module::new(); rhai_generate_into_module(&mut m, false); + m.build_index(); m } #[allow(unused_mut)] @@ -2132,6 +2301,7 @@ mod generate_tests { pub fn rhai_module_generate() -> Module { let mut m = Module::new(); rhai_generate_into_module(&mut m, false); + m.build_index(); m } #[allow(unused_mut)] @@ -2146,6 +2316,7 @@ mod generate_tests { pub fn rhai_module_generate() -> Module { let mut m = Module::new(); rhai_generate_into_module(&mut m, false); + m.build_index(); m } #[allow(unused_mut)] @@ -2167,6 +2338,7 @@ mod generate_tests { pub fn rhai_module_generate() -> Module { let mut m = Module::new(); rhai_generate_into_module(&mut m, false); + m.build_index(); m } #[allow(unused_mut)] diff --git a/doc/src/SUMMARY.md b/doc/src/SUMMARY.md index 73332147..e6a4da84 100644 --- a/doc/src/SUMMARY.md +++ b/doc/src/SUMMARY.md @@ -97,7 +97,7 @@ The Rhai Scripting Language 19. [Modules](language/modules/index.md) 1. [Export Variables, Functions and Sub-Modules](language/modules/export.md) 2. [Import Modules](language/modules/import.md) - 20. [Eval Statement](language/eval.md) + 20. [Eval Function](language/eval.md) 6. [Safety and Protection](safety/index.md) 1. [Checked Arithmetic](safety/checked.md) 2. [Sand-Boxing](safety/sandbox.md) diff --git a/doc/src/appendix/keywords.md b/doc/src/appendix/keywords.md index 0e542dcf..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 | @@ -49,6 +46,8 @@ Reserved Keywords | --------- | --------------------- | | `var` | variable declaration | | `static` | variable declaration | +| `begin` | block scope | +| `end` | block scope | | `shared` | share value | | `each` | looping | | `then` | control flow | diff --git a/doc/src/language/constants.md b/doc/src/language/constants.md index da2a13be..52124d21 100644 --- a/doc/src/language/constants.md +++ b/doc/src/language/constants.md @@ -34,23 +34,28 @@ It is very useful to have a constant value hold a [custom type], which essential as a [_singleton_](../patterns/singleton.md). ```rust -use rhai::{Engine, Scope}; +use rhai::{Engine, Scope, RegisterFn}; +#[derive(Debug, Clone)] struct TestStruct(i64); // custom type -let engine = Engine::new() +let mut engine = Engine::new(); + +engine .register_type_with_name::("TestStruct") // register custom type - .register_get_set("value", - |obj: &mut TestStruct| obj.0, // property getter - |obj: &mut TestStruct, value: i64| obj.0 = value // property setter + .register_get("value", |obj: &mut TestStruct| obj.0), // property getter + .register_fn("update_value", + |obj: &mut TestStruct, value: i64| obj.0 = value // mutating method ); let mut scope = Scope::new(); // create custom scope scope.push_constant("MY_NUMBER", TestStruct(123_i64)); // add constant variable -engine.consume_with_scope(&mut scope, r" - MY_NUMBER.value = 42; // constant objects can be modified +// Beware: constant objects can still be modified via a method call! +engine.consume_with_scope(&mut scope, +r" + MY_NUMBER.update_value(42); print(MY_NUMBER.value); // prints 42 ")?; ``` diff --git a/doc/src/language/eval.md b/doc/src/language/eval.md index fec9569a..7e9bdd4d 100644 --- a/doc/src/language/eval.md +++ b/doc/src/language/eval.md @@ -1,4 +1,4 @@ -`eval` Statement +`eval` Function =============== {{#include ../links.md}} @@ -60,7 +60,13 @@ print(x); -------------- For those who subscribe to the (very sensible) motto of ["`eval` is evil"](http://linterrors.com/js/eval-is-evil), -disable `eval` by overloading it, probably with something that throws. +disable `eval` using [`Engine::disable_symbol`][disable keywords and operators]: + +```rust +engine.disable_symbol("eval"); // disable usage of 'eval' +``` + +`eval` can also be disabled by overloading it, probably with something that throws: ```rust fn eval(script) { throw "eval is evil! I refuse to run " + script } @@ -75,20 +81,3 @@ engine.register_result_fn("eval", |script: String| -> Result<(), Box 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,21 @@ 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)); + + #[cfg(not(feature = "no_function"))] + main_ast + .iter_functions() + .for_each(|(_, _, _, _, f)| println!("{}", f)); + + println!(); + continue; + } _ => (), } diff --git a/src/ast.rs b/src/ast.rs index 5e71db1e..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(", ") ) } } @@ -1154,8 +1154,8 @@ mod tests { assert_eq!(size_of::(), 32); assert_eq!(size_of::>(), 32); assert_eq!(size_of::(), 72); - assert_eq!(size_of::(), 32); + assert_eq!(size_of::(), 56); assert_eq!(size_of::(), 16); - assert_eq!(size_of::(), 64); + assert_eq!(size_of::(), 72); } } diff --git a/src/engine.rs b/src/engine.rs index ac747dfb..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)>) { @@ -178,9 +179,7 @@ pub const KEYWORD_FN_PTR_CURRY: &str = "curry"; #[cfg(not(feature = "no_closure"))] pub const KEYWORD_IS_SHARED: &str = "is_shared"; pub const KEYWORD_IS_DEF_VAR: &str = "is_def_var"; -pub const KEYWORD_IS_DEF_FN: &str = "is_def_fn"; pub const KEYWORD_THIS: &str = "this"; -pub const FN_TO_STRING: &str = "to_string"; #[cfg(not(feature = "no_object"))] pub const FN_GET: &str = "get$"; #[cfg(not(feature = "no_object"))] diff --git a/src/engine_api.rs b/src/engine_api.rs index f005071e..b801a33c 100644 --- a/src/engine_api.rs +++ b/src/engine_api.rs @@ -7,8 +7,10 @@ use crate::optimize::OptimizationLevel; use crate::stdlib::{ any::{type_name, TypeId}, boxed::Box, + format, hash::{Hash, Hasher}, string::String, + vec::Vec, }; use crate::utils::get_hasher; use crate::{ @@ -1515,7 +1517,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 +1580,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 +1648,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_call.rs b/src/fn_call.rs index bbf40192..1c629bf2 100644 --- a/src/fn_call.rs +++ b/src/fn_call.rs @@ -3,8 +3,7 @@ use crate::ast::{Expr, Stmt}; use crate::engine::{ search_imports, Imports, State, 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_TYPE_OF, + KEYWORD_FN_PTR_CALL, KEYWORD_FN_PTR_CURRY, KEYWORD_IS_DEF_VAR, KEYWORD_PRINT, KEYWORD_TYPE_OF, }; use crate::fn_native::FnCallArgs; use crate::module::NamespaceRef; @@ -419,7 +418,7 @@ impl Engine { #[inline] pub(crate) fn has_override_by_name_and_arguments( &self, - mods: &Imports, + mods: Option<&Imports>, lib: &[&Module], fn_name: &str, arg_types: impl AsRef<[TypeId]>, @@ -436,7 +435,7 @@ impl Engine { #[inline(always)] pub(crate) fn has_override( &self, - mods: &Imports, + mods: Option<&Imports>, lib: &[&Module], hash_fn: u64, hash_script: u64, @@ -454,8 +453,7 @@ impl Engine { || self.packages.contains_fn(hash_script) || self.packages.contains_fn(hash_fn) // Then check imported modules - || mods.contains_fn(hash_script) - || mods.contains_fn(hash_fn) + || mods.map(|m| m.contains_fn(hash_script) || m.contains_fn(hash_fn)).unwrap_or(false) } /// Perform an actual function call, native Rust or scripted, taking care of special functions. @@ -494,7 +492,7 @@ impl Engine { // type_of KEYWORD_TYPE_OF if args.len() == 1 - && !self.has_override(mods, lib, hash_fn, hash_script, pub_only) => + && !self.has_override(Some(mods), lib, hash_fn, hash_script, pub_only) => { Ok(( self.map_type_name(args[0].type_name()).to_string().into(), @@ -506,7 +504,7 @@ impl Engine { // by a function pointer so it isn't caught at parse time. KEYWORD_FN_PTR | KEYWORD_EVAL if args.len() == 1 - && !self.has_override(mods, lib, hash_fn, hash_script, pub_only) => + && !self.has_override(Some(mods), lib, hash_fn, hash_script, pub_only) => { EvalAltResult::ErrorRuntime( format!( @@ -521,7 +519,7 @@ impl Engine { // Script-like function found #[cfg(not(feature = "no_function"))] - _ if self.has_override(mods, lib, 0, hash_script, pub_only) => { + _ if self.has_override(Some(mods), lib, 0, hash_script, pub_only) => { // Get function let func = lib .iter() @@ -856,7 +854,7 @@ impl Engine { let hash_fn = calc_native_fn_hash(empty(), fn_name, once(TypeId::of::())); - if !self.has_override(mods, lib, hash_fn, hash_script, pub_only) { + if !self.has_override(Some(mods), lib, hash_fn, hash_script, pub_only) { // Fn - only in function call style return self .eval_expr(scope, mods, state, lib, this_ptr, &args_expr[0], level)? @@ -912,7 +910,7 @@ impl Engine { if name == KEYWORD_FN_PTR_CALL && args_expr.len() >= 1 - && !self.has_override(mods, lib, 0, hash_script, pub_only) + && !self.has_override(Some(mods), lib, 0, hash_script, pub_only) { let fn_ptr = self.eval_expr(scope, mods, state, lib, this_ptr, &args_expr[0], level)?; @@ -942,7 +940,7 @@ impl Engine { if name == KEYWORD_IS_DEF_VAR && args_expr.len() == 1 { let hash_fn = calc_native_fn_hash(empty(), name, once(TypeId::of::())); - if !self.has_override(mods, lib, hash_fn, hash_script, pub_only) { + if !self.has_override(Some(mods), lib, hash_fn, hash_script, pub_only) { let var_name = self.eval_expr(scope, mods, state, lib, this_ptr, &args_expr[0], level)?; let var_name = var_name.as_str().map_err(|err| { @@ -952,44 +950,11 @@ impl Engine { } } - // Handle is_def_fn() - if name == KEYWORD_IS_DEF_FN && args_expr.len() == 2 { - let hash_fn = calc_native_fn_hash( - empty(), - name, - [TypeId::of::(), TypeId::of::()] - .iter() - .cloned(), - ); - - if !self.has_override(mods, lib, hash_fn, hash_script, pub_only) { - let fn_name = - self.eval_expr(scope, mods, state, lib, this_ptr, &args_expr[0], level)?; - let num_params = - self.eval_expr(scope, mods, state, lib, this_ptr, &args_expr[1], level)?; - - let fn_name = fn_name.as_str().map_err(|err| { - self.make_type_mismatch_err::(err, args_expr[0].position()) - })?; - let num_params = num_params.as_int().map_err(|err| { - self.make_type_mismatch_err::(err, args_expr[1].position()) - })?; - - return Ok(if num_params < 0 { - false - } else { - let hash = calc_script_fn_hash(empty(), fn_name, num_params as usize); - lib.iter().any(|&m| m.contains_fn(hash, false)) - } - .into()); - } - } - // Handle eval() if name == KEYWORD_EVAL && args_expr.len() == 1 { let hash_fn = calc_native_fn_hash(empty(), name, once(TypeId::of::())); - if !self.has_override(mods, lib, hash_fn, hash_script, pub_only) { + if !self.has_override(Some(mods), lib, hash_fn, hash_script, pub_only) { // eval - only in function call style let prev_len = scope.len(); let script = diff --git a/src/fn_native.rs b/src/fn_native.rs index c29464f9..e605df82 100644 --- a/src/fn_native.rs +++ b/src/fn_native.rs @@ -49,8 +49,8 @@ pub type Locked = crate::stdlib::sync::RwLock; #[derive(Debug, Copy, Clone)] pub struct NativeCallContext<'e, 'a, 'm, 'pm: 'm> { engine: &'e Engine, - mods: Option<&'a Imports>, - lib: &'m [&'pm Module], + pub(crate) mods: Option<&'a Imports>, + pub(crate) lib: &'m [&'pm Module], } impl<'e, 'a, 'm, 'pm: 'm, M: AsRef<[&'pm Module]> + ?Sized> From<(&'e Engine, &'a Imports, &'m M)> 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 4e548e14..c4d5147b 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -2,9 +2,7 @@ use crate::ast::{FnAccess, IdentX}; use crate::dynamic::Variant; -use crate::fn_native::{ - shared_make_mut, shared_take_or_clone, CallableFunction, FnCallArgs, IteratorFn, SendSync, -}; +use crate::fn_native::{shared_take_or_clone, CallableFunction, FnCallArgs, IteratorFn, SendSync}; use crate::fn_register::by_value as cast_arg; use crate::stdlib::{ any::TypeId, @@ -81,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, @@ -232,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 @@ -331,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(), }, ); @@ -399,23 +430,6 @@ impl Module { self.modules.get(name).map(|m| m.as_ref()) } - /// Get a mutable reference to a sub-module. - /// - /// # Example - /// - /// ``` - /// use rhai::Module; - /// - /// let mut module = Module::new(); - /// let sub_module = Module::new(); - /// module.set_sub_module("question", sub_module); - /// assert!(module.get_sub_module_mut("question").is_some()); - /// ``` - #[inline(always)] - pub fn get_sub_module_mut(&mut self, name: &str) -> Option<&mut Module> { - self.modules.get_mut(name).map(shared_make_mut) - } - /// Set a sub-module into the module. /// /// If there is an existing sub-module of the same name, it is replaced. @@ -469,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. @@ -481,6 +506,7 @@ impl Module { name: impl Into, namespace: FnNamespace, access: FnAccess, + arg_names: Option<&[&str]>, arg_types: &[TypeId], func: CallableFunction, ) -> u64 { @@ -507,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(), }, ); @@ -595,6 +622,7 @@ impl Module { name, namespace, access, + None, arg_types, CallableFunction::from_method(Box::new(f)), ) @@ -625,6 +653,7 @@ impl Module { name, FnNamespace::Internal, FnAccess::Public, + None, &arg_types, CallableFunction::from_pure(Box::new(f)), ) @@ -657,6 +686,7 @@ impl Module { name, FnNamespace::Internal, FnAccess::Public, + None, &arg_types, CallableFunction::from_pure(Box::new(f)), ) @@ -692,6 +722,7 @@ impl Module { name, namespace, FnAccess::Public, + None, &arg_types, CallableFunction::from_method(Box::new(f)), ) @@ -757,6 +788,7 @@ impl Module { name, FnNamespace::Internal, FnAccess::Public, + None, &arg_types, CallableFunction::from_pure(Box::new(f)), ) @@ -799,6 +831,7 @@ impl Module { name, namespace, FnAccess::Public, + None, &arg_types, CallableFunction::from_method(Box::new(f)), ) @@ -919,6 +952,7 @@ impl Module { name, FnNamespace::Internal, FnAccess::Public, + None, &arg_types, CallableFunction::from_pure(Box::new(f)), ) @@ -967,6 +1001,7 @@ impl Module { name, namespace, FnAccess::Public, + None, &arg_types, CallableFunction::from_method(Box::new(f)), ) @@ -1027,6 +1062,7 @@ impl Module { crate::engine::FN_IDX_SET, FnNamespace::Internal, FnAccess::Public, + None, &arg_types, CallableFunction::from_method(Box::new(f)), ) @@ -1119,6 +1155,7 @@ impl Module { name, FnNamespace::Internal, FnAccess::Public, + None, &arg_types, CallableFunction::from_pure(Box::new(f)), ) @@ -1174,6 +1211,7 @@ impl Module { name, namespace, FnAccess::Public, + None, &arg_types, CallableFunction::from_method(Box::new(f)), ) @@ -1537,6 +1575,8 @@ impl Module { }); } + module.build_index(); + Ok(module) } @@ -1544,8 +1584,7 @@ impl Module { /// variables and functions as one flattened namespace. /// /// If the module is already indexed, this method has no effect. - #[cfg(not(feature = "no_module"))] - pub fn build_index(&mut self) { + pub fn build_index(&mut self) -> &mut Self { // Collect a particular module. fn index_module<'a>( module: &'a Module, @@ -1586,7 +1625,7 @@ impl Module { name, namespace, params, - types, + param_types: types, func, .. }, @@ -1645,6 +1684,8 @@ impl Module { self.all_type_iterators = type_iterators; self.indexed = true; } + + self } /// Does a type iterator exist in the entire module tree? diff --git a/src/module/resolvers/file.rs b/src/module/resolvers/file.rs index 17423536..c8146b3c 100644 --- a/src/module/resolvers/file.rs +++ b/src/module/resolvers/file.rs @@ -159,12 +159,10 @@ impl ModuleResolver for FileModuleResolver { _ => Box::new(EvalAltResult::ErrorInModule(path.to_string(), err, pos)), })?; - let mut m = Module::eval_ast_as_new(scope, &ast, engine).map_err(|err| { + let m = Module::eval_ast_as_new(scope, &ast, engine).map_err(|err| { Box::new(EvalAltResult::ErrorInModule(path.to_string(), err, pos)) })?; - m.build_index(); - module = Some(m.into()); module_ref = module.clone(); }; diff --git a/src/optimize.rs b/src/optimize.rs index 51fe2d2a..19ee51ce 100644 --- a/src/optimize.rs +++ b/src/optimize.rs @@ -1,10 +1,7 @@ //! Module implementing the AST optimizer. use crate::ast::{Expr, ScriptFnDef, Stmt}; -use crate::engine::{ - KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_IS_DEF_FN, KEYWORD_IS_DEF_VAR, KEYWORD_PRINT, - KEYWORD_TYPE_OF, -}; +use crate::engine::{KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_PRINT, KEYWORD_TYPE_OF}; use crate::fn_call::run_builtin_binary_op; use crate::parser::map_dynamic_to_expr; use crate::stdlib::{ @@ -467,11 +464,9 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) { fn optimize_expr(expr: &mut Expr, state: &mut State) { // These keywords are handled specially const DONT_EVAL_KEYWORDS: &[&str] = &[ - KEYWORD_PRINT, // side effects - KEYWORD_DEBUG, // side effects - KEYWORD_EVAL, // arbitrary scripts - KEYWORD_IS_DEF_FN, // functions collection is volatile - KEYWORD_IS_DEF_VAR, // variables scope is volatile + KEYWORD_PRINT, // side effects + KEYWORD_DEBUG, // side effects + KEYWORD_EVAL, // arbitrary scripts ]; match expr { @@ -650,7 +645,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) { let arg_types: StaticVec<_> = arg_values.iter().map(Dynamic::type_id).collect(); // Search for overloaded operators (can override built-in). - if !state.engine.has_override_by_name_and_arguments(&state.engine.global_sub_modules, state.lib, x.name.as_ref(), arg_types.as_ref(), false) { + if !state.engine.has_override_by_name_and_arguments(Some(&state.engine.global_sub_modules), state.lib, x.name.as_ref(), arg_types.as_ref(), false) { if let Some(result) = run_builtin_binary_op(x.name.as_ref(), &arg_values[0], &arg_values[1]) .ok().flatten() .and_then(|result| map_dynamic_to_expr(result, *pos)) diff --git a/src/packages/eval.rs b/src/packages/eval.rs deleted file mode 100644 index 525b8f3a..00000000 --- a/src/packages/eval.rs +++ /dev/null @@ -1,14 +0,0 @@ -use crate::plugin::*; -use crate::{def_package, Dynamic, EvalAltResult, ImmutableString}; - -def_package!(crate:EvalPackage:"Disable 'eval'.", lib, { - combine_with_exported_module!(lib, "eval", eval_override); -}); - -#[export_module] -mod eval_override { - #[rhai_fn(return_raw)] - pub fn eval(_script: ImmutableString) -> Result> { - Err("eval is evil!".into()) - } -} 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 ede3da61..bc764554 100644 --- a/src/packages/mod.rs +++ b/src/packages/mod.rs @@ -1,12 +1,11 @@ //! Module containing all built-in _packages_ available to Rhai, plus facilities to define custom packages. use crate::fn_native::{CallableFunction, IteratorFn}; -use crate::stdlib::any::TypeId; +use crate::stdlib::{any::TypeId, string::String}; use crate::{Module, Shared, StaticVec}; pub(crate) mod arithmetic; mod array_basic; -mod eval; mod fn_basic; mod iter_basic; mod logic; @@ -21,7 +20,6 @@ mod time_basic; pub use arithmetic::ArithmeticPackage; #[cfg(not(feature = "no_index"))] pub use array_basic::BasicArrayPackage; -pub use eval::EvalPackage; pub use fn_basic::BasicFnPackage; pub use iter_basic::BasicIteratorPackage; pub use logic::LogicPackage; @@ -89,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) @@ -133,6 +140,7 @@ macro_rules! def_package { pub fn new() -> Self { let mut module = $root::Module::new_with_capacity(1024); ::init(&mut module); + module.build_index(); Self(module.into()) } } diff --git a/src/packages/pkg_core.rs b/src/packages/pkg_core.rs index 284c5997..7480fdd1 100644 --- a/src/packages/pkg_core.rs +++ b/src/packages/pkg_core.rs @@ -4,9 +4,34 @@ use super::iter_basic::BasicIteratorPackage; use super::logic::LogicPackage; use super::string_basic::BasicStringPackage; -use crate::def_package; +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, +}; 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/packages/string_basic.rs b/src/packages/string_basic.rs index 2db28c7a..80686547 100644 --- a/src/packages/string_basic.rs +++ b/src/packages/string_basic.rs @@ -1,6 +1,6 @@ #![allow(non_snake_case)] -use crate::engine::{FN_TO_STRING, KEYWORD_DEBUG, KEYWORD_PRINT}; +use crate::engine::{KEYWORD_DEBUG, KEYWORD_PRINT}; use crate::plugin::*; use crate::stdlib::{ fmt::{Debug, Display}, @@ -32,7 +32,7 @@ macro_rules! gen_functions { macro_rules! reg_print_functions { ($mod_name:ident += $root:ident ; $($arg_type:ident),+) => { $( - set_exported_fn!($mod_name, FN_TO_STRING, $root::$arg_type::to_string_func); + set_exported_fn!($mod_name, "to_string", $root::$arg_type::to_string_func); set_exported_fn!($mod_name, KEYWORD_PRINT, $root::$arg_type::to_string_func); )* } } diff --git a/src/parse_error.rs b/src/parse_error.rs index 9b851818..010864cf 100644 --- a/src/parse_error.rs +++ b/src/parse_error.rs @@ -32,7 +32,7 @@ pub enum LexError { /// An identifier is in an invalid format. MalformedIdentifier(String), /// Bad symbol encountered when tokenizing the script text. - ImproperSymbol(String), + ImproperSymbol(String, String), } impl Error for LexError {} @@ -47,7 +47,10 @@ impl fmt::Display for LexError { Self::MalformedIdentifier(s) => write!(f, "{}: '{}'", self.desc(), s), Self::UnterminatedString => f.write_str(self.desc()), Self::StringTooLong(max) => write!(f, "{} ({})", self.desc(), max), - Self::ImproperSymbol(s) => f.write_str(s), + Self::ImproperSymbol(s, d) if d.is_empty() => { + write!(f, "Invalid symbol encountered: '{}'", s) + } + Self::ImproperSymbol(_, d) => f.write_str(d), } } } @@ -62,7 +65,7 @@ impl LexError { Self::MalformedNumber(_) => "Invalid number", Self::MalformedChar(_) => "Invalid character", Self::MalformedIdentifier(_) => "Variable name is not proper", - Self::ImproperSymbol(_) => "Invalid symbol encountered", + Self::ImproperSymbol(_, _) => "Invalid symbol encountered", } } /// Convert a `&LexError` into a [`ParseError`]. diff --git a/src/parser.rs b/src/parser.rs index aa64a272..10ebf146 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -46,11 +46,11 @@ struct ParseState<'e> { strings: HashMap, /// Encapsulates a local stack with variable names to simulate an actual runtime scope. stack: Vec<(ImmutableString, ScopeEntryType)>, + /// Size of the local variables stack upon entry of the current block scope. + entry_stack_len: usize, /// Tracks a list of external variables (variables that are not explicitly declared in the scope). #[cfg(not(feature = "no_closure"))] externals: HashMap, - /// Always search for variables instead of direct indexing into the scope. - always_search: bool, /// An indicator that disables variable capturing into externals one single time /// up until the nearest consumed Identifier token. /// If set to false the next call to `access_var` will not capture the variable. @@ -94,7 +94,7 @@ impl<'e> ParseState<'e> { allow_capture: true, strings: HashMap::with_capacity(64), stack: Vec::with_capacity(16), - always_search: false, + entry_stack_len: 0, #[cfg(not(feature = "no_module"))] modules: Default::default(), } @@ -110,12 +110,22 @@ impl<'e> ParseState<'e> { /// Return `None` when the variable name is not found in the `stack`. #[inline] fn access_var(&mut self, name: &str, _pos: Position) -> Option { + let mut barrier = false; + let index = self .stack .iter() .rev() .enumerate() - .find(|(_, (n, _))| *n == name) + .find(|(_, (n, _))| { + if n.is_empty() { + // Do not go beyond empty variable names + barrier = true; + false + } else { + *n == name + } + }) .and_then(|(i, _)| NonZeroUsize::new(i + 1)); #[cfg(not(feature = "no_closure"))] @@ -127,7 +137,7 @@ impl<'e> ParseState<'e> { self.allow_capture = true } - if self.always_search { + if barrier { None } else { index @@ -985,11 +995,8 @@ fn parse_primary( // Access to `this` as a variable is OK Token::Reserved(s) if s == KEYWORD_THIS && *next_token != Token::LeftParen => { if !settings.is_function_scope { - return Err(PERR::BadInput(LexError::ImproperSymbol(format!( - "'{}' can only be used in functions", - s - ))) - .into_err(settings.pos)); + let msg = format!("'{}' can only be used in functions", s); + return Err(PERR::BadInput(LexError::ImproperSymbol(s, msg)).into_err(settings.pos)); } else { let var_name_def = IdentX::new(state.get_interned_string(s), settings.pos); Expr::Variable(Box::new((None, None, 0, var_name_def))) @@ -1035,6 +1042,7 @@ fn parse_primary( LexError::UnexpectedInput(Token::Bang.syntax().to_string()).into_err(token_pos) } else { PERR::BadInput(LexError::ImproperSymbol( + "!".to_string(), "'!' cannot be used to call module functions".to_string(), )) .into_err(token_pos) @@ -1323,6 +1331,7 @@ fn make_assignment_stmt<'a>( } // ??? && ??? = rhs, ??? || ??? = rhs Expr::And(_, _) | Expr::Or(_, _) => Err(PERR::BadInput(LexError::ImproperSymbol( + "=".to_string(), "Possibly a typo of '=='?".to_string(), )) .into_err(pos)), @@ -1428,10 +1437,13 @@ fn make_dot_expr( && [crate::engine::KEYWORD_FN_PTR, crate::engine::KEYWORD_EVAL] .contains(&x.name.as_ref()) => { - return Err(PERR::BadInput(LexError::ImproperSymbol(format!( - "'{}' should not be called in method style. Try {}(...);", - x.name, x.name - ))) + return Err(PERR::BadInput(LexError::ImproperSymbol( + x.name.to_string(), + format!( + "'{}' should not be called in method style. Try {}(...);", + x.name, x.name + ), + )) .into_err(pos)); } // lhs.func!(...) @@ -1789,11 +1801,13 @@ fn parse_custom_syntax( // Adjust the variables stack match syntax.scope_delta { delta if delta > 0 => { + // Add enough empty variable names to the stack. + // Empty variable names act as a barrier so earlier variables will not be matched. + // Variable searches stop at the first empty variable name. state.stack.resize( state.stack.len() + delta as usize, ("".into(), ScopeEntryType::Normal), ); - state.always_search = true; } delta if delta < 0 && state.stack.len() <= delta.abs() as usize => state.stack.clear(), delta if delta < 0 => state @@ -1920,20 +1934,22 @@ fn ensure_not_statement_expr(input: &mut TokenStream, type_name: &str) -> Result fn ensure_not_assignment(input: &mut TokenStream) -> Result<(), ParseError> { match input.peek().unwrap() { (Token::Equals, pos) => Err(PERR::BadInput(LexError::ImproperSymbol( + "=".to_string(), "Possibly a typo of '=='?".to_string(), )) .into_err(*pos)), - (Token::PlusAssign, pos) - | (Token::MinusAssign, pos) - | (Token::MultiplyAssign, pos) - | (Token::DivideAssign, pos) - | (Token::LeftShiftAssign, pos) - | (Token::RightShiftAssign, pos) - | (Token::ModuloAssign, pos) - | (Token::PowerOfAssign, pos) - | (Token::AndAssign, pos) - | (Token::OrAssign, pos) - | (Token::XOrAssign, pos) => Err(PERR::BadInput(LexError::ImproperSymbol( + (token @ Token::PlusAssign, pos) + | (token @ Token::MinusAssign, pos) + | (token @ Token::MultiplyAssign, pos) + | (token @ Token::DivideAssign, pos) + | (token @ Token::LeftShiftAssign, pos) + | (token @ Token::RightShiftAssign, pos) + | (token @ Token::ModuloAssign, pos) + | (token @ Token::PowerOfAssign, pos) + | (token @ Token::AndAssign, pos) + | (token @ Token::OrAssign, pos) + | (token @ Token::XOrAssign, pos) => Err(PERR::BadInput(LexError::ImproperSymbol( + token.syntax().to_string(), "Expecting a boolean expression, not an assignment".to_string(), )) .into_err(*pos)), @@ -2293,8 +2309,9 @@ fn parse_block( settings.ensure_level_within_max_limit(state.max_expr_depth)?; let mut statements = Vec::with_capacity(8); - let prev_always_search = state.always_search; - let prev_stack_len = state.stack.len(); + + let prev_entry_stack_len = state.entry_stack_len; + state.entry_stack_len = state.stack.len(); #[cfg(not(feature = "no_module"))] let prev_mods_len = state.modules.len(); @@ -2338,15 +2355,12 @@ fn parse_block( } } - state.stack.truncate(prev_stack_len); + state.stack.truncate(state.entry_stack_len); + state.entry_stack_len = prev_entry_stack_len; #[cfg(not(feature = "no_module"))] state.modules.truncate(prev_mods_len); - // The impact of new local variables goes away at the end of a block - // because any new variables introduced will go out of scope - state.always_search = prev_always_search; - Ok(Stmt::Block(statements, settings.pos)) } 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/serde_impl/de.rs b/src/serde_impl/de.rs index b4e6786c..6d7fcfed 100644 --- a/src/serde_impl/de.rs +++ b/src/serde_impl/de.rs @@ -120,7 +120,7 @@ pub fn from_dynamic<'de, T: Deserialize<'de>>( impl Error for Box { fn custom(err: T) -> Self { EvalAltResult::ErrorParsing( - ParseErrorType::BadInput(LexError::ImproperSymbol(err.to_string())), + ParseErrorType::BadInput(LexError::ImproperSymbol("".to_string(), err.to_string())), Position::NONE, ) .into() diff --git a/src/syntax.rs b/src/syntax.rs index e3e6f1ca..695822e9 100644 --- a/src/syntax.rs +++ b/src/syntax.rs @@ -137,11 +137,14 @@ impl Engine { .map(|v| v.is_keyword() || v.is_reserved()) .unwrap_or(false) => { - return Err(LexError::ImproperSymbol(format!( - "Improper symbol for custom syntax at position #{}: '{}'", - segments.len() + 1, - s - )) + return Err(LexError::ImproperSymbol( + s.to_string(), + format!( + "Improper symbol for custom syntax at position #{}: '{}'", + segments.len() + 1, + s + ), + ) .into_err(Position::NONE) .into()); } @@ -154,11 +157,14 @@ impl Engine { } // Anything else is an error _ => { - return Err(LexError::ImproperSymbol(format!( - "Improper symbol for custom syntax at position #{}: '{}'", - segments.len() + 1, - s - )) + return Err(LexError::ImproperSymbol( + s.to_string(), + format!( + "Improper symbol for custom syntax at position #{}: '{}'", + segments.len() + 1, + s + ), + ) .into_err(Position::NONE) .into()); } diff --git a/src/token.rs b/src/token.rs index c36a5651..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, @@ -532,18 +532,13 @@ impl Token { "import" | "export" | "as" => Reserved(syntax.into()), "===" | "!==" | "->" | "<-" | ":=" | "::<" | "(*" | "*)" | "#" | "public" | "new" - | "use" | "module" | "package" | "var" | "static" | "shared" | "with" | "each" - | "then" | "goto" | "unless" | "exit" | "match" | "case" | "default" | "void" - | "null" | "nil" | "spawn" | "thread" | "go" | "sync" | "async" | "await" | "yield" => { - Reserved(syntax.into()) - } + | "use" | "module" | "package" | "var" | "static" | "begin" | "end" | "shared" + | "with" | "each" | "then" | "goto" | "unless" | "exit" | "match" | "case" + | "default" | "void" | "null" | "nil" | "spawn" | "thread" | "go" | "sync" + | "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, }) @@ -1514,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, } } @@ -1529,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, } } @@ -1658,39 +1648,41 @@ impl<'a> Iterator for TokenIterator<'a, '_> { Some((Token::Reserved(s), pos)) => Some((match (s.as_str(), self.engine.custom_keywords.contains_key(&s)) { - ("===", false) => Token::LexError(LERR::ImproperSymbol( + ("===", false) => Token::LexError(LERR::ImproperSymbol(s, "'===' is not a valid operator. This is not JavaScript! Should it be '=='?".to_string(), )), - ("!==", false) => Token::LexError(LERR::ImproperSymbol( + ("!==", false) => Token::LexError(LERR::ImproperSymbol(s, "'!==' is not a valid operator. This is not JavaScript! Should it be '!='?".to_string(), )), - ("->", false) => Token::LexError(LERR::ImproperSymbol( + ("->", false) => Token::LexError(LERR::ImproperSymbol(s, "'->' is not a valid symbol. This is not C or C++!".to_string())), - ("<-", false) => Token::LexError(LERR::ImproperSymbol( + ("<-", false) => Token::LexError(LERR::ImproperSymbol(s, "'<-' is not a valid symbol. This is not Go! Should it be '<='?".to_string(), )), - (":=", false) => Token::LexError(LERR::ImproperSymbol( - "':=' is not a valid assignment operator. This is not Go! Should it be simply '='?".to_string(), + (":=", false) => Token::LexError(LERR::ImproperSymbol(s, + "':=' is not a valid assignment operator. This is not Go or Pascal! Should it be simply '='?".to_string(), )), - ("::<", false) => Token::LexError(LERR::ImproperSymbol( + ("::<", false) => Token::LexError(LERR::ImproperSymbol(s, "'::<>' is not a valid symbol. This is not Rust! Should it be '::'?".to_string(), )), - ("(*", false) | ("*)", false) => Token::LexError(LERR::ImproperSymbol( + ("(*", false) | ("*)", false) | ("begin", false) | ("end", false) => Token::LexError(LERR::ImproperSymbol(s, "'(* .. *)' is not a valid comment format. This is not Pascal! Should it be '/* .. */'?".to_string(), )), - ("#", false) => Token::LexError(LERR::ImproperSymbol( + ("#", false) => Token::LexError(LERR::ImproperSymbol(s, "'#' is not a valid symbol. Should it be '#{'?".to_string(), )), // Reserved keyword/operator that is custom. (_, true) => Token::Custom(s), // Reserved operator that is not custom. - (token, false) if !is_valid_identifier(token.chars()) => Token::LexError(LERR::ImproperSymbol( - format!("'{}' is a reserved symbol", token) - )), + (token, false) if !is_valid_identifier(token.chars()) => { + let msg = format!("'{}' is a reserved symbol", token); + Token::LexError(LERR::ImproperSymbol(s, msg)) + }, // Reserved keyword that is not custom and disabled. - (token, false) if self.engine.disabled_symbols.contains(token) => Token::LexError(LERR::ImproperSymbol( - format!("reserved symbol '{}' is disabled", token) - )), + (token, false) if self.engine.disabled_symbols.contains(token) => { + let msg = format!("reserved symbol '{}' is disabled", token); + Token::LexError(LERR::ImproperSymbol(s, msg)) + }, // Reserved keyword/operator that is not custom. (_, false) => Token::Reserved(s), }, pos)), diff --git a/tests/constants.rs b/tests/constants.rs index 2f516a1b..090abfaa 100644 --- a/tests/constants.rs +++ b/tests/constants.rs @@ -1,4 +1,4 @@ -use rhai::{Engine, EvalAltResult, ParseErrorType, Scope, INT}; +use rhai::{Engine, EvalAltResult, ParseErrorType, RegisterFn, Scope, INT}; #[test] fn test_constant() -> Result<(), Box> { @@ -62,3 +62,36 @@ fn test_var_is_def() -> Result<(), Box> { Ok(()) } + +#[cfg(not(feature = "no_object"))] +#[test] +fn test_constant_mut() -> Result<(), Box> { + #[derive(Debug, Clone)] + struct TestStruct(INT); // custom type + + let mut engine = Engine::new(); + + engine + .register_type_with_name::("TestStruct") + .register_get("value", |obj: &mut TestStruct| obj.0) + .register_fn("update_value", |obj: &mut TestStruct, value: INT| { + obj.0 = value + }); + + let mut scope = Scope::new(); + + scope.push_constant("MY_NUMBER", TestStruct(123)); + + assert_eq!( + engine.eval_with_scope::( + &mut scope, + r" + MY_NUMBER.update_value(42); + MY_NUMBER.value + ", + )?, + 42 + ); + + Ok(()) +} diff --git a/tests/eval.rs b/tests/eval.rs index 7732bd25..568a5e5b 100644 --- a/tests/eval.rs +++ b/tests/eval.rs @@ -1,4 +1,4 @@ -use rhai::{Engine, EvalAltResult, Scope, INT}; +use rhai::{Engine, EvalAltResult, LexError, ParseErrorType, RegisterFn, Scope, INT}; #[test] fn test_eval() -> Result<(), Box> { @@ -98,10 +98,34 @@ fn test_eval_override() -> Result<(), Box> { fn eval(x) { x } // reflect the script back eval("40 + 2") - "# + "# )?, "40 + 2" ); + let mut engine = Engine::new(); + + // Reflect the script back + engine.register_fn("eval", |script: &str| script.to_string()); + + assert_eq!(engine.eval::(r#"eval("40 + 2")"#)?, "40 + 2"); + + Ok(()) +} + +#[test] +fn test_eval_disabled() -> Result<(), Box> { + let mut engine = Engine::new(); + + engine.disable_symbol("eval"); + + assert!(matches!( + *engine + .compile(r#"eval("40 + 2")"#) + .expect_err("should error") + .0, + ParseErrorType::BadInput(LexError::ImproperSymbol(err, _)) if err == "eval" + )); + Ok(()) } diff --git a/tests/syntax.rs b/tests/syntax.rs index 819931e6..526ebf92 100644 --- a/tests/syntax.rs +++ b/tests/syntax.rs @@ -79,6 +79,7 @@ fn test_custom_syntax() -> Result<(), Box> { .expect_err("should error") .0, ParseErrorType::BadInput(LexError::ImproperSymbol( + "!".to_string(), "Improper symbol for custom syntax at position #1: '!'".to_string() )) ); @@ -100,6 +101,7 @@ fn test_custom_syntax_raw() -> Result<(), Box> { s => Err(ParseError( Box::new(ParseErrorType::BadInput(LexError::ImproperSymbol( s.to_string(), + "".to_string(), ))), Position::NONE, )), @@ -128,7 +130,7 @@ fn test_custom_syntax_raw() -> Result<(), Box> { assert_eq!(engine.eval::("(hello kitty) + foo")?, 1041); assert_eq!( *engine.compile("hello hey").expect_err("should error").0, - ParseErrorType::BadInput(LexError::ImproperSymbol("hey".to_string())) + ParseErrorType::BadInput(LexError::ImproperSymbol("hey".to_string(), "".to_string())) ); Ok(())