Merge pull request #296 from schungx/master

gen_fn_signatures API.
This commit is contained in:
Stephen Chung 2020-11-22 17:59:43 +08:00 committed by GitHub
commit c18a6451f4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
39 changed files with 709 additions and 310 deletions

View File

@ -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
------------

View File

@ -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<Vec<String>>,
@ -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<syn::Stmt> = Vec::new();
let mut unpack_exprs: Vec<syn::Expr> = Vec::new();
let mut input_type_names: Vec<String> = Vec::new();
let mut input_type_exprs: Vec<syn::Expr> = 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::<syn::Expr>(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::<syn::Expr>(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<dyn PluginFunction> { 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()
}

View File

@ -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());
};

View File

@ -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::Type>, 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<String> = 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<syn::Expr> = function
.arg_list()
.map(|fnarg| match fnarg {
@ -117,6 +128,7 @@ pub(crate) fn generate_body(
},
t => t.clone(),
};
syn::parse2::<syn::Expr>(quote! {
core::any::TypeId::of::<#arg_type>()})
.unwrap()
@ -138,7 +150,8 @@ pub(crate) fn generate_body(
);
set_fn_stmts.push(
syn::parse2::<syn::Stmt>(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<ExportedFn>) -> 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);

View File

@ -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<dyn PluginFunction> { 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<dyn PluginFunction> { 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::<usize>()].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<dyn PluginFunction> { 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::<usize>()].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<dyn PluginFunction> { 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<dyn PluginFunction> { 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::<usize>()].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<dyn PluginFunction> { 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::<usize>(),
TypeId::of::<usize>()].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<dyn PluginFunction> { 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::<usize>(),
TypeId::of::<usize>()].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<dyn PluginFunction> { 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::<ImmutableString>()].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()
}

View File

@ -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<dyn PluginFunction> {
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::<INT>()],
m.set_fn("add_one_to", FnNamespace::Global, FnAccess::Public, Some(&["x: INT"]), &[core::any::TypeId::of::<INT>()],
add_one_to_token().into());
if flatten {} else {}
}
@ -375,6 +384,9 @@ mod generate_tests {
fn clone_boxed(&self) -> Box<dyn PluginFunction> {
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::<INT>()].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::<INT>()],
m.set_fn("add_one_to", FnNamespace::Internal, FnAccess::Public, Some(&["x: INT"]), &[core::any::TypeId::of::<INT>()],
add_one_to_token().into());
if flatten {} else {}
}
@ -436,6 +452,9 @@ mod generate_tests {
fn clone_boxed(&self) -> Box<dyn PluginFunction> {
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::<INT>()].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::<INT>()],
m.set_fn("add_n", FnNamespace::Internal, FnAccess::Public, Some(&["x: INT"]), &[core::any::TypeId::of::<INT>()],
add_one_to_token().into());
m.set_fn("add_n", FnNamespace::Internal, FnAccess::Public, &[core::any::TypeId::of::<INT>(),
core::any::TypeId::of::<INT>()],
m.set_fn("add_n", FnNamespace::Internal, FnAccess::Public, Some(&["x: INT", "y: INT"]),
&[core::any::TypeId::of::<INT>(), core::any::TypeId::of::<INT>()],
add_n_to_token().into());
if flatten {} else {}
}
@ -511,6 +534,9 @@ mod generate_tests {
fn clone_boxed(&self) -> Box<dyn PluginFunction> {
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::<INT>()].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<dyn PluginFunction> {
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::<INT>(),
TypeId::of::<INT>()].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::<INT>(),
core::any::TypeId::of::<INT>()],
m.set_fn("add_together", FnNamespace::Internal, FnAccess::Public, Some(&["x: INT", "y: INT"]),
&[core::any::TypeId::of::<INT>(), core::any::TypeId::of::<INT>()],
add_together_token().into());
if flatten {} else {}
}
@ -602,6 +638,9 @@ mod generate_tests {
fn clone_boxed(&self) -> Box<dyn PluginFunction> {
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::<INT>(),
TypeId::of::<INT>()].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::<INT>(),
core::any::TypeId::of::<INT>()],
m.set_fn("add", FnNamespace::Internal, FnAccess::Public, Some(&["x: INT", "y: INT"]),
&[core::any::TypeId::of::<INT>(), core::any::TypeId::of::<INT>()],
add_together_token().into());
m.set_fn("+", FnNamespace::Internal, FnAccess::Public, &[core::any::TypeId::of::<INT>(),
core::any::TypeId::of::<INT>()],
m.set_fn("+", FnNamespace::Internal, FnAccess::Public, Some(&["x: INT", "y: INT"]),
&[core::any::TypeId::of::<INT>(), core::any::TypeId::of::<INT>()],
add_together_token().into());
m.set_fn("add_together", FnNamespace::Internal, FnAccess::Public, &[core::any::TypeId::of::<INT>(),
core::any::TypeId::of::<INT>()],
m.set_fn("add_together", FnNamespace::Internal, FnAccess::Public, Some(&["x: INT", "y: INT"]),
&[core::any::TypeId::of::<INT>(), core::any::TypeId::of::<INT>()],
add_together_token().into());
if flatten {} else {}
}
@ -673,6 +716,9 @@ mod generate_tests {
fn clone_boxed(&self) -> Box<dyn PluginFunction> {
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::<INT>(),
TypeId::of::<INT>()].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<dyn PluginFunction> {
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::<ImmutableString>()],
Some(&["x: &str"]), &[core::any::TypeId::of::<ImmutableString>()],
print_out_to_token().into());
if flatten {} else {}
}
@ -1003,6 +1066,9 @@ mod generate_tests {
fn clone_boxed(&self) -> Box<dyn PluginFunction> {
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::<ImmutableString>()].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::<ImmutableString>()],
Some(&["x: String"]), &[core::any::TypeId::of::<ImmutableString>()],
print_out_to_token().into());
if flatten {} else {}
}
@ -1065,6 +1135,9 @@ mod generate_tests {
fn clone_boxed(&self) -> Box<dyn PluginFunction> {
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::<ImmutableString>()].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::<FLOAT>()],
Some(&["x: &mut FLOAT"]), &[core::any::TypeId::of::<FLOAT>()],
increment_token().into());
if flatten {} else {}
}
@ -1127,6 +1204,9 @@ mod generate_tests {
fn clone_boxed(&self) -> Box<dyn PluginFunction> {
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::<FLOAT>()].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::<FLOAT>()],
Some(&["x: &mut FLOAT"]), &[core::any::TypeId::of::<FLOAT>()],
increment_token().into());
if flatten {} else {}
}
@ -1192,6 +1276,9 @@ mod generate_tests {
fn clone_boxed(&self) -> Box<dyn PluginFunction> {
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::<FLOAT>()].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::<FLOAT>()],
Some(&["x: &mut FLOAT"]), &[core::any::TypeId::of::<FLOAT>()],
increment_token().into());
if flatten {} else {}
}
@ -1277,6 +1369,9 @@ mod generate_tests {
fn clone_boxed(&self) -> Box<dyn PluginFunction> {
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::<FLOAT>()].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::<u64>()],
m.set_fn("get$square", FnNamespace::Internal, FnAccess::Public, Some(&["x: &mut u64"]), &[core::any::TypeId::of::<u64>()],
int_foo_token().into());
if flatten {} else {}
}
@ -1360,6 +1460,9 @@ mod generate_tests {
fn clone_boxed(&self) -> Box<dyn PluginFunction> {
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::<u64>()].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::<u64>()],
m.set_fn("square", FnNamespace::Internal, FnAccess::Public, Some(&["x: &mut u64"]), &[core::any::TypeId::of::<u64>()],
int_foo_token().into());
m.set_fn("get$square", FnNamespace::Internal, FnAccess::Public, &[core::any::TypeId::of::<u64>()],
m.set_fn("get$square", FnNamespace::Internal, FnAccess::Public, Some(&["x: &mut u64"]), &[core::any::TypeId::of::<u64>()],
int_foo_token().into());
if flatten {} else {}
}
@ -1424,6 +1531,9 @@ mod generate_tests {
fn clone_boxed(&self) -> Box<dyn PluginFunction> {
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::<u64>()].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::<u64>(),
core::any::TypeId::of::<u64>()],
m.set_fn("set$squared", FnNamespace::Internal, FnAccess::Public, Some(&["x: &mut u64", "y: u64"]),
&[core::any::TypeId::of::<u64>(), core::any::TypeId::of::<u64>()],
int_foo_token().into());
if flatten {} else {}
}
@ -1489,6 +1602,9 @@ mod generate_tests {
fn clone_boxed(&self) -> Box<dyn PluginFunction> {
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::<u64>(), TypeId::of::<u64>()].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::<u64>(),
core::any::TypeId::of::<u64>()],
m.set_fn("set_sq", FnNamespace::Internal, FnAccess::Public, Some(&["x: &mut u64", "y: u64"]),
&[core::any::TypeId::of::<u64>(), core::any::TypeId::of::<u64>()],
int_foo_token().into());
m.set_fn("set$squared", FnNamespace::Internal, FnAccess::Public,
&[core::any::TypeId::of::<u64>(),
core::any::TypeId::of::<u64>()],
m.set_fn("set$squared", FnNamespace::Internal, FnAccess::Public, Some(&["x: &mut u64", "y: u64"]),
&[core::any::TypeId::of::<u64>(), core::any::TypeId::of::<u64>()],
int_foo_token().into());
if flatten {} else {}
}
@ -1558,6 +1676,9 @@ mod generate_tests {
fn clone_boxed(&self) -> Box<dyn PluginFunction> {
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::<u64>(), TypeId::of::<u64>()].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::<MyCollection>(),
core::any::TypeId::of::<u64>()],
get_by_index_token().into());
@ -1623,6 +1749,9 @@ mod generate_tests {
fn clone_boxed(&self) -> Box<dyn PluginFunction> {
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::<MyCollection>(),
TypeId::of::<u64>()].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::<MyCollection>(),
core::any::TypeId::of::<u64>()],
get_by_index_token().into());
m.set_fn("index$get$", FnNamespace::Internal, FnAccess::Public,
Some(&["x: &mut MyCollection", "i: u64"]),
&[core::any::TypeId::of::<MyCollection>(),
core::any::TypeId::of::<u64>()],
get_by_index_token().into());
@ -1693,6 +1828,9 @@ mod generate_tests {
fn clone_boxed(&self) -> Box<dyn PluginFunction> {
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::<MyCollection>(),
TypeId::of::<u64>()].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::<MyCollection>(),
core::any::TypeId::of::<u64>(),
core::any::TypeId::of::<FLOAT>()],
@ -1761,6 +1904,9 @@ mod generate_tests {
fn clone_boxed(&self) -> Box<dyn PluginFunction> {
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::<MyCollection>(),
TypeId::of::<u64>(),
@ -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::<MyCollection>(),
core::any::TypeId::of::<u64>(),
core::any::TypeId::of::<FLOAT>()],
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::<MyCollection>(),
core::any::TypeId::of::<u64>(),
core::any::TypeId::of::<FLOAT>()],
@ -1835,6 +1987,9 @@ mod generate_tests {
fn clone_boxed(&self) -> Box<dyn PluginFunction> {
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::<MyCollection>(),
TypeId::of::<u64>(),
@ -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)]

View File

@ -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)

View File

@ -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 |

View File

@ -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>("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
")?;
```

View File

@ -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<EvalAltResu
Err(format!("eval is evil! I refuse to run {}", script).into())
});
```
`EvalPackage`
-------------
There is even a package named [`EvalPackage`][packages] which implements the disabling override:
```rust
use rhai::Engine;
use rhai::packages::Package // load the 'Package' trait to use packages
use rhai::packages::EvalPackage; // the 'eval' package disables 'eval'
let mut engine = Engine::new();
let package = EvalPackage::new(); // create the package
engine.load_package(package.get()); // load the package
```

View File

@ -104,7 +104,7 @@ This is similar to Rust and many other modern languages, such as JavaScript's `f
`is_def_fn`
-----------
Use `is_def_fn` to detect if a function is defined (and therefore callable), based on its name
Use `is_def_fn` to detect if a Rhai function is defined (and therefore callable), based on its name
and the number of parameters.
```rust

View File

@ -6,13 +6,14 @@ Keywords
The following are reserved keywords in Rhai:
| Active keywords | Reserved keywords | Usage | Inactive under feature |
| ------------------------------------------------- | ---------------------------------------------------------- | ---------------------- | :--------------------: |
| `true`, `false` | | boolean constants | |
| `let`, `const` | `var`, `static` | variable declarations | |
| ---------------------------------------------------------------- | ---------------------------------------------------------- | ---------------------- | :--------------------: |
| `true`, `false` | | constants | |
| `let`, `const` | `var`, `static` | variables | |
| | `begin`, `end` | block scopes | |
| `is_shared` | | shared values | [`no_closure`] |
| `if`, `else` | `then`, `goto`, `exit` | control flow | |
| | `switch`, `match`, `case` | matching | |
| `while`, `loop`, `for`, `in`, `continue`, `break` | `do`, `each` | looping | |
| `if`, `else` | `then`, `unless`, `goto`, `exit` | control flow | |
| `switch` | `match`, `case` | switching and matching | |
| `do`, `while`, `loop`, `until`, `for`, `in`, `continue`, `break` | `each` | looping | |
| `fn`, `private` | `public`, `new` | functions | [`no_function`] |
| `return` | | return values | |
| `throw`, `try`, `catch` | | throw/catch exceptions | |

View File

@ -9,7 +9,7 @@ Terminated by '`;`'
Statements are terminated by semicolons '`;`' and they are mandatory,
except for the _last_ statement in a _block_ (enclosed by '`{`' .. '`}`' pairs) where it can be omitted.
Semicolons can also be omitted if the statement contains a block itself
Semicolons can also be omitted if the statement ends with a block itself
(e.g. the `if`, `while`, `for` and `loop` statements).
```rust
@ -35,6 +35,8 @@ Statement Expression
A statement can be used anywhere where an expression is expected. These are called, for lack of a more
creative name, "statement expressions."
The _last_ statement of a statement block is _always_ the block's return value when used as a statement.
The _last_ statement of a statement block is _always_ the block's return value when used as a statement,
_regardless_ of whether it is terminated by a semicolon or not. This is different from Rust where,
if the last statement is terminated by a semicolon, the block's return value is taken to be `()`.
If the last statement has no return value (e.g. variable definitions, assignments) then it is assumed to be [`()`].

View File

@ -19,7 +19,6 @@ Built-In Packages
| `BasicArrayPackage` | basic [array] functions (not available under `no_index`) | no | yes |
| `BasicMapPackage` | basic [object map] functions (not available under `no_object`) | no | yes |
| `BasicFnPackage` | basic methods for [function pointers]. | yes | yes |
| `EvalPackage` | disable [`eval`] | no | no |
| `CorePackage` | basic essentials | yes | yes |
| `StandardPackage` | standard library (default for `Engine::new`) | no | yes |

View File

@ -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,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;
}
_ => (),
}

View File

@ -1154,8 +1154,8 @@ mod tests {
assert_eq!(size_of::<crate::ast::Stmt>(), 32);
assert_eq!(size_of::<Option<crate::ast::Stmt>>(), 32);
assert_eq!(size_of::<crate::Scope>(), 72);
assert_eq!(size_of::<crate::LexError>(), 32);
assert_eq!(size_of::<crate::LexError>(), 56);
assert_eq!(size_of::<crate::ParseError>(), 16);
assert_eq!(size_of::<crate::EvalAltResult>(), 64);
assert_eq!(size_of::<crate::EvalAltResult>(), 72);
}
}

View File

@ -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<Item = (&str, Shared<Module>)> {
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<Item = (ImmutableString, Shared<Module>)> + '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<Item = (ImmutableString, Shared<Module>)> {
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<Item = (ImmutableString, Shared<Module>)>) {
@ -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"))]

View File

@ -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<String> {
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

View File

@ -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::<ImmutableString>()));
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::<ImmutableString>()));
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::<ImmutableString>(), TypeId::of::<INT>()]
.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::<ImmutableString>(err, args_expr[0].position())
})?;
let num_params = num_params.as_int().map_err(|err| {
self.make_type_mismatch_err::<INT>(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::<ImmutableString>()));
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 =

View File

@ -49,8 +49,8 @@ pub type Locked<T> = crate::stdlib::sync::RwLock<T>;
#[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)>

View File

@ -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),*))
);

View File

@ -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<StaticVec<TypeId>>,
pub param_types: Option<StaticVec<TypeId>>,
/// Parameter names (if available).
pub param_names: Option<StaticVec<ImmutableString>>,
}
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(&params.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<Item = String> + '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<String>,
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?

View File

@ -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();
};

View File

@ -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::{
@ -470,8 +467,6 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) {
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
];
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))

View File

@ -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<Dynamic, Box<EvalAltResult>> {
Err("eval is evil!".into())
}
}

View File

@ -48,7 +48,8 @@ macro_rules! reg_range {
($lib:expr, $x:expr, $( $y:ty ),*) => (
$(
$lib.set_iterator::<Range<$y>>();
$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::<StepRange<$y>>();
$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::<Range<INT>>();
lib.set_fn_2("range", get_range::<INT>);
let hash = lib.set_fn_2("range", get_range::<INT>);
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::<StepRange<INT>>();
lib.set_fn_3("range", get_step_range::<INT>);
let hash = lib.set_fn_3("range", get_step_range::<INT>);
lib.update_fn_param_names(hash, &["from: INT", "to: INT", "step: INT"]);
#[cfg(not(feature = "only_i32"))]
#[cfg(not(feature = "only_i64"))]

View File

@ -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<Item = &PackageLibrary> {
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<Item = String> + '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);
<Self as $root::packages::Package>::init(&mut module);
module.build_index();
Self(module.into())
}
}

View File

@ -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::<INT>();
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::<ImmutableString>(), TypeId::of::<INT>()],
CallableFunction::from_method(Box::new(f)));
}
ArithmeticPackage::init(lib);
LogicPackage::init(lib);
BasicStringPackage::init(lib);

View File

@ -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);
)* }
}

View File

@ -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`].

View File

@ -46,11 +46,11 @@ struct ParseState<'e> {
strings: HashMap<String, ImmutableString>,
/// 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<ImmutableString, Position>,
/// 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<NonZeroUsize> {
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!(
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))
}

View File

@ -33,6 +33,9 @@ pub trait PluginFunction {
/// Convert a plugin function into a boxed trait object.
fn clone_boxed(&self) -> Box<dyn PluginFunction>;
/// 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]>;
}

View File

@ -120,7 +120,7 @@ pub fn from_dynamic<'de, T: Deserialize<'de>>(
impl Error for Box<EvalAltResult> {
fn custom<T: fmt::Display>(err: T) -> Self {
EvalAltResult::ErrorParsing(
ParseErrorType::BadInput(LexError::ImproperSymbol(err.to_string())),
ParseErrorType::BadInput(LexError::ImproperSymbol("".to_string(), err.to_string())),
Position::NONE,
)
.into()

View File

@ -137,11 +137,14 @@ impl Engine {
.map(|v| v.is_keyword() || v.is_reserved())
.unwrap_or(false) =>
{
return Err(LexError::ImproperSymbol(format!(
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!(
return Err(LexError::ImproperSymbol(
s.to_string(),
format!(
"Improper symbol for custom syntax at position #{}: '{}'",
segments.len() + 1,
s
))
),
)
.into_err(Position::NONE)
.into());
}

View File

@ -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)),

View File

@ -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<EvalAltResult>> {
@ -62,3 +62,36 @@ fn test_var_is_def() -> Result<(), Box<EvalAltResult>> {
Ok(())
}
#[cfg(not(feature = "no_object"))]
#[test]
fn test_constant_mut() -> Result<(), Box<EvalAltResult>> {
#[derive(Debug, Clone)]
struct TestStruct(INT); // custom type
let mut engine = Engine::new();
engine
.register_type_with_name::<TestStruct>("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::<INT>(
&mut scope,
r"
MY_NUMBER.update_value(42);
MY_NUMBER.value
",
)?,
42
);
Ok(())
}

View File

@ -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<EvalAltResult>> {
@ -103,5 +103,29 @@ fn test_eval_override() -> Result<(), Box<EvalAltResult>> {
"40 + 2"
);
let mut engine = Engine::new();
// Reflect the script back
engine.register_fn("eval", |script: &str| script.to_string());
assert_eq!(engine.eval::<String>(r#"eval("40 + 2")"#)?, "40 + 2");
Ok(())
}
#[test]
fn test_eval_disabled() -> Result<(), Box<EvalAltResult>> {
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(())
}

View File

@ -79,6 +79,7 @@ fn test_custom_syntax() -> Result<(), Box<EvalAltResult>> {
.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<EvalAltResult>> {
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<EvalAltResult>> {
assert_eq!(engine.eval::<INT>("(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(())