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]
flags:
- ""
- "--features metadata,internals"
- "--features metadata,serde,internals"
- "--features unchecked"
- "--features sync"
- "--features no_optimize"
@ -34,7 +34,7 @@ jobs:
- "--features no_module"
- "--features no_closure"
- "--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]
experimental: [false]
include:

View File

@ -4,31 +4,37 @@ Rhai Release Notes
Version 0.19.15
===============
This version replaces internal usage of `HashMap` with `BTreeMap` in many cases, which should result
in speed improvements because a `BTreeMap` is faster when the number of items held is small.
This version replaces all internal usage of `HashMap` with `BTreeMap`, which should result
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
is now aliased to `BTreeMap` instead. This change is due to the fact that, in the vast majority of
object map usages, the number of properties held is small.
The Rhai object map type, `Map`, used to be an alias to `HashMap` and is now aliased to `BTreeMap`
instead. This is also because, in the vast majority of usage cases, the number of properties held by
an object map is small.
`HashMap` and `BTreeMap` have almost identical public API's so this change is unlikely to break a
lot of existing code.
`HashMap` and `BTreeMap` have almost identical public API's so this change is unlikely to break
existing code.
All function signature/metadata methods are now grouped under the umbrella `metadata` feature.
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`.
* `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`.
* `Array::reduce` and `Array::reduce_rev` now take a `Dynamic` as initial value instead of a function pointer.
* `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
------------
* 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.
* `#[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
internals = [] # expose internal data structures
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"]
@ -92,4 +92,4 @@ instant = { version = "0.1" } # WASM implementation of std::time::Instant
instant = { version = "0.1" } # WASM implementation of std::time::Instant
[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;
// Determine if the function requires a call context
if let Some(first_arg) = fn_all.sig.inputs.first() {
if let syn::FnArg::Typed(syn::PatType { ref ty, .. }) = first_arg {
match fn_all.sig.inputs.first() {
Some(syn::FnArg::Typed(syn::PatType { ref ty, .. })) => {
match flatten_type_groups(ty.as_ref()) {
syn::Type::Path(p)
if p.path == context_type_path1 || p.path == context_type_path2 =>
{
pass_context = true;
}
_ => (),
_ => {}
}
}
_ => {}
}
let skip_slots = if pass_context { 1 } else { 0 };
// Determine whether function generates a special calling convention for a mutable receiver.
let mut_receiver = {
if let Some(first_arg) = fn_all.sig.inputs.iter().skip(skip_slots).next() {
match first_arg {
syn::FnArg::Receiver(syn::Receiver {
let mut_receiver = match fn_all.sig.inputs.iter().skip(skip_slots).next() {
Some(syn::FnArg::Receiver(syn::Receiver {
reference: Some(_), ..
}) => true,
syn::FnArg::Typed(syn::PatType { ref ty, .. }) => {
})) => true,
Some(syn::FnArg::Typed(syn::PatType { ref ty, .. })) => {
match flatten_type_groups(ty.as_ref()) {
syn::Type::Reference(syn::TypeReference {
mutability: Some(_),
@ -345,10 +344,6 @@ impl Parse for ExportedFn {
}
}
_ => false,
}
} else {
false
}
};
// All arguments after the first must be moved except for &str.
@ -381,7 +376,8 @@ impl Parse for ExportedFn {
}
// 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()) {
syn::Type::Ptr(_) => {
return Err(syn::Error::new(
@ -398,6 +394,8 @@ impl Parse for ExportedFn {
_ => {}
}
}
_ => {}
}
Ok(ExportedFn {
entire_span,
signature: fn_all.sig,
@ -494,10 +492,9 @@ impl ExportedFn {
}
pub fn return_type(&self) -> Option<&syn::Type> {
if let syn::ReturnType::Type(_, ref ret_type) = self.signature.output {
Some(flatten_type_groups(ret_type))
} else {
None
match self.signature.output {
syn::ReturnType::Type(_, ref ret_type) => Some(flatten_type_groups(ret_type)),
_ => None,
}
}
@ -590,20 +587,14 @@ impl ExportedFn {
let name: syn::Ident =
syn::Ident::new(&format!("rhai_fn_{}", self.name()), self.name().span());
let impl_block = self.generate_impl("Token");
let callable_block = self.generate_callable("Token");
let 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 vis = self.visibility;
quote! {
#[automatically_derived]
#vis mod #name {
use super::*;
struct Token();
pub struct Token();
#impl_block
#callable_block
#param_names_block
#input_types_block
#dyn_result_fn_block
}
}
@ -616,22 +607,18 @@ impl ExportedFn {
dynamic_signature.ident =
syn::Ident::new("dynamic_result_fn", proc_macro2::Span::call_site());
dynamic_signature.output = syn::parse2::<syn::ReturnType>(quote! {
-> Result<Dynamic, Box<EvalAltResult>>
-> RhaiResult
})
.unwrap();
let arguments: Vec<syn::Ident> = dynamic_signature
.inputs
.iter()
.filter_map(|fn_arg| {
if let syn::FnArg::Typed(syn::PatType { ref pat, .. }) = fn_arg {
if let syn::Pat::Ident(ref ident) = pat.as_ref() {
Some(ident.ident.clone())
} else {
None
}
} else {
None
}
.filter_map(|fn_arg| match fn_arg {
syn::FnArg::Typed(syn::PatType { ref pat, .. }) => match pat.as_ref() {
syn::Pat::Ident(ref ident) => Some(ident.ident.clone()),
_ => None,
},
_ => None,
})
.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 {
let sig_name = self.name().clone();
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());
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 {
#[inline(always)]
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)*
#return_expr
}
#[inline(always)] fn is_method_call(&self) -> bool { #is_method_call }
#[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]
pub fn exported_module(module_path: proc_macro::TokenStream) -> proc_macro::TokenStream {
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()
};
proc_macro::TokenStream::from(tokens)
})
}
/// 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]
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)
{
Ok(triple) => triple,
Err(e) => return e.to_compile_error().into(),
};
let tokens = quote! {
match crate::register::parse_register_macro(args) {
Ok((module_expr, _export_name, module_path)) => proc_macro::TokenStream::from(quote! {
#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`.
@ -293,16 +289,15 @@ pub fn combine_with_exported_module(args: proc_macro::TokenStream) -> proc_macro
/// ```
#[proc_macro]
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) {
Ok(triple) => triple,
Err(e) => return e.to_compile_error().into(),
};
Ok((engine_expr, export_name, rust_mod_path)) => {
let gen_mod_path = crate::register::generated_module_path(&rust_mod_path);
let tokens = quote! {
#engine_expr.register_result_fn(&(#export_name), #gen_mod_path::dynamic_result_fn);
};
proc_macro::TokenStream::from(tokens)
proc_macro::TokenStream::from(quote! {
#engine_expr.register_result_fn(#export_name, #gen_mod_path::dynamic_result_fn);
})
}
Err(e) => e.to_compile_error().into(),
}
}
/// 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]
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) {
Ok(triple) => triple,
Err(e) => return e.to_compile_error().into(),
};
Ok((module_expr, export_name, 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,
Some(#gen_mod_path::token_param_names().as_ref()),
#gen_mod_path::token_input_types().as_ref(),
#gen_mod_path::token_callable());
};
proc_macro::TokenStream::from(tokens)
Some(#gen_mod_path::Token::PARAM_NAMES),
&#gen_mod_path::Token::param_types(),
#gen_mod_path::Token().into());
})
}
Err(e) => e.to_compile_error().into(),
}
}
/// 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]
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) {
Ok(triple) => triple,
Err(e) => return e.to_compile_error().into(),
};
Ok((module_expr, export_name, 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,
Some(#gen_mod_path::token_param_names().as_ref()),
#gen_mod_path::token_input_types().as_ref(),
#gen_mod_path::token_callable());
};
proc_macro::TokenStream::from(tokens)
Some(#gen_mod_path::Token::PARAM_NAMES),
&#gen_mod_path::Token::param_types(),
#gen_mod_path::Token().into());
})
}
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
crate::attrs::deny_cfg_attr(&attrs)?;
if let syn::Visibility::Public(_) = vis {
consts.push((ident.to_string(), ty.clone(), expr.as_ref().clone()));
match vis {
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());
let mut i = 0;
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) {
syn::Item::Mod(m) => m,
_ => unreachable!(),
};
let params: ExportedModParams = match crate::attrs::inner_item_attributes(
&mut item_mod.attrs,
"rhai_mod",
) {
Ok(p) => p,
Err(e) => return Err(e),
};
let module =
syn::parse2::<Module>(item_mod.to_token_stream()).and_then(|mut m| {
let params: ExportedModParams =
crate::attrs::inner_item_attributes(&mut item_mod.attrs, "rhai_mod")?;
let module = syn::parse2::<Module>(item_mod.to_token_stream()).and_then(
|mut m| {
m.set_params(params)?;
Ok(m)
})?;
},
)?;
sub_modules.push(module);
} else {
i += 1;
}
_ => i += 1,
}
}
} else {

View File

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

View File

@ -1,6 +1,6 @@
use std::collections::BTreeMap;
use quote::{quote, ToTokens};
use quote::quote;
use crate::attrs::ExportScope;
use crate::function::{
@ -81,16 +81,6 @@ pub fn generate_body(
);
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
.arg_list()
.map(|fn_arg| match fn_arg {
@ -128,17 +118,12 @@ pub fn generate_body(
};
syn::parse2::<syn::Expr>(quote! {
core::any::TypeId::of::<#arg_type>()})
TypeId::of::<#arg_type>()})
.unwrap()
}
})
.collect();
let return_type = function
.return_type()
.map(print_type)
.unwrap_or_else(|| "()".to_string());
for fn_literal in reg_names {
let mut namespace = FnNamespaceAccess::Internal;
@ -172,7 +157,7 @@ pub fn generate_body(
set_fn_statements.push(
syn::parse2::<syn::Stmt>(quote! {
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());
})
.unwrap(),
@ -181,12 +166,9 @@ pub fn generate_body(
gen_fn_tokens.push(quote! {
#[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_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! {

View File

@ -275,35 +275,21 @@ mod generate_tests {
#[automatically_derived]
pub mod rhai_fn_do_nothing {
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 {
#[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()))
}
#[inline(always)] fn is_method_call(&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)]
#[inline(always)] pub fn dynamic_result_fn() -> Result<Dynamic, Box<EvalAltResult> > {
#[inline(always)] pub fn dynamic_result_fn() -> RhaiResult {
Ok(Dynamic::from(do_nothing()))
}
}
@ -323,37 +309,23 @@ mod generate_tests {
#[automatically_derived]
pub mod rhai_fn_do_something {
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 {
#[inline(always)]
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>();
Ok(Dynamic::from(do_something(arg0)))
}
#[inline(always)] fn is_method_call(&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)]
#[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)))
}
}
@ -373,37 +345,23 @@ mod generate_tests {
#[automatically_derived]
pub mod rhai_fn_do_something {
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 {
#[inline(always)]
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>();
Ok(Dynamic::from(do_something(context, arg0)))
}
#[inline(always)] fn is_method_call(&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)]
#[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)))
}
}
@ -426,36 +384,22 @@ mod generate_tests {
#[automatically_derived]
pub mod rhai_fn_return_dynamic {
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 {
#[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(return_dynamic()))
}
#[inline(always)] fn is_method_call(&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)]
#[inline(always)] pub fn dynamic_result_fn() -> Result<Dynamic, Box<EvalAltResult> > {
#[inline(always)] pub fn dynamic_result_fn() -> RhaiResult {
Ok(Dynamic::from(return_dynamic()))
}
}
@ -472,24 +416,19 @@ mod generate_tests {
};
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 {
#[inline(always)]
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>();
Ok(Dynamic::from(do_something(arg0)))
}
#[inline(always)] fn is_method_call(&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]
pub mod rhai_fn_add_together {
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 {
#[inline(always)]
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 arg1 = mem::take(args[1usize]).cast::<usize>();
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_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)]
#[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)))
}
}
@ -559,12 +483,14 @@ mod generate_tests {
#[automatically_derived]
pub mod rhai_fn_increment {
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 {
#[inline(always)]
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() {
return Err(Box::new(
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_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)]
#[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)))
}
}
@ -617,37 +526,23 @@ mod generate_tests {
#[automatically_derived]
pub mod rhai_fn_special_print {
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 {
#[inline(always)]
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();
Ok(Dynamic::from(special_print(&arg0)))
}
#[inline(always)] fn is_method_call(&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)]
#[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)))
}
}

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 crate::new_vec::fmt::Pointer;
11 | use crate::mem::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`.
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>> {

View File

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

View File

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

View File

@ -1349,7 +1349,7 @@ impl Dynamic {
}
if TypeId::of::<T>() == TypeId::of::<String>() {
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,
};
}

View File

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

View File

@ -9,12 +9,11 @@ use crate::stdlib::{
any::{type_name, TypeId},
boxed::Box,
format,
string::{String, ToString},
vec::Vec,
string::String,
};
use crate::{
scope::Scope, Dynamic, Engine, EvalAltResult, FnAccess, FnNamespace, Module, NativeCallContext,
ParseError, Position, RhaiResult, Shared, StaticVec, AST,
scope::Scope, Dynamic, Engine, EvalAltResult, FnAccess, FnNamespace, ImmutableString, Module,
NativeCallContext, ParseError, Position, RhaiResult, Shared, AST,
};
#[cfg(not(feature = "no_index"))]
@ -52,26 +51,36 @@ impl Engine {
/// # }
/// ```
#[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
N: AsRef<str> + Into<ImmutableString>,
F: RegisterNativeFunction<A, ()>,
{
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()
.map(|ty| format!("_: {}", self.map_type_name(ty)))
.collect();
#[cfg(feature = "metadata")]
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(
name,
FnNamespace::Global,
FnAccess::Public,
Some(&param_type_names),
param_type_names.as_ref().map(|v| v.as_ref()),
&param_types,
func.into_callable_function(),
);
@ -102,24 +111,34 @@ impl Engine {
/// .expect_err("expecting division by zero error!");
/// ```
#[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
N: AsRef<str> + Into<ImmutableString>,
F: RegisterNativeFunction<A, Result<R, Box<EvalAltResult>>>,
{
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()
.map(|ty| format!("_: {}", self.map_type_name(ty)))
.chain(crate::stdlib::iter::once(
self.map_type_name(F::return_type_name()).into(),
))
.collect();
param_type_names.push(self.map_type_name(F::return_type_name()).to_string());
let param_type_names: StaticVec<&str> =
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(
name,
FnNamespace::Global,
FnAccess::Public,
Some(&param_type_names),
param_type_names.as_ref().map(|v| v.as_ref()),
&param_types,
func.into_callable_function(),
);
@ -142,14 +161,18 @@ impl Engine {
/// To access the first mutable parameter, use `args.get_mut(0).unwrap()`
#[deprecated = "this function is volatile and may change"]
#[inline(always)]
pub fn register_raw_fn<T: Variant + Clone>(
pub fn register_raw_fn<N, T>(
&mut self,
name: &str,
name: N,
arg_types: &[TypeId],
func: impl Fn(NativeCallContext, &mut FnCallArgs) -> Result<T, Box<EvalAltResult>>
+ SendSync
+ 'static,
) -> &mut Self {
) -> &mut Self
where
N: AsRef<str> + Into<ImmutableString>,
T: Variant + Clone,
{
self.global_namespace.set_raw_fn(
name,
FnNamespace::Global,
@ -878,25 +901,29 @@ impl Engine {
/// # }
/// ```
#[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(
root: &mut crate::stdlib::collections::BTreeMap<crate::ImmutableString, Shared<Module>>,
name: &str,
name: impl AsRef<str> + Into<ImmutableString>,
module: Shared<Module>,
) {
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() {
// 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);
module.build_index();
root.insert(name.trim().into(), module.into());
root.insert(name.into(), module.into());
} else {
root.insert(name.trim().into(), module);
root.insert(name.into(), module);
}
} 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 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
}
@ -927,7 +954,11 @@ impl Engine {
#[cfg(not(feature = "no_module"))]
#[inline(always)]
#[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())
}
/// 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,
module::resolvers::StaticModuleResolver,
stdlib::collections::BTreeSet,
ImmutableString,
};
fn collect_imports(
@ -1232,7 +1262,7 @@ impl Engine {
Self::read_file(path).and_then(|contents| Ok(self.compile_with_scope(scope, &contents)?))
}
/// 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.
///
@ -1271,16 +1301,21 @@ impl Engine {
/// # }
/// ```
#[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;
let json = json.as_ref();
let mut scope = Default::default();
// Trims the JSON string and add a '#' in front
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, ""]
} else if json_text.starts_with(Token::LeftBrace.syntax().as_ref()) {
} else if json_text.starts_with(Token::LeftBrace.keyword_syntax()) {
["#", json_text]
} else {
return Err(crate::ParseErrorType::MissingToken(
@ -1765,7 +1800,7 @@ impl Engine {
&self,
scope: &mut Scope,
ast: &AST,
name: &str,
name: impl AsRef<str>,
args: impl crate::fn_args::FuncArgs,
) -> Result<T, Box<EvalAltResult>> {
let mut arg_values: crate::StaticVec<_> = Default::default();
@ -1844,7 +1879,7 @@ impl Engine {
scope: &mut Scope,
ast: &AST,
eval_ast: bool,
name: &str,
name: impl AsRef<str>,
mut this_ptr: Option<&mut Dynamic>,
mut arg_values: impl AsMut<[Dynamic]>,
) -> RhaiResult {
@ -1867,7 +1902,7 @@ impl Engine {
scope: &mut Scope,
ast: &AST,
eval_ast: bool,
name: &str,
name: impl AsRef<str>,
this_ptr: &mut Option<&mut Dynamic>,
args: &mut FnCallArgs,
) -> RhaiResult {
@ -1881,12 +1916,14 @@ impl Engine {
let fn_def = ast
.lib()
.get_script_fn(name, args.len())
.ok_or_else(|| EvalAltResult::ErrorFunctionNotFound(name.into(), Position::NONE))?;
.get_script_fn(name.as_ref(), args.len())
.ok_or_else(|| {
EvalAltResult::ErrorFunctionNotFound(name.as_ref().into(), Position::NONE)
})?;
// Check for data race.
#[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(
scope,
@ -1936,13 +1973,15 @@ impl Engine {
crate::optimize::optimize_into_ast(self, scope, stmt.into_vec(), lib, optimization_level)
}
/// Generate a list of all registered functions.
/// Available under the `metadata` feature only.
///
/// Functions from the following sources are included, in order:
/// 1) Functions registered into the global namespace
/// 2) Functions in registered sub-modules
/// 3) Functions in packages (optional)
pub fn gen_fn_signatures(&self, include_packages: bool) -> Vec<String> {
let mut signatures: Vec<_> = Default::default();
#[cfg(feature = "metadata")]
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());

View File

@ -236,7 +236,7 @@ impl Engine {
/// # }
/// ```
#[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
}
@ -270,7 +270,7 @@ impl Engine {
/// ```
pub fn register_custom_operator(
&mut self,
keyword: &str,
keyword: impl AsRef<str> + Into<String>,
precedence: u8,
) -> Result<&mut Self, String> {
let precedence = Precedence::new(precedence);
@ -279,25 +279,25 @@ impl Engine {
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
None | Some(Token::Reserved(_)) | Some(Token::Custom(_)) => (),
// Active standard keywords cannot be made custom
// Disabled keywords are OK
Some(token) if token.is_keyword() => {
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
Some(token) if token.is_symbol() => {
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
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
Some(_) => (),

View File

@ -26,7 +26,7 @@ pub trait FuncArgs {
/// }
///
/// 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.bar.into()));
/// container.extend(std::iter::once(self.baz.into()));
@ -51,11 +51,12 @@ pub trait FuncArgs {
/// # 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> {
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));
}
}

View File

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

View File

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

View File

@ -5,13 +5,7 @@
use crate::dynamic::{DynamicWriteLock, Variant};
use crate::fn_native::{CallableFunction, FnAny, FnCallArgs, SendSync};
use crate::r#unsafe::unsafe_try_cast;
use crate::stdlib::{
any::{type_name, TypeId},
boxed::Box,
mem,
string::String,
vec,
};
use crate::stdlib::{any::TypeId, boxed::Box, mem, string::String, vec};
use crate::{Dynamic, EvalAltResult, NativeCallContext};
// 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.
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.
fn param_types() -> Box<[TypeId]>;
/// Get the type names of this function's parameters.
/// Available under the `metadata` feature only.
#[cfg(feature = "metadata")]
fn param_names() -> Box<[&'static str]>;
/// Get the type ID of this function's return value.
/// Available under the `metadata` feature only.
#[cfg(feature = "metadata")]
fn return_type() -> TypeId;
/// 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;
/// Convert this function into a [`CallableFunction`].
fn into_callable_function(self) -> CallableFunction;
}
macro_rules! def_register {
@ -91,9 +91,9 @@ macro_rules! def_register {
RET: Variant + Clone
> RegisterNativeFunction<($($mark,)*), ()> for FN {
#[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() }
#[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 param_names() -> Box<[&'static str]> { vec![$(crate::stdlib::any::type_name::<$par>()),*].into_boxed_slice() }
#[cfg(feature = "metadata")] #[inline(always)] fn return_type() -> TypeId { TypeId::of::<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 {
CallableFunction::$abi(Box::new(move |_: NativeCallContext, args: &mut FnCallArgs| {
// The arguments are assumed to be of the correct number and types!
@ -115,9 +115,9 @@ macro_rules! def_register {
RET: Variant + Clone
> RegisterNativeFunction<(NativeCallContext<'static>, $($mark,)*), ()> for FN {
#[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() }
#[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 param_names() -> Box<[&'static str]> { vec![$(crate::stdlib::any::type_name::<$par>()),*].into_boxed_slice() }
#[cfg(feature = "metadata")] #[inline(always)] fn return_type() -> TypeId { TypeId::of::<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 {
CallableFunction::$abi(Box::new(move |ctx: NativeCallContext, args: &mut FnCallArgs| {
// The arguments are assumed to be of the correct number and types!
@ -139,9 +139,9 @@ macro_rules! def_register {
RET: Variant + Clone
> 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_names() -> Box<[&'static str]> { vec![$(type_name::<$par>()),*].into_boxed_slice() }
#[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 param_names() -> Box<[&'static str]> { vec![$(crate::stdlib::any::type_name::<$par>()),*].into_boxed_slice() }
#[cfg(feature = "metadata")] #[inline(always)] fn return_type() -> TypeId { TypeId::of::<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 {
CallableFunction::$abi(Box::new(move |_: NativeCallContext, args: &mut FnCallArgs| {
// The arguments are assumed to be of the correct number and types!
@ -160,9 +160,9 @@ macro_rules! def_register {
RET: Variant + Clone
> 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_names() -> Box<[&'static str]> { vec![$(type_name::<$par>()),*].into_boxed_slice() }
#[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 param_names() -> Box<[&'static str]> { vec![$(crate::stdlib::any::type_name::<$par>()),*].into_boxed_slice() }
#[cfg(feature = "metadata")] #[inline(always)] fn return_type() -> TypeId { TypeId::of::<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 {
CallableFunction::$abi(Box::new(move |ctx: NativeCallContext, args: &mut FnCallArgs| {
// The arguments are assumed to be of the correct number and types!

View File

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

View File

@ -142,8 +142,10 @@ macro_rules! reg_range {
($lib:ident | $x:expr => $( $y:ty ),*) => {
$(
$lib.set_iterator::<Range<$y>>();
let hash = $lib.set_native_fn($x, get_range::<$y>);
$lib.update_fn_metadata(hash, &[
let _hash = $lib.set_native_fn($x, get_range::<$y>);
#[cfg(feature = "metadata")]
$lib.update_fn_metadata(_hash, &[
concat!("from: ", stringify!($y)),
concat!("to: ", stringify!($y)),
concat!("Iterator<Item=", stringify!($y), ">")
@ -153,8 +155,10 @@ macro_rules! reg_range {
($lib:ident | step $x:expr => $( $y:ty ),*) => {
$(
$lib.set_iterator::<StepRange<$y>>();
let hash = $lib.set_native_fn($x, get_step_range::<$y>);
$lib.update_fn_metadata(hash, &[
let _hash = $lib.set_native_fn($x, get_step_range::<$y>);
#[cfg(feature = "metadata")]
$lib.update_fn_metadata(_hash, &[
concat!("from: ", stringify!($y)),
concat!("to: ", stringify!($y)),
concat!("step: ", stringify!($y)),
@ -251,10 +255,12 @@ def_package!(crate:BasicIteratorPackage:"Basic range iterators.", lib, {
lib.set_iterator::<StepDecimalRange>();
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>"]);
let _hash = lib.set_native_fn("range", |from, to| StepDecimalRange::new(from, to, Decimal::one()));
#[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));
lib.update_fn_metadata(hash, &["from: Decimal", "to: Decimal", "step: Decimal", "Iterator<Item=Decimal>"]);
let _hash = lib.set_native_fn("range", |from, to, step| StepDecimalRange::new(from, to, step));
#[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::OptimizationLevel;
use crate::stdlib::{
borrow::Cow,
boxed::Box,
collections::BTreeMap,
format,
@ -23,7 +22,7 @@ use crate::stdlib::{
};
use crate::syntax::{CustomSyntax, MARKER_BLOCK, MARKER_EXPR, MARKER_IDENT};
use crate::token::{is_keyword_function, is_valid_identifier, Token, TokenStream};
use crate::utils::get_hasher;
use crate::utils::{get_hasher, StringInterner};
use crate::{
calc_fn_hash, Dynamic, Engine, ImmutableString, LexError, ParseError, ParseErrorType, Position,
Scope, Shared, StaticVec, AST,
@ -45,7 +44,7 @@ struct ParseState<'e> {
/// Reference to the scripting [`Engine`].
engine: &'e Engine,
/// Interned strings.
interned_strings: BTreeMap<String, ImmutableString>,
interned_strings: StringInterner,
/// Encapsulates a local stack with variable names to simulate an actual runtime scope.
stack: Vec<(ImmutableString, AccessMode)>,
/// Size of the local variables stack upon entry of the current block scope.
@ -161,24 +160,17 @@ impl<'e> ParseState<'e> {
.iter()
.rev()
.enumerate()
.find(|(_, n)| **n == name)
.find(|&(_, n)| *n == name)
.and_then(|(i, _)| NonZeroUsize::new(i + 1))
}
/// Get an interned string, creating one if it is not yet interned.
#[inline(always)]
pub fn get_interned_string(
&mut self,
text: impl AsRef<str> + Into<ImmutableString>,
) -> ImmutableString {
#[allow(clippy::map_entry)]
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()
}
self.interned_strings.get(text)
}
}
@ -690,6 +682,7 @@ fn parse_map_literal(
settings.pos = eat_token(input, Token::MapStart);
let mut map: StaticVec<(Ident, Expr)> = Default::default();
let mut template: BTreeMap<ImmutableString, Dynamic> = Default::default();
loop {
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 name = state.get_interned_string(name);
template.insert(name.clone(), Default::default());
map.push((Ident { name, pos }, expr));
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.
@ -1353,7 +1347,7 @@ fn parse_unary(
/// Make an assignment statement.
fn make_assignment_stmt<'a>(
op: Cow<'static, str>,
op: &'static str,
state: &mut ParseState,
lhs: Expr,
rhs: Expr,
@ -1477,7 +1471,7 @@ fn parse_op_assignment_stmt(
| Token::PowerOfAssign
| Token::AndAssign
| Token::OrAssign
| Token::XOrAssign => token.syntax(),
| Token::XOrAssign => token.keyword_syntax(),
_ => return Ok(Stmt::Expr(lhs)),
};

View File

@ -1,7 +1,7 @@
//! Module defining macros for developing _plugins_.
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::{
Dynamic, Engine, EvalAltResult, FnAccess, FnNamespace, ImmutableString, Module,
NativeCallContext, Position,
@ -26,13 +26,4 @@ pub trait PluginFunction {
/// Is this plugin function variadic?
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 IntoIter = Box<dyn Iterator<Item = Self::Item> + 'a>;
#[inline(always)]
fn into_iter(self) -> Self::IntoIter {
Box::new(
self.values

View File

@ -211,7 +211,8 @@ impl From<&crate::Module> for ModuleMetadata {
#[cfg(feature = "metadata")]
impl Engine {
/// _(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:
/// 1) Functions defined in an [`AST`][crate::AST]

View File

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

View File

@ -381,25 +381,15 @@ pub enum Token {
}
impl Token {
/// Get the syntax of the token.
pub fn syntax(&self) -> Cow<'static, str> {
/// Get the syntax of the token if it is a keyword.
///
/// # Panics
///
/// Panics if the token is not a keyword.
pub fn keyword_syntax(&self) -> &'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(),
token => match token {
LeftBrace => "{",
RightBrace => "}",
LeftParen => "(",
@ -479,10 +469,32 @@ impl Token {
Export => "export",
#[cfg(not(feature = "no_module"))]
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,
boxed::Box,
cmp::Ordering,
collections::BTreeSet,
fmt,
fmt::{Debug, Display},
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.
#[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();
// 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)
.for_each(|m| m.hash(s));
len.hash(s);
fn_name.hash(s);
fn_name.as_ref().hash(s);
num.hash(s);
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_eq!(s, "hello, world!");
/// ```
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Default)]
#[derive(Clone, Eq, Ord, Hash, Default)]
pub struct ImmutableString(Shared<String>);
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 {
#[inline(always)]
fn borrow(&self) -> &String {
@ -559,7 +571,6 @@ impl PartialEq<ImmutableString> for String {
}
impl<S: AsRef<str>> PartialOrd<S> for ImmutableString {
#[inline(always)]
fn partial_cmp(&self, other: &S) -> Option<Ordering> {
self.as_str().partial_cmp(other.as_ref())
}
@ -594,3 +605,19 @@ impl ImmutableString {
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);
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);
}
)*