Merge pull request #376 from schungx/master
Put functions metadata under metadata feature gate.
This commit is contained in:
commit
05ddeb0d9e
4
.github/workflows/build.yml
vendored
4
.github/workflows/build.yml
vendored
@ -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:
|
||||
|
24
CHANGELOG.md
24
CHANGELOG.md
@ -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>>`.
|
||||
|
||||
|
@ -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
|
||||
|
@ -300,55 +300,50 @@ 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 {
|
||||
reference: Some(_), ..
|
||||
let mut_receiver = match fn_all.sig.inputs.iter().skip(skip_slots).next() {
|
||||
Some(syn::FnArg::Receiver(syn::Receiver {
|
||||
reference: Some(_), ..
|
||||
})) => true,
|
||||
Some(syn::FnArg::Typed(syn::PatType { ref ty, .. })) => {
|
||||
match flatten_type_groups(ty.as_ref()) {
|
||||
syn::Type::Reference(syn::TypeReference {
|
||||
mutability: Some(_),
|
||||
..
|
||||
}) => true,
|
||||
syn::FnArg::Typed(syn::PatType { ref ty, .. }) => {
|
||||
match flatten_type_groups(ty.as_ref()) {
|
||||
syn::Type::Reference(syn::TypeReference {
|
||||
mutability: Some(_),
|
||||
..
|
||||
}) => true,
|
||||
syn::Type::Reference(syn::TypeReference {
|
||||
mutability: None,
|
||||
ref elem,
|
||||
..
|
||||
}) => match flatten_type_groups(elem.as_ref()) {
|
||||
syn::Type::Path(ref p) if p.path == str_type_path => false,
|
||||
_ => {
|
||||
return Err(syn::Error::new(
|
||||
ty.span(),
|
||||
"references from Rhai in this position must be mutable",
|
||||
))
|
||||
}
|
||||
},
|
||||
_ => false,
|
||||
syn::Type::Reference(syn::TypeReference {
|
||||
mutability: None,
|
||||
ref elem,
|
||||
..
|
||||
}) => match flatten_type_groups(elem.as_ref()) {
|
||||
syn::Type::Path(ref p) if p.path == str_type_path => false,
|
||||
_ => {
|
||||
return Err(syn::Error::new(
|
||||
ty.span(),
|
||||
"references from Rhai in this position must be mutable",
|
||||
))
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
_ => false,
|
||||
};
|
||||
|
||||
// All arguments after the first must be moved except for &str.
|
||||
@ -381,22 +376,25 @@ impl Parse for ExportedFn {
|
||||
}
|
||||
|
||||
// Check return type.
|
||||
if let syn::ReturnType::Type(_, ref ret_type) = fn_all.sig.output {
|
||||
match flatten_type_groups(ret_type.as_ref()) {
|
||||
syn::Type::Ptr(_) => {
|
||||
return Err(syn::Error::new(
|
||||
fn_all.sig.output.span(),
|
||||
"Rhai functions cannot return pointers",
|
||||
))
|
||||
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(
|
||||
fn_all.sig.output.span(),
|
||||
"Rhai functions cannot return pointers",
|
||||
))
|
||||
}
|
||||
syn::Type::Reference(_) => {
|
||||
return Err(syn::Error::new(
|
||||
fn_all.sig.output.span(),
|
||||
"Rhai functions cannot return references",
|
||||
))
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
syn::Type::Reference(_) => {
|
||||
return Err(syn::Error::new(
|
||||
fn_all.sig.output.span(),
|
||||
"Rhai functions cannot return references",
|
||||
))
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
Ok(ExportedFn {
|
||||
entire_span,
|
||||
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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! {
|
||||
#module_path::rhai_generate_into_module(#module_expr, true);
|
||||
};
|
||||
proc_macro::TokenStream::from(tokens)
|
||||
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);
|
||||
}),
|
||||
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(),
|
||||
};
|
||||
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)
|
||||
match crate::register::parse_register_macro(args) {
|
||||
Ok((engine_expr, export_name, rust_mod_path)) => {
|
||||
let gen_mod_path = crate::register::generated_module_path(&rust_mod_path);
|
||||
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(),
|
||||
};
|
||||
let gen_mod_path = crate::register::generated_module_path(&rust_mod_path);
|
||||
let tokens = 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)
|
||||
match crate::register::parse_register_macro(args) {
|
||||
Ok((module_expr, export_name, rust_mod_path)) => {
|
||||
let gen_mod_path = crate::register::generated_module_path(&rust_mod_path);
|
||||
proc_macro::TokenStream::from(quote! {
|
||||
#module_expr.set_fn(#export_name, FnNamespace::Internal, FnAccess::Public,
|
||||
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(),
|
||||
};
|
||||
let gen_mod_path = crate::register::generated_module_path(&rust_mod_path);
|
||||
let tokens = 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)
|
||||
match crate::register::parse_register_macro(args) {
|
||||
Ok((module_expr, export_name, rust_mod_path)) => {
|
||||
let gen_mod_path = crate::register::generated_module_path(&rust_mod_path);
|
||||
proc_macro::TokenStream::from(quote! {
|
||||
#module_expr.set_fn(#export_name, FnNamespace::Global, FnAccess::Public,
|
||||
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(),
|
||||
}
|
||||
}
|
||||
|
@ -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] {
|
||||
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| {
|
||||
m.set_params(params)?;
|
||||
Ok(m)
|
||||
})?;
|
||||
sub_modules.push(module);
|
||||
} else {
|
||||
i += 1;
|
||||
match content[i] {
|
||||
syn::Item::Mod(_) => {
|
||||
let mut item_mod = match content.remove(i) {
|
||||
syn::Item::Mod(m) => m,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
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);
|
||||
}
|
||||
_ => i += 1,
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -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] {
|
||||
|
@ -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! {
|
||||
|
@ -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,25 +416,20 @@ 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()
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let item_fn = syn::parse2::<ExportedFn>(input_tokens).unwrap();
|
||||
@ -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
@ -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;
|
||||
|
|
||||
|
@ -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>> {
|
||||
|
46
src/ast.rs
46
src/ast.rs
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
};
|
||||
}
|
||||
|
@ -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)
|
||||
.map(Target::from)
|
||||
.unwrap_or_else(|| Target::from(()))
|
||||
})
|
||||
Ok(map
|
||||
.get_mut(index)
|
||||
.map(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 {
|
||||
|
@ -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(¶m_type_names),
|
||||
param_type_names.as_ref().map(|v| v.as_ref()),
|
||||
¶m_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(¶m_type_names),
|
||||
param_type_names.as_ref().map(|v| v.as_ref()),
|
||||
¶m_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());
|
||||
|
||||
|
@ -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(_) => (),
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
))
|
||||
|
@ -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
|
||||
.curry()
|
||||
.iter()
|
||||
.cloned()
|
||||
.chain(arg_values.iter_mut().map(mem::take))
|
||||
.collect::<Vec<_>>();
|
||||
let arg_values = if self.curry().is_empty() {
|
||||
arg_values.as_mut()
|
||||
} else {
|
||||
args_data = self
|
||||
.curry()
|
||||
.iter()
|
||||
.cloned()
|
||||
.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())
|
||||
}
|
||||
|
@ -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!
|
||||
|
@ -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(¶ms.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.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, ¶m_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),
|
||||
|
@ -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
|
||||
|
@ -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>"]);
|
||||
}
|
||||
});
|
||||
|
@ -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)),
|
||||
};
|
||||
|
@ -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]>;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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]
|
||||
|
@ -8,6 +8,7 @@ mod serialize;
|
||||
mod str;
|
||||
|
||||
#[cfg(feature = "metadata")]
|
||||
#[cfg(feature = "serde")]
|
||||
mod metadata;
|
||||
|
||||
pub use de::from_dynamic;
|
||||
|
178
src/token.rs
178
src/token.rs
@ -381,6 +381,99 @@ pub enum Token {
|
||||
}
|
||||
|
||||
impl Token {
|
||||
/// 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 {
|
||||
LeftBrace => "{",
|
||||
RightBrace => "}",
|
||||
LeftParen => "(",
|
||||
RightParen => ")",
|
||||
LeftBracket => "[",
|
||||
RightBracket => "]",
|
||||
Plus => "+",
|
||||
UnaryPlus => "+",
|
||||
Minus => "-",
|
||||
UnaryMinus => "-",
|
||||
Multiply => "*",
|
||||
Divide => "/",
|
||||
SemiColon => ";",
|
||||
Colon => ":",
|
||||
DoubleColon => "::",
|
||||
DoubleArrow => "=>",
|
||||
Underscore => "_",
|
||||
Comma => ",",
|
||||
Period => ".",
|
||||
MapStart => "#{",
|
||||
Equals => "=",
|
||||
True => "true",
|
||||
False => "false",
|
||||
Let => "let",
|
||||
Const => "const",
|
||||
If => "if",
|
||||
Else => "else",
|
||||
Switch => "switch",
|
||||
Do => "do",
|
||||
While => "while",
|
||||
Until => "until",
|
||||
Loop => "loop",
|
||||
For => "for",
|
||||
In => "in",
|
||||
LessThan => "<",
|
||||
GreaterThan => ">",
|
||||
Bang => "!",
|
||||
LessThanEqualsTo => "<=",
|
||||
GreaterThanEqualsTo => ">=",
|
||||
EqualsTo => "==",
|
||||
NotEqualsTo => "!=",
|
||||
Pipe => "|",
|
||||
Or => "||",
|
||||
Ampersand => "&",
|
||||
And => "&&",
|
||||
Continue => "continue",
|
||||
Break => "break",
|
||||
Return => "return",
|
||||
Throw => "throw",
|
||||
Try => "try",
|
||||
Catch => "catch",
|
||||
PlusAssign => "+=",
|
||||
MinusAssign => "-=",
|
||||
MultiplyAssign => "*=",
|
||||
DivideAssign => "/=",
|
||||
LeftShiftAssign => "<<=",
|
||||
RightShiftAssign => ">>=",
|
||||
AndAssign => "&=",
|
||||
OrAssign => "|=",
|
||||
XOrAssign => "^=",
|
||||
LeftShift => "<<",
|
||||
RightShift => ">>",
|
||||
XOr => "^",
|
||||
Modulo => "%",
|
||||
ModuloAssign => "%=",
|
||||
PowerOf => "**",
|
||||
PowerOfAssign => "**=",
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
Fn => "fn",
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
Private => "private",
|
||||
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
Import => "import",
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
Export => "export",
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
As => "as",
|
||||
|
||||
t => unreachable!("{:?} is not a keyword", t),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the syntax of the token.
|
||||
pub fn syntax(&self) -> Cow<'static, str> {
|
||||
use Token::*;
|
||||
@ -399,90 +492,9 @@ impl Token {
|
||||
LexError(err) => err.to_string().into(),
|
||||
Comment(s) => s.clone().into(),
|
||||
|
||||
token => match token {
|
||||
LeftBrace => "{",
|
||||
RightBrace => "}",
|
||||
LeftParen => "(",
|
||||
RightParen => ")",
|
||||
LeftBracket => "[",
|
||||
RightBracket => "]",
|
||||
Plus => "+",
|
||||
UnaryPlus => "+",
|
||||
Minus => "-",
|
||||
UnaryMinus => "-",
|
||||
Multiply => "*",
|
||||
Divide => "/",
|
||||
SemiColon => ";",
|
||||
Colon => ":",
|
||||
DoubleColon => "::",
|
||||
DoubleArrow => "=>",
|
||||
Underscore => "_",
|
||||
Comma => ",",
|
||||
Period => ".",
|
||||
MapStart => "#{",
|
||||
Equals => "=",
|
||||
True => "true",
|
||||
False => "false",
|
||||
Let => "let",
|
||||
Const => "const",
|
||||
If => "if",
|
||||
Else => "else",
|
||||
Switch => "switch",
|
||||
Do => "do",
|
||||
While => "while",
|
||||
Until => "until",
|
||||
Loop => "loop",
|
||||
For => "for",
|
||||
In => "in",
|
||||
LessThan => "<",
|
||||
GreaterThan => ">",
|
||||
Bang => "!",
|
||||
LessThanEqualsTo => "<=",
|
||||
GreaterThanEqualsTo => ">=",
|
||||
EqualsTo => "==",
|
||||
NotEqualsTo => "!=",
|
||||
Pipe => "|",
|
||||
Or => "||",
|
||||
Ampersand => "&",
|
||||
And => "&&",
|
||||
Continue => "continue",
|
||||
Break => "break",
|
||||
Return => "return",
|
||||
Throw => "throw",
|
||||
Try => "try",
|
||||
Catch => "catch",
|
||||
PlusAssign => "+=",
|
||||
MinusAssign => "-=",
|
||||
MultiplyAssign => "*=",
|
||||
DivideAssign => "/=",
|
||||
LeftShiftAssign => "<<=",
|
||||
RightShiftAssign => ">>=",
|
||||
AndAssign => "&=",
|
||||
OrAssign => "|=",
|
||||
XOrAssign => "^=",
|
||||
LeftShift => "<<",
|
||||
RightShift => ">>",
|
||||
XOr => "^",
|
||||
Modulo => "%",
|
||||
ModuloAssign => "%=",
|
||||
PowerOf => "**",
|
||||
PowerOfAssign => "**=",
|
||||
EOF => "{EOF}".into(),
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
Fn => "fn",
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
Private => "private",
|
||||
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
Import => "import",
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
Export => "export",
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
As => "as",
|
||||
EOF => "{EOF}",
|
||||
t => unreachable!("operator should be matched in outer scope: {:?}", t),
|
||||
}
|
||||
.into(),
|
||||
token => token.keyword_syntax().into(),
|
||||
}
|
||||
}
|
||||
|
||||
|
35
src/utils.rs
35
src/utils.rs
@ -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
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
)*
|
||||
|
Loading…
Reference in New Issue
Block a user