Merge pull request #376 from schungx/master

Put functions metadata under metadata feature gate.
This commit is contained in:
Stephen Chung 2021-03-25 18:58:47 +08:00 committed by GitHub
commit 05ddeb0d9e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 805 additions and 1242 deletions

View File

@ -18,7 +18,7 @@ jobs:
os: [ubuntu-latest] os: [ubuntu-latest]
flags: flags:
- "" - ""
- "--features metadata,internals" - "--features metadata,serde,internals"
- "--features unchecked" - "--features unchecked"
- "--features sync" - "--features sync"
- "--features no_optimize" - "--features no_optimize"
@ -34,7 +34,7 @@ jobs:
- "--features no_module" - "--features no_module"
- "--features no_closure" - "--features no_closure"
- "--features unicode-xid-ident" - "--features unicode-xid-ident"
- "--features sync,no_function,no_float,no_optimize,no_module,no_closure,metadata,unchecked" - "--features sync,no_function,no_float,no_optimize,no_module,no_closure,metadata,serde,unchecked"
toolchain: [stable] toolchain: [stable]
experimental: [false] experimental: [false]
include: include:

View File

@ -4,31 +4,37 @@ Rhai Release Notes
Version 0.19.15 Version 0.19.15
=============== ===============
This version replaces internal usage of `HashMap` with `BTreeMap` in many cases, which should result This version replaces all internal usage of `HashMap` with `BTreeMap`, which should result
in speed improvements because a `BTreeMap` is faster when the number of items held is small. in some speed improvement because a `BTreeMap` is leaner when the number of items held is small.
Most, if not all, collections in Rhai hold very few data items, so this is a typical scenario of
many tiny-sized collections.
This also translates to the Rhai object map type, `Map`, which used to be an alias to `HashMap` and The Rhai object map type, `Map`, used to be an alias to `HashMap` and is now aliased to `BTreeMap`
is now aliased to `BTreeMap` instead. This change is due to the fact that, in the vast majority of instead. This is also because, in the vast majority of usage cases, the number of properties held by
object map usages, the number of properties held is small. an object map is small.
`HashMap` and `BTreeMap` have almost identical public API's so this change is unlikely to break a `HashMap` and `BTreeMap` have almost identical public API's so this change is unlikely to break
lot of existing code. existing code.
All function signature/metadata methods are now grouped under the umbrella `metadata` feature.
Breaking changes Breaking changes
---------------- ----------------
* `Map` is now an alias to `BTreeMap` instead of `HashMap`. This is because most object maps used have few properties. * `Map` is now an alias to `BTreeMap` instead of `HashMap` because most object maps hold few properties.
* The traits `RegisterFn` and `RegisterResultFn` are removed. `Engine::register_fn` and `Engine::register_result_fn` are now implemented directly on `Engine`. * The traits `RegisterFn` and `RegisterResultFn` are removed. `Engine::register_fn` and `Engine::register_result_fn` are now implemented directly on `Engine`.
* `FnPtr::call_dynamic` now takes `&NativeCallContext` instead of consuming it. * `FnPtr::call_dynamic` now takes `&NativeCallContext` instead of consuming it.
* All `Module::set_fn_XXX` methods are removed, in favor of `Module::set_native_fn`. * All `Module::set_fn_XXX` methods are removed, in favor of `Module::set_native_fn`.
* `Array::reduce` and `Array::reduce_rev` now take a `Dynamic` as initial value instead of a function pointer. * `Array::reduce` and `Array::reduce_rev` now take a `Dynamic` as initial value instead of a function pointer.
* `protected`, `super` are now reserved keywords. * `protected`, `super` are now reserved keywords.
* The `Module::set_fn_XXX` API now take `&str` as the function name instead of `Into<String>`.
* The _reflections_ API such as `Engine::gen_fn_signatures`, `Module::update_fn_metadata` etc. are put under the `metadata` feature gate.
Enhancements Enhancements
------------ ------------
* Replaced most `HashMap` usage with `BTreeMap` for better performance when the number of items is small. * Replaced all `HashMap` usage with `BTreeMap` for better performance because collections in Rhai are tiny.
* `Engine::register_result_fn` no longer requires the successful return type to be `Dynamic`. It can now be any clonable type. * `Engine::register_result_fn` no longer requires the successful return type to be `Dynamic`. It can now be any clonable type.
* `#[rhai_fn(return_raw)]` can now return `Result<T, Box<EvalAltResult>>` where `T` is any clonable type instead of `Result<Dynamic, Box<EvalAltResult>>`. * `#[rhai_fn(return_raw)]` can now return `Result<T, Box<EvalAltResult>>` where `T` is any clonable type instead of `Result<Dynamic, Box<EvalAltResult>>`.

View File

@ -38,7 +38,7 @@ no_closure = [] # no automatic sharing and capture of anonymous
no_module = [] # no modules no_module = [] # no modules
internals = [] # expose internal data structures internals = [] # expose internal data structures
unicode-xid-ident = ["unicode-xid"] # allow Unicode Standard Annex #31 for identifiers. unicode-xid-ident = ["unicode-xid"] # allow Unicode Standard Annex #31 for identifiers.
metadata = ["serde", "serde_json"] # enables exporting functions metadata to JSON metadata = ["serde_json"] # enable exporting functions metadata
no_std = ["smallvec/union", "num-traits/libm", "core-error", "libm", "ahash/compile-time-rng"] no_std = ["smallvec/union", "num-traits/libm", "core-error", "libm", "ahash/compile-time-rng"]
@ -92,4 +92,4 @@ instant = { version = "0.1" } # WASM implementation of std::time::Instant
instant = { version = "0.1" } # WASM implementation of std::time::Instant instant = { version = "0.1" } # WASM implementation of std::time::Instant
[package.metadata.docs.rs] [package.metadata.docs.rs]
features = ["metadata", "internals", "decimal"] # compiling for no-std features = ["metadata", "serde", "internals", "decimal"] # compiling for no-std

View File

@ -300,29 +300,28 @@ impl Parse for ExportedFn {
let visibility = fn_all.vis; let visibility = fn_all.vis;
// Determine if the function requires a call context // Determine if the function requires a call context
if let Some(first_arg) = fn_all.sig.inputs.first() { match fn_all.sig.inputs.first() {
if let syn::FnArg::Typed(syn::PatType { ref ty, .. }) = first_arg { Some(syn::FnArg::Typed(syn::PatType { ref ty, .. })) => {
match flatten_type_groups(ty.as_ref()) { match flatten_type_groups(ty.as_ref()) {
syn::Type::Path(p) syn::Type::Path(p)
if p.path == context_type_path1 || p.path == context_type_path2 => if p.path == context_type_path1 || p.path == context_type_path2 =>
{ {
pass_context = true; pass_context = true;
} }
_ => (), _ => {}
} }
} }
_ => {}
} }
let skip_slots = if pass_context { 1 } else { 0 }; let skip_slots = if pass_context { 1 } else { 0 };
// Determine whether function generates a special calling convention for a mutable receiver. // Determine whether function generates a special calling convention for a mutable receiver.
let mut_receiver = { let mut_receiver = match fn_all.sig.inputs.iter().skip(skip_slots).next() {
if let Some(first_arg) = fn_all.sig.inputs.iter().skip(skip_slots).next() { Some(syn::FnArg::Receiver(syn::Receiver {
match first_arg {
syn::FnArg::Receiver(syn::Receiver {
reference: Some(_), .. reference: Some(_), ..
}) => true, })) => true,
syn::FnArg::Typed(syn::PatType { ref ty, .. }) => { Some(syn::FnArg::Typed(syn::PatType { ref ty, .. })) => {
match flatten_type_groups(ty.as_ref()) { match flatten_type_groups(ty.as_ref()) {
syn::Type::Reference(syn::TypeReference { syn::Type::Reference(syn::TypeReference {
mutability: Some(_), mutability: Some(_),
@ -345,10 +344,6 @@ impl Parse for ExportedFn {
} }
} }
_ => false, _ => false,
}
} else {
false
}
}; };
// All arguments after the first must be moved except for &str. // All arguments after the first must be moved except for &str.
@ -381,7 +376,8 @@ impl Parse for ExportedFn {
} }
// Check return type. // Check return type.
if let syn::ReturnType::Type(_, ref ret_type) = fn_all.sig.output { match fn_all.sig.output {
syn::ReturnType::Type(_, ref ret_type) => {
match flatten_type_groups(ret_type.as_ref()) { match flatten_type_groups(ret_type.as_ref()) {
syn::Type::Ptr(_) => { syn::Type::Ptr(_) => {
return Err(syn::Error::new( return Err(syn::Error::new(
@ -398,6 +394,8 @@ impl Parse for ExportedFn {
_ => {} _ => {}
} }
} }
_ => {}
}
Ok(ExportedFn { Ok(ExportedFn {
entire_span, entire_span,
signature: fn_all.sig, signature: fn_all.sig,
@ -494,10 +492,9 @@ impl ExportedFn {
} }
pub fn return_type(&self) -> Option<&syn::Type> { pub fn return_type(&self) -> Option<&syn::Type> {
if let syn::ReturnType::Type(_, ref ret_type) = self.signature.output { match self.signature.output {
Some(flatten_type_groups(ret_type)) syn::ReturnType::Type(_, ref ret_type) => Some(flatten_type_groups(ret_type)),
} else { _ => None,
None
} }
} }
@ -590,20 +587,14 @@ impl ExportedFn {
let name: syn::Ident = let name: syn::Ident =
syn::Ident::new(&format!("rhai_fn_{}", self.name()), self.name().span()); syn::Ident::new(&format!("rhai_fn_{}", self.name()), self.name().span());
let impl_block = self.generate_impl("Token"); let impl_block = self.generate_impl("Token");
let callable_block = self.generate_callable("Token");
let param_names_block = self.generate_param_names("Token");
let input_types_block = self.generate_input_types("Token");
let dyn_result_fn_block = self.generate_dynamic_fn(); let dyn_result_fn_block = self.generate_dynamic_fn();
let vis = self.visibility; let vis = self.visibility;
quote! { quote! {
#[automatically_derived] #[automatically_derived]
#vis mod #name { #vis mod #name {
use super::*; use super::*;
struct Token(); pub struct Token();
#impl_block #impl_block
#callable_block
#param_names_block
#input_types_block
#dyn_result_fn_block #dyn_result_fn_block
} }
} }
@ -616,22 +607,18 @@ impl ExportedFn {
dynamic_signature.ident = dynamic_signature.ident =
syn::Ident::new("dynamic_result_fn", proc_macro2::Span::call_site()); syn::Ident::new("dynamic_result_fn", proc_macro2::Span::call_site());
dynamic_signature.output = syn::parse2::<syn::ReturnType>(quote! { dynamic_signature.output = syn::parse2::<syn::ReturnType>(quote! {
-> Result<Dynamic, Box<EvalAltResult>> -> RhaiResult
}) })
.unwrap(); .unwrap();
let arguments: Vec<syn::Ident> = dynamic_signature let arguments: Vec<syn::Ident> = dynamic_signature
.inputs .inputs
.iter() .iter()
.filter_map(|fn_arg| { .filter_map(|fn_arg| match fn_arg {
if let syn::FnArg::Typed(syn::PatType { ref pat, .. }) = fn_arg { syn::FnArg::Typed(syn::PatType { ref pat, .. }) => match pat.as_ref() {
if let syn::Pat::Ident(ref ident) = pat.as_ref() { syn::Pat::Ident(ref ident) => Some(ident.ident.clone()),
Some(ident.ident.clone()) _ => None,
} else { },
None _ => None,
}
} else {
None
}
}) })
.collect(); .collect();
@ -656,48 +643,6 @@ impl ExportedFn {
} }
} }
pub fn generate_callable(&self, on_type_name: &str) -> proc_macro2::TokenStream {
let token_name: syn::Ident = syn::Ident::new(on_type_name, self.name().span());
let callable_fn_name: syn::Ident = syn::Ident::new(
&format!("{}_callable", on_type_name.to_lowercase()),
self.name().span(),
);
quote! {
#[inline(always)]
pub fn #callable_fn_name() -> CallableFunction {
#token_name().into()
}
}
}
pub fn generate_param_names(&self, on_type_name: &str) -> proc_macro2::TokenStream {
let token_name: syn::Ident = syn::Ident::new(on_type_name, self.name().span());
let param_names_fn_name: syn::Ident = syn::Ident::new(
&format!("{}_param_names", on_type_name.to_lowercase()),
self.name().span(),
);
quote! {
#[inline(always)]
pub fn #param_names_fn_name() -> Box<[&'static str]> {
#token_name().param_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(
&format!("{}_input_types", on_type_name.to_lowercase()),
self.name().span(),
);
quote! {
#[inline(always)]
pub fn #input_types_fn_name() -> Box<[TypeId]> {
#token_name().input_types()
}
}
}
pub fn generate_impl(&self, on_type_name: &str) -> proc_macro2::TokenStream { pub fn generate_impl(&self, on_type_name: &str) -> proc_macro2::TokenStream {
let sig_name = self.name().clone(); let sig_name = self.name().clone();
let arg_count = self.arg_count(); let arg_count = self.arg_count();
@ -873,23 +818,19 @@ impl ExportedFn {
let type_name = syn::Ident::new(on_type_name, proc_macro2::Span::call_site()); let type_name = syn::Ident::new(on_type_name, proc_macro2::Span::call_site());
quote! { quote! {
impl #type_name {
pub const PARAM_NAMES: &'static [&'static str] = &[#(#input_type_names,)* #return_type];
#[inline(always)] pub fn param_types() -> [TypeId; #arg_count] { [#(#input_type_exprs),*] }
}
impl PluginFunction for #type_name { impl PluginFunction for #type_name {
#[inline(always)] #[inline(always)]
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult { fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
debug_assert_eq!(args.len(), #arg_count, "wrong arg count: {} != {}", args.len(), #arg_count);
#(#unpack_statements)* #(#unpack_statements)*
#return_expr #return_expr
} }
#[inline(always)] fn is_method_call(&self) -> bool { #is_method_call } #[inline(always)] fn is_method_call(&self) -> bool { #is_method_call }
#[inline(always)] fn is_variadic(&self) -> bool { false } #[inline(always)] fn is_variadic(&self) -> bool { false }
#[inline(always)] fn clone_boxed(&self) -> Box<dyn PluginFunction> { Box::new(#type_name()) }
#[inline(always)] fn param_names(&self) -> Box<[&'static str]> {
new_vec![#(#input_type_names,)* #return_type].into_boxed_slice()
}
#[inline(always)] fn input_types(&self) -> Box<[TypeId]> {
new_vec![#(#input_type_exprs),*].into_boxed_slice()
}
} }
} }
} }

View File

@ -212,10 +212,9 @@ pub fn export_module(
#[proc_macro] #[proc_macro]
pub fn exported_module(module_path: proc_macro::TokenStream) -> proc_macro::TokenStream { pub fn exported_module(module_path: proc_macro::TokenStream) -> proc_macro::TokenStream {
let module_path = parse_macro_input!(module_path as syn::Path); let module_path = parse_macro_input!(module_path as syn::Path);
let tokens = quote::quote! { proc_macro::TokenStream::from(quote::quote! {
#module_path::rhai_module_generate() #module_path::rhai_module_generate()
}; })
proc_macro::TokenStream::from(tokens)
} }
/// Macro to combine a _plugin module_ into an existing module. /// Macro to combine a _plugin module_ into an existing module.
@ -258,15 +257,12 @@ pub fn exported_module(module_path: proc_macro::TokenStream) -> proc_macro::Toke
/// ``` /// ```
#[proc_macro] #[proc_macro]
pub fn combine_with_exported_module(args: proc_macro::TokenStream) -> proc_macro::TokenStream { pub fn combine_with_exported_module(args: proc_macro::TokenStream) -> proc_macro::TokenStream {
let (module_expr, _export_name, module_path) = match crate::register::parse_register_macro(args) match crate::register::parse_register_macro(args) {
{ Ok((module_expr, _export_name, module_path)) => proc_macro::TokenStream::from(quote! {
Ok(triple) => triple,
Err(e) => return e.to_compile_error().into(),
};
let tokens = quote! {
#module_path::rhai_generate_into_module(#module_expr, true); #module_path::rhai_generate_into_module(#module_expr, true);
}; }),
proc_macro::TokenStream::from(tokens) Err(e) => e.to_compile_error().into(),
}
} }
/// Macro to register a _plugin function_ (defined via [`#[export_fn]`][export_fn]) into an `Engine`. /// Macro to register a _plugin function_ (defined via [`#[export_fn]`][export_fn]) into an `Engine`.
@ -293,16 +289,15 @@ pub fn combine_with_exported_module(args: proc_macro::TokenStream) -> proc_macro
/// ``` /// ```
#[proc_macro] #[proc_macro]
pub fn register_exported_fn(args: proc_macro::TokenStream) -> proc_macro::TokenStream { pub fn register_exported_fn(args: proc_macro::TokenStream) -> proc_macro::TokenStream {
let (engine_expr, export_name, rust_mod_path) =
match crate::register::parse_register_macro(args) { match crate::register::parse_register_macro(args) {
Ok(triple) => triple, Ok((engine_expr, export_name, rust_mod_path)) => {
Err(e) => return e.to_compile_error().into(),
};
let gen_mod_path = crate::register::generated_module_path(&rust_mod_path); let gen_mod_path = crate::register::generated_module_path(&rust_mod_path);
let tokens = quote! { proc_macro::TokenStream::from(quote! {
#engine_expr.register_result_fn(&(#export_name), #gen_mod_path::dynamic_result_fn); #engine_expr.register_result_fn(#export_name, #gen_mod_path::dynamic_result_fn);
}; })
proc_macro::TokenStream::from(tokens) }
Err(e) => e.to_compile_error().into(),
}
} }
/// Macro to register a _plugin function_ into a Rhai `Module`. /// Macro to register a _plugin function_ into a Rhai `Module`.
@ -332,19 +327,18 @@ pub fn register_exported_fn(args: proc_macro::TokenStream) -> proc_macro::TokenS
/// ``` /// ```
#[proc_macro] #[proc_macro]
pub fn set_exported_fn(args: proc_macro::TokenStream) -> proc_macro::TokenStream { pub fn set_exported_fn(args: proc_macro::TokenStream) -> proc_macro::TokenStream {
let (module_expr, export_name, rust_mod_path) =
match crate::register::parse_register_macro(args) { match crate::register::parse_register_macro(args) {
Ok(triple) => triple, Ok((module_expr, export_name, rust_mod_path)) => {
Err(e) => return e.to_compile_error().into(),
};
let gen_mod_path = crate::register::generated_module_path(&rust_mod_path); let gen_mod_path = crate::register::generated_module_path(&rust_mod_path);
let tokens = quote! { proc_macro::TokenStream::from(quote! {
#module_expr.set_fn(#export_name, FnNamespace::Internal, FnAccess::Public, #module_expr.set_fn(#export_name, FnNamespace::Internal, FnAccess::Public,
Some(#gen_mod_path::token_param_names().as_ref()), Some(#gen_mod_path::Token::PARAM_NAMES),
#gen_mod_path::token_input_types().as_ref(), &#gen_mod_path::Token::param_types(),
#gen_mod_path::token_callable()); #gen_mod_path::Token().into());
}; })
proc_macro::TokenStream::from(tokens) }
Err(e) => e.to_compile_error().into(),
}
} }
/// Macro to register a _plugin function_ into a Rhai `Module` and expose it globally. /// Macro to register a _plugin function_ into a Rhai `Module` and expose it globally.
@ -374,17 +368,16 @@ pub fn set_exported_fn(args: proc_macro::TokenStream) -> proc_macro::TokenStream
/// ``` /// ```
#[proc_macro] #[proc_macro]
pub fn set_exported_global_fn(args: proc_macro::TokenStream) -> proc_macro::TokenStream { pub fn set_exported_global_fn(args: proc_macro::TokenStream) -> proc_macro::TokenStream {
let (module_expr, export_name, rust_mod_path) =
match crate::register::parse_register_macro(args) { match crate::register::parse_register_macro(args) {
Ok(triple) => triple, Ok((module_expr, export_name, rust_mod_path)) => {
Err(e) => return e.to_compile_error().into(),
};
let gen_mod_path = crate::register::generated_module_path(&rust_mod_path); let gen_mod_path = crate::register::generated_module_path(&rust_mod_path);
let tokens = quote! { proc_macro::TokenStream::from(quote! {
#module_expr.set_fn(#export_name, FnNamespace::Global, FnAccess::Public, #module_expr.set_fn(#export_name, FnNamespace::Global, FnAccess::Public,
Some(#gen_mod_path::token_param_names().as_ref()), Some(#gen_mod_path::Token::PARAM_NAMES),
#gen_mod_path::token_input_types().as_ref(), &#gen_mod_path::Token::param_types(),
#gen_mod_path::token_callable()); #gen_mod_path::Token().into());
}; })
proc_macro::TokenStream::from(tokens) }
Err(e) => e.to_compile_error().into(),
}
} }

View File

@ -156,8 +156,11 @@ impl Parse for Module {
}) => { }) => {
// #[cfg] attributes are not allowed on const declarations // #[cfg] attributes are not allowed on const declarations
crate::attrs::deny_cfg_attr(&attrs)?; crate::attrs::deny_cfg_attr(&attrs)?;
if let syn::Visibility::Public(_) = vis { match vis {
consts.push((ident.to_string(), ty.clone(), expr.as_ref().clone())); syn::Visibility::Public(_) => {
consts.push((ident.to_string(), ty.clone(), expr.as_ref().clone()))
}
_ => {}
} }
} }
_ => {} _ => {}
@ -170,26 +173,23 @@ impl Parse for Module {
sub_modules.reserve(content.len() - fns.len() - consts.len()); sub_modules.reserve(content.len() - fns.len() - consts.len());
let mut i = 0; let mut i = 0;
while i < content.len() { while i < content.len() {
if let syn::Item::Mod(_) = &content[i] { match content[i] {
syn::Item::Mod(_) => {
let mut item_mod = match content.remove(i) { let mut item_mod = match content.remove(i) {
syn::Item::Mod(m) => m, syn::Item::Mod(m) => m,
_ => unreachable!(), _ => unreachable!(),
}; };
let params: ExportedModParams = match crate::attrs::inner_item_attributes( let params: ExportedModParams =
&mut item_mod.attrs, crate::attrs::inner_item_attributes(&mut item_mod.attrs, "rhai_mod")?;
"rhai_mod", let module = syn::parse2::<Module>(item_mod.to_token_stream()).and_then(
) { |mut m| {
Ok(p) => p,
Err(e) => return Err(e),
};
let module =
syn::parse2::<Module>(item_mod.to_token_stream()).and_then(|mut m| {
m.set_params(params)?; m.set_params(params)?;
Ok(m) Ok(m)
})?; },
)?;
sub_modules.push(module); sub_modules.push(module);
} else { }
i += 1; _ => i += 1,
} }
} }
} else { } else {

View File

@ -33,8 +33,7 @@ pub fn parse_register_macro(
)); ));
} }
let export_name = match &items[1] { let export_name = match &items[1] {
syn::Expr::Lit(lit_str) => quote_spanned!(items[1].span() => syn::Expr::Lit(lit_str) => quote_spanned!(items[1].span() => #lit_str),
#lit_str.to_string()),
expr => quote! { #expr }, expr => quote! { #expr },
}; };
let rust_mod_path = if let syn::Expr::Path(ref path) = &items[2] { let rust_mod_path = if let syn::Expr::Path(ref path) = &items[2] {

View File

@ -1,6 +1,6 @@
use std::collections::BTreeMap; use std::collections::BTreeMap;
use quote::{quote, ToTokens}; use quote::quote;
use crate::attrs::ExportScope; use crate::attrs::ExportScope;
use crate::function::{ use crate::function::{
@ -81,16 +81,6 @@ pub fn generate_body(
); );
let reg_names = function.exported_names(); let reg_names = function.exported_names();
let fn_input_names: Vec<String> = function
.arg_list()
.map(|fn_arg| match fn_arg {
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 let fn_input_types: Vec<syn::Expr> = function
.arg_list() .arg_list()
.map(|fn_arg| match fn_arg { .map(|fn_arg| match fn_arg {
@ -128,17 +118,12 @@ pub fn generate_body(
}; };
syn::parse2::<syn::Expr>(quote! { syn::parse2::<syn::Expr>(quote! {
core::any::TypeId::of::<#arg_type>()}) TypeId::of::<#arg_type>()})
.unwrap() .unwrap()
} }
}) })
.collect(); .collect();
let return_type = function
.return_type()
.map(print_type)
.unwrap_or_else(|| "()".to_string());
for fn_literal in reg_names { for fn_literal in reg_names {
let mut namespace = FnNamespaceAccess::Internal; let mut namespace = FnNamespaceAccess::Internal;
@ -172,7 +157,7 @@ pub fn generate_body(
set_fn_statements.push( set_fn_statements.push(
syn::parse2::<syn::Stmt>(quote! { syn::parse2::<syn::Stmt>(quote! {
m.set_fn(#fn_literal, FnNamespace::#ns_str, FnAccess::Public, m.set_fn(#fn_literal, FnNamespace::#ns_str, FnAccess::Public,
Some(&[#(#fn_input_names,)* #return_type]), &[#(#fn_input_types),*], Some(#fn_token_name::PARAM_NAMES), &[#(#fn_input_types),*],
#fn_token_name().into()); #fn_token_name().into());
}) })
.unwrap(), .unwrap(),
@ -181,12 +166,9 @@ pub fn generate_body(
gen_fn_tokens.push(quote! { gen_fn_tokens.push(quote! {
#[allow(non_camel_case_types)] #[allow(non_camel_case_types)]
struct #fn_token_name(); pub struct #fn_token_name();
}); });
gen_fn_tokens.push(function.generate_impl(&fn_token_name.to_string())); 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_param_names(&fn_token_name.to_string()));
gen_fn_tokens.push(function.generate_input_types(&fn_token_name.to_string()));
} }
let mut generate_fn_call = syn::parse2::<syn::ItemMod>(quote! { let mut generate_fn_call = syn::parse2::<syn::ItemMod>(quote! {

View File

@ -275,35 +275,21 @@ mod generate_tests {
#[automatically_derived] #[automatically_derived]
pub mod rhai_fn_do_nothing { pub mod rhai_fn_do_nothing {
use super::*; use super::*;
struct Token(); pub struct Token();
impl Token {
pub const PARAM_NAMES: &'static [&'static str] = &["()"];
#[inline(always)] pub fn param_types() -> [TypeId; 0usize] { [] }
}
impl PluginFunction for Token { impl PluginFunction for Token {
#[inline(always)] fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult { #[inline(always)] fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
debug_assert_eq!(args.len(), 0usize,
"wrong arg count: {} != {}", args.len(), 0usize);
Ok(Dynamic::from(do_nothing())) Ok(Dynamic::from(do_nothing()))
} }
#[inline(always)] fn is_method_call(&self) -> bool { false } #[inline(always)] fn is_method_call(&self) -> bool { false }
#[inline(always)] fn is_variadic(&self) -> bool { false } #[inline(always)] fn is_variadic(&self) -> bool { false }
#[inline(always)] fn clone_boxed(&self) -> Box<dyn PluginFunction> { Box::new(Token()) }
#[inline(always)] fn param_names(&self) -> Box<[&'static str]> {
new_vec!["()"].into_boxed_slice()
}
#[inline(always)] fn input_types(&self) -> Box<[TypeId]> {
new_vec![].into_boxed_slice()
}
}
#[inline(always)] pub fn token_callable() -> CallableFunction {
Token().into()
}
#[inline(always)] pub fn token_param_names() -> Box<[&'static str]> {
Token().param_names()
}
#[inline(always)] pub fn token_input_types() -> Box<[TypeId]> {
Token().input_types()
} }
#[allow(unused)] #[allow(unused)]
#[inline(always)] pub fn dynamic_result_fn() -> Result<Dynamic, Box<EvalAltResult> > { #[inline(always)] pub fn dynamic_result_fn() -> RhaiResult {
Ok(Dynamic::from(do_nothing())) Ok(Dynamic::from(do_nothing()))
} }
} }
@ -323,37 +309,23 @@ mod generate_tests {
#[automatically_derived] #[automatically_derived]
pub mod rhai_fn_do_something { pub mod rhai_fn_do_something {
use super::*; use super::*;
struct Token(); pub struct Token();
impl Token {
pub const PARAM_NAMES: &'static [&'static str] = &["x: usize", "()"];
#[inline(always)] pub fn param_types() -> [TypeId; 1usize] { [TypeId::of::<usize>()] }
}
impl PluginFunction for Token { impl PluginFunction for Token {
#[inline(always)] #[inline(always)]
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult { fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
debug_assert_eq!(args.len(), 1usize,
"wrong arg count: {} != {}", args.len(), 1usize);
let arg0 = mem::take(args[0usize]).cast::<usize>(); let arg0 = mem::take(args[0usize]).cast::<usize>();
Ok(Dynamic::from(do_something(arg0))) Ok(Dynamic::from(do_something(arg0)))
} }
#[inline(always)] fn is_method_call(&self) -> bool { false } #[inline(always)] fn is_method_call(&self) -> bool { false }
#[inline(always)] fn is_variadic(&self) -> bool { false } #[inline(always)] fn is_variadic(&self) -> bool { false }
#[inline(always)] fn clone_boxed(&self) -> Box<dyn PluginFunction> { Box::new(Token()) }
#[inline(always)] fn param_names(&self) -> Box<[&'static str]> {
new_vec!["x: usize", "()"].into_boxed_slice()
}
#[inline(always)] fn input_types(&self) -> Box<[TypeId]> {
new_vec![TypeId::of::<usize>()].into_boxed_slice()
}
}
#[inline(always)] pub fn token_callable() -> CallableFunction {
Token().into()
}
#[inline(always)] pub fn token_param_names() -> Box<[&'static str]> {
Token().param_names()
}
#[inline(always)] pub fn token_input_types() -> Box<[TypeId]> {
Token().input_types()
} }
#[allow(unused)] #[allow(unused)]
#[inline(always)] pub fn dynamic_result_fn(x: usize) -> Result<Dynamic, Box<EvalAltResult> > { #[inline(always)] pub fn dynamic_result_fn(x: usize) -> RhaiResult {
Ok(Dynamic::from(do_something(x))) Ok(Dynamic::from(do_something(x)))
} }
} }
@ -373,37 +345,23 @@ mod generate_tests {
#[automatically_derived] #[automatically_derived]
pub mod rhai_fn_do_something { pub mod rhai_fn_do_something {
use super::*; use super::*;
struct Token(); pub struct Token();
impl Token {
pub const PARAM_NAMES: &'static [&'static str] = &["x: usize", "()"];
#[inline(always)] pub fn param_types() -> [TypeId; 1usize] { [TypeId::of::<usize>()] }
}
impl PluginFunction for Token { impl PluginFunction for Token {
#[inline(always)] #[inline(always)]
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult { fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
debug_assert_eq!(args.len(), 1usize,
"wrong arg count: {} != {}", args.len(), 1usize);
let arg0 = mem::take(args[0usize]).cast::<usize>(); let arg0 = mem::take(args[0usize]).cast::<usize>();
Ok(Dynamic::from(do_something(context, arg0))) Ok(Dynamic::from(do_something(context, arg0)))
} }
#[inline(always)] fn is_method_call(&self) -> bool { false } #[inline(always)] fn is_method_call(&self) -> bool { false }
#[inline(always)] fn is_variadic(&self) -> bool { false } #[inline(always)] fn is_variadic(&self) -> bool { false }
#[inline(always)] fn clone_boxed(&self) -> Box<dyn PluginFunction> { Box::new(Token()) }
#[inline(always)] fn param_names(&self) -> Box<[&'static str]> {
new_vec!["x: usize", "()"].into_boxed_slice()
}
#[inline(always)] fn input_types(&self) -> Box<[TypeId]> {
new_vec![TypeId::of::<usize>()].into_boxed_slice()
}
}
#[inline(always)] pub fn token_callable() -> CallableFunction {
Token().into()
}
#[inline(always)] pub fn token_param_names() -> Box<[&'static str]> {
Token().param_names()
}
#[inline(always)] pub fn token_input_types() -> Box<[TypeId]> {
Token().input_types()
} }
#[allow(unused)] #[allow(unused)]
#[inline(always)] pub fn dynamic_result_fn(context: NativeCallContext, x: usize) -> Result<Dynamic, Box<EvalAltResult> > { #[inline(always)] pub fn dynamic_result_fn(context: NativeCallContext, x: usize) -> RhaiResult {
Ok(Dynamic::from(do_something(context, x))) Ok(Dynamic::from(do_something(context, x)))
} }
} }
@ -426,36 +384,22 @@ mod generate_tests {
#[automatically_derived] #[automatically_derived]
pub mod rhai_fn_return_dynamic { pub mod rhai_fn_return_dynamic {
use super::*; use super::*;
struct Token(); pub struct Token();
impl Token {
pub const PARAM_NAMES: &'static [&'static str] = &["rhai::Dynamic"];
#[inline(always)] pub fn param_types() -> [TypeId; 0usize] { [] }
}
impl PluginFunction for Token { impl PluginFunction for Token {
#[inline(always)] #[inline(always)]
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult { fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
debug_assert_eq!(args.len(), 0usize,
"wrong arg count: {} != {}", args.len(), 0usize);
Ok(Dynamic::from(return_dynamic())) Ok(Dynamic::from(return_dynamic()))
} }
#[inline(always)] fn is_method_call(&self) -> bool { false } #[inline(always)] fn is_method_call(&self) -> bool { false }
#[inline(always)] fn is_variadic(&self) -> bool { false } #[inline(always)] fn is_variadic(&self) -> bool { false }
#[inline(always)] fn clone_boxed(&self) -> Box<dyn PluginFunction> { Box::new(Token()) }
#[inline(always)] fn param_names(&self) -> Box<[&'static str]> {
new_vec!["rhai::Dynamic"].into_boxed_slice()
}
#[inline(always)] fn input_types(&self) -> Box<[TypeId]> {
new_vec![].into_boxed_slice()
}
}
#[inline(always)] pub fn token_callable() -> CallableFunction {
Token().into()
}
#[inline(always)] pub fn token_param_names() -> Box<[&'static str]> {
Token().param_names()
}
#[inline(always)] pub fn token_input_types() -> Box<[TypeId]> {
Token().input_types()
} }
#[allow(unused)] #[allow(unused)]
#[inline(always)] pub fn dynamic_result_fn() -> Result<Dynamic, Box<EvalAltResult> > { #[inline(always)] pub fn dynamic_result_fn() -> RhaiResult {
Ok(Dynamic::from(return_dynamic())) Ok(Dynamic::from(return_dynamic()))
} }
} }
@ -472,24 +416,19 @@ mod generate_tests {
}; };
let expected_tokens = quote! { let expected_tokens = quote! {
impl TestStruct {
pub const PARAM_NAMES: &'static [&'static str] = &["x: usize", "()"];
#[inline(always)] pub fn param_types() -> [TypeId; 1usize] { [TypeId::of::<usize>()] }
}
impl PluginFunction for TestStruct { impl PluginFunction for TestStruct {
#[inline(always)] #[inline(always)]
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult { fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
debug_assert_eq!(args.len(), 1usize,
"wrong arg count: {} != {}", args.len(), 1usize);
let arg0 = mem::take(args[0usize]).cast::<usize>(); let arg0 = mem::take(args[0usize]).cast::<usize>();
Ok(Dynamic::from(do_something(arg0))) Ok(Dynamic::from(do_something(arg0)))
} }
#[inline(always)] fn is_method_call(&self) -> bool { false } #[inline(always)] fn is_method_call(&self) -> bool { false }
#[inline(always)] fn is_variadic(&self) -> bool { false } #[inline(always)] fn is_variadic(&self) -> bool { false }
#[inline(always)] fn clone_boxed(&self) -> Box<dyn PluginFunction> { Box::new(TestStruct()) }
#[inline(always)] fn param_names(&self) -> Box<[&'static str]> {
new_vec!["x: usize", "()"].into_boxed_slice()
}
#[inline(always)] fn input_types(&self) -> Box<[TypeId]> {
new_vec![TypeId::of::<usize>()].into_boxed_slice()
}
} }
}; };
@ -507,12 +446,14 @@ mod generate_tests {
#[automatically_derived] #[automatically_derived]
pub mod rhai_fn_add_together { pub mod rhai_fn_add_together {
use super::*; use super::*;
struct Token(); pub struct Token();
impl Token {
pub const PARAM_NAMES: &'static [&'static str] = &["x: usize", "y: usize", "usize"];
#[inline(always)] pub fn param_types() -> [TypeId; 2usize] { [TypeId::of::<usize>(), TypeId::of::<usize>()] }
}
impl PluginFunction for Token { impl PluginFunction for Token {
#[inline(always)] #[inline(always)]
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult { fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
debug_assert_eq!(args.len(), 2usize,
"wrong arg count: {} != {}", args.len(), 2usize);
let arg0 = mem::take(args[0usize]).cast::<usize>(); let arg0 = mem::take(args[0usize]).cast::<usize>();
let arg1 = mem::take(args[1usize]).cast::<usize>(); let arg1 = mem::take(args[1usize]).cast::<usize>();
Ok(Dynamic::from(add_together(arg0, arg1))) Ok(Dynamic::from(add_together(arg0, arg1)))
@ -520,26 +461,9 @@ mod generate_tests {
#[inline(always)] fn is_method_call(&self) -> bool { false } #[inline(always)] fn is_method_call(&self) -> bool { false }
#[inline(always)] fn is_variadic(&self) -> bool { false } #[inline(always)] fn is_variadic(&self) -> bool { false }
#[inline(always)] fn clone_boxed(&self) -> Box<dyn PluginFunction> { Box::new(Token()) }
#[inline(always)] fn param_names(&self) -> Box<[&'static str]> {
new_vec!["x: usize", "y: usize", "usize"].into_boxed_slice()
}
#[inline(always)] fn input_types(&self) -> Box<[TypeId]> {
new_vec![TypeId::of::<usize>(),
TypeId::of::<usize>()].into_boxed_slice()
}
}
#[inline(always)] pub fn token_callable() -> CallableFunction {
Token().into()
}
#[inline(always)] pub fn token_param_names() -> Box<[&'static str]> {
Token().param_names()
}
#[inline(always)] pub fn token_input_types() -> Box<[TypeId]> {
Token().input_types()
} }
#[allow(unused)] #[allow(unused)]
#[inline(always)] pub fn dynamic_result_fn(x: usize, y: usize) -> Result<Dynamic, Box<EvalAltResult> > { #[inline(always)] pub fn dynamic_result_fn(x: usize, y: usize) -> RhaiResult {
Ok(Dynamic::from(add_together(x, y))) Ok(Dynamic::from(add_together(x, y)))
} }
} }
@ -559,12 +483,14 @@ mod generate_tests {
#[automatically_derived] #[automatically_derived]
pub mod rhai_fn_increment { pub mod rhai_fn_increment {
use super::*; use super::*;
struct Token(); pub struct Token();
impl Token {
pub const PARAM_NAMES: &'static [&'static str] = &["x: &mut usize", "y: usize", "()"];
#[inline(always)] pub fn param_types() -> [TypeId; 2usize] { [TypeId::of::<usize>(), TypeId::of::<usize>()] }
}
impl PluginFunction for Token { impl PluginFunction for Token {
#[inline(always)] #[inline(always)]
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult { fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
debug_assert_eq!(args.len(), 2usize,
"wrong arg count: {} != {}", args.len(), 2usize);
if args[0usize].is_read_only() { if args[0usize].is_read_only() {
return Err(Box::new( return Err(Box::new(
EvalAltResult::ErrorAssignmentToConstant("x".to_string(), Position::NONE) EvalAltResult::ErrorAssignmentToConstant("x".to_string(), Position::NONE)
@ -577,26 +503,9 @@ mod generate_tests {
#[inline(always)] fn is_method_call(&self) -> bool { true } #[inline(always)] fn is_method_call(&self) -> bool { true }
#[inline(always)] fn is_variadic(&self) -> bool { false } #[inline(always)] fn is_variadic(&self) -> bool { false }
#[inline(always)] fn clone_boxed(&self) -> Box<dyn PluginFunction> { Box::new(Token()) }
#[inline(always)] fn param_names(&self) -> Box<[&'static str]> {
new_vec!["x: &mut usize", "y: usize", "()"].into_boxed_slice()
}
#[inline(always)] fn input_types(&self) -> Box<[TypeId]> {
new_vec![TypeId::of::<usize>(),
TypeId::of::<usize>()].into_boxed_slice()
}
}
#[inline(always)] pub fn token_callable() -> CallableFunction {
Token().into()
}
#[inline(always)] pub fn token_param_names() -> Box<[&'static str]> {
Token().param_names()
}
#[inline(always)] pub fn token_input_types() -> Box<[TypeId]> {
Token().input_types()
} }
#[allow(unused)] #[allow(unused)]
#[inline(always)] pub fn dynamic_result_fn(x: &mut usize, y: usize) -> Result<Dynamic, Box<EvalAltResult> > { #[inline(always)] pub fn dynamic_result_fn(x: &mut usize, y: usize) -> RhaiResult {
Ok(Dynamic::from(increment(x, y))) Ok(Dynamic::from(increment(x, y)))
} }
} }
@ -617,37 +526,23 @@ mod generate_tests {
#[automatically_derived] #[automatically_derived]
pub mod rhai_fn_special_print { pub mod rhai_fn_special_print {
use super::*; use super::*;
struct Token(); pub struct Token();
impl Token {
pub const PARAM_NAMES: &'static [&'static str] = &["message: &str", "()"];
#[inline(always)] pub fn param_types() -> [TypeId; 1usize] { [TypeId::of::<ImmutableString>()] }
}
impl PluginFunction for Token { impl PluginFunction for Token {
#[inline(always)] #[inline(always)]
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult { fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
debug_assert_eq!(args.len(), 1usize,
"wrong arg count: {} != {}", args.len(), 1usize);
let arg0 = mem::take(args[0usize]).take_immutable_string().unwrap(); let arg0 = mem::take(args[0usize]).take_immutable_string().unwrap();
Ok(Dynamic::from(special_print(&arg0))) Ok(Dynamic::from(special_print(&arg0)))
} }
#[inline(always)] fn is_method_call(&self) -> bool { false } #[inline(always)] fn is_method_call(&self) -> bool { false }
#[inline(always)] fn is_variadic(&self) -> bool { false } #[inline(always)] fn is_variadic(&self) -> bool { false }
#[inline(always)] fn clone_boxed(&self) -> Box<dyn PluginFunction> { Box::new(Token()) }
#[inline(always)] fn param_names(&self) -> Box<[&'static str]> {
new_vec!["message: &str", "()"].into_boxed_slice()
}
#[inline(always)] fn input_types(&self) -> Box<[TypeId]> {
new_vec![TypeId::of::<ImmutableString>()].into_boxed_slice()
}
}
#[inline(always)] pub fn token_callable() -> CallableFunction {
Token().into()
}
#[inline(always)] pub fn token_param_names() -> Box<[&'static str]> {
Token().param_names()
}
#[inline(always)] pub fn token_input_types() -> Box<[TypeId]> {
Token().input_types()
} }
#[allow(unused)] #[allow(unused)]
#[inline(always)] pub fn dynamic_result_fn(message: &str) -> Result<Dynamic, Box<EvalAltResult> > { #[inline(always)] pub fn dynamic_result_fn(message: &str) -> RhaiResult {
Ok(Dynamic::from(special_print(message))) Ok(Dynamic::from(special_print(message)))
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -15,7 +15,7 @@ help: consider importing one of these items
| |
11 | use core::fmt::Pointer; 11 | use core::fmt::Pointer;
| |
11 | use crate::new_vec::fmt::Pointer; 11 | use crate::mem::fmt::Pointer;
| |
11 | use std::fmt::Pointer; 11 | use std::fmt::Pointer;
| |

View File

@ -21,7 +21,7 @@ fn count_string_bytes(s: &str) -> INT {
/// This version uses `ImmutableString` and `&str`. /// This version uses `ImmutableString` and `&str`.
fn find_substring(s: ImmutableString, sub: &str) -> INT { fn find_substring(s: ImmutableString, sub: &str) -> INT {
s.as_str().find(sub).map(|x| x as INT).unwrap_or(-1) s.find(sub).map(|x| x as INT).unwrap_or(-1)
} }
fn main() -> Result<(), Box<EvalAltResult>> { fn main() -> Result<(), Box<EvalAltResult>> {

View File

@ -6,7 +6,7 @@ use crate::module::NamespaceRef;
use crate::stdlib::{ use crate::stdlib::{
borrow::Cow, borrow::Cow,
boxed::Box, boxed::Box,
collections::BTreeMap, collections::{BTreeMap, BTreeSet},
fmt, fmt,
hash::Hash, hash::Hash,
num::NonZeroUsize, num::NonZeroUsize,
@ -26,9 +26,6 @@ use crate::{stdlib::str::FromStr, FLOAT};
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
use crate::Array; use crate::Array;
#[cfg(not(feature = "no_object"))]
use crate::Map;
/// A type representing the access mode of a function. /// A type representing the access mode of a function.
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub enum FnAccess { pub enum FnAccess {
@ -61,7 +58,7 @@ pub struct ScriptFnDef {
pub params: StaticVec<ImmutableString>, pub params: StaticVec<ImmutableString>,
/// Access to external variables. /// Access to external variables.
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
pub externals: StaticVec<ImmutableString>, pub externals: BTreeSet<ImmutableString>,
/// Function doc-comments (if any). /// Function doc-comments (if any).
pub comments: StaticVec<String>, pub comments: StaticVec<String>,
} }
@ -87,6 +84,7 @@ impl fmt::Display for ScriptFnDef {
} }
/// A type containing the metadata of a script-defined function. /// A type containing the metadata of a script-defined function.
/// Not available under `no_function`.
/// ///
/// Created by [`AST::iter_functions`]. /// Created by [`AST::iter_functions`].
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
@ -263,6 +261,7 @@ impl AST {
} }
/// _(INTERNALS)_ Get the internal shared [`Module`] containing all script-defined functions. /// _(INTERNALS)_ Get the internal shared [`Module`] containing all script-defined functions.
/// Exported under the `internals` feature only. /// Exported under the `internals` feature only.
/// Not available under `no_function`.
#[cfg(feature = "internals")] #[cfg(feature = "internals")]
#[deprecated = "this method is volatile and may change"] #[deprecated = "this method is volatile and may change"]
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
@ -279,6 +278,7 @@ impl AST {
} }
/// _(INTERNALS)_ Get the internal [`Module`] containing all script-defined functions. /// _(INTERNALS)_ Get the internal [`Module`] containing all script-defined functions.
/// Exported under the `internals` feature only. /// Exported under the `internals` feature only.
/// Not available under `no_function`.
#[cfg(feature = "internals")] #[cfg(feature = "internals")]
#[deprecated = "this method is volatile and may change"] #[deprecated = "this method is volatile and may change"]
#[inline(always)] #[inline(always)]
@ -314,10 +314,9 @@ impl AST {
} }
/// Clone the [`AST`]'s functions into a new [`AST`]. /// Clone the [`AST`]'s functions into a new [`AST`].
/// No statements are cloned. /// No statements are cloned.
/// Not available under `no_function`.
/// ///
/// This operation is cheap because functions are shared. /// This operation is cheap because functions are shared.
///
/// Not available under `no_function`.
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
#[inline(always)] #[inline(always)]
pub fn clone_functions_only(&self) -> Self { pub fn clone_functions_only(&self) -> Self {
@ -325,10 +324,9 @@ impl AST {
} }
/// Clone the [`AST`]'s functions into a new [`AST`] based on a filter predicate. /// Clone the [`AST`]'s functions into a new [`AST`] based on a filter predicate.
/// No statements are cloned. /// No statements are cloned.
/// Not available under `no_function`.
/// ///
/// This operation is cheap because functions are shared. /// This operation is cheap because functions are shared.
///
/// Not available under `no_function`.
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
#[inline(always)] #[inline(always)]
pub fn clone_functions_only_filtered( pub fn clone_functions_only_filtered(
@ -357,8 +355,8 @@ impl AST {
resolver: self.resolver.clone(), resolver: self.resolver.clone(),
} }
} }
/// Merge two [`AST`] into one. Both [`AST`]'s are untouched and a new, merged, version /// Merge two [`AST`] into one. Both [`AST`]'s are untouched and a new, merged,
/// is returned. /// version is returned.
/// ///
/// Statements in the second [`AST`] are simply appended to the end of the first _without any processing_. /// Statements in the second [`AST`] are simply appended to the end of the first _without any processing_.
/// Thus, the return value of the first [`AST`] (if using expression-statement syntax) is buried. /// Thus, the return value of the first [`AST`] (if using expression-statement syntax) is buried.
@ -1288,7 +1286,7 @@ pub struct BinaryExpr {
pub struct OpAssignment { pub struct OpAssignment {
pub hash_op_assign: u64, pub hash_op_assign: u64,
pub hash_op: u64, pub hash_op: u64,
pub op: Cow<'static, str>, pub op: &'static str,
} }
/// _(INTERNALS)_ An set of function call hashes. /// _(INTERNALS)_ An set of function call hashes.
@ -1516,7 +1514,7 @@ impl FloatWrapper {
#[derive(Debug, Clone, Hash)] #[derive(Debug, Clone, Hash)]
pub enum Expr { pub enum Expr {
/// Dynamic constant. /// Dynamic constant.
/// Used to hold either an [`Array`] or [`Map`] literal for quick cloning. /// Used to hold either an [`Array`] or [`Map`][crate::Map] literal for quick cloning.
/// All other primitive data types should use the appropriate variants for better speed. /// All other primitive data types should use the appropriate variants for better speed.
DynamicConstant(Box<Dynamic>, Position), DynamicConstant(Box<Dynamic>, Position),
/// Boolean constant. /// Boolean constant.
@ -1535,7 +1533,10 @@ pub enum Expr {
/// [ expr, ... ] /// [ expr, ... ]
Array(Box<StaticVec<Expr>>, Position), Array(Box<StaticVec<Expr>>, Position),
/// #{ name:expr, ... } /// #{ name:expr, ... }
Map(Box<StaticVec<(Ident, Expr)>>, Position), Map(
Box<(StaticVec<(Ident, Expr)>, BTreeMap<ImmutableString, Dynamic>)>,
Position,
),
/// () /// ()
Unit(Position), Unit(Position),
/// Variable access - (optional index, optional (hash, modules), variable name) /// Variable access - (optional index, optional (hash, modules), variable name)
@ -1594,11 +1595,10 @@ impl Expr {
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Self::Map(x, _) if self.is_constant() => { Self::Map(x, _) if self.is_constant() => {
let mut map = Map::new(); let mut map = x.1.clone();
map.extend( x.0.iter().for_each(|(k, v)| {
x.iter() *map.get_mut(&k.name).unwrap() = v.get_constant_value().unwrap()
.map(|(k, v)| (k.name.clone(), v.get_constant_value().unwrap())), });
);
Dynamic(Union::Map(Box::new(map), AccessMode::ReadOnly)) Dynamic(Union::Map(Box::new(map), AccessMode::ReadOnly))
} }
@ -1677,7 +1677,7 @@ impl Expr {
match self { match self {
Self::Array(x, _) => x.iter().all(Self::is_pure), Self::Array(x, _) => x.iter().all(Self::is_pure),
Self::Map(x, _) => x.iter().map(|(_, v)| v).all(Self::is_pure), Self::Map(x, _) => x.0.iter().map(|(_, v)| v).all(Self::is_pure),
Self::Index(x, _) | Self::And(x, _) | Self::Or(x, _) => { Self::Index(x, _) | Self::And(x, _) | Self::Or(x, _) => {
x.lhs.is_pure() && x.rhs.is_pure() x.lhs.is_pure() && x.rhs.is_pure()
@ -1717,7 +1717,7 @@ impl Expr {
Self::Array(x, _) => x.iter().all(Self::is_constant), Self::Array(x, _) => x.iter().all(Self::is_constant),
// An map literal is constant if all items are constant // An map literal is constant if all items are constant
Self::Map(x, _) => x.iter().map(|(_, expr)| expr).all(Self::is_constant), Self::Map(x, _) => x.0.iter().map(|(_, expr)| expr).all(Self::is_constant),
_ => false, _ => false,
} }
@ -1804,7 +1804,7 @@ impl Expr {
} }
} }
Self::Map(x, _) => { Self::Map(x, _) => {
for (_, e) in x.as_ref() { for (_, e) in &x.0 {
if !e.walk(path, on_node) { if !e.walk(path, on_node) {
return false; return false;
} }
@ -1856,7 +1856,7 @@ mod tests {
assert_eq!(size_of::<Option<ast::Expr>>(), 16); assert_eq!(size_of::<Option<ast::Expr>>(), 16);
assert_eq!(size_of::<ast::Stmt>(), 40); assert_eq!(size_of::<ast::Stmt>(), 40);
assert_eq!(size_of::<Option<ast::Stmt>>(), 40); assert_eq!(size_of::<Option<ast::Stmt>>(), 40);
assert_eq!(size_of::<FnPtr>(), 32); assert_eq!(size_of::<FnPtr>(), 80);
assert_eq!(size_of::<Scope>(), 288); assert_eq!(size_of::<Scope>(), 288);
assert_eq!(size_of::<LexError>(), 56); assert_eq!(size_of::<LexError>(), 56);
assert_eq!(size_of::<ParseError>(), 16); assert_eq!(size_of::<ParseError>(), 16);

View File

@ -45,6 +45,7 @@ fn print_help() {
println!("help => print this help"); println!("help => print this help");
println!("quit, exit => quit"); println!("quit, exit => quit");
println!("scope => print all variables in the scope"); println!("scope => print all variables in the scope");
#[cfg(feature = "metadata")]
println!("functions => print all functions defined"); println!("functions => print all functions defined");
println!("ast => print the last AST (optimized)"); println!("ast => print the last AST (optimized)");
println!("astu => print the last raw, un-optimized AST"); println!("astu => print the last raw, un-optimized AST");
@ -202,6 +203,7 @@ fn main() {
println!("{:#?}\n", ast); println!("{:#?}\n", ast);
continue; continue;
} }
#[cfg(feature = "metadata")]
"functions" => { "functions" => {
// print a list of all registered functions // print a list of all registered functions
engine engine

View File

@ -1349,7 +1349,7 @@ impl Dynamic {
} }
if TypeId::of::<T>() == TypeId::of::<String>() { if TypeId::of::<T>() == TypeId::of::<String>() {
return match &self.0 { return match &self.0 {
Union::Str(value, _) => <dyn Any>::downcast_ref::<T>(value.as_ref()), Union::Str(value, _) => <dyn Any>::downcast_ref::<T>(value.as_ref() as &String),
_ => None, _ => None,
}; };
} }

View File

@ -75,7 +75,7 @@ impl Imports {
.iter() .iter()
.enumerate() .enumerate()
.rev() .rev()
.find_map(|(i, key)| if key.as_str() == name { Some(i) } else { None }) .find_map(|(i, key)| if *key == name { Some(i) } else { None })
} }
/// Push an imported [modules][Module] onto the stack. /// Push an imported [modules][Module] onto the stack.
#[inline(always)] #[inline(always)]
@ -342,13 +342,13 @@ impl<'a> Target<'a> {
#[inline(always)] #[inline(always)]
pub fn is<T: Variant + Clone>(&self) -> bool { pub fn is<T: Variant + Clone>(&self) -> bool {
match self { match self {
Target::Ref(r) => r.is::<T>(), Self::Ref(r) => r.is::<T>(),
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Target::LockGuard((r, _)) => r.is::<T>(), Self::LockGuard((r, _)) => r.is::<T>(),
Target::Value(r) => r.is::<T>(), Self::Value(r) => r.is::<T>(),
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Target::StringChar(_, _, _) => TypeId::of::<T>() == TypeId::of::<char>(), Self::StringChar(_, _, _) => TypeId::of::<T>() == TypeId::of::<char>(),
} }
} }
/// Get the value of the `Target` as a `Dynamic`, cloning a referenced value if necessary. /// Get the value of the `Target` as a `Dynamic`, cloning a referenced value if necessary.
@ -996,12 +996,12 @@ impl Engine {
}; };
// Check if the variable is `this` // Check if the variable is `this`
if name.as_str() == KEYWORD_THIS { if *name == KEYWORD_THIS {
if let Some(val) = this_ptr { return if let Some(val) = this_ptr {
return Ok(((*val).into(), *pos)); Ok(((*val).into(), *pos))
} else { } else {
return EvalAltResult::ErrorUnboundThis(*pos).into(); EvalAltResult::ErrorUnboundThis(*pos).into()
} };
} }
// Check if it is directly indexed // Check if it is directly indexed
@ -1056,7 +1056,7 @@ impl Engine {
idx_values: &mut StaticVec<ChainArgument>, idx_values: &mut StaticVec<ChainArgument>,
chain_type: ChainType, chain_type: ChainType,
level: usize, level: usize,
new_val: Option<((Dynamic, Position), (&Option<OpAssignment>, Position))>, new_val: Option<((Dynamic, Position), (Option<OpAssignment>, Position))>,
) -> Result<(Dynamic, bool), Box<EvalAltResult>> { ) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
assert!(chain_type != ChainType::NonChaining); assert!(chain_type != ChainType::NonChaining);
@ -1097,6 +1097,8 @@ impl Engine {
// xxx[rhs] op= new_val // xxx[rhs] op= new_val
_ if new_val.is_some() => { _ if new_val.is_some() => {
let idx_val = idx_val.as_index_value(); let idx_val = idx_val.as_index_value();
#[cfg(not(feature = "no_index"))]
let mut idx_val2 = idx_val.clone(); let mut idx_val2 = idx_val.clone();
// `call_setter` is introduced to bypass double mutable borrowing of target // `call_setter` is introduced to bypass double mutable borrowing of target
@ -1357,7 +1359,7 @@ impl Engine {
this_ptr: &mut Option<&mut Dynamic>, this_ptr: &mut Option<&mut Dynamic>,
expr: &Expr, expr: &Expr,
level: usize, level: usize,
new_val: Option<((Dynamic, Position), (&Option<OpAssignment>, Position))>, new_val: Option<((Dynamic, Position), (Option<OpAssignment>, Position))>,
) -> RhaiResult { ) -> RhaiResult {
let (crate::ast::BinaryExpr { lhs, rhs }, chain_type, op_pos) = match expr { let (crate::ast::BinaryExpr { lhs, rhs }, chain_type, op_pos) = match expr {
Expr::Index(x, pos) => (x.as_ref(), ChainType::Index, *pos), Expr::Index(x, pos) => (x.as_ref(), ChainType::Index, *pos),
@ -1524,7 +1526,7 @@ impl Engine {
state: &mut State, state: &mut State,
_lib: &[&Module], _lib: &[&Module],
target: &'t mut Dynamic, target: &'t mut Dynamic,
idx: Dynamic, mut idx: Dynamic,
idx_pos: Position, idx_pos: Position,
_create: bool, _create: bool,
_is_ref: bool, _is_ref: bool,
@ -1557,21 +1559,18 @@ impl Engine {
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Dynamic(Union::Map(map, _)) => { Dynamic(Union::Map(map, _)) => {
// val_map[idx] // val_map[idx]
Ok(if _create { let index = &*idx.read_lock::<ImmutableString>().ok_or_else(|| {
let index = idx.take_immutable_string().map_err(|err| { self.make_type_mismatch_err::<ImmutableString>(idx.type_name(), idx_pos)
self.make_type_mismatch_err::<ImmutableString>(err, idx_pos)
})?; })?;
map.entry(index).or_insert_with(Default::default).into() if _create && !map.contains_key(index) {
} else { map.insert(index.clone(), Default::default());
let index = idx.read_lock::<ImmutableString>().ok_or_else(|| { }
self.make_type_mismatch_err::<ImmutableString>("", idx_pos)
})?;
map.get_mut(&*index) Ok(map
.get_mut(index)
.map(Target::from) .map(Target::from)
.unwrap_or_else(|| Target::from(())) .unwrap_or_else(|| Target::from(())))
})
} }
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
@ -1596,7 +1595,6 @@ impl Engine {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
_ if _indexers => { _ if _indexers => {
let type_name = target.type_name(); let type_name = target.type_name();
let mut idx = idx;
let args = &mut [target, &mut idx]; let args = &mut [target, &mut idx];
let hash_get = FnCallHash::from_native(calc_fn_hash(empty(), FN_IDX_GET, 2)); let hash_get = FnCallHash::from_native(calc_fn_hash(empty(), FN_IDX_GET, 2));
self.exec_fn_call( self.exec_fn_call(
@ -1686,13 +1684,11 @@ impl Engine {
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Expr::Map(x, _) => { Expr::Map(x, _) => {
let mut map = Map::new(); let mut map = x.1.clone();
for (Ident { name: key, .. }, expr) in x.as_ref() { for (Ident { name: key, .. }, expr) in &x.0 {
map.insert( *map.get_mut(key).unwrap() = self
key.clone(), .eval_expr(scope, mods, state, lib, this_ptr, expr, level)?
self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)? .flatten();
.flatten(),
);
} }
Ok(Dynamic(Union::Map(Box::new(map), AccessMode::ReadWrite))) Ok(Dynamic(Union::Map(Box::new(map), AccessMode::ReadWrite)))
} }
@ -1865,7 +1861,7 @@ impl Engine {
mods: &mut Imports, mods: &mut Imports,
state: &mut State, state: &mut State,
lib: &[&Module], lib: &[&Module],
op_info: &Option<OpAssignment>, op_info: Option<OpAssignment>,
op_pos: Position, op_pos: Position,
mut target: Target, mut target: Target,
mut new_value: Dynamic, mut new_value: Dynamic,
@ -1891,20 +1887,19 @@ impl Engine {
lhs_ptr_inner = target.as_mut(); lhs_ptr_inner = target.as_mut();
} }
let hash = *hash_op_assign; let hash = hash_op_assign;
let args = &mut [lhs_ptr_inner, &mut new_value]; let args = &mut [lhs_ptr_inner, &mut new_value];
match self.call_native_fn(mods, state, lib, op, hash, args, true, true, op_pos) { match self.call_native_fn(mods, state, lib, op, hash, args, true, true, op_pos) {
Ok(_) => (), Ok(_) => (),
Err(err) if matches!(err.as_ref(), EvalAltResult::ErrorFunctionNotFound(f, _) if f.starts_with(op.as_ref())) => Err(err) if matches!(err.as_ref(), EvalAltResult::ErrorFunctionNotFound(f, _) if f.starts_with(op)) =>
{ {
// Expand to `var = var op rhs` // Expand to `var = var op rhs`
let op = &op[..op.len() - 1]; // extract operator without = let op = &op[..op.len() - 1]; // extract operator without =
// Run function // Run function
let (value, _) = self.call_native_fn( let (value, _) = self
mods, state, lib, op, *hash_op, args, true, false, op_pos, .call_native_fn(mods, state, lib, op, hash_op, args, true, false, op_pos)?;
)?;
*args[0] = value.flatten(); *args[0] = value.flatten();
} }
@ -1977,7 +1972,7 @@ impl Engine {
mods, mods,
state, state,
lib, lib,
op_info, op_info.clone(),
*op_pos, *op_pos,
lhs_ptr, lhs_ptr,
rhs_val, rhs_val,
@ -1993,7 +1988,7 @@ impl Engine {
let rhs_val = self let rhs_val = self
.eval_expr(scope, mods, state, lib, this_ptr, rhs_expr, level)? .eval_expr(scope, mods, state, lib, this_ptr, rhs_expr, level)?
.flatten(); .flatten();
let _new_val = Some(((rhs_val, rhs_expr.position()), (op_info, *op_pos))); let _new_val = Some(((rhs_val, rhs_expr.position()), (op_info.clone(), *op_pos)));
// Must be either `var[index] op= val` or `var.prop op= val` // Must be either `var[index] op= val` or `var.prop op= val`
match lhs_expr { match lhs_expr {

View File

@ -9,12 +9,11 @@ use crate::stdlib::{
any::{type_name, TypeId}, any::{type_name, TypeId},
boxed::Box, boxed::Box,
format, format,
string::{String, ToString}, string::String,
vec::Vec,
}; };
use crate::{ use crate::{
scope::Scope, Dynamic, Engine, EvalAltResult, FnAccess, FnNamespace, Module, NativeCallContext, scope::Scope, Dynamic, Engine, EvalAltResult, FnAccess, FnNamespace, ImmutableString, Module,
ParseError, Position, RhaiResult, Shared, StaticVec, AST, NativeCallContext, ParseError, Position, RhaiResult, Shared, AST,
}; };
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
@ -52,26 +51,36 @@ impl Engine {
/// # } /// # }
/// ``` /// ```
#[inline] #[inline]
pub fn register_fn<A, F>(&mut self, name: &str, func: F) -> &mut Self pub fn register_fn<N, A, F>(&mut self, name: N, func: F) -> &mut Self
where where
N: AsRef<str> + Into<ImmutableString>,
F: RegisterNativeFunction<A, ()>, F: RegisterNativeFunction<A, ()>,
{ {
let param_types = F::param_types(); let param_types = F::param_types();
let mut param_type_names: StaticVec<_> = F::param_names()
#[cfg(feature = "metadata")]
let mut param_type_names: crate::StaticVec<_> = F::param_names()
.iter() .iter()
.map(|ty| format!("_: {}", self.map_type_name(ty))) .map(|ty| format!("_: {}", self.map_type_name(ty)))
.collect(); .collect();
#[cfg(feature = "metadata")]
if F::return_type() != TypeId::of::<()>() { if F::return_type() != TypeId::of::<()>() {
param_type_names.push(self.map_type_name(F::return_type_name()).to_string()); param_type_names.push(self.map_type_name(F::return_type_name()).into());
} }
let param_type_names: StaticVec<_> =
param_type_names.iter().map(|ty| ty.as_str()).collect(); #[cfg(feature = "metadata")]
let param_type_names: Option<crate::StaticVec<_>> =
Some(param_type_names.iter().map(|ty| ty.as_str()).collect());
#[cfg(not(feature = "metadata"))]
let param_type_names: Option<[&str; 0]> = None;
self.global_namespace.set_fn( self.global_namespace.set_fn(
name, name,
FnNamespace::Global, FnNamespace::Global,
FnAccess::Public, FnAccess::Public,
Some(&param_type_names), param_type_names.as_ref().map(|v| v.as_ref()),
&param_types, &param_types,
func.into_callable_function(), func.into_callable_function(),
); );
@ -102,24 +111,34 @@ impl Engine {
/// .expect_err("expecting division by zero error!"); /// .expect_err("expecting division by zero error!");
/// ``` /// ```
#[inline] #[inline]
pub fn register_result_fn<A, F, R>(&mut self, name: &str, func: F) -> &mut Self pub fn register_result_fn<N, A, F, R>(&mut self, name: N, func: F) -> &mut Self
where where
N: AsRef<str> + Into<ImmutableString>,
F: RegisterNativeFunction<A, Result<R, Box<EvalAltResult>>>, F: RegisterNativeFunction<A, Result<R, Box<EvalAltResult>>>,
{ {
let param_types = F::param_types(); let param_types = F::param_types();
let mut param_type_names: StaticVec<_> = F::param_names()
#[cfg(feature = "metadata")]
let param_type_names: crate::StaticVec<_> = F::param_names()
.iter() .iter()
.map(|ty| format!("_: {}", self.map_type_name(ty))) .map(|ty| format!("_: {}", self.map_type_name(ty)))
.chain(crate::stdlib::iter::once(
self.map_type_name(F::return_type_name()).into(),
))
.collect(); .collect();
param_type_names.push(self.map_type_name(F::return_type_name()).to_string());
let param_type_names: StaticVec<&str> = #[cfg(feature = "metadata")]
param_type_names.iter().map(|ty| ty.as_str()).collect(); let param_type_names: Option<crate::StaticVec<_>> =
Some(param_type_names.iter().map(|ty| ty.as_str()).collect());
#[cfg(not(feature = "metadata"))]
let param_type_names: Option<[&str; 0]> = None;
self.global_namespace.set_fn( self.global_namespace.set_fn(
name, name,
FnNamespace::Global, FnNamespace::Global,
FnAccess::Public, FnAccess::Public,
Some(&param_type_names), param_type_names.as_ref().map(|v| v.as_ref()),
&param_types, &param_types,
func.into_callable_function(), func.into_callable_function(),
); );
@ -142,14 +161,18 @@ impl Engine {
/// To access the first mutable parameter, use `args.get_mut(0).unwrap()` /// To access the first mutable parameter, use `args.get_mut(0).unwrap()`
#[deprecated = "this function is volatile and may change"] #[deprecated = "this function is volatile and may change"]
#[inline(always)] #[inline(always)]
pub fn register_raw_fn<T: Variant + Clone>( pub fn register_raw_fn<N, T>(
&mut self, &mut self,
name: &str, name: N,
arg_types: &[TypeId], arg_types: &[TypeId],
func: impl Fn(NativeCallContext, &mut FnCallArgs) -> Result<T, Box<EvalAltResult>> func: impl Fn(NativeCallContext, &mut FnCallArgs) -> Result<T, Box<EvalAltResult>>
+ SendSync + SendSync
+ 'static, + 'static,
) -> &mut Self { ) -> &mut Self
where
N: AsRef<str> + Into<ImmutableString>,
T: Variant + Clone,
{
self.global_namespace.set_raw_fn( self.global_namespace.set_raw_fn(
name, name,
FnNamespace::Global, FnNamespace::Global,
@ -878,25 +901,29 @@ impl Engine {
/// # } /// # }
/// ``` /// ```
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
pub fn register_static_module(&mut self, name: &str, module: Shared<Module>) -> &mut Self { pub fn register_static_module(
&mut self,
name: impl AsRef<str> + Into<ImmutableString>,
module: Shared<Module>,
) -> &mut Self {
fn register_static_module_raw( fn register_static_module_raw(
root: &mut crate::stdlib::collections::BTreeMap<crate::ImmutableString, Shared<Module>>, root: &mut crate::stdlib::collections::BTreeMap<crate::ImmutableString, Shared<Module>>,
name: &str, name: impl AsRef<str> + Into<ImmutableString>,
module: Shared<Module>, module: Shared<Module>,
) { ) {
let separator = crate::token::Token::DoubleColon.syntax(); let separator = crate::token::Token::DoubleColon.syntax();
if !name.contains(separator.as_ref()) { if !name.as_ref().contains(separator.as_ref()) {
if !module.is_indexed() { if !module.is_indexed() {
// Index the module (making a clone copy if necessary) if it is not indexed // Index the module (making a clone copy if necessary) if it is not indexed
let mut module = crate::fn_native::shared_take_or_clone(module); let mut module = crate::fn_native::shared_take_or_clone(module);
module.build_index(); module.build_index();
root.insert(name.trim().into(), module.into()); root.insert(name.into(), module.into());
} else { } else {
root.insert(name.trim().into(), module); root.insert(name.into(), module);
} }
} else { } else {
let mut iter = name.splitn(2, separator.as_ref()); let mut iter = name.as_ref().splitn(2, separator.as_ref());
let sub_module = iter.next().unwrap().trim(); let sub_module = iter.next().unwrap().trim();
let remainder = iter.next().unwrap().trim(); let remainder = iter.next().unwrap().trim();
@ -915,7 +942,7 @@ impl Engine {
} }
} }
register_static_module_raw(&mut self.global_sub_modules, name.as_ref(), module); register_static_module_raw(&mut self.global_sub_modules, name, module);
self self
} }
@ -927,7 +954,11 @@ impl Engine {
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
#[inline(always)] #[inline(always)]
#[deprecated = "use `register_static_module` instead"] #[deprecated = "use `register_static_module` instead"]
pub fn register_module(&mut self, name: &str, module: impl Into<Shared<Module>>) -> &mut Self { pub fn register_module(
&mut self,
name: impl AsRef<str> + Into<ImmutableString>,
module: impl Into<Shared<Module>>,
) -> &mut Self {
self.register_static_module(name, module.into()) self.register_static_module(name, module.into())
} }
/// Compile a string into an [`AST`], which can be used later for evaluation. /// Compile a string into an [`AST`], which can be used later for evaluation.
@ -1013,7 +1044,6 @@ impl Engine {
fn_native::shared_take_or_clone, fn_native::shared_take_or_clone,
module::resolvers::StaticModuleResolver, module::resolvers::StaticModuleResolver,
stdlib::collections::BTreeSet, stdlib::collections::BTreeSet,
ImmutableString,
}; };
fn collect_imports( fn collect_imports(
@ -1232,7 +1262,7 @@ impl Engine {
Self::read_file(path).and_then(|contents| Ok(self.compile_with_scope(scope, &contents)?)) Self::read_file(path).and_then(|contents| Ok(self.compile_with_scope(scope, &contents)?))
} }
/// Parse a JSON string into an [object map][`Map`]. /// Parse a JSON string into an [object map][`Map`].
/// This is a light-weight alternative to using, say, [`serde_json`][https://crates.io/crates/serde_json] to deserialize the JSON. /// This is a light-weight alternative to using, say, [`serde_json`] to deserialize the JSON.
/// ///
/// The JSON string must be an object hash. It cannot be a simple scalar value. /// The JSON string must be an object hash. It cannot be a simple scalar value.
/// ///
@ -1271,16 +1301,21 @@ impl Engine {
/// # } /// # }
/// ``` /// ```
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
pub fn parse_json(&self, json: &str, has_null: bool) -> Result<Map, Box<EvalAltResult>> { pub fn parse_json(
&self,
json: impl AsRef<str>,
has_null: bool,
) -> Result<Map, Box<EvalAltResult>> {
use crate::token::Token; use crate::token::Token;
let json = json.as_ref();
let mut scope = Default::default(); let mut scope = Default::default();
// Trims the JSON string and add a '#' in front // Trims the JSON string and add a '#' in front
let json_text = json.trim_start(); let json_text = json.trim_start();
let scripts = if json_text.starts_with(Token::MapStart.syntax().as_ref()) { let scripts = if json_text.starts_with(Token::MapStart.keyword_syntax()) {
[json_text, ""] [json_text, ""]
} else if json_text.starts_with(Token::LeftBrace.syntax().as_ref()) { } else if json_text.starts_with(Token::LeftBrace.keyword_syntax()) {
["#", json_text] ["#", json_text]
} else { } else {
return Err(crate::ParseErrorType::MissingToken( return Err(crate::ParseErrorType::MissingToken(
@ -1765,7 +1800,7 @@ impl Engine {
&self, &self,
scope: &mut Scope, scope: &mut Scope,
ast: &AST, ast: &AST,
name: &str, name: impl AsRef<str>,
args: impl crate::fn_args::FuncArgs, args: impl crate::fn_args::FuncArgs,
) -> Result<T, Box<EvalAltResult>> { ) -> Result<T, Box<EvalAltResult>> {
let mut arg_values: crate::StaticVec<_> = Default::default(); let mut arg_values: crate::StaticVec<_> = Default::default();
@ -1844,7 +1879,7 @@ impl Engine {
scope: &mut Scope, scope: &mut Scope,
ast: &AST, ast: &AST,
eval_ast: bool, eval_ast: bool,
name: &str, name: impl AsRef<str>,
mut this_ptr: Option<&mut Dynamic>, mut this_ptr: Option<&mut Dynamic>,
mut arg_values: impl AsMut<[Dynamic]>, mut arg_values: impl AsMut<[Dynamic]>,
) -> RhaiResult { ) -> RhaiResult {
@ -1867,7 +1902,7 @@ impl Engine {
scope: &mut Scope, scope: &mut Scope,
ast: &AST, ast: &AST,
eval_ast: bool, eval_ast: bool,
name: &str, name: impl AsRef<str>,
this_ptr: &mut Option<&mut Dynamic>, this_ptr: &mut Option<&mut Dynamic>,
args: &mut FnCallArgs, args: &mut FnCallArgs,
) -> RhaiResult { ) -> RhaiResult {
@ -1881,12 +1916,14 @@ impl Engine {
let fn_def = ast let fn_def = ast
.lib() .lib()
.get_script_fn(name, args.len()) .get_script_fn(name.as_ref(), args.len())
.ok_or_else(|| EvalAltResult::ErrorFunctionNotFound(name.into(), Position::NONE))?; .ok_or_else(|| {
EvalAltResult::ErrorFunctionNotFound(name.as_ref().into(), Position::NONE)
})?;
// Check for data race. // Check for data race.
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
crate::fn_call::ensure_no_data_race(name, args, false)?; crate::fn_call::ensure_no_data_race(name.as_ref(), args, false)?;
self.call_script_fn( self.call_script_fn(
scope, scope,
@ -1936,13 +1973,15 @@ impl Engine {
crate::optimize::optimize_into_ast(self, scope, stmt.into_vec(), lib, optimization_level) crate::optimize::optimize_into_ast(self, scope, stmt.into_vec(), lib, optimization_level)
} }
/// Generate a list of all registered functions. /// Generate a list of all registered functions.
/// Available under the `metadata` feature only.
/// ///
/// Functions from the following sources are included, in order: /// Functions from the following sources are included, in order:
/// 1) Functions registered into the global namespace /// 1) Functions registered into the global namespace
/// 2) Functions in registered sub-modules /// 2) Functions in registered sub-modules
/// 3) Functions in packages (optional) /// 3) Functions in packages (optional)
pub fn gen_fn_signatures(&self, include_packages: bool) -> Vec<String> { #[cfg(feature = "metadata")]
let mut signatures: Vec<_> = Default::default(); pub fn gen_fn_signatures(&self, include_packages: bool) -> crate::stdlib::vec::Vec<String> {
let mut signatures: crate::stdlib::vec::Vec<_> = Default::default();
signatures.extend(self.global_namespace.gen_fn_signatures()); signatures.extend(self.global_namespace.gen_fn_signatures());

View File

@ -236,7 +236,7 @@ impl Engine {
/// # } /// # }
/// ``` /// ```
#[inline(always)] #[inline(always)]
pub fn disable_symbol(&mut self, symbol: &str) -> &mut Self { pub fn disable_symbol(&mut self, symbol: impl Into<String>) -> &mut Self {
self.disabled_symbols.insert(symbol.into()); self.disabled_symbols.insert(symbol.into());
self self
} }
@ -270,7 +270,7 @@ impl Engine {
/// ``` /// ```
pub fn register_custom_operator( pub fn register_custom_operator(
&mut self, &mut self,
keyword: &str, keyword: impl AsRef<str> + Into<String>,
precedence: u8, precedence: u8,
) -> Result<&mut Self, String> { ) -> Result<&mut Self, String> {
let precedence = Precedence::new(precedence); let precedence = Precedence::new(precedence);
@ -279,25 +279,25 @@ impl Engine {
return Err("precedence cannot be zero".into()); return Err("precedence cannot be zero".into());
} }
match Token::lookup_from_syntax(keyword) { match Token::lookup_from_syntax(keyword.as_ref()) {
// Standard identifiers, reserved keywords and custom keywords are OK // Standard identifiers, reserved keywords and custom keywords are OK
None | Some(Token::Reserved(_)) | Some(Token::Custom(_)) => (), None | Some(Token::Reserved(_)) | Some(Token::Custom(_)) => (),
// Active standard keywords cannot be made custom // Active standard keywords cannot be made custom
// Disabled keywords are OK // Disabled keywords are OK
Some(token) if token.is_keyword() => { Some(token) if token.is_keyword() => {
if !self.disabled_symbols.contains(token.syntax().as_ref()) { if !self.disabled_symbols.contains(token.syntax().as_ref()) {
return Err(format!("'{}' is a reserved keyword", keyword).into()); return Err(format!("'{}' is a reserved keyword", keyword.as_ref()).into());
} }
} }
// Active standard symbols cannot be made custom // Active standard symbols cannot be made custom
Some(token) if token.is_symbol() => { Some(token) if token.is_symbol() => {
if !self.disabled_symbols.contains(token.syntax().as_ref()) { if !self.disabled_symbols.contains(token.syntax().as_ref()) {
return Err(format!("'{}' is a reserved operator", keyword).into()); return Err(format!("'{}' is a reserved operator", keyword.as_ref()).into());
} }
} }
// Active standard symbols cannot be made custom // Active standard symbols cannot be made custom
Some(token) if !self.disabled_symbols.contains(token.syntax().as_ref()) => { Some(token) if !self.disabled_symbols.contains(token.syntax().as_ref()) => {
return Err(format!("'{}' is a reserved symbol", keyword).into()) return Err(format!("'{}' is a reserved symbol", keyword.as_ref()).into())
} }
// Disabled symbols are OK // Disabled symbols are OK
Some(_) => (), Some(_) => (),

View File

@ -26,7 +26,7 @@ pub trait FuncArgs {
/// } /// }
/// ///
/// impl FuncArgs for Options { /// impl FuncArgs for Options {
/// fn parse<C: Extend<Dynamic>>(self, container: &mut C) { /// fn parse<CONTAINER: Extend<Dynamic>>(self, container: &mut CONTAINER) {
/// container.extend(std::iter::once(self.foo.into())); /// container.extend(std::iter::once(self.foo.into()));
/// container.extend(std::iter::once(self.bar.into())); /// container.extend(std::iter::once(self.bar.into()));
/// container.extend(std::iter::once(self.baz.into())); /// container.extend(std::iter::once(self.baz.into()));
@ -51,11 +51,12 @@ pub trait FuncArgs {
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
fn parse<T: Extend<Dynamic>>(self, container: &mut T); fn parse<CONTAINER: Extend<Dynamic>>(self, container: &mut CONTAINER);
} }
impl<T: Variant + Clone> FuncArgs for Vec<T> { impl<T: Variant + Clone> FuncArgs for Vec<T> {
fn parse<C: Extend<Dynamic>>(self, container: &mut C) { #[inline(always)]
fn parse<CONTAINER: Extend<Dynamic>>(self, container: &mut CONTAINER) {
container.extend(self.into_iter().map(Variant::into_dynamic)); container.extend(self.into_iter().map(Variant::into_dynamic));
} }
} }

View File

@ -475,9 +475,9 @@ impl Engine {
fn_def fn_def
.lib .lib
.as_ref() .as_ref()
.and_then(|m| m.id()) .and_then(|m| m.id().map(|id| id.to_string()))
.unwrap_or_else(|| state.source.as_ref().map_or_else(|| "", |s| s.as_str())) .or_else(|| state.source.as_ref().map(|s| s.to_string()))
.to_string(), .unwrap_or_default(),
err, err,
pos, pos,
) )
@ -651,14 +651,14 @@ impl Engine {
crate::engine::KEYWORD_IS_DEF_FN crate::engine::KEYWORD_IS_DEF_FN
if args.len() == 2 && args[0].is::<FnPtr>() && args[1].is::<crate::INT>() => if args.len() == 2 && args[0].is::<FnPtr>() && args[1].is::<crate::INT>() =>
{ {
let fn_name = args[0].read_lock::<ImmutableString>().unwrap(); let fn_name = &*args[0].read_lock::<ImmutableString>().unwrap();
let num_params = args[1].as_int().unwrap(); let num_params = args[1].as_int().unwrap();
return Ok(( return Ok((
if num_params < 0 { if num_params < 0 {
Dynamic::FALSE Dynamic::FALSE
} else { } else {
let hash_script = calc_fn_hash(empty(), &fn_name, num_params as usize); let hash_script = calc_fn_hash(empty(), fn_name, num_params as usize);
self.has_script_fn(Some(mods), state, lib, hash_script) self.has_script_fn(Some(mods), state, lib, hash_script)
.into() .into()
}, },
@ -737,7 +737,7 @@ impl Engine {
if !func.externals.is_empty() { if !func.externals.is_empty() {
captured captured
.into_iter() .into_iter()
.filter(|(name, _, _)| func.externals.iter().any(|ex| ex == name)) .filter(|(name, _, _)| func.externals.contains(name.as_ref()))
.for_each(|(name, value, _)| { .for_each(|(name, value, _)| {
// Consume the scope values. // Consume the scope values.
scope.push_dynamic(name, value); scope.push_dynamic(name, value);
@ -1132,13 +1132,10 @@ impl Engine {
// Append the new curried arguments to the existing list. // Append the new curried arguments to the existing list.
args_expr.iter().skip(1).try_for_each( args_expr.iter().skip(1).try_for_each(|expr| {
|expr| -> Result<(), Box<EvalAltResult>> { self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)
fn_curry .map(|value| fn_curry.push(value))
.push(self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?); })?;
Ok(())
},
)?;
return Ok(FnPtr::new_unchecked(name, fn_curry).into()); return Ok(FnPtr::new_unchecked(name, fn_curry).into());
} }
@ -1222,8 +1219,8 @@ impl Engine {
state state
.source .source
.as_ref() .as_ref()
.map_or_else(|| "", |s| s.as_str()) .map(|s| s.to_string())
.to_string(), .unwrap_or_default(),
err, err,
pos, pos,
)) ))

View File

@ -7,15 +7,14 @@ use crate::stdlib::{
boxed::Box, boxed::Box,
convert::{TryFrom, TryInto}, convert::{TryFrom, TryInto},
fmt, fmt,
iter::empty, iter::{empty, once},
mem, mem,
string::String, string::String,
vec::Vec,
}; };
use crate::token::is_valid_identifier; use crate::token::is_valid_identifier;
use crate::{ use crate::{
calc_fn_hash, Dynamic, Engine, EvalAltResult, EvalContext, ImmutableString, Module, Position, calc_fn_hash, Dynamic, Engine, EvalAltResult, EvalContext, ImmutableString, Module, Position,
RhaiResult, RhaiResult, StaticVec,
}; };
/// Trait that maps to `Send + Sync` only under the `sync` feature. /// Trait that maps to `Send + Sync` only under the `sync` feature.
@ -185,10 +184,12 @@ impl<'a> NativeCallContext<'a> {
#[inline(always)] #[inline(always)]
pub fn call_fn_dynamic_raw( pub fn call_fn_dynamic_raw(
&self, &self,
fn_name: &str, fn_name: impl AsRef<str>,
is_method: bool, is_method: bool,
args: &mut [&mut Dynamic], args: &mut [&mut Dynamic],
) -> RhaiResult { ) -> RhaiResult {
let fn_name = fn_name.as_ref();
let hash = if is_method { let hash = if is_method {
FnCallHash::from_script_and_native( FnCallHash::from_script_and_native(
calc_fn_hash(empty(), fn_name, args.len() - 1), calc_fn_hash(empty(), fn_name, args.len() - 1),
@ -251,7 +252,7 @@ pub type FnCallArgs<'a> = [&'a mut Dynamic];
/// A general function pointer, which may carry additional (i.e. curried) argument values /// A general function pointer, which may carry additional (i.e. curried) argument values
/// to be passed onto a function during a call. /// to be passed onto a function during a call.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct FnPtr(ImmutableString, Vec<Dynamic>); pub struct FnPtr(ImmutableString, StaticVec<Dynamic>);
impl FnPtr { impl FnPtr {
/// Create a new function pointer. /// Create a new function pointer.
@ -261,7 +262,10 @@ impl FnPtr {
} }
/// Create a new function pointer without checking its parameters. /// Create a new function pointer without checking its parameters.
#[inline(always)] #[inline(always)]
pub(crate) fn new_unchecked(name: impl Into<ImmutableString>, curry: Vec<Dynamic>) -> Self { pub(crate) fn new_unchecked(
name: impl Into<ImmutableString>,
curry: StaticVec<Dynamic>,
) -> Self {
Self(name.into(), curry) Self(name.into(), curry)
} }
/// Get the name of the function. /// Get the name of the function.
@ -276,7 +280,7 @@ impl FnPtr {
} }
/// Get the underlying data of the function pointer. /// Get the underlying data of the function pointer.
#[inline(always)] #[inline(always)]
pub(crate) fn take_data(self) -> (ImmutableString, Vec<Dynamic>) { pub(crate) fn take_data(self) -> (ImmutableString, StaticVec<Dynamic>) {
(self.0, self.1) (self.0, self.1)
} }
/// Get the curried arguments. /// Get the curried arguments.
@ -324,22 +328,28 @@ impl FnPtr {
this_ptr: Option<&mut Dynamic>, this_ptr: Option<&mut Dynamic>,
mut arg_values: impl AsMut<[Dynamic]>, mut arg_values: impl AsMut<[Dynamic]>,
) -> RhaiResult { ) -> RhaiResult {
let arg_values = arg_values.as_mut(); let mut args_data;
let mut args_data = self let arg_values = if self.curry().is_empty() {
arg_values.as_mut()
} else {
args_data = self
.curry() .curry()
.iter() .iter()
.cloned() .cloned()
.chain(arg_values.iter_mut().map(mem::take)) .chain(arg_values.as_mut().iter_mut().map(mem::take))
.collect::<Vec<_>>(); .collect::<StaticVec<_>>();
let mut args = args_data.iter_mut().collect::<Vec<_>>(); args_data.as_mut()
};
let is_method = this_ptr.is_some(); let is_method = this_ptr.is_some();
if let Some(obj) = this_ptr { let mut args: StaticVec<_> = if let Some(obj) = this_ptr {
args.insert(0, obj); once(obj).chain(arg_values.iter_mut()).collect()
} } else {
arg_values.iter_mut().collect()
};
ctx.call_fn_dynamic_raw(self.fn_name(), is_method, args.as_mut()) ctx.call_fn_dynamic_raw(self.fn_name(), is_method, args.as_mut())
} }

View File

@ -5,13 +5,7 @@
use crate::dynamic::{DynamicWriteLock, Variant}; use crate::dynamic::{DynamicWriteLock, Variant};
use crate::fn_native::{CallableFunction, FnAny, FnCallArgs, SendSync}; use crate::fn_native::{CallableFunction, FnAny, FnCallArgs, SendSync};
use crate::r#unsafe::unsafe_try_cast; use crate::r#unsafe::unsafe_try_cast;
use crate::stdlib::{ use crate::stdlib::{any::TypeId, boxed::Box, mem, string::String, vec};
any::{type_name, TypeId},
boxed::Box,
mem,
string::String,
vec,
};
use crate::{Dynamic, EvalAltResult, NativeCallContext}; use crate::{Dynamic, EvalAltResult, NativeCallContext};
// These types are used to build a unique _marker_ tuple type for each combination // These types are used to build a unique _marker_ tuple type for each combination
@ -61,16 +55,22 @@ pub fn by_value<T: Variant + Clone>(data: &mut Dynamic) -> T {
/// Trait to register custom Rust functions. /// Trait to register custom Rust functions.
pub trait RegisterNativeFunction<Args, Result> { pub trait RegisterNativeFunction<Args, Result> {
/// Convert this function into a [`CallableFunction`].
fn into_callable_function(self) -> CallableFunction;
/// Get the type ID's of this function's parameters. /// Get the type ID's of this function's parameters.
fn param_types() -> Box<[TypeId]>; fn param_types() -> Box<[TypeId]>;
/// Get the type names of this function's parameters. /// Get the type names of this function's parameters.
/// Available under the `metadata` feature only.
#[cfg(feature = "metadata")]
fn param_names() -> Box<[&'static str]>; fn param_names() -> Box<[&'static str]>;
/// Get the type ID of this function's return value. /// Get the type ID of this function's return value.
/// Available under the `metadata` feature only.
#[cfg(feature = "metadata")]
fn return_type() -> TypeId; fn return_type() -> TypeId;
/// Get the type name of this function's return value. /// Get the type name of this function's return value.
/// Available under the `metadata` feature only.
#[cfg(feature = "metadata")]
fn return_type_name() -> &'static str; fn return_type_name() -> &'static str;
/// Convert this function into a [`CallableFunction`].
fn into_callable_function(self) -> CallableFunction;
} }
macro_rules! def_register { macro_rules! def_register {
@ -91,9 +91,9 @@ macro_rules! def_register {
RET: Variant + Clone RET: Variant + Clone
> RegisterNativeFunction<($($mark,)*), ()> for FN { > RegisterNativeFunction<($($mark,)*), ()> for FN {
#[inline(always)] fn param_types() -> Box<[TypeId]> { vec![$(TypeId::of::<$par>()),*].into_boxed_slice() } #[inline(always)] fn param_types() -> Box<[TypeId]> { vec![$(TypeId::of::<$par>()),*].into_boxed_slice() }
#[inline(always)] fn param_names() -> Box<[&'static str]> { vec![$(type_name::<$par>()),*].into_boxed_slice() } #[cfg(feature = "metadata")] #[inline(always)] fn param_names() -> Box<[&'static str]> { vec![$(crate::stdlib::any::type_name::<$par>()),*].into_boxed_slice() }
#[inline(always)] fn return_type() -> TypeId { TypeId::of::<RET>() } #[cfg(feature = "metadata")] #[inline(always)] fn return_type() -> TypeId { TypeId::of::<RET>() }
#[inline(always)] fn return_type_name() -> &'static str { type_name::<RET>() } #[cfg(feature = "metadata")] #[inline(always)] fn return_type_name() -> &'static str { crate::stdlib::any::type_name::<RET>() }
#[inline(always)] fn into_callable_function(self) -> CallableFunction { #[inline(always)] fn into_callable_function(self) -> CallableFunction {
CallableFunction::$abi(Box::new(move |_: NativeCallContext, args: &mut FnCallArgs| { CallableFunction::$abi(Box::new(move |_: NativeCallContext, args: &mut FnCallArgs| {
// The arguments are assumed to be of the correct number and types! // The arguments are assumed to be of the correct number and types!
@ -115,9 +115,9 @@ macro_rules! def_register {
RET: Variant + Clone RET: Variant + Clone
> RegisterNativeFunction<(NativeCallContext<'static>, $($mark,)*), ()> for FN { > RegisterNativeFunction<(NativeCallContext<'static>, $($mark,)*), ()> for FN {
#[inline(always)] fn param_types() -> Box<[TypeId]> { vec![$(TypeId::of::<$par>()),*].into_boxed_slice() } #[inline(always)] fn param_types() -> Box<[TypeId]> { vec![$(TypeId::of::<$par>()),*].into_boxed_slice() }
#[inline(always)] fn param_names() -> Box<[&'static str]> { vec![$(type_name::<$par>()),*].into_boxed_slice() } #[cfg(feature = "metadata")] #[inline(always)] fn param_names() -> Box<[&'static str]> { vec![$(crate::stdlib::any::type_name::<$par>()),*].into_boxed_slice() }
#[inline(always)] fn return_type() -> TypeId { TypeId::of::<RET>() } #[cfg(feature = "metadata")] #[inline(always)] fn return_type() -> TypeId { TypeId::of::<RET>() }
#[inline(always)] fn return_type_name() -> &'static str { type_name::<RET>() } #[cfg(feature = "metadata")] #[inline(always)] fn return_type_name() -> &'static str { crate::stdlib::any::type_name::<RET>() }
#[inline(always)] fn into_callable_function(self) -> CallableFunction { #[inline(always)] fn into_callable_function(self) -> CallableFunction {
CallableFunction::$abi(Box::new(move |ctx: NativeCallContext, args: &mut FnCallArgs| { CallableFunction::$abi(Box::new(move |ctx: NativeCallContext, args: &mut FnCallArgs| {
// The arguments are assumed to be of the correct number and types! // The arguments are assumed to be of the correct number and types!
@ -139,9 +139,9 @@ macro_rules! def_register {
RET: Variant + Clone RET: Variant + Clone
> RegisterNativeFunction<($($mark,)*), Result<RET, Box<EvalAltResult>>> for FN { > RegisterNativeFunction<($($mark,)*), Result<RET, Box<EvalAltResult>>> for FN {
#[inline(always)] fn param_types() -> Box<[TypeId]> { vec![$(TypeId::of::<$par>()),*].into_boxed_slice() } #[inline(always)] fn param_types() -> Box<[TypeId]> { vec![$(TypeId::of::<$par>()),*].into_boxed_slice() }
#[inline(always)] fn param_names() -> Box<[&'static str]> { vec![$(type_name::<$par>()),*].into_boxed_slice() } #[cfg(feature = "metadata")] #[inline(always)] fn param_names() -> Box<[&'static str]> { vec![$(crate::stdlib::any::type_name::<$par>()),*].into_boxed_slice() }
#[inline(always)] fn return_type() -> TypeId { TypeId::of::<Result<RET, Box<EvalAltResult>>>() } #[cfg(feature = "metadata")] #[inline(always)] fn return_type() -> TypeId { TypeId::of::<Result<RET, Box<EvalAltResult>>>() }
#[inline(always)] fn return_type_name() -> &'static str { type_name::<Result<RET, Box<EvalAltResult>>>() } #[cfg(feature = "metadata")] #[inline(always)] fn return_type_name() -> &'static str { crate::stdlib::any::type_name::<Result<RET, Box<EvalAltResult>>>() }
#[inline(always)] fn into_callable_function(self) -> CallableFunction { #[inline(always)] fn into_callable_function(self) -> CallableFunction {
CallableFunction::$abi(Box::new(move |_: NativeCallContext, args: &mut FnCallArgs| { CallableFunction::$abi(Box::new(move |_: NativeCallContext, args: &mut FnCallArgs| {
// The arguments are assumed to be of the correct number and types! // The arguments are assumed to be of the correct number and types!
@ -160,9 +160,9 @@ macro_rules! def_register {
RET: Variant + Clone RET: Variant + Clone
> RegisterNativeFunction<(NativeCallContext<'static>, $($mark,)*), Result<RET, Box<EvalAltResult>>> for FN { > RegisterNativeFunction<(NativeCallContext<'static>, $($mark,)*), Result<RET, Box<EvalAltResult>>> for FN {
#[inline(always)] fn param_types() -> Box<[TypeId]> { vec![$(TypeId::of::<$par>()),*].into_boxed_slice() } #[inline(always)] fn param_types() -> Box<[TypeId]> { vec![$(TypeId::of::<$par>()),*].into_boxed_slice() }
#[inline(always)] fn param_names() -> Box<[&'static str]> { vec![$(type_name::<$par>()),*].into_boxed_slice() } #[cfg(feature = "metadata")] #[inline(always)] fn param_names() -> Box<[&'static str]> { vec![$(crate::stdlib::any::type_name::<$par>()),*].into_boxed_slice() }
#[inline(always)] fn return_type() -> TypeId { TypeId::of::<Result<RET, Box<EvalAltResult>>>() } #[cfg(feature = "metadata")] #[inline(always)] fn return_type() -> TypeId { TypeId::of::<Result<RET, Box<EvalAltResult>>>() }
#[inline(always)] fn return_type_name() -> &'static str { type_name::<Result<RET, Box<EvalAltResult>>>() } #[cfg(feature = "metadata")] #[inline(always)] fn return_type_name() -> &'static str { crate::stdlib::any::type_name::<Result<RET, Box<EvalAltResult>>>() }
#[inline(always)] fn into_callable_function(self) -> CallableFunction { #[inline(always)] fn into_callable_function(self) -> CallableFunction {
CallableFunction::$abi(Box::new(move |ctx: NativeCallContext, args: &mut FnCallArgs| { CallableFunction::$abi(Box::new(move |ctx: NativeCallContext, args: &mut FnCallArgs| {
// The arguments are assumed to be of the correct number and types! // The arguments are assumed to be of the correct number and types!

View File

@ -12,10 +12,11 @@ use crate::stdlib::{
iter::empty, iter::empty,
num::NonZeroUsize, num::NonZeroUsize,
ops::{Add, AddAssign, Deref, DerefMut}, ops::{Add, AddAssign, Deref, DerefMut},
string::{String, ToString}, string::String,
vec::Vec, vec::Vec,
}; };
use crate::token::Token; use crate::token::Token;
use crate::utils::StringInterner;
use crate::{ use crate::{
calc_fn_hash, calc_fn_params_hash, combine_hashes, Dynamic, EvalAltResult, ImmutableString, calc_fn_hash, calc_fn_params_hash, combine_hashes, Dynamic, EvalAltResult, ImmutableString,
NativeCallContext, Position, Shared, StaticVec, NativeCallContext, Position, Shared, StaticVec,
@ -54,27 +55,27 @@ pub struct FuncInfo {
/// Function access mode. /// Function access mode.
pub access: FnAccess, pub access: FnAccess,
/// Function name. /// Function name.
pub name: String, pub name: ImmutableString,
/// Number of parameters. /// Number of parameters.
pub params: usize, pub params: usize,
/// Parameter types (if applicable). /// Parameter types (if applicable).
pub param_types: StaticVec<TypeId>, pub param_types: StaticVec<TypeId>,
/// Parameter names (if available). /// Parameter names (if available).
#[cfg(feature = "metadata")]
pub param_names: StaticVec<ImmutableString>, pub param_names: StaticVec<ImmutableString>,
} }
impl FuncInfo { impl FuncInfo {
/// Generate a signature of the function. /// Generate a signature of the function.
/// Available under the `metadata` feature only.
#[cfg(feature = "metadata")]
pub fn gen_signature(&self) -> String { pub fn gen_signature(&self) -> String {
let mut sig = format!("{}(", self.name); let mut sig = format!("{}(", self.name);
if !self.param_names.is_empty() { if !self.param_names.is_empty() {
let mut params: Vec<_> = self let mut params: crate::stdlib::vec::Vec<String> =
.param_names self.param_names.iter().map(|s| s.as_str().into()).collect();
.iter() let return_type = params.pop().unwrap_or_else(|| "()".into());
.map(ImmutableString::to_string)
.collect();
let return_type = params.pop().unwrap_or_else(|| "()".to_string());
sig.push_str(&params.join(", ")); sig.push_str(&params.join(", "));
if return_type != "()" { if return_type != "()" {
sig.push_str(") -> "); sig.push_str(") -> ");
@ -114,7 +115,7 @@ impl FuncInfo {
#[inline(always)] #[inline(always)]
fn calc_native_fn_hash<'a>( fn calc_native_fn_hash<'a>(
modules: impl Iterator<Item = &'a str>, modules: impl Iterator<Item = &'a str>,
fn_name: &str, fn_name: impl AsRef<str>,
params: &[TypeId], params: &[TypeId],
) -> u64 { ) -> u64 {
let hash_script = calc_fn_hash(modules, fn_name, params.len()); let hash_script = calc_fn_hash(modules, fn_name, params.len());
@ -147,6 +148,8 @@ pub struct Module {
indexed: bool, indexed: bool,
/// Does the [`Module`] contain indexed functions that have been exposed to the global namespace? /// Does the [`Module`] contain indexed functions that have been exposed to the global namespace?
contains_indexed_global_functions: bool, contains_indexed_global_functions: bool,
/// Interned strings
interned_strings: StringInterner,
} }
impl Default for Module { impl Default for Module {
@ -163,6 +166,7 @@ impl Default for Module {
all_type_iterators: Default::default(), all_type_iterators: Default::default(),
indexed: false, indexed: false,
contains_indexed_global_functions: false, contains_indexed_global_functions: false,
interned_strings: Default::default(),
} }
} }
} }
@ -172,11 +176,10 @@ impl fmt::Debug for Module {
write!( write!(
f, f,
"Module({}\n{}{}{})", "Module({}\n{}{}{})",
if let Some(ref id) = self.id { self.id
format!("id: {:?},", id) .as_ref()
} else { .map(|id| format!("id: {:?},", id))
"".to_string() .unwrap_or_default(),
},
if !self.modules.is_empty() { if !self.modules.is_empty() {
format!( format!(
" modules: {}\n", " modules: {}\n",
@ -187,7 +190,7 @@ impl fmt::Debug for Module {
.join(", ") .join(", ")
) )
} else { } else {
"".to_string() Default::default()
}, },
if !self.variables.is_empty() { if !self.variables.is_empty() {
format!( format!(
@ -199,19 +202,19 @@ impl fmt::Debug for Module {
.join(", ") .join(", ")
) )
} else { } else {
"".to_string() Default::default()
}, },
if !self.functions.is_empty() { if !self.functions.is_empty() {
format!( format!(
" functions: {}\n", " functions: {}\n",
self.functions self.functions
.values() .values()
.map(|f| f.func.to_string()) .map(|f| crate::stdlib::string::ToString::to_string(&f.func))
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join(", ") .join(", ")
) )
} else { } else {
"".to_string() Default::default()
} }
) )
} }
@ -360,6 +363,8 @@ impl Module {
} }
/// Generate signatures for all the non-private functions in the [`Module`]. /// Generate signatures for all the non-private functions in the [`Module`].
/// Available under the `metadata` feature only.
#[cfg(feature = "metadata")]
#[inline(always)] #[inline(always)]
pub fn gen_fn_signatures(&self) -> impl Iterator<Item = String> + '_ { pub fn gen_fn_signatures(&self) -> impl Iterator<Item = String> + '_ {
self.functions self.functions
@ -466,16 +471,17 @@ impl Module {
// None + function name + number of arguments. // None + function name + number of arguments.
let num_params = fn_def.params.len(); let num_params = fn_def.params.len();
let hash_script = crate::calc_fn_hash(empty(), &fn_def.name, num_params); let hash_script = crate::calc_fn_hash(empty(), &fn_def.name, num_params);
let mut param_names: StaticVec<_> = fn_def.params.iter().cloned().collect(); let mut param_names = fn_def.params.clone();
param_names.push("Dynamic".into()); param_names.push("Dynamic".into());
self.functions.insert( self.functions.insert(
hash_script, hash_script,
Box::new(FuncInfo { Box::new(FuncInfo {
name: fn_def.name.to_string(), name: fn_def.name.clone(),
namespace: FnNamespace::Internal, namespace: FnNamespace::Internal,
access: fn_def.access, access: fn_def.access,
params: num_params, params: num_params,
param_types: Default::default(), param_types: Default::default(),
#[cfg(feature = "metadata")]
param_names, param_names,
func: fn_def.into(), func: fn_def.into(),
}), }),
@ -599,6 +605,7 @@ impl Module {
} }
/// Update the metadata (parameter names/types and return type) of a registered function. /// Update the metadata (parameter names/types and return type) of a registered function.
/// Available under the `metadata` feature only.
/// ///
/// The [`u64`] hash is returned by the [`set_native_fn`][Module::set_native_fn] call. /// The [`u64`] hash is returned by the [`set_native_fn`][Module::set_native_fn] call.
/// ///
@ -610,10 +617,16 @@ impl Module {
/// ///
/// The _last entry_ in the list should be the _return type_ of the function. /// The _last entry_ in the list should be the _return type_ of the function.
/// In other words, the number of entries should be one larger than the number of parameters. /// In other words, the number of entries should be one larger than the number of parameters.
#[cfg(feature = "metadata")]
#[inline(always)] #[inline(always)]
pub fn update_fn_metadata(&mut self, hash_fn: u64, arg_names: &[&str]) -> &mut Self { pub fn update_fn_metadata(&mut self, hash_fn: u64, arg_names: &[&str]) -> &mut Self {
let param_names = arg_names
.iter()
.map(|&name| self.interned_strings.get(name))
.collect();
if let Some(f) = self.functions.get_mut(&hash_fn) { if let Some(f) = self.functions.get_mut(&hash_fn) {
f.param_names = arg_names.iter().map(|&n| n.into()).collect(); f.param_names = param_names;
} }
self self
} }
@ -625,9 +638,9 @@ impl Module {
pub fn update_fn_namespace(&mut self, hash_fn: u64, namespace: FnNamespace) -> &mut Self { pub fn update_fn_namespace(&mut self, hash_fn: u64, namespace: FnNamespace) -> &mut Self {
if let Some(f) = self.functions.get_mut(&hash_fn) { if let Some(f) = self.functions.get_mut(&hash_fn) {
f.namespace = namespace; f.namespace = namespace;
}
self.indexed = false; self.indexed = false;
self.contains_indexed_global_functions = false; self.contains_indexed_global_functions = false;
}
self self
} }
@ -641,14 +654,13 @@ impl Module {
#[inline] #[inline]
pub fn set_fn( pub fn set_fn(
&mut self, &mut self,
name: impl Into<String>, name: impl AsRef<str> + Into<ImmutableString>,
namespace: FnNamespace, namespace: FnNamespace,
access: FnAccess, access: FnAccess,
arg_names: Option<&[&str]>, _arg_names: Option<&[&str]>,
arg_types: &[TypeId], arg_types: &[TypeId],
func: CallableFunction, func: CallableFunction,
) -> u64 { ) -> u64 {
let name = name.into();
let is_method = func.is_method(); let is_method = func.is_method();
let param_types = arg_types let param_types = arg_types
@ -675,6 +687,15 @@ impl Module {
let hash_fn = calc_native_fn_hash(empty(), &name, &param_types); let hash_fn = calc_native_fn_hash(empty(), &name, &param_types);
let name = self.interned_strings.get(name);
#[cfg(feature = "metadata")]
let param_names = _arg_names
.iter()
.flat_map(|p| p.iter())
.map(|&arg| self.interned_strings.get(arg))
.collect();
self.functions.insert( self.functions.insert(
hash_fn, hash_fn,
Box::new(FuncInfo { Box::new(FuncInfo {
@ -683,11 +704,8 @@ impl Module {
access, access,
params: param_types.len(), params: param_types.len(),
param_types, param_types,
param_names: if let Some(p) = arg_names { #[cfg(feature = "metadata")]
p.iter().map(|&v| v.into()).collect() param_names,
} else {
Default::default()
},
func: func.into(), func: func.into(),
}), }),
); );
@ -765,16 +783,21 @@ impl Module {
/// assert!(module.contains_fn(hash)); /// assert!(module.contains_fn(hash));
/// ``` /// ```
#[inline(always)] #[inline(always)]
pub fn set_raw_fn<T: Variant + Clone>( pub fn set_raw_fn<N, T, F>(
&mut self, &mut self,
name: impl Into<String>, name: N,
namespace: FnNamespace, namespace: FnNamespace,
access: FnAccess, access: FnAccess,
arg_types: &[TypeId], arg_types: &[TypeId],
func: impl Fn(NativeCallContext, &mut FnCallArgs) -> Result<T, Box<EvalAltResult>> func: F,
) -> u64
where
N: AsRef<str> + Into<ImmutableString>,
T: Variant + Clone,
F: Fn(NativeCallContext, &mut FnCallArgs) -> Result<T, Box<EvalAltResult>>
+ SendSync + SendSync
+ 'static, + 'static,
) -> u64 { {
let f = let f =
move |ctx: NativeCallContext, args: &mut FnCallArgs| func(ctx, args).map(Dynamic::from); move |ctx: NativeCallContext, args: &mut FnCallArgs| func(ctx, args).map(Dynamic::from);
@ -812,8 +835,9 @@ impl Module {
/// assert!(module.contains_fn(hash)); /// assert!(module.contains_fn(hash));
/// ``` /// ```
#[inline(always)] #[inline(always)]
pub fn set_native_fn<ARGS, T, F>(&mut self, name: impl Into<String>, func: F) -> u64 pub fn set_native_fn<ARGS, N, T, F>(&mut self, name: N, func: F) -> u64
where where
N: AsRef<str> + Into<ImmutableString>,
T: Variant + Clone, T: Variant + Clone,
F: RegisterNativeFunction<ARGS, Result<T, Box<EvalAltResult>>>, F: RegisterNativeFunction<ARGS, Result<T, Box<EvalAltResult>>>,
{ {
@ -847,7 +871,7 @@ impl Module {
/// ``` /// ```
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
#[inline(always)] #[inline(always)]
pub fn set_getter_fn<ARGS, A, T, F>(&mut self, name: impl Into<String>, func: F) -> u64 pub fn set_getter_fn<ARGS, A, T, F>(&mut self, name: &str, func: F) -> u64
where where
A: Variant + Clone, A: Variant + Clone,
T: Variant + Clone, T: Variant + Clone,
@ -855,7 +879,7 @@ impl Module {
F: Fn(&mut A) -> Result<T, Box<EvalAltResult>> + SendSync + 'static, F: Fn(&mut A) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
{ {
self.set_fn( self.set_fn(
crate::engine::make_getter(&name.into()), &crate::engine::make_getter(name),
FnNamespace::Global, FnNamespace::Global,
FnAccess::Public, FnAccess::Public,
None, None,
@ -888,7 +912,7 @@ impl Module {
/// ``` /// ```
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
#[inline(always)] #[inline(always)]
pub fn set_setter_fn<ARGS, A, B, F>(&mut self, name: impl Into<String>, func: F) -> u64 pub fn set_setter_fn<ARGS, A, B, F>(&mut self, name: &str, func: F) -> u64
where where
A: Variant + Clone, A: Variant + Clone,
B: Variant + Clone, B: Variant + Clone,
@ -896,7 +920,7 @@ impl Module {
F: Fn(&mut A, B) -> Result<(), Box<EvalAltResult>> + SendSync + 'static, F: Fn(&mut A, B) -> Result<(), Box<EvalAltResult>> + SendSync + 'static,
{ {
self.set_fn( self.set_fn(
crate::engine::make_setter(&name.into()), &crate::engine::make_setter(name),
FnNamespace::Global, FnNamespace::Global,
FnAccess::Public, FnAccess::Public,
None, None,
@ -1061,11 +1085,16 @@ impl Module {
/// ``` /// ```
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
#[inline(always)] #[inline(always)]
pub fn set_indexer_get_set_fn<A: Variant + Clone, B: Variant + Clone, T: Variant + Clone>( pub fn set_indexer_get_set_fn<A, B, T>(
&mut self, &mut self,
get_fn: impl Fn(&mut A, B) -> Result<T, Box<EvalAltResult>> + SendSync + 'static, get_fn: impl Fn(&mut A, B) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
set_fn: impl Fn(&mut A, B, T) -> Result<(), Box<EvalAltResult>> + SendSync + 'static, set_fn: impl Fn(&mut A, B, T) -> Result<(), Box<EvalAltResult>> + SendSync + 'static,
) -> (u64, u64) { ) -> (u64, u64)
where
A: Variant + Clone,
B: Variant + Clone,
T: Variant + Clone,
{
( (
self.set_indexer_get_fn(get_fn), self.set_indexer_get_fn(get_fn),
self.set_indexer_set_fn(set_fn), self.set_indexer_set_fn(set_fn),

View File

@ -615,12 +615,12 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) {
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Expr::Dot(x, _) => match (&mut x.lhs, &mut x.rhs) { Expr::Dot(x, _) => match (&mut x.lhs, &mut x.rhs) {
// map.string // map.string
(Expr::Map(m, pos), Expr::Property(p)) if m.iter().all(|(_, x)| x.is_pure()) => { (Expr::Map(m, pos), Expr::Property(p)) if m.0.iter().all(|(_, x)| x.is_pure()) => {
let prop = &p.2.name; let prop = &p.2.name;
// Map literal where everything is pure - promote the indexed item. // Map literal where everything is pure - promote the indexed item.
// All other items can be thrown away. // All other items can be thrown away.
state.set_dirty(); state.set_dirty();
*expr = mem::take(m).into_iter().find(|(x, _)| &x.name == prop) *expr = mem::take(&mut m.0).into_iter().find(|(x, _)| &x.name == prop)
.map(|(_, mut expr)| { expr.set_position(*pos); expr }) .map(|(_, mut expr)| { expr.set_position(*pos); expr })
.unwrap_or_else(|| Expr::Unit(*pos)); .unwrap_or_else(|| Expr::Unit(*pos));
} }
@ -645,11 +645,11 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) {
*expr = result; *expr = result;
} }
// map[string] // map[string]
(Expr::Map(m, pos), Expr::StringConstant(s, _)) if m.iter().all(|(_, x)| x.is_pure()) => { (Expr::Map(m, pos), Expr::StringConstant(s, _)) if m.0.iter().all(|(_, x)| x.is_pure()) => {
// Map literal where everything is pure - promote the indexed item. // Map literal where everything is pure - promote the indexed item.
// All other items can be thrown away. // All other items can be thrown away.
state.set_dirty(); state.set_dirty();
*expr = mem::take(m).into_iter().find(|(x, _)| x.name == *s) *expr = mem::take(&mut m.0).into_iter().find(|(x, _)| x.name == *s)
.map(|(_, mut expr)| { expr.set_position(*pos); expr }) .map(|(_, mut expr)| { expr.set_position(*pos); expr })
.unwrap_or_else(|| Expr::Unit(*pos)); .unwrap_or_else(|| Expr::Unit(*pos));
} }
@ -681,7 +681,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) {
} }
// #{ key:value, .. } // #{ key:value, .. }
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Expr::Map(x, _) => x.iter_mut().for_each(|(_, expr)| optimize_expr(expr, state)), Expr::Map(x, _) => x.0.iter_mut().for_each(|(_, expr)| optimize_expr(expr, state)),
// lhs && rhs // lhs && rhs
Expr::And(x, _) => match (&mut x.lhs, &mut x.rhs) { Expr::And(x, _) => match (&mut x.lhs, &mut x.rhs) {
// true && rhs -> rhs // true && rhs -> rhs

View File

@ -142,8 +142,10 @@ macro_rules! reg_range {
($lib:ident | $x:expr => $( $y:ty ),*) => { ($lib:ident | $x:expr => $( $y:ty ),*) => {
$( $(
$lib.set_iterator::<Range<$y>>(); $lib.set_iterator::<Range<$y>>();
let hash = $lib.set_native_fn($x, get_range::<$y>); let _hash = $lib.set_native_fn($x, get_range::<$y>);
$lib.update_fn_metadata(hash, &[
#[cfg(feature = "metadata")]
$lib.update_fn_metadata(_hash, &[
concat!("from: ", stringify!($y)), concat!("from: ", stringify!($y)),
concat!("to: ", stringify!($y)), concat!("to: ", stringify!($y)),
concat!("Iterator<Item=", stringify!($y), ">") concat!("Iterator<Item=", stringify!($y), ">")
@ -153,8 +155,10 @@ macro_rules! reg_range {
($lib:ident | step $x:expr => $( $y:ty ),*) => { ($lib:ident | step $x:expr => $( $y:ty ),*) => {
$( $(
$lib.set_iterator::<StepRange<$y>>(); $lib.set_iterator::<StepRange<$y>>();
let hash = $lib.set_native_fn($x, get_step_range::<$y>); let _hash = $lib.set_native_fn($x, get_step_range::<$y>);
$lib.update_fn_metadata(hash, &[
#[cfg(feature = "metadata")]
$lib.update_fn_metadata(_hash, &[
concat!("from: ", stringify!($y)), concat!("from: ", stringify!($y)),
concat!("to: ", stringify!($y)), concat!("to: ", stringify!($y)),
concat!("step: ", stringify!($y)), concat!("step: ", stringify!($y)),
@ -251,10 +255,12 @@ def_package!(crate:BasicIteratorPackage:"Basic range iterators.", lib, {
lib.set_iterator::<StepDecimalRange>(); lib.set_iterator::<StepDecimalRange>();
let hash = lib.set_native_fn("range", |from, to| StepDecimalRange::new(from, to, Decimal::one())); let _hash = lib.set_native_fn("range", |from, to| StepDecimalRange::new(from, to, Decimal::one()));
lib.update_fn_metadata(hash, &["from: Decimal", "to: Decimal", "Iterator<Item=Decimal>"]); #[cfg(feature = "metadata")]
lib.update_fn_metadata(_hash, &["from: Decimal", "to: Decimal", "Iterator<Item=Decimal>"]);
let hash = lib.set_native_fn("range", |from, to, step| StepDecimalRange::new(from, to, step)); let _hash = lib.set_native_fn("range", |from, to, step| StepDecimalRange::new(from, to, step));
lib.update_fn_metadata(hash, &["from: Decimal", "to: Decimal", "step: Decimal", "Iterator<Item=Decimal>"]); #[cfg(feature = "metadata")]
lib.update_fn_metadata(_hash, &["from: Decimal", "to: Decimal", "step: Decimal", "Iterator<Item=Decimal>"]);
} }
}); });

View File

@ -10,7 +10,6 @@ use crate::module::NamespaceRef;
use crate::optimize::optimize_into_ast; use crate::optimize::optimize_into_ast;
use crate::optimize::OptimizationLevel; use crate::optimize::OptimizationLevel;
use crate::stdlib::{ use crate::stdlib::{
borrow::Cow,
boxed::Box, boxed::Box,
collections::BTreeMap, collections::BTreeMap,
format, format,
@ -23,7 +22,7 @@ use crate::stdlib::{
}; };
use crate::syntax::{CustomSyntax, MARKER_BLOCK, MARKER_EXPR, MARKER_IDENT}; use crate::syntax::{CustomSyntax, MARKER_BLOCK, MARKER_EXPR, MARKER_IDENT};
use crate::token::{is_keyword_function, is_valid_identifier, Token, TokenStream}; use crate::token::{is_keyword_function, is_valid_identifier, Token, TokenStream};
use crate::utils::get_hasher; use crate::utils::{get_hasher, StringInterner};
use crate::{ use crate::{
calc_fn_hash, Dynamic, Engine, ImmutableString, LexError, ParseError, ParseErrorType, Position, calc_fn_hash, Dynamic, Engine, ImmutableString, LexError, ParseError, ParseErrorType, Position,
Scope, Shared, StaticVec, AST, Scope, Shared, StaticVec, AST,
@ -45,7 +44,7 @@ struct ParseState<'e> {
/// Reference to the scripting [`Engine`]. /// Reference to the scripting [`Engine`].
engine: &'e Engine, engine: &'e Engine,
/// Interned strings. /// Interned strings.
interned_strings: BTreeMap<String, ImmutableString>, interned_strings: StringInterner,
/// Encapsulates a local stack with variable names to simulate an actual runtime scope. /// Encapsulates a local stack with variable names to simulate an actual runtime scope.
stack: Vec<(ImmutableString, AccessMode)>, stack: Vec<(ImmutableString, AccessMode)>,
/// Size of the local variables stack upon entry of the current block scope. /// Size of the local variables stack upon entry of the current block scope.
@ -161,24 +160,17 @@ impl<'e> ParseState<'e> {
.iter() .iter()
.rev() .rev()
.enumerate() .enumerate()
.find(|(_, n)| **n == name) .find(|&(_, n)| *n == name)
.and_then(|(i, _)| NonZeroUsize::new(i + 1)) .and_then(|(i, _)| NonZeroUsize::new(i + 1))
} }
/// Get an interned string, creating one if it is not yet interned. /// Get an interned string, creating one if it is not yet interned.
#[inline(always)]
pub fn get_interned_string( pub fn get_interned_string(
&mut self, &mut self,
text: impl AsRef<str> + Into<ImmutableString>, text: impl AsRef<str> + Into<ImmutableString>,
) -> ImmutableString { ) -> ImmutableString {
#[allow(clippy::map_entry)] self.interned_strings.get(text)
if !self.interned_strings.contains_key(text.as_ref()) {
let value = text.into();
self.interned_strings
.insert(value.clone().into(), value.clone());
value
} else {
self.interned_strings.get(text.as_ref()).unwrap().clone()
}
} }
} }
@ -690,6 +682,7 @@ fn parse_map_literal(
settings.pos = eat_token(input, Token::MapStart); settings.pos = eat_token(input, Token::MapStart);
let mut map: StaticVec<(Ident, Expr)> = Default::default(); let mut map: StaticVec<(Ident, Expr)> = Default::default();
let mut template: BTreeMap<ImmutableString, Dynamic> = Default::default();
loop { loop {
const MISSING_RBRACE: &str = "to end this object map literal"; const MISSING_RBRACE: &str = "to end this object map literal";
@ -760,6 +753,7 @@ fn parse_map_literal(
let expr = parse_expr(input, state, lib, settings.level_up())?; let expr = parse_expr(input, state, lib, settings.level_up())?;
let name = state.get_interned_string(name); let name = state.get_interned_string(name);
template.insert(name.clone(), Default::default());
map.push((Ident { name, pos }, expr)); map.push((Ident { name, pos }, expr));
match input.peek().unwrap() { match input.peek().unwrap() {
@ -784,7 +778,7 @@ fn parse_map_literal(
} }
} }
Ok(Expr::Map(Box::new(map), settings.pos)) Ok(Expr::Map(Box::new((map, template)), settings.pos))
} }
/// Parse a switch expression. /// Parse a switch expression.
@ -1353,7 +1347,7 @@ fn parse_unary(
/// Make an assignment statement. /// Make an assignment statement.
fn make_assignment_stmt<'a>( fn make_assignment_stmt<'a>(
op: Cow<'static, str>, op: &'static str,
state: &mut ParseState, state: &mut ParseState,
lhs: Expr, lhs: Expr,
rhs: Expr, rhs: Expr,
@ -1477,7 +1471,7 @@ fn parse_op_assignment_stmt(
| Token::PowerOfAssign | Token::PowerOfAssign
| Token::AndAssign | Token::AndAssign
| Token::OrAssign | Token::OrAssign
| Token::XOrAssign => token.syntax(), | Token::XOrAssign => token.keyword_syntax(),
_ => return Ok(Stmt::Expr(lhs)), _ => return Ok(Stmt::Expr(lhs)),
}; };

View File

@ -1,7 +1,7 @@
//! Module defining macros for developing _plugins_. //! Module defining macros for developing _plugins_.
pub use crate::fn_native::{CallableFunction, FnCallArgs}; pub use crate::fn_native::{CallableFunction, FnCallArgs};
pub use crate::stdlib::{any::TypeId, boxed::Box, format, mem, string::ToString, vec as new_vec}; pub use crate::stdlib::{any::TypeId, boxed::Box, format, mem, string::ToString};
pub use crate::{ pub use crate::{
Dynamic, Engine, EvalAltResult, FnAccess, FnNamespace, ImmutableString, Module, Dynamic, Engine, EvalAltResult, FnAccess, FnNamespace, ImmutableString, Module,
NativeCallContext, Position, NativeCallContext, Position,
@ -26,13 +26,4 @@ pub trait PluginFunction {
/// Is this plugin function variadic? /// Is this plugin function variadic?
fn is_variadic(&self) -> bool; fn is_variadic(&self) -> bool;
/// 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 and return type.
fn param_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

@ -71,6 +71,7 @@ impl<'a> IntoIterator for Scope<'a> {
type Item = (Cow<'a, str>, Dynamic); type Item = (Cow<'a, str>, Dynamic);
type IntoIter = Box<dyn Iterator<Item = Self::Item> + 'a>; type IntoIter = Box<dyn Iterator<Item = Self::Item> + 'a>;
#[inline(always)]
fn into_iter(self) -> Self::IntoIter { fn into_iter(self) -> Self::IntoIter {
Box::new( Box::new(
self.values self.values

View File

@ -211,7 +211,8 @@ impl From<&crate::Module> for ModuleMetadata {
#[cfg(feature = "metadata")] #[cfg(feature = "metadata")]
impl Engine { impl Engine {
/// _(METADATA)_ Generate a list of all functions (including those defined in an /// _(METADATA)_ Generate a list of all functions (including those defined in an
/// [`AST`][crate::AST]) in JSON format. Available only under the `metadata` feature. /// [`AST`][crate::AST]) in JSON format.
/// Available under the `metadata` feature only.
/// ///
/// Functions from the following sources are included: /// Functions from the following sources are included:
/// 1) Functions defined in an [`AST`][crate::AST] /// 1) Functions defined in an [`AST`][crate::AST]

View File

@ -8,6 +8,7 @@ mod serialize;
mod str; mod str;
#[cfg(feature = "metadata")] #[cfg(feature = "metadata")]
#[cfg(feature = "serde")]
mod metadata; mod metadata;
pub use de::from_dynamic; pub use de::from_dynamic;

View File

@ -381,25 +381,15 @@ pub enum Token {
} }
impl Token { impl Token {
/// Get the syntax of the token. /// Get the syntax of the token if it is a keyword.
pub fn syntax(&self) -> Cow<'static, str> { ///
/// # Panics
///
/// Panics if the token is not a keyword.
pub fn keyword_syntax(&self) -> &'static str {
use Token::*; use Token::*;
match self { match self {
IntegerConstant(i) => i.to_string().into(),
#[cfg(not(feature = "no_float"))]
FloatConstant(f) => f.to_string().into(),
#[cfg(feature = "decimal")]
DecimalConstant(d) => d.to_string().into(),
StringConstant(_) => "string".into(),
CharConstant(c) => c.to_string().into(),
Identifier(s) => s.clone().into(),
Reserved(s) => s.clone().into(),
Custom(s) => s.clone().into(),
LexError(err) => err.to_string().into(),
Comment(s) => s.clone().into(),
token => match token {
LeftBrace => "{", LeftBrace => "{",
RightBrace => "}", RightBrace => "}",
LeftParen => "(", LeftParen => "(",
@ -479,10 +469,32 @@ impl Token {
Export => "export", Export => "export",
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
As => "as", As => "as",
EOF => "{EOF}",
t => unreachable!("operator should be matched in outer scope: {:?}", t), t => unreachable!("{:?} is not a keyword", t),
} }
.into(), }
/// Get the syntax of the token.
pub fn syntax(&self) -> Cow<'static, str> {
use Token::*;
match self {
IntegerConstant(i) => i.to_string().into(),
#[cfg(not(feature = "no_float"))]
FloatConstant(f) => f.to_string().into(),
#[cfg(feature = "decimal")]
DecimalConstant(d) => d.to_string().into(),
StringConstant(_) => "string".into(),
CharConstant(c) => c.to_string().into(),
Identifier(s) => s.clone().into(),
Reserved(s) => s.clone().into(),
Custom(s) => s.clone().into(),
LexError(err) => err.to_string().into(),
Comment(s) => s.clone().into(),
EOF => "{EOF}".into(),
token => token.keyword_syntax().into(),
} }
} }

View File

@ -6,6 +6,7 @@ use crate::stdlib::{
borrow::Borrow, borrow::Borrow,
boxed::Box, boxed::Box,
cmp::Ordering, cmp::Ordering,
collections::BTreeSet,
fmt, fmt,
fmt::{Debug, Display}, fmt::{Debug, Display},
hash::{BuildHasher, Hash, Hasher}, hash::{BuildHasher, Hash, Hasher},
@ -70,7 +71,11 @@ pub fn get_hasher() -> ahash::AHasher {
/// ///
/// The first module name is skipped. Hashing starts from the _second_ module in the chain. /// The first module name is skipped. Hashing starts from the _second_ module in the chain.
#[inline(always)] #[inline(always)]
pub fn calc_fn_hash<'a>(modules: impl Iterator<Item = &'a str>, fn_name: &str, num: usize) -> u64 { pub fn calc_fn_hash<'a>(
modules: impl Iterator<Item = &'a str>,
fn_name: impl AsRef<str>,
num: usize,
) -> u64 {
let s = &mut get_hasher(); let s = &mut get_hasher();
// We always skip the first module // We always skip the first module
@ -80,7 +85,7 @@ pub fn calc_fn_hash<'a>(modules: impl Iterator<Item = &'a str>, fn_name: &str, n
.skip(1) .skip(1)
.for_each(|m| m.hash(s)); .for_each(|m| m.hash(s));
len.hash(s); len.hash(s);
fn_name.hash(s); fn_name.as_ref().hash(s);
num.hash(s); num.hash(s);
s.finish() s.finish()
} }
@ -137,7 +142,7 @@ pub(crate) fn combine_hashes(a: u64, b: u64) -> u64 {
/// assert_ne!(s2.as_str(), s.as_str()); /// assert_ne!(s2.as_str(), s.as_str());
/// assert_eq!(s, "hello, world!"); /// assert_eq!(s, "hello, world!");
/// ``` /// ```
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Default)] #[derive(Clone, Eq, Ord, Hash, Default)]
pub struct ImmutableString(Shared<String>); pub struct ImmutableString(Shared<String>);
impl Deref for ImmutableString { impl Deref for ImmutableString {
@ -156,6 +161,13 @@ impl AsRef<String> for ImmutableString {
} }
} }
impl AsRef<str> for ImmutableString {
#[inline(always)]
fn as_ref(&self) -> &str {
&self.0
}
}
impl Borrow<String> for ImmutableString { impl Borrow<String> for ImmutableString {
#[inline(always)] #[inline(always)]
fn borrow(&self) -> &String { fn borrow(&self) -> &String {
@ -559,7 +571,6 @@ impl PartialEq<ImmutableString> for String {
} }
impl<S: AsRef<str>> PartialOrd<S> for ImmutableString { impl<S: AsRef<str>> PartialOrd<S> for ImmutableString {
#[inline(always)]
fn partial_cmp(&self, other: &S) -> Option<Ordering> { fn partial_cmp(&self, other: &S) -> Option<Ordering> {
self.as_str().partial_cmp(other.as_ref()) self.as_str().partial_cmp(other.as_ref())
} }
@ -594,3 +605,19 @@ impl ImmutableString {
shared_make_mut(&mut self.0) shared_make_mut(&mut self.0)
} }
} }
/// A collection of interned strings.
#[derive(Debug, Clone, Default, Hash)]
pub struct StringInterner(BTreeSet<ImmutableString>);
impl StringInterner {
/// Get an interned string, creating one if it is not yet interned.
#[inline(always)]
pub fn get(&mut self, text: impl AsRef<str> + Into<ImmutableString>) -> ImmutableString {
self.0.get(text.as_ref()).cloned().unwrap_or_else(|| {
let s = text.into();
self.0.insert(s.clone());
s
})
}
}

View File

@ -34,7 +34,7 @@ macro_rules! register_in_bulk {
{ {
let type_str = stringify!($type_names); let type_str = stringify!($type_names);
set_exported_fn!($mod_name, set_exported_fn!($mod_name,
format!(concat!(stringify!($op_name), "_{}"), type_str), &format!(concat!(stringify!($op_name), "_{}"), type_str),
crate::$op_name::$type_names::op); crate::$op_name::$type_names::op);
} }
)* )*