Merge pull request #262 from schungx/master
Support NativeCallContext parameter in plugin functions.
This commit is contained in:
commit
5c7c947488
@ -24,7 +24,7 @@ categories = [ "no-std", "embedded", "wasm", "parser-implementations" ]
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
smallvec = { version = "1.4.2", default-features = false }
|
smallvec = { version = "1.4.2", default-features = false }
|
||||||
rhai_codegen = { version = "0.1", path = "codegen" }
|
rhai_codegen = { version = "0.2", path = "codegen" }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
|
28
RELEASES.md
28
RELEASES.md
@ -1,6 +1,34 @@
|
|||||||
Rhai Release Notes
|
Rhai Release Notes
|
||||||
==================
|
==================
|
||||||
|
|
||||||
|
|
||||||
|
Version 0.19.3
|
||||||
|
==============
|
||||||
|
|
||||||
|
Breaking changes
|
||||||
|
----------------
|
||||||
|
|
||||||
|
* `EvalAltResult::ErrorReadingScriptFile` is removed in favor of the new `EvalAltResult::ErrorSystem`.
|
||||||
|
* `EvalAltResult::ErrorLoopBreak` is renamed to `EvalAltResult::LoopBreak`.
|
||||||
|
* `Engine::register_raw_fn` and `FnPtr::call_dynamic` function signatures have changed.
|
||||||
|
|
||||||
|
New features
|
||||||
|
------------
|
||||||
|
|
||||||
|
* The plugins system is enhanced to support functions taking a `NativeCallContext` as the first parameter.
|
||||||
|
|
||||||
|
Enhancements
|
||||||
|
------------
|
||||||
|
|
||||||
|
* Calling `eval` or `Fn` in method-call style, which is an error, is now caught during parsing.
|
||||||
|
|
||||||
|
|
||||||
|
Version 0.19.2
|
||||||
|
==============
|
||||||
|
|
||||||
|
Bug fix on call module functions.
|
||||||
|
|
||||||
|
|
||||||
Version 0.19.1
|
Version 0.19.1
|
||||||
==============
|
==============
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "rhai_codegen"
|
name = "rhai_codegen"
|
||||||
version = "0.1.1"
|
version = "0.2.0"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
authors = ["jhwgh1968"]
|
authors = ["jhwgh1968"]
|
||||||
description = "Procedural macro support package for Rhai, a scripting language for Rust"
|
description = "Procedural macro support package for Rhai, a scripting language for Rust"
|
||||||
|
@ -222,6 +222,7 @@ pub(crate) struct ExportedFn {
|
|||||||
entire_span: proc_macro2::Span,
|
entire_span: proc_macro2::Span,
|
||||||
signature: syn::Signature,
|
signature: syn::Signature,
|
||||||
is_public: bool,
|
is_public: bool,
|
||||||
|
pass_context: bool,
|
||||||
return_dynamic: bool,
|
return_dynamic: bool,
|
||||||
mut_receiver: bool,
|
mut_receiver: bool,
|
||||||
params: ExportedFnParams,
|
params: ExportedFnParams,
|
||||||
@ -237,15 +238,36 @@ impl Parse for ExportedFn {
|
|||||||
let dynamic_type_path2 = syn::parse2::<syn::Path>(quote! { rhai::Dynamic }).unwrap();
|
let dynamic_type_path2 = syn::parse2::<syn::Path>(quote! { rhai::Dynamic }).unwrap();
|
||||||
let mut return_dynamic = false;
|
let mut return_dynamic = false;
|
||||||
|
|
||||||
|
let context_type_path1 = syn::parse2::<syn::Path>(quote! { NativeCallContext }).unwrap();
|
||||||
|
let context_type_path2 =
|
||||||
|
syn::parse2::<syn::Path>(quote! { rhai::NativeCallContext }).unwrap();
|
||||||
|
let mut pass_context = false;
|
||||||
|
|
||||||
// #[cfg] attributes are not allowed on functions due to what is generated for them
|
// #[cfg] attributes are not allowed on functions due to what is generated for them
|
||||||
crate::attrs::deny_cfg_attr(&fn_all.attrs)?;
|
crate::attrs::deny_cfg_attr(&fn_all.attrs)?;
|
||||||
|
|
||||||
// Determine if the function is public.
|
// Determine if the function is public.
|
||||||
let is_public = matches!(fn_all.vis, syn::Visibility::Public(_));
|
let is_public = matches!(fn_all.vis, syn::Visibility::Public(_));
|
||||||
// Determine whether function generates a special calling convention for a mutable
|
|
||||||
// reciever.
|
// Determine if the function requires a call context
|
||||||
let mut_receiver = {
|
|
||||||
if let Some(first_arg) = fn_all.sig.inputs.first() {
|
if let Some(first_arg) = fn_all.sig.inputs.first() {
|
||||||
|
if let syn::FnArg::Typed(syn::PatType { ref ty, .. }) = first_arg {
|
||||||
|
match flatten_type_groups(ty.as_ref()) {
|
||||||
|
syn::Type::Path(p)
|
||||||
|
if p.path == context_type_path1 || p.path == context_type_path2 =>
|
||||||
|
{
|
||||||
|
pass_context = true;
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let skip_slots = if pass_context { 1 } else { 0 };
|
||||||
|
|
||||||
|
// Determine whether function generates a special calling convention for a mutable receiver.
|
||||||
|
let mut_receiver = {
|
||||||
|
if let Some(first_arg) = fn_all.sig.inputs.iter().skip(skip_slots).next() {
|
||||||
match first_arg {
|
match first_arg {
|
||||||
syn::FnArg::Receiver(syn::Receiver {
|
syn::FnArg::Receiver(syn::Receiver {
|
||||||
reference: Some(_), ..
|
reference: Some(_), ..
|
||||||
@ -265,8 +287,7 @@ impl Parse for ExportedFn {
|
|||||||
_ => {
|
_ => {
|
||||||
return Err(syn::Error::new(
|
return Err(syn::Error::new(
|
||||||
ty.span(),
|
ty.span(),
|
||||||
"references from Rhai in this position \
|
"references from Rhai in this position must be mutable",
|
||||||
must be mutable",
|
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -281,7 +302,7 @@ impl Parse for ExportedFn {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// All arguments after the first must be moved except for &str.
|
// All arguments after the first must be moved except for &str.
|
||||||
for arg in fn_all.sig.inputs.iter().skip(1) {
|
for arg in fn_all.sig.inputs.iter().skip(skip_slots + 1) {
|
||||||
let ty = match arg {
|
let ty = match arg {
|
||||||
syn::FnArg::Typed(syn::PatType { ref ty, .. }) => ty,
|
syn::FnArg::Typed(syn::PatType { ref ty, .. }) => ty,
|
||||||
_ => panic!("internal error: receiver argument outside of first position!?"),
|
_ => panic!("internal error: receiver argument outside of first position!?"),
|
||||||
@ -304,8 +325,7 @@ impl Parse for ExportedFn {
|
|||||||
if !is_ok {
|
if !is_ok {
|
||||||
return Err(syn::Error::new(
|
return Err(syn::Error::new(
|
||||||
ty.span(),
|
ty.span(),
|
||||||
"this type in this position passes from \
|
"this type in this position passes from Rhai by value",
|
||||||
Rhai by value",
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -337,6 +357,7 @@ impl Parse for ExportedFn {
|
|||||||
entire_span,
|
entire_span,
|
||||||
signature: fn_all.sig,
|
signature: fn_all.sig,
|
||||||
is_public,
|
is_public,
|
||||||
|
pass_context,
|
||||||
return_dynamic,
|
return_dynamic,
|
||||||
mut_receiver,
|
mut_receiver,
|
||||||
params: ExportedFnParams::default(),
|
params: ExportedFnParams::default(),
|
||||||
@ -363,6 +384,10 @@ impl ExportedFn {
|
|||||||
self.params.skip
|
self.params.skip
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn pass_context(&self) -> bool {
|
||||||
|
self.pass_context
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn signature(&self) -> &syn::Signature {
|
pub(crate) fn signature(&self) -> &syn::Signature {
|
||||||
&self.signature
|
&self.signature
|
||||||
}
|
}
|
||||||
@ -418,11 +443,13 @@ impl ExportedFn {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn arg_list(&self) -> impl Iterator<Item = &syn::FnArg> {
|
pub(crate) fn arg_list(&self) -> impl Iterator<Item = &syn::FnArg> {
|
||||||
self.signature.inputs.iter()
|
let skip = if self.pass_context { 1 } else { 0 };
|
||||||
|
self.signature.inputs.iter().skip(skip)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn arg_count(&self) -> usize {
|
pub(crate) fn arg_count(&self) -> usize {
|
||||||
self.signature.inputs.len()
|
let skip = if self.pass_context { 1 } else { 0 };
|
||||||
|
self.signature.inputs.len() - skip
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn return_type(&self) -> Option<&syn::Type> {
|
pub(crate) fn return_type(&self) -> Option<&syn::Type> {
|
||||||
@ -625,6 +652,10 @@ impl ExportedFn {
|
|||||||
let mut input_type_exprs: Vec<syn::Expr> = Vec::new();
|
let mut input_type_exprs: Vec<syn::Expr> = Vec::new();
|
||||||
let skip_first_arg;
|
let skip_first_arg;
|
||||||
|
|
||||||
|
if self.pass_context {
|
||||||
|
unpack_exprs.push(syn::parse2::<syn::Expr>(quote! { context }).unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
// Handle the first argument separately if the function has a "method like" receiver
|
// Handle the first argument separately if the function has a "method like" receiver
|
||||||
if is_method_call {
|
if is_method_call {
|
||||||
skip_first_arg = true;
|
skip_first_arg = true;
|
||||||
@ -764,9 +795,7 @@ 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 PluginFunction for #type_name {
|
impl PluginFunction for #type_name {
|
||||||
fn call(&self,
|
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
args: &mut [&mut Dynamic]
|
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
|
||||||
debug_assert_eq!(args.len(), #arg_count,
|
debug_assert_eq!(args.len(), #arg_count,
|
||||||
"wrong arg count: {} != {}",
|
"wrong arg count: {} != {}",
|
||||||
args.len(), #arg_count);
|
args.len(), #arg_count);
|
||||||
|
@ -68,7 +68,7 @@ pub(crate) fn generate_body(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// NB: these are token streams, because reparsing messes up "> >" vs ">>"
|
// NB: these are token streams, because re-parsing messes up "> >" vs ">>"
|
||||||
let mut gen_fn_tokens: Vec<proc_macro2::TokenStream> = Vec::new();
|
let mut gen_fn_tokens: Vec<proc_macro2::TokenStream> = Vec::new();
|
||||||
for function in fns {
|
for function in fns {
|
||||||
function.update_scope(&parent_scope);
|
function.update_scope(&parent_scope);
|
||||||
|
@ -277,9 +277,7 @@ mod generate_tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
struct Token();
|
struct Token();
|
||||||
impl PluginFunction for Token {
|
impl PluginFunction for Token {
|
||||||
fn call(&self,
|
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
args: &mut [&mut Dynamic]
|
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
|
||||||
debug_assert_eq!(args.len(), 0usize,
|
debug_assert_eq!(args.len(), 0usize,
|
||||||
"wrong arg count: {} != {}", args.len(), 0usize);
|
"wrong arg count: {} != {}", args.len(), 0usize);
|
||||||
Ok(Dynamic::from(do_nothing()))
|
Ok(Dynamic::from(do_nothing()))
|
||||||
@ -320,9 +318,7 @@ mod generate_tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
struct Token();
|
struct Token();
|
||||||
impl PluginFunction for Token {
|
impl PluginFunction for Token {
|
||||||
fn call(&self,
|
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
args: &mut [&mut Dynamic]
|
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
|
||||||
debug_assert_eq!(args.len(), 1usize,
|
debug_assert_eq!(args.len(), 1usize,
|
||||||
"wrong arg count: {} != {}", 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>();
|
||||||
@ -352,6 +348,49 @@ mod generate_tests {
|
|||||||
assert_streams_eq(item_fn.generate(), expected_tokens);
|
assert_streams_eq(item_fn.generate(), expected_tokens);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn one_arg_fn_with_context() {
|
||||||
|
let input_tokens: TokenStream = quote! {
|
||||||
|
pub fn do_something(context: NativeCallContext, x: usize) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
let expected_tokens = quote! {
|
||||||
|
#[allow(unused)]
|
||||||
|
pub mod rhai_fn_do_something {
|
||||||
|
use super::*;
|
||||||
|
struct Token();
|
||||||
|
impl PluginFunction for Token {
|
||||||
|
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
|
debug_assert_eq!(args.len(), 1usize,
|
||||||
|
"wrong arg count: {} != {}", args.len(), 1usize);
|
||||||
|
let arg0 = mem::take(args[0usize]).cast::<usize>();
|
||||||
|
Ok(Dynamic::from(do_something(context, arg0)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_method_call(&self) -> bool { false }
|
||||||
|
fn is_variadic(&self) -> bool { false }
|
||||||
|
fn clone_boxed(&self) -> Box<dyn PluginFunction> { Box::new(Token()) }
|
||||||
|
fn input_types(&self) -> Box<[TypeId]> {
|
||||||
|
new_vec![TypeId::of::<usize>()].into_boxed_slice()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn token_callable() -> CallableFunction {
|
||||||
|
Token().into()
|
||||||
|
}
|
||||||
|
pub fn token_input_types() -> Box<[TypeId]> {
|
||||||
|
Token().input_types()
|
||||||
|
}
|
||||||
|
pub fn dynamic_result_fn(context: NativeCallContext, x: usize) -> Result<Dynamic, Box<EvalAltResult> > {
|
||||||
|
Ok(Dynamic::from(super::do_something(context, x)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let item_fn = syn::parse2::<ExportedFn>(input_tokens).unwrap();
|
||||||
|
assert!(item_fn.pass_context());
|
||||||
|
assert_streams_eq(item_fn.generate(), expected_tokens);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn return_dynamic() {
|
fn return_dynamic() {
|
||||||
let input_tokens: TokenStream = quote! {
|
let input_tokens: TokenStream = quote! {
|
||||||
@ -366,9 +405,7 @@ mod generate_tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
struct Token();
|
struct Token();
|
||||||
impl PluginFunction for Token {
|
impl PluginFunction for Token {
|
||||||
fn call(&self,
|
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
args: &mut [&mut Dynamic]
|
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
|
||||||
debug_assert_eq!(args.len(), 0usize,
|
debug_assert_eq!(args.len(), 0usize,
|
||||||
"wrong arg count: {} != {}", args.len(), 0usize);
|
"wrong arg count: {} != {}", args.len(), 0usize);
|
||||||
Ok(return_dynamic())
|
Ok(return_dynamic())
|
||||||
@ -405,9 +442,7 @@ mod generate_tests {
|
|||||||
|
|
||||||
let expected_tokens = quote! {
|
let expected_tokens = quote! {
|
||||||
impl PluginFunction for MyType {
|
impl PluginFunction for MyType {
|
||||||
fn call(&self,
|
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
args: &mut [&mut Dynamic]
|
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
|
||||||
debug_assert_eq!(args.len(), 1usize,
|
debug_assert_eq!(args.len(), 1usize,
|
||||||
"wrong arg count: {} != {}", 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>();
|
||||||
@ -439,9 +474,7 @@ mod generate_tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
struct Token();
|
struct Token();
|
||||||
impl PluginFunction for Token {
|
impl PluginFunction for Token {
|
||||||
fn call(&self,
|
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
args: &mut [&mut Dynamic]
|
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
|
||||||
debug_assert_eq!(args.len(), 2usize,
|
debug_assert_eq!(args.len(), 2usize,
|
||||||
"wrong arg count: {} != {}", 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>();
|
||||||
@ -485,9 +518,7 @@ mod generate_tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
struct Token();
|
struct Token();
|
||||||
impl PluginFunction for Token {
|
impl PluginFunction for Token {
|
||||||
fn call(&self,
|
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
args: &mut [&mut Dynamic]
|
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
|
||||||
debug_assert_eq!(args.len(), 2usize,
|
debug_assert_eq!(args.len(), 2usize,
|
||||||
"wrong arg count: {} != {}", args.len(), 2usize);
|
"wrong arg count: {} != {}", args.len(), 2usize);
|
||||||
let arg1 = mem::take(args[1usize]).cast::<usize>();
|
let arg1 = mem::take(args[1usize]).cast::<usize>();
|
||||||
@ -532,9 +563,7 @@ mod generate_tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
struct Token();
|
struct Token();
|
||||||
impl PluginFunction for Token {
|
impl PluginFunction for Token {
|
||||||
fn call(&self,
|
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
args: &mut [&mut Dynamic]
|
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
|
||||||
debug_assert_eq!(args.len(), 1usize,
|
debug_assert_eq!(args.len(), 1usize,
|
||||||
"wrong arg count: {} != {}", 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();
|
||||||
|
@ -302,9 +302,7 @@ mod generate_tests {
|
|||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
struct get_mystic_number_token();
|
struct get_mystic_number_token();
|
||||||
impl PluginFunction for get_mystic_number_token {
|
impl PluginFunction for get_mystic_number_token {
|
||||||
fn call(&self,
|
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
args: &mut [&mut Dynamic]
|
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
|
||||||
debug_assert_eq!(args.len(), 0usize,
|
debug_assert_eq!(args.len(), 0usize,
|
||||||
"wrong arg count: {} != {}", args.len(), 0usize);
|
"wrong arg count: {} != {}", args.len(), 0usize);
|
||||||
Ok(Dynamic::from(get_mystic_number()))
|
Ok(Dynamic::from(get_mystic_number()))
|
||||||
@ -364,9 +362,7 @@ mod generate_tests {
|
|||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
struct add_one_to_token();
|
struct add_one_to_token();
|
||||||
impl PluginFunction for add_one_to_token {
|
impl PluginFunction for add_one_to_token {
|
||||||
fn call(&self,
|
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
args: &mut [&mut Dynamic]
|
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
|
||||||
debug_assert_eq!(args.len(), 1usize,
|
debug_assert_eq!(args.len(), 1usize,
|
||||||
"wrong arg count: {} != {}", args.len(), 1usize);
|
"wrong arg count: {} != {}", args.len(), 1usize);
|
||||||
let arg0 = mem::take(args[0usize]).cast::<INT>();
|
let arg0 = mem::take(args[0usize]).cast::<INT>();
|
||||||
@ -441,9 +437,7 @@ mod generate_tests {
|
|||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
struct add_one_to_token();
|
struct add_one_to_token();
|
||||||
impl PluginFunction for add_one_to_token {
|
impl PluginFunction for add_one_to_token {
|
||||||
fn call(&self,
|
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
args: &mut [&mut Dynamic]
|
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
|
||||||
debug_assert_eq!(args.len(), 1usize,
|
debug_assert_eq!(args.len(), 1usize,
|
||||||
"wrong arg count: {} != {}", args.len(), 1usize);
|
"wrong arg count: {} != {}", args.len(), 1usize);
|
||||||
let arg0 = mem::take(args[0usize]).cast::<INT>();
|
let arg0 = mem::take(args[0usize]).cast::<INT>();
|
||||||
@ -469,9 +463,7 @@ mod generate_tests {
|
|||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
struct add_n_to_token();
|
struct add_n_to_token();
|
||||||
impl PluginFunction for add_n_to_token {
|
impl PluginFunction for add_n_to_token {
|
||||||
fn call(&self,
|
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
args: &mut [&mut Dynamic]
|
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
|
||||||
debug_assert_eq!(args.len(), 2usize,
|
debug_assert_eq!(args.len(), 2usize,
|
||||||
"wrong arg count: {} != {}", args.len(), 2usize);
|
"wrong arg count: {} != {}", args.len(), 2usize);
|
||||||
let arg0 = mem::take(args[0usize]).cast::<INT>();
|
let arg0 = mem::take(args[0usize]).cast::<INT>();
|
||||||
@ -535,9 +527,7 @@ mod generate_tests {
|
|||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
struct add_together_token();
|
struct add_together_token();
|
||||||
impl PluginFunction for add_together_token {
|
impl PluginFunction for add_together_token {
|
||||||
fn call(&self,
|
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
args: &mut [&mut Dynamic]
|
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
|
||||||
debug_assert_eq!(args.len(), 2usize,
|
debug_assert_eq!(args.len(), 2usize,
|
||||||
"wrong arg count: {} != {}", args.len(), 2usize);
|
"wrong arg count: {} != {}", args.len(), 2usize);
|
||||||
let arg0 = mem::take(args[0usize]).cast::<INT>();
|
let arg0 = mem::take(args[0usize]).cast::<INT>();
|
||||||
@ -608,9 +598,7 @@ mod generate_tests {
|
|||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
struct add_together_token();
|
struct add_together_token();
|
||||||
impl PluginFunction for add_together_token {
|
impl PluginFunction for add_together_token {
|
||||||
fn call(&self,
|
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
args: &mut [&mut Dynamic]
|
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
|
||||||
debug_assert_eq!(args.len(), 2usize,
|
debug_assert_eq!(args.len(), 2usize,
|
||||||
"wrong arg count: {} != {}", args.len(), 2usize);
|
"wrong arg count: {} != {}", args.len(), 2usize);
|
||||||
let arg0 = mem::take(args[0usize]).cast::<INT>();
|
let arg0 = mem::take(args[0usize]).cast::<INT>();
|
||||||
@ -850,9 +838,7 @@ mod generate_tests {
|
|||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
struct get_mystic_number_token();
|
struct get_mystic_number_token();
|
||||||
impl PluginFunction for get_mystic_number_token {
|
impl PluginFunction for get_mystic_number_token {
|
||||||
fn call(&self,
|
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
args: &mut [&mut Dynamic]
|
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
|
||||||
debug_assert_eq!(args.len(), 0usize,
|
debug_assert_eq!(args.len(), 0usize,
|
||||||
"wrong arg count: {} != {}", args.len(), 0usize);
|
"wrong arg count: {} != {}", args.len(), 0usize);
|
||||||
Ok(Dynamic::from(get_mystic_number()))
|
Ok(Dynamic::from(get_mystic_number()))
|
||||||
@ -943,9 +929,7 @@ mod generate_tests {
|
|||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
struct print_out_to_token();
|
struct print_out_to_token();
|
||||||
impl PluginFunction for print_out_to_token {
|
impl PluginFunction for print_out_to_token {
|
||||||
fn call(&self,
|
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
args: &mut [&mut Dynamic]
|
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
|
||||||
debug_assert_eq!(args.len(), 1usize,
|
debug_assert_eq!(args.len(), 1usize,
|
||||||
"wrong arg count: {} != {}", 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();
|
||||||
@ -1007,9 +991,7 @@ mod generate_tests {
|
|||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
struct print_out_to_token();
|
struct print_out_to_token();
|
||||||
impl PluginFunction for print_out_to_token {
|
impl PluginFunction for print_out_to_token {
|
||||||
fn call(&self,
|
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
args: &mut [&mut Dynamic]
|
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
|
||||||
debug_assert_eq!(args.len(), 1usize,
|
debug_assert_eq!(args.len(), 1usize,
|
||||||
"wrong arg count: {} != {}", args.len(), 1usize);
|
"wrong arg count: {} != {}", args.len(), 1usize);
|
||||||
let arg0 = mem::take(args[0usize]).take_string().unwrap();
|
let arg0 = mem::take(args[0usize]).take_string().unwrap();
|
||||||
@ -1071,9 +1053,7 @@ mod generate_tests {
|
|||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
struct increment_token();
|
struct increment_token();
|
||||||
impl PluginFunction for increment_token {
|
impl PluginFunction for increment_token {
|
||||||
fn call(&self,
|
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
args: &mut [&mut Dynamic]
|
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
|
||||||
debug_assert_eq!(args.len(), 1usize,
|
debug_assert_eq!(args.len(), 1usize,
|
||||||
"wrong arg count: {} != {}", args.len(), 1usize);
|
"wrong arg count: {} != {}", args.len(), 1usize);
|
||||||
let arg0 = &mut args[0usize].write_lock::<FLOAT>().unwrap();
|
let arg0 = &mut args[0usize].write_lock::<FLOAT>().unwrap();
|
||||||
@ -1138,9 +1118,7 @@ mod generate_tests {
|
|||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
struct increment_token();
|
struct increment_token();
|
||||||
impl PluginFunction for increment_token {
|
impl PluginFunction for increment_token {
|
||||||
fn call(&self,
|
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
args: &mut [&mut Dynamic]
|
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
|
||||||
debug_assert_eq!(args.len(), 1usize,
|
debug_assert_eq!(args.len(), 1usize,
|
||||||
"wrong arg count: {} != {}", args.len(), 1usize);
|
"wrong arg count: {} != {}", args.len(), 1usize);
|
||||||
let arg0 = &mut args[0usize].write_lock::<FLOAT>().unwrap();
|
let arg0 = &mut args[0usize].write_lock::<FLOAT>().unwrap();
|
||||||
@ -1225,9 +1203,7 @@ mod generate_tests {
|
|||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
struct increment_token();
|
struct increment_token();
|
||||||
impl PluginFunction for increment_token {
|
impl PluginFunction for increment_token {
|
||||||
fn call(&self,
|
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
args: &mut [&mut Dynamic]
|
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
|
||||||
debug_assert_eq!(args.len(), 1usize,
|
debug_assert_eq!(args.len(), 1usize,
|
||||||
"wrong arg count: {} != {}", args.len(), 1usize);
|
"wrong arg count: {} != {}", args.len(), 1usize);
|
||||||
let arg0 = &mut args[0usize].write_lock::<FLOAT>().unwrap();
|
let arg0 = &mut args[0usize].write_lock::<FLOAT>().unwrap();
|
||||||
@ -1310,9 +1286,7 @@ mod generate_tests {
|
|||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
struct int_foo_token();
|
struct int_foo_token();
|
||||||
impl PluginFunction for int_foo_token {
|
impl PluginFunction for int_foo_token {
|
||||||
fn call(&self,
|
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
args: &mut [&mut Dynamic]
|
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
|
||||||
debug_assert_eq!(args.len(), 1usize,
|
debug_assert_eq!(args.len(), 1usize,
|
||||||
"wrong arg count: {} != {}", args.len(), 1usize);
|
"wrong arg count: {} != {}", args.len(), 1usize);
|
||||||
let arg0 = &mut args[0usize].write_lock::<u64>().unwrap();
|
let arg0 = &mut args[0usize].write_lock::<u64>().unwrap();
|
||||||
@ -1376,9 +1350,7 @@ mod generate_tests {
|
|||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
struct int_foo_token();
|
struct int_foo_token();
|
||||||
impl PluginFunction for int_foo_token {
|
impl PluginFunction for int_foo_token {
|
||||||
fn call(&self,
|
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
args: &mut [&mut Dynamic]
|
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
|
||||||
debug_assert_eq!(args.len(), 1usize,
|
debug_assert_eq!(args.len(), 1usize,
|
||||||
"wrong arg count: {} != {}", args.len(), 1usize);
|
"wrong arg count: {} != {}", args.len(), 1usize);
|
||||||
let arg0 = &mut args[0usize].write_lock::<u64>().unwrap();
|
let arg0 = &mut args[0usize].write_lock::<u64>().unwrap();
|
||||||
@ -1442,9 +1414,7 @@ mod generate_tests {
|
|||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
struct int_foo_token();
|
struct int_foo_token();
|
||||||
impl PluginFunction for int_foo_token {
|
impl PluginFunction for int_foo_token {
|
||||||
fn call(&self,
|
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
args: &mut [&mut Dynamic]
|
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
|
||||||
debug_assert_eq!(args.len(), 2usize,
|
debug_assert_eq!(args.len(), 2usize,
|
||||||
"wrong arg count: {} != {}", args.len(), 2usize);
|
"wrong arg count: {} != {}", args.len(), 2usize);
|
||||||
let arg1 = mem::take(args[1usize]).cast::<u64>();
|
let arg1 = mem::take(args[1usize]).cast::<u64>();
|
||||||
@ -1513,9 +1483,7 @@ mod generate_tests {
|
|||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
struct int_foo_token();
|
struct int_foo_token();
|
||||||
impl PluginFunction for int_foo_token {
|
impl PluginFunction for int_foo_token {
|
||||||
fn call(&self,
|
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
args: &mut [&mut Dynamic]
|
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
|
||||||
debug_assert_eq!(args.len(), 2usize,
|
debug_assert_eq!(args.len(), 2usize,
|
||||||
"wrong arg count: {} != {}", args.len(), 2usize);
|
"wrong arg count: {} != {}", args.len(), 2usize);
|
||||||
let arg1 = mem::take(args[1usize]).cast::<u64>();
|
let arg1 = mem::take(args[1usize]).cast::<u64>();
|
||||||
@ -1580,9 +1548,7 @@ mod generate_tests {
|
|||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
struct get_by_index_token();
|
struct get_by_index_token();
|
||||||
impl PluginFunction for get_by_index_token {
|
impl PluginFunction for get_by_index_token {
|
||||||
fn call(&self,
|
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
args: &mut [&mut Dynamic]
|
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
|
||||||
debug_assert_eq!(args.len(), 2usize,
|
debug_assert_eq!(args.len(), 2usize,
|
||||||
"wrong arg count: {} != {}", args.len(), 2usize);
|
"wrong arg count: {} != {}", args.len(), 2usize);
|
||||||
let arg1 = mem::take(args[1usize]).cast::<u64>();
|
let arg1 = mem::take(args[1usize]).cast::<u64>();
|
||||||
@ -1652,9 +1618,7 @@ mod generate_tests {
|
|||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
struct get_by_index_token();
|
struct get_by_index_token();
|
||||||
impl PluginFunction for get_by_index_token {
|
impl PluginFunction for get_by_index_token {
|
||||||
fn call(&self,
|
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
args: &mut [&mut Dynamic]
|
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
|
||||||
debug_assert_eq!(args.len(), 2usize,
|
debug_assert_eq!(args.len(), 2usize,
|
||||||
"wrong arg count: {} != {}", args.len(), 2usize);
|
"wrong arg count: {} != {}", args.len(), 2usize);
|
||||||
let arg1 = mem::take(args[1usize]).cast::<u64>();
|
let arg1 = mem::take(args[1usize]).cast::<u64>();
|
||||||
@ -1721,9 +1685,7 @@ mod generate_tests {
|
|||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
struct set_by_index_token();
|
struct set_by_index_token();
|
||||||
impl PluginFunction for set_by_index_token {
|
impl PluginFunction for set_by_index_token {
|
||||||
fn call(&self,
|
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
args: &mut [&mut Dynamic]
|
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
|
||||||
debug_assert_eq!(args.len(), 3usize,
|
debug_assert_eq!(args.len(), 3usize,
|
||||||
"wrong arg count: {} != {}", args.len(), 3usize);
|
"wrong arg count: {} != {}", args.len(), 3usize);
|
||||||
let arg1 = mem::take(args[1usize]).cast::<u64>();
|
let arg1 = mem::take(args[1usize]).cast::<u64>();
|
||||||
@ -1797,9 +1759,7 @@ mod generate_tests {
|
|||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
struct set_by_index_token();
|
struct set_by_index_token();
|
||||||
impl PluginFunction for set_by_index_token {
|
impl PluginFunction for set_by_index_token {
|
||||||
fn call(&self,
|
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
args: &mut [&mut Dynamic]
|
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
|
||||||
debug_assert_eq!(args.len(), 3usize,
|
debug_assert_eq!(args.len(), 3usize,
|
||||||
"wrong arg count: {} != {}", args.len(), 3usize);
|
"wrong arg count: {} != {}", args.len(), 3usize);
|
||||||
let arg1 = mem::take(args[1usize]).cast::<u64>();
|
let arg1 = mem::take(args[1usize]).cast::<u64>();
|
||||||
|
@ -31,7 +31,7 @@ Built-in Functions
|
|||||||
The following methods (mostly defined in the [`BasicArrayPackage`][packages] but excluded if using a [raw `Engine`]) operate on arrays:
|
The following methods (mostly defined in the [`BasicArrayPackage`][packages] but excluded if using a [raw `Engine`]) operate on arrays:
|
||||||
|
|
||||||
| Function | Parameter(s) | Description |
|
| Function | Parameter(s) | Description |
|
||||||
| ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
| ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| `push` | element to insert | inserts an element at the end |
|
| `push` | element to insert | inserts an element at the end |
|
||||||
| `append` | array to append | concatenates the second array to the end of the first |
|
| `append` | array to append | concatenates the second array to the end of the first |
|
||||||
| `+=` operator | 1) array<br/>2) element to insert (not another array) | inserts an element at the end |
|
| `+=` operator | 1) array<br/>2) element to insert (not another array) | inserts an element at the end |
|
||||||
@ -40,7 +40,7 @@ The following methods (mostly defined in the [`BasicArrayPackage`][packages] but
|
|||||||
| `insert` | 1) element to insert<br/>2) position, beginning if < 0, end if > length | inserts an element at a certain index |
|
| `insert` | 1) element to insert<br/>2) position, beginning if < 0, end if > length | inserts an element at a certain index |
|
||||||
| `pop` | _none_ | removes the last element and returns it ([`()`] if empty) |
|
| `pop` | _none_ | removes the last element and returns it ([`()`] if empty) |
|
||||||
| `shift` | _none_ | removes the first element and returns it ([`()`] if empty) |
|
| `shift` | _none_ | removes the first element and returns it ([`()`] if empty) |
|
||||||
| `extract` | 1) start position, beginning if < 0, end if > length,<br/>2) _(optional)_ number of items to extract, none if < 0 | extracts a portion of the array into a new array |
|
| `extract` | 1) start position, beginning if < 0, end if > length<br/>2) _(optional)_ number of items to extract, none if < 0 | extracts a portion of the array into a new array |
|
||||||
| `remove` | index | removes an element at a particular index and returns it ([`()`] if the index is not valid) |
|
| `remove` | index | removes an element at a particular index and returns it ([`()`] if the index is not valid) |
|
||||||
| `reverse` | _none_ | reverses the array |
|
| `reverse` | _none_ | reverses the array |
|
||||||
| `len` method and property | _none_ | returns the number of elements |
|
| `len` method and property | _none_ | returns the number of elements |
|
||||||
@ -48,14 +48,18 @@ The following methods (mostly defined in the [`BasicArrayPackage`][packages] but
|
|||||||
| `clear` | _none_ | empties the array |
|
| `clear` | _none_ | empties the array |
|
||||||
| `truncate` | target length | cuts off the array at exactly a specified length (discarding all subsequent elements) |
|
| `truncate` | target length | cuts off the array at exactly a specified length (discarding all subsequent elements) |
|
||||||
| `chop` | target length | cuts off the head of the array, leaving the tail at exactly a specified length |
|
| `chop` | target length | cuts off the head of the array, leaving the tail at exactly a specified length |
|
||||||
| `splice` | 1) start position, beginning if < 0, end if > length,<br/>2) number of items to remove, none if < 0,<br/>3) array to insert | replaces a portion of the array with another (not necessarily of the same length as the replaced portion) |
|
| `drain` | 1) [function pointer] to predicate (usually a [closure])<br/>2) _(optional)_ [function pointer] to function (usually a [closure]) that provides the initial value | removes all items (returning them) that return `true` when called with the predicate function:<br/>1st parameter: array item<br/>2nd parameter: _(optional)_ offset index |
|
||||||
| `filter` | [function pointer] to predicate (usually a [closure]) | constructs a new array with all items that returns `true` when called with the predicate function:<br/>1st parameter: array item,<br/>2nd parameter: _(optional)_ offset index |
|
| `drain` | 1) start position, beginning if < 0, end if > length<br/>2) number of items to remove, none if < 0 | removes a portion of the array, returning the removed items (not in original order) |
|
||||||
| `map` | [function pointer] to conversion function (usually a [closure]) | constructs a new array with all items mapped to the result of applying the conversion function:<br/>1st parameter: array item,<br/>2nd parameter: _(optional)_ offset index |
|
| `retain` | 1) [function pointer] to predicate (usually a [closure])<br/>2) _(optional)_ [function pointer] to function (usually a [closure]) that provides the initial value | removes all items (returning them) that do not return `true` when called with the predicate function:<br/>1st parameter: array item<br/>2nd parameter: _(optional)_ offset index |
|
||||||
| `reduce` | 1) [function pointer] to accumulator function (usually a [closure]),<br/>2) _(optional)_ [function pointer] to function (usually a [closure]) that provides the initial value | reduces the array into a single value via the accumulator function:<br/>1st parameter: accumulated value ([`()`] initially),<br/>2nd parameter: array item,<br/>3rd parameter: _(optional)_ offset index |
|
| `retain` | 1) start position, beginning if < 0, end if > length<br/>2) number of items to retain, none if < 0 | retains a portion of the array, removes all other items and returning them (not in original order) |
|
||||||
| `reduce_rev` | 1) [function pointer] to accumulator function (usually a [closure]),<br/>2) _(optional)_ [function pointer] to function (usually a [closure]) that provides the initial value | reduces the array (in reverse order) into a single value via the accumulator function:<br/>1st parameter: accumulated value ([`()`] initially),<br/>2nd parameter: array item,<br/>3rd parameter: _(optional)_ offset index |
|
| `splice` | 1) start position, beginning if < 0, end if > length<br/>2) number of items to remove, none if < 0<br/>3) array to insert | replaces a portion of the array with another (not necessarily of the same length as the replaced portion) |
|
||||||
| `some` | [function pointer] to predicate (usually a [closure]) | returns `true` if any item returns `true` when called with the predicate function:<br/>1st parameter: array item,<br/>2nd parameter: _(optional)_ offset index |
|
| `filter` | [function pointer] to predicate (usually a [closure]) | constructs a new array with all items that return `true` when called with the predicate function:<br/>1st parameter: array item<br/>2nd parameter: _(optional)_ offset index |
|
||||||
| `all` | [function pointer] to predicate (usually a [closure]) | returns `true` if all items return `true` when called with the predicate function:<br/>1st parameter: array item,<br/>2nd parameter: _(optional)_ offset index |
|
| `map` | [function pointer] to conversion function (usually a [closure]) | constructs a new array with all items mapped to the result of applying the conversion function:<br/>1st parameter: array item<br/>2nd parameter: _(optional)_ offset index |
|
||||||
| `sort` | [function pointer] to a comparison function (usually a [closure]) | sorts the array with a comparison function:<br/>1st parameter: first item,<br/>2nd parameter: second item,<br/>return value: `INT` < 0 if first < second, > 0 if first > second, 0 if first == second |
|
| `reduce` | 1) [function pointer] to accumulator function (usually a [closure])<br/>2) _(optional)_ [function pointer] to function (usually a [closure]) that provides the initial value | reduces the array into a single value via the accumulator function:<br/>1st parameter: accumulated value ([`()`] initially)<br/>2nd parameter: array item<br/>3rd parameter: _(optional)_ offset index |
|
||||||
|
| `reduce_rev` | 1) [function pointer] to accumulator function (usually a [closure])<br/>2) _(optional)_ [function pointer] to function (usually a [closure]) that provides the initial value | reduces the array (in reverse order) into a single value via the accumulator function:<br/>1st parameter: accumulated value ([`()`] initially)<br/>2nd parameter: array item<br/>3rd parameter: _(optional)_ offset index |
|
||||||
|
| `some` | [function pointer] to predicate (usually a [closure]) | returns `true` if any item returns `true` when called with the predicate function:<br/>1st parameter: array item<br/>2nd parameter: _(optional)_ offset index |
|
||||||
|
| `all` | [function pointer] to predicate (usually a [closure]) | returns `true` if all items return `true` when called with the predicate function:<br/>1st parameter: array item<br/>2nd parameter: _(optional)_ offset index |
|
||||||
|
| `sort` | [function pointer] to a comparison function (usually a [closure]) | sorts the array with a comparison function:<br/>1st parameter: first item<br/>2nd parameter: second item<br/>return value: `INT` < 0 if first < second, > 0 if first > second, 0 if first == second |
|
||||||
|
|
||||||
|
|
||||||
Use Custom Types With Arrays
|
Use Custom Types With Arrays
|
||||||
@ -199,4 +203,12 @@ a.splice(1, 1, [1, 3, 2]); // a == [42, 1, 3, 2, 99]
|
|||||||
a.extract(1, 3); // returns [1, 3, 2]
|
a.extract(1, 3); // returns [1, 3, 2]
|
||||||
|
|
||||||
a.sort(|x, y| x - y); // a == [1, 2, 3, 42, 99]
|
a.sort(|x, y| x - y); // a == [1, 2, 3, 42, 99]
|
||||||
|
|
||||||
|
a.drain(|v| v <= 1); // a == [2, 3, 42, 99]
|
||||||
|
|
||||||
|
a.drain(|v, i| i >= 3); // a == [2, 3, 42]
|
||||||
|
|
||||||
|
a.retain(|v| v > 10); // a == [42]
|
||||||
|
|
||||||
|
a.retain(|v, i| i > 0); // a == []
|
||||||
```
|
```
|
||||||
|
@ -6,6 +6,8 @@ Function Pointers
|
|||||||
It is possible to store a _function pointer_ in a variable just like a normal value.
|
It is possible to store a _function pointer_ in a variable just like a normal value.
|
||||||
In fact, internally a function pointer simply stores the _name_ of the function as a string.
|
In fact, internally a function pointer simply stores the _name_ of the function as a string.
|
||||||
|
|
||||||
|
A function pointer is created via the `Fn` function, which takes a [string] parameter.
|
||||||
|
|
||||||
Call a function pointer using the `call` method.
|
Call a function pointer using the `call` method.
|
||||||
|
|
||||||
|
|
||||||
@ -16,8 +18,9 @@ The following standard methods (mostly defined in the [`BasicFnPackage`][package
|
|||||||
using a [raw `Engine`]) operate on function pointers:
|
using a [raw `Engine`]) operate on function pointers:
|
||||||
|
|
||||||
| Function | Parameter(s) | Description |
|
| Function | Parameter(s) | Description |
|
||||||
| -------------------------- | ------------ | ---------------------------------------------------------------------------- |
|
| ---------------------------------- | ------------ | ------------------------------------------------------------------------------------------------ |
|
||||||
| `name` method and property | _none_ | returns the name of the function encapsulated by the function pointer |
|
| `name` method and property | _none_ | returns the name of the function encapsulated by the function pointer |
|
||||||
|
| `is_anonymous` method and property | _none_ | does the function pointer refer to an [anonymous function]? Not available under [`no_function`]. |
|
||||||
| `call` | _arguments_ | calls the function matching the function pointer's name with the _arguments_ |
|
| `call` | _arguments_ | calls the function matching the function pointer's name with the _arguments_ |
|
||||||
|
|
||||||
|
|
||||||
@ -184,16 +187,15 @@ must be used to register the function.
|
|||||||
|
|
||||||
Essentially, use the low-level `Engine::register_raw_fn` method to register the function.
|
Essentially, use the low-level `Engine::register_raw_fn` method to register the function.
|
||||||
`FnPtr::call_dynamic` is used to actually call the function pointer, passing to it the
|
`FnPtr::call_dynamic` is used to actually call the function pointer, passing to it the
|
||||||
current scripting [`Engine`], collection of script-defined functions, the `this` pointer,
|
current _native call context_, the `this` pointer, and other necessary arguments.
|
||||||
and other necessary arguments.
|
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
use rhai::{Engine, Module, Dynamic, FnPtr};
|
use rhai::{Engine, Module, Dynamic, FnPtr, NativeCallContext};
|
||||||
|
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
// Define Rust function in required low-level API signature
|
// Define Rust function in required low-level API signature
|
||||||
fn call_fn_ptr_with_value(engine: &Engine, lib: &Module, args: &mut [&mut Dynamic])
|
fn call_fn_ptr_with_value(context: NativeCallContext, args: &mut [&mut Dynamic])
|
||||||
-> Result<Dynamic, Box<EvalAltResult>>
|
-> Result<Dynamic, Box<EvalAltResult>>
|
||||||
{
|
{
|
||||||
// 'args' is guaranteed to contain enough arguments of the correct types
|
// 'args' is guaranteed to contain enough arguments of the correct types
|
||||||
@ -203,7 +205,7 @@ fn call_fn_ptr_with_value(engine: &Engine, lib: &Module, args: &mut [&mut Dynami
|
|||||||
|
|
||||||
// Use 'FnPtr::call_dynamic' to call the function pointer.
|
// Use 'FnPtr::call_dynamic' to call the function pointer.
|
||||||
// Beware, private script-defined functions will not be found.
|
// Beware, private script-defined functions will not be found.
|
||||||
fp.call_dynamic(engine, lib, Some(this_ptr), [value])
|
fp.call_dynamic(context, Some(this_ptr), [value])
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register a Rust function using the low-level API
|
// Register a Rust function using the low-level API
|
||||||
@ -216,3 +218,43 @@ engine.register_raw_fn("super_call",
|
|||||||
call_fn_ptr_with_value
|
call_fn_ptr_with_value
|
||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
`NativeCallContext`
|
||||||
|
------------------
|
||||||
|
|
||||||
|
`FnPtr::call_dynamic` takes a parameter of type `NativeCallContext` which holds the _native call context_
|
||||||
|
of the particular call to a registered Rust function.
|
||||||
|
|
||||||
|
This type is normally provided by the [`Engine`] (e.g. when using [`Engine::register_fn_raw`](../rust/register-raw.md)).
|
||||||
|
However, it may also be manually constructed from a tuple:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use rhai::{Engine, FnPtr};
|
||||||
|
|
||||||
|
let engine = Engine::new();
|
||||||
|
|
||||||
|
// Compile script to AST
|
||||||
|
let mut ast = engine.compile(
|
||||||
|
r#"
|
||||||
|
let test = "hello";
|
||||||
|
|x| test + x // this creates an closure
|
||||||
|
"#,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// Save the closure together with captured variables
|
||||||
|
let fn_ptr = engine.eval_ast::<FnPtr>(&ast)?;
|
||||||
|
|
||||||
|
// Get rid of the script, retaining only functions
|
||||||
|
ast.retain_functions(|_, _, _| true);
|
||||||
|
|
||||||
|
// Create native call context via a tuple containing the Engine and the
|
||||||
|
// set of script-defined functions (within the AST)
|
||||||
|
let context = (&engine, ast.as_ref()).into();
|
||||||
|
|
||||||
|
// 'f' captures: the engine, the AST, and the closure
|
||||||
|
let f = move |x: i64| fn_ptr.call_dynamic(context, None, [x.into()]);
|
||||||
|
|
||||||
|
// 'f' can be called like a normal function
|
||||||
|
let result = f(42)?;
|
||||||
|
```
|
||||||
|
@ -9,7 +9,7 @@ using a [raw `Engine`]) operate on [strings]:
|
|||||||
| Function | Parameter(s) | Description |
|
| Function | Parameter(s) | Description |
|
||||||
| ------------------------- | ------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
|
| ------------------------- | ------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
|
||||||
| `len` method and property | _none_ | returns the number of characters (not number of bytes) in the string |
|
| `len` method and property | _none_ | returns the number of characters (not number of bytes) in the string |
|
||||||
| `pad` | 1) character to pad<br/>2) target length | pads the string with an character to at least a specified length |
|
| `pad` | 1) target length<br/>2) character/string to pad | pads the string with a character or a string to at least a specified length |
|
||||||
| `+=` operator, `append` | character/string to append | Adds a character or a string to the end of another string |
|
| `+=` operator, `append` | character/string to append | Adds a character or a string to the end of another string |
|
||||||
| `clear` | _none_ | empties the string |
|
| `clear` | _none_ | empties the string |
|
||||||
| `truncate` | target length | cuts off the string at exactly a specified number of characters |
|
| `truncate` | target length | cuts off the string at exactly a specified number of characters |
|
||||||
|
@ -75,3 +75,78 @@ fn main() {
|
|||||||
register_exported_fn!(engine, "+", double_and_divide);
|
register_exported_fn!(engine, "+", double_and_divide);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
`NativeCallContext` Parameter
|
||||||
|
----------------------------
|
||||||
|
|
||||||
|
If the _first_ parameter of a function is of type `rhai::NativeCallContext`, then it is treated
|
||||||
|
specially by the plugins system.
|
||||||
|
|
||||||
|
`NativeCallContext` is a type that encapsulates the current _native call context_ and exposes the following:
|
||||||
|
|
||||||
|
* `NativeCallContext::engine(): &Engine` - the current [`Engine`], with all configurations and settings.
|
||||||
|
This is sometimes useful for calling a script-defined function within the same evaluation context
|
||||||
|
using [`Engine::call_fn`][`call_fn`].
|
||||||
|
|
||||||
|
* `NativeCallContext::namespace(): &Module` - the global namespace of script-defined functions, as a [`Module`].
|
||||||
|
|
||||||
|
This first parameter, if exists, will be stripped before all other processing. It is _virtual_.
|
||||||
|
Most importantly, it does _not_ count as a parameter to the function and there is no need to provide
|
||||||
|
this argument when calling the function in Rhai.
|
||||||
|
|
||||||
|
The native call context can be used to call a [function pointer] or [closure] that has been passed
|
||||||
|
as a parameter to the function, thereby implementing a _callback_:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use rhai::{Dynamic, FnPtr, NativeCallContext, EvalAltResult};
|
||||||
|
use rhai::plugin::*; // a "prelude" import for macros
|
||||||
|
|
||||||
|
#[export_fn]
|
||||||
|
#[rhai_fn(return_raw)]
|
||||||
|
pub fn greet(context: NativeCallContext, callback: FnPtr)
|
||||||
|
-> Result<Dynamic, Box<EvalAltResult>>
|
||||||
|
{
|
||||||
|
// Call the callback closure with the current context
|
||||||
|
// to obtain the name to greet!
|
||||||
|
let name = callback.call_dynamic(context, None, [])?;
|
||||||
|
Ok(format!("hello, {}!", name).into())
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The native call context is also useful in another scenario: protecting a function from malicious scripts.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use rhai::{Dynamic, Array, NativeCallContext, EvalAltResult, Position};
|
||||||
|
use rhai::plugin::*; // a "prelude" import for macros
|
||||||
|
|
||||||
|
// This function builds an array of arbitrary size, but is protected
|
||||||
|
// against attacks by first checking with the allowed limit set
|
||||||
|
// into the 'Engine'.
|
||||||
|
#[export_fn]
|
||||||
|
#[rhai_fn(return_raw)]
|
||||||
|
pub fn grow(context: NativeCallContext, size: i64)
|
||||||
|
-> Result<Dynamic, Box<EvalAltResult>>
|
||||||
|
{
|
||||||
|
// Make sure the function does not generate a
|
||||||
|
// data structure larger than the allowed limit
|
||||||
|
// for the Engine!
|
||||||
|
if size as usize > context.engine().max_array_size()
|
||||||
|
{
|
||||||
|
return EvalAltResult::ErrorDataTooLarge(
|
||||||
|
"Size to grow".to_string(),
|
||||||
|
context.engine().max_array_size(),
|
||||||
|
size as usize,
|
||||||
|
Position::none(),
|
||||||
|
).into();
|
||||||
|
}
|
||||||
|
|
||||||
|
let array = Array::new();
|
||||||
|
|
||||||
|
for x in 0..size {
|
||||||
|
array.push(x.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
OK(array.into())
|
||||||
|
}
|
||||||
|
```
|
||||||
|
@ -334,6 +334,85 @@ mod my_module {
|
|||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
`NativeCallContext` Parameter
|
||||||
|
----------------------------
|
||||||
|
|
||||||
|
If the _first_ parameter of a function is of type `rhai::NativeCallContext`, then it is treated
|
||||||
|
specially by the plugins system.
|
||||||
|
|
||||||
|
`NativeCallContext` is a type that encapsulates the current _native call context_ and exposes the following:
|
||||||
|
|
||||||
|
* `NativeCallContext::engine(): &Engine` - the current [`Engine`], with all configurations and settings.
|
||||||
|
This is sometimes useful for calling a script-defined function within the same evaluation context
|
||||||
|
using [`Engine::call_fn`][`call_fn`].
|
||||||
|
|
||||||
|
* `NativeCallContext::namespace(): &Module` - the global namespace of script-defined functions, as a [`Module`].
|
||||||
|
|
||||||
|
This first parameter, if exists, will be stripped before all other processing. It is _virtual_.
|
||||||
|
Most importantly, it does _not_ count as a parameter to the function and there is no need to provide
|
||||||
|
this argument when calling the function in Rhai.
|
||||||
|
|
||||||
|
The native call context can be used to call a [function pointer] or [closure] that has been passed
|
||||||
|
as a parameter to the function, thereby implementing a _callback_:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use rhai::{Dynamic, FnPtr, NativeCallContext, EvalAltResult};
|
||||||
|
use rhai::plugin::*; // a "prelude" import for macros
|
||||||
|
|
||||||
|
#[export_module]
|
||||||
|
mod my_module {
|
||||||
|
#[rhai_fn(return_raw)]
|
||||||
|
pub fn greet(context: NativeCallContext, callback: FnPtr)
|
||||||
|
-> Result<Dynamic, Box<EvalAltResult>>
|
||||||
|
{
|
||||||
|
// Call the callback closure with the current context
|
||||||
|
// to obtain the name to greet!
|
||||||
|
let name = callback.call_dynamic(context, None, [])?;
|
||||||
|
Ok(format!("hello, {}!", name).into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The native call context is also useful in another scenario: protecting a function from malicious scripts.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use rhai::{Dynamic, Array, NativeCallContext, EvalAltResult, Position};
|
||||||
|
use rhai::plugin::*; // a "prelude" import for macros
|
||||||
|
|
||||||
|
#[export_module]
|
||||||
|
mod my_module {
|
||||||
|
// This function builds an array of arbitrary size, but is protected
|
||||||
|
// against attacks by first checking with the allowed limit set
|
||||||
|
// into the 'Engine'.
|
||||||
|
#[rhai_fn(return_raw)]
|
||||||
|
pub fn grow(context: NativeCallContext, size: i64)
|
||||||
|
-> Result<Dynamic, Box<EvalAltResult>>
|
||||||
|
{
|
||||||
|
// Make sure the function does not generate a
|
||||||
|
// data structure larger than the allowed limit
|
||||||
|
// for the Engine!
|
||||||
|
if size as usize > context.engine().max_array_size()
|
||||||
|
{
|
||||||
|
return EvalAltResult::ErrorDataTooLarge(
|
||||||
|
"Size to grow".to_string(),
|
||||||
|
context.engine().max_array_size(),
|
||||||
|
size as usize,
|
||||||
|
Position::none(),
|
||||||
|
).into();
|
||||||
|
}
|
||||||
|
|
||||||
|
let array = Array::new();
|
||||||
|
|
||||||
|
for x in 0..size {
|
||||||
|
array.push(x.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
OK(array.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
`#[export_module]` Parameters
|
`#[export_module]` Parameters
|
||||||
----------------------------
|
----------------------------
|
||||||
|
|
||||||
|
@ -18,8 +18,9 @@ The `Engine::register_raw_fn` method is marked _volatile_, meaning that it may b
|
|||||||
|
|
||||||
If this is acceptable, then using this method to register a Rust function opens up more opportunities.
|
If this is acceptable, then using this method to register a Rust function opens up more opportunities.
|
||||||
|
|
||||||
In particular, a reference to the current `Engine` instance is passed as an argument so the Rust function
|
In particular, a the current _native call context_ (in form of the `NativeCallContext` type) is passed as an argument.
|
||||||
can also use `Engine` facilities (like evaluating a script).
|
`NativeCallContext` exposes the current [`Engine`], among others, so the Rust function can also use [`Engine`] facilities
|
||||||
|
(such as evaluating a script).
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
engine.register_raw_fn(
|
engine.register_raw_fn(
|
||||||
@ -28,7 +29,7 @@ engine.register_raw_fn(
|
|||||||
std::any::TypeId::of::<i64>(), // type of first parameter
|
std::any::TypeId::of::<i64>(), // type of first parameter
|
||||||
std::any::TypeId::of::<i64>() // type of second parameter
|
std::any::TypeId::of::<i64>() // type of second parameter
|
||||||
],
|
],
|
||||||
|engine: &Engine, lib: &Module, args: &mut [&mut Dynamic]| { // fixed function signature
|
|context, args| { // fixed function signature
|
||||||
// Arguments are guaranteed to be correct in number and of the correct types.
|
// Arguments are guaranteed to be correct in number and of the correct types.
|
||||||
|
|
||||||
// But remember this is Rust, so you can keep only one mutable reference at any one time!
|
// But remember this is Rust, so you can keep only one mutable reference at any one time!
|
||||||
@ -59,17 +60,20 @@ Function Signature
|
|||||||
|
|
||||||
The function signature passed to `Engine::register_raw_fn` takes the following form:
|
The function signature passed to `Engine::register_raw_fn` takes the following form:
|
||||||
|
|
||||||
> `Fn(engine: &Engine, lib: &Module, args: &mut [&mut Dynamic])`
|
> `Fn(context: NativeCallContext, args: &mut [&mut Dynamic])`
|
||||||
> `-> Result<T, Box<EvalAltResult>> + 'static`
|
> `-> Result<T, Box<EvalAltResult>> + 'static`
|
||||||
|
|
||||||
where:
|
where:
|
||||||
|
|
||||||
* `T: Variant + Clone` - return type of the function.
|
* `T: Variant + Clone` - return type of the function.
|
||||||
|
|
||||||
* `engine: &Engine` - the current [`Engine`], with all configurations and settings.
|
* `context: NativeCallContext` - the current _native call context_, which exposes the following:
|
||||||
|
|
||||||
* `lib: &Module` - the current global library of script-defined functions, as a [`Module`].
|
* `context.engine(): &Engine` - the current [`Engine`], with all configurations and settings.
|
||||||
This is sometimes useful for calling a script-defined function within the same evaluation context using [`Engine::call_fn`][`call_fn`].
|
This is sometimes useful for calling a script-defined function within the same evaluation context
|
||||||
|
using [`Engine::call_fn`][`call_fn`], or calling a [function pointer].
|
||||||
|
|
||||||
|
* `context.namespace(): &Module` - the global namespace of script-defined functions, as a [`Module`].
|
||||||
|
|
||||||
* `args: &mut [&mut Dynamic]` - a slice containing `&mut` references to [`Dynamic`] values.
|
* `args: &mut [&mut Dynamic]` - a slice containing `&mut` references to [`Dynamic`] values.
|
||||||
The slice is guaranteed to contain enough arguments _of the correct types_.
|
The slice is guaranteed to contain enough arguments _of the correct types_.
|
||||||
@ -106,7 +110,7 @@ then calls it within the same [`Engine`]. This way, a _callback_ function can b
|
|||||||
to a native Rust function.
|
to a native Rust function.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
use rhai::{Engine, Module, Dynamic, FnPtr};
|
use rhai::{Engine, FnPtr};
|
||||||
|
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
@ -118,7 +122,7 @@ engine.register_raw_fn(
|
|||||||
std::any::TypeId::of::<FnPtr>(),
|
std::any::TypeId::of::<FnPtr>(),
|
||||||
std::any::TypeId::of::<i64>(),
|
std::any::TypeId::of::<i64>(),
|
||||||
],
|
],
|
||||||
|engine: &Engine, lib: &Module, args: &mut [&mut Dynamic]| {
|
|context, args| {
|
||||||
// 'args' is guaranteed to contain enough arguments of the correct types
|
// 'args' is guaranteed to contain enough arguments of the correct types
|
||||||
|
|
||||||
let fp = std::mem::take(args[1]).cast::<FnPtr>(); // 2nd argument - function pointer
|
let fp = std::mem::take(args[1]).cast::<FnPtr>(); // 2nd argument - function pointer
|
||||||
@ -127,7 +131,7 @@ engine.register_raw_fn(
|
|||||||
|
|
||||||
// Use 'FnPtr::call_dynamic' to call the function pointer.
|
// Use 'FnPtr::call_dynamic' to call the function pointer.
|
||||||
// Beware, private script-defined functions will not be found.
|
// Beware, private script-defined functions will not be found.
|
||||||
fp.call_dynamic(engine, lib, Some(this_ptr), [value])
|
fp.call_dynamic(context, Some(this_ptr), [value])
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
41
src/any.rs
41
src/any.rs
@ -5,7 +5,7 @@ use crate::parser::{ImmutableString, INT};
|
|||||||
use crate::r#unsafe::{unsafe_cast_box, unsafe_try_cast};
|
use crate::r#unsafe::{unsafe_cast_box, unsafe_try_cast};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
use crate::fn_native::{shared_try_take, Shared};
|
use crate::fn_native::{shared_try_take, Locked, Shared};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
use crate::parser::FLOAT;
|
use crate::parser::FLOAT;
|
||||||
@ -26,14 +26,11 @@ use crate::stdlib::{
|
|||||||
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
use crate::stdlib::{
|
use crate::stdlib::cell::{Ref, RefMut};
|
||||||
cell::{Ref, RefCell, RefMut},
|
|
||||||
rc::Rc,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
use crate::stdlib::sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard};
|
use crate::stdlib::sync::{RwLockReadGuard, RwLockWriteGuard};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
use crate::stdlib::collections::HashMap;
|
use crate::stdlib::collections::HashMap;
|
||||||
@ -165,11 +162,7 @@ pub enum Union {
|
|||||||
Variant(Box<Box<dyn Variant>>),
|
Variant(Box<Box<dyn Variant>>),
|
||||||
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
#[cfg(not(feature = "sync"))]
|
Shared(Shared<Locked<Dynamic>>),
|
||||||
Shared(Shared<RefCell<Dynamic>>),
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
|
||||||
#[cfg(feature = "sync")]
|
|
||||||
Shared(Shared<RwLock<Dynamic>>),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Underlying `Variant` read guard for `Dynamic`.
|
/// Underlying `Variant` read guard for `Dynamic`.
|
||||||
@ -644,10 +637,7 @@ impl Dynamic {
|
|||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
return match self.0 {
|
return match self.0 {
|
||||||
Union::Shared(..) => self,
|
Union::Shared(..) => self,
|
||||||
#[cfg(not(feature = "sync"))]
|
_ => Self(Union::Shared(Locked::new(self).into())),
|
||||||
_ => Self(Union::Shared(Rc::new(RefCell::new(self)))),
|
|
||||||
#[cfg(feature = "sync")]
|
|
||||||
_ => Self(Union::Shared(Arc::new(RwLock::new(self)))),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "no_closure")]
|
#[cfg(feature = "no_closure")]
|
||||||
@ -859,15 +849,20 @@ impl Dynamic {
|
|||||||
pub fn flatten(self) -> Self {
|
pub fn flatten(self) -> Self {
|
||||||
match self.0 {
|
match self.0 {
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Union::Shared(cell) => {
|
Union::Shared(cell) => shared_try_take(cell).map_or_else(
|
||||||
|
|cell| {
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
return shared_try_take(cell)
|
return cell.borrow().clone();
|
||||||
.map_or_else(|c| c.borrow().clone(), RefCell::into_inner);
|
|
||||||
|
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
return shared_try_take(cell)
|
return cell.read().unwrap().clone();
|
||||||
.map_or_else(|c| c.read().unwrap().clone(), |v| v.into_inner().unwrap());
|
},
|
||||||
}
|
|value| {
|
||||||
|
#[cfg(not(feature = "sync"))]
|
||||||
|
return value.into_inner();
|
||||||
|
#[cfg(feature = "sync")]
|
||||||
|
return value.into_inner().unwrap();
|
||||||
|
},
|
||||||
|
),
|
||||||
_ => self,
|
_ => self,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -910,7 +905,6 @@ impl Dynamic {
|
|||||||
Union::Shared(ref cell) => {
|
Union::Shared(ref cell) => {
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
let data = cell.borrow();
|
let data = cell.borrow();
|
||||||
|
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
let data = cell.read().unwrap();
|
let data = cell.read().unwrap();
|
||||||
|
|
||||||
@ -944,7 +938,6 @@ impl Dynamic {
|
|||||||
Union::Shared(ref cell) => {
|
Union::Shared(ref cell) => {
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
let data = cell.borrow_mut();
|
let data = cell.borrow_mut();
|
||||||
|
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
let data = cell.write().unwrap();
|
let data = cell.write().unwrap();
|
||||||
|
|
||||||
|
23
src/api.rs
23
src/api.rs
@ -3,8 +3,7 @@
|
|||||||
use crate::any::{Dynamic, Variant};
|
use crate::any::{Dynamic, Variant};
|
||||||
use crate::engine::{Engine, EvalContext, Imports, State};
|
use crate::engine::{Engine, EvalContext, Imports, State};
|
||||||
use crate::error::ParseError;
|
use crate::error::ParseError;
|
||||||
use crate::fn_native::SendSync;
|
use crate::fn_native::{NativeCallContext, SendSync};
|
||||||
use crate::module::{FuncReturn, Module};
|
|
||||||
use crate::optimize::OptimizationLevel;
|
use crate::optimize::OptimizationLevel;
|
||||||
use crate::parser::AST;
|
use crate::parser::AST;
|
||||||
use crate::result::EvalAltResult;
|
use crate::result::EvalAltResult;
|
||||||
@ -28,7 +27,7 @@ use crate::{
|
|||||||
use crate::fn_register::{RegisterFn, RegisterResultFn};
|
use crate::fn_register::{RegisterFn, RegisterResultFn};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
use crate::{fn_args::FuncArgs, fn_call::ensure_no_data_race, StaticVec};
|
use crate::{fn_args::FuncArgs, fn_call::ensure_no_data_race, module::Module, StaticVec};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_optimize"))]
|
#[cfg(not(feature = "no_optimize"))]
|
||||||
use crate::optimize::optimize_into_ast;
|
use crate::optimize::optimize_into_ast;
|
||||||
@ -69,7 +68,9 @@ impl Engine {
|
|||||||
&mut self,
|
&mut self,
|
||||||
name: &str,
|
name: &str,
|
||||||
arg_types: &[TypeId],
|
arg_types: &[TypeId],
|
||||||
func: impl Fn(&Engine, &Module, &mut [&mut Dynamic]) -> FuncReturn<T> + SendSync + 'static,
|
func: impl Fn(NativeCallContext, &mut [&mut Dynamic]) -> Result<T, Box<EvalAltResult>>
|
||||||
|
+ SendSync
|
||||||
|
+ 'static,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
self.global_module.set_raw_fn(name, arg_types, func);
|
self.global_module.set_raw_fn(name, arg_types, func);
|
||||||
self
|
self
|
||||||
@ -924,13 +925,19 @@ impl Engine {
|
|||||||
#[inline]
|
#[inline]
|
||||||
fn read_file(path: PathBuf) -> Result<String, Box<EvalAltResult>> {
|
fn read_file(path: PathBuf) -> Result<String, Box<EvalAltResult>> {
|
||||||
let mut f = File::open(path.clone()).map_err(|err| {
|
let mut f = File::open(path.clone()).map_err(|err| {
|
||||||
EvalAltResult::ErrorReadingScriptFile(path.clone(), Position::none(), err)
|
EvalAltResult::ErrorSystem(
|
||||||
|
format!("Cannot open script file '{}'", path.to_string_lossy()),
|
||||||
|
err.into(),
|
||||||
|
)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let mut contents = String::new();
|
let mut contents = String::new();
|
||||||
|
|
||||||
f.read_to_string(&mut contents).map_err(|err| {
|
f.read_to_string(&mut contents).map_err(|err| {
|
||||||
EvalAltResult::ErrorReadingScriptFile(path.clone(), Position::none(), err)
|
EvalAltResult::ErrorSystem(
|
||||||
|
format!("Cannot read script file '{}'", path.to_string_lossy()),
|
||||||
|
err.into(),
|
||||||
|
)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
Ok(contents)
|
Ok(contents)
|
||||||
@ -1616,7 +1623,7 @@ impl Engine {
|
|||||||
name: &str,
|
name: &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]>,
|
||||||
) -> FuncReturn<Dynamic> {
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
let mut args: StaticVec<_> = arg_values.as_mut().iter_mut().collect();
|
let mut args: StaticVec<_> = arg_values.as_mut().iter_mut().collect();
|
||||||
|
|
||||||
self.call_fn_dynamic_raw(scope, lib.as_ref(), name, &mut this_ptr, args.as_mut())
|
self.call_fn_dynamic_raw(scope, lib.as_ref(), name, &mut this_ptr, args.as_mut())
|
||||||
@ -1639,7 +1646,7 @@ impl Engine {
|
|||||||
name: &str,
|
name: &str,
|
||||||
this_ptr: &mut Option<&mut Dynamic>,
|
this_ptr: &mut Option<&mut Dynamic>,
|
||||||
args: &mut [&mut Dynamic],
|
args: &mut [&mut Dynamic],
|
||||||
) -> FuncReturn<Dynamic> {
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
let fn_def = lib
|
let fn_def = lib
|
||||||
.get_script_fn(name, args.len(), true)
|
.get_script_fn(name, args.len(), true)
|
||||||
.ok_or_else(|| EvalAltResult::ErrorFunctionNotFound(name.into(), Position::none()))?;
|
.ok_or_else(|| EvalAltResult::ErrorFunctionNotFound(name.into(), Position::none()))?;
|
||||||
|
164
src/engine.rs
164
src/engine.rs
@ -1,12 +1,12 @@
|
|||||||
//! Main module defining the script evaluation `Engine`.
|
//! Main module defining the script evaluation `Engine`.
|
||||||
|
|
||||||
use crate::any::{map_std_type_name, Dynamic, Union};
|
use crate::any::{map_std_type_name, Dynamic, Union, Variant};
|
||||||
use crate::fn_call::run_builtin_op_assignment;
|
use crate::fn_call::run_builtin_op_assignment;
|
||||||
use crate::fn_native::{Callback, FnPtr, OnVarCallback};
|
use crate::fn_native::{Callback, FnPtr, OnVarCallback};
|
||||||
use crate::module::{Module, ModuleRef};
|
use crate::module::{Module, ModuleRef};
|
||||||
use crate::optimize::OptimizationLevel;
|
use crate::optimize::OptimizationLevel;
|
||||||
use crate::packages::{Package, PackagesCollection, StandardPackage};
|
use crate::packages::{Package, PackagesCollection, StandardPackage};
|
||||||
use crate::parser::{Expr, ReturnType, Stmt, INT};
|
use crate::parser::{Expr, ReturnType, Stmt};
|
||||||
use crate::r#unsafe::unsafe_cast_var_name_to_lifetime;
|
use crate::r#unsafe::unsafe_cast_var_name_to_lifetime;
|
||||||
use crate::result::EvalAltResult;
|
use crate::result::EvalAltResult;
|
||||||
use crate::scope::{EntryType as ScopeEntryType, Scope};
|
use crate::scope::{EntryType as ScopeEntryType, Scope};
|
||||||
@ -14,8 +14,8 @@ use crate::syntax::CustomSyntax;
|
|||||||
use crate::token::Position;
|
use crate::token::Position;
|
||||||
use crate::{calc_fn_hash, StaticVec};
|
use crate::{calc_fn_hash, StaticVec};
|
||||||
|
|
||||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
use crate::any::Variant;
|
use crate::parser::INT;
|
||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
use crate::module::ModuleResolver;
|
use crate::module::ModuleResolver;
|
||||||
@ -80,6 +80,7 @@ pub const MAX_CALL_STACK_DEPTH: usize = 12;
|
|||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
pub const MAX_EXPR_DEPTH: usize = 32;
|
pub const MAX_EXPR_DEPTH: usize = 32;
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
pub const MAX_FUNCTION_EXPR_DEPTH: usize = 16;
|
pub const MAX_FUNCTION_EXPR_DEPTH: usize = 16;
|
||||||
|
|
||||||
@ -90,6 +91,7 @@ pub const MAX_CALL_STACK_DEPTH: usize = 128;
|
|||||||
#[cfg(not(debug_assertions))]
|
#[cfg(not(debug_assertions))]
|
||||||
pub const MAX_EXPR_DEPTH: usize = 128;
|
pub const MAX_EXPR_DEPTH: usize = 128;
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
#[cfg(not(debug_assertions))]
|
#[cfg(not(debug_assertions))]
|
||||||
pub const MAX_FUNCTION_EXPR_DEPTH: usize = 32;
|
pub const MAX_FUNCTION_EXPR_DEPTH: usize = 32;
|
||||||
|
|
||||||
@ -144,6 +146,7 @@ impl IndexChainValue {
|
|||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// Panics if not `IndexChainValue::Value`.
|
/// Panics if not `IndexChainValue::Value`.
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
pub fn as_value(self) -> Dynamic {
|
pub fn as_value(self) -> Dynamic {
|
||||||
match self {
|
match self {
|
||||||
Self::None | Self::FnCallArgs(_) => panic!("expecting IndexChainValue::Value"),
|
Self::None | Self::FnCallArgs(_) => panic!("expecting IndexChainValue::Value"),
|
||||||
@ -155,6 +158,7 @@ impl IndexChainValue {
|
|||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// Panics if not `IndexChainValue::FnCallArgs`.
|
/// Panics if not `IndexChainValue::FnCallArgs`.
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
pub fn as_fn_call_args(self) -> StaticVec<Dynamic> {
|
pub fn as_fn_call_args(self) -> StaticVec<Dynamic> {
|
||||||
match self {
|
match self {
|
||||||
Self::None | Self::Value(_) => panic!("expecting IndexChainValue::FnCallArgs"),
|
Self::None | Self::Value(_) => panic!("expecting IndexChainValue::FnCallArgs"),
|
||||||
@ -178,7 +182,6 @@ impl From<Dynamic> for IndexChainValue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// A type that encapsulates a mutation target for an expression with side effects.
|
/// A type that encapsulates a mutation target for an expression with side effects.
|
||||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Target<'a> {
|
pub enum Target<'a> {
|
||||||
/// The target is a mutable reference to a `Dynamic` value somewhere.
|
/// The target is a mutable reference to a `Dynamic` value somewhere.
|
||||||
@ -196,7 +199,6 @@ pub enum Target<'a> {
|
|||||||
StringChar(&'a mut Dynamic, usize, Dynamic),
|
StringChar(&'a mut Dynamic, usize, Dynamic),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
|
||||||
impl<'a> Target<'a> {
|
impl<'a> Target<'a> {
|
||||||
/// Is the `Target` a reference pointing to other data?
|
/// Is the `Target` a reference pointing to other data?
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
@ -306,6 +308,7 @@ impl<'a> Target<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Update the value of the `Target`.
|
/// Update the value of the `Target`.
|
||||||
|
#[cfg(any(not(feature = "no_object"), not(feature = "no_index")))]
|
||||||
pub fn set_value(
|
pub fn set_value(
|
||||||
&mut self,
|
&mut self,
|
||||||
new_val: (Dynamic, Position),
|
new_val: (Dynamic, Position),
|
||||||
@ -348,7 +351,6 @@ impl<'a> Target<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
|
||||||
impl<'a> From<&'a mut Dynamic> for Target<'a> {
|
impl<'a> From<&'a mut Dynamic> for Target<'a> {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn from(value: &'a mut Dynamic) -> Self {
|
fn from(value: &'a mut Dynamic) -> Self {
|
||||||
@ -364,7 +366,6 @@ impl<'a> From<&'a mut Dynamic> for Target<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
|
||||||
impl<T: Into<Dynamic>> From<T> for Target<'_> {
|
impl<T: Into<Dynamic>> From<T> for Target<'_> {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn from(value: T) -> Self {
|
fn from(value: T) -> Self {
|
||||||
@ -414,50 +415,62 @@ pub struct Limits {
|
|||||||
///
|
///
|
||||||
/// Defaults to 16 for debug builds and 128 for non-debug builds.
|
/// Defaults to 16 for debug builds and 128 for non-debug builds.
|
||||||
pub max_call_stack_depth: usize,
|
pub max_call_stack_depth: usize,
|
||||||
/// Maximum depth of statements/expressions at global level.
|
/// Maximum depth of statements/expressions at global level (0 = unlimited).
|
||||||
pub max_expr_depth: usize,
|
pub max_expr_depth: usize,
|
||||||
/// Maximum depth of statements/expressions in functions.
|
/// Maximum depth of statements/expressions in functions (0 = unlimited).
|
||||||
|
/// Not available under `no_function`.
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
pub max_function_expr_depth: usize,
|
pub max_function_expr_depth: usize,
|
||||||
/// Maximum number of operations allowed to run.
|
/// Maximum number of operations allowed to run (0 = unlimited).
|
||||||
pub max_operations: u64,
|
pub max_operations: u64,
|
||||||
/// Maximum number of modules allowed to load.
|
/// Maximum number of modules allowed to load.
|
||||||
|
/// Not available under `no_module`.
|
||||||
|
#[cfg(not(feature = "no_module"))]
|
||||||
pub max_modules: usize,
|
pub max_modules: usize,
|
||||||
/// Maximum length of a string.
|
/// Maximum length of a string (0 = unlimited).
|
||||||
pub max_string_size: usize,
|
pub max_string_size: usize,
|
||||||
/// Maximum length of an array.
|
/// Maximum length of an array (0 = unlimited).
|
||||||
|
/// Not available under `no_index`.
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
pub max_array_size: usize,
|
pub max_array_size: usize,
|
||||||
/// Maximum number of properties in a map.
|
/// Maximum number of properties in a map (0 = unlimited).
|
||||||
|
/// Not available under `no_object`.
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
pub max_map_size: usize,
|
pub max_map_size: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Context of a script evaluation process.
|
/// Context of a script evaluation process.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct EvalContext<'e, 'a, 's, 'm, 't, 'd: 't> {
|
pub struct EvalContext<'e, 'a, 's, 'm, 't, 'd: 't> {
|
||||||
pub(crate) engine: &'e Engine,
|
engine: &'e Engine,
|
||||||
pub(crate) mods: &'a mut Imports,
|
pub(crate) mods: &'a mut Imports,
|
||||||
pub(crate) state: &'s mut State,
|
pub(crate) state: &'s mut State,
|
||||||
pub(crate) lib: &'m Module,
|
lib: &'m Module,
|
||||||
pub(crate) this_ptr: &'t mut Option<&'d mut Dynamic>,
|
pub(crate) this_ptr: &'t mut Option<&'d mut Dynamic>,
|
||||||
pub(crate) level: usize,
|
level: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'e, 'a, 's, 'm, 't, 'd> EvalContext<'e, 'a, 's, 'm, 't, 'd> {
|
impl<'e, 'a, 's, 'm, 't, 'd> EvalContext<'e, 'a, 's, 'm, 't, 'd> {
|
||||||
/// The current `Engine`.
|
/// The current `Engine`.
|
||||||
|
#[inline(always)]
|
||||||
pub fn engine(&self) -> &'e Engine {
|
pub fn engine(&self) -> &'e Engine {
|
||||||
self.engine
|
self.engine
|
||||||
}
|
}
|
||||||
/// _[INTERNALS]_ The current set of modules imported via `import` statements.
|
/// _[INTERNALS]_ The current set of modules imported via `import` statements.
|
||||||
/// Available under the `internals` feature only.
|
/// Available under the `internals` feature only.
|
||||||
#[cfg(feature = "internals")]
|
#[cfg(feature = "internals")]
|
||||||
#[cfg(not(feature = "no_modules"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
|
#[inline(always)]
|
||||||
pub fn imports(&self) -> &'a Imports {
|
pub fn imports(&self) -> &'a Imports {
|
||||||
self.mods
|
self.mods
|
||||||
}
|
}
|
||||||
/// The global namespace containing definition of all script-defined functions.
|
/// The global namespace containing definition of all script-defined functions.
|
||||||
|
#[inline(always)]
|
||||||
pub fn namespace(&self) -> &'m Module {
|
pub fn namespace(&self) -> &'m Module {
|
||||||
self.lib
|
self.lib
|
||||||
}
|
}
|
||||||
/// The current nesting level of function calls.
|
/// The current nesting level of function calls.
|
||||||
|
#[inline(always)]
|
||||||
pub fn call_level(&self) -> usize {
|
pub fn call_level(&self) -> usize {
|
||||||
self.level
|
self.level
|
||||||
}
|
}
|
||||||
@ -516,7 +529,7 @@ pub struct Engine {
|
|||||||
|
|
||||||
/// Max limits.
|
/// Max limits.
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
pub(crate) limits: Limits,
|
pub(crate) limits_set: Limits,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for Engine {
|
impl fmt::Debug for Engine {
|
||||||
@ -662,14 +675,18 @@ impl Engine {
|
|||||||
},
|
},
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
limits: Limits {
|
limits_set: Limits {
|
||||||
max_call_stack_depth: MAX_CALL_STACK_DEPTH,
|
max_call_stack_depth: MAX_CALL_STACK_DEPTH,
|
||||||
max_expr_depth: MAX_EXPR_DEPTH,
|
max_expr_depth: MAX_EXPR_DEPTH,
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
max_function_expr_depth: MAX_FUNCTION_EXPR_DEPTH,
|
max_function_expr_depth: MAX_FUNCTION_EXPR_DEPTH,
|
||||||
max_operations: 0,
|
max_operations: 0,
|
||||||
|
#[cfg(not(feature = "no_module"))]
|
||||||
max_modules: usize::MAX,
|
max_modules: usize::MAX,
|
||||||
max_string_size: 0,
|
max_string_size: 0,
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
max_array_size: 0,
|
max_array_size: 0,
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
max_map_size: 0,
|
max_map_size: 0,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -710,14 +727,18 @@ impl Engine {
|
|||||||
},
|
},
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
limits: Limits {
|
limits_set: Limits {
|
||||||
max_call_stack_depth: MAX_CALL_STACK_DEPTH,
|
max_call_stack_depth: MAX_CALL_STACK_DEPTH,
|
||||||
max_expr_depth: MAX_EXPR_DEPTH,
|
max_expr_depth: MAX_EXPR_DEPTH,
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
max_function_expr_depth: MAX_FUNCTION_EXPR_DEPTH,
|
max_function_expr_depth: MAX_FUNCTION_EXPR_DEPTH,
|
||||||
max_operations: 0,
|
max_operations: 0,
|
||||||
|
#[cfg(not(feature = "no_module"))]
|
||||||
max_modules: usize::MAX,
|
max_modules: usize::MAX,
|
||||||
max_string_size: 0,
|
max_string_size: 0,
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
max_array_size: 0,
|
max_array_size: 0,
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
max_map_size: 0,
|
max_map_size: 0,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -1285,7 +1306,7 @@ impl Engine {
|
|||||||
) -> Result<Target<'a>, Box<EvalAltResult>> {
|
) -> Result<Target<'a>, Box<EvalAltResult>> {
|
||||||
self.inc_operations(state)?;
|
self.inc_operations(state)?;
|
||||||
|
|
||||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
let is_ref = target.is_ref();
|
let is_ref = target.is_ref();
|
||||||
|
|
||||||
let val = target.as_mut();
|
let val = target.as_mut();
|
||||||
@ -1479,7 +1500,7 @@ impl Engine {
|
|||||||
Expr::Stmt(x) => self.eval_stmt(scope, mods, state, lib, this_ptr, &x.0, level),
|
Expr::Stmt(x) => self.eval_stmt(scope, mods, state, lib, this_ptr, &x.0, level),
|
||||||
|
|
||||||
// var op= rhs
|
// var op= rhs
|
||||||
Expr::Assignment(x) if matches!(x.0, Expr::Variable(_)) => {
|
Expr::Assignment(x) if x.0.get_variable_access(false).is_some() => {
|
||||||
let (lhs_expr, op, rhs_expr, op_pos) = x.as_ref();
|
let (lhs_expr, op, rhs_expr, op_pos) = x.as_ref();
|
||||||
let mut rhs_val = self
|
let mut 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)?
|
||||||
@ -1540,9 +1561,9 @@ impl Engine {
|
|||||||
|
|
||||||
// Overriding exact implementation
|
// Overriding exact implementation
|
||||||
if func.is_plugin_fn() {
|
if func.is_plugin_fn() {
|
||||||
func.get_plugin_fn().call(args)?;
|
func.get_plugin_fn().call((self, lib).into(), args)?;
|
||||||
} else {
|
} else {
|
||||||
func.get_native_fn()(self, lib, args)?;
|
func.get_native_fn()((self, lib).into(), args)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Built-in op-assignment function
|
// Built-in op-assignment function
|
||||||
@ -1814,10 +1835,8 @@ impl Engine {
|
|||||||
match self.eval_stmt(scope, mods, state, lib, this_ptr, body, level) {
|
match self.eval_stmt(scope, mods, state, lib, this_ptr, body, level) {
|
||||||
Ok(_) => (),
|
Ok(_) => (),
|
||||||
Err(err) => match *err {
|
Err(err) => match *err {
|
||||||
EvalAltResult::ErrorLoopBreak(false, _) => (),
|
EvalAltResult::LoopBreak(false, _) => (),
|
||||||
EvalAltResult::ErrorLoopBreak(true, _) => {
|
EvalAltResult::LoopBreak(true, _) => return Ok(Default::default()),
|
||||||
return Ok(Default::default())
|
|
||||||
}
|
|
||||||
_ => return Err(err),
|
_ => return Err(err),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -1834,8 +1853,8 @@ impl Engine {
|
|||||||
match self.eval_stmt(scope, mods, state, lib, this_ptr, &x.0, level) {
|
match self.eval_stmt(scope, mods, state, lib, this_ptr, &x.0, level) {
|
||||||
Ok(_) => (),
|
Ok(_) => (),
|
||||||
Err(err) => match *err {
|
Err(err) => match *err {
|
||||||
EvalAltResult::ErrorLoopBreak(false, _) => (),
|
EvalAltResult::LoopBreak(false, _) => (),
|
||||||
EvalAltResult::ErrorLoopBreak(true, _) => return Ok(Default::default()),
|
EvalAltResult::LoopBreak(true, _) => return Ok(Default::default()),
|
||||||
_ => return Err(err),
|
_ => return Err(err),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -1875,8 +1894,8 @@ impl Engine {
|
|||||||
match self.eval_stmt(scope, mods, state, lib, this_ptr, stmt, level) {
|
match self.eval_stmt(scope, mods, state, lib, this_ptr, stmt, level) {
|
||||||
Ok(_) => (),
|
Ok(_) => (),
|
||||||
Err(err) => match *err {
|
Err(err) => match *err {
|
||||||
EvalAltResult::ErrorLoopBreak(false, _) => (),
|
EvalAltResult::LoopBreak(false, _) => (),
|
||||||
EvalAltResult::ErrorLoopBreak(true, _) => break,
|
EvalAltResult::LoopBreak(true, _) => break,
|
||||||
_ => return Err(err),
|
_ => return Err(err),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -1891,10 +1910,10 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Continue statement
|
// Continue statement
|
||||||
Stmt::Continue(pos) => EvalAltResult::ErrorLoopBreak(false, *pos).into(),
|
Stmt::Continue(pos) => EvalAltResult::LoopBreak(false, *pos).into(),
|
||||||
|
|
||||||
// Break statement
|
// Break statement
|
||||||
Stmt::Break(pos) => EvalAltResult::ErrorLoopBreak(true, *pos).into(),
|
Stmt::Break(pos) => EvalAltResult::LoopBreak(true, *pos).into(),
|
||||||
|
|
||||||
// Return value
|
// Return value
|
||||||
Stmt::ReturnWithVal(x) if x.1.is_some() && (x.0).0 == ReturnType::Return => {
|
Stmt::ReturnWithVal(x) if x.1.is_some() && (x.0).0 == ReturnType::Return => {
|
||||||
@ -1956,7 +1975,7 @@ impl Engine {
|
|||||||
|
|
||||||
// Guard against too many modules
|
// Guard against too many modules
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
if state.modules >= self.limits.max_modules {
|
if state.modules >= self.max_modules() {
|
||||||
return EvalAltResult::ErrorTooManyModules(*_pos).into();
|
return EvalAltResult::ErrorTooManyModules(*_pos).into();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2044,8 +2063,19 @@ impl Engine {
|
|||||||
result: Result<Dynamic, Box<EvalAltResult>>,
|
result: Result<Dynamic, Box<EvalAltResult>>,
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
// If no data size limits, just return
|
// If no data size limits, just return
|
||||||
if self.limits.max_string_size + self.limits.max_array_size + self.limits.max_map_size == 0
|
let mut total = 0;
|
||||||
|
|
||||||
|
total += self.max_string_size();
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
{
|
{
|
||||||
|
total += self.max_array_size();
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
{
|
||||||
|
total += self.max_map_size();
|
||||||
|
}
|
||||||
|
|
||||||
|
if total == 0 {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2105,46 +2135,52 @@ impl Engine {
|
|||||||
// Simply return all errors
|
// Simply return all errors
|
||||||
Err(_) => return result,
|
Err(_) => return result,
|
||||||
// String with limit
|
// String with limit
|
||||||
Ok(Dynamic(Union::Str(_))) if self.limits.max_string_size > 0 => (),
|
Ok(Dynamic(Union::Str(_))) if self.max_string_size() > 0 => (),
|
||||||
// Array with limit
|
// Array with limit
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Ok(Dynamic(Union::Array(_))) if self.limits.max_array_size > 0 => (),
|
Ok(Dynamic(Union::Array(_))) if self.max_array_size() > 0 => (),
|
||||||
// Map with limit
|
// Map with limit
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Ok(Dynamic(Union::Map(_))) if self.limits.max_map_size > 0 => (),
|
Ok(Dynamic(Union::Map(_))) if self.max_map_size() > 0 => (),
|
||||||
// Everything else is simply returned
|
// Everything else is simply returned
|
||||||
Ok(_) => return result,
|
Ok(_) => return result,
|
||||||
};
|
};
|
||||||
|
|
||||||
let (arr, map, s) = calc_size(result.as_ref().unwrap());
|
let (_arr, _map, s) = calc_size(result.as_ref().unwrap());
|
||||||
|
|
||||||
if s > self.limits.max_string_size {
|
if s > self.max_string_size() {
|
||||||
EvalAltResult::ErrorDataTooLarge(
|
return EvalAltResult::ErrorDataTooLarge(
|
||||||
"Length of string".to_string(),
|
"Length of string".to_string(),
|
||||||
self.limits.max_string_size,
|
self.max_string_size(),
|
||||||
s,
|
s,
|
||||||
Position::none(),
|
Position::none(),
|
||||||
)
|
)
|
||||||
.into()
|
.into();
|
||||||
} else if arr > self.limits.max_array_size {
|
|
||||||
EvalAltResult::ErrorDataTooLarge(
|
|
||||||
"Size of array".to_string(),
|
|
||||||
self.limits.max_array_size,
|
|
||||||
arr,
|
|
||||||
Position::none(),
|
|
||||||
)
|
|
||||||
.into()
|
|
||||||
} else if map > self.limits.max_map_size {
|
|
||||||
EvalAltResult::ErrorDataTooLarge(
|
|
||||||
"Number of properties in object map".to_string(),
|
|
||||||
self.limits.max_map_size,
|
|
||||||
map,
|
|
||||||
Position::none(),
|
|
||||||
)
|
|
||||||
.into()
|
|
||||||
} else {
|
|
||||||
result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
if _arr > self.max_array_size() {
|
||||||
|
return EvalAltResult::ErrorDataTooLarge(
|
||||||
|
"Size of array".to_string(),
|
||||||
|
self.max_array_size(),
|
||||||
|
_arr,
|
||||||
|
Position::none(),
|
||||||
|
)
|
||||||
|
.into();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
if _map > self.max_map_size() {
|
||||||
|
return EvalAltResult::ErrorDataTooLarge(
|
||||||
|
"Number of properties in object map".to_string(),
|
||||||
|
self.max_map_size(),
|
||||||
|
_map,
|
||||||
|
Position::none(),
|
||||||
|
)
|
||||||
|
.into();
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if the number of operations stay within limit.
|
/// Check if the number of operations stay within limit.
|
||||||
@ -2154,7 +2190,7 @@ impl Engine {
|
|||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
// Guard against too many operations
|
// Guard against too many operations
|
||||||
if self.limits.max_operations > 0 && state.operations > self.limits.max_operations {
|
if self.max_operations() > 0 && state.operations > self.max_operations() {
|
||||||
return EvalAltResult::ErrorTooManyOperations(Position::none()).into();
|
return EvalAltResult::ErrorTooManyOperations(Position::none()).into();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
132
src/fn_call.rs
132
src/fn_call.rs
@ -206,9 +206,9 @@ impl Engine {
|
|||||||
|
|
||||||
// Run external function
|
// Run external function
|
||||||
let result = if func.is_plugin_fn() {
|
let result = if func.is_plugin_fn() {
|
||||||
func.get_plugin_fn().call(args)
|
func.get_plugin_fn().call((self, lib).into(), args)
|
||||||
} else {
|
} else {
|
||||||
func.get_native_fn()(self, lib, args)
|
func.get_native_fn()((self, lib).into(), args)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Restore the original reference
|
// Restore the original reference
|
||||||
@ -356,7 +356,7 @@ impl Engine {
|
|||||||
// Check for stack overflow
|
// Check for stack overflow
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
if level > self.limits.max_call_stack_depth {
|
if level > self.max_call_levels() {
|
||||||
return Err(Box::new(
|
return Err(Box::new(
|
||||||
EvalAltResult::ErrorStackOverflow(Position::none()),
|
EvalAltResult::ErrorStackOverflow(Position::none()),
|
||||||
));
|
));
|
||||||
@ -524,23 +524,16 @@ impl Engine {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fn
|
// Fn/eval - reaching this point it must be a method-style call, mostly like redirected
|
||||||
KEYWORD_FN_PTR
|
// by a function pointer so it isn't caught at parse time.
|
||||||
|
KEYWORD_FN_PTR | KEYWORD_EVAL
|
||||||
if args.len() == 1 && !self.has_override(lib, hash_fn, hash_script, pub_only) =>
|
if args.len() == 1 && !self.has_override(lib, hash_fn, hash_script, pub_only) =>
|
||||||
{
|
{
|
||||||
EvalAltResult::ErrorRuntime(
|
EvalAltResult::ErrorRuntime(
|
||||||
"'Fn' should not be called in method style. Try Fn(...);".into(),
|
format!(
|
||||||
Position::none(),
|
"'{}' should not be called in method style. Try {}(...);",
|
||||||
)
|
fn_name, fn_name
|
||||||
.into()
|
),
|
||||||
}
|
|
||||||
|
|
||||||
// eval - reaching this point it must be a method-style call
|
|
||||||
KEYWORD_EVAL
|
|
||||||
if args.len() == 1 && !self.has_override(lib, hash_fn, hash_script, pub_only) =>
|
|
||||||
{
|
|
||||||
EvalAltResult::ErrorRuntime(
|
|
||||||
"'eval' should not be called in method style. Try eval(...);".into(),
|
|
||||||
Position::none(),
|
Position::none(),
|
||||||
)
|
)
|
||||||
.into()
|
.into()
|
||||||
@ -655,7 +648,7 @@ impl Engine {
|
|||||||
// Check for stack overflow
|
// Check for stack overflow
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
if _level > self.limits.max_call_stack_depth {
|
if _level > self.max_call_levels() {
|
||||||
return Err(Box::new(
|
return Err(Box::new(
|
||||||
EvalAltResult::ErrorStackOverflow(Position::none()),
|
EvalAltResult::ErrorStackOverflow(Position::none()),
|
||||||
));
|
));
|
||||||
@ -711,16 +704,17 @@ impl Engine {
|
|||||||
let (result, updated) = if _fn_name == KEYWORD_FN_PTR_CALL && obj.is::<FnPtr>() {
|
let (result, updated) = if _fn_name == KEYWORD_FN_PTR_CALL && obj.is::<FnPtr>() {
|
||||||
// FnPtr call
|
// FnPtr call
|
||||||
let fn_ptr = obj.read_lock::<FnPtr>().unwrap();
|
let fn_ptr = obj.read_lock::<FnPtr>().unwrap();
|
||||||
let mut curry = fn_ptr.curry().iter().cloned().collect::<StaticVec<_>>();
|
|
||||||
// Redirect function name
|
// Redirect function name
|
||||||
let fn_name = fn_ptr.fn_name();
|
let fn_name = fn_ptr.fn_name();
|
||||||
|
let args_len = call_args.len() + fn_ptr.curry().len();
|
||||||
// Recalculate hash
|
// Recalculate hash
|
||||||
let hash = if native {
|
let hash = if native {
|
||||||
0
|
0
|
||||||
} else {
|
} else {
|
||||||
calc_fn_hash(empty(), fn_name, curry.len() + call_args.len(), empty())
|
calc_fn_hash(empty(), fn_name, args_len, empty())
|
||||||
};
|
};
|
||||||
// Arguments are passed as-is, adding the curried arguments
|
// Arguments are passed as-is, adding the curried arguments
|
||||||
|
let mut curry = fn_ptr.curry().iter().cloned().collect::<StaticVec<_>>();
|
||||||
let mut arg_values = curry
|
let mut arg_values = curry
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
.chain(call_args.iter_mut())
|
.chain(call_args.iter_mut())
|
||||||
@ -737,16 +731,17 @@ impl Engine {
|
|||||||
{
|
{
|
||||||
// FnPtr call on object
|
// FnPtr call on object
|
||||||
let fn_ptr = call_args.remove(0).cast::<FnPtr>();
|
let fn_ptr = call_args.remove(0).cast::<FnPtr>();
|
||||||
let mut curry = fn_ptr.curry().iter().cloned().collect::<StaticVec<_>>();
|
|
||||||
// Redirect function name
|
// Redirect function name
|
||||||
let fn_name = fn_ptr.get_fn_name().clone();
|
let fn_name = fn_ptr.fn_name();
|
||||||
|
let args_len = call_args.len() + fn_ptr.curry().len();
|
||||||
// Recalculate hash
|
// Recalculate hash
|
||||||
let hash = if native {
|
let hash = if native {
|
||||||
0
|
0
|
||||||
} else {
|
} else {
|
||||||
calc_fn_hash(empty(), &fn_name, curry.len() + call_args.len(), empty())
|
calc_fn_hash(empty(), fn_name, args_len, empty())
|
||||||
};
|
};
|
||||||
// Replace the first argument with the object pointer, adding the curried arguments
|
// Replace the first argument with the object pointer, adding the curried arguments
|
||||||
|
let mut curry = fn_ptr.curry().iter().cloned().collect::<StaticVec<_>>();
|
||||||
let mut arg_values = once(obj)
|
let mut arg_values = once(obj)
|
||||||
.chain(curry.iter_mut())
|
.chain(curry.iter_mut())
|
||||||
.chain(call_args.iter_mut())
|
.chain(call_args.iter_mut())
|
||||||
@ -755,7 +750,7 @@ impl Engine {
|
|||||||
|
|
||||||
// Map it to name(args) in function-call style
|
// Map it to name(args) in function-call style
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
state, lib, &fn_name, hash, args, is_ref, true, pub_only, None, def_val, level,
|
state, lib, fn_name, hash, args, is_ref, true, pub_only, None, def_val, level,
|
||||||
)
|
)
|
||||||
} else if _fn_name == KEYWORD_FN_PTR_CURRY && obj.is::<FnPtr>() {
|
} else if _fn_name == KEYWORD_FN_PTR_CURRY && obj.is::<FnPtr>() {
|
||||||
// Curry call
|
// Curry call
|
||||||
@ -796,14 +791,12 @@ impl Engine {
|
|||||||
_redirected = fn_ptr.get_fn_name().clone();
|
_redirected = fn_ptr.get_fn_name().clone();
|
||||||
_fn_name = &_redirected;
|
_fn_name = &_redirected;
|
||||||
// Add curried arguments
|
// Add curried arguments
|
||||||
if !fn_ptr.curry().is_empty() {
|
|
||||||
fn_ptr
|
fn_ptr
|
||||||
.curry()
|
.curry()
|
||||||
.iter()
|
.iter()
|
||||||
.cloned()
|
.cloned()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.for_each(|(i, v)| call_args.insert(i, v));
|
.for_each(|(i, v)| call_args.insert(i, v));
|
||||||
}
|
|
||||||
// Recalculate the hash based on the new function name and new arguments
|
// Recalculate the hash based on the new function name and new arguments
|
||||||
hash = if native {
|
hash = if native {
|
||||||
0
|
0
|
||||||
@ -861,35 +854,33 @@ impl Engine {
|
|||||||
|
|
||||||
if !self.has_override(lib, hash_fn, hash_script, pub_only) {
|
if !self.has_override(lib, hash_fn, hash_script, pub_only) {
|
||||||
// Fn - only in function call style
|
// Fn - only in function call style
|
||||||
let expr = args_expr.get(0).unwrap();
|
return self
|
||||||
let arg_value = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?;
|
.eval_expr(scope, mods, state, lib, this_ptr, &args_expr[0], level)?
|
||||||
|
|
||||||
return arg_value
|
|
||||||
.take_immutable_string()
|
.take_immutable_string()
|
||||||
.map_err(|typ| {
|
.map_err(|typ| {
|
||||||
self.make_type_mismatch_err::<ImmutableString>(typ, expr.position())
|
self.make_type_mismatch_err::<ImmutableString>(typ, args_expr[0].position())
|
||||||
})
|
})
|
||||||
.and_then(|s| FnPtr::try_from(s))
|
.and_then(|s| FnPtr::try_from(s))
|
||||||
.map(Into::<Dynamic>::into)
|
.map(Into::<Dynamic>::into)
|
||||||
.map_err(|err| err.fill_position(expr.position()));
|
.map_err(|err| err.fill_position(args_expr[0].position()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle curry()
|
// Handle curry()
|
||||||
if name == KEYWORD_FN_PTR_CURRY && args_expr.len() > 1 {
|
if name == KEYWORD_FN_PTR_CURRY && args_expr.len() > 1 {
|
||||||
let expr = args_expr.get(0).unwrap();
|
let fn_ptr = self.eval_expr(scope, mods, state, lib, this_ptr, &args_expr[0], level)?;
|
||||||
let fn_ptr = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?;
|
|
||||||
|
|
||||||
if !fn_ptr.is::<FnPtr>() {
|
if !fn_ptr.is::<FnPtr>() {
|
||||||
return Err(self.make_type_mismatch_err::<FnPtr>(
|
return Err(self.make_type_mismatch_err::<FnPtr>(
|
||||||
self.map_type_name(fn_ptr.type_name()),
|
self.map_type_name(fn_ptr.type_name()),
|
||||||
expr.position(),
|
args_expr[0].position(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let (fn_name, mut fn_curry) = fn_ptr.cast::<FnPtr>().take_data();
|
let (fn_name, mut fn_curry) = fn_ptr.cast::<FnPtr>().take_data();
|
||||||
|
|
||||||
// Append the new curried arguments to the existing list.
|
// Append the new curried arguments to the existing list.
|
||||||
|
|
||||||
args_expr
|
args_expr
|
||||||
.iter()
|
.iter()
|
||||||
.skip(1)
|
.skip(1)
|
||||||
@ -904,8 +895,7 @@ impl Engine {
|
|||||||
// Handle is_shared()
|
// Handle is_shared()
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
if name == KEYWORD_IS_SHARED && args_expr.len() == 1 {
|
if name == KEYWORD_IS_SHARED && args_expr.len() == 1 {
|
||||||
let expr = args_expr.get(0).unwrap();
|
let value = self.eval_expr(scope, mods, state, lib, this_ptr, &args_expr[0], level)?;
|
||||||
let value = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?;
|
|
||||||
|
|
||||||
return Ok(value.is_shared().into());
|
return Ok(value.is_shared().into());
|
||||||
}
|
}
|
||||||
@ -913,25 +903,24 @@ impl Engine {
|
|||||||
// Handle call() - Redirect function call
|
// Handle call() - Redirect function call
|
||||||
let redirected;
|
let redirected;
|
||||||
let mut args_expr = args_expr.as_ref();
|
let mut args_expr = args_expr.as_ref();
|
||||||
let mut curry: StaticVec<_> = Default::default();
|
let mut curry = StaticVec::new();
|
||||||
let mut name = name;
|
let mut name = name;
|
||||||
|
|
||||||
if name == KEYWORD_FN_PTR_CALL
|
if name == KEYWORD_FN_PTR_CALL
|
||||||
&& args_expr.len() >= 1
|
&& args_expr.len() >= 1
|
||||||
&& !self.has_override(lib, 0, hash_script, pub_only)
|
&& !self.has_override(lib, 0, hash_script, pub_only)
|
||||||
{
|
{
|
||||||
let expr = args_expr.get(0).unwrap();
|
let fn_ptr = self.eval_expr(scope, mods, state, lib, this_ptr, &args_expr[0], level)?;
|
||||||
let fn_ptr = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?;
|
|
||||||
|
|
||||||
if !fn_ptr.is::<FnPtr>() {
|
if !fn_ptr.is::<FnPtr>() {
|
||||||
return Err(self.make_type_mismatch_err::<FnPtr>(
|
return Err(self.make_type_mismatch_err::<FnPtr>(
|
||||||
self.map_type_name(fn_ptr.type_name()),
|
self.map_type_name(fn_ptr.type_name()),
|
||||||
expr.position(),
|
args_expr[0].position(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let fn_ptr = fn_ptr.cast::<FnPtr>();
|
let fn_ptr = fn_ptr.cast::<FnPtr>();
|
||||||
curry = fn_ptr.curry().iter().cloned().collect();
|
curry.extend(fn_ptr.curry().iter().cloned());
|
||||||
|
|
||||||
// Redirect function name
|
// Redirect function name
|
||||||
redirected = fn_ptr.take_data().0;
|
redirected = fn_ptr.take_data().0;
|
||||||
@ -941,7 +930,8 @@ impl Engine {
|
|||||||
args_expr = &args_expr.as_ref()[1..];
|
args_expr = &args_expr.as_ref()[1..];
|
||||||
|
|
||||||
// Recalculate hash
|
// Recalculate hash
|
||||||
hash_script = calc_fn_hash(empty(), name, curry.len() + args_expr.len(), empty());
|
let args_len = args_expr.len() + curry.len();
|
||||||
|
hash_script = calc_fn_hash(empty(), name, args_len, empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle is_def_var()
|
// Handle is_def_var()
|
||||||
@ -949,10 +939,10 @@ impl Engine {
|
|||||||
let hash_fn = calc_fn_hash(empty(), name, 1, once(TypeId::of::<ImmutableString>()));
|
let hash_fn = calc_fn_hash(empty(), name, 1, once(TypeId::of::<ImmutableString>()));
|
||||||
|
|
||||||
if !self.has_override(lib, hash_fn, hash_script, pub_only) {
|
if !self.has_override(lib, hash_fn, hash_script, pub_only) {
|
||||||
let expr = args_expr.get(0).unwrap();
|
let var_name =
|
||||||
let var_name = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?;
|
self.eval_expr(scope, mods, state, lib, this_ptr, &args_expr[0], level)?;
|
||||||
let var_name = var_name.as_str().map_err(|err| {
|
let var_name = var_name.as_str().map_err(|err| {
|
||||||
self.make_type_mismatch_err::<ImmutableString>(err, expr.position())
|
self.make_type_mismatch_err::<ImmutableString>(err, args_expr[0].position())
|
||||||
})?;
|
})?;
|
||||||
if var_name.is_empty() {
|
if var_name.is_empty() {
|
||||||
return Ok(false.into());
|
return Ok(false.into());
|
||||||
@ -974,18 +964,17 @@ impl Engine {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if !self.has_override(lib, hash_fn, hash_script, pub_only) {
|
if !self.has_override(lib, hash_fn, hash_script, pub_only) {
|
||||||
let expr0 = args_expr.get(0).unwrap();
|
let fn_name =
|
||||||
let expr1 = args_expr.get(1).unwrap();
|
self.eval_expr(scope, mods, state, lib, this_ptr, &args_expr[0], level)?;
|
||||||
|
let num_params =
|
||||||
let fn_name = self.eval_expr(scope, mods, state, lib, this_ptr, expr0, level)?;
|
self.eval_expr(scope, mods, state, lib, this_ptr, &args_expr[1], level)?;
|
||||||
let num_params = self.eval_expr(scope, mods, state, lib, this_ptr, expr1, level)?;
|
|
||||||
|
|
||||||
let fn_name = fn_name.as_str().map_err(|err| {
|
let fn_name = fn_name.as_str().map_err(|err| {
|
||||||
self.make_type_mismatch_err::<ImmutableString>(err, expr0.position())
|
self.make_type_mismatch_err::<ImmutableString>(err, args_expr[0].position())
|
||||||
|
})?;
|
||||||
|
let num_params = num_params.as_int().map_err(|err| {
|
||||||
|
self.make_type_mismatch_err::<INT>(err, args_expr[1].position())
|
||||||
})?;
|
})?;
|
||||||
let num_params = num_params
|
|
||||||
.as_int()
|
|
||||||
.map_err(|err| self.make_type_mismatch_err::<INT>(err, expr1.position()))?;
|
|
||||||
|
|
||||||
if fn_name.is_empty() || num_params < 0 {
|
if fn_name.is_empty() || num_params < 0 {
|
||||||
return Ok(false.into());
|
return Ok(false.into());
|
||||||
@ -1003,14 +992,14 @@ impl Engine {
|
|||||||
if !self.has_override(lib, hash_fn, hash_script, pub_only) {
|
if !self.has_override(lib, hash_fn, hash_script, pub_only) {
|
||||||
// eval - only in function call style
|
// eval - only in function call style
|
||||||
let prev_len = scope.len();
|
let prev_len = scope.len();
|
||||||
let expr = args_expr.get(0).unwrap();
|
let script =
|
||||||
let script = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?;
|
self.eval_expr(scope, mods, state, lib, this_ptr, &args_expr[0], level)?;
|
||||||
let script = script.as_str().map_err(|typ| {
|
let script = script.as_str().map_err(|typ| {
|
||||||
self.make_type_mismatch_err::<ImmutableString>(typ, expr.position())
|
self.make_type_mismatch_err::<ImmutableString>(typ, args_expr[0].position())
|
||||||
})?;
|
})?;
|
||||||
let result = if !script.is_empty() {
|
let result = if !script.is_empty() {
|
||||||
self.eval_script_expr(scope, mods, state, lib, script, level + 1)
|
self.eval_script_expr(scope, mods, state, lib, script, level + 1)
|
||||||
.map_err(|err| err.fill_position(expr.position()))
|
.map_err(|err| err.fill_position(args_expr[0].position()))
|
||||||
} else {
|
} else {
|
||||||
Ok(().into())
|
Ok(().into())
|
||||||
};
|
};
|
||||||
@ -1041,9 +1030,8 @@ impl Engine {
|
|||||||
} else {
|
} else {
|
||||||
// If the first argument is a variable, and there is no curried arguments, convert to method-call style
|
// If the first argument is a variable, and there is no curried arguments, convert to method-call style
|
||||||
// in order to leverage potential &mut first argument and avoid cloning the value
|
// in order to leverage potential &mut first argument and avoid cloning the value
|
||||||
match args_expr.get(0).unwrap() {
|
if args_expr[0].get_variable_access(false).is_some() && curry.is_empty() {
|
||||||
// func(x, ...) -> x.func(...)
|
// func(x, ...) -> x.func(...)
|
||||||
lhs @ Expr::Variable(_) if curry.is_empty() => {
|
|
||||||
arg_values = args_expr
|
arg_values = args_expr
|
||||||
.iter()
|
.iter()
|
||||||
.skip(1)
|
.skip(1)
|
||||||
@ -1051,7 +1039,7 @@ impl Engine {
|
|||||||
.collect::<Result<_, _>>()?;
|
.collect::<Result<_, _>>()?;
|
||||||
|
|
||||||
let (target, _, _, pos) =
|
let (target, _, _, pos) =
|
||||||
self.search_namespace(scope, mods, state, lib, this_ptr, lhs)?;
|
self.search_namespace(scope, mods, state, lib, this_ptr, &args_expr[0])?;
|
||||||
|
|
||||||
self.inc_operations(state)
|
self.inc_operations(state)
|
||||||
.map_err(|err| err.fill_position(pos))?;
|
.map_err(|err| err.fill_position(pos))?;
|
||||||
@ -1066,9 +1054,8 @@ impl Engine {
|
|||||||
.chain(arg_values.iter_mut())
|
.chain(arg_values.iter_mut())
|
||||||
.collect()
|
.collect()
|
||||||
};
|
};
|
||||||
}
|
} else {
|
||||||
// func(..., ...)
|
// func(..., ...)
|
||||||
_ => {
|
|
||||||
arg_values = args_expr
|
arg_values = args_expr
|
||||||
.iter()
|
.iter()
|
||||||
.map(|expr| self.eval_expr(scope, mods, state, lib, this_ptr, expr, level))
|
.map(|expr| self.eval_expr(scope, mods, state, lib, this_ptr, expr, level))
|
||||||
@ -1077,7 +1064,6 @@ impl Engine {
|
|||||||
args = curry.iter_mut().chain(arg_values.iter_mut()).collect();
|
args = curry.iter_mut().chain(arg_values.iter_mut()).collect();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
let hash = if native { 0 } else { hash_script };
|
let hash = if native { 0 } else { hash_script };
|
||||||
let args = args.as_mut();
|
let args = args.as_mut();
|
||||||
@ -1116,9 +1102,8 @@ impl Engine {
|
|||||||
// See if the first argument is a variable (not module-qualified).
|
// See if the first argument is a variable (not module-qualified).
|
||||||
// If so, convert to method-call style in order to leverage potential
|
// If so, convert to method-call style in order to leverage potential
|
||||||
// &mut first argument and avoid cloning the value
|
// &mut first argument and avoid cloning the value
|
||||||
match args_expr.get(0).unwrap() {
|
if args_expr[0].get_variable_access(true).is_some() {
|
||||||
// func(x, ...) -> x.func(...)
|
// func(x, ...) -> x.func(...)
|
||||||
Expr::Variable(x) if x.1.is_none() => {
|
|
||||||
arg_values = args_expr
|
arg_values = args_expr
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
@ -1133,9 +1118,8 @@ impl Engine {
|
|||||||
.collect::<Result<_, _>>()?;
|
.collect::<Result<_, _>>()?;
|
||||||
|
|
||||||
// Get target reference to first argument
|
// Get target reference to first argument
|
||||||
let var_expr = args_expr.get(0).unwrap();
|
|
||||||
let (target, _, _, pos) =
|
let (target, _, _, pos) =
|
||||||
self.search_scope_only(scope, mods, state, lib, this_ptr, var_expr)?;
|
self.search_scope_only(scope, mods, state, lib, this_ptr, &args_expr[0])?;
|
||||||
|
|
||||||
self.inc_operations(state)
|
self.inc_operations(state)
|
||||||
.map_err(|err| err.fill_position(pos))?;
|
.map_err(|err| err.fill_position(pos))?;
|
||||||
@ -1150,9 +1134,8 @@ impl Engine {
|
|||||||
.chain(rest.iter_mut())
|
.chain(rest.iter_mut())
|
||||||
.collect();
|
.collect();
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
// func(..., ...) or func(mod::x, ...)
|
// func(..., ...) or func(mod::x, ...)
|
||||||
_ => {
|
|
||||||
arg_values = args_expr
|
arg_values = args_expr
|
||||||
.iter()
|
.iter()
|
||||||
.map(|expr| self.eval_expr(scope, mods, state, lib, this_ptr, expr, level))
|
.map(|expr| self.eval_expr(scope, mods, state, lib, this_ptr, expr, level))
|
||||||
@ -1161,7 +1144,6 @@ impl Engine {
|
|||||||
args = arg_values.iter_mut().collect();
|
args = arg_values.iter_mut().collect();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
let module = search_imports(mods, state, modules)?;
|
let module = search_imports(mods, state, modules)?;
|
||||||
|
|
||||||
@ -1203,7 +1185,9 @@ impl Engine {
|
|||||||
|
|
||||||
self.call_script_fn(new_scope, mods, state, lib, &mut None, fn_def, args, level)
|
self.call_script_fn(new_scope, mods, state, lib, &mut None, fn_def, args, level)
|
||||||
}
|
}
|
||||||
Some(f) if f.is_plugin_fn() => f.get_plugin_fn().call(args.as_mut()),
|
Some(f) if f.is_plugin_fn() => {
|
||||||
|
f.get_plugin_fn().call((self, lib).into(), args.as_mut())
|
||||||
|
}
|
||||||
Some(f) if f.is_native() => {
|
Some(f) if f.is_native() => {
|
||||||
if !f.is_method() {
|
if !f.is_method() {
|
||||||
// Clone first argument
|
// Clone first argument
|
||||||
@ -1214,7 +1198,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
f.get_native_fn()(self, lib, args.as_mut())
|
f.get_native_fn()((self, lib).into(), args.as_mut())
|
||||||
}
|
}
|
||||||
Some(_) => unreachable!(),
|
Some(_) => unreachable!(),
|
||||||
None if def_val.is_some() => Ok(def_val.unwrap().into()),
|
None if def_val.is_some() => Ok(def_val.unwrap().into()),
|
||||||
|
@ -11,14 +11,15 @@ use crate::token::{is_valid_identifier, Position};
|
|||||||
use crate::utils::ImmutableString;
|
use crate::utils::ImmutableString;
|
||||||
use crate::{calc_fn_hash, StaticVec};
|
use crate::{calc_fn_hash, StaticVec};
|
||||||
|
|
||||||
use crate::stdlib::{
|
#[cfg(not(feature = "no_function"))]
|
||||||
boxed::Box, convert::TryFrom, fmt, iter::empty, mem, string::String, vec::Vec,
|
use crate::engine::FN_ANONYMOUS;
|
||||||
};
|
|
||||||
|
use crate::stdlib::{boxed::Box, convert::TryFrom, fmt, iter::empty, mem, string::String};
|
||||||
|
|
||||||
#[cfg(not(feature = "sync"))]
|
|
||||||
use crate::stdlib::rc::Rc;
|
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
use crate::stdlib::sync::Arc;
|
use crate::stdlib::sync::{Arc, RwLock};
|
||||||
|
#[cfg(not(feature = "sync"))]
|
||||||
|
use crate::stdlib::{cell::RefCell, rc::Rc};
|
||||||
|
|
||||||
/// Trait that maps to `Send + Sync` only under the `sync` feature.
|
/// Trait that maps to `Send + Sync` only under the `sync` feature.
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
@ -34,13 +35,49 @@ pub trait SendSync {}
|
|||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
impl<T> SendSync for T {}
|
impl<T> SendSync for T {}
|
||||||
|
|
||||||
/// Immutable reference-counted container
|
/// Immutable reference-counted container.
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
pub type Shared<T> = Rc<T>;
|
pub type Shared<T> = Rc<T>;
|
||||||
/// Immutable reference-counted container
|
/// Immutable reference-counted container.
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
pub type Shared<T> = Arc<T>;
|
pub type Shared<T> = Arc<T>;
|
||||||
|
|
||||||
|
/// Synchronized shared object.
|
||||||
|
#[cfg(not(feature = "sync"))]
|
||||||
|
pub type Locked<T> = RefCell<T>;
|
||||||
|
/// Synchronized shared object.
|
||||||
|
#[cfg(feature = "sync")]
|
||||||
|
pub type Locked<T> = RwLock<T>;
|
||||||
|
|
||||||
|
/// Context of native Rust function call.
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct NativeCallContext<'e, 'm> {
|
||||||
|
engine: &'e Engine,
|
||||||
|
lib: &'m Module,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'e, 'm> From<(&'e Engine, &'m Module)> for NativeCallContext<'e, 'm> {
|
||||||
|
fn from(value: (&'e Engine, &'m Module)) -> Self {
|
||||||
|
Self {
|
||||||
|
engine: value.0,
|
||||||
|
lib: value.1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'e, 'm> NativeCallContext<'e, 'm> {
|
||||||
|
/// The current `Engine`.
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn engine(&self) -> &'e Engine {
|
||||||
|
self.engine
|
||||||
|
}
|
||||||
|
/// The global namespace containing definition of all script-defined functions.
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn namespace(&self) -> &'m Module {
|
||||||
|
self.lib
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Consume a `Shared` resource and return a mutable reference to the wrapped value.
|
/// Consume a `Shared` resource and return a mutable reference to the wrapped value.
|
||||||
/// If the resource is shared (i.e. has other outstanding references), a cloned copy is used.
|
/// If the resource is shared (i.e. has other outstanding references), a cloned copy is used.
|
||||||
pub fn shared_make_mut<T: Clone>(value: &mut Shared<T>) -> &mut T {
|
pub fn shared_make_mut<T: Clone>(value: &mut Shared<T>) -> &mut T {
|
||||||
@ -72,12 +109,15 @@ 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, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
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.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn new_unchecked<S: Into<ImmutableString>>(name: S, curry: Vec<Dynamic>) -> Self {
|
pub(crate) fn new_unchecked<S: Into<ImmutableString>>(
|
||||||
|
name: S,
|
||||||
|
curry: StaticVec<Dynamic>,
|
||||||
|
) -> Self {
|
||||||
Self(name.into(), curry)
|
Self(name.into(), curry)
|
||||||
}
|
}
|
||||||
/// Get the name of the function.
|
/// Get the name of the function.
|
||||||
@ -92,13 +132,19 @@ 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.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn curry(&self) -> &[Dynamic] {
|
pub fn curry(&self) -> &[Dynamic] {
|
||||||
&self.1
|
self.1.as_ref()
|
||||||
|
}
|
||||||
|
/// Does this function pointer refer to an anonymous function?
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn is_anonymous(&self) -> bool {
|
||||||
|
self.0.starts_with(FN_ANONYMOUS)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Call the function pointer with curried arguments (if any).
|
/// Call the function pointer with curried arguments (if any).
|
||||||
@ -113,33 +159,33 @@ impl FnPtr {
|
|||||||
/// clone them _before_ calling this function.
|
/// clone them _before_ calling this function.
|
||||||
pub fn call_dynamic(
|
pub fn call_dynamic(
|
||||||
&self,
|
&self,
|
||||||
engine: &Engine,
|
context: NativeCallContext,
|
||||||
lib: impl AsRef<Module>,
|
|
||||||
this_ptr: Option<&mut Dynamic>,
|
this_ptr: Option<&mut Dynamic>,
|
||||||
mut arg_values: impl AsMut<[Dynamic]>,
|
mut arg_values: impl AsMut<[Dynamic]>,
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
|
let arg_values = arg_values.as_mut();
|
||||||
|
let fn_name = self.fn_name();
|
||||||
|
|
||||||
let mut args_data = self
|
let mut args_data = self
|
||||||
.1
|
.curry()
|
||||||
.iter()
|
.iter()
|
||||||
.cloned()
|
.cloned()
|
||||||
.chain(arg_values.as_mut().iter_mut().map(|v| mem::take(v)))
|
.chain(arg_values.iter_mut().map(mem::take))
|
||||||
.collect::<StaticVec<_>>();
|
.collect::<StaticVec<_>>();
|
||||||
|
|
||||||
let has_this = this_ptr.is_some();
|
let has_this = this_ptr.is_some();
|
||||||
let args_len = args_data.len();
|
|
||||||
let mut args = args_data.iter_mut().collect::<StaticVec<_>>();
|
let mut args = args_data.iter_mut().collect::<StaticVec<_>>();
|
||||||
|
let hash_script = calc_fn_hash(empty(), fn_name, args.len(), empty());
|
||||||
|
|
||||||
if let Some(obj) = this_ptr {
|
if let Some(obj) = this_ptr {
|
||||||
args.insert(0, obj);
|
args.insert(0, obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
let fn_name = self.0.as_str();
|
context
|
||||||
let hash_script = calc_fn_hash(empty(), fn_name, args_len, empty());
|
.engine()
|
||||||
|
|
||||||
engine
|
|
||||||
.exec_fn_call(
|
.exec_fn_call(
|
||||||
&mut Default::default(),
|
&mut Default::default(),
|
||||||
lib.as_ref(),
|
context.namespace(),
|
||||||
fn_name,
|
fn_name,
|
||||||
hash_script,
|
hash_script,
|
||||||
args.as_mut(),
|
args.as_mut(),
|
||||||
@ -196,11 +242,11 @@ impl TryFrom<&str> for FnPtr {
|
|||||||
|
|
||||||
/// A general function trail object.
|
/// A general function trail object.
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
pub type FnAny = dyn Fn(&Engine, &Module, &mut FnCallArgs) -> Result<Dynamic, Box<EvalAltResult>>;
|
pub type FnAny = dyn Fn(NativeCallContext, &mut FnCallArgs) -> Result<Dynamic, Box<EvalAltResult>>;
|
||||||
/// A general function trail object.
|
/// A general function trail object.
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
pub type FnAny =
|
pub type FnAny =
|
||||||
dyn Fn(&Engine, &Module, &mut FnCallArgs) -> Result<Dynamic, Box<EvalAltResult>> + Send + Sync;
|
dyn Fn(NativeCallContext, &mut FnCallArgs) -> Result<Dynamic, Box<EvalAltResult>> + Send + Sync;
|
||||||
|
|
||||||
/// A standard function that gets an iterator from a type.
|
/// A standard function that gets an iterator from a type.
|
||||||
pub type IteratorFn = fn(Dynamic) -> Box<dyn Iterator<Item = Dynamic>>;
|
pub type IteratorFn = fn(Dynamic) -> Box<dyn Iterator<Item = Dynamic>>;
|
||||||
|
@ -4,8 +4,7 @@
|
|||||||
|
|
||||||
use crate::any::{Dynamic, DynamicWriteLock, Variant};
|
use crate::any::{Dynamic, DynamicWriteLock, Variant};
|
||||||
use crate::engine::Engine;
|
use crate::engine::Engine;
|
||||||
use crate::fn_native::{CallableFunction, FnAny, FnCallArgs, SendSync};
|
use crate::fn_native::{CallableFunction, FnAny, FnCallArgs, NativeCallContext, SendSync};
|
||||||
use crate::module::Module;
|
|
||||||
use crate::parser::FnAccess;
|
use crate::parser::FnAccess;
|
||||||
use crate::r#unsafe::unsafe_cast_box;
|
use crate::r#unsafe::unsafe_cast_box;
|
||||||
use crate::result::EvalAltResult;
|
use crate::result::EvalAltResult;
|
||||||
@ -127,7 +126,7 @@ macro_rules! make_func {
|
|||||||
// ^ dereferencing function
|
// ^ dereferencing function
|
||||||
// ^ argument reference expression(like A, *B, &mut C etc)
|
// ^ argument reference expression(like A, *B, &mut C etc)
|
||||||
|
|
||||||
Box::new(move |_: &Engine, _: &Module, args: &mut FnCallArgs| {
|
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!
|
||||||
|
|
||||||
let mut _drain = args.iter_mut();
|
let mut _drain = args.iter_mut();
|
||||||
|
@ -85,7 +85,7 @@ mod utils;
|
|||||||
pub use any::Dynamic;
|
pub use any::Dynamic;
|
||||||
pub use engine::{Engine, EvalContext};
|
pub use engine::{Engine, EvalContext};
|
||||||
pub use error::{ParseError, ParseErrorType};
|
pub use error::{ParseError, ParseErrorType};
|
||||||
pub use fn_native::FnPtr;
|
pub use fn_native::{FnPtr, NativeCallContext};
|
||||||
pub use fn_register::{RegisterFn, RegisterResultFn};
|
pub use fn_register::{RegisterFn, RegisterResultFn};
|
||||||
pub use module::Module;
|
pub use module::Module;
|
||||||
pub use parser::{ImmutableString, AST, INT};
|
pub use parser::{ImmutableString, AST, INT};
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
//! Module defining external-loaded modules for Rhai.
|
//! Module defining external-loaded modules for Rhai.
|
||||||
|
|
||||||
use crate::any::{Dynamic, Variant};
|
use crate::any::{Dynamic, Variant};
|
||||||
use crate::engine::Engine;
|
use crate::fn_native::{CallableFunction, FnCallArgs, IteratorFn, NativeCallContext, SendSync};
|
||||||
use crate::fn_native::{CallableFunction, FnCallArgs, IteratorFn, SendSync};
|
|
||||||
use crate::fn_register::by_value as cast_arg;
|
use crate::fn_register::by_value as cast_arg;
|
||||||
use crate::parser::FnAccess;
|
use crate::parser::FnAccess;
|
||||||
use crate::result::EvalAltResult;
|
use crate::result::EvalAltResult;
|
||||||
@ -15,7 +14,7 @@ use crate::{fn_native::Shared, parser::ScriptFnDef};
|
|||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
use crate::{
|
use crate::{
|
||||||
engine::Imports,
|
engine::{Engine, Imports},
|
||||||
parser::AST,
|
parser::AST,
|
||||||
scope::{Entry as ScopeEntry, Scope},
|
scope::{Entry as ScopeEntry, Scope},
|
||||||
};
|
};
|
||||||
@ -24,7 +23,11 @@ use crate::{
|
|||||||
use crate::engine::{Array, FN_IDX_GET, FN_IDX_SET};
|
use crate::engine::{Array, FN_IDX_GET, FN_IDX_SET};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
use crate::engine::{make_getter, make_setter, Map};
|
use crate::engine::{make_getter, make_setter};
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
use crate::engine::Map;
|
||||||
|
|
||||||
use crate::stdlib::{
|
use crate::stdlib::{
|
||||||
any::TypeId,
|
any::TypeId,
|
||||||
@ -38,9 +41,6 @@ use crate::stdlib::{
|
|||||||
vec::Vec,
|
vec::Vec,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Return type of module-level Rust function.
|
|
||||||
pub type FuncReturn<T> = Result<T, Box<EvalAltResult>>;
|
|
||||||
|
|
||||||
pub type FuncInfo = (
|
pub type FuncInfo = (
|
||||||
String,
|
String,
|
||||||
FnAccess,
|
FnAccess,
|
||||||
@ -510,7 +510,7 @@ impl Module {
|
|||||||
/// // Pass parameter types via a slice with TypeId's
|
/// // Pass parameter types via a slice with TypeId's
|
||||||
/// &[std::any::TypeId::of::<i64>(), std::any::TypeId::of::<bool>()],
|
/// &[std::any::TypeId::of::<i64>(), std::any::TypeId::of::<bool>()],
|
||||||
/// // Fixed closure signature
|
/// // Fixed closure signature
|
||||||
/// |engine, lib, args| {
|
/// |context, args| {
|
||||||
/// // 'args' is guaranteed to be the right length and of the correct types
|
/// // 'args' is guaranteed to be the right length and of the correct types
|
||||||
///
|
///
|
||||||
/// // Get the second parameter by 'consuming' it
|
/// // Get the second parameter by 'consuming' it
|
||||||
@ -536,10 +536,12 @@ impl Module {
|
|||||||
&mut self,
|
&mut self,
|
||||||
name: impl Into<String>,
|
name: impl Into<String>,
|
||||||
arg_types: &[TypeId],
|
arg_types: &[TypeId],
|
||||||
func: impl Fn(&Engine, &Module, &mut [&mut Dynamic]) -> FuncReturn<T> + SendSync + 'static,
|
func: impl Fn(NativeCallContext, &mut [&mut Dynamic]) -> Result<T, Box<EvalAltResult>>
|
||||||
|
+ SendSync
|
||||||
|
+ 'static,
|
||||||
) -> u64 {
|
) -> u64 {
|
||||||
let f = move |engine: &Engine, lib: &Module, args: &mut FnCallArgs| {
|
let f = move |context: NativeCallContext, args: &mut [&mut Dynamic]| {
|
||||||
func(engine, lib, args).map(Dynamic::from)
|
func(context, args).map(Dynamic::from)
|
||||||
};
|
};
|
||||||
self.set_fn(
|
self.set_fn(
|
||||||
name,
|
name,
|
||||||
@ -566,9 +568,9 @@ impl Module {
|
|||||||
pub fn set_fn_0<T: Variant + Clone>(
|
pub fn set_fn_0<T: Variant + Clone>(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: impl Into<String>,
|
name: impl Into<String>,
|
||||||
func: impl Fn() -> FuncReturn<T> + SendSync + 'static,
|
func: impl Fn() -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
|
||||||
) -> u64 {
|
) -> u64 {
|
||||||
let f = move |_: &Engine, _: &Module, _: &mut FnCallArgs| func().map(Dynamic::from);
|
let f = move |_: NativeCallContext, _: &mut FnCallArgs| func().map(Dynamic::from);
|
||||||
let arg_types = [];
|
let arg_types = [];
|
||||||
self.set_fn(
|
self.set_fn(
|
||||||
name,
|
name,
|
||||||
@ -595,9 +597,9 @@ impl Module {
|
|||||||
pub fn set_fn_1<A: Variant + Clone, T: Variant + Clone>(
|
pub fn set_fn_1<A: Variant + Clone, T: Variant + Clone>(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: impl Into<String>,
|
name: impl Into<String>,
|
||||||
func: impl Fn(A) -> FuncReturn<T> + SendSync + 'static,
|
func: impl Fn(A) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
|
||||||
) -> u64 {
|
) -> u64 {
|
||||||
let f = move |_: &Engine, _: &Module, args: &mut FnCallArgs| {
|
let f = move |_: NativeCallContext, args: &mut FnCallArgs| {
|
||||||
func(cast_arg::<A>(&mut args[0])).map(Dynamic::from)
|
func(cast_arg::<A>(&mut args[0])).map(Dynamic::from)
|
||||||
};
|
};
|
||||||
let arg_types = [TypeId::of::<A>()];
|
let arg_types = [TypeId::of::<A>()];
|
||||||
@ -626,9 +628,9 @@ impl Module {
|
|||||||
pub fn set_fn_1_mut<A: Variant + Clone, T: Variant + Clone>(
|
pub fn set_fn_1_mut<A: Variant + Clone, T: Variant + Clone>(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: impl Into<String>,
|
name: impl Into<String>,
|
||||||
func: impl Fn(&mut A) -> FuncReturn<T> + SendSync + 'static,
|
func: impl Fn(&mut A) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
|
||||||
) -> u64 {
|
) -> u64 {
|
||||||
let f = move |_: &Engine, _: &Module, args: &mut FnCallArgs| {
|
let f = move |_: NativeCallContext, args: &mut FnCallArgs| {
|
||||||
func(&mut args[0].write_lock::<A>().unwrap()).map(Dynamic::from)
|
func(&mut args[0].write_lock::<A>().unwrap()).map(Dynamic::from)
|
||||||
};
|
};
|
||||||
let arg_types = [TypeId::of::<A>()];
|
let arg_types = [TypeId::of::<A>()];
|
||||||
@ -658,7 +660,7 @@ impl Module {
|
|||||||
pub fn set_getter_fn<A: Variant + Clone, T: Variant + Clone>(
|
pub fn set_getter_fn<A: Variant + Clone, T: Variant + Clone>(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: impl Into<String>,
|
name: impl Into<String>,
|
||||||
func: impl Fn(&mut A) -> FuncReturn<T> + SendSync + 'static,
|
func: impl Fn(&mut A) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
|
||||||
) -> u64 {
|
) -> u64 {
|
||||||
self.set_fn_1_mut(make_getter(&name.into()), func)
|
self.set_fn_1_mut(make_getter(&name.into()), func)
|
||||||
}
|
}
|
||||||
@ -682,9 +684,9 @@ impl Module {
|
|||||||
pub fn set_fn_2<A: Variant + Clone, B: Variant + Clone, T: Variant + Clone>(
|
pub fn set_fn_2<A: Variant + Clone, B: Variant + Clone, T: Variant + Clone>(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: impl Into<String>,
|
name: impl Into<String>,
|
||||||
func: impl Fn(A, B) -> FuncReturn<T> + SendSync + 'static,
|
func: impl Fn(A, B) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
|
||||||
) -> u64 {
|
) -> u64 {
|
||||||
let f = move |_: &Engine, _: &Module, args: &mut FnCallArgs| {
|
let f = move |_: NativeCallContext, args: &mut FnCallArgs| {
|
||||||
let a = cast_arg::<A>(&mut args[0]);
|
let a = cast_arg::<A>(&mut args[0]);
|
||||||
let b = cast_arg::<B>(&mut args[1]);
|
let b = cast_arg::<B>(&mut args[1]);
|
||||||
|
|
||||||
@ -719,9 +721,9 @@ impl Module {
|
|||||||
pub fn set_fn_2_mut<A: Variant + Clone, B: Variant + Clone, T: Variant + Clone>(
|
pub fn set_fn_2_mut<A: Variant + Clone, B: Variant + Clone, T: Variant + Clone>(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: impl Into<String>,
|
name: impl Into<String>,
|
||||||
func: impl Fn(&mut A, B) -> FuncReturn<T> + SendSync + 'static,
|
func: impl Fn(&mut A, B) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
|
||||||
) -> u64 {
|
) -> u64 {
|
||||||
let f = move |_: &Engine, _: &Module, args: &mut FnCallArgs| {
|
let f = move |_: NativeCallContext, args: &mut FnCallArgs| {
|
||||||
let b = cast_arg::<B>(&mut args[1]);
|
let b = cast_arg::<B>(&mut args[1]);
|
||||||
let a = &mut args[0].write_lock::<A>().unwrap();
|
let a = &mut args[0].write_lock::<A>().unwrap();
|
||||||
|
|
||||||
@ -758,7 +760,7 @@ impl Module {
|
|||||||
pub fn set_setter_fn<A: Variant + Clone, B: Variant + Clone>(
|
pub fn set_setter_fn<A: Variant + Clone, B: Variant + Clone>(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: impl Into<String>,
|
name: impl Into<String>,
|
||||||
func: impl Fn(&mut A, B) -> FuncReturn<()> + SendSync + 'static,
|
func: impl Fn(&mut A, B) -> Result<(), Box<EvalAltResult>> + SendSync + 'static,
|
||||||
) -> u64 {
|
) -> u64 {
|
||||||
self.set_fn_2_mut(make_setter(&name.into()), func)
|
self.set_fn_2_mut(make_setter(&name.into()), func)
|
||||||
}
|
}
|
||||||
@ -788,7 +790,7 @@ impl Module {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_indexer_get_fn<A: Variant + Clone, B: Variant + Clone, T: Variant + Clone>(
|
pub fn set_indexer_get_fn<A: Variant + Clone, B: Variant + Clone, T: Variant + Clone>(
|
||||||
&mut self,
|
&mut self,
|
||||||
func: impl Fn(&mut A, B) -> FuncReturn<T> + SendSync + 'static,
|
func: impl Fn(&mut A, B) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
|
||||||
) -> u64 {
|
) -> u64 {
|
||||||
if TypeId::of::<A>() == TypeId::of::<Array>() {
|
if TypeId::of::<A>() == TypeId::of::<Array>() {
|
||||||
panic!("Cannot register indexer for arrays.");
|
panic!("Cannot register indexer for arrays.");
|
||||||
@ -831,9 +833,9 @@ impl Module {
|
|||||||
>(
|
>(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: impl Into<String>,
|
name: impl Into<String>,
|
||||||
func: impl Fn(A, B, C) -> FuncReturn<T> + SendSync + 'static,
|
func: impl Fn(A, B, C) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
|
||||||
) -> u64 {
|
) -> u64 {
|
||||||
let f = move |_: &Engine, _: &Module, args: &mut FnCallArgs| {
|
let f = move |_: NativeCallContext, args: &mut FnCallArgs| {
|
||||||
let a = cast_arg::<A>(&mut args[0]);
|
let a = cast_arg::<A>(&mut args[0]);
|
||||||
let b = cast_arg::<B>(&mut args[1]);
|
let b = cast_arg::<B>(&mut args[1]);
|
||||||
let c = cast_arg::<C>(&mut args[2]);
|
let c = cast_arg::<C>(&mut args[2]);
|
||||||
@ -874,9 +876,9 @@ impl Module {
|
|||||||
>(
|
>(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: impl Into<String>,
|
name: impl Into<String>,
|
||||||
func: impl Fn(&mut A, B, C) -> FuncReturn<T> + SendSync + 'static,
|
func: impl Fn(&mut A, B, C) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
|
||||||
) -> u64 {
|
) -> u64 {
|
||||||
let f = move |_: &Engine, _: &Module, args: &mut FnCallArgs| {
|
let f = move |_: NativeCallContext, args: &mut FnCallArgs| {
|
||||||
let b = cast_arg::<B>(&mut args[2]);
|
let b = cast_arg::<B>(&mut args[2]);
|
||||||
let c = cast_arg::<C>(&mut args[3]);
|
let c = cast_arg::<C>(&mut args[3]);
|
||||||
let a = &mut args[0].write_lock::<A>().unwrap();
|
let a = &mut args[0].write_lock::<A>().unwrap();
|
||||||
@ -918,7 +920,7 @@ impl Module {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_indexer_set_fn<A: Variant + Clone, B: Variant + Clone, C: Variant + Clone>(
|
pub fn set_indexer_set_fn<A: Variant + Clone, B: Variant + Clone, C: Variant + Clone>(
|
||||||
&mut self,
|
&mut self,
|
||||||
func: impl Fn(&mut A, B, C) -> FuncReturn<()> + SendSync + 'static,
|
func: impl Fn(&mut A, B, C) -> Result<(), Box<EvalAltResult>> + SendSync + 'static,
|
||||||
) -> u64 {
|
) -> u64 {
|
||||||
if TypeId::of::<A>() == TypeId::of::<Array>() {
|
if TypeId::of::<A>() == TypeId::of::<Array>() {
|
||||||
panic!("Cannot register indexer for arrays.");
|
panic!("Cannot register indexer for arrays.");
|
||||||
@ -934,7 +936,7 @@ impl Module {
|
|||||||
panic!("Cannot register indexer for strings.");
|
panic!("Cannot register indexer for strings.");
|
||||||
}
|
}
|
||||||
|
|
||||||
let f = move |_: &Engine, _: &Module, args: &mut FnCallArgs| {
|
let f = move |_: NativeCallContext, args: &mut FnCallArgs| {
|
||||||
let b = cast_arg::<B>(&mut args[1]);
|
let b = cast_arg::<B>(&mut args[1]);
|
||||||
let c = cast_arg::<C>(&mut args[2]);
|
let c = cast_arg::<C>(&mut args[2]);
|
||||||
let a = &mut args[0].write_lock::<A>().unwrap();
|
let a = &mut args[0].write_lock::<A>().unwrap();
|
||||||
@ -982,8 +984,8 @@ impl Module {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_indexer_get_set_fn<A: Variant + Clone, B: Variant + Clone, T: Variant + Clone>(
|
pub fn set_indexer_get_set_fn<A: Variant + Clone, B: Variant + Clone, T: Variant + Clone>(
|
||||||
&mut self,
|
&mut self,
|
||||||
getter: impl Fn(&mut A, B) -> FuncReturn<T> + SendSync + 'static,
|
getter: impl Fn(&mut A, B) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
|
||||||
setter: impl Fn(&mut A, B, T) -> FuncReturn<()> + SendSync + 'static,
|
setter: impl Fn(&mut A, B, T) -> Result<(), Box<EvalAltResult>> + SendSync + 'static,
|
||||||
) -> (u64, u64) {
|
) -> (u64, u64) {
|
||||||
(
|
(
|
||||||
self.set_indexer_get_fn(getter),
|
self.set_indexer_get_fn(getter),
|
||||||
@ -1016,9 +1018,9 @@ impl Module {
|
|||||||
>(
|
>(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: impl Into<String>,
|
name: impl Into<String>,
|
||||||
func: impl Fn(A, B, C, D) -> FuncReturn<T> + SendSync + 'static,
|
func: impl Fn(A, B, C, D) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
|
||||||
) -> u64 {
|
) -> u64 {
|
||||||
let f = move |_: &Engine, _: &Module, args: &mut FnCallArgs| {
|
let f = move |_: NativeCallContext, args: &mut FnCallArgs| {
|
||||||
let a = cast_arg::<A>(&mut args[0]);
|
let a = cast_arg::<A>(&mut args[0]);
|
||||||
let b = cast_arg::<B>(&mut args[1]);
|
let b = cast_arg::<B>(&mut args[1]);
|
||||||
let c = cast_arg::<C>(&mut args[2]);
|
let c = cast_arg::<C>(&mut args[2]);
|
||||||
@ -1066,9 +1068,9 @@ impl Module {
|
|||||||
>(
|
>(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: impl Into<String>,
|
name: impl Into<String>,
|
||||||
func: impl Fn(&mut A, B, C, D) -> FuncReturn<T> + SendSync + 'static,
|
func: impl Fn(&mut A, B, C, D) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
|
||||||
) -> u64 {
|
) -> u64 {
|
||||||
let f = move |_: &Engine, _: &Module, args: &mut FnCallArgs| {
|
let f = move |_: NativeCallContext, args: &mut FnCallArgs| {
|
||||||
let b = cast_arg::<B>(&mut args[1]);
|
let b = cast_arg::<B>(&mut args[1]);
|
||||||
let c = cast_arg::<C>(&mut args[2]);
|
let c = cast_arg::<C>(&mut args[2]);
|
||||||
let d = cast_arg::<D>(&mut args[3]);
|
let d = cast_arg::<D>(&mut args[3]);
|
||||||
@ -1340,7 +1342,11 @@ impl Module {
|
|||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
pub fn eval_ast_as_new(mut scope: Scope, ast: &AST, engine: &Engine) -> FuncReturn<Self> {
|
pub fn eval_ast_as_new(
|
||||||
|
mut scope: Scope,
|
||||||
|
ast: &AST,
|
||||||
|
engine: &Engine,
|
||||||
|
) -> Result<Self, Box<EvalAltResult>> {
|
||||||
let mut mods = Imports::new();
|
let mut mods = Imports::new();
|
||||||
|
|
||||||
// Run the script
|
// Run the script
|
||||||
@ -1580,6 +1586,7 @@ impl ModuleRef {
|
|||||||
pub(crate) fn index(&self) -> Option<NonZeroUsize> {
|
pub(crate) fn index(&self) -> Option<NonZeroUsize> {
|
||||||
self.1
|
self.1
|
||||||
}
|
}
|
||||||
|
#[cfg(not(feature = "no_module"))]
|
||||||
pub(crate) fn set_index(&mut self, index: Option<NonZeroUsize>) {
|
pub(crate) fn set_index(&mut self, index: Option<NonZeroUsize>) {
|
||||||
self.1 = index
|
self.1 = index
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use crate::engine::Engine;
|
use crate::engine::Engine;
|
||||||
|
use crate::fn_native::Locked;
|
||||||
use crate::module::{Module, ModuleResolver};
|
use crate::module::{Module, ModuleResolver};
|
||||||
use crate::parser::AST;
|
use crate::parser::AST;
|
||||||
use crate::result::EvalAltResult;
|
use crate::result::EvalAltResult;
|
||||||
@ -6,12 +7,6 @@ use crate::token::Position;
|
|||||||
|
|
||||||
use crate::stdlib::{boxed::Box, collections::HashMap, path::PathBuf, string::String};
|
use crate::stdlib::{boxed::Box, collections::HashMap, path::PathBuf, string::String};
|
||||||
|
|
||||||
#[cfg(not(feature = "sync"))]
|
|
||||||
use crate::stdlib::cell::RefCell;
|
|
||||||
|
|
||||||
#[cfg(feature = "sync")]
|
|
||||||
use crate::stdlib::sync::RwLock;
|
|
||||||
|
|
||||||
/// Module resolution service that loads module script files from the file system.
|
/// Module resolution service that loads module script files from the file system.
|
||||||
///
|
///
|
||||||
/// Script files are cached so they are are not reloaded and recompiled in subsequent requests.
|
/// Script files are cached so they are are not reloaded and recompiled in subsequent requests.
|
||||||
@ -21,6 +16,12 @@ use crate::stdlib::sync::RwLock;
|
|||||||
/// to the base directory. The script file is then forced to be in a specified extension
|
/// to the base directory. The script file is then forced to be in a specified extension
|
||||||
/// (default `.rhai`).
|
/// (default `.rhai`).
|
||||||
///
|
///
|
||||||
|
/// # Function Namespace
|
||||||
|
///
|
||||||
|
/// When a function within a script file module is loaded, all functions in the _global_ namespace
|
||||||
|
/// plus all those defined within the same module are _merged_ into a _unified_ namespace before
|
||||||
|
/// the call. Therefore, functions in a module script can cross-call each other.
|
||||||
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
@ -39,12 +40,7 @@ use crate::stdlib::sync::RwLock;
|
|||||||
pub struct FileModuleResolver {
|
pub struct FileModuleResolver {
|
||||||
path: PathBuf,
|
path: PathBuf,
|
||||||
extension: String,
|
extension: String,
|
||||||
|
cache: Locked<HashMap<PathBuf, AST>>,
|
||||||
#[cfg(not(feature = "sync"))]
|
|
||||||
cache: RefCell<HashMap<PathBuf, AST>>,
|
|
||||||
|
|
||||||
#[cfg(feature = "sync")]
|
|
||||||
cache: RwLock<HashMap<PathBuf, AST>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for FileModuleResolver {
|
impl Default for FileModuleResolver {
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
#![cfg(not(feature = "no_index"))]
|
#![cfg(not(feature = "no_index"))]
|
||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
|
|
||||||
use crate::any::{Dynamic, Variant};
|
use crate::any::Dynamic;
|
||||||
use crate::def_package;
|
use crate::def_package;
|
||||||
use crate::engine::{Array, Engine};
|
use crate::engine::Array;
|
||||||
use crate::fn_native::FnPtr;
|
use crate::fn_native::{FnPtr, NativeCallContext};
|
||||||
use crate::parser::{ImmutableString, INT};
|
use crate::parser::{ImmutableString, INT};
|
||||||
use crate::plugin::*;
|
use crate::plugin::*;
|
||||||
use crate::result::EvalAltResult;
|
use crate::result::EvalAltResult;
|
||||||
@ -38,6 +38,23 @@ macro_rules! gen_array_functions {
|
|||||||
list.insert(position as usize, Dynamic::from(item));
|
list.insert(position as usize, Dynamic::from(item));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rhai_fn(return_raw)]
|
||||||
|
pub fn pad(context: NativeCallContext, list: &mut Array, len: INT, item: $arg_type) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
|
// Check if array will be over max size limit
|
||||||
|
#[cfg(not(feature = "unchecked"))]
|
||||||
|
if context.engine().max_array_size() > 0 && len > 0 && (len as usize) > context.engine().max_array_size() {
|
||||||
|
return EvalAltResult::ErrorDataTooLarge(
|
||||||
|
"Size of array".to_string(), context.engine().max_array_size(), len as usize, Position::none(),
|
||||||
|
).into();
|
||||||
|
}
|
||||||
|
|
||||||
|
if len > 0 && len as usize > list.len() {
|
||||||
|
list.resize(len as usize, Dynamic::from(item));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(().into())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})* }
|
})* }
|
||||||
}
|
}
|
||||||
@ -46,10 +63,6 @@ macro_rules! gen_array_functions {
|
|||||||
macro_rules! reg_functions {
|
macro_rules! reg_functions {
|
||||||
($mod_name:ident += $root:ident ; $($arg_type:ident),+) => { $(
|
($mod_name:ident += $root:ident ; $($arg_type:ident),+) => { $(
|
||||||
combine_with_exported_module!($mod_name, "array_functions", $root::$arg_type::functions);
|
combine_with_exported_module!($mod_name, "array_functions", $root::$arg_type::functions);
|
||||||
|
|
||||||
$mod_name.set_raw_fn("pad",
|
|
||||||
&[TypeId::of::<Array>(), TypeId::of::<INT>(), TypeId::of::<$arg_type>()],
|
|
||||||
pad::<$arg_type>);
|
|
||||||
)* }
|
)* }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,16 +84,6 @@ def_package!(crate:BasicArrayPackage:"Basic array utilities.", lib, {
|
|||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
reg_functions!(lib += map; Map);
|
reg_functions!(lib += map; Map);
|
||||||
|
|
||||||
lib.set_raw_fn("map", &[TypeId::of::<Array>(), TypeId::of::<FnPtr>()], map);
|
|
||||||
lib.set_raw_fn("filter", &[TypeId::of::<Array>(), TypeId::of::<FnPtr>()], filter);
|
|
||||||
lib.set_raw_fn("reduce", &[TypeId::of::<Array>(), TypeId::of::<FnPtr>()], reduce);
|
|
||||||
lib.set_raw_fn("reduce", &[TypeId::of::<Array>(), TypeId::of::<FnPtr>(), TypeId::of::<FnPtr>()], reduce_with_initial);
|
|
||||||
lib.set_raw_fn("reduce_rev", &[TypeId::of::<Array>(), TypeId::of::<FnPtr>()], reduce_rev);
|
|
||||||
lib.set_raw_fn("reduce_rev", &[TypeId::of::<Array>(), TypeId::of::<FnPtr>(), TypeId::of::<FnPtr>()], reduce_rev_with_initial);
|
|
||||||
lib.set_raw_fn("some", &[TypeId::of::<Array>(), TypeId::of::<FnPtr>()], some);
|
|
||||||
lib.set_raw_fn("all", &[TypeId::of::<Array>(), TypeId::of::<FnPtr>()], all);
|
|
||||||
lib.set_raw_fn("sort", &[TypeId::of::<Array>(), TypeId::of::<FnPtr>()], sort);
|
|
||||||
|
|
||||||
// Merge in the module at the end to override `+=` for arrays
|
// Merge in the module at the end to override `+=` for arrays
|
||||||
combine_with_exported_module!(lib, "array", array_functions);
|
combine_with_exported_module!(lib, "array", array_functions);
|
||||||
|
|
||||||
@ -191,58 +194,23 @@ mod array_functions {
|
|||||||
|
|
||||||
list[start..].iter().cloned().collect()
|
list[start..].iter().cloned().collect()
|
||||||
}
|
}
|
||||||
}
|
#[rhai_fn(return_raw)]
|
||||||
|
pub fn map(
|
||||||
fn pad<T: Variant + Clone>(
|
context: NativeCallContext,
|
||||||
_engine: &Engine,
|
list: &mut Array,
|
||||||
_: &Module,
|
mapper: FnPtr,
|
||||||
args: &mut [&mut Dynamic],
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
) -> Result<(), Box<EvalAltResult>> {
|
|
||||||
let len = *args[1].read_lock::<INT>().unwrap();
|
|
||||||
|
|
||||||
// Check if array will be over max size limit
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
|
||||||
if _engine.limits.max_array_size > 0
|
|
||||||
&& len > 0
|
|
||||||
&& (len as usize) > _engine.limits.max_array_size
|
|
||||||
{
|
|
||||||
return EvalAltResult::ErrorDataTooLarge(
|
|
||||||
"Size of array".to_string(),
|
|
||||||
_engine.limits.max_array_size,
|
|
||||||
len as usize,
|
|
||||||
Position::none(),
|
|
||||||
)
|
|
||||||
.into();
|
|
||||||
}
|
|
||||||
|
|
||||||
if len > 0 {
|
|
||||||
let item = args[2].clone();
|
|
||||||
let mut list = args[0].write_lock::<Array>().unwrap();
|
|
||||||
|
|
||||||
if len as usize > list.len() {
|
|
||||||
list.resize(len as usize, item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn map(
|
|
||||||
engine: &Engine,
|
|
||||||
lib: &Module,
|
|
||||||
args: &mut [&mut Dynamic],
|
|
||||||
) -> Result<Array, Box<EvalAltResult>> {
|
|
||||||
let list = args[0].read_lock::<Array>().unwrap();
|
|
||||||
let mapper = args[1].read_lock::<FnPtr>().unwrap();
|
|
||||||
|
|
||||||
let mut array = Array::with_capacity(list.len());
|
let mut array = Array::with_capacity(list.len());
|
||||||
|
|
||||||
for (i, item) in list.iter().enumerate() {
|
for (i, item) in list.iter().enumerate() {
|
||||||
array.push(
|
array.push(
|
||||||
mapper
|
mapper
|
||||||
.call_dynamic(engine, lib, None, [item.clone()])
|
.call_dynamic(context, None, [item.clone()])
|
||||||
.or_else(|err| match *err {
|
.or_else(|err| match *err {
|
||||||
EvalAltResult::ErrorFunctionNotFound(_, _) => {
|
EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
|
||||||
mapper.call_dynamic(engine, lib, None, [item.clone(), (i as INT).into()])
|
if fn_sig.starts_with(mapper.fn_name()) =>
|
||||||
|
{
|
||||||
|
mapper.call_dynamic(context, None, [item.clone(), (i as INT).into()])
|
||||||
}
|
}
|
||||||
_ => Err(err),
|
_ => Err(err),
|
||||||
})
|
})
|
||||||
@ -256,25 +224,24 @@ fn map(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(array)
|
Ok(array.into())
|
||||||
}
|
}
|
||||||
|
#[rhai_fn(return_raw)]
|
||||||
fn filter(
|
pub fn filter(
|
||||||
engine: &Engine,
|
context: NativeCallContext,
|
||||||
lib: &Module,
|
list: &mut Array,
|
||||||
args: &mut [&mut Dynamic],
|
filter: FnPtr,
|
||||||
) -> Result<Array, Box<EvalAltResult>> {
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
let list = args[0].read_lock::<Array>().unwrap();
|
|
||||||
let filter = args[1].read_lock::<FnPtr>().unwrap();
|
|
||||||
|
|
||||||
let mut array = Array::with_capacity(list.len());
|
let mut array = Array::with_capacity(list.len());
|
||||||
|
|
||||||
for (i, item) in list.iter().enumerate() {
|
for (i, item) in list.iter().enumerate() {
|
||||||
if filter
|
if filter
|
||||||
.call_dynamic(engine, lib, None, [item.clone()])
|
.call_dynamic(context, None, [item.clone()])
|
||||||
.or_else(|err| match *err {
|
.or_else(|err| match *err {
|
||||||
EvalAltResult::ErrorFunctionNotFound(_, _) => {
|
EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
|
||||||
filter.call_dynamic(engine, lib, None, [item.clone(), (i as INT).into()])
|
if fn_sig.starts_with(filter.fn_name()) =>
|
||||||
|
{
|
||||||
|
filter.call_dynamic(context, None, [item.clone(), (i as INT).into()])
|
||||||
}
|
}
|
||||||
_ => Err(err),
|
_ => Err(err),
|
||||||
})
|
})
|
||||||
@ -292,23 +259,22 @@ fn filter(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(array)
|
Ok(array.into())
|
||||||
}
|
}
|
||||||
|
#[rhai_fn(return_raw)]
|
||||||
fn some(
|
pub fn some(
|
||||||
engine: &Engine,
|
context: NativeCallContext,
|
||||||
lib: &Module,
|
list: &mut Array,
|
||||||
args: &mut [&mut Dynamic],
|
filter: FnPtr,
|
||||||
) -> Result<bool, Box<EvalAltResult>> {
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
let list = args[0].read_lock::<Array>().unwrap();
|
|
||||||
let filter = args[1].read_lock::<FnPtr>().unwrap();
|
|
||||||
|
|
||||||
for (i, item) in list.iter().enumerate() {
|
for (i, item) in list.iter().enumerate() {
|
||||||
if filter
|
if filter
|
||||||
.call_dynamic(engine, lib, None, [item.clone()])
|
.call_dynamic(context, None, [item.clone()])
|
||||||
.or_else(|err| match *err {
|
.or_else(|err| match *err {
|
||||||
EvalAltResult::ErrorFunctionNotFound(_, _) => {
|
EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
|
||||||
filter.call_dynamic(engine, lib, None, [item.clone(), (i as INT).into()])
|
if fn_sig.starts_with(filter.fn_name()) =>
|
||||||
|
{
|
||||||
|
filter.call_dynamic(context, None, [item.clone(), (i as INT).into()])
|
||||||
}
|
}
|
||||||
_ => Err(err),
|
_ => Err(err),
|
||||||
})
|
})
|
||||||
@ -327,22 +293,21 @@ fn some(
|
|||||||
}
|
}
|
||||||
|
|
||||||
Ok(false.into())
|
Ok(false.into())
|
||||||
}
|
}
|
||||||
|
#[rhai_fn(return_raw)]
|
||||||
fn all(
|
pub fn all(
|
||||||
engine: &Engine,
|
context: NativeCallContext,
|
||||||
lib: &Module,
|
list: &mut Array,
|
||||||
args: &mut [&mut Dynamic],
|
filter: FnPtr,
|
||||||
) -> Result<bool, Box<EvalAltResult>> {
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
let list = args[0].read_lock::<Array>().unwrap();
|
|
||||||
let filter = args[1].read_lock::<FnPtr>().unwrap();
|
|
||||||
|
|
||||||
for (i, item) in list.iter().enumerate() {
|
for (i, item) in list.iter().enumerate() {
|
||||||
if !filter
|
if !filter
|
||||||
.call_dynamic(engine, lib, None, [item.clone()])
|
.call_dynamic(context, None, [item.clone()])
|
||||||
.or_else(|err| match *err {
|
.or_else(|err| match *err {
|
||||||
EvalAltResult::ErrorFunctionNotFound(_, _) => {
|
EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
|
||||||
filter.call_dynamic(engine, lib, None, [item.clone(), (i as INT).into()])
|
if fn_sig.starts_with(filter.fn_name()) =>
|
||||||
|
{
|
||||||
|
filter.call_dynamic(context, None, [item.clone(), (i as INT).into()])
|
||||||
}
|
}
|
||||||
_ => Err(err),
|
_ => Err(err),
|
||||||
})
|
})
|
||||||
@ -361,28 +326,28 @@ fn all(
|
|||||||
}
|
}
|
||||||
|
|
||||||
Ok(true.into())
|
Ok(true.into())
|
||||||
}
|
}
|
||||||
|
#[rhai_fn(return_raw)]
|
||||||
fn reduce(
|
pub fn reduce(
|
||||||
engine: &Engine,
|
context: NativeCallContext,
|
||||||
lib: &Module,
|
list: &mut Array,
|
||||||
args: &mut [&mut Dynamic],
|
reducer: FnPtr,
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
let list = args[0].read_lock::<Array>().unwrap();
|
|
||||||
let reducer = args[1].read_lock::<FnPtr>().unwrap();
|
|
||||||
|
|
||||||
let mut result: Dynamic = ().into();
|
let mut result: Dynamic = ().into();
|
||||||
|
|
||||||
for (i, item) in list.iter().enumerate() {
|
for (i, item) in list.iter().enumerate() {
|
||||||
result = reducer
|
result = reducer
|
||||||
.call_dynamic(engine, lib, None, [result.clone(), item.clone()])
|
.call_dynamic(context, None, [result.clone(), item.clone()])
|
||||||
.or_else(|err| match *err {
|
.or_else(|err| match *err {
|
||||||
EvalAltResult::ErrorFunctionNotFound(_, _) => reducer.call_dynamic(
|
EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
|
||||||
engine,
|
if fn_sig.starts_with(reducer.fn_name()) =>
|
||||||
lib,
|
{
|
||||||
|
reducer.call_dynamic(
|
||||||
|
context,
|
||||||
None,
|
None,
|
||||||
[result, item.clone(), (i as INT).into()],
|
[result, item.clone(), (i as INT).into()],
|
||||||
),
|
)
|
||||||
|
}
|
||||||
_ => Err(err),
|
_ => Err(err),
|
||||||
})
|
})
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
@ -395,18 +360,15 @@ fn reduce(
|
|||||||
}
|
}
|
||||||
|
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
#[rhai_fn(name = "reduce", return_raw)]
|
||||||
fn reduce_with_initial(
|
pub fn reduce_with_initial(
|
||||||
engine: &Engine,
|
context: NativeCallContext,
|
||||||
lib: &Module,
|
list: &mut Array,
|
||||||
args: &mut [&mut Dynamic],
|
reducer: FnPtr,
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
initial: FnPtr,
|
||||||
let list = args[0].read_lock::<Array>().unwrap();
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
let reducer = args[1].read_lock::<FnPtr>().unwrap();
|
let mut result = initial.call_dynamic(context, None, []).map_err(|err| {
|
||||||
let initial = args[2].read_lock::<FnPtr>().unwrap();
|
|
||||||
|
|
||||||
let mut result = initial.call_dynamic(engine, lib, None, []).map_err(|err| {
|
|
||||||
Box::new(EvalAltResult::ErrorInFunctionCall(
|
Box::new(EvalAltResult::ErrorInFunctionCall(
|
||||||
"reduce".to_string(),
|
"reduce".to_string(),
|
||||||
err,
|
err,
|
||||||
@ -416,14 +378,17 @@ fn reduce_with_initial(
|
|||||||
|
|
||||||
for (i, item) in list.iter().enumerate() {
|
for (i, item) in list.iter().enumerate() {
|
||||||
result = reducer
|
result = reducer
|
||||||
.call_dynamic(engine, lib, None, [result.clone(), item.clone()])
|
.call_dynamic(context, None, [result.clone(), item.clone()])
|
||||||
.or_else(|err| match *err {
|
.or_else(|err| match *err {
|
||||||
EvalAltResult::ErrorFunctionNotFound(_, _) => reducer.call_dynamic(
|
EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
|
||||||
engine,
|
if fn_sig.starts_with(reducer.fn_name()) =>
|
||||||
lib,
|
{
|
||||||
|
reducer.call_dynamic(
|
||||||
|
context,
|
||||||
None,
|
None,
|
||||||
[result, item.clone(), (i as INT).into()],
|
[result, item.clone(), (i as INT).into()],
|
||||||
),
|
)
|
||||||
|
}
|
||||||
_ => Err(err),
|
_ => Err(err),
|
||||||
})
|
})
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
@ -436,28 +401,28 @@ fn reduce_with_initial(
|
|||||||
}
|
}
|
||||||
|
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
#[rhai_fn(return_raw)]
|
||||||
fn reduce_rev(
|
pub fn reduce_rev(
|
||||||
engine: &Engine,
|
context: NativeCallContext,
|
||||||
lib: &Module,
|
list: &mut Array,
|
||||||
args: &mut [&mut Dynamic],
|
reducer: FnPtr,
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
let list = args[0].read_lock::<Array>().unwrap();
|
|
||||||
let reducer = args[1].read_lock::<FnPtr>().unwrap();
|
|
||||||
|
|
||||||
let mut result: Dynamic = ().into();
|
let mut result: Dynamic = ().into();
|
||||||
|
|
||||||
for (i, item) in list.iter().enumerate().rev() {
|
for (i, item) in list.iter().enumerate().rev() {
|
||||||
result = reducer
|
result = reducer
|
||||||
.call_dynamic(engine, lib, None, [result.clone(), item.clone()])
|
.call_dynamic(context, None, [result.clone(), item.clone()])
|
||||||
.or_else(|err| match *err {
|
.or_else(|err| match *err {
|
||||||
EvalAltResult::ErrorFunctionNotFound(_, _) => reducer.call_dynamic(
|
EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
|
||||||
engine,
|
if fn_sig.starts_with(reducer.fn_name()) =>
|
||||||
lib,
|
{
|
||||||
|
reducer.call_dynamic(
|
||||||
|
context,
|
||||||
None,
|
None,
|
||||||
[result, item.clone(), (i as INT).into()],
|
[result, item.clone(), (i as INT).into()],
|
||||||
),
|
)
|
||||||
|
}
|
||||||
_ => Err(err),
|
_ => Err(err),
|
||||||
})
|
})
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
@ -470,18 +435,15 @@ fn reduce_rev(
|
|||||||
}
|
}
|
||||||
|
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
#[rhai_fn(name = "reduce_rev", return_raw)]
|
||||||
fn reduce_rev_with_initial(
|
pub fn reduce_rev_with_initial(
|
||||||
engine: &Engine,
|
context: NativeCallContext,
|
||||||
lib: &Module,
|
list: &mut Array,
|
||||||
args: &mut [&mut Dynamic],
|
reducer: FnPtr,
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
initial: FnPtr,
|
||||||
let list = args[0].read_lock::<Array>().unwrap();
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
let reducer = args[1].read_lock::<FnPtr>().unwrap();
|
let mut result = initial.call_dynamic(context, None, []).map_err(|err| {
|
||||||
let initial = args[2].read_lock::<FnPtr>().unwrap();
|
|
||||||
|
|
||||||
let mut result = initial.call_dynamic(engine, lib, None, []).map_err(|err| {
|
|
||||||
Box::new(EvalAltResult::ErrorInFunctionCall(
|
Box::new(EvalAltResult::ErrorInFunctionCall(
|
||||||
"reduce".to_string(),
|
"reduce".to_string(),
|
||||||
err,
|
err,
|
||||||
@ -491,14 +453,17 @@ fn reduce_rev_with_initial(
|
|||||||
|
|
||||||
for (i, item) in list.iter().enumerate().rev() {
|
for (i, item) in list.iter().enumerate().rev() {
|
||||||
result = reducer
|
result = reducer
|
||||||
.call_dynamic(engine, lib, None, [result.clone(), item.clone()])
|
.call_dynamic(context, None, [result.clone(), item.clone()])
|
||||||
.or_else(|err| match *err {
|
.or_else(|err| match *err {
|
||||||
EvalAltResult::ErrorFunctionNotFound(_, _) => reducer.call_dynamic(
|
EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
|
||||||
engine,
|
if fn_sig.starts_with(reducer.fn_name()) =>
|
||||||
lib,
|
{
|
||||||
|
reducer.call_dynamic(
|
||||||
|
context,
|
||||||
None,
|
None,
|
||||||
[result, item.clone(), (i as INT).into()],
|
[result, item.clone(), (i as INT).into()],
|
||||||
),
|
)
|
||||||
|
}
|
||||||
_ => Err(err),
|
_ => Err(err),
|
||||||
})
|
})
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
@ -511,19 +476,16 @@ fn reduce_rev_with_initial(
|
|||||||
}
|
}
|
||||||
|
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
#[rhai_fn(return_raw)]
|
||||||
fn sort(
|
pub fn sort(
|
||||||
engine: &Engine,
|
context: NativeCallContext,
|
||||||
lib: &Module,
|
list: &mut Array,
|
||||||
args: &mut [&mut Dynamic],
|
comparer: FnPtr,
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
let comparer = args[1].read_lock::<FnPtr>().unwrap().clone();
|
|
||||||
let mut list = args[0].write_lock::<Array>().unwrap();
|
|
||||||
|
|
||||||
list.sort_by(|x, y| {
|
list.sort_by(|x, y| {
|
||||||
comparer
|
comparer
|
||||||
.call_dynamic(engine, lib, None, [x.clone(), y.clone()])
|
.call_dynamic(context, None, [x.clone(), y.clone()])
|
||||||
.ok()
|
.ok()
|
||||||
.and_then(|v| v.as_int().ok())
|
.and_then(|v| v.as_int().ok())
|
||||||
.map(|v| {
|
.map(|v| {
|
||||||
@ -550,6 +512,128 @@ fn sort(
|
|||||||
});
|
});
|
||||||
|
|
||||||
Ok(().into())
|
Ok(().into())
|
||||||
|
}
|
||||||
|
#[rhai_fn(return_raw)]
|
||||||
|
pub fn drain(
|
||||||
|
context: NativeCallContext,
|
||||||
|
list: &mut Array,
|
||||||
|
filter: FnPtr,
|
||||||
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
|
let mut drained = Array::with_capacity(list.len());
|
||||||
|
|
||||||
|
let mut i = list.len();
|
||||||
|
|
||||||
|
while i > 0 {
|
||||||
|
i -= 1;
|
||||||
|
|
||||||
|
if filter
|
||||||
|
.call_dynamic(context, None, [list[i].clone()])
|
||||||
|
.or_else(|err| match *err {
|
||||||
|
EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
|
||||||
|
if fn_sig.starts_with(filter.fn_name()) =>
|
||||||
|
{
|
||||||
|
filter.call_dynamic(context, None, [list[i].clone(), (i as INT).into()])
|
||||||
|
}
|
||||||
|
_ => Err(err),
|
||||||
|
})
|
||||||
|
.map_err(|err| {
|
||||||
|
Box::new(EvalAltResult::ErrorInFunctionCall(
|
||||||
|
"filter".to_string(),
|
||||||
|
err,
|
||||||
|
Position::none(),
|
||||||
|
))
|
||||||
|
})?
|
||||||
|
.as_bool()
|
||||||
|
.unwrap_or(false)
|
||||||
|
{
|
||||||
|
drained.push(list.remove(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(drained.into())
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "drain")]
|
||||||
|
pub fn drain_range(list: &mut Array, start: INT, len: INT) -> Array {
|
||||||
|
let start = if start < 0 {
|
||||||
|
0
|
||||||
|
} else if start as usize >= list.len() {
|
||||||
|
list.len() - 1
|
||||||
|
} else {
|
||||||
|
start as usize
|
||||||
|
};
|
||||||
|
|
||||||
|
let len = if len < 0 {
|
||||||
|
0
|
||||||
|
} else if len as usize > list.len() - start {
|
||||||
|
list.len() - start
|
||||||
|
} else {
|
||||||
|
len as usize
|
||||||
|
};
|
||||||
|
|
||||||
|
list.drain(start..start + len - 1).collect()
|
||||||
|
}
|
||||||
|
#[rhai_fn(return_raw)]
|
||||||
|
pub fn retain(
|
||||||
|
context: NativeCallContext,
|
||||||
|
list: &mut Array,
|
||||||
|
filter: FnPtr,
|
||||||
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
|
let mut drained = Array::with_capacity(list.len());
|
||||||
|
|
||||||
|
let mut i = list.len();
|
||||||
|
|
||||||
|
while i > 0 {
|
||||||
|
i -= 1;
|
||||||
|
|
||||||
|
if !filter
|
||||||
|
.call_dynamic(context, None, [list[i].clone()])
|
||||||
|
.or_else(|err| match *err {
|
||||||
|
EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
|
||||||
|
if fn_sig.starts_with(filter.fn_name()) =>
|
||||||
|
{
|
||||||
|
filter.call_dynamic(context, None, [list[i].clone(), (i as INT).into()])
|
||||||
|
}
|
||||||
|
_ => Err(err),
|
||||||
|
})
|
||||||
|
.map_err(|err| {
|
||||||
|
Box::new(EvalAltResult::ErrorInFunctionCall(
|
||||||
|
"filter".to_string(),
|
||||||
|
err,
|
||||||
|
Position::none(),
|
||||||
|
))
|
||||||
|
})?
|
||||||
|
.as_bool()
|
||||||
|
.unwrap_or(false)
|
||||||
|
{
|
||||||
|
drained.push(list.remove(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(drained.into())
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "retain")]
|
||||||
|
pub fn retain_range(list: &mut Array, start: INT, len: INT) -> Array {
|
||||||
|
let start = if start < 0 {
|
||||||
|
0
|
||||||
|
} else if start as usize >= list.len() {
|
||||||
|
list.len() - 1
|
||||||
|
} else {
|
||||||
|
start as usize
|
||||||
|
};
|
||||||
|
|
||||||
|
let len = if len < 0 {
|
||||||
|
0
|
||||||
|
} else if len as usize > list.len() - start {
|
||||||
|
list.len() - start
|
||||||
|
} else {
|
||||||
|
len as usize
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut drained = list.drain(start + len..).collect::<Array>();
|
||||||
|
drained.extend(list.drain(..start));
|
||||||
|
|
||||||
|
drained
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
gen_array_functions!(basic => INT, bool, char, ImmutableString, FnPtr, Array, Unit);
|
gen_array_functions!(basic => INT, bool, char, ImmutableString, FnPtr, Array, Unit);
|
||||||
|
@ -12,4 +12,12 @@ mod fn_ptr_functions {
|
|||||||
pub fn name(f: &mut FnPtr) -> ImmutableString {
|
pub fn name(f: &mut FnPtr) -> ImmutableString {
|
||||||
f.get_fn_name().clone()
|
f.get_fn_name().clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
pub mod anonymous {
|
||||||
|
#[rhai_fn(name = "is_anonymous", get = "is_anonymous")]
|
||||||
|
pub fn is_anonymous(f: &mut FnPtr) -> bool {
|
||||||
|
f.is_anonymous()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,14 @@
|
|||||||
use crate::any::Variant;
|
use crate::any::Variant;
|
||||||
use crate::def_package;
|
use crate::def_package;
|
||||||
use crate::module::FuncReturn;
|
|
||||||
use crate::parser::INT;
|
use crate::parser::INT;
|
||||||
|
use crate::result::EvalAltResult;
|
||||||
|
|
||||||
use crate::stdlib::ops::{Add, Range};
|
use crate::stdlib::{
|
||||||
|
boxed::Box,
|
||||||
|
ops::{Add, Range},
|
||||||
|
};
|
||||||
|
|
||||||
fn get_range<T: Variant + Clone>(from: T, to: T) -> FuncReturn<Range<T>> {
|
fn get_range<T: Variant + Clone>(from: T, to: T) -> Result<Range<T>, Box<EvalAltResult>> {
|
||||||
Ok(from..to)
|
Ok(from..to)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -34,7 +37,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_step_range<T>(from: T, to: T, step: T) -> FuncReturn<StepRange<T>>
|
fn get_step_range<T>(from: T, to: T, step: T) -> Result<StepRange<T>, Box<EvalAltResult>>
|
||||||
where
|
where
|
||||||
for<'a> &'a T: Add<&'a T, Output = T>,
|
for<'a> &'a T: Add<&'a T, Output = T>,
|
||||||
T: Variant + Clone + PartialOrd,
|
T: Variant + Clone + PartialOrd,
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
use crate::any::Dynamic;
|
use crate::any::Dynamic;
|
||||||
use crate::def_package;
|
use crate::def_package;
|
||||||
use crate::engine::Engine;
|
|
||||||
use crate::fn_native::FnPtr;
|
use crate::fn_native::FnPtr;
|
||||||
use crate::parser::{ImmutableString, INT};
|
use crate::parser::{ImmutableString, INT};
|
||||||
use crate::plugin::*;
|
use crate::plugin::*;
|
||||||
@ -59,52 +58,6 @@ def_package!(crate:MoreStringPackage:"Additional string utilities, including str
|
|||||||
|
|
||||||
combine_with_exported_module!(lib, "string", string_functions);
|
combine_with_exported_module!(lib, "string", string_functions);
|
||||||
|
|
||||||
lib.set_raw_fn(
|
|
||||||
"pad",
|
|
||||||
&[TypeId::of::<ImmutableString>(), TypeId::of::<INT>(), TypeId::of::<char>()],
|
|
||||||
|_engine: &Engine, _: &Module, args: &mut [&mut Dynamic]| {
|
|
||||||
let len = *args[1].read_lock::<INT>().unwrap();
|
|
||||||
|
|
||||||
// Check if string will be over max size limit
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
|
||||||
if _engine.limits.max_string_size > 0 && len > 0 && (len as usize) > _engine.limits.max_string_size {
|
|
||||||
return EvalAltResult::ErrorDataTooLarge(
|
|
||||||
"Length of string".to_string(),
|
|
||||||
_engine.limits.max_string_size,
|
|
||||||
len as usize,
|
|
||||||
Position::none(),
|
|
||||||
).into();
|
|
||||||
}
|
|
||||||
|
|
||||||
if len > 0 {
|
|
||||||
let ch = mem::take(args[2]).cast::<char>();
|
|
||||||
let mut s = args[0].write_lock::<ImmutableString>().unwrap();
|
|
||||||
|
|
||||||
let orig_len = s.chars().count();
|
|
||||||
|
|
||||||
if len as usize > orig_len {
|
|
||||||
let p = s.make_mut();
|
|
||||||
|
|
||||||
for _ in 0..(len as usize - orig_len) {
|
|
||||||
p.push(ch);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
|
||||||
if _engine.limits.max_string_size > 0 && s.len() > _engine.limits.max_string_size {
|
|
||||||
return EvalAltResult::ErrorDataTooLarge(
|
|
||||||
"Length of string".to_string(),
|
|
||||||
_engine.limits.max_string_size,
|
|
||||||
s.len(),
|
|
||||||
Position::none(),
|
|
||||||
).into();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
// Register string iterator
|
// Register string iterator
|
||||||
lib.set_iter(
|
lib.set_iter(
|
||||||
TypeId::of::<ImmutableString>(),
|
TypeId::of::<ImmutableString>(),
|
||||||
@ -175,7 +128,7 @@ mod string_functions {
|
|||||||
pub fn index_of_char_starting_from(s: &str, ch: char, start: INT) -> INT {
|
pub fn index_of_char_starting_from(s: &str, ch: char, start: INT) -> INT {
|
||||||
let start = if start < 0 {
|
let start = if start < 0 {
|
||||||
0
|
0
|
||||||
} else if (start as usize) >= s.chars().count() {
|
} else if start as usize >= s.chars().count() {
|
||||||
return -1 as INT;
|
return -1 as INT;
|
||||||
} else {
|
} else {
|
||||||
s.chars().take(start as usize).collect::<String>().len()
|
s.chars().take(start as usize).collect::<String>().len()
|
||||||
@ -196,7 +149,7 @@ mod string_functions {
|
|||||||
pub fn index_of_string_starting_from(s: &str, find: ImmutableString, start: INT) -> INT {
|
pub fn index_of_string_starting_from(s: &str, find: ImmutableString, start: INT) -> INT {
|
||||||
let start = if start < 0 {
|
let start = if start < 0 {
|
||||||
0
|
0
|
||||||
} else if (start as usize) >= s.chars().count() {
|
} else if start as usize >= s.chars().count() {
|
||||||
return -1 as INT;
|
return -1 as INT;
|
||||||
} else {
|
} else {
|
||||||
s.chars().take(start as usize).collect::<String>().len()
|
s.chars().take(start as usize).collect::<String>().len()
|
||||||
@ -219,7 +172,7 @@ mod string_functions {
|
|||||||
return "".to_string().into();
|
return "".to_string().into();
|
||||||
} else if start < 0 {
|
} else if start < 0 {
|
||||||
0
|
0
|
||||||
} else if (start as usize) >= s.chars().count() {
|
} else if start as usize >= s.chars().count() {
|
||||||
return "".to_string().into();
|
return "".to_string().into();
|
||||||
} else {
|
} else {
|
||||||
start as usize
|
start as usize
|
||||||
@ -227,7 +180,7 @@ mod string_functions {
|
|||||||
|
|
||||||
let chars: StaticVec<_> = s.chars().collect();
|
let chars: StaticVec<_> = s.chars().collect();
|
||||||
|
|
||||||
let len = if offset + (len as usize) > chars.len() {
|
let len = if offset + len as usize > chars.len() {
|
||||||
chars.len() - offset
|
chars.len() - offset
|
||||||
} else {
|
} else {
|
||||||
len as usize
|
len as usize
|
||||||
@ -254,7 +207,7 @@ mod string_functions {
|
|||||||
return;
|
return;
|
||||||
} else if start < 0 {
|
} else if start < 0 {
|
||||||
0
|
0
|
||||||
} else if (start as usize) >= s.chars().count() {
|
} else if start as usize >= s.chars().count() {
|
||||||
s.make_mut().clear();
|
s.make_mut().clear();
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
@ -263,7 +216,7 @@ mod string_functions {
|
|||||||
|
|
||||||
let chars: StaticVec<_> = s.chars().collect();
|
let chars: StaticVec<_> = s.chars().collect();
|
||||||
|
|
||||||
let len = if offset + (len as usize) > chars.len() {
|
let len = if offset + len as usize > chars.len() {
|
||||||
chars.len() - offset
|
chars.len() - offset
|
||||||
} else {
|
} else {
|
||||||
len as usize
|
len as usize
|
||||||
@ -294,6 +247,109 @@ mod string_functions {
|
|||||||
pub fn replace_char(s: &mut ImmutableString, find: char, sub: char) {
|
pub fn replace_char(s: &mut ImmutableString, find: char, sub: char) {
|
||||||
*s = s.replace(&find.to_string(), &sub.to_string()).into();
|
*s = s.replace(&find.to_string(), &sub.to_string()).into();
|
||||||
}
|
}
|
||||||
|
#[rhai_fn(return_raw)]
|
||||||
|
pub fn pad(
|
||||||
|
_context: NativeCallContext,
|
||||||
|
s: &mut ImmutableString,
|
||||||
|
len: INT,
|
||||||
|
ch: char,
|
||||||
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
|
// Check if string will be over max size limit
|
||||||
|
#[cfg(not(feature = "unchecked"))]
|
||||||
|
if _context.engine().max_string_size() > 0
|
||||||
|
&& len as usize > _context.engine().max_string_size()
|
||||||
|
{
|
||||||
|
return EvalAltResult::ErrorDataTooLarge(
|
||||||
|
"Length of string".to_string(),
|
||||||
|
_context.engine().max_string_size(),
|
||||||
|
len as usize,
|
||||||
|
Position::none(),
|
||||||
|
)
|
||||||
|
.into();
|
||||||
|
}
|
||||||
|
|
||||||
|
if len > 0 {
|
||||||
|
let orig_len = s.chars().count();
|
||||||
|
|
||||||
|
if len as usize > orig_len {
|
||||||
|
let p = s.make_mut();
|
||||||
|
|
||||||
|
for _ in 0..(len as usize - orig_len) {
|
||||||
|
p.push(ch);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "unchecked"))]
|
||||||
|
if _context.engine().max_string_size() > 0
|
||||||
|
&& s.len() > _context.engine().max_string_size()
|
||||||
|
{
|
||||||
|
return EvalAltResult::ErrorDataTooLarge(
|
||||||
|
"Length of string".to_string(),
|
||||||
|
_context.engine().max_string_size(),
|
||||||
|
s.len(),
|
||||||
|
Position::none(),
|
||||||
|
)
|
||||||
|
.into();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(().into())
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "pad", return_raw)]
|
||||||
|
pub fn pad_with_string(
|
||||||
|
_context: NativeCallContext,
|
||||||
|
s: &mut ImmutableString,
|
||||||
|
len: INT,
|
||||||
|
padding: &str,
|
||||||
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
|
// Check if string will be over max size limit
|
||||||
|
#[cfg(not(feature = "unchecked"))]
|
||||||
|
if _context.engine().max_string_size() > 0
|
||||||
|
&& len as usize > _context.engine().max_string_size()
|
||||||
|
{
|
||||||
|
return EvalAltResult::ErrorDataTooLarge(
|
||||||
|
"Length of string".to_string(),
|
||||||
|
_context.engine().max_string_size(),
|
||||||
|
len as usize,
|
||||||
|
Position::none(),
|
||||||
|
)
|
||||||
|
.into();
|
||||||
|
}
|
||||||
|
|
||||||
|
if len > 0 {
|
||||||
|
let mut str_len = s.chars().count();
|
||||||
|
let padding_len = padding.chars().count();
|
||||||
|
|
||||||
|
if len as usize > str_len {
|
||||||
|
let p = s.make_mut();
|
||||||
|
|
||||||
|
while str_len < len as usize {
|
||||||
|
if str_len + padding_len <= len as usize {
|
||||||
|
p.push_str(padding);
|
||||||
|
str_len += padding_len;
|
||||||
|
} else {
|
||||||
|
p.extend(padding.chars().take(len as usize - str_len));
|
||||||
|
str_len = len as usize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "unchecked"))]
|
||||||
|
if _context.engine().max_string_size() > 0
|
||||||
|
&& s.len() > _context.engine().max_string_size()
|
||||||
|
{
|
||||||
|
return EvalAltResult::ErrorDataTooLarge(
|
||||||
|
"Length of string".to_string(),
|
||||||
|
_context.engine().max_string_size(),
|
||||||
|
s.len(),
|
||||||
|
Position::none(),
|
||||||
|
)
|
||||||
|
.into();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(().into())
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
pub mod arrays {
|
pub mod arrays {
|
||||||
|
@ -16,7 +16,7 @@ use crate::{calc_fn_hash, StaticVec};
|
|||||||
use crate::engine::Array;
|
use crate::engine::Array;
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
use crate::engine::{make_getter, make_setter, Map};
|
use crate::engine::{make_getter, make_setter, Map, KEYWORD_EVAL, KEYWORD_FN_PTR};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
use crate::engine::{FN_ANONYMOUS, KEYWORD_FN_PTR_CURRY};
|
use crate::engine::{FN_ANONYMOUS, KEYWORD_FN_PTR_CURRY};
|
||||||
@ -601,13 +601,15 @@ struct ParseState<'e> {
|
|||||||
/// All consequent calls to `access_var` will not be affected
|
/// All consequent calls to `access_var` will not be affected
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
allow_capture: bool,
|
allow_capture: bool,
|
||||||
/// Encapsulates a local stack with variable names to simulate an actual runtime scope.
|
/// Encapsulates a local stack with imported module names.
|
||||||
|
#[cfg(not(feature = "no_module"))]
|
||||||
modules: Vec<String>,
|
modules: Vec<String>,
|
||||||
/// Maximum levels of expression nesting.
|
/// Maximum levels of expression nesting.
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
max_expr_depth: usize,
|
max_expr_depth: usize,
|
||||||
/// Maximum levels of expression nesting in functions.
|
/// Maximum levels of expression nesting in functions.
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
max_function_expr_depth: usize,
|
max_function_expr_depth: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -617,19 +619,23 @@ impl<'e> ParseState<'e> {
|
|||||||
pub fn new(
|
pub fn new(
|
||||||
engine: &'e Engine,
|
engine: &'e Engine,
|
||||||
#[cfg(not(feature = "unchecked"))] max_expr_depth: usize,
|
#[cfg(not(feature = "unchecked"))] max_expr_depth: usize,
|
||||||
#[cfg(not(feature = "unchecked"))] max_function_expr_depth: usize,
|
#[cfg(not(feature = "unchecked"))]
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
max_function_expr_depth: usize,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
engine,
|
engine,
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
max_expr_depth,
|
max_expr_depth,
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
max_function_expr_depth,
|
max_function_expr_depth,
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
externals: Default::default(),
|
externals: Default::default(),
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
allow_capture: true,
|
allow_capture: true,
|
||||||
stack: Default::default(),
|
stack: Default::default(),
|
||||||
|
#[cfg(not(feature = "no_module"))]
|
||||||
modules: Default::default(),
|
modules: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -664,9 +670,16 @@ impl<'e> ParseState<'e> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Find a module by name in the `ParseState`, searching in reverse.
|
/// Find a module by name in the `ParseState`, searching in reverse.
|
||||||
/// The return value is the offset to be deducted from `Stack::len`,
|
///
|
||||||
|
/// Returns the offset to be deducted from `Stack::len`,
|
||||||
/// i.e. the top element of the `ParseState` is offset 1.
|
/// i.e. the top element of the `ParseState` is offset 1.
|
||||||
/// Return `None` when the variable name is not found in the `ParseState`.
|
///
|
||||||
|
/// Returns `None` when the variable name is not found in the `ParseState`.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics when called under `no_module`.
|
||||||
|
#[cfg(not(feature = "no_module"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn find_module(&self, name: &str) -> Option<NonZeroUsize> {
|
pub fn find_module(&self, name: &str) -> Option<NonZeroUsize> {
|
||||||
self.modules
|
self.modules
|
||||||
@ -1079,6 +1092,14 @@ impl Expr {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Is the expression a simple variable access?
|
||||||
|
pub(crate) fn get_variable_access(&self, non_qualified: bool) -> Option<&str> {
|
||||||
|
match self {
|
||||||
|
Self::Variable(x) if !non_qualified || x.1.is_none() => Some((x.0).0.as_str()),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the `Position` of the expression.
|
/// Get the `Position` of the expression.
|
||||||
pub fn position(&self) -> Position {
|
pub fn position(&self) -> Position {
|
||||||
match self {
|
match self {
|
||||||
@ -1397,6 +1418,7 @@ fn parse_fn_call(
|
|||||||
eat_token(input, Token::RightParen);
|
eat_token(input, Token::RightParen);
|
||||||
|
|
||||||
let hash_script = if let Some(modules) = modules.as_mut() {
|
let hash_script = if let Some(modules) = modules.as_mut() {
|
||||||
|
#[cfg(not(feature = "no_module"))]
|
||||||
modules.set_index(state.find_module(&modules[0].0));
|
modules.set_index(state.find_module(&modules[0].0));
|
||||||
|
|
||||||
// Rust functions are indexed in two steps:
|
// Rust functions are indexed in two steps:
|
||||||
@ -1439,6 +1461,7 @@ fn parse_fn_call(
|
|||||||
eat_token(input, Token::RightParen);
|
eat_token(input, Token::RightParen);
|
||||||
|
|
||||||
let hash_script = if let Some(modules) = modules.as_mut() {
|
let hash_script = if let Some(modules) = modules.as_mut() {
|
||||||
|
#[cfg(not(feature = "no_module"))]
|
||||||
modules.set_index(state.find_module(&modules[0].0));
|
modules.set_index(state.find_module(&modules[0].0));
|
||||||
|
|
||||||
// Rust functions are indexed in two steps:
|
// Rust functions are indexed in two steps:
|
||||||
@ -1686,11 +1709,10 @@ fn parse_array_literal(
|
|||||||
|
|
||||||
while !input.peek().unwrap().0.is_eof() {
|
while !input.peek().unwrap().0.is_eof() {
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
if state.engine.limits.max_array_size > 0 && arr.len() >= state.engine.limits.max_array_size
|
if state.engine.max_array_size() > 0 && arr.len() >= state.engine.max_array_size() {
|
||||||
{
|
|
||||||
return Err(PERR::LiteralTooLarge(
|
return Err(PERR::LiteralTooLarge(
|
||||||
"Size of array literal".to_string(),
|
"Size of array literal".to_string(),
|
||||||
state.engine.limits.max_array_size,
|
state.engine.max_array_size(),
|
||||||
)
|
)
|
||||||
.into_err(input.peek().unwrap().1));
|
.into_err(input.peek().unwrap().1));
|
||||||
}
|
}
|
||||||
@ -1794,10 +1816,10 @@ fn parse_map_literal(
|
|||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
if state.engine.limits.max_map_size > 0 && map.len() >= state.engine.limits.max_map_size {
|
if state.engine.max_map_size() > 0 && map.len() >= state.engine.max_map_size() {
|
||||||
return Err(PERR::LiteralTooLarge(
|
return Err(PERR::LiteralTooLarge(
|
||||||
"Number of properties in object map literal".to_string(),
|
"Number of properties in object map literal".to_string(),
|
||||||
state.engine.limits.max_map_size,
|
state.engine.max_map_size(),
|
||||||
)
|
)
|
||||||
.into_err(input.peek().unwrap().1));
|
.into_err(input.peek().unwrap().1));
|
||||||
}
|
}
|
||||||
@ -2025,6 +2047,8 @@ fn parse_primary(
|
|||||||
|
|
||||||
// Qualifiers + variable name
|
// Qualifiers + variable name
|
||||||
*hash = calc_fn_hash(modules.iter().map(|(v, _)| v.as_str()), name, 0, empty());
|
*hash = calc_fn_hash(modules.iter().map(|(v, _)| v.as_str()), name, 0, empty());
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_module"))]
|
||||||
modules.set_index(state.find_module(&modules[0].0));
|
modules.set_index(state.find_module(&modules[0].0));
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
@ -2310,6 +2334,17 @@ fn make_dot_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result<Expr, ParseEr
|
|||||||
op_pos,
|
op_pos,
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
|
// lhs.Fn() or lhs.eval()
|
||||||
|
(_, Expr::FnCall(x))
|
||||||
|
if x.3.len() == 0 && [KEYWORD_FN_PTR, KEYWORD_EVAL].contains(&(x.0).0.as_ref()) =>
|
||||||
|
{
|
||||||
|
return Err(PERR::BadInput(format!(
|
||||||
|
"'{}' should not be called in method style. Try {}(...);",
|
||||||
|
(x.0).0,
|
||||||
|
(x.0).0
|
||||||
|
))
|
||||||
|
.into_err((x.0).3));
|
||||||
|
}
|
||||||
// lhs.func!(...)
|
// lhs.func!(...)
|
||||||
(_, Expr::FnCall(x)) if (x.0).2 => {
|
(_, Expr::FnCall(x)) if (x.0).2 => {
|
||||||
return Err(PERR::MalformedCapture(
|
return Err(PERR::MalformedCapture(
|
||||||
@ -3057,6 +3092,8 @@ fn parse_block(
|
|||||||
|
|
||||||
let mut statements = StaticVec::new();
|
let mut statements = StaticVec::new();
|
||||||
let prev_stack_len = state.stack.len();
|
let prev_stack_len = state.stack.len();
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_module"))]
|
||||||
let prev_mods_len = state.modules.len();
|
let prev_mods_len = state.modules.len();
|
||||||
|
|
||||||
while !match_token(input, Token::RightBrace)? {
|
while !match_token(input, Token::RightBrace)? {
|
||||||
@ -3102,6 +3139,8 @@ fn parse_block(
|
|||||||
}
|
}
|
||||||
|
|
||||||
state.stack.truncate(prev_stack_len);
|
state.stack.truncate(prev_stack_len);
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_module"))]
|
||||||
state.modules.truncate(prev_mods_len);
|
state.modules.truncate(prev_mods_len);
|
||||||
|
|
||||||
Ok(Stmt::Block(Box::new((statements, settings.pos))))
|
Ok(Stmt::Block(Box::new((statements, settings.pos))))
|
||||||
@ -3578,9 +3617,10 @@ impl Engine {
|
|||||||
let mut state = ParseState::new(
|
let mut state = ParseState::new(
|
||||||
self,
|
self,
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
self.limits.max_expr_depth,
|
self.max_expr_depth(),
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
self.limits.max_function_expr_depth,
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
self.max_function_expr_depth(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let settings = ParseSettings {
|
let settings = ParseSettings {
|
||||||
@ -3625,9 +3665,10 @@ impl Engine {
|
|||||||
let mut state = ParseState::new(
|
let mut state = ParseState::new(
|
||||||
self,
|
self,
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
self.limits.max_expr_depth,
|
self.max_expr_depth(),
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
self.limits.max_function_expr_depth,
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
self.max_function_expr_depth(),
|
||||||
);
|
);
|
||||||
|
|
||||||
while !input.peek().unwrap().0.is_eof() {
|
while !input.peek().unwrap().0.is_eof() {
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
pub use crate::any::Dynamic;
|
pub use crate::any::Dynamic;
|
||||||
pub use crate::engine::Engine;
|
pub use crate::engine::Engine;
|
||||||
pub use crate::fn_native::CallableFunction;
|
pub use crate::fn_native::{CallableFunction, NativeCallContext};
|
||||||
pub use crate::fn_register::{RegisterFn, RegisterResultFn};
|
pub use crate::fn_register::{RegisterFn, RegisterResultFn};
|
||||||
pub use crate::module::Module;
|
pub use crate::module::Module;
|
||||||
pub use crate::parser::FnAccess;
|
pub use crate::parser::FnAccess;
|
||||||
@ -22,7 +22,11 @@ pub use rhai_codegen::{export_fn, register_exported_fn};
|
|||||||
/// Use the `#[export_module]` and `#[export_fn]` procedural attributes instead.
|
/// Use the `#[export_module]` and `#[export_fn]` procedural attributes instead.
|
||||||
pub trait PluginFunction {
|
pub trait PluginFunction {
|
||||||
/// Call the plugin function with the arguments provided.
|
/// Call the plugin function with the arguments provided.
|
||||||
fn call(&self, args: &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>>;
|
fn call(
|
||||||
|
&self,
|
||||||
|
context: NativeCallContext,
|
||||||
|
args: &mut [&mut Dynamic],
|
||||||
|
) -> Result<Dynamic, Box<EvalAltResult>>;
|
||||||
|
|
||||||
/// Is this plugin function a method?
|
/// Is this plugin function a method?
|
||||||
fn is_method_call(&self) -> bool;
|
fn is_method_call(&self) -> bool;
|
||||||
|
@ -15,10 +15,6 @@ use crate::stdlib::{
|
|||||||
string::{String, ToString},
|
string::{String, ToString},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_std"))]
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
use crate::stdlib::path::PathBuf;
|
|
||||||
|
|
||||||
/// Evaluation result.
|
/// Evaluation result.
|
||||||
///
|
///
|
||||||
/// All wrapped `Position` values represent the location in the script where the error occurs.
|
/// All wrapped `Position` values represent the location in the script where the error occurs.
|
||||||
@ -27,16 +23,12 @@ use crate::stdlib::path::PathBuf;
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
pub enum EvalAltResult {
|
pub enum EvalAltResult {
|
||||||
|
/// System error. Wrapped values are the error message and the internal error.
|
||||||
|
ErrorSystem(String, Box<dyn Error>),
|
||||||
|
|
||||||
/// Syntax error.
|
/// Syntax error.
|
||||||
ErrorParsing(ParseErrorType, Position),
|
ErrorParsing(ParseErrorType, Position),
|
||||||
|
|
||||||
/// Error reading from a script file. Wrapped value is the path of the script file.
|
|
||||||
///
|
|
||||||
/// Never appears under the `no_std` feature.
|
|
||||||
#[cfg(not(feature = "no_std"))]
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
ErrorReadingScriptFile(PathBuf, Position, std::io::Error),
|
|
||||||
|
|
||||||
/// Usage of an unknown variable. Wrapped value is the variable name.
|
/// Usage of an unknown variable. Wrapped value is the variable name.
|
||||||
ErrorVariableNotFound(String, Position),
|
ErrorVariableNotFound(String, Position),
|
||||||
/// Call to an unknown function. Wrapped value is the function signature.
|
/// Call to an unknown function. Wrapped value is the function signature.
|
||||||
@ -96,7 +88,7 @@ pub enum EvalAltResult {
|
|||||||
/// Breaking out of loops - not an error if within a loop.
|
/// Breaking out of loops - not an error if within a loop.
|
||||||
/// The wrapped value, if true, means breaking clean out of the loop (i.e. a `break` statement).
|
/// The wrapped value, if true, means breaking clean out of the loop (i.e. a `break` statement).
|
||||||
/// The wrapped value, if false, means breaking the current context (i.e. a `continue` statement).
|
/// The wrapped value, if false, means breaking the current context (i.e. a `continue` statement).
|
||||||
ErrorLoopBreak(bool, Position),
|
LoopBreak(bool, Position),
|
||||||
/// Not an error: Value returned from a script via the `return` keyword.
|
/// Not an error: Value returned from a script via the `return` keyword.
|
||||||
/// Wrapped value is the result value.
|
/// Wrapped value is the result value.
|
||||||
Return(Dynamic, Position),
|
Return(Dynamic, Position),
|
||||||
@ -105,10 +97,8 @@ pub enum EvalAltResult {
|
|||||||
impl EvalAltResult {
|
impl EvalAltResult {
|
||||||
pub(crate) fn desc(&self) -> &str {
|
pub(crate) fn desc(&self) -> &str {
|
||||||
match self {
|
match self {
|
||||||
#[cfg(not(feature = "no_std"))]
|
#[allow(deprecated)]
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
Self::ErrorSystem(_, s) => s.description(),
|
||||||
Self::ErrorReadingScriptFile(_, _, _) => "Cannot read from script file",
|
|
||||||
|
|
||||||
Self::ErrorParsing(p, _) => p.desc(),
|
Self::ErrorParsing(p, _) => p.desc(),
|
||||||
Self::ErrorInFunctionCall(_, _, _) => "Error in called function",
|
Self::ErrorInFunctionCall(_, _, _) => "Error in called function",
|
||||||
Self::ErrorInModule(_, _, _) => "Error in module",
|
Self::ErrorInModule(_, _, _) => "Error in module",
|
||||||
@ -146,8 +136,8 @@ impl EvalAltResult {
|
|||||||
Self::ErrorDataTooLarge(_, _, _, _) => "Data size exceeds maximum limit",
|
Self::ErrorDataTooLarge(_, _, _, _) => "Data size exceeds maximum limit",
|
||||||
Self::ErrorTerminated(_) => "Script terminated.",
|
Self::ErrorTerminated(_) => "Script terminated.",
|
||||||
Self::ErrorRuntime(_, _) => "Runtime error",
|
Self::ErrorRuntime(_, _) => "Runtime error",
|
||||||
Self::ErrorLoopBreak(true, _) => "Break statement not inside a loop",
|
Self::LoopBreak(true, _) => "Break statement not inside a loop",
|
||||||
Self::ErrorLoopBreak(false, _) => "Continue statement not inside a loop",
|
Self::LoopBreak(false, _) => "Continue statement not inside a loop",
|
||||||
Self::Return(_, _) => "[Not Error] Function returns value",
|
Self::Return(_, _) => "[Not Error] Function returns value",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -161,11 +151,8 @@ impl fmt::Display for EvalAltResult {
|
|||||||
let pos = self.position();
|
let pos = self.position();
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
#[cfg(not(feature = "no_std"))]
|
Self::ErrorSystem(s, _) if s.is_empty() => f.write_str(desc)?,
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
Self::ErrorSystem(s, _) => write!(f, "{}: {}", s, desc)?,
|
||||||
Self::ErrorReadingScriptFile(path, _, err) => {
|
|
||||||
write!(f, "{} '{}': {}", desc, path.display(), err)?
|
|
||||||
}
|
|
||||||
|
|
||||||
Self::ErrorParsing(p, _) => write!(f, "Syntax error: {}", p)?,
|
Self::ErrorParsing(p, _) => write!(f, "Syntax error: {}", p)?,
|
||||||
|
|
||||||
@ -213,7 +200,7 @@ impl fmt::Display for EvalAltResult {
|
|||||||
}
|
}
|
||||||
Self::ErrorArithmetic(s, _) => f.write_str(s)?,
|
Self::ErrorArithmetic(s, _) => f.write_str(s)?,
|
||||||
|
|
||||||
Self::ErrorLoopBreak(_, _) => f.write_str(desc)?,
|
Self::LoopBreak(_, _) => f.write_str(desc)?,
|
||||||
Self::Return(_, _) => f.write_str(desc)?,
|
Self::Return(_, _) => f.write_str(desc)?,
|
||||||
|
|
||||||
Self::ErrorArrayBounds(_, index, _) if *index < 0 => {
|
Self::ErrorArrayBounds(_, index, _) if *index < 0 => {
|
||||||
@ -258,6 +245,13 @@ impl fmt::Display for EvalAltResult {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T: AsRef<str>> From<T> for EvalAltResult {
|
||||||
|
#[inline(always)]
|
||||||
|
fn from(err: T) -> Self {
|
||||||
|
Self::ErrorRuntime(err.as_ref().to_string(), Position::none())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T: AsRef<str>> From<T> for Box<EvalAltResult> {
|
impl<T: AsRef<str>> From<T> for Box<EvalAltResult> {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn from(err: T) -> Self {
|
fn from(err: T) -> Self {
|
||||||
@ -272,9 +266,7 @@ impl EvalAltResult {
|
|||||||
/// Get the `Position` of this error.
|
/// Get the `Position` of this error.
|
||||||
pub fn position(&self) -> Position {
|
pub fn position(&self) -> Position {
|
||||||
match self {
|
match self {
|
||||||
#[cfg(not(feature = "no_std"))]
|
Self::ErrorSystem(_, _) => Position::none(),
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
Self::ErrorReadingScriptFile(_, pos, _) => *pos,
|
|
||||||
|
|
||||||
Self::ErrorParsing(_, pos)
|
Self::ErrorParsing(_, pos)
|
||||||
| Self::ErrorFunctionNotFound(_, pos)
|
| Self::ErrorFunctionNotFound(_, pos)
|
||||||
@ -301,7 +293,7 @@ impl EvalAltResult {
|
|||||||
| Self::ErrorDataTooLarge(_, _, _, pos)
|
| Self::ErrorDataTooLarge(_, _, _, pos)
|
||||||
| Self::ErrorTerminated(pos)
|
| Self::ErrorTerminated(pos)
|
||||||
| Self::ErrorRuntime(_, pos)
|
| Self::ErrorRuntime(_, pos)
|
||||||
| Self::ErrorLoopBreak(_, pos)
|
| Self::LoopBreak(_, pos)
|
||||||
| Self::Return(_, pos) => *pos,
|
| Self::Return(_, pos) => *pos,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -309,9 +301,7 @@ impl EvalAltResult {
|
|||||||
/// Override the `Position` of this error.
|
/// Override the `Position` of this error.
|
||||||
pub fn set_position(&mut self, new_position: Position) {
|
pub fn set_position(&mut self, new_position: Position) {
|
||||||
match self {
|
match self {
|
||||||
#[cfg(not(feature = "no_std"))]
|
Self::ErrorSystem(_, _) => (),
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
Self::ErrorReadingScriptFile(_, pos, _) => *pos = new_position,
|
|
||||||
|
|
||||||
Self::ErrorParsing(_, pos)
|
Self::ErrorParsing(_, pos)
|
||||||
| Self::ErrorFunctionNotFound(_, pos)
|
| Self::ErrorFunctionNotFound(_, pos)
|
||||||
@ -338,7 +328,7 @@ impl EvalAltResult {
|
|||||||
| Self::ErrorDataTooLarge(_, _, _, pos)
|
| Self::ErrorDataTooLarge(_, _, _, pos)
|
||||||
| Self::ErrorTerminated(pos)
|
| Self::ErrorTerminated(pos)
|
||||||
| Self::ErrorRuntime(_, pos)
|
| Self::ErrorRuntime(_, pos)
|
||||||
| Self::ErrorLoopBreak(_, pos)
|
| Self::LoopBreak(_, pos)
|
||||||
| Self::Return(_, pos) => *pos = new_position,
|
| Self::Return(_, pos) => *pos = new_position,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -53,7 +53,7 @@ impl Engine {
|
|||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn set_max_call_levels(&mut self, levels: usize) -> &mut Self {
|
pub fn set_max_call_levels(&mut self, levels: usize) -> &mut Self {
|
||||||
self.limits.max_call_stack_depth = levels;
|
self.limits_set.max_call_stack_depth = levels;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,7 +61,7 @@ impl Engine {
|
|||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn max_call_levels(&self) -> usize {
|
pub fn max_call_levels(&self) -> usize {
|
||||||
self.limits.max_call_stack_depth
|
self.limits_set.max_call_stack_depth
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the maximum number of operations allowed for a script to run to avoid
|
/// Set the maximum number of operations allowed for a script to run to avoid
|
||||||
@ -69,7 +69,7 @@ impl Engine {
|
|||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn set_max_operations(&mut self, operations: u64) -> &mut Self {
|
pub fn set_max_operations(&mut self, operations: u64) -> &mut Self {
|
||||||
self.limits.max_operations = if operations == u64::MAX {
|
self.limits_set.max_operations = if operations == u64::MAX {
|
||||||
0
|
0
|
||||||
} else {
|
} else {
|
||||||
operations
|
operations
|
||||||
@ -81,22 +81,24 @@ impl Engine {
|
|||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn max_operations(&self) -> u64 {
|
pub fn max_operations(&self) -> u64 {
|
||||||
self.limits.max_operations
|
self.limits_set.max_operations
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the maximum number of imported modules allowed for a script.
|
/// Set the maximum number of imported modules allowed for a script.
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
|
#[cfg(not(feature = "no_module"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn set_max_modules(&mut self, modules: usize) -> &mut Self {
|
pub fn set_max_modules(&mut self, modules: usize) -> &mut Self {
|
||||||
self.limits.max_modules = modules;
|
self.limits_set.max_modules = modules;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The maximum number of imported modules allowed for a script.
|
/// The maximum number of imported modules allowed for a script.
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
|
#[cfg(not(feature = "no_module"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn max_modules(&self) -> usize {
|
pub fn max_modules(&self) -> usize {
|
||||||
self.limits.max_modules
|
self.limits_set.max_modules
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the depth limits for expressions (0 for unlimited).
|
/// Set the depth limits for expressions (0 for unlimited).
|
||||||
@ -105,18 +107,21 @@ impl Engine {
|
|||||||
pub fn set_max_expr_depths(
|
pub fn set_max_expr_depths(
|
||||||
&mut self,
|
&mut self,
|
||||||
max_expr_depth: usize,
|
max_expr_depth: usize,
|
||||||
max_function_expr_depth: usize,
|
#[cfg(not(feature = "no_function"))] max_function_expr_depth: usize,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
self.limits.max_expr_depth = if max_expr_depth == usize::MAX {
|
self.limits_set.max_expr_depth = if max_expr_depth == usize::MAX {
|
||||||
0
|
0
|
||||||
} else {
|
} else {
|
||||||
max_expr_depth
|
max_expr_depth
|
||||||
};
|
};
|
||||||
self.limits.max_function_expr_depth = if max_function_expr_depth == usize::MAX {
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
{
|
||||||
|
self.limits_set.max_function_expr_depth = if max_function_expr_depth == usize::MAX {
|
||||||
0
|
0
|
||||||
} else {
|
} else {
|
||||||
max_function_expr_depth
|
max_function_expr_depth
|
||||||
};
|
};
|
||||||
|
}
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,21 +129,22 @@ impl Engine {
|
|||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn max_expr_depth(&self) -> usize {
|
pub fn max_expr_depth(&self) -> usize {
|
||||||
self.limits.max_expr_depth
|
self.limits_set.max_expr_depth
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The depth limit for expressions in functions (0 for unlimited).
|
/// The depth limit for expressions in functions (0 for unlimited).
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn max_function_expr_depth(&self) -> usize {
|
pub fn max_function_expr_depth(&self) -> usize {
|
||||||
self.limits.max_function_expr_depth
|
self.limits_set.max_function_expr_depth
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the maximum length of strings (0 for unlimited).
|
/// Set the maximum length of strings (0 for unlimited).
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn set_max_string_size(&mut self, max_size: usize) -> &mut Self {
|
pub fn set_max_string_size(&mut self, max_size: usize) -> &mut Self {
|
||||||
self.limits.max_string_size = if max_size == usize::MAX { 0 } else { max_size };
|
self.limits_set.max_string_size = if max_size == usize::MAX { 0 } else { max_size };
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -146,7 +152,7 @@ impl Engine {
|
|||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn max_string_size(&self) -> usize {
|
pub fn max_string_size(&self) -> usize {
|
||||||
self.limits.max_string_size
|
self.limits_set.max_string_size
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the maximum length of arrays (0 for unlimited).
|
/// Set the maximum length of arrays (0 for unlimited).
|
||||||
@ -154,7 +160,7 @@ impl Engine {
|
|||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn set_max_array_size(&mut self, max_size: usize) -> &mut Self {
|
pub fn set_max_array_size(&mut self, max_size: usize) -> &mut Self {
|
||||||
self.limits.max_array_size = if max_size == usize::MAX { 0 } else { max_size };
|
self.limits_set.max_array_size = if max_size == usize::MAX { 0 } else { max_size };
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -163,7 +169,7 @@ impl Engine {
|
|||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn max_array_size(&self) -> usize {
|
pub fn max_array_size(&self) -> usize {
|
||||||
self.limits.max_array_size
|
self.limits_set.max_array_size
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the maximum length of object maps (0 for unlimited).
|
/// Set the maximum length of object maps (0 for unlimited).
|
||||||
@ -171,7 +177,7 @@ impl Engine {
|
|||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn set_max_map_size(&mut self, max_size: usize) -> &mut Self {
|
pub fn set_max_map_size(&mut self, max_size: usize) -> &mut Self {
|
||||||
self.limits.max_map_size = if max_size == usize::MAX { 0 } else { max_size };
|
self.limits_set.max_map_size = if max_size == usize::MAX { 0 } else { max_size };
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -180,7 +186,7 @@ impl Engine {
|
|||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn max_map_size(&self) -> usize {
|
pub fn max_map_size(&self) -> usize {
|
||||||
self.limits.max_map_size
|
self.limits_set.max_map_size
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the module resolution service used by the `Engine`.
|
/// Set the module resolution service used by the `Engine`.
|
||||||
|
@ -41,10 +41,7 @@ impl Expression<'_> {
|
|||||||
/// If this expression is a variable name, return it. Otherwise `None`.
|
/// If this expression is a variable name, return it. Otherwise `None`.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn get_variable_name(&self) -> Option<&str> {
|
pub fn get_variable_name(&self) -> Option<&str> {
|
||||||
match self.0 {
|
self.0.get_variable_access(true)
|
||||||
Expr::Variable(x) => Some((x.0).0.as_str()),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
/// Get the expression.
|
/// Get the expression.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@ -70,14 +67,14 @@ impl EvalContext<'_, '_, '_, '_, '_, '_> {
|
|||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
expr: &Expression,
|
expr: &Expression,
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
self.engine.eval_expr(
|
self.engine().eval_expr(
|
||||||
scope,
|
scope,
|
||||||
self.mods,
|
self.mods,
|
||||||
self.state,
|
self.state,
|
||||||
self.lib,
|
self.namespace(),
|
||||||
self.this_ptr,
|
self.this_ptr,
|
||||||
expr.expr(),
|
expr.expr(),
|
||||||
self.level,
|
self.call_level(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1747,7 +1747,7 @@ pub fn lex<'a, 'e>(
|
|||||||
engine,
|
engine,
|
||||||
state: TokenizeState {
|
state: TokenizeState {
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
max_string_size: engine.limits.max_string_size,
|
max_string_size: engine.limits_set.max_string_size,
|
||||||
#[cfg(feature = "unchecked")]
|
#[cfg(feature = "unchecked")]
|
||||||
max_string_size: 0,
|
max_string_size: 0,
|
||||||
non_unary: false,
|
non_unary: false,
|
||||||
|
@ -98,12 +98,12 @@ fn test_fn_ptr_raw() -> Result<(), Box<EvalAltResult>> {
|
|||||||
TypeId::of::<FnPtr>(),
|
TypeId::of::<FnPtr>(),
|
||||||
TypeId::of::<INT>(),
|
TypeId::of::<INT>(),
|
||||||
],
|
],
|
||||||
move |engine: &Engine, lib: &Module, args: &mut [&mut Dynamic]| {
|
move |context, args| {
|
||||||
let fp = std::mem::take(args[1]).cast::<FnPtr>();
|
let fp = std::mem::take(args[1]).cast::<FnPtr>();
|
||||||
let value = args[2].clone();
|
let value = args[2].clone();
|
||||||
let this_ptr = args.get_mut(0).unwrap();
|
let this_ptr = args.get_mut(0).unwrap();
|
||||||
|
|
||||||
fp.call_dynamic(engine, lib, Some(this_ptr), [value])
|
fp.call_dynamic(context, Some(this_ptr), [value])
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#![cfg(not(feature = "no_function"))]
|
#![cfg(not(feature = "no_function"))]
|
||||||
use rhai::{Dynamic, Engine, EvalAltResult, FnPtr, Module, ParseErrorType, RegisterFn, Scope, INT};
|
use rhai::{Engine, EvalAltResult, FnPtr, ParseErrorType, RegisterFn, Scope, INT};
|
||||||
use std::any::TypeId;
|
use std::any::TypeId;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::mem::take;
|
use std::mem::take;
|
||||||
@ -16,9 +16,9 @@ fn test_fn_ptr_curry_call() -> Result<(), Box<EvalAltResult>> {
|
|||||||
engine.register_raw_fn(
|
engine.register_raw_fn(
|
||||||
"call_with_arg",
|
"call_with_arg",
|
||||||
&[TypeId::of::<FnPtr>(), TypeId::of::<INT>()],
|
&[TypeId::of::<FnPtr>(), TypeId::of::<INT>()],
|
||||||
|engine: &Engine, lib: &Module, args: &mut [&mut Dynamic]| {
|
|context, args| {
|
||||||
let fn_ptr = std::mem::take(args[0]).cast::<FnPtr>();
|
let fn_ptr = std::mem::take(args[0]).cast::<FnPtr>();
|
||||||
fn_ptr.call_dynamic(engine, lib, None, [std::mem::take(args[1])])
|
fn_ptr.call_dynamic(context, None, [std::mem::take(args[1])])
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -135,10 +135,10 @@ fn test_closures() -> Result<(), Box<EvalAltResult>> {
|
|||||||
engine.register_raw_fn(
|
engine.register_raw_fn(
|
||||||
"custom_call",
|
"custom_call",
|
||||||
&[TypeId::of::<INT>(), TypeId::of::<FnPtr>()],
|
&[TypeId::of::<INT>(), TypeId::of::<FnPtr>()],
|
||||||
|engine: &Engine, module: &Module, args: &mut [&mut Dynamic]| {
|
|context, args| {
|
||||||
let func = take(args[1]).cast::<FnPtr>();
|
let func = take(args[1]).cast::<FnPtr>();
|
||||||
|
|
||||||
func.call_dynamic(engine, module, None, [])
|
func.call_dynamic(context, None, [])
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -259,7 +259,6 @@ fn test_closures_external() -> Result<(), Box<EvalAltResult>> {
|
|||||||
let mut ast = engine.compile(
|
let mut ast = engine.compile(
|
||||||
r#"
|
r#"
|
||||||
let test = "hello";
|
let test = "hello";
|
||||||
|
|
||||||
|x| test + x
|
|x| test + x
|
||||||
"#,
|
"#,
|
||||||
)?;
|
)?;
|
||||||
@ -271,7 +270,7 @@ fn test_closures_external() -> Result<(), Box<EvalAltResult>> {
|
|||||||
ast.retain_functions(|_, _, _| true);
|
ast.retain_functions(|_, _, _| true);
|
||||||
|
|
||||||
// Closure 'f' captures: the engine, the AST, and the curried function pointer
|
// Closure 'f' captures: the engine, the AST, and the curried function pointer
|
||||||
let f = move |x: INT| fn_ptr.call_dynamic(&engine, ast, None, [x.into()]);
|
let f = move |x: INT| fn_ptr.call_dynamic((&engine, ast.as_ref()).into(), None, [x.into()]);
|
||||||
|
|
||||||
assert_eq!(f(42)?.as_str(), Ok("hello42"));
|
assert_eq!(f(42)?.as_str(), Ok("hello42"));
|
||||||
|
|
||||||
|
@ -40,7 +40,11 @@ fn test_stack_overflow_parsing() -> Result<(), Box<EvalAltResult>> {
|
|||||||
ParseErrorType::ExprTooDeep
|
ParseErrorType::ExprTooDeep
|
||||||
);
|
);
|
||||||
|
|
||||||
engine.set_max_expr_depths(100, 6);
|
engine.set_max_expr_depths(
|
||||||
|
100,
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
6,
|
||||||
|
);
|
||||||
|
|
||||||
engine.compile("1 + 2")?;
|
engine.compile("1 + 2")?;
|
||||||
engine.compile(
|
engine.compile(
|
||||||
|
Loading…
Reference in New Issue
Block a user