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]
|
os: [ubuntu-latest]
|
||||||
flags:
|
flags:
|
||||||
- ""
|
- ""
|
||||||
- "--features metadata,internals"
|
- "--features metadata,serde,internals"
|
||||||
- "--features unchecked"
|
- "--features unchecked"
|
||||||
- "--features sync"
|
- "--features sync"
|
||||||
- "--features no_optimize"
|
- "--features no_optimize"
|
||||||
@ -34,7 +34,7 @@ jobs:
|
|||||||
- "--features no_module"
|
- "--features no_module"
|
||||||
- "--features no_closure"
|
- "--features no_closure"
|
||||||
- "--features unicode-xid-ident"
|
- "--features unicode-xid-ident"
|
||||||
- "--features sync,no_function,no_float,no_optimize,no_module,no_closure,metadata,unchecked"
|
- "--features sync,no_function,no_float,no_optimize,no_module,no_closure,metadata,serde,unchecked"
|
||||||
toolchain: [stable]
|
toolchain: [stable]
|
||||||
experimental: [false]
|
experimental: [false]
|
||||||
include:
|
include:
|
||||||
|
24
CHANGELOG.md
24
CHANGELOG.md
@ -4,31 +4,37 @@ Rhai Release Notes
|
|||||||
Version 0.19.15
|
Version 0.19.15
|
||||||
===============
|
===============
|
||||||
|
|
||||||
This version replaces internal usage of `HashMap` with `BTreeMap` in many cases, which should result
|
This version replaces all internal usage of `HashMap` with `BTreeMap`, which should result
|
||||||
in speed improvements because a `BTreeMap` is faster when the number of items held is small.
|
in some speed improvement because a `BTreeMap` is leaner when the number of items held is small.
|
||||||
|
Most, if not all, collections in Rhai hold very few data items, so this is a typical scenario of
|
||||||
|
many tiny-sized collections.
|
||||||
|
|
||||||
This also translates to the Rhai object map type, `Map`, which used to be an alias to `HashMap` and
|
The Rhai object map type, `Map`, used to be an alias to `HashMap` and is now aliased to `BTreeMap`
|
||||||
is now aliased to `BTreeMap` instead. This change is due to the fact that, in the vast majority of
|
instead. This is also because, in the vast majority of usage cases, the number of properties held by
|
||||||
object map usages, the number of properties held is small.
|
an object map is small.
|
||||||
|
|
||||||
`HashMap` and `BTreeMap` have almost identical public API's so this change is unlikely to break a
|
`HashMap` and `BTreeMap` have almost identical public API's so this change is unlikely to break
|
||||||
lot of existing code.
|
existing code.
|
||||||
|
|
||||||
|
All function signature/metadata methods are now grouped under the umbrella `metadata` feature.
|
||||||
|
|
||||||
|
|
||||||
Breaking changes
|
Breaking changes
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
* `Map` is now an alias to `BTreeMap` instead of `HashMap`. This is because most object maps used have few properties.
|
* `Map` is now an alias to `BTreeMap` instead of `HashMap` because most object maps hold few properties.
|
||||||
* The traits `RegisterFn` and `RegisterResultFn` are removed. `Engine::register_fn` and `Engine::register_result_fn` are now implemented directly on `Engine`.
|
* The traits `RegisterFn` and `RegisterResultFn` are removed. `Engine::register_fn` and `Engine::register_result_fn` are now implemented directly on `Engine`.
|
||||||
* `FnPtr::call_dynamic` now takes `&NativeCallContext` instead of consuming it.
|
* `FnPtr::call_dynamic` now takes `&NativeCallContext` instead of consuming it.
|
||||||
* All `Module::set_fn_XXX` methods are removed, in favor of `Module::set_native_fn`.
|
* All `Module::set_fn_XXX` methods are removed, in favor of `Module::set_native_fn`.
|
||||||
* `Array::reduce` and `Array::reduce_rev` now take a `Dynamic` as initial value instead of a function pointer.
|
* `Array::reduce` and `Array::reduce_rev` now take a `Dynamic` as initial value instead of a function pointer.
|
||||||
* `protected`, `super` are now reserved keywords.
|
* `protected`, `super` are now reserved keywords.
|
||||||
|
* The `Module::set_fn_XXX` API now take `&str` as the function name instead of `Into<String>`.
|
||||||
|
* The _reflections_ API such as `Engine::gen_fn_signatures`, `Module::update_fn_metadata` etc. are put under the `metadata` feature gate.
|
||||||
|
|
||||||
Enhancements
|
Enhancements
|
||||||
------------
|
------------
|
||||||
|
|
||||||
* Replaced most `HashMap` usage with `BTreeMap` for better performance when the number of items is small.
|
* Replaced all `HashMap` usage with `BTreeMap` for better performance because collections in Rhai are tiny.
|
||||||
* `Engine::register_result_fn` no longer requires the successful return type to be `Dynamic`. It can now be any clonable type.
|
* `Engine::register_result_fn` no longer requires the successful return type to be `Dynamic`. It can now be any clonable type.
|
||||||
* `#[rhai_fn(return_raw)]` can now return `Result<T, Box<EvalAltResult>>` where `T` is any clonable type instead of `Result<Dynamic, Box<EvalAltResult>>`.
|
* `#[rhai_fn(return_raw)]` can now return `Result<T, Box<EvalAltResult>>` where `T` is any clonable type instead of `Result<Dynamic, Box<EvalAltResult>>`.
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ no_closure = [] # no automatic sharing and capture of anonymous
|
|||||||
no_module = [] # no modules
|
no_module = [] # no modules
|
||||||
internals = [] # expose internal data structures
|
internals = [] # expose internal data structures
|
||||||
unicode-xid-ident = ["unicode-xid"] # allow Unicode Standard Annex #31 for identifiers.
|
unicode-xid-ident = ["unicode-xid"] # allow Unicode Standard Annex #31 for identifiers.
|
||||||
metadata = ["serde", "serde_json"] # enables exporting functions metadata to JSON
|
metadata = ["serde_json"] # enable exporting functions metadata
|
||||||
|
|
||||||
no_std = ["smallvec/union", "num-traits/libm", "core-error", "libm", "ahash/compile-time-rng"]
|
no_std = ["smallvec/union", "num-traits/libm", "core-error", "libm", "ahash/compile-time-rng"]
|
||||||
|
|
||||||
@ -92,4 +92,4 @@ instant = { version = "0.1" } # WASM implementation of std::time::Instant
|
|||||||
instant = { version = "0.1" } # WASM implementation of std::time::Instant
|
instant = { version = "0.1" } # WASM implementation of std::time::Instant
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
features = ["metadata", "internals", "decimal"] # compiling for no-std
|
features = ["metadata", "serde", "internals", "decimal"] # compiling for no-std
|
||||||
|
@ -300,29 +300,28 @@ impl Parse for ExportedFn {
|
|||||||
let visibility = fn_all.vis;
|
let visibility = fn_all.vis;
|
||||||
|
|
||||||
// Determine if the function requires a call context
|
// Determine if the function requires a call context
|
||||||
if let Some(first_arg) = fn_all.sig.inputs.first() {
|
match fn_all.sig.inputs.first() {
|
||||||
if let syn::FnArg::Typed(syn::PatType { ref ty, .. }) = first_arg {
|
Some(syn::FnArg::Typed(syn::PatType { ref ty, .. })) => {
|
||||||
match flatten_type_groups(ty.as_ref()) {
|
match flatten_type_groups(ty.as_ref()) {
|
||||||
syn::Type::Path(p)
|
syn::Type::Path(p)
|
||||||
if p.path == context_type_path1 || p.path == context_type_path2 =>
|
if p.path == context_type_path1 || p.path == context_type_path2 =>
|
||||||
{
|
{
|
||||||
pass_context = true;
|
pass_context = true;
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
let skip_slots = if pass_context { 1 } else { 0 };
|
let skip_slots = if pass_context { 1 } else { 0 };
|
||||||
|
|
||||||
// Determine whether function generates a special calling convention for a mutable receiver.
|
// Determine whether function generates a special calling convention for a mutable receiver.
|
||||||
let mut_receiver = {
|
let mut_receiver = match fn_all.sig.inputs.iter().skip(skip_slots).next() {
|
||||||
if let Some(first_arg) = fn_all.sig.inputs.iter().skip(skip_slots).next() {
|
Some(syn::FnArg::Receiver(syn::Receiver {
|
||||||
match first_arg {
|
|
||||||
syn::FnArg::Receiver(syn::Receiver {
|
|
||||||
reference: Some(_), ..
|
reference: Some(_), ..
|
||||||
}) => true,
|
})) => true,
|
||||||
syn::FnArg::Typed(syn::PatType { ref ty, .. }) => {
|
Some(syn::FnArg::Typed(syn::PatType { ref ty, .. })) => {
|
||||||
match flatten_type_groups(ty.as_ref()) {
|
match flatten_type_groups(ty.as_ref()) {
|
||||||
syn::Type::Reference(syn::TypeReference {
|
syn::Type::Reference(syn::TypeReference {
|
||||||
mutability: Some(_),
|
mutability: Some(_),
|
||||||
@ -345,10 +344,6 @@ impl Parse for ExportedFn {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// All arguments after the first must be moved except for &str.
|
// All arguments after the first must be moved except for &str.
|
||||||
@ -381,7 +376,8 @@ impl Parse for ExportedFn {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check return type.
|
// Check return type.
|
||||||
if let syn::ReturnType::Type(_, ref ret_type) = fn_all.sig.output {
|
match fn_all.sig.output {
|
||||||
|
syn::ReturnType::Type(_, ref ret_type) => {
|
||||||
match flatten_type_groups(ret_type.as_ref()) {
|
match flatten_type_groups(ret_type.as_ref()) {
|
||||||
syn::Type::Ptr(_) => {
|
syn::Type::Ptr(_) => {
|
||||||
return Err(syn::Error::new(
|
return Err(syn::Error::new(
|
||||||
@ -398,6 +394,8 @@ impl Parse for ExportedFn {
|
|||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
Ok(ExportedFn {
|
Ok(ExportedFn {
|
||||||
entire_span,
|
entire_span,
|
||||||
signature: fn_all.sig,
|
signature: fn_all.sig,
|
||||||
@ -494,10 +492,9 @@ impl ExportedFn {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn return_type(&self) -> Option<&syn::Type> {
|
pub fn return_type(&self) -> Option<&syn::Type> {
|
||||||
if let syn::ReturnType::Type(_, ref ret_type) = self.signature.output {
|
match self.signature.output {
|
||||||
Some(flatten_type_groups(ret_type))
|
syn::ReturnType::Type(_, ref ret_type) => Some(flatten_type_groups(ret_type)),
|
||||||
} else {
|
_ => None,
|
||||||
None
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -590,20 +587,14 @@ impl ExportedFn {
|
|||||||
let name: syn::Ident =
|
let name: syn::Ident =
|
||||||
syn::Ident::new(&format!("rhai_fn_{}", self.name()), self.name().span());
|
syn::Ident::new(&format!("rhai_fn_{}", self.name()), self.name().span());
|
||||||
let impl_block = self.generate_impl("Token");
|
let impl_block = self.generate_impl("Token");
|
||||||
let callable_block = self.generate_callable("Token");
|
|
||||||
let param_names_block = self.generate_param_names("Token");
|
|
||||||
let input_types_block = self.generate_input_types("Token");
|
|
||||||
let dyn_result_fn_block = self.generate_dynamic_fn();
|
let dyn_result_fn_block = self.generate_dynamic_fn();
|
||||||
let vis = self.visibility;
|
let vis = self.visibility;
|
||||||
quote! {
|
quote! {
|
||||||
#[automatically_derived]
|
#[automatically_derived]
|
||||||
#vis mod #name {
|
#vis mod #name {
|
||||||
use super::*;
|
use super::*;
|
||||||
struct Token();
|
pub struct Token();
|
||||||
#impl_block
|
#impl_block
|
||||||
#callable_block
|
|
||||||
#param_names_block
|
|
||||||
#input_types_block
|
|
||||||
#dyn_result_fn_block
|
#dyn_result_fn_block
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -616,22 +607,18 @@ impl ExportedFn {
|
|||||||
dynamic_signature.ident =
|
dynamic_signature.ident =
|
||||||
syn::Ident::new("dynamic_result_fn", proc_macro2::Span::call_site());
|
syn::Ident::new("dynamic_result_fn", proc_macro2::Span::call_site());
|
||||||
dynamic_signature.output = syn::parse2::<syn::ReturnType>(quote! {
|
dynamic_signature.output = syn::parse2::<syn::ReturnType>(quote! {
|
||||||
-> Result<Dynamic, Box<EvalAltResult>>
|
-> RhaiResult
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let arguments: Vec<syn::Ident> = dynamic_signature
|
let arguments: Vec<syn::Ident> = dynamic_signature
|
||||||
.inputs
|
.inputs
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|fn_arg| {
|
.filter_map(|fn_arg| match fn_arg {
|
||||||
if let syn::FnArg::Typed(syn::PatType { ref pat, .. }) = fn_arg {
|
syn::FnArg::Typed(syn::PatType { ref pat, .. }) => match pat.as_ref() {
|
||||||
if let syn::Pat::Ident(ref ident) = pat.as_ref() {
|
syn::Pat::Ident(ref ident) => Some(ident.ident.clone()),
|
||||||
Some(ident.ident.clone())
|
_ => None,
|
||||||
} else {
|
},
|
||||||
None
|
_ => None,
|
||||||
}
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
@ -656,48 +643,6 @@ impl ExportedFn {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn generate_callable(&self, on_type_name: &str) -> proc_macro2::TokenStream {
|
|
||||||
let token_name: syn::Ident = syn::Ident::new(on_type_name, self.name().span());
|
|
||||||
let callable_fn_name: syn::Ident = syn::Ident::new(
|
|
||||||
&format!("{}_callable", on_type_name.to_lowercase()),
|
|
||||||
self.name().span(),
|
|
||||||
);
|
|
||||||
quote! {
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn #callable_fn_name() -> CallableFunction {
|
|
||||||
#token_name().into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn generate_param_names(&self, on_type_name: &str) -> proc_macro2::TokenStream {
|
|
||||||
let token_name: syn::Ident = syn::Ident::new(on_type_name, self.name().span());
|
|
||||||
let param_names_fn_name: syn::Ident = syn::Ident::new(
|
|
||||||
&format!("{}_param_names", on_type_name.to_lowercase()),
|
|
||||||
self.name().span(),
|
|
||||||
);
|
|
||||||
quote! {
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn #param_names_fn_name() -> Box<[&'static str]> {
|
|
||||||
#token_name().param_names()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn generate_input_types(&self, on_type_name: &str) -> proc_macro2::TokenStream {
|
|
||||||
let token_name: syn::Ident = syn::Ident::new(on_type_name, self.name().span());
|
|
||||||
let input_types_fn_name: syn::Ident = syn::Ident::new(
|
|
||||||
&format!("{}_input_types", on_type_name.to_lowercase()),
|
|
||||||
self.name().span(),
|
|
||||||
);
|
|
||||||
quote! {
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn #input_types_fn_name() -> Box<[TypeId]> {
|
|
||||||
#token_name().input_types()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn generate_impl(&self, on_type_name: &str) -> proc_macro2::TokenStream {
|
pub fn generate_impl(&self, on_type_name: &str) -> proc_macro2::TokenStream {
|
||||||
let sig_name = self.name().clone();
|
let sig_name = self.name().clone();
|
||||||
let arg_count = self.arg_count();
|
let arg_count = self.arg_count();
|
||||||
@ -873,23 +818,19 @@ impl ExportedFn {
|
|||||||
|
|
||||||
let type_name = syn::Ident::new(on_type_name, proc_macro2::Span::call_site());
|
let type_name = syn::Ident::new(on_type_name, proc_macro2::Span::call_site());
|
||||||
quote! {
|
quote! {
|
||||||
|
impl #type_name {
|
||||||
|
pub const PARAM_NAMES: &'static [&'static str] = &[#(#input_type_names,)* #return_type];
|
||||||
|
#[inline(always)] pub fn param_types() -> [TypeId; #arg_count] { [#(#input_type_exprs),*] }
|
||||||
|
}
|
||||||
impl PluginFunction for #type_name {
|
impl PluginFunction for #type_name {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||||
debug_assert_eq!(args.len(), #arg_count, "wrong arg count: {} != {}", args.len(), #arg_count);
|
|
||||||
#(#unpack_statements)*
|
#(#unpack_statements)*
|
||||||
#return_expr
|
#return_expr
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)] fn is_method_call(&self) -> bool { #is_method_call }
|
#[inline(always)] fn is_method_call(&self) -> bool { #is_method_call }
|
||||||
#[inline(always)] fn is_variadic(&self) -> bool { false }
|
#[inline(always)] fn is_variadic(&self) -> bool { false }
|
||||||
#[inline(always)] fn clone_boxed(&self) -> Box<dyn PluginFunction> { Box::new(#type_name()) }
|
|
||||||
#[inline(always)] fn param_names(&self) -> Box<[&'static str]> {
|
|
||||||
new_vec![#(#input_type_names,)* #return_type].into_boxed_slice()
|
|
||||||
}
|
|
||||||
#[inline(always)] fn input_types(&self) -> Box<[TypeId]> {
|
|
||||||
new_vec![#(#input_type_exprs),*].into_boxed_slice()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -212,10 +212,9 @@ pub fn export_module(
|
|||||||
#[proc_macro]
|
#[proc_macro]
|
||||||
pub fn exported_module(module_path: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
pub fn exported_module(module_path: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||||
let module_path = parse_macro_input!(module_path as syn::Path);
|
let module_path = parse_macro_input!(module_path as syn::Path);
|
||||||
let tokens = quote::quote! {
|
proc_macro::TokenStream::from(quote::quote! {
|
||||||
#module_path::rhai_module_generate()
|
#module_path::rhai_module_generate()
|
||||||
};
|
})
|
||||||
proc_macro::TokenStream::from(tokens)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Macro to combine a _plugin module_ into an existing module.
|
/// Macro to combine a _plugin module_ into an existing module.
|
||||||
@ -258,15 +257,12 @@ pub fn exported_module(module_path: proc_macro::TokenStream) -> proc_macro::Toke
|
|||||||
/// ```
|
/// ```
|
||||||
#[proc_macro]
|
#[proc_macro]
|
||||||
pub fn combine_with_exported_module(args: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
pub fn combine_with_exported_module(args: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||||
let (module_expr, _export_name, module_path) = match crate::register::parse_register_macro(args)
|
match crate::register::parse_register_macro(args) {
|
||||||
{
|
Ok((module_expr, _export_name, module_path)) => proc_macro::TokenStream::from(quote! {
|
||||||
Ok(triple) => triple,
|
|
||||||
Err(e) => return e.to_compile_error().into(),
|
|
||||||
};
|
|
||||||
let tokens = quote! {
|
|
||||||
#module_path::rhai_generate_into_module(#module_expr, true);
|
#module_path::rhai_generate_into_module(#module_expr, true);
|
||||||
};
|
}),
|
||||||
proc_macro::TokenStream::from(tokens)
|
Err(e) => e.to_compile_error().into(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Macro to register a _plugin function_ (defined via [`#[export_fn]`][export_fn]) into an `Engine`.
|
/// Macro to register a _plugin function_ (defined via [`#[export_fn]`][export_fn]) into an `Engine`.
|
||||||
@ -293,16 +289,15 @@ pub fn combine_with_exported_module(args: proc_macro::TokenStream) -> proc_macro
|
|||||||
/// ```
|
/// ```
|
||||||
#[proc_macro]
|
#[proc_macro]
|
||||||
pub fn register_exported_fn(args: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
pub fn register_exported_fn(args: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||||
let (engine_expr, export_name, rust_mod_path) =
|
|
||||||
match crate::register::parse_register_macro(args) {
|
match crate::register::parse_register_macro(args) {
|
||||||
Ok(triple) => triple,
|
Ok((engine_expr, export_name, rust_mod_path)) => {
|
||||||
Err(e) => return e.to_compile_error().into(),
|
|
||||||
};
|
|
||||||
let gen_mod_path = crate::register::generated_module_path(&rust_mod_path);
|
let gen_mod_path = crate::register::generated_module_path(&rust_mod_path);
|
||||||
let tokens = quote! {
|
proc_macro::TokenStream::from(quote! {
|
||||||
#engine_expr.register_result_fn(&(#export_name), #gen_mod_path::dynamic_result_fn);
|
#engine_expr.register_result_fn(#export_name, #gen_mod_path::dynamic_result_fn);
|
||||||
};
|
})
|
||||||
proc_macro::TokenStream::from(tokens)
|
}
|
||||||
|
Err(e) => e.to_compile_error().into(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Macro to register a _plugin function_ into a Rhai `Module`.
|
/// Macro to register a _plugin function_ into a Rhai `Module`.
|
||||||
@ -332,19 +327,18 @@ pub fn register_exported_fn(args: proc_macro::TokenStream) -> proc_macro::TokenS
|
|||||||
/// ```
|
/// ```
|
||||||
#[proc_macro]
|
#[proc_macro]
|
||||||
pub fn set_exported_fn(args: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
pub fn set_exported_fn(args: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||||
let (module_expr, export_name, rust_mod_path) =
|
|
||||||
match crate::register::parse_register_macro(args) {
|
match crate::register::parse_register_macro(args) {
|
||||||
Ok(triple) => triple,
|
Ok((module_expr, export_name, rust_mod_path)) => {
|
||||||
Err(e) => return e.to_compile_error().into(),
|
|
||||||
};
|
|
||||||
let gen_mod_path = crate::register::generated_module_path(&rust_mod_path);
|
let gen_mod_path = crate::register::generated_module_path(&rust_mod_path);
|
||||||
let tokens = quote! {
|
proc_macro::TokenStream::from(quote! {
|
||||||
#module_expr.set_fn(#export_name, FnNamespace::Internal, FnAccess::Public,
|
#module_expr.set_fn(#export_name, FnNamespace::Internal, FnAccess::Public,
|
||||||
Some(#gen_mod_path::token_param_names().as_ref()),
|
Some(#gen_mod_path::Token::PARAM_NAMES),
|
||||||
#gen_mod_path::token_input_types().as_ref(),
|
&#gen_mod_path::Token::param_types(),
|
||||||
#gen_mod_path::token_callable());
|
#gen_mod_path::Token().into());
|
||||||
};
|
})
|
||||||
proc_macro::TokenStream::from(tokens)
|
}
|
||||||
|
Err(e) => e.to_compile_error().into(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Macro to register a _plugin function_ into a Rhai `Module` and expose it globally.
|
/// Macro to register a _plugin function_ into a Rhai `Module` and expose it globally.
|
||||||
@ -374,17 +368,16 @@ pub fn set_exported_fn(args: proc_macro::TokenStream) -> proc_macro::TokenStream
|
|||||||
/// ```
|
/// ```
|
||||||
#[proc_macro]
|
#[proc_macro]
|
||||||
pub fn set_exported_global_fn(args: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
pub fn set_exported_global_fn(args: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||||
let (module_expr, export_name, rust_mod_path) =
|
|
||||||
match crate::register::parse_register_macro(args) {
|
match crate::register::parse_register_macro(args) {
|
||||||
Ok(triple) => triple,
|
Ok((module_expr, export_name, rust_mod_path)) => {
|
||||||
Err(e) => return e.to_compile_error().into(),
|
|
||||||
};
|
|
||||||
let gen_mod_path = crate::register::generated_module_path(&rust_mod_path);
|
let gen_mod_path = crate::register::generated_module_path(&rust_mod_path);
|
||||||
let tokens = quote! {
|
proc_macro::TokenStream::from(quote! {
|
||||||
#module_expr.set_fn(#export_name, FnNamespace::Global, FnAccess::Public,
|
#module_expr.set_fn(#export_name, FnNamespace::Global, FnAccess::Public,
|
||||||
Some(#gen_mod_path::token_param_names().as_ref()),
|
Some(#gen_mod_path::Token::PARAM_NAMES),
|
||||||
#gen_mod_path::token_input_types().as_ref(),
|
&#gen_mod_path::Token::param_types(),
|
||||||
#gen_mod_path::token_callable());
|
#gen_mod_path::Token().into());
|
||||||
};
|
})
|
||||||
proc_macro::TokenStream::from(tokens)
|
}
|
||||||
|
Err(e) => e.to_compile_error().into(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -156,8 +156,11 @@ impl Parse for Module {
|
|||||||
}) => {
|
}) => {
|
||||||
// #[cfg] attributes are not allowed on const declarations
|
// #[cfg] attributes are not allowed on const declarations
|
||||||
crate::attrs::deny_cfg_attr(&attrs)?;
|
crate::attrs::deny_cfg_attr(&attrs)?;
|
||||||
if let syn::Visibility::Public(_) = vis {
|
match vis {
|
||||||
consts.push((ident.to_string(), ty.clone(), expr.as_ref().clone()));
|
syn::Visibility::Public(_) => {
|
||||||
|
consts.push((ident.to_string(), ty.clone(), expr.as_ref().clone()))
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
@ -170,26 +173,23 @@ impl Parse for Module {
|
|||||||
sub_modules.reserve(content.len() - fns.len() - consts.len());
|
sub_modules.reserve(content.len() - fns.len() - consts.len());
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
while i < content.len() {
|
while i < content.len() {
|
||||||
if let syn::Item::Mod(_) = &content[i] {
|
match content[i] {
|
||||||
|
syn::Item::Mod(_) => {
|
||||||
let mut item_mod = match content.remove(i) {
|
let mut item_mod = match content.remove(i) {
|
||||||
syn::Item::Mod(m) => m,
|
syn::Item::Mod(m) => m,
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
let params: ExportedModParams = match crate::attrs::inner_item_attributes(
|
let params: ExportedModParams =
|
||||||
&mut item_mod.attrs,
|
crate::attrs::inner_item_attributes(&mut item_mod.attrs, "rhai_mod")?;
|
||||||
"rhai_mod",
|
let module = syn::parse2::<Module>(item_mod.to_token_stream()).and_then(
|
||||||
) {
|
|mut m| {
|
||||||
Ok(p) => p,
|
|
||||||
Err(e) => return Err(e),
|
|
||||||
};
|
|
||||||
let module =
|
|
||||||
syn::parse2::<Module>(item_mod.to_token_stream()).and_then(|mut m| {
|
|
||||||
m.set_params(params)?;
|
m.set_params(params)?;
|
||||||
Ok(m)
|
Ok(m)
|
||||||
})?;
|
},
|
||||||
|
)?;
|
||||||
sub_modules.push(module);
|
sub_modules.push(module);
|
||||||
} else {
|
}
|
||||||
i += 1;
|
_ => i += 1,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -33,8 +33,7 @@ pub fn parse_register_macro(
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
let export_name = match &items[1] {
|
let export_name = match &items[1] {
|
||||||
syn::Expr::Lit(lit_str) => quote_spanned!(items[1].span() =>
|
syn::Expr::Lit(lit_str) => quote_spanned!(items[1].span() => #lit_str),
|
||||||
#lit_str.to_string()),
|
|
||||||
expr => quote! { #expr },
|
expr => quote! { #expr },
|
||||||
};
|
};
|
||||||
let rust_mod_path = if let syn::Expr::Path(ref path) = &items[2] {
|
let rust_mod_path = if let syn::Expr::Path(ref path) = &items[2] {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
use quote::{quote, ToTokens};
|
use quote::quote;
|
||||||
|
|
||||||
use crate::attrs::ExportScope;
|
use crate::attrs::ExportScope;
|
||||||
use crate::function::{
|
use crate::function::{
|
||||||
@ -81,16 +81,6 @@ pub fn generate_body(
|
|||||||
);
|
);
|
||||||
let reg_names = function.exported_names();
|
let reg_names = function.exported_names();
|
||||||
|
|
||||||
let fn_input_names: Vec<String> = function
|
|
||||||
.arg_list()
|
|
||||||
.map(|fn_arg| match fn_arg {
|
|
||||||
syn::FnArg::Receiver(_) => panic!("internal error: receiver fn outside impl!?"),
|
|
||||||
syn::FnArg::Typed(syn::PatType { pat, ty, .. }) => {
|
|
||||||
format!("{}: {}", pat.to_token_stream(), print_type(ty))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let fn_input_types: Vec<syn::Expr> = function
|
let fn_input_types: Vec<syn::Expr> = function
|
||||||
.arg_list()
|
.arg_list()
|
||||||
.map(|fn_arg| match fn_arg {
|
.map(|fn_arg| match fn_arg {
|
||||||
@ -128,17 +118,12 @@ pub fn generate_body(
|
|||||||
};
|
};
|
||||||
|
|
||||||
syn::parse2::<syn::Expr>(quote! {
|
syn::parse2::<syn::Expr>(quote! {
|
||||||
core::any::TypeId::of::<#arg_type>()})
|
TypeId::of::<#arg_type>()})
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let return_type = function
|
|
||||||
.return_type()
|
|
||||||
.map(print_type)
|
|
||||||
.unwrap_or_else(|| "()".to_string());
|
|
||||||
|
|
||||||
for fn_literal in reg_names {
|
for fn_literal in reg_names {
|
||||||
let mut namespace = FnNamespaceAccess::Internal;
|
let mut namespace = FnNamespaceAccess::Internal;
|
||||||
|
|
||||||
@ -172,7 +157,7 @@ pub fn generate_body(
|
|||||||
set_fn_statements.push(
|
set_fn_statements.push(
|
||||||
syn::parse2::<syn::Stmt>(quote! {
|
syn::parse2::<syn::Stmt>(quote! {
|
||||||
m.set_fn(#fn_literal, FnNamespace::#ns_str, FnAccess::Public,
|
m.set_fn(#fn_literal, FnNamespace::#ns_str, FnAccess::Public,
|
||||||
Some(&[#(#fn_input_names,)* #return_type]), &[#(#fn_input_types),*],
|
Some(#fn_token_name::PARAM_NAMES), &[#(#fn_input_types),*],
|
||||||
#fn_token_name().into());
|
#fn_token_name().into());
|
||||||
})
|
})
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
@ -181,12 +166,9 @@ pub fn generate_body(
|
|||||||
|
|
||||||
gen_fn_tokens.push(quote! {
|
gen_fn_tokens.push(quote! {
|
||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
struct #fn_token_name();
|
pub struct #fn_token_name();
|
||||||
});
|
});
|
||||||
gen_fn_tokens.push(function.generate_impl(&fn_token_name.to_string()));
|
gen_fn_tokens.push(function.generate_impl(&fn_token_name.to_string()));
|
||||||
gen_fn_tokens.push(function.generate_callable(&fn_token_name.to_string()));
|
|
||||||
gen_fn_tokens.push(function.generate_param_names(&fn_token_name.to_string()));
|
|
||||||
gen_fn_tokens.push(function.generate_input_types(&fn_token_name.to_string()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut generate_fn_call = syn::parse2::<syn::ItemMod>(quote! {
|
let mut generate_fn_call = syn::parse2::<syn::ItemMod>(quote! {
|
||||||
|
@ -275,35 +275,21 @@ mod generate_tests {
|
|||||||
#[automatically_derived]
|
#[automatically_derived]
|
||||||
pub mod rhai_fn_do_nothing {
|
pub mod rhai_fn_do_nothing {
|
||||||
use super::*;
|
use super::*;
|
||||||
struct Token();
|
pub struct Token();
|
||||||
|
impl Token {
|
||||||
|
pub const PARAM_NAMES: &'static [&'static str] = &["()"];
|
||||||
|
#[inline(always)] pub fn param_types() -> [TypeId; 0usize] { [] }
|
||||||
|
}
|
||||||
impl PluginFunction for Token {
|
impl PluginFunction for Token {
|
||||||
#[inline(always)] fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
#[inline(always)] fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||||
debug_assert_eq!(args.len(), 0usize,
|
|
||||||
"wrong arg count: {} != {}", args.len(), 0usize);
|
|
||||||
Ok(Dynamic::from(do_nothing()))
|
Ok(Dynamic::from(do_nothing()))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)] fn is_method_call(&self) -> bool { false }
|
#[inline(always)] fn is_method_call(&self) -> bool { false }
|
||||||
#[inline(always)] fn is_variadic(&self) -> bool { false }
|
#[inline(always)] fn is_variadic(&self) -> bool { false }
|
||||||
#[inline(always)] fn clone_boxed(&self) -> Box<dyn PluginFunction> { Box::new(Token()) }
|
|
||||||
#[inline(always)] fn param_names(&self) -> Box<[&'static str]> {
|
|
||||||
new_vec!["()"].into_boxed_slice()
|
|
||||||
}
|
|
||||||
#[inline(always)] fn input_types(&self) -> Box<[TypeId]> {
|
|
||||||
new_vec![].into_boxed_slice()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[inline(always)] pub fn token_callable() -> CallableFunction {
|
|
||||||
Token().into()
|
|
||||||
}
|
|
||||||
#[inline(always)] pub fn token_param_names() -> Box<[&'static str]> {
|
|
||||||
Token().param_names()
|
|
||||||
}
|
|
||||||
#[inline(always)] pub fn token_input_types() -> Box<[TypeId]> {
|
|
||||||
Token().input_types()
|
|
||||||
}
|
}
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
#[inline(always)] pub fn dynamic_result_fn() -> Result<Dynamic, Box<EvalAltResult> > {
|
#[inline(always)] pub fn dynamic_result_fn() -> RhaiResult {
|
||||||
Ok(Dynamic::from(do_nothing()))
|
Ok(Dynamic::from(do_nothing()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -323,37 +309,23 @@ mod generate_tests {
|
|||||||
#[automatically_derived]
|
#[automatically_derived]
|
||||||
pub mod rhai_fn_do_something {
|
pub mod rhai_fn_do_something {
|
||||||
use super::*;
|
use super::*;
|
||||||
struct Token();
|
pub struct Token();
|
||||||
|
impl Token {
|
||||||
|
pub const PARAM_NAMES: &'static [&'static str] = &["x: usize", "()"];
|
||||||
|
#[inline(always)] pub fn param_types() -> [TypeId; 1usize] { [TypeId::of::<usize>()] }
|
||||||
|
}
|
||||||
impl PluginFunction for Token {
|
impl PluginFunction for Token {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||||
debug_assert_eq!(args.len(), 1usize,
|
|
||||||
"wrong arg count: {} != {}", args.len(), 1usize);
|
|
||||||
let arg0 = mem::take(args[0usize]).cast::<usize>();
|
let arg0 = mem::take(args[0usize]).cast::<usize>();
|
||||||
Ok(Dynamic::from(do_something(arg0)))
|
Ok(Dynamic::from(do_something(arg0)))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)] fn is_method_call(&self) -> bool { false }
|
#[inline(always)] fn is_method_call(&self) -> bool { false }
|
||||||
#[inline(always)] fn is_variadic(&self) -> bool { false }
|
#[inline(always)] fn is_variadic(&self) -> bool { false }
|
||||||
#[inline(always)] fn clone_boxed(&self) -> Box<dyn PluginFunction> { Box::new(Token()) }
|
|
||||||
#[inline(always)] fn param_names(&self) -> Box<[&'static str]> {
|
|
||||||
new_vec!["x: usize", "()"].into_boxed_slice()
|
|
||||||
}
|
|
||||||
#[inline(always)] fn input_types(&self) -> Box<[TypeId]> {
|
|
||||||
new_vec![TypeId::of::<usize>()].into_boxed_slice()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[inline(always)] pub fn token_callable() -> CallableFunction {
|
|
||||||
Token().into()
|
|
||||||
}
|
|
||||||
#[inline(always)] pub fn token_param_names() -> Box<[&'static str]> {
|
|
||||||
Token().param_names()
|
|
||||||
}
|
|
||||||
#[inline(always)] pub fn token_input_types() -> Box<[TypeId]> {
|
|
||||||
Token().input_types()
|
|
||||||
}
|
}
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
#[inline(always)] pub fn dynamic_result_fn(x: usize) -> Result<Dynamic, Box<EvalAltResult> > {
|
#[inline(always)] pub fn dynamic_result_fn(x: usize) -> RhaiResult {
|
||||||
Ok(Dynamic::from(do_something(x)))
|
Ok(Dynamic::from(do_something(x)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -373,37 +345,23 @@ mod generate_tests {
|
|||||||
#[automatically_derived]
|
#[automatically_derived]
|
||||||
pub mod rhai_fn_do_something {
|
pub mod rhai_fn_do_something {
|
||||||
use super::*;
|
use super::*;
|
||||||
struct Token();
|
pub struct Token();
|
||||||
|
impl Token {
|
||||||
|
pub const PARAM_NAMES: &'static [&'static str] = &["x: usize", "()"];
|
||||||
|
#[inline(always)] pub fn param_types() -> [TypeId; 1usize] { [TypeId::of::<usize>()] }
|
||||||
|
}
|
||||||
impl PluginFunction for Token {
|
impl PluginFunction for Token {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||||
debug_assert_eq!(args.len(), 1usize,
|
|
||||||
"wrong arg count: {} != {}", args.len(), 1usize);
|
|
||||||
let arg0 = mem::take(args[0usize]).cast::<usize>();
|
let arg0 = mem::take(args[0usize]).cast::<usize>();
|
||||||
Ok(Dynamic::from(do_something(context, arg0)))
|
Ok(Dynamic::from(do_something(context, arg0)))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)] fn is_method_call(&self) -> bool { false }
|
#[inline(always)] fn is_method_call(&self) -> bool { false }
|
||||||
#[inline(always)] fn is_variadic(&self) -> bool { false }
|
#[inline(always)] fn is_variadic(&self) -> bool { false }
|
||||||
#[inline(always)] fn clone_boxed(&self) -> Box<dyn PluginFunction> { Box::new(Token()) }
|
|
||||||
#[inline(always)] fn param_names(&self) -> Box<[&'static str]> {
|
|
||||||
new_vec!["x: usize", "()"].into_boxed_slice()
|
|
||||||
}
|
|
||||||
#[inline(always)] fn input_types(&self) -> Box<[TypeId]> {
|
|
||||||
new_vec![TypeId::of::<usize>()].into_boxed_slice()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[inline(always)] pub fn token_callable() -> CallableFunction {
|
|
||||||
Token().into()
|
|
||||||
}
|
|
||||||
#[inline(always)] pub fn token_param_names() -> Box<[&'static str]> {
|
|
||||||
Token().param_names()
|
|
||||||
}
|
|
||||||
#[inline(always)] pub fn token_input_types() -> Box<[TypeId]> {
|
|
||||||
Token().input_types()
|
|
||||||
}
|
}
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
#[inline(always)] pub fn dynamic_result_fn(context: NativeCallContext, x: usize) -> Result<Dynamic, Box<EvalAltResult> > {
|
#[inline(always)] pub fn dynamic_result_fn(context: NativeCallContext, x: usize) -> RhaiResult {
|
||||||
Ok(Dynamic::from(do_something(context, x)))
|
Ok(Dynamic::from(do_something(context, x)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -426,36 +384,22 @@ mod generate_tests {
|
|||||||
#[automatically_derived]
|
#[automatically_derived]
|
||||||
pub mod rhai_fn_return_dynamic {
|
pub mod rhai_fn_return_dynamic {
|
||||||
use super::*;
|
use super::*;
|
||||||
struct Token();
|
pub struct Token();
|
||||||
|
impl Token {
|
||||||
|
pub const PARAM_NAMES: &'static [&'static str] = &["rhai::Dynamic"];
|
||||||
|
#[inline(always)] pub fn param_types() -> [TypeId; 0usize] { [] }
|
||||||
|
}
|
||||||
impl PluginFunction for Token {
|
impl PluginFunction for Token {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||||
debug_assert_eq!(args.len(), 0usize,
|
|
||||||
"wrong arg count: {} != {}", args.len(), 0usize);
|
|
||||||
Ok(Dynamic::from(return_dynamic()))
|
Ok(Dynamic::from(return_dynamic()))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)] fn is_method_call(&self) -> bool { false }
|
#[inline(always)] fn is_method_call(&self) -> bool { false }
|
||||||
#[inline(always)] fn is_variadic(&self) -> bool { false }
|
#[inline(always)] fn is_variadic(&self) -> bool { false }
|
||||||
#[inline(always)] fn clone_boxed(&self) -> Box<dyn PluginFunction> { Box::new(Token()) }
|
|
||||||
#[inline(always)] fn param_names(&self) -> Box<[&'static str]> {
|
|
||||||
new_vec!["rhai::Dynamic"].into_boxed_slice()
|
|
||||||
}
|
|
||||||
#[inline(always)] fn input_types(&self) -> Box<[TypeId]> {
|
|
||||||
new_vec![].into_boxed_slice()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[inline(always)] pub fn token_callable() -> CallableFunction {
|
|
||||||
Token().into()
|
|
||||||
}
|
|
||||||
#[inline(always)] pub fn token_param_names() -> Box<[&'static str]> {
|
|
||||||
Token().param_names()
|
|
||||||
}
|
|
||||||
#[inline(always)] pub fn token_input_types() -> Box<[TypeId]> {
|
|
||||||
Token().input_types()
|
|
||||||
}
|
}
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
#[inline(always)] pub fn dynamic_result_fn() -> Result<Dynamic, Box<EvalAltResult> > {
|
#[inline(always)] pub fn dynamic_result_fn() -> RhaiResult {
|
||||||
Ok(Dynamic::from(return_dynamic()))
|
Ok(Dynamic::from(return_dynamic()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -472,24 +416,19 @@ mod generate_tests {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let expected_tokens = quote! {
|
let expected_tokens = quote! {
|
||||||
|
impl TestStruct {
|
||||||
|
pub const PARAM_NAMES: &'static [&'static str] = &["x: usize", "()"];
|
||||||
|
#[inline(always)] pub fn param_types() -> [TypeId; 1usize] { [TypeId::of::<usize>()] }
|
||||||
|
}
|
||||||
impl PluginFunction for TestStruct {
|
impl PluginFunction for TestStruct {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||||
debug_assert_eq!(args.len(), 1usize,
|
|
||||||
"wrong arg count: {} != {}", args.len(), 1usize);
|
|
||||||
let arg0 = mem::take(args[0usize]).cast::<usize>();
|
let arg0 = mem::take(args[0usize]).cast::<usize>();
|
||||||
Ok(Dynamic::from(do_something(arg0)))
|
Ok(Dynamic::from(do_something(arg0)))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)] fn is_method_call(&self) -> bool { false }
|
#[inline(always)] fn is_method_call(&self) -> bool { false }
|
||||||
#[inline(always)] fn is_variadic(&self) -> bool { false }
|
#[inline(always)] fn is_variadic(&self) -> bool { false }
|
||||||
#[inline(always)] fn clone_boxed(&self) -> Box<dyn PluginFunction> { Box::new(TestStruct()) }
|
|
||||||
#[inline(always)] fn param_names(&self) -> Box<[&'static str]> {
|
|
||||||
new_vec!["x: usize", "()"].into_boxed_slice()
|
|
||||||
}
|
|
||||||
#[inline(always)] fn input_types(&self) -> Box<[TypeId]> {
|
|
||||||
new_vec![TypeId::of::<usize>()].into_boxed_slice()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -507,12 +446,14 @@ mod generate_tests {
|
|||||||
#[automatically_derived]
|
#[automatically_derived]
|
||||||
pub mod rhai_fn_add_together {
|
pub mod rhai_fn_add_together {
|
||||||
use super::*;
|
use super::*;
|
||||||
struct Token();
|
pub struct Token();
|
||||||
|
impl Token {
|
||||||
|
pub const PARAM_NAMES: &'static [&'static str] = &["x: usize", "y: usize", "usize"];
|
||||||
|
#[inline(always)] pub fn param_types() -> [TypeId; 2usize] { [TypeId::of::<usize>(), TypeId::of::<usize>()] }
|
||||||
|
}
|
||||||
impl PluginFunction for Token {
|
impl PluginFunction for Token {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||||
debug_assert_eq!(args.len(), 2usize,
|
|
||||||
"wrong arg count: {} != {}", args.len(), 2usize);
|
|
||||||
let arg0 = mem::take(args[0usize]).cast::<usize>();
|
let arg0 = mem::take(args[0usize]).cast::<usize>();
|
||||||
let arg1 = mem::take(args[1usize]).cast::<usize>();
|
let arg1 = mem::take(args[1usize]).cast::<usize>();
|
||||||
Ok(Dynamic::from(add_together(arg0, arg1)))
|
Ok(Dynamic::from(add_together(arg0, arg1)))
|
||||||
@ -520,26 +461,9 @@ mod generate_tests {
|
|||||||
|
|
||||||
#[inline(always)] fn is_method_call(&self) -> bool { false }
|
#[inline(always)] fn is_method_call(&self) -> bool { false }
|
||||||
#[inline(always)] fn is_variadic(&self) -> bool { false }
|
#[inline(always)] fn is_variadic(&self) -> bool { false }
|
||||||
#[inline(always)] fn clone_boxed(&self) -> Box<dyn PluginFunction> { Box::new(Token()) }
|
|
||||||
#[inline(always)] fn param_names(&self) -> Box<[&'static str]> {
|
|
||||||
new_vec!["x: usize", "y: usize", "usize"].into_boxed_slice()
|
|
||||||
}
|
|
||||||
#[inline(always)] fn input_types(&self) -> Box<[TypeId]> {
|
|
||||||
new_vec![TypeId::of::<usize>(),
|
|
||||||
TypeId::of::<usize>()].into_boxed_slice()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[inline(always)] pub fn token_callable() -> CallableFunction {
|
|
||||||
Token().into()
|
|
||||||
}
|
|
||||||
#[inline(always)] pub fn token_param_names() -> Box<[&'static str]> {
|
|
||||||
Token().param_names()
|
|
||||||
}
|
|
||||||
#[inline(always)] pub fn token_input_types() -> Box<[TypeId]> {
|
|
||||||
Token().input_types()
|
|
||||||
}
|
}
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
#[inline(always)] pub fn dynamic_result_fn(x: usize, y: usize) -> Result<Dynamic, Box<EvalAltResult> > {
|
#[inline(always)] pub fn dynamic_result_fn(x: usize, y: usize) -> RhaiResult {
|
||||||
Ok(Dynamic::from(add_together(x, y)))
|
Ok(Dynamic::from(add_together(x, y)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -559,12 +483,14 @@ mod generate_tests {
|
|||||||
#[automatically_derived]
|
#[automatically_derived]
|
||||||
pub mod rhai_fn_increment {
|
pub mod rhai_fn_increment {
|
||||||
use super::*;
|
use super::*;
|
||||||
struct Token();
|
pub struct Token();
|
||||||
|
impl Token {
|
||||||
|
pub const PARAM_NAMES: &'static [&'static str] = &["x: &mut usize", "y: usize", "()"];
|
||||||
|
#[inline(always)] pub fn param_types() -> [TypeId; 2usize] { [TypeId::of::<usize>(), TypeId::of::<usize>()] }
|
||||||
|
}
|
||||||
impl PluginFunction for Token {
|
impl PluginFunction for Token {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||||
debug_assert_eq!(args.len(), 2usize,
|
|
||||||
"wrong arg count: {} != {}", args.len(), 2usize);
|
|
||||||
if args[0usize].is_read_only() {
|
if args[0usize].is_read_only() {
|
||||||
return Err(Box::new(
|
return Err(Box::new(
|
||||||
EvalAltResult::ErrorAssignmentToConstant("x".to_string(), Position::NONE)
|
EvalAltResult::ErrorAssignmentToConstant("x".to_string(), Position::NONE)
|
||||||
@ -577,26 +503,9 @@ mod generate_tests {
|
|||||||
|
|
||||||
#[inline(always)] fn is_method_call(&self) -> bool { true }
|
#[inline(always)] fn is_method_call(&self) -> bool { true }
|
||||||
#[inline(always)] fn is_variadic(&self) -> bool { false }
|
#[inline(always)] fn is_variadic(&self) -> bool { false }
|
||||||
#[inline(always)] fn clone_boxed(&self) -> Box<dyn PluginFunction> { Box::new(Token()) }
|
|
||||||
#[inline(always)] fn param_names(&self) -> Box<[&'static str]> {
|
|
||||||
new_vec!["x: &mut usize", "y: usize", "()"].into_boxed_slice()
|
|
||||||
}
|
|
||||||
#[inline(always)] fn input_types(&self) -> Box<[TypeId]> {
|
|
||||||
new_vec![TypeId::of::<usize>(),
|
|
||||||
TypeId::of::<usize>()].into_boxed_slice()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[inline(always)] pub fn token_callable() -> CallableFunction {
|
|
||||||
Token().into()
|
|
||||||
}
|
|
||||||
#[inline(always)] pub fn token_param_names() -> Box<[&'static str]> {
|
|
||||||
Token().param_names()
|
|
||||||
}
|
|
||||||
#[inline(always)] pub fn token_input_types() -> Box<[TypeId]> {
|
|
||||||
Token().input_types()
|
|
||||||
}
|
}
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
#[inline(always)] pub fn dynamic_result_fn(x: &mut usize, y: usize) -> Result<Dynamic, Box<EvalAltResult> > {
|
#[inline(always)] pub fn dynamic_result_fn(x: &mut usize, y: usize) -> RhaiResult {
|
||||||
Ok(Dynamic::from(increment(x, y)))
|
Ok(Dynamic::from(increment(x, y)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -617,37 +526,23 @@ mod generate_tests {
|
|||||||
#[automatically_derived]
|
#[automatically_derived]
|
||||||
pub mod rhai_fn_special_print {
|
pub mod rhai_fn_special_print {
|
||||||
use super::*;
|
use super::*;
|
||||||
struct Token();
|
pub struct Token();
|
||||||
|
impl Token {
|
||||||
|
pub const PARAM_NAMES: &'static [&'static str] = &["message: &str", "()"];
|
||||||
|
#[inline(always)] pub fn param_types() -> [TypeId; 1usize] { [TypeId::of::<ImmutableString>()] }
|
||||||
|
}
|
||||||
impl PluginFunction for Token {
|
impl PluginFunction for Token {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||||
debug_assert_eq!(args.len(), 1usize,
|
|
||||||
"wrong arg count: {} != {}", args.len(), 1usize);
|
|
||||||
let arg0 = mem::take(args[0usize]).take_immutable_string().unwrap();
|
let arg0 = mem::take(args[0usize]).take_immutable_string().unwrap();
|
||||||
Ok(Dynamic::from(special_print(&arg0)))
|
Ok(Dynamic::from(special_print(&arg0)))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)] fn is_method_call(&self) -> bool { false }
|
#[inline(always)] fn is_method_call(&self) -> bool { false }
|
||||||
#[inline(always)] fn is_variadic(&self) -> bool { false }
|
#[inline(always)] fn is_variadic(&self) -> bool { false }
|
||||||
#[inline(always)] fn clone_boxed(&self) -> Box<dyn PluginFunction> { Box::new(Token()) }
|
|
||||||
#[inline(always)] fn param_names(&self) -> Box<[&'static str]> {
|
|
||||||
new_vec!["message: &str", "()"].into_boxed_slice()
|
|
||||||
}
|
|
||||||
#[inline(always)] fn input_types(&self) -> Box<[TypeId]> {
|
|
||||||
new_vec![TypeId::of::<ImmutableString>()].into_boxed_slice()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[inline(always)] pub fn token_callable() -> CallableFunction {
|
|
||||||
Token().into()
|
|
||||||
}
|
|
||||||
#[inline(always)] pub fn token_param_names() -> Box<[&'static str]> {
|
|
||||||
Token().param_names()
|
|
||||||
}
|
|
||||||
#[inline(always)] pub fn token_input_types() -> Box<[TypeId]> {
|
|
||||||
Token().input_types()
|
|
||||||
}
|
}
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
#[inline(always)] pub fn dynamic_result_fn(message: &str) -> Result<Dynamic, Box<EvalAltResult> > {
|
#[inline(always)] pub fn dynamic_result_fn(message: &str) -> RhaiResult {
|
||||||
Ok(Dynamic::from(special_print(message)))
|
Ok(Dynamic::from(special_print(message)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -15,7 +15,7 @@ help: consider importing one of these items
|
|||||||
|
|
|
|
||||||
11 | use core::fmt::Pointer;
|
11 | use core::fmt::Pointer;
|
||||||
|
|
|
|
||||||
11 | use crate::new_vec::fmt::Pointer;
|
11 | use crate::mem::fmt::Pointer;
|
||||||
|
|
|
|
||||||
11 | use std::fmt::Pointer;
|
11 | use std::fmt::Pointer;
|
||||||
|
|
|
|
||||||
|
@ -21,7 +21,7 @@ fn count_string_bytes(s: &str) -> INT {
|
|||||||
|
|
||||||
/// This version uses `ImmutableString` and `&str`.
|
/// This version uses `ImmutableString` and `&str`.
|
||||||
fn find_substring(s: ImmutableString, sub: &str) -> INT {
|
fn find_substring(s: ImmutableString, sub: &str) -> INT {
|
||||||
s.as_str().find(sub).map(|x| x as INT).unwrap_or(-1)
|
s.find(sub).map(|x| x as INT).unwrap_or(-1)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> Result<(), Box<EvalAltResult>> {
|
fn main() -> Result<(), Box<EvalAltResult>> {
|
||||||
|
46
src/ast.rs
46
src/ast.rs
@ -6,7 +6,7 @@ use crate::module::NamespaceRef;
|
|||||||
use crate::stdlib::{
|
use crate::stdlib::{
|
||||||
borrow::Cow,
|
borrow::Cow,
|
||||||
boxed::Box,
|
boxed::Box,
|
||||||
collections::BTreeMap,
|
collections::{BTreeMap, BTreeSet},
|
||||||
fmt,
|
fmt,
|
||||||
hash::Hash,
|
hash::Hash,
|
||||||
num::NonZeroUsize,
|
num::NonZeroUsize,
|
||||||
@ -26,9 +26,6 @@ use crate::{stdlib::str::FromStr, FLOAT};
|
|||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
use crate::Array;
|
use crate::Array;
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
use crate::Map;
|
|
||||||
|
|
||||||
/// A type representing the access mode of a function.
|
/// A type representing the access mode of a function.
|
||||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
|
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
|
||||||
pub enum FnAccess {
|
pub enum FnAccess {
|
||||||
@ -61,7 +58,7 @@ pub struct ScriptFnDef {
|
|||||||
pub params: StaticVec<ImmutableString>,
|
pub params: StaticVec<ImmutableString>,
|
||||||
/// Access to external variables.
|
/// Access to external variables.
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
pub externals: StaticVec<ImmutableString>,
|
pub externals: BTreeSet<ImmutableString>,
|
||||||
/// Function doc-comments (if any).
|
/// Function doc-comments (if any).
|
||||||
pub comments: StaticVec<String>,
|
pub comments: StaticVec<String>,
|
||||||
}
|
}
|
||||||
@ -87,6 +84,7 @@ impl fmt::Display for ScriptFnDef {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// A type containing the metadata of a script-defined function.
|
/// A type containing the metadata of a script-defined function.
|
||||||
|
/// Not available under `no_function`.
|
||||||
///
|
///
|
||||||
/// Created by [`AST::iter_functions`].
|
/// Created by [`AST::iter_functions`].
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
@ -263,6 +261,7 @@ impl AST {
|
|||||||
}
|
}
|
||||||
/// _(INTERNALS)_ Get the internal shared [`Module`] containing all script-defined functions.
|
/// _(INTERNALS)_ Get the internal shared [`Module`] containing all script-defined functions.
|
||||||
/// Exported under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
|
/// Not available under `no_function`.
|
||||||
#[cfg(feature = "internals")]
|
#[cfg(feature = "internals")]
|
||||||
#[deprecated = "this method is volatile and may change"]
|
#[deprecated = "this method is volatile and may change"]
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
@ -279,6 +278,7 @@ impl AST {
|
|||||||
}
|
}
|
||||||
/// _(INTERNALS)_ Get the internal [`Module`] containing all script-defined functions.
|
/// _(INTERNALS)_ Get the internal [`Module`] containing all script-defined functions.
|
||||||
/// Exported under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
|
/// Not available under `no_function`.
|
||||||
#[cfg(feature = "internals")]
|
#[cfg(feature = "internals")]
|
||||||
#[deprecated = "this method is volatile and may change"]
|
#[deprecated = "this method is volatile and may change"]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@ -314,10 +314,9 @@ impl AST {
|
|||||||
}
|
}
|
||||||
/// Clone the [`AST`]'s functions into a new [`AST`].
|
/// Clone the [`AST`]'s functions into a new [`AST`].
|
||||||
/// No statements are cloned.
|
/// No statements are cloned.
|
||||||
|
/// Not available under `no_function`.
|
||||||
///
|
///
|
||||||
/// This operation is cheap because functions are shared.
|
/// This operation is cheap because functions are shared.
|
||||||
///
|
|
||||||
/// Not available under `no_function`.
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn clone_functions_only(&self) -> Self {
|
pub fn clone_functions_only(&self) -> Self {
|
||||||
@ -325,10 +324,9 @@ impl AST {
|
|||||||
}
|
}
|
||||||
/// Clone the [`AST`]'s functions into a new [`AST`] based on a filter predicate.
|
/// Clone the [`AST`]'s functions into a new [`AST`] based on a filter predicate.
|
||||||
/// No statements are cloned.
|
/// No statements are cloned.
|
||||||
|
/// Not available under `no_function`.
|
||||||
///
|
///
|
||||||
/// This operation is cheap because functions are shared.
|
/// This operation is cheap because functions are shared.
|
||||||
///
|
|
||||||
/// Not available under `no_function`.
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn clone_functions_only_filtered(
|
pub fn clone_functions_only_filtered(
|
||||||
@ -357,8 +355,8 @@ impl AST {
|
|||||||
resolver: self.resolver.clone(),
|
resolver: self.resolver.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Merge two [`AST`] into one. Both [`AST`]'s are untouched and a new, merged, version
|
/// Merge two [`AST`] into one. Both [`AST`]'s are untouched and a new, merged,
|
||||||
/// is returned.
|
/// version is returned.
|
||||||
///
|
///
|
||||||
/// Statements in the second [`AST`] are simply appended to the end of the first _without any processing_.
|
/// Statements in the second [`AST`] are simply appended to the end of the first _without any processing_.
|
||||||
/// Thus, the return value of the first [`AST`] (if using expression-statement syntax) is buried.
|
/// Thus, the return value of the first [`AST`] (if using expression-statement syntax) is buried.
|
||||||
@ -1288,7 +1286,7 @@ pub struct BinaryExpr {
|
|||||||
pub struct OpAssignment {
|
pub struct OpAssignment {
|
||||||
pub hash_op_assign: u64,
|
pub hash_op_assign: u64,
|
||||||
pub hash_op: u64,
|
pub hash_op: u64,
|
||||||
pub op: Cow<'static, str>,
|
pub op: &'static str,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// _(INTERNALS)_ An set of function call hashes.
|
/// _(INTERNALS)_ An set of function call hashes.
|
||||||
@ -1516,7 +1514,7 @@ impl FloatWrapper {
|
|||||||
#[derive(Debug, Clone, Hash)]
|
#[derive(Debug, Clone, Hash)]
|
||||||
pub enum Expr {
|
pub enum Expr {
|
||||||
/// Dynamic constant.
|
/// Dynamic constant.
|
||||||
/// Used to hold either an [`Array`] or [`Map`] literal for quick cloning.
|
/// Used to hold either an [`Array`] or [`Map`][crate::Map] literal for quick cloning.
|
||||||
/// All other primitive data types should use the appropriate variants for better speed.
|
/// All other primitive data types should use the appropriate variants for better speed.
|
||||||
DynamicConstant(Box<Dynamic>, Position),
|
DynamicConstant(Box<Dynamic>, Position),
|
||||||
/// Boolean constant.
|
/// Boolean constant.
|
||||||
@ -1535,7 +1533,10 @@ pub enum Expr {
|
|||||||
/// [ expr, ... ]
|
/// [ expr, ... ]
|
||||||
Array(Box<StaticVec<Expr>>, Position),
|
Array(Box<StaticVec<Expr>>, Position),
|
||||||
/// #{ name:expr, ... }
|
/// #{ name:expr, ... }
|
||||||
Map(Box<StaticVec<(Ident, Expr)>>, Position),
|
Map(
|
||||||
|
Box<(StaticVec<(Ident, Expr)>, BTreeMap<ImmutableString, Dynamic>)>,
|
||||||
|
Position,
|
||||||
|
),
|
||||||
/// ()
|
/// ()
|
||||||
Unit(Position),
|
Unit(Position),
|
||||||
/// Variable access - (optional index, optional (hash, modules), variable name)
|
/// Variable access - (optional index, optional (hash, modules), variable name)
|
||||||
@ -1594,11 +1595,10 @@ impl Expr {
|
|||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Self::Map(x, _) if self.is_constant() => {
|
Self::Map(x, _) if self.is_constant() => {
|
||||||
let mut map = Map::new();
|
let mut map = x.1.clone();
|
||||||
map.extend(
|
x.0.iter().for_each(|(k, v)| {
|
||||||
x.iter()
|
*map.get_mut(&k.name).unwrap() = v.get_constant_value().unwrap()
|
||||||
.map(|(k, v)| (k.name.clone(), v.get_constant_value().unwrap())),
|
});
|
||||||
);
|
|
||||||
Dynamic(Union::Map(Box::new(map), AccessMode::ReadOnly))
|
Dynamic(Union::Map(Box::new(map), AccessMode::ReadOnly))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1677,7 +1677,7 @@ impl Expr {
|
|||||||
match self {
|
match self {
|
||||||
Self::Array(x, _) => x.iter().all(Self::is_pure),
|
Self::Array(x, _) => x.iter().all(Self::is_pure),
|
||||||
|
|
||||||
Self::Map(x, _) => x.iter().map(|(_, v)| v).all(Self::is_pure),
|
Self::Map(x, _) => x.0.iter().map(|(_, v)| v).all(Self::is_pure),
|
||||||
|
|
||||||
Self::Index(x, _) | Self::And(x, _) | Self::Or(x, _) => {
|
Self::Index(x, _) | Self::And(x, _) | Self::Or(x, _) => {
|
||||||
x.lhs.is_pure() && x.rhs.is_pure()
|
x.lhs.is_pure() && x.rhs.is_pure()
|
||||||
@ -1717,7 +1717,7 @@ impl Expr {
|
|||||||
Self::Array(x, _) => x.iter().all(Self::is_constant),
|
Self::Array(x, _) => x.iter().all(Self::is_constant),
|
||||||
|
|
||||||
// An map literal is constant if all items are constant
|
// An map literal is constant if all items are constant
|
||||||
Self::Map(x, _) => x.iter().map(|(_, expr)| expr).all(Self::is_constant),
|
Self::Map(x, _) => x.0.iter().map(|(_, expr)| expr).all(Self::is_constant),
|
||||||
|
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
@ -1804,7 +1804,7 @@ impl Expr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Self::Map(x, _) => {
|
Self::Map(x, _) => {
|
||||||
for (_, e) in x.as_ref() {
|
for (_, e) in &x.0 {
|
||||||
if !e.walk(path, on_node) {
|
if !e.walk(path, on_node) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -1856,7 +1856,7 @@ mod tests {
|
|||||||
assert_eq!(size_of::<Option<ast::Expr>>(), 16);
|
assert_eq!(size_of::<Option<ast::Expr>>(), 16);
|
||||||
assert_eq!(size_of::<ast::Stmt>(), 40);
|
assert_eq!(size_of::<ast::Stmt>(), 40);
|
||||||
assert_eq!(size_of::<Option<ast::Stmt>>(), 40);
|
assert_eq!(size_of::<Option<ast::Stmt>>(), 40);
|
||||||
assert_eq!(size_of::<FnPtr>(), 32);
|
assert_eq!(size_of::<FnPtr>(), 80);
|
||||||
assert_eq!(size_of::<Scope>(), 288);
|
assert_eq!(size_of::<Scope>(), 288);
|
||||||
assert_eq!(size_of::<LexError>(), 56);
|
assert_eq!(size_of::<LexError>(), 56);
|
||||||
assert_eq!(size_of::<ParseError>(), 16);
|
assert_eq!(size_of::<ParseError>(), 16);
|
||||||
|
@ -45,6 +45,7 @@ fn print_help() {
|
|||||||
println!("help => print this help");
|
println!("help => print this help");
|
||||||
println!("quit, exit => quit");
|
println!("quit, exit => quit");
|
||||||
println!("scope => print all variables in the scope");
|
println!("scope => print all variables in the scope");
|
||||||
|
#[cfg(feature = "metadata")]
|
||||||
println!("functions => print all functions defined");
|
println!("functions => print all functions defined");
|
||||||
println!("ast => print the last AST (optimized)");
|
println!("ast => print the last AST (optimized)");
|
||||||
println!("astu => print the last raw, un-optimized AST");
|
println!("astu => print the last raw, un-optimized AST");
|
||||||
@ -202,6 +203,7 @@ fn main() {
|
|||||||
println!("{:#?}\n", ast);
|
println!("{:#?}\n", ast);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
#[cfg(feature = "metadata")]
|
||||||
"functions" => {
|
"functions" => {
|
||||||
// print a list of all registered functions
|
// print a list of all registered functions
|
||||||
engine
|
engine
|
||||||
|
@ -1349,7 +1349,7 @@ impl Dynamic {
|
|||||||
}
|
}
|
||||||
if TypeId::of::<T>() == TypeId::of::<String>() {
|
if TypeId::of::<T>() == TypeId::of::<String>() {
|
||||||
return match &self.0 {
|
return match &self.0 {
|
||||||
Union::Str(value, _) => <dyn Any>::downcast_ref::<T>(value.as_ref()),
|
Union::Str(value, _) => <dyn Any>::downcast_ref::<T>(value.as_ref() as &String),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -75,7 +75,7 @@ impl Imports {
|
|||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.rev()
|
.rev()
|
||||||
.find_map(|(i, key)| if key.as_str() == name { Some(i) } else { None })
|
.find_map(|(i, key)| if *key == name { Some(i) } else { None })
|
||||||
}
|
}
|
||||||
/// Push an imported [modules][Module] onto the stack.
|
/// Push an imported [modules][Module] onto the stack.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@ -342,13 +342,13 @@ impl<'a> Target<'a> {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn is<T: Variant + Clone>(&self) -> bool {
|
pub fn is<T: Variant + Clone>(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Target::Ref(r) => r.is::<T>(),
|
Self::Ref(r) => r.is::<T>(),
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Target::LockGuard((r, _)) => r.is::<T>(),
|
Self::LockGuard((r, _)) => r.is::<T>(),
|
||||||
Target::Value(r) => r.is::<T>(),
|
Self::Value(r) => r.is::<T>(),
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Target::StringChar(_, _, _) => TypeId::of::<T>() == TypeId::of::<char>(),
|
Self::StringChar(_, _, _) => TypeId::of::<T>() == TypeId::of::<char>(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Get the value of the `Target` as a `Dynamic`, cloning a referenced value if necessary.
|
/// Get the value of the `Target` as a `Dynamic`, cloning a referenced value if necessary.
|
||||||
@ -996,12 +996,12 @@ impl Engine {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Check if the variable is `this`
|
// Check if the variable is `this`
|
||||||
if name.as_str() == KEYWORD_THIS {
|
if *name == KEYWORD_THIS {
|
||||||
if let Some(val) = this_ptr {
|
return if let Some(val) = this_ptr {
|
||||||
return Ok(((*val).into(), *pos));
|
Ok(((*val).into(), *pos))
|
||||||
} else {
|
} else {
|
||||||
return EvalAltResult::ErrorUnboundThis(*pos).into();
|
EvalAltResult::ErrorUnboundThis(*pos).into()
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if it is directly indexed
|
// Check if it is directly indexed
|
||||||
@ -1056,7 +1056,7 @@ impl Engine {
|
|||||||
idx_values: &mut StaticVec<ChainArgument>,
|
idx_values: &mut StaticVec<ChainArgument>,
|
||||||
chain_type: ChainType,
|
chain_type: ChainType,
|
||||||
level: usize,
|
level: usize,
|
||||||
new_val: Option<((Dynamic, Position), (&Option<OpAssignment>, Position))>,
|
new_val: Option<((Dynamic, Position), (Option<OpAssignment>, Position))>,
|
||||||
) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
|
) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
|
||||||
assert!(chain_type != ChainType::NonChaining);
|
assert!(chain_type != ChainType::NonChaining);
|
||||||
|
|
||||||
@ -1097,6 +1097,8 @@ impl Engine {
|
|||||||
// xxx[rhs] op= new_val
|
// xxx[rhs] op= new_val
|
||||||
_ if new_val.is_some() => {
|
_ if new_val.is_some() => {
|
||||||
let idx_val = idx_val.as_index_value();
|
let idx_val = idx_val.as_index_value();
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
let mut idx_val2 = idx_val.clone();
|
let mut idx_val2 = idx_val.clone();
|
||||||
|
|
||||||
// `call_setter` is introduced to bypass double mutable borrowing of target
|
// `call_setter` is introduced to bypass double mutable borrowing of target
|
||||||
@ -1357,7 +1359,7 @@ impl Engine {
|
|||||||
this_ptr: &mut Option<&mut Dynamic>,
|
this_ptr: &mut Option<&mut Dynamic>,
|
||||||
expr: &Expr,
|
expr: &Expr,
|
||||||
level: usize,
|
level: usize,
|
||||||
new_val: Option<((Dynamic, Position), (&Option<OpAssignment>, Position))>,
|
new_val: Option<((Dynamic, Position), (Option<OpAssignment>, Position))>,
|
||||||
) -> RhaiResult {
|
) -> RhaiResult {
|
||||||
let (crate::ast::BinaryExpr { lhs, rhs }, chain_type, op_pos) = match expr {
|
let (crate::ast::BinaryExpr { lhs, rhs }, chain_type, op_pos) = match expr {
|
||||||
Expr::Index(x, pos) => (x.as_ref(), ChainType::Index, *pos),
|
Expr::Index(x, pos) => (x.as_ref(), ChainType::Index, *pos),
|
||||||
@ -1524,7 +1526,7 @@ impl Engine {
|
|||||||
state: &mut State,
|
state: &mut State,
|
||||||
_lib: &[&Module],
|
_lib: &[&Module],
|
||||||
target: &'t mut Dynamic,
|
target: &'t mut Dynamic,
|
||||||
idx: Dynamic,
|
mut idx: Dynamic,
|
||||||
idx_pos: Position,
|
idx_pos: Position,
|
||||||
_create: bool,
|
_create: bool,
|
||||||
_is_ref: bool,
|
_is_ref: bool,
|
||||||
@ -1557,21 +1559,18 @@ impl Engine {
|
|||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Dynamic(Union::Map(map, _)) => {
|
Dynamic(Union::Map(map, _)) => {
|
||||||
// val_map[idx]
|
// val_map[idx]
|
||||||
Ok(if _create {
|
let index = &*idx.read_lock::<ImmutableString>().ok_or_else(|| {
|
||||||
let index = idx.take_immutable_string().map_err(|err| {
|
self.make_type_mismatch_err::<ImmutableString>(idx.type_name(), idx_pos)
|
||||||
self.make_type_mismatch_err::<ImmutableString>(err, idx_pos)
|
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
map.entry(index).or_insert_with(Default::default).into()
|
if _create && !map.contains_key(index) {
|
||||||
} else {
|
map.insert(index.clone(), Default::default());
|
||||||
let index = idx.read_lock::<ImmutableString>().ok_or_else(|| {
|
}
|
||||||
self.make_type_mismatch_err::<ImmutableString>("", idx_pos)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
map.get_mut(&*index)
|
Ok(map
|
||||||
|
.get_mut(index)
|
||||||
.map(Target::from)
|
.map(Target::from)
|
||||||
.unwrap_or_else(|| Target::from(()))
|
.unwrap_or_else(|| Target::from(())))
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
@ -1596,7 +1595,6 @@ impl Engine {
|
|||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
_ if _indexers => {
|
_ if _indexers => {
|
||||||
let type_name = target.type_name();
|
let type_name = target.type_name();
|
||||||
let mut idx = idx;
|
|
||||||
let args = &mut [target, &mut idx];
|
let args = &mut [target, &mut idx];
|
||||||
let hash_get = FnCallHash::from_native(calc_fn_hash(empty(), FN_IDX_GET, 2));
|
let hash_get = FnCallHash::from_native(calc_fn_hash(empty(), FN_IDX_GET, 2));
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
@ -1686,13 +1684,11 @@ impl Engine {
|
|||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Expr::Map(x, _) => {
|
Expr::Map(x, _) => {
|
||||||
let mut map = Map::new();
|
let mut map = x.1.clone();
|
||||||
for (Ident { name: key, .. }, expr) in x.as_ref() {
|
for (Ident { name: key, .. }, expr) in &x.0 {
|
||||||
map.insert(
|
*map.get_mut(key).unwrap() = self
|
||||||
key.clone(),
|
.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?
|
||||||
self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?
|
.flatten();
|
||||||
.flatten(),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
Ok(Dynamic(Union::Map(Box::new(map), AccessMode::ReadWrite)))
|
Ok(Dynamic(Union::Map(Box::new(map), AccessMode::ReadWrite)))
|
||||||
}
|
}
|
||||||
@ -1865,7 +1861,7 @@ impl Engine {
|
|||||||
mods: &mut Imports,
|
mods: &mut Imports,
|
||||||
state: &mut State,
|
state: &mut State,
|
||||||
lib: &[&Module],
|
lib: &[&Module],
|
||||||
op_info: &Option<OpAssignment>,
|
op_info: Option<OpAssignment>,
|
||||||
op_pos: Position,
|
op_pos: Position,
|
||||||
mut target: Target,
|
mut target: Target,
|
||||||
mut new_value: Dynamic,
|
mut new_value: Dynamic,
|
||||||
@ -1891,20 +1887,19 @@ impl Engine {
|
|||||||
lhs_ptr_inner = target.as_mut();
|
lhs_ptr_inner = target.as_mut();
|
||||||
}
|
}
|
||||||
|
|
||||||
let hash = *hash_op_assign;
|
let hash = hash_op_assign;
|
||||||
let args = &mut [lhs_ptr_inner, &mut new_value];
|
let args = &mut [lhs_ptr_inner, &mut new_value];
|
||||||
|
|
||||||
match self.call_native_fn(mods, state, lib, op, hash, args, true, true, op_pos) {
|
match self.call_native_fn(mods, state, lib, op, hash, args, true, true, op_pos) {
|
||||||
Ok(_) => (),
|
Ok(_) => (),
|
||||||
Err(err) if matches!(err.as_ref(), EvalAltResult::ErrorFunctionNotFound(f, _) if f.starts_with(op.as_ref())) =>
|
Err(err) if matches!(err.as_ref(), EvalAltResult::ErrorFunctionNotFound(f, _) if f.starts_with(op)) =>
|
||||||
{
|
{
|
||||||
// Expand to `var = var op rhs`
|
// Expand to `var = var op rhs`
|
||||||
let op = &op[..op.len() - 1]; // extract operator without =
|
let op = &op[..op.len() - 1]; // extract operator without =
|
||||||
|
|
||||||
// Run function
|
// Run function
|
||||||
let (value, _) = self.call_native_fn(
|
let (value, _) = self
|
||||||
mods, state, lib, op, *hash_op, args, true, false, op_pos,
|
.call_native_fn(mods, state, lib, op, hash_op, args, true, false, op_pos)?;
|
||||||
)?;
|
|
||||||
|
|
||||||
*args[0] = value.flatten();
|
*args[0] = value.flatten();
|
||||||
}
|
}
|
||||||
@ -1977,7 +1972,7 @@ impl Engine {
|
|||||||
mods,
|
mods,
|
||||||
state,
|
state,
|
||||||
lib,
|
lib,
|
||||||
op_info,
|
op_info.clone(),
|
||||||
*op_pos,
|
*op_pos,
|
||||||
lhs_ptr,
|
lhs_ptr,
|
||||||
rhs_val,
|
rhs_val,
|
||||||
@ -1993,7 +1988,7 @@ impl Engine {
|
|||||||
let rhs_val = self
|
let rhs_val = self
|
||||||
.eval_expr(scope, mods, state, lib, this_ptr, rhs_expr, level)?
|
.eval_expr(scope, mods, state, lib, this_ptr, rhs_expr, level)?
|
||||||
.flatten();
|
.flatten();
|
||||||
let _new_val = Some(((rhs_val, rhs_expr.position()), (op_info, *op_pos)));
|
let _new_val = Some(((rhs_val, rhs_expr.position()), (op_info.clone(), *op_pos)));
|
||||||
|
|
||||||
// Must be either `var[index] op= val` or `var.prop op= val`
|
// Must be either `var[index] op= val` or `var.prop op= val`
|
||||||
match lhs_expr {
|
match lhs_expr {
|
||||||
|
@ -9,12 +9,11 @@ use crate::stdlib::{
|
|||||||
any::{type_name, TypeId},
|
any::{type_name, TypeId},
|
||||||
boxed::Box,
|
boxed::Box,
|
||||||
format,
|
format,
|
||||||
string::{String, ToString},
|
string::String,
|
||||||
vec::Vec,
|
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
scope::Scope, Dynamic, Engine, EvalAltResult, FnAccess, FnNamespace, Module, NativeCallContext,
|
scope::Scope, Dynamic, Engine, EvalAltResult, FnAccess, FnNamespace, ImmutableString, Module,
|
||||||
ParseError, Position, RhaiResult, Shared, StaticVec, AST,
|
NativeCallContext, ParseError, Position, RhaiResult, Shared, AST,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
@ -52,26 +51,36 @@ impl Engine {
|
|||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn register_fn<A, F>(&mut self, name: &str, func: F) -> &mut Self
|
pub fn register_fn<N, A, F>(&mut self, name: N, func: F) -> &mut Self
|
||||||
where
|
where
|
||||||
|
N: AsRef<str> + Into<ImmutableString>,
|
||||||
F: RegisterNativeFunction<A, ()>,
|
F: RegisterNativeFunction<A, ()>,
|
||||||
{
|
{
|
||||||
let param_types = F::param_types();
|
let param_types = F::param_types();
|
||||||
let mut param_type_names: StaticVec<_> = F::param_names()
|
|
||||||
|
#[cfg(feature = "metadata")]
|
||||||
|
let mut param_type_names: crate::StaticVec<_> = F::param_names()
|
||||||
.iter()
|
.iter()
|
||||||
.map(|ty| format!("_: {}", self.map_type_name(ty)))
|
.map(|ty| format!("_: {}", self.map_type_name(ty)))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
#[cfg(feature = "metadata")]
|
||||||
if F::return_type() != TypeId::of::<()>() {
|
if F::return_type() != TypeId::of::<()>() {
|
||||||
param_type_names.push(self.map_type_name(F::return_type_name()).to_string());
|
param_type_names.push(self.map_type_name(F::return_type_name()).into());
|
||||||
}
|
}
|
||||||
let param_type_names: StaticVec<_> =
|
|
||||||
param_type_names.iter().map(|ty| ty.as_str()).collect();
|
#[cfg(feature = "metadata")]
|
||||||
|
let param_type_names: Option<crate::StaticVec<_>> =
|
||||||
|
Some(param_type_names.iter().map(|ty| ty.as_str()).collect());
|
||||||
|
|
||||||
|
#[cfg(not(feature = "metadata"))]
|
||||||
|
let param_type_names: Option<[&str; 0]> = None;
|
||||||
|
|
||||||
self.global_namespace.set_fn(
|
self.global_namespace.set_fn(
|
||||||
name,
|
name,
|
||||||
FnNamespace::Global,
|
FnNamespace::Global,
|
||||||
FnAccess::Public,
|
FnAccess::Public,
|
||||||
Some(¶m_type_names),
|
param_type_names.as_ref().map(|v| v.as_ref()),
|
||||||
¶m_types,
|
¶m_types,
|
||||||
func.into_callable_function(),
|
func.into_callable_function(),
|
||||||
);
|
);
|
||||||
@ -102,24 +111,34 @@ impl Engine {
|
|||||||
/// .expect_err("expecting division by zero error!");
|
/// .expect_err("expecting division by zero error!");
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn register_result_fn<A, F, R>(&mut self, name: &str, func: F) -> &mut Self
|
pub fn register_result_fn<N, A, F, R>(&mut self, name: N, func: F) -> &mut Self
|
||||||
where
|
where
|
||||||
|
N: AsRef<str> + Into<ImmutableString>,
|
||||||
F: RegisterNativeFunction<A, Result<R, Box<EvalAltResult>>>,
|
F: RegisterNativeFunction<A, Result<R, Box<EvalAltResult>>>,
|
||||||
{
|
{
|
||||||
let param_types = F::param_types();
|
let param_types = F::param_types();
|
||||||
let mut param_type_names: StaticVec<_> = F::param_names()
|
|
||||||
|
#[cfg(feature = "metadata")]
|
||||||
|
let param_type_names: crate::StaticVec<_> = F::param_names()
|
||||||
.iter()
|
.iter()
|
||||||
.map(|ty| format!("_: {}", self.map_type_name(ty)))
|
.map(|ty| format!("_: {}", self.map_type_name(ty)))
|
||||||
|
.chain(crate::stdlib::iter::once(
|
||||||
|
self.map_type_name(F::return_type_name()).into(),
|
||||||
|
))
|
||||||
.collect();
|
.collect();
|
||||||
param_type_names.push(self.map_type_name(F::return_type_name()).to_string());
|
|
||||||
let param_type_names: StaticVec<&str> =
|
#[cfg(feature = "metadata")]
|
||||||
param_type_names.iter().map(|ty| ty.as_str()).collect();
|
let param_type_names: Option<crate::StaticVec<_>> =
|
||||||
|
Some(param_type_names.iter().map(|ty| ty.as_str()).collect());
|
||||||
|
|
||||||
|
#[cfg(not(feature = "metadata"))]
|
||||||
|
let param_type_names: Option<[&str; 0]> = None;
|
||||||
|
|
||||||
self.global_namespace.set_fn(
|
self.global_namespace.set_fn(
|
||||||
name,
|
name,
|
||||||
FnNamespace::Global,
|
FnNamespace::Global,
|
||||||
FnAccess::Public,
|
FnAccess::Public,
|
||||||
Some(¶m_type_names),
|
param_type_names.as_ref().map(|v| v.as_ref()),
|
||||||
¶m_types,
|
¶m_types,
|
||||||
func.into_callable_function(),
|
func.into_callable_function(),
|
||||||
);
|
);
|
||||||
@ -142,14 +161,18 @@ impl Engine {
|
|||||||
/// To access the first mutable parameter, use `args.get_mut(0).unwrap()`
|
/// To access the first mutable parameter, use `args.get_mut(0).unwrap()`
|
||||||
#[deprecated = "this function is volatile and may change"]
|
#[deprecated = "this function is volatile and may change"]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn register_raw_fn<T: Variant + Clone>(
|
pub fn register_raw_fn<N, T>(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: &str,
|
name: N,
|
||||||
arg_types: &[TypeId],
|
arg_types: &[TypeId],
|
||||||
func: impl Fn(NativeCallContext, &mut FnCallArgs) -> Result<T, Box<EvalAltResult>>
|
func: impl Fn(NativeCallContext, &mut FnCallArgs) -> Result<T, Box<EvalAltResult>>
|
||||||
+ SendSync
|
+ SendSync
|
||||||
+ 'static,
|
+ 'static,
|
||||||
) -> &mut Self {
|
) -> &mut Self
|
||||||
|
where
|
||||||
|
N: AsRef<str> + Into<ImmutableString>,
|
||||||
|
T: Variant + Clone,
|
||||||
|
{
|
||||||
self.global_namespace.set_raw_fn(
|
self.global_namespace.set_raw_fn(
|
||||||
name,
|
name,
|
||||||
FnNamespace::Global,
|
FnNamespace::Global,
|
||||||
@ -878,25 +901,29 @@ impl Engine {
|
|||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
pub fn register_static_module(&mut self, name: &str, module: Shared<Module>) -> &mut Self {
|
pub fn register_static_module(
|
||||||
|
&mut self,
|
||||||
|
name: impl AsRef<str> + Into<ImmutableString>,
|
||||||
|
module: Shared<Module>,
|
||||||
|
) -> &mut Self {
|
||||||
fn register_static_module_raw(
|
fn register_static_module_raw(
|
||||||
root: &mut crate::stdlib::collections::BTreeMap<crate::ImmutableString, Shared<Module>>,
|
root: &mut crate::stdlib::collections::BTreeMap<crate::ImmutableString, Shared<Module>>,
|
||||||
name: &str,
|
name: impl AsRef<str> + Into<ImmutableString>,
|
||||||
module: Shared<Module>,
|
module: Shared<Module>,
|
||||||
) {
|
) {
|
||||||
let separator = crate::token::Token::DoubleColon.syntax();
|
let separator = crate::token::Token::DoubleColon.syntax();
|
||||||
|
|
||||||
if !name.contains(separator.as_ref()) {
|
if !name.as_ref().contains(separator.as_ref()) {
|
||||||
if !module.is_indexed() {
|
if !module.is_indexed() {
|
||||||
// Index the module (making a clone copy if necessary) if it is not indexed
|
// Index the module (making a clone copy if necessary) if it is not indexed
|
||||||
let mut module = crate::fn_native::shared_take_or_clone(module);
|
let mut module = crate::fn_native::shared_take_or_clone(module);
|
||||||
module.build_index();
|
module.build_index();
|
||||||
root.insert(name.trim().into(), module.into());
|
root.insert(name.into(), module.into());
|
||||||
} else {
|
} else {
|
||||||
root.insert(name.trim().into(), module);
|
root.insert(name.into(), module);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let mut iter = name.splitn(2, separator.as_ref());
|
let mut iter = name.as_ref().splitn(2, separator.as_ref());
|
||||||
let sub_module = iter.next().unwrap().trim();
|
let sub_module = iter.next().unwrap().trim();
|
||||||
let remainder = iter.next().unwrap().trim();
|
let remainder = iter.next().unwrap().trim();
|
||||||
|
|
||||||
@ -915,7 +942,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
register_static_module_raw(&mut self.global_sub_modules, name.as_ref(), module);
|
register_static_module_raw(&mut self.global_sub_modules, name, module);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -927,7 +954,11 @@ impl Engine {
|
|||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[deprecated = "use `register_static_module` instead"]
|
#[deprecated = "use `register_static_module` instead"]
|
||||||
pub fn register_module(&mut self, name: &str, module: impl Into<Shared<Module>>) -> &mut Self {
|
pub fn register_module(
|
||||||
|
&mut self,
|
||||||
|
name: impl AsRef<str> + Into<ImmutableString>,
|
||||||
|
module: impl Into<Shared<Module>>,
|
||||||
|
) -> &mut Self {
|
||||||
self.register_static_module(name, module.into())
|
self.register_static_module(name, module.into())
|
||||||
}
|
}
|
||||||
/// Compile a string into an [`AST`], which can be used later for evaluation.
|
/// Compile a string into an [`AST`], which can be used later for evaluation.
|
||||||
@ -1013,7 +1044,6 @@ impl Engine {
|
|||||||
fn_native::shared_take_or_clone,
|
fn_native::shared_take_or_clone,
|
||||||
module::resolvers::StaticModuleResolver,
|
module::resolvers::StaticModuleResolver,
|
||||||
stdlib::collections::BTreeSet,
|
stdlib::collections::BTreeSet,
|
||||||
ImmutableString,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
fn collect_imports(
|
fn collect_imports(
|
||||||
@ -1232,7 +1262,7 @@ impl Engine {
|
|||||||
Self::read_file(path).and_then(|contents| Ok(self.compile_with_scope(scope, &contents)?))
|
Self::read_file(path).and_then(|contents| Ok(self.compile_with_scope(scope, &contents)?))
|
||||||
}
|
}
|
||||||
/// Parse a JSON string into an [object map][`Map`].
|
/// Parse a JSON string into an [object map][`Map`].
|
||||||
/// This is a light-weight alternative to using, say, [`serde_json`][https://crates.io/crates/serde_json] to deserialize the JSON.
|
/// This is a light-weight alternative to using, say, [`serde_json`] to deserialize the JSON.
|
||||||
///
|
///
|
||||||
/// The JSON string must be an object hash. It cannot be a simple scalar value.
|
/// The JSON string must be an object hash. It cannot be a simple scalar value.
|
||||||
///
|
///
|
||||||
@ -1271,16 +1301,21 @@ impl Engine {
|
|||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
pub fn parse_json(&self, json: &str, has_null: bool) -> Result<Map, Box<EvalAltResult>> {
|
pub fn parse_json(
|
||||||
|
&self,
|
||||||
|
json: impl AsRef<str>,
|
||||||
|
has_null: bool,
|
||||||
|
) -> Result<Map, Box<EvalAltResult>> {
|
||||||
use crate::token::Token;
|
use crate::token::Token;
|
||||||
|
|
||||||
|
let json = json.as_ref();
|
||||||
let mut scope = Default::default();
|
let mut scope = Default::default();
|
||||||
|
|
||||||
// Trims the JSON string and add a '#' in front
|
// Trims the JSON string and add a '#' in front
|
||||||
let json_text = json.trim_start();
|
let json_text = json.trim_start();
|
||||||
let scripts = if json_text.starts_with(Token::MapStart.syntax().as_ref()) {
|
let scripts = if json_text.starts_with(Token::MapStart.keyword_syntax()) {
|
||||||
[json_text, ""]
|
[json_text, ""]
|
||||||
} else if json_text.starts_with(Token::LeftBrace.syntax().as_ref()) {
|
} else if json_text.starts_with(Token::LeftBrace.keyword_syntax()) {
|
||||||
["#", json_text]
|
["#", json_text]
|
||||||
} else {
|
} else {
|
||||||
return Err(crate::ParseErrorType::MissingToken(
|
return Err(crate::ParseErrorType::MissingToken(
|
||||||
@ -1765,7 +1800,7 @@ impl Engine {
|
|||||||
&self,
|
&self,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
ast: &AST,
|
ast: &AST,
|
||||||
name: &str,
|
name: impl AsRef<str>,
|
||||||
args: impl crate::fn_args::FuncArgs,
|
args: impl crate::fn_args::FuncArgs,
|
||||||
) -> Result<T, Box<EvalAltResult>> {
|
) -> Result<T, Box<EvalAltResult>> {
|
||||||
let mut arg_values: crate::StaticVec<_> = Default::default();
|
let mut arg_values: crate::StaticVec<_> = Default::default();
|
||||||
@ -1844,7 +1879,7 @@ impl Engine {
|
|||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
ast: &AST,
|
ast: &AST,
|
||||||
eval_ast: bool,
|
eval_ast: bool,
|
||||||
name: &str,
|
name: impl AsRef<str>,
|
||||||
mut this_ptr: Option<&mut Dynamic>,
|
mut this_ptr: Option<&mut Dynamic>,
|
||||||
mut arg_values: impl AsMut<[Dynamic]>,
|
mut arg_values: impl AsMut<[Dynamic]>,
|
||||||
) -> RhaiResult {
|
) -> RhaiResult {
|
||||||
@ -1867,7 +1902,7 @@ impl Engine {
|
|||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
ast: &AST,
|
ast: &AST,
|
||||||
eval_ast: bool,
|
eval_ast: bool,
|
||||||
name: &str,
|
name: impl AsRef<str>,
|
||||||
this_ptr: &mut Option<&mut Dynamic>,
|
this_ptr: &mut Option<&mut Dynamic>,
|
||||||
args: &mut FnCallArgs,
|
args: &mut FnCallArgs,
|
||||||
) -> RhaiResult {
|
) -> RhaiResult {
|
||||||
@ -1881,12 +1916,14 @@ impl Engine {
|
|||||||
|
|
||||||
let fn_def = ast
|
let fn_def = ast
|
||||||
.lib()
|
.lib()
|
||||||
.get_script_fn(name, args.len())
|
.get_script_fn(name.as_ref(), args.len())
|
||||||
.ok_or_else(|| EvalAltResult::ErrorFunctionNotFound(name.into(), Position::NONE))?;
|
.ok_or_else(|| {
|
||||||
|
EvalAltResult::ErrorFunctionNotFound(name.as_ref().into(), Position::NONE)
|
||||||
|
})?;
|
||||||
|
|
||||||
// Check for data race.
|
// Check for data race.
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
crate::fn_call::ensure_no_data_race(name, args, false)?;
|
crate::fn_call::ensure_no_data_race(name.as_ref(), args, false)?;
|
||||||
|
|
||||||
self.call_script_fn(
|
self.call_script_fn(
|
||||||
scope,
|
scope,
|
||||||
@ -1936,13 +1973,15 @@ impl Engine {
|
|||||||
crate::optimize::optimize_into_ast(self, scope, stmt.into_vec(), lib, optimization_level)
|
crate::optimize::optimize_into_ast(self, scope, stmt.into_vec(), lib, optimization_level)
|
||||||
}
|
}
|
||||||
/// Generate a list of all registered functions.
|
/// Generate a list of all registered functions.
|
||||||
|
/// Available under the `metadata` feature only.
|
||||||
///
|
///
|
||||||
/// Functions from the following sources are included, in order:
|
/// Functions from the following sources are included, in order:
|
||||||
/// 1) Functions registered into the global namespace
|
/// 1) Functions registered into the global namespace
|
||||||
/// 2) Functions in registered sub-modules
|
/// 2) Functions in registered sub-modules
|
||||||
/// 3) Functions in packages (optional)
|
/// 3) Functions in packages (optional)
|
||||||
pub fn gen_fn_signatures(&self, include_packages: bool) -> Vec<String> {
|
#[cfg(feature = "metadata")]
|
||||||
let mut signatures: Vec<_> = Default::default();
|
pub fn gen_fn_signatures(&self, include_packages: bool) -> crate::stdlib::vec::Vec<String> {
|
||||||
|
let mut signatures: crate::stdlib::vec::Vec<_> = Default::default();
|
||||||
|
|
||||||
signatures.extend(self.global_namespace.gen_fn_signatures());
|
signatures.extend(self.global_namespace.gen_fn_signatures());
|
||||||
|
|
||||||
|
@ -236,7 +236,7 @@ impl Engine {
|
|||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn disable_symbol(&mut self, symbol: &str) -> &mut Self {
|
pub fn disable_symbol(&mut self, symbol: impl Into<String>) -> &mut Self {
|
||||||
self.disabled_symbols.insert(symbol.into());
|
self.disabled_symbols.insert(symbol.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@ -270,7 +270,7 @@ impl Engine {
|
|||||||
/// ```
|
/// ```
|
||||||
pub fn register_custom_operator(
|
pub fn register_custom_operator(
|
||||||
&mut self,
|
&mut self,
|
||||||
keyword: &str,
|
keyword: impl AsRef<str> + Into<String>,
|
||||||
precedence: u8,
|
precedence: u8,
|
||||||
) -> Result<&mut Self, String> {
|
) -> Result<&mut Self, String> {
|
||||||
let precedence = Precedence::new(precedence);
|
let precedence = Precedence::new(precedence);
|
||||||
@ -279,25 +279,25 @@ impl Engine {
|
|||||||
return Err("precedence cannot be zero".into());
|
return Err("precedence cannot be zero".into());
|
||||||
}
|
}
|
||||||
|
|
||||||
match Token::lookup_from_syntax(keyword) {
|
match Token::lookup_from_syntax(keyword.as_ref()) {
|
||||||
// Standard identifiers, reserved keywords and custom keywords are OK
|
// Standard identifiers, reserved keywords and custom keywords are OK
|
||||||
None | Some(Token::Reserved(_)) | Some(Token::Custom(_)) => (),
|
None | Some(Token::Reserved(_)) | Some(Token::Custom(_)) => (),
|
||||||
// Active standard keywords cannot be made custom
|
// Active standard keywords cannot be made custom
|
||||||
// Disabled keywords are OK
|
// Disabled keywords are OK
|
||||||
Some(token) if token.is_keyword() => {
|
Some(token) if token.is_keyword() => {
|
||||||
if !self.disabled_symbols.contains(token.syntax().as_ref()) {
|
if !self.disabled_symbols.contains(token.syntax().as_ref()) {
|
||||||
return Err(format!("'{}' is a reserved keyword", keyword).into());
|
return Err(format!("'{}' is a reserved keyword", keyword.as_ref()).into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Active standard symbols cannot be made custom
|
// Active standard symbols cannot be made custom
|
||||||
Some(token) if token.is_symbol() => {
|
Some(token) if token.is_symbol() => {
|
||||||
if !self.disabled_symbols.contains(token.syntax().as_ref()) {
|
if !self.disabled_symbols.contains(token.syntax().as_ref()) {
|
||||||
return Err(format!("'{}' is a reserved operator", keyword).into());
|
return Err(format!("'{}' is a reserved operator", keyword.as_ref()).into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Active standard symbols cannot be made custom
|
// Active standard symbols cannot be made custom
|
||||||
Some(token) if !self.disabled_symbols.contains(token.syntax().as_ref()) => {
|
Some(token) if !self.disabled_symbols.contains(token.syntax().as_ref()) => {
|
||||||
return Err(format!("'{}' is a reserved symbol", keyword).into())
|
return Err(format!("'{}' is a reserved symbol", keyword.as_ref()).into())
|
||||||
}
|
}
|
||||||
// Disabled symbols are OK
|
// Disabled symbols are OK
|
||||||
Some(_) => (),
|
Some(_) => (),
|
||||||
|
@ -26,7 +26,7 @@ pub trait FuncArgs {
|
|||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// impl FuncArgs for Options {
|
/// impl FuncArgs for Options {
|
||||||
/// fn parse<C: Extend<Dynamic>>(self, container: &mut C) {
|
/// fn parse<CONTAINER: Extend<Dynamic>>(self, container: &mut CONTAINER) {
|
||||||
/// container.extend(std::iter::once(self.foo.into()));
|
/// container.extend(std::iter::once(self.foo.into()));
|
||||||
/// container.extend(std::iter::once(self.bar.into()));
|
/// container.extend(std::iter::once(self.bar.into()));
|
||||||
/// container.extend(std::iter::once(self.baz.into()));
|
/// container.extend(std::iter::once(self.baz.into()));
|
||||||
@ -51,11 +51,12 @@ pub trait FuncArgs {
|
|||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
fn parse<T: Extend<Dynamic>>(self, container: &mut T);
|
fn parse<CONTAINER: Extend<Dynamic>>(self, container: &mut CONTAINER);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Variant + Clone> FuncArgs for Vec<T> {
|
impl<T: Variant + Clone> FuncArgs for Vec<T> {
|
||||||
fn parse<C: Extend<Dynamic>>(self, container: &mut C) {
|
#[inline(always)]
|
||||||
|
fn parse<CONTAINER: Extend<Dynamic>>(self, container: &mut CONTAINER) {
|
||||||
container.extend(self.into_iter().map(Variant::into_dynamic));
|
container.extend(self.into_iter().map(Variant::into_dynamic));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -475,9 +475,9 @@ impl Engine {
|
|||||||
fn_def
|
fn_def
|
||||||
.lib
|
.lib
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|m| m.id())
|
.and_then(|m| m.id().map(|id| id.to_string()))
|
||||||
.unwrap_or_else(|| state.source.as_ref().map_or_else(|| "", |s| s.as_str()))
|
.or_else(|| state.source.as_ref().map(|s| s.to_string()))
|
||||||
.to_string(),
|
.unwrap_or_default(),
|
||||||
err,
|
err,
|
||||||
pos,
|
pos,
|
||||||
)
|
)
|
||||||
@ -651,14 +651,14 @@ impl Engine {
|
|||||||
crate::engine::KEYWORD_IS_DEF_FN
|
crate::engine::KEYWORD_IS_DEF_FN
|
||||||
if args.len() == 2 && args[0].is::<FnPtr>() && args[1].is::<crate::INT>() =>
|
if args.len() == 2 && args[0].is::<FnPtr>() && args[1].is::<crate::INT>() =>
|
||||||
{
|
{
|
||||||
let fn_name = args[0].read_lock::<ImmutableString>().unwrap();
|
let fn_name = &*args[0].read_lock::<ImmutableString>().unwrap();
|
||||||
let num_params = args[1].as_int().unwrap();
|
let num_params = args[1].as_int().unwrap();
|
||||||
|
|
||||||
return Ok((
|
return Ok((
|
||||||
if num_params < 0 {
|
if num_params < 0 {
|
||||||
Dynamic::FALSE
|
Dynamic::FALSE
|
||||||
} else {
|
} else {
|
||||||
let hash_script = calc_fn_hash(empty(), &fn_name, num_params as usize);
|
let hash_script = calc_fn_hash(empty(), fn_name, num_params as usize);
|
||||||
self.has_script_fn(Some(mods), state, lib, hash_script)
|
self.has_script_fn(Some(mods), state, lib, hash_script)
|
||||||
.into()
|
.into()
|
||||||
},
|
},
|
||||||
@ -737,7 +737,7 @@ impl Engine {
|
|||||||
if !func.externals.is_empty() {
|
if !func.externals.is_empty() {
|
||||||
captured
|
captured
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|(name, _, _)| func.externals.iter().any(|ex| ex == name))
|
.filter(|(name, _, _)| func.externals.contains(name.as_ref()))
|
||||||
.for_each(|(name, value, _)| {
|
.for_each(|(name, value, _)| {
|
||||||
// Consume the scope values.
|
// Consume the scope values.
|
||||||
scope.push_dynamic(name, value);
|
scope.push_dynamic(name, value);
|
||||||
@ -1132,13 +1132,10 @@ impl Engine {
|
|||||||
|
|
||||||
// Append the new curried arguments to the existing list.
|
// Append the new curried arguments to the existing list.
|
||||||
|
|
||||||
args_expr.iter().skip(1).try_for_each(
|
args_expr.iter().skip(1).try_for_each(|expr| {
|
||||||
|expr| -> Result<(), Box<EvalAltResult>> {
|
self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)
|
||||||
fn_curry
|
.map(|value| fn_curry.push(value))
|
||||||
.push(self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?);
|
})?;
|
||||||
Ok(())
|
|
||||||
},
|
|
||||||
)?;
|
|
||||||
|
|
||||||
return Ok(FnPtr::new_unchecked(name, fn_curry).into());
|
return Ok(FnPtr::new_unchecked(name, fn_curry).into());
|
||||||
}
|
}
|
||||||
@ -1222,8 +1219,8 @@ impl Engine {
|
|||||||
state
|
state
|
||||||
.source
|
.source
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map_or_else(|| "", |s| s.as_str())
|
.map(|s| s.to_string())
|
||||||
.to_string(),
|
.unwrap_or_default(),
|
||||||
err,
|
err,
|
||||||
pos,
|
pos,
|
||||||
))
|
))
|
||||||
|
@ -7,15 +7,14 @@ use crate::stdlib::{
|
|||||||
boxed::Box,
|
boxed::Box,
|
||||||
convert::{TryFrom, TryInto},
|
convert::{TryFrom, TryInto},
|
||||||
fmt,
|
fmt,
|
||||||
iter::empty,
|
iter::{empty, once},
|
||||||
mem,
|
mem,
|
||||||
string::String,
|
string::String,
|
||||||
vec::Vec,
|
|
||||||
};
|
};
|
||||||
use crate::token::is_valid_identifier;
|
use crate::token::is_valid_identifier;
|
||||||
use crate::{
|
use crate::{
|
||||||
calc_fn_hash, Dynamic, Engine, EvalAltResult, EvalContext, ImmutableString, Module, Position,
|
calc_fn_hash, Dynamic, Engine, EvalAltResult, EvalContext, ImmutableString, Module, Position,
|
||||||
RhaiResult,
|
RhaiResult, StaticVec,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Trait that maps to `Send + Sync` only under the `sync` feature.
|
/// Trait that maps to `Send + Sync` only under the `sync` feature.
|
||||||
@ -185,10 +184,12 @@ impl<'a> NativeCallContext<'a> {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn call_fn_dynamic_raw(
|
pub fn call_fn_dynamic_raw(
|
||||||
&self,
|
&self,
|
||||||
fn_name: &str,
|
fn_name: impl AsRef<str>,
|
||||||
is_method: bool,
|
is_method: bool,
|
||||||
args: &mut [&mut Dynamic],
|
args: &mut [&mut Dynamic],
|
||||||
) -> RhaiResult {
|
) -> RhaiResult {
|
||||||
|
let fn_name = fn_name.as_ref();
|
||||||
|
|
||||||
let hash = if is_method {
|
let hash = if is_method {
|
||||||
FnCallHash::from_script_and_native(
|
FnCallHash::from_script_and_native(
|
||||||
calc_fn_hash(empty(), fn_name, args.len() - 1),
|
calc_fn_hash(empty(), fn_name, args.len() - 1),
|
||||||
@ -251,7 +252,7 @@ pub type FnCallArgs<'a> = [&'a mut Dynamic];
|
|||||||
/// A general function pointer, which may carry additional (i.e. curried) argument values
|
/// A general function pointer, which may carry additional (i.e. curried) argument values
|
||||||
/// to be passed onto a function during a call.
|
/// to be passed onto a function during a call.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct FnPtr(ImmutableString, Vec<Dynamic>);
|
pub struct FnPtr(ImmutableString, StaticVec<Dynamic>);
|
||||||
|
|
||||||
impl FnPtr {
|
impl FnPtr {
|
||||||
/// Create a new function pointer.
|
/// Create a new function pointer.
|
||||||
@ -261,7 +262,10 @@ impl FnPtr {
|
|||||||
}
|
}
|
||||||
/// Create a new function pointer without checking its parameters.
|
/// Create a new function pointer without checking its parameters.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn new_unchecked(name: impl Into<ImmutableString>, curry: Vec<Dynamic>) -> Self {
|
pub(crate) fn new_unchecked(
|
||||||
|
name: impl Into<ImmutableString>,
|
||||||
|
curry: StaticVec<Dynamic>,
|
||||||
|
) -> Self {
|
||||||
Self(name.into(), curry)
|
Self(name.into(), curry)
|
||||||
}
|
}
|
||||||
/// Get the name of the function.
|
/// Get the name of the function.
|
||||||
@ -276,7 +280,7 @@ impl FnPtr {
|
|||||||
}
|
}
|
||||||
/// Get the underlying data of the function pointer.
|
/// Get the underlying data of the function pointer.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn take_data(self) -> (ImmutableString, Vec<Dynamic>) {
|
pub(crate) fn take_data(self) -> (ImmutableString, StaticVec<Dynamic>) {
|
||||||
(self.0, self.1)
|
(self.0, self.1)
|
||||||
}
|
}
|
||||||
/// Get the curried arguments.
|
/// Get the curried arguments.
|
||||||
@ -324,22 +328,28 @@ impl FnPtr {
|
|||||||
this_ptr: Option<&mut Dynamic>,
|
this_ptr: Option<&mut Dynamic>,
|
||||||
mut arg_values: impl AsMut<[Dynamic]>,
|
mut arg_values: impl AsMut<[Dynamic]>,
|
||||||
) -> RhaiResult {
|
) -> RhaiResult {
|
||||||
let arg_values = arg_values.as_mut();
|
let mut args_data;
|
||||||
|
|
||||||
let mut args_data = self
|
let arg_values = if self.curry().is_empty() {
|
||||||
|
arg_values.as_mut()
|
||||||
|
} else {
|
||||||
|
args_data = self
|
||||||
.curry()
|
.curry()
|
||||||
.iter()
|
.iter()
|
||||||
.cloned()
|
.cloned()
|
||||||
.chain(arg_values.iter_mut().map(mem::take))
|
.chain(arg_values.as_mut().iter_mut().map(mem::take))
|
||||||
.collect::<Vec<_>>();
|
.collect::<StaticVec<_>>();
|
||||||
|
|
||||||
let mut args = args_data.iter_mut().collect::<Vec<_>>();
|
args_data.as_mut()
|
||||||
|
};
|
||||||
|
|
||||||
let is_method = this_ptr.is_some();
|
let is_method = this_ptr.is_some();
|
||||||
|
|
||||||
if let Some(obj) = this_ptr {
|
let mut args: StaticVec<_> = if let Some(obj) = this_ptr {
|
||||||
args.insert(0, obj);
|
once(obj).chain(arg_values.iter_mut()).collect()
|
||||||
}
|
} else {
|
||||||
|
arg_values.iter_mut().collect()
|
||||||
|
};
|
||||||
|
|
||||||
ctx.call_fn_dynamic_raw(self.fn_name(), is_method, args.as_mut())
|
ctx.call_fn_dynamic_raw(self.fn_name(), is_method, args.as_mut())
|
||||||
}
|
}
|
||||||
|
@ -5,13 +5,7 @@
|
|||||||
use crate::dynamic::{DynamicWriteLock, Variant};
|
use crate::dynamic::{DynamicWriteLock, Variant};
|
||||||
use crate::fn_native::{CallableFunction, FnAny, FnCallArgs, SendSync};
|
use crate::fn_native::{CallableFunction, FnAny, FnCallArgs, SendSync};
|
||||||
use crate::r#unsafe::unsafe_try_cast;
|
use crate::r#unsafe::unsafe_try_cast;
|
||||||
use crate::stdlib::{
|
use crate::stdlib::{any::TypeId, boxed::Box, mem, string::String, vec};
|
||||||
any::{type_name, TypeId},
|
|
||||||
boxed::Box,
|
|
||||||
mem,
|
|
||||||
string::String,
|
|
||||||
vec,
|
|
||||||
};
|
|
||||||
use crate::{Dynamic, EvalAltResult, NativeCallContext};
|
use crate::{Dynamic, EvalAltResult, NativeCallContext};
|
||||||
|
|
||||||
// These types are used to build a unique _marker_ tuple type for each combination
|
// These types are used to build a unique _marker_ tuple type for each combination
|
||||||
@ -61,16 +55,22 @@ pub fn by_value<T: Variant + Clone>(data: &mut Dynamic) -> T {
|
|||||||
|
|
||||||
/// Trait to register custom Rust functions.
|
/// Trait to register custom Rust functions.
|
||||||
pub trait RegisterNativeFunction<Args, Result> {
|
pub trait RegisterNativeFunction<Args, Result> {
|
||||||
|
/// Convert this function into a [`CallableFunction`].
|
||||||
|
fn into_callable_function(self) -> CallableFunction;
|
||||||
/// Get the type ID's of this function's parameters.
|
/// Get the type ID's of this function's parameters.
|
||||||
fn param_types() -> Box<[TypeId]>;
|
fn param_types() -> Box<[TypeId]>;
|
||||||
/// Get the type names of this function's parameters.
|
/// Get the type names of this function's parameters.
|
||||||
|
/// Available under the `metadata` feature only.
|
||||||
|
#[cfg(feature = "metadata")]
|
||||||
fn param_names() -> Box<[&'static str]>;
|
fn param_names() -> Box<[&'static str]>;
|
||||||
/// Get the type ID of this function's return value.
|
/// Get the type ID of this function's return value.
|
||||||
|
/// Available under the `metadata` feature only.
|
||||||
|
#[cfg(feature = "metadata")]
|
||||||
fn return_type() -> TypeId;
|
fn return_type() -> TypeId;
|
||||||
/// Get the type name of this function's return value.
|
/// Get the type name of this function's return value.
|
||||||
|
/// Available under the `metadata` feature only.
|
||||||
|
#[cfg(feature = "metadata")]
|
||||||
fn return_type_name() -> &'static str;
|
fn return_type_name() -> &'static str;
|
||||||
/// Convert this function into a [`CallableFunction`].
|
|
||||||
fn into_callable_function(self) -> CallableFunction;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! def_register {
|
macro_rules! def_register {
|
||||||
@ -91,9 +91,9 @@ macro_rules! def_register {
|
|||||||
RET: Variant + Clone
|
RET: Variant + Clone
|
||||||
> RegisterNativeFunction<($($mark,)*), ()> for FN {
|
> RegisterNativeFunction<($($mark,)*), ()> for FN {
|
||||||
#[inline(always)] fn param_types() -> Box<[TypeId]> { vec![$(TypeId::of::<$par>()),*].into_boxed_slice() }
|
#[inline(always)] fn param_types() -> Box<[TypeId]> { vec![$(TypeId::of::<$par>()),*].into_boxed_slice() }
|
||||||
#[inline(always)] fn param_names() -> Box<[&'static str]> { vec![$(type_name::<$par>()),*].into_boxed_slice() }
|
#[cfg(feature = "metadata")] #[inline(always)] fn param_names() -> Box<[&'static str]> { vec![$(crate::stdlib::any::type_name::<$par>()),*].into_boxed_slice() }
|
||||||
#[inline(always)] fn return_type() -> TypeId { TypeId::of::<RET>() }
|
#[cfg(feature = "metadata")] #[inline(always)] fn return_type() -> TypeId { TypeId::of::<RET>() }
|
||||||
#[inline(always)] fn return_type_name() -> &'static str { type_name::<RET>() }
|
#[cfg(feature = "metadata")] #[inline(always)] fn return_type_name() -> &'static str { crate::stdlib::any::type_name::<RET>() }
|
||||||
#[inline(always)] fn into_callable_function(self) -> CallableFunction {
|
#[inline(always)] fn into_callable_function(self) -> CallableFunction {
|
||||||
CallableFunction::$abi(Box::new(move |_: NativeCallContext, args: &mut FnCallArgs| {
|
CallableFunction::$abi(Box::new(move |_: NativeCallContext, args: &mut FnCallArgs| {
|
||||||
// The arguments are assumed to be of the correct number and types!
|
// The arguments are assumed to be of the correct number and types!
|
||||||
@ -115,9 +115,9 @@ macro_rules! def_register {
|
|||||||
RET: Variant + Clone
|
RET: Variant + Clone
|
||||||
> RegisterNativeFunction<(NativeCallContext<'static>, $($mark,)*), ()> for FN {
|
> RegisterNativeFunction<(NativeCallContext<'static>, $($mark,)*), ()> for FN {
|
||||||
#[inline(always)] fn param_types() -> Box<[TypeId]> { vec![$(TypeId::of::<$par>()),*].into_boxed_slice() }
|
#[inline(always)] fn param_types() -> Box<[TypeId]> { vec![$(TypeId::of::<$par>()),*].into_boxed_slice() }
|
||||||
#[inline(always)] fn param_names() -> Box<[&'static str]> { vec![$(type_name::<$par>()),*].into_boxed_slice() }
|
#[cfg(feature = "metadata")] #[inline(always)] fn param_names() -> Box<[&'static str]> { vec![$(crate::stdlib::any::type_name::<$par>()),*].into_boxed_slice() }
|
||||||
#[inline(always)] fn return_type() -> TypeId { TypeId::of::<RET>() }
|
#[cfg(feature = "metadata")] #[inline(always)] fn return_type() -> TypeId { TypeId::of::<RET>() }
|
||||||
#[inline(always)] fn return_type_name() -> &'static str { type_name::<RET>() }
|
#[cfg(feature = "metadata")] #[inline(always)] fn return_type_name() -> &'static str { crate::stdlib::any::type_name::<RET>() }
|
||||||
#[inline(always)] fn into_callable_function(self) -> CallableFunction {
|
#[inline(always)] fn into_callable_function(self) -> CallableFunction {
|
||||||
CallableFunction::$abi(Box::new(move |ctx: NativeCallContext, args: &mut FnCallArgs| {
|
CallableFunction::$abi(Box::new(move |ctx: NativeCallContext, args: &mut FnCallArgs| {
|
||||||
// The arguments are assumed to be of the correct number and types!
|
// The arguments are assumed to be of the correct number and types!
|
||||||
@ -139,9 +139,9 @@ macro_rules! def_register {
|
|||||||
RET: Variant + Clone
|
RET: Variant + Clone
|
||||||
> RegisterNativeFunction<($($mark,)*), Result<RET, Box<EvalAltResult>>> for FN {
|
> RegisterNativeFunction<($($mark,)*), Result<RET, Box<EvalAltResult>>> for FN {
|
||||||
#[inline(always)] fn param_types() -> Box<[TypeId]> { vec![$(TypeId::of::<$par>()),*].into_boxed_slice() }
|
#[inline(always)] fn param_types() -> Box<[TypeId]> { vec![$(TypeId::of::<$par>()),*].into_boxed_slice() }
|
||||||
#[inline(always)] fn param_names() -> Box<[&'static str]> { vec![$(type_name::<$par>()),*].into_boxed_slice() }
|
#[cfg(feature = "metadata")] #[inline(always)] fn param_names() -> Box<[&'static str]> { vec![$(crate::stdlib::any::type_name::<$par>()),*].into_boxed_slice() }
|
||||||
#[inline(always)] fn return_type() -> TypeId { TypeId::of::<Result<RET, Box<EvalAltResult>>>() }
|
#[cfg(feature = "metadata")] #[inline(always)] fn return_type() -> TypeId { TypeId::of::<Result<RET, Box<EvalAltResult>>>() }
|
||||||
#[inline(always)] fn return_type_name() -> &'static str { type_name::<Result<RET, Box<EvalAltResult>>>() }
|
#[cfg(feature = "metadata")] #[inline(always)] fn return_type_name() -> &'static str { crate::stdlib::any::type_name::<Result<RET, Box<EvalAltResult>>>() }
|
||||||
#[inline(always)] fn into_callable_function(self) -> CallableFunction {
|
#[inline(always)] fn into_callable_function(self) -> CallableFunction {
|
||||||
CallableFunction::$abi(Box::new(move |_: NativeCallContext, args: &mut FnCallArgs| {
|
CallableFunction::$abi(Box::new(move |_: NativeCallContext, args: &mut FnCallArgs| {
|
||||||
// The arguments are assumed to be of the correct number and types!
|
// The arguments are assumed to be of the correct number and types!
|
||||||
@ -160,9 +160,9 @@ macro_rules! def_register {
|
|||||||
RET: Variant + Clone
|
RET: Variant + Clone
|
||||||
> RegisterNativeFunction<(NativeCallContext<'static>, $($mark,)*), Result<RET, Box<EvalAltResult>>> for FN {
|
> RegisterNativeFunction<(NativeCallContext<'static>, $($mark,)*), Result<RET, Box<EvalAltResult>>> for FN {
|
||||||
#[inline(always)] fn param_types() -> Box<[TypeId]> { vec![$(TypeId::of::<$par>()),*].into_boxed_slice() }
|
#[inline(always)] fn param_types() -> Box<[TypeId]> { vec![$(TypeId::of::<$par>()),*].into_boxed_slice() }
|
||||||
#[inline(always)] fn param_names() -> Box<[&'static str]> { vec![$(type_name::<$par>()),*].into_boxed_slice() }
|
#[cfg(feature = "metadata")] #[inline(always)] fn param_names() -> Box<[&'static str]> { vec![$(crate::stdlib::any::type_name::<$par>()),*].into_boxed_slice() }
|
||||||
#[inline(always)] fn return_type() -> TypeId { TypeId::of::<Result<RET, Box<EvalAltResult>>>() }
|
#[cfg(feature = "metadata")] #[inline(always)] fn return_type() -> TypeId { TypeId::of::<Result<RET, Box<EvalAltResult>>>() }
|
||||||
#[inline(always)] fn return_type_name() -> &'static str { type_name::<Result<RET, Box<EvalAltResult>>>() }
|
#[cfg(feature = "metadata")] #[inline(always)] fn return_type_name() -> &'static str { crate::stdlib::any::type_name::<Result<RET, Box<EvalAltResult>>>() }
|
||||||
#[inline(always)] fn into_callable_function(self) -> CallableFunction {
|
#[inline(always)] fn into_callable_function(self) -> CallableFunction {
|
||||||
CallableFunction::$abi(Box::new(move |ctx: NativeCallContext, args: &mut FnCallArgs| {
|
CallableFunction::$abi(Box::new(move |ctx: NativeCallContext, args: &mut FnCallArgs| {
|
||||||
// The arguments are assumed to be of the correct number and types!
|
// The arguments are assumed to be of the correct number and types!
|
||||||
|
@ -12,10 +12,11 @@ use crate::stdlib::{
|
|||||||
iter::empty,
|
iter::empty,
|
||||||
num::NonZeroUsize,
|
num::NonZeroUsize,
|
||||||
ops::{Add, AddAssign, Deref, DerefMut},
|
ops::{Add, AddAssign, Deref, DerefMut},
|
||||||
string::{String, ToString},
|
string::String,
|
||||||
vec::Vec,
|
vec::Vec,
|
||||||
};
|
};
|
||||||
use crate::token::Token;
|
use crate::token::Token;
|
||||||
|
use crate::utils::StringInterner;
|
||||||
use crate::{
|
use crate::{
|
||||||
calc_fn_hash, calc_fn_params_hash, combine_hashes, Dynamic, EvalAltResult, ImmutableString,
|
calc_fn_hash, calc_fn_params_hash, combine_hashes, Dynamic, EvalAltResult, ImmutableString,
|
||||||
NativeCallContext, Position, Shared, StaticVec,
|
NativeCallContext, Position, Shared, StaticVec,
|
||||||
@ -54,27 +55,27 @@ pub struct FuncInfo {
|
|||||||
/// Function access mode.
|
/// Function access mode.
|
||||||
pub access: FnAccess,
|
pub access: FnAccess,
|
||||||
/// Function name.
|
/// Function name.
|
||||||
pub name: String,
|
pub name: ImmutableString,
|
||||||
/// Number of parameters.
|
/// Number of parameters.
|
||||||
pub params: usize,
|
pub params: usize,
|
||||||
/// Parameter types (if applicable).
|
/// Parameter types (if applicable).
|
||||||
pub param_types: StaticVec<TypeId>,
|
pub param_types: StaticVec<TypeId>,
|
||||||
/// Parameter names (if available).
|
/// Parameter names (if available).
|
||||||
|
#[cfg(feature = "metadata")]
|
||||||
pub param_names: StaticVec<ImmutableString>,
|
pub param_names: StaticVec<ImmutableString>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FuncInfo {
|
impl FuncInfo {
|
||||||
/// Generate a signature of the function.
|
/// Generate a signature of the function.
|
||||||
|
/// Available under the `metadata` feature only.
|
||||||
|
#[cfg(feature = "metadata")]
|
||||||
pub fn gen_signature(&self) -> String {
|
pub fn gen_signature(&self) -> String {
|
||||||
let mut sig = format!("{}(", self.name);
|
let mut sig = format!("{}(", self.name);
|
||||||
|
|
||||||
if !self.param_names.is_empty() {
|
if !self.param_names.is_empty() {
|
||||||
let mut params: Vec<_> = self
|
let mut params: crate::stdlib::vec::Vec<String> =
|
||||||
.param_names
|
self.param_names.iter().map(|s| s.as_str().into()).collect();
|
||||||
.iter()
|
let return_type = params.pop().unwrap_or_else(|| "()".into());
|
||||||
.map(ImmutableString::to_string)
|
|
||||||
.collect();
|
|
||||||
let return_type = params.pop().unwrap_or_else(|| "()".to_string());
|
|
||||||
sig.push_str(¶ms.join(", "));
|
sig.push_str(¶ms.join(", "));
|
||||||
if return_type != "()" {
|
if return_type != "()" {
|
||||||
sig.push_str(") -> ");
|
sig.push_str(") -> ");
|
||||||
@ -114,7 +115,7 @@ impl FuncInfo {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn calc_native_fn_hash<'a>(
|
fn calc_native_fn_hash<'a>(
|
||||||
modules: impl Iterator<Item = &'a str>,
|
modules: impl Iterator<Item = &'a str>,
|
||||||
fn_name: &str,
|
fn_name: impl AsRef<str>,
|
||||||
params: &[TypeId],
|
params: &[TypeId],
|
||||||
) -> u64 {
|
) -> u64 {
|
||||||
let hash_script = calc_fn_hash(modules, fn_name, params.len());
|
let hash_script = calc_fn_hash(modules, fn_name, params.len());
|
||||||
@ -147,6 +148,8 @@ pub struct Module {
|
|||||||
indexed: bool,
|
indexed: bool,
|
||||||
/// Does the [`Module`] contain indexed functions that have been exposed to the global namespace?
|
/// Does the [`Module`] contain indexed functions that have been exposed to the global namespace?
|
||||||
contains_indexed_global_functions: bool,
|
contains_indexed_global_functions: bool,
|
||||||
|
/// Interned strings
|
||||||
|
interned_strings: StringInterner,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Module {
|
impl Default for Module {
|
||||||
@ -163,6 +166,7 @@ impl Default for Module {
|
|||||||
all_type_iterators: Default::default(),
|
all_type_iterators: Default::default(),
|
||||||
indexed: false,
|
indexed: false,
|
||||||
contains_indexed_global_functions: false,
|
contains_indexed_global_functions: false,
|
||||||
|
interned_strings: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -172,11 +176,10 @@ impl fmt::Debug for Module {
|
|||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
"Module({}\n{}{}{})",
|
"Module({}\n{}{}{})",
|
||||||
if let Some(ref id) = self.id {
|
self.id
|
||||||
format!("id: {:?},", id)
|
.as_ref()
|
||||||
} else {
|
.map(|id| format!("id: {:?},", id))
|
||||||
"".to_string()
|
.unwrap_or_default(),
|
||||||
},
|
|
||||||
if !self.modules.is_empty() {
|
if !self.modules.is_empty() {
|
||||||
format!(
|
format!(
|
||||||
" modules: {}\n",
|
" modules: {}\n",
|
||||||
@ -187,7 +190,7 @@ impl fmt::Debug for Module {
|
|||||||
.join(", ")
|
.join(", ")
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
"".to_string()
|
Default::default()
|
||||||
},
|
},
|
||||||
if !self.variables.is_empty() {
|
if !self.variables.is_empty() {
|
||||||
format!(
|
format!(
|
||||||
@ -199,19 +202,19 @@ impl fmt::Debug for Module {
|
|||||||
.join(", ")
|
.join(", ")
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
"".to_string()
|
Default::default()
|
||||||
},
|
},
|
||||||
if !self.functions.is_empty() {
|
if !self.functions.is_empty() {
|
||||||
format!(
|
format!(
|
||||||
" functions: {}\n",
|
" functions: {}\n",
|
||||||
self.functions
|
self.functions
|
||||||
.values()
|
.values()
|
||||||
.map(|f| f.func.to_string())
|
.map(|f| crate::stdlib::string::ToString::to_string(&f.func))
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join(", ")
|
.join(", ")
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
"".to_string()
|
Default::default()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -360,6 +363,8 @@ impl Module {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Generate signatures for all the non-private functions in the [`Module`].
|
/// Generate signatures for all the non-private functions in the [`Module`].
|
||||||
|
/// Available under the `metadata` feature only.
|
||||||
|
#[cfg(feature = "metadata")]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn gen_fn_signatures(&self) -> impl Iterator<Item = String> + '_ {
|
pub fn gen_fn_signatures(&self) -> impl Iterator<Item = String> + '_ {
|
||||||
self.functions
|
self.functions
|
||||||
@ -466,16 +471,17 @@ impl Module {
|
|||||||
// None + function name + number of arguments.
|
// None + function name + number of arguments.
|
||||||
let num_params = fn_def.params.len();
|
let num_params = fn_def.params.len();
|
||||||
let hash_script = crate::calc_fn_hash(empty(), &fn_def.name, num_params);
|
let hash_script = crate::calc_fn_hash(empty(), &fn_def.name, num_params);
|
||||||
let mut param_names: StaticVec<_> = fn_def.params.iter().cloned().collect();
|
let mut param_names = fn_def.params.clone();
|
||||||
param_names.push("Dynamic".into());
|
param_names.push("Dynamic".into());
|
||||||
self.functions.insert(
|
self.functions.insert(
|
||||||
hash_script,
|
hash_script,
|
||||||
Box::new(FuncInfo {
|
Box::new(FuncInfo {
|
||||||
name: fn_def.name.to_string(),
|
name: fn_def.name.clone(),
|
||||||
namespace: FnNamespace::Internal,
|
namespace: FnNamespace::Internal,
|
||||||
access: fn_def.access,
|
access: fn_def.access,
|
||||||
params: num_params,
|
params: num_params,
|
||||||
param_types: Default::default(),
|
param_types: Default::default(),
|
||||||
|
#[cfg(feature = "metadata")]
|
||||||
param_names,
|
param_names,
|
||||||
func: fn_def.into(),
|
func: fn_def.into(),
|
||||||
}),
|
}),
|
||||||
@ -599,6 +605,7 @@ impl Module {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Update the metadata (parameter names/types and return type) of a registered function.
|
/// Update the metadata (parameter names/types and return type) of a registered function.
|
||||||
|
/// Available under the `metadata` feature only.
|
||||||
///
|
///
|
||||||
/// The [`u64`] hash is returned by the [`set_native_fn`][Module::set_native_fn] call.
|
/// The [`u64`] hash is returned by the [`set_native_fn`][Module::set_native_fn] call.
|
||||||
///
|
///
|
||||||
@ -610,10 +617,16 @@ impl Module {
|
|||||||
///
|
///
|
||||||
/// The _last entry_ in the list should be the _return type_ of the function.
|
/// The _last entry_ in the list should be the _return type_ of the function.
|
||||||
/// In other words, the number of entries should be one larger than the number of parameters.
|
/// In other words, the number of entries should be one larger than the number of parameters.
|
||||||
|
#[cfg(feature = "metadata")]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn update_fn_metadata(&mut self, hash_fn: u64, arg_names: &[&str]) -> &mut Self {
|
pub fn update_fn_metadata(&mut self, hash_fn: u64, arg_names: &[&str]) -> &mut Self {
|
||||||
|
let param_names = arg_names
|
||||||
|
.iter()
|
||||||
|
.map(|&name| self.interned_strings.get(name))
|
||||||
|
.collect();
|
||||||
|
|
||||||
if let Some(f) = self.functions.get_mut(&hash_fn) {
|
if let Some(f) = self.functions.get_mut(&hash_fn) {
|
||||||
f.param_names = arg_names.iter().map(|&n| n.into()).collect();
|
f.param_names = param_names;
|
||||||
}
|
}
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@ -625,9 +638,9 @@ impl Module {
|
|||||||
pub fn update_fn_namespace(&mut self, hash_fn: u64, namespace: FnNamespace) -> &mut Self {
|
pub fn update_fn_namespace(&mut self, hash_fn: u64, namespace: FnNamespace) -> &mut Self {
|
||||||
if let Some(f) = self.functions.get_mut(&hash_fn) {
|
if let Some(f) = self.functions.get_mut(&hash_fn) {
|
||||||
f.namespace = namespace;
|
f.namespace = namespace;
|
||||||
}
|
|
||||||
self.indexed = false;
|
self.indexed = false;
|
||||||
self.contains_indexed_global_functions = false;
|
self.contains_indexed_global_functions = false;
|
||||||
|
}
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -641,14 +654,13 @@ impl Module {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_fn(
|
pub fn set_fn(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: impl Into<String>,
|
name: impl AsRef<str> + Into<ImmutableString>,
|
||||||
namespace: FnNamespace,
|
namespace: FnNamespace,
|
||||||
access: FnAccess,
|
access: FnAccess,
|
||||||
arg_names: Option<&[&str]>,
|
_arg_names: Option<&[&str]>,
|
||||||
arg_types: &[TypeId],
|
arg_types: &[TypeId],
|
||||||
func: CallableFunction,
|
func: CallableFunction,
|
||||||
) -> u64 {
|
) -> u64 {
|
||||||
let name = name.into();
|
|
||||||
let is_method = func.is_method();
|
let is_method = func.is_method();
|
||||||
|
|
||||||
let param_types = arg_types
|
let param_types = arg_types
|
||||||
@ -675,6 +687,15 @@ impl Module {
|
|||||||
|
|
||||||
let hash_fn = calc_native_fn_hash(empty(), &name, ¶m_types);
|
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(
|
self.functions.insert(
|
||||||
hash_fn,
|
hash_fn,
|
||||||
Box::new(FuncInfo {
|
Box::new(FuncInfo {
|
||||||
@ -683,11 +704,8 @@ impl Module {
|
|||||||
access,
|
access,
|
||||||
params: param_types.len(),
|
params: param_types.len(),
|
||||||
param_types,
|
param_types,
|
||||||
param_names: if let Some(p) = arg_names {
|
#[cfg(feature = "metadata")]
|
||||||
p.iter().map(|&v| v.into()).collect()
|
param_names,
|
||||||
} else {
|
|
||||||
Default::default()
|
|
||||||
},
|
|
||||||
func: func.into(),
|
func: func.into(),
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@ -765,16 +783,21 @@ impl Module {
|
|||||||
/// assert!(module.contains_fn(hash));
|
/// assert!(module.contains_fn(hash));
|
||||||
/// ```
|
/// ```
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn set_raw_fn<T: Variant + Clone>(
|
pub fn set_raw_fn<N, T, F>(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: impl Into<String>,
|
name: N,
|
||||||
namespace: FnNamespace,
|
namespace: FnNamespace,
|
||||||
access: FnAccess,
|
access: FnAccess,
|
||||||
arg_types: &[TypeId],
|
arg_types: &[TypeId],
|
||||||
func: impl Fn(NativeCallContext, &mut FnCallArgs) -> Result<T, Box<EvalAltResult>>
|
func: F,
|
||||||
|
) -> u64
|
||||||
|
where
|
||||||
|
N: AsRef<str> + Into<ImmutableString>,
|
||||||
|
T: Variant + Clone,
|
||||||
|
F: Fn(NativeCallContext, &mut FnCallArgs) -> Result<T, Box<EvalAltResult>>
|
||||||
+ SendSync
|
+ SendSync
|
||||||
+ 'static,
|
+ 'static,
|
||||||
) -> u64 {
|
{
|
||||||
let f =
|
let f =
|
||||||
move |ctx: NativeCallContext, args: &mut FnCallArgs| func(ctx, args).map(Dynamic::from);
|
move |ctx: NativeCallContext, args: &mut FnCallArgs| func(ctx, args).map(Dynamic::from);
|
||||||
|
|
||||||
@ -812,8 +835,9 @@ impl Module {
|
|||||||
/// assert!(module.contains_fn(hash));
|
/// assert!(module.contains_fn(hash));
|
||||||
/// ```
|
/// ```
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn set_native_fn<ARGS, T, F>(&mut self, name: impl Into<String>, func: F) -> u64
|
pub fn set_native_fn<ARGS, N, T, F>(&mut self, name: N, func: F) -> u64
|
||||||
where
|
where
|
||||||
|
N: AsRef<str> + Into<ImmutableString>,
|
||||||
T: Variant + Clone,
|
T: Variant + Clone,
|
||||||
F: RegisterNativeFunction<ARGS, Result<T, Box<EvalAltResult>>>,
|
F: RegisterNativeFunction<ARGS, Result<T, Box<EvalAltResult>>>,
|
||||||
{
|
{
|
||||||
@ -847,7 +871,7 @@ impl Module {
|
|||||||
/// ```
|
/// ```
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn set_getter_fn<ARGS, A, T, F>(&mut self, name: impl Into<String>, func: F) -> u64
|
pub fn set_getter_fn<ARGS, A, T, F>(&mut self, name: &str, func: F) -> u64
|
||||||
where
|
where
|
||||||
A: Variant + Clone,
|
A: Variant + Clone,
|
||||||
T: Variant + Clone,
|
T: Variant + Clone,
|
||||||
@ -855,7 +879,7 @@ impl Module {
|
|||||||
F: Fn(&mut A) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
|
F: Fn(&mut A) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
|
||||||
{
|
{
|
||||||
self.set_fn(
|
self.set_fn(
|
||||||
crate::engine::make_getter(&name.into()),
|
&crate::engine::make_getter(name),
|
||||||
FnNamespace::Global,
|
FnNamespace::Global,
|
||||||
FnAccess::Public,
|
FnAccess::Public,
|
||||||
None,
|
None,
|
||||||
@ -888,7 +912,7 @@ impl Module {
|
|||||||
/// ```
|
/// ```
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn set_setter_fn<ARGS, A, B, F>(&mut self, name: impl Into<String>, func: F) -> u64
|
pub fn set_setter_fn<ARGS, A, B, F>(&mut self, name: &str, func: F) -> u64
|
||||||
where
|
where
|
||||||
A: Variant + Clone,
|
A: Variant + Clone,
|
||||||
B: Variant + Clone,
|
B: Variant + Clone,
|
||||||
@ -896,7 +920,7 @@ impl Module {
|
|||||||
F: Fn(&mut A, B) -> Result<(), Box<EvalAltResult>> + SendSync + 'static,
|
F: Fn(&mut A, B) -> Result<(), Box<EvalAltResult>> + SendSync + 'static,
|
||||||
{
|
{
|
||||||
self.set_fn(
|
self.set_fn(
|
||||||
crate::engine::make_setter(&name.into()),
|
&crate::engine::make_setter(name),
|
||||||
FnNamespace::Global,
|
FnNamespace::Global,
|
||||||
FnAccess::Public,
|
FnAccess::Public,
|
||||||
None,
|
None,
|
||||||
@ -1061,11 +1085,16 @@ impl Module {
|
|||||||
/// ```
|
/// ```
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn set_indexer_get_set_fn<A: Variant + Clone, B: Variant + Clone, T: Variant + Clone>(
|
pub fn set_indexer_get_set_fn<A, B, T>(
|
||||||
&mut self,
|
&mut self,
|
||||||
get_fn: impl Fn(&mut A, B) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
|
get_fn: impl Fn(&mut A, B) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
|
||||||
set_fn: impl Fn(&mut A, B, T) -> Result<(), Box<EvalAltResult>> + SendSync + 'static,
|
set_fn: impl Fn(&mut A, B, T) -> Result<(), Box<EvalAltResult>> + SendSync + 'static,
|
||||||
) -> (u64, u64) {
|
) -> (u64, u64)
|
||||||
|
where
|
||||||
|
A: Variant + Clone,
|
||||||
|
B: Variant + Clone,
|
||||||
|
T: Variant + Clone,
|
||||||
|
{
|
||||||
(
|
(
|
||||||
self.set_indexer_get_fn(get_fn),
|
self.set_indexer_get_fn(get_fn),
|
||||||
self.set_indexer_set_fn(set_fn),
|
self.set_indexer_set_fn(set_fn),
|
||||||
|
@ -615,12 +615,12 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) {
|
|||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Expr::Dot(x, _) => match (&mut x.lhs, &mut x.rhs) {
|
Expr::Dot(x, _) => match (&mut x.lhs, &mut x.rhs) {
|
||||||
// map.string
|
// map.string
|
||||||
(Expr::Map(m, pos), Expr::Property(p)) if m.iter().all(|(_, x)| x.is_pure()) => {
|
(Expr::Map(m, pos), Expr::Property(p)) if m.0.iter().all(|(_, x)| x.is_pure()) => {
|
||||||
let prop = &p.2.name;
|
let prop = &p.2.name;
|
||||||
// Map literal where everything is pure - promote the indexed item.
|
// Map literal where everything is pure - promote the indexed item.
|
||||||
// All other items can be thrown away.
|
// All other items can be thrown away.
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
*expr = mem::take(m).into_iter().find(|(x, _)| &x.name == prop)
|
*expr = mem::take(&mut m.0).into_iter().find(|(x, _)| &x.name == prop)
|
||||||
.map(|(_, mut expr)| { expr.set_position(*pos); expr })
|
.map(|(_, mut expr)| { expr.set_position(*pos); expr })
|
||||||
.unwrap_or_else(|| Expr::Unit(*pos));
|
.unwrap_or_else(|| Expr::Unit(*pos));
|
||||||
}
|
}
|
||||||
@ -645,11 +645,11 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) {
|
|||||||
*expr = result;
|
*expr = result;
|
||||||
}
|
}
|
||||||
// map[string]
|
// map[string]
|
||||||
(Expr::Map(m, pos), Expr::StringConstant(s, _)) if m.iter().all(|(_, x)| x.is_pure()) => {
|
(Expr::Map(m, pos), Expr::StringConstant(s, _)) if m.0.iter().all(|(_, x)| x.is_pure()) => {
|
||||||
// Map literal where everything is pure - promote the indexed item.
|
// Map literal where everything is pure - promote the indexed item.
|
||||||
// All other items can be thrown away.
|
// All other items can be thrown away.
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
*expr = mem::take(m).into_iter().find(|(x, _)| x.name == *s)
|
*expr = mem::take(&mut m.0).into_iter().find(|(x, _)| x.name == *s)
|
||||||
.map(|(_, mut expr)| { expr.set_position(*pos); expr })
|
.map(|(_, mut expr)| { expr.set_position(*pos); expr })
|
||||||
.unwrap_or_else(|| Expr::Unit(*pos));
|
.unwrap_or_else(|| Expr::Unit(*pos));
|
||||||
}
|
}
|
||||||
@ -681,7 +681,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) {
|
|||||||
}
|
}
|
||||||
// #{ key:value, .. }
|
// #{ key:value, .. }
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Expr::Map(x, _) => x.iter_mut().for_each(|(_, expr)| optimize_expr(expr, state)),
|
Expr::Map(x, _) => x.0.iter_mut().for_each(|(_, expr)| optimize_expr(expr, state)),
|
||||||
// lhs && rhs
|
// lhs && rhs
|
||||||
Expr::And(x, _) => match (&mut x.lhs, &mut x.rhs) {
|
Expr::And(x, _) => match (&mut x.lhs, &mut x.rhs) {
|
||||||
// true && rhs -> rhs
|
// true && rhs -> rhs
|
||||||
|
@ -142,8 +142,10 @@ macro_rules! reg_range {
|
|||||||
($lib:ident | $x:expr => $( $y:ty ),*) => {
|
($lib:ident | $x:expr => $( $y:ty ),*) => {
|
||||||
$(
|
$(
|
||||||
$lib.set_iterator::<Range<$y>>();
|
$lib.set_iterator::<Range<$y>>();
|
||||||
let hash = $lib.set_native_fn($x, get_range::<$y>);
|
let _hash = $lib.set_native_fn($x, get_range::<$y>);
|
||||||
$lib.update_fn_metadata(hash, &[
|
|
||||||
|
#[cfg(feature = "metadata")]
|
||||||
|
$lib.update_fn_metadata(_hash, &[
|
||||||
concat!("from: ", stringify!($y)),
|
concat!("from: ", stringify!($y)),
|
||||||
concat!("to: ", stringify!($y)),
|
concat!("to: ", stringify!($y)),
|
||||||
concat!("Iterator<Item=", stringify!($y), ">")
|
concat!("Iterator<Item=", stringify!($y), ">")
|
||||||
@ -153,8 +155,10 @@ macro_rules! reg_range {
|
|||||||
($lib:ident | step $x:expr => $( $y:ty ),*) => {
|
($lib:ident | step $x:expr => $( $y:ty ),*) => {
|
||||||
$(
|
$(
|
||||||
$lib.set_iterator::<StepRange<$y>>();
|
$lib.set_iterator::<StepRange<$y>>();
|
||||||
let hash = $lib.set_native_fn($x, get_step_range::<$y>);
|
let _hash = $lib.set_native_fn($x, get_step_range::<$y>);
|
||||||
$lib.update_fn_metadata(hash, &[
|
|
||||||
|
#[cfg(feature = "metadata")]
|
||||||
|
$lib.update_fn_metadata(_hash, &[
|
||||||
concat!("from: ", stringify!($y)),
|
concat!("from: ", stringify!($y)),
|
||||||
concat!("to: ", stringify!($y)),
|
concat!("to: ", stringify!($y)),
|
||||||
concat!("step: ", stringify!($y)),
|
concat!("step: ", stringify!($y)),
|
||||||
@ -251,10 +255,12 @@ def_package!(crate:BasicIteratorPackage:"Basic range iterators.", lib, {
|
|||||||
|
|
||||||
lib.set_iterator::<StepDecimalRange>();
|
lib.set_iterator::<StepDecimalRange>();
|
||||||
|
|
||||||
let hash = lib.set_native_fn("range", |from, to| StepDecimalRange::new(from, to, Decimal::one()));
|
let _hash = lib.set_native_fn("range", |from, to| StepDecimalRange::new(from, to, Decimal::one()));
|
||||||
lib.update_fn_metadata(hash, &["from: Decimal", "to: Decimal", "Iterator<Item=Decimal>"]);
|
#[cfg(feature = "metadata")]
|
||||||
|
lib.update_fn_metadata(_hash, &["from: Decimal", "to: Decimal", "Iterator<Item=Decimal>"]);
|
||||||
|
|
||||||
let hash = lib.set_native_fn("range", |from, to, step| StepDecimalRange::new(from, to, step));
|
let _hash = lib.set_native_fn("range", |from, to, step| StepDecimalRange::new(from, to, step));
|
||||||
lib.update_fn_metadata(hash, &["from: Decimal", "to: Decimal", "step: Decimal", "Iterator<Item=Decimal>"]);
|
#[cfg(feature = "metadata")]
|
||||||
|
lib.update_fn_metadata(_hash, &["from: Decimal", "to: Decimal", "step: Decimal", "Iterator<Item=Decimal>"]);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -10,7 +10,6 @@ use crate::module::NamespaceRef;
|
|||||||
use crate::optimize::optimize_into_ast;
|
use crate::optimize::optimize_into_ast;
|
||||||
use crate::optimize::OptimizationLevel;
|
use crate::optimize::OptimizationLevel;
|
||||||
use crate::stdlib::{
|
use crate::stdlib::{
|
||||||
borrow::Cow,
|
|
||||||
boxed::Box,
|
boxed::Box,
|
||||||
collections::BTreeMap,
|
collections::BTreeMap,
|
||||||
format,
|
format,
|
||||||
@ -23,7 +22,7 @@ use crate::stdlib::{
|
|||||||
};
|
};
|
||||||
use crate::syntax::{CustomSyntax, MARKER_BLOCK, MARKER_EXPR, MARKER_IDENT};
|
use crate::syntax::{CustomSyntax, MARKER_BLOCK, MARKER_EXPR, MARKER_IDENT};
|
||||||
use crate::token::{is_keyword_function, is_valid_identifier, Token, TokenStream};
|
use crate::token::{is_keyword_function, is_valid_identifier, Token, TokenStream};
|
||||||
use crate::utils::get_hasher;
|
use crate::utils::{get_hasher, StringInterner};
|
||||||
use crate::{
|
use crate::{
|
||||||
calc_fn_hash, Dynamic, Engine, ImmutableString, LexError, ParseError, ParseErrorType, Position,
|
calc_fn_hash, Dynamic, Engine, ImmutableString, LexError, ParseError, ParseErrorType, Position,
|
||||||
Scope, Shared, StaticVec, AST,
|
Scope, Shared, StaticVec, AST,
|
||||||
@ -45,7 +44,7 @@ struct ParseState<'e> {
|
|||||||
/// Reference to the scripting [`Engine`].
|
/// Reference to the scripting [`Engine`].
|
||||||
engine: &'e Engine,
|
engine: &'e Engine,
|
||||||
/// Interned strings.
|
/// Interned strings.
|
||||||
interned_strings: BTreeMap<String, ImmutableString>,
|
interned_strings: StringInterner,
|
||||||
/// Encapsulates a local stack with variable names to simulate an actual runtime scope.
|
/// Encapsulates a local stack with variable names to simulate an actual runtime scope.
|
||||||
stack: Vec<(ImmutableString, AccessMode)>,
|
stack: Vec<(ImmutableString, AccessMode)>,
|
||||||
/// Size of the local variables stack upon entry of the current block scope.
|
/// Size of the local variables stack upon entry of the current block scope.
|
||||||
@ -161,24 +160,17 @@ impl<'e> ParseState<'e> {
|
|||||||
.iter()
|
.iter()
|
||||||
.rev()
|
.rev()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.find(|(_, n)| **n == name)
|
.find(|&(_, n)| *n == name)
|
||||||
.and_then(|(i, _)| NonZeroUsize::new(i + 1))
|
.and_then(|(i, _)| NonZeroUsize::new(i + 1))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get an interned string, creating one if it is not yet interned.
|
/// Get an interned string, creating one if it is not yet interned.
|
||||||
|
#[inline(always)]
|
||||||
pub fn get_interned_string(
|
pub fn get_interned_string(
|
||||||
&mut self,
|
&mut self,
|
||||||
text: impl AsRef<str> + Into<ImmutableString>,
|
text: impl AsRef<str> + Into<ImmutableString>,
|
||||||
) -> ImmutableString {
|
) -> ImmutableString {
|
||||||
#[allow(clippy::map_entry)]
|
self.interned_strings.get(text)
|
||||||
if !self.interned_strings.contains_key(text.as_ref()) {
|
|
||||||
let value = text.into();
|
|
||||||
self.interned_strings
|
|
||||||
.insert(value.clone().into(), value.clone());
|
|
||||||
value
|
|
||||||
} else {
|
|
||||||
self.interned_strings.get(text.as_ref()).unwrap().clone()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -690,6 +682,7 @@ fn parse_map_literal(
|
|||||||
settings.pos = eat_token(input, Token::MapStart);
|
settings.pos = eat_token(input, Token::MapStart);
|
||||||
|
|
||||||
let mut map: StaticVec<(Ident, Expr)> = Default::default();
|
let mut map: StaticVec<(Ident, Expr)> = Default::default();
|
||||||
|
let mut template: BTreeMap<ImmutableString, Dynamic> = Default::default();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
const MISSING_RBRACE: &str = "to end this object map literal";
|
const MISSING_RBRACE: &str = "to end this object map literal";
|
||||||
@ -760,6 +753,7 @@ fn parse_map_literal(
|
|||||||
|
|
||||||
let expr = parse_expr(input, state, lib, settings.level_up())?;
|
let expr = parse_expr(input, state, lib, settings.level_up())?;
|
||||||
let name = state.get_interned_string(name);
|
let name = state.get_interned_string(name);
|
||||||
|
template.insert(name.clone(), Default::default());
|
||||||
map.push((Ident { name, pos }, expr));
|
map.push((Ident { name, pos }, expr));
|
||||||
|
|
||||||
match input.peek().unwrap() {
|
match input.peek().unwrap() {
|
||||||
@ -784,7 +778,7 @@ fn parse_map_literal(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Expr::Map(Box::new(map), settings.pos))
|
Ok(Expr::Map(Box::new((map, template)), settings.pos))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a switch expression.
|
/// Parse a switch expression.
|
||||||
@ -1353,7 +1347,7 @@ fn parse_unary(
|
|||||||
|
|
||||||
/// Make an assignment statement.
|
/// Make an assignment statement.
|
||||||
fn make_assignment_stmt<'a>(
|
fn make_assignment_stmt<'a>(
|
||||||
op: Cow<'static, str>,
|
op: &'static str,
|
||||||
state: &mut ParseState,
|
state: &mut ParseState,
|
||||||
lhs: Expr,
|
lhs: Expr,
|
||||||
rhs: Expr,
|
rhs: Expr,
|
||||||
@ -1477,7 +1471,7 @@ fn parse_op_assignment_stmt(
|
|||||||
| Token::PowerOfAssign
|
| Token::PowerOfAssign
|
||||||
| Token::AndAssign
|
| Token::AndAssign
|
||||||
| Token::OrAssign
|
| Token::OrAssign
|
||||||
| Token::XOrAssign => token.syntax(),
|
| Token::XOrAssign => token.keyword_syntax(),
|
||||||
|
|
||||||
_ => return Ok(Stmt::Expr(lhs)),
|
_ => return Ok(Stmt::Expr(lhs)),
|
||||||
};
|
};
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
//! Module defining macros for developing _plugins_.
|
//! Module defining macros for developing _plugins_.
|
||||||
|
|
||||||
pub use crate::fn_native::{CallableFunction, FnCallArgs};
|
pub use crate::fn_native::{CallableFunction, FnCallArgs};
|
||||||
pub use crate::stdlib::{any::TypeId, boxed::Box, format, mem, string::ToString, vec as new_vec};
|
pub use crate::stdlib::{any::TypeId, boxed::Box, format, mem, string::ToString};
|
||||||
pub use crate::{
|
pub use crate::{
|
||||||
Dynamic, Engine, EvalAltResult, FnAccess, FnNamespace, ImmutableString, Module,
|
Dynamic, Engine, EvalAltResult, FnAccess, FnNamespace, ImmutableString, Module,
|
||||||
NativeCallContext, Position,
|
NativeCallContext, Position,
|
||||||
@ -26,13 +26,4 @@ pub trait PluginFunction {
|
|||||||
|
|
||||||
/// Is this plugin function variadic?
|
/// Is this plugin function variadic?
|
||||||
fn is_variadic(&self) -> bool;
|
fn is_variadic(&self) -> bool;
|
||||||
|
|
||||||
/// Convert a plugin function into a boxed trait object.
|
|
||||||
fn clone_boxed(&self) -> Box<dyn PluginFunction>;
|
|
||||||
|
|
||||||
/// Return a boxed slice of the names of the function's parameters and return type.
|
|
||||||
fn param_names(&self) -> Box<[&'static str]>;
|
|
||||||
|
|
||||||
/// Return a boxed slice of type ID's of the function's parameters.
|
|
||||||
fn input_types(&self) -> Box<[TypeId]>;
|
|
||||||
}
|
}
|
||||||
|
@ -71,6 +71,7 @@ impl<'a> IntoIterator for Scope<'a> {
|
|||||||
type Item = (Cow<'a, str>, Dynamic);
|
type Item = (Cow<'a, str>, Dynamic);
|
||||||
type IntoIter = Box<dyn Iterator<Item = Self::Item> + 'a>;
|
type IntoIter = Box<dyn Iterator<Item = Self::Item> + 'a>;
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
Box::new(
|
Box::new(
|
||||||
self.values
|
self.values
|
||||||
|
@ -211,7 +211,8 @@ impl From<&crate::Module> for ModuleMetadata {
|
|||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
impl Engine {
|
impl Engine {
|
||||||
/// _(METADATA)_ Generate a list of all functions (including those defined in an
|
/// _(METADATA)_ Generate a list of all functions (including those defined in an
|
||||||
/// [`AST`][crate::AST]) in JSON format. Available only under the `metadata` feature.
|
/// [`AST`][crate::AST]) in JSON format.
|
||||||
|
/// Available under the `metadata` feature only.
|
||||||
///
|
///
|
||||||
/// Functions from the following sources are included:
|
/// Functions from the following sources are included:
|
||||||
/// 1) Functions defined in an [`AST`][crate::AST]
|
/// 1) Functions defined in an [`AST`][crate::AST]
|
||||||
|
@ -8,6 +8,7 @@ mod serialize;
|
|||||||
mod str;
|
mod str;
|
||||||
|
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
mod metadata;
|
mod metadata;
|
||||||
|
|
||||||
pub use de::from_dynamic;
|
pub use de::from_dynamic;
|
||||||
|
50
src/token.rs
50
src/token.rs
@ -381,25 +381,15 @@ pub enum Token {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Token {
|
impl Token {
|
||||||
/// Get the syntax of the token.
|
/// Get the syntax of the token if it is a keyword.
|
||||||
pub fn syntax(&self) -> Cow<'static, str> {
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if the token is not a keyword.
|
||||||
|
pub fn keyword_syntax(&self) -> &'static str {
|
||||||
use Token::*;
|
use Token::*;
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
IntegerConstant(i) => i.to_string().into(),
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
|
||||||
FloatConstant(f) => f.to_string().into(),
|
|
||||||
#[cfg(feature = "decimal")]
|
|
||||||
DecimalConstant(d) => d.to_string().into(),
|
|
||||||
StringConstant(_) => "string".into(),
|
|
||||||
CharConstant(c) => c.to_string().into(),
|
|
||||||
Identifier(s) => s.clone().into(),
|
|
||||||
Reserved(s) => s.clone().into(),
|
|
||||||
Custom(s) => s.clone().into(),
|
|
||||||
LexError(err) => err.to_string().into(),
|
|
||||||
Comment(s) => s.clone().into(),
|
|
||||||
|
|
||||||
token => match token {
|
|
||||||
LeftBrace => "{",
|
LeftBrace => "{",
|
||||||
RightBrace => "}",
|
RightBrace => "}",
|
||||||
LeftParen => "(",
|
LeftParen => "(",
|
||||||
@ -479,10 +469,32 @@ impl Token {
|
|||||||
Export => "export",
|
Export => "export",
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
As => "as",
|
As => "as",
|
||||||
EOF => "{EOF}",
|
|
||||||
t => unreachable!("operator should be matched in outer scope: {:?}", t),
|
t => unreachable!("{:?} is not a keyword", t),
|
||||||
}
|
}
|
||||||
.into(),
|
}
|
||||||
|
|
||||||
|
/// Get the syntax of the token.
|
||||||
|
pub fn syntax(&self) -> Cow<'static, str> {
|
||||||
|
use Token::*;
|
||||||
|
|
||||||
|
match self {
|
||||||
|
IntegerConstant(i) => i.to_string().into(),
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
FloatConstant(f) => f.to_string().into(),
|
||||||
|
#[cfg(feature = "decimal")]
|
||||||
|
DecimalConstant(d) => d.to_string().into(),
|
||||||
|
StringConstant(_) => "string".into(),
|
||||||
|
CharConstant(c) => c.to_string().into(),
|
||||||
|
Identifier(s) => s.clone().into(),
|
||||||
|
Reserved(s) => s.clone().into(),
|
||||||
|
Custom(s) => s.clone().into(),
|
||||||
|
LexError(err) => err.to_string().into(),
|
||||||
|
Comment(s) => s.clone().into(),
|
||||||
|
|
||||||
|
EOF => "{EOF}".into(),
|
||||||
|
|
||||||
|
token => token.keyword_syntax().into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
35
src/utils.rs
35
src/utils.rs
@ -6,6 +6,7 @@ use crate::stdlib::{
|
|||||||
borrow::Borrow,
|
borrow::Borrow,
|
||||||
boxed::Box,
|
boxed::Box,
|
||||||
cmp::Ordering,
|
cmp::Ordering,
|
||||||
|
collections::BTreeSet,
|
||||||
fmt,
|
fmt,
|
||||||
fmt::{Debug, Display},
|
fmt::{Debug, Display},
|
||||||
hash::{BuildHasher, Hash, Hasher},
|
hash::{BuildHasher, Hash, Hasher},
|
||||||
@ -70,7 +71,11 @@ pub fn get_hasher() -> ahash::AHasher {
|
|||||||
///
|
///
|
||||||
/// The first module name is skipped. Hashing starts from the _second_ module in the chain.
|
/// The first module name is skipped. Hashing starts from the _second_ module in the chain.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn calc_fn_hash<'a>(modules: impl Iterator<Item = &'a str>, fn_name: &str, num: usize) -> u64 {
|
pub fn calc_fn_hash<'a>(
|
||||||
|
modules: impl Iterator<Item = &'a str>,
|
||||||
|
fn_name: impl AsRef<str>,
|
||||||
|
num: usize,
|
||||||
|
) -> u64 {
|
||||||
let s = &mut get_hasher();
|
let s = &mut get_hasher();
|
||||||
|
|
||||||
// We always skip the first module
|
// We always skip the first module
|
||||||
@ -80,7 +85,7 @@ pub fn calc_fn_hash<'a>(modules: impl Iterator<Item = &'a str>, fn_name: &str, n
|
|||||||
.skip(1)
|
.skip(1)
|
||||||
.for_each(|m| m.hash(s));
|
.for_each(|m| m.hash(s));
|
||||||
len.hash(s);
|
len.hash(s);
|
||||||
fn_name.hash(s);
|
fn_name.as_ref().hash(s);
|
||||||
num.hash(s);
|
num.hash(s);
|
||||||
s.finish()
|
s.finish()
|
||||||
}
|
}
|
||||||
@ -137,7 +142,7 @@ pub(crate) fn combine_hashes(a: u64, b: u64) -> u64 {
|
|||||||
/// assert_ne!(s2.as_str(), s.as_str());
|
/// assert_ne!(s2.as_str(), s.as_str());
|
||||||
/// assert_eq!(s, "hello, world!");
|
/// assert_eq!(s, "hello, world!");
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Default)]
|
#[derive(Clone, Eq, Ord, Hash, Default)]
|
||||||
pub struct ImmutableString(Shared<String>);
|
pub struct ImmutableString(Shared<String>);
|
||||||
|
|
||||||
impl Deref for ImmutableString {
|
impl Deref for ImmutableString {
|
||||||
@ -156,6 +161,13 @@ impl AsRef<String> for ImmutableString {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl AsRef<str> for ImmutableString {
|
||||||
|
#[inline(always)]
|
||||||
|
fn as_ref(&self) -> &str {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Borrow<String> for ImmutableString {
|
impl Borrow<String> for ImmutableString {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn borrow(&self) -> &String {
|
fn borrow(&self) -> &String {
|
||||||
@ -559,7 +571,6 @@ impl PartialEq<ImmutableString> for String {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<S: AsRef<str>> PartialOrd<S> for ImmutableString {
|
impl<S: AsRef<str>> PartialOrd<S> for ImmutableString {
|
||||||
#[inline(always)]
|
|
||||||
fn partial_cmp(&self, other: &S) -> Option<Ordering> {
|
fn partial_cmp(&self, other: &S) -> Option<Ordering> {
|
||||||
self.as_str().partial_cmp(other.as_ref())
|
self.as_str().partial_cmp(other.as_ref())
|
||||||
}
|
}
|
||||||
@ -594,3 +605,19 @@ impl ImmutableString {
|
|||||||
shared_make_mut(&mut self.0)
|
shared_make_mut(&mut self.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A collection of interned strings.
|
||||||
|
#[derive(Debug, Clone, Default, Hash)]
|
||||||
|
pub struct StringInterner(BTreeSet<ImmutableString>);
|
||||||
|
|
||||||
|
impl StringInterner {
|
||||||
|
/// Get an interned string, creating one if it is not yet interned.
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn get(&mut self, text: impl AsRef<str> + Into<ImmutableString>) -> ImmutableString {
|
||||||
|
self.0.get(text.as_ref()).cloned().unwrap_or_else(|| {
|
||||||
|
let s = text.into();
|
||||||
|
self.0.insert(s.clone());
|
||||||
|
s
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -34,7 +34,7 @@ macro_rules! register_in_bulk {
|
|||||||
{
|
{
|
||||||
let type_str = stringify!($type_names);
|
let type_str = stringify!($type_names);
|
||||||
set_exported_fn!($mod_name,
|
set_exported_fn!($mod_name,
|
||||||
format!(concat!(stringify!($op_name), "_{}"), type_str),
|
&format!(concat!(stringify!($op_name), "_{}"), type_str),
|
||||||
crate::$op_name::$type_names::op);
|
crate::$op_name::$type_names::op);
|
||||||
}
|
}
|
||||||
)*
|
)*
|
||||||
|
Loading…
Reference in New Issue
Block a user