Merge pull request #259 from schungx/master
Refactor and minor enhancements.
This commit is contained in:
commit
ae6dbd6c84
@ -6,7 +6,7 @@ members = [
|
|||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "rhai"
|
name = "rhai"
|
||||||
version = "0.20.0"
|
version = "0.19.1"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
authors = ["Jonathan Turner", "Lukáš Hozda", "Stephen Chung", "jhwgh1968"]
|
authors = ["Jonathan Turner", "Lukáš Hozda", "Stephen Chung", "jhwgh1968"]
|
||||||
description = "Embedded scripting for Rust"
|
description = "Embedded scripting for Rust"
|
||||||
|
11
RELEASES.md
11
RELEASES.md
@ -1,10 +1,11 @@
|
|||||||
Rhai Release Notes
|
Rhai Release Notes
|
||||||
==================
|
==================
|
||||||
|
|
||||||
Version 0.20.0
|
Version 0.19.1
|
||||||
==============
|
==============
|
||||||
|
|
||||||
This version adds a variable resolver with the ability to short-circuit variable access.
|
This version adds a variable resolver with the ability to short-circuit variable access,
|
||||||
|
plus a whole bunch of array methods.
|
||||||
|
|
||||||
Breaking changes
|
Breaking changes
|
||||||
----------------
|
----------------
|
||||||
@ -12,6 +13,7 @@ Breaking changes
|
|||||||
* `AST::iter_functions` now returns an iterator instead of taking a closure.
|
* `AST::iter_functions` now returns an iterator instead of taking a closure.
|
||||||
* `Module::get_script_function_by_signature` renamed to `Module::get_script_fn` and returns `&<Shared<ScriptFnDef>>`.
|
* `Module::get_script_function_by_signature` renamed to `Module::get_script_fn` and returns `&<Shared<ScriptFnDef>>`.
|
||||||
* `Module::num_fn`, `Module::num_var` and `Module::num_iter` are removed and merged into `Module::count`.
|
* `Module::num_fn`, `Module::num_var` and `Module::num_iter` are removed and merged into `Module::count`.
|
||||||
|
* `Module::set_iter` is renamed to `Module::set_iter_raw`.
|
||||||
* The `merge_namespaces` parameter to `Module::eval_ast_as_new` is removed and now defaults to `true`.
|
* The `merge_namespaces` parameter to `Module::eval_ast_as_new` is removed and now defaults to `true`.
|
||||||
* `GlobalFileModuleResolver` is removed because its performance gain over the `FileModuleResolver` is no longer very significant.
|
* `GlobalFileModuleResolver` is removed because its performance gain over the `FileModuleResolver` is no longer very significant.
|
||||||
* The following `EvalAltResult` variants are removed and merged into `EvalAltResult::ErrorMismatchDataType`: `ErrorCharMismatch`, `ErrorNumericIndexExpr`, `ErrorStringIndexExpr`, `ErrorImportExpr`, `ErrorLogicGuard`, `ErrorBooleanArgMismatch`
|
* The following `EvalAltResult` variants are removed and merged into `EvalAltResult::ErrorMismatchDataType`: `ErrorCharMismatch`, `ErrorNumericIndexExpr`, `ErrorStringIndexExpr`, `ErrorImportExpr`, `ErrorLogicGuard`, `ErrorBooleanArgMismatch`
|
||||||
@ -20,7 +22,7 @@ Breaking changes
|
|||||||
* New reserved symbols: `++`, `--`, `..`, `...`.
|
* New reserved symbols: `++`, `--`, `..`, `...`.
|
||||||
* Callback signature for custom syntax implementation function is changed to allow for more flexibility.
|
* Callback signature for custom syntax implementation function is changed to allow for more flexibility.
|
||||||
* Default call stack depth for `debug` builds is reduced to 12 (from 16).
|
* Default call stack depth for `debug` builds is reduced to 12 (from 16).
|
||||||
* Precedence for `~` and `%` is raised.
|
* Precedence for `~` is raised, while `in` is moved below logic comparison operators.
|
||||||
|
|
||||||
New features
|
New features
|
||||||
------------
|
------------
|
||||||
@ -31,7 +33,8 @@ New features
|
|||||||
* `is_def_var()` to detect if variable is defined, and `is_def_fn()` to detect if script function is defined.
|
* `is_def_var()` to detect if variable is defined, and `is_def_fn()` to detect if script function is defined.
|
||||||
* `Dynamic::from(&str)` now constructs a `Dynamic` with a copy of the string as value.
|
* `Dynamic::from(&str)` now constructs a `Dynamic` with a copy of the string as value.
|
||||||
* `AST::combine` and `AST::combine_filtered` allows combining two `AST`'s without creating a new one.
|
* `AST::combine` and `AST::combine_filtered` allows combining two `AST`'s without creating a new one.
|
||||||
* `map`, `filter`, `reduce`, `reduce_rev`, `some`, `all`, `splice` and `sort` functions for arrays.
|
* `map`, `filter`, `reduce`, `reduce_rev`, `some`, `all`, `extract`, `splice`, `chop` and `sort` functions for arrays.
|
||||||
|
* New `Module::set_iterable` and `Module::set_iterator` to define type iterators more easily. `Engine::register_iterator` is changed to use the simpler version.
|
||||||
|
|
||||||
Enhancements
|
Enhancements
|
||||||
------------
|
------------
|
||||||
|
@ -16,7 +16,6 @@ use quote::{quote, quote_spanned};
|
|||||||
use syn::{parse::Parse, parse::ParseStream, parse::Parser, spanned::Spanned};
|
use syn::{parse::Parse, parse::ParseStream, parse::Parser, spanned::Spanned};
|
||||||
|
|
||||||
use crate::attrs::{ExportInfo, ExportScope, ExportedParams};
|
use crate::attrs::{ExportInfo, ExportScope, ExportedParams};
|
||||||
use crate::rhai_module::flatten_type_groups;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
pub enum Index {
|
pub enum Index {
|
||||||
@ -48,10 +47,10 @@ impl FnSpecialAccess {
|
|||||||
match self {
|
match self {
|
||||||
FnSpecialAccess::None => None,
|
FnSpecialAccess::None => None,
|
||||||
FnSpecialAccess::Property(Property::Get(ref g)) => {
|
FnSpecialAccess::Property(Property::Get(ref g)) => {
|
||||||
Some((format!("get${}", g.to_string()), g.to_string(), g.span()))
|
Some((format!("{}{}", FN_GET, g), g.to_string(), g.span()))
|
||||||
}
|
}
|
||||||
FnSpecialAccess::Property(Property::Set(ref s)) => {
|
FnSpecialAccess::Property(Property::Set(ref s)) => {
|
||||||
Some((format!("set${}", s.to_string()), s.to_string(), s.span()))
|
Some((format!("{}{}", FN_SET, s), s.to_string(), s.span()))
|
||||||
}
|
}
|
||||||
FnSpecialAccess::Index(Index::Get) => Some((
|
FnSpecialAccess::Index(Index::Get) => Some((
|
||||||
FN_IDX_GET.to_string(),
|
FN_IDX_GET.to_string(),
|
||||||
@ -67,6 +66,14 @@ impl FnSpecialAccess {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn flatten_type_groups(ty: &syn::Type) -> &syn::Type {
|
||||||
|
match ty {
|
||||||
|
syn::Type::Group(syn::TypeGroup { ref elem, .. })
|
||||||
|
| syn::Type::Paren(syn::TypeParen { ref elem, .. }) => flatten_type_groups(elem.as_ref()),
|
||||||
|
_ => ty,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub(crate) struct ExportedFnParams {
|
pub(crate) struct ExportedFnParams {
|
||||||
pub name: Option<Vec<String>>,
|
pub name: Option<Vec<String>>,
|
||||||
@ -76,6 +83,8 @@ pub(crate) struct ExportedFnParams {
|
|||||||
pub special: FnSpecialAccess,
|
pub special: FnSpecialAccess,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub const FN_GET: &str = "get$";
|
||||||
|
pub const FN_SET: &str = "set$";
|
||||||
pub const FN_IDX_GET: &str = "index$get$";
|
pub const FN_IDX_GET: &str = "index$get$";
|
||||||
pub const FN_IDX_SET: &str = "index$set$";
|
pub const FN_IDX_SET: &str = "index$set$";
|
||||||
|
|
||||||
@ -130,36 +139,24 @@ impl ExportedParams for ExportedFnParams {
|
|||||||
"use attribute 'index_set' instead",
|
"use attribute 'index_set' instead",
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
("name", Some(s)) if s.value().starts_with("get$") => {
|
("name", Some(s)) if s.value().starts_with(FN_GET) => {
|
||||||
return Err(syn::Error::new(
|
return Err(syn::Error::new(
|
||||||
item_span,
|
item_span,
|
||||||
format!(
|
format!(
|
||||||
"use attribute 'getter = \"{}\"' instead",
|
"use attribute 'getter = \"{}\"' instead",
|
||||||
&s.value()["get$".len()..]
|
&s.value()[FN_GET.len()..]
|
||||||
),
|
),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
("name", Some(s)) if s.value().starts_with("set$") => {
|
("name", Some(s)) if s.value().starts_with(FN_SET) => {
|
||||||
return Err(syn::Error::new(
|
return Err(syn::Error::new(
|
||||||
item_span,
|
item_span,
|
||||||
format!(
|
format!(
|
||||||
"use attribute 'setter = \"{}\"' instead",
|
"use attribute 'setter = \"{}\"' instead",
|
||||||
&s.value()["set$".len()..]
|
&s.value()[FN_SET.len()..]
|
||||||
),
|
),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
("name", Some(s)) if s.value().contains('$') => {
|
|
||||||
return Err(syn::Error::new(
|
|
||||||
s.span(),
|
|
||||||
"Rhai function names may not contain dollar sign",
|
|
||||||
))
|
|
||||||
}
|
|
||||||
("name", Some(s)) if s.value().contains('.') => {
|
|
||||||
return Err(syn::Error::new(
|
|
||||||
s.span(),
|
|
||||||
"Rhai function names may not contain dot",
|
|
||||||
))
|
|
||||||
}
|
|
||||||
("name", Some(s)) => name.push(s.value()),
|
("name", Some(s)) => name.push(s.value()),
|
||||||
("set", Some(s)) => {
|
("set", Some(s)) => {
|
||||||
special = match special {
|
special = match special {
|
||||||
@ -225,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,
|
||||||
|
return_dynamic: bool,
|
||||||
mut_receiver: bool,
|
mut_receiver: bool,
|
||||||
params: ExportedFnParams,
|
params: ExportedFnParams,
|
||||||
}
|
}
|
||||||
@ -235,6 +233,10 @@ impl Parse for ExportedFn {
|
|||||||
let entire_span = fn_all.span();
|
let entire_span = fn_all.span();
|
||||||
let str_type_path = syn::parse2::<syn::Path>(quote! { str }).unwrap();
|
let str_type_path = syn::parse2::<syn::Path>(quote! { str }).unwrap();
|
||||||
|
|
||||||
|
let dynamic_type_path1 = syn::parse2::<syn::Path>(quote! { Dynamic }).unwrap();
|
||||||
|
let dynamic_type_path2 = syn::parse2::<syn::Path>(quote! { rhai::Dynamic }).unwrap();
|
||||||
|
let mut return_dynamic = 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)?;
|
||||||
|
|
||||||
@ -250,16 +252,16 @@ impl Parse for ExportedFn {
|
|||||||
}) => true,
|
}) => true,
|
||||||
syn::FnArg::Typed(syn::PatType { ref ty, .. }) => {
|
syn::FnArg::Typed(syn::PatType { ref ty, .. }) => {
|
||||||
match flatten_type_groups(ty.as_ref()) {
|
match flatten_type_groups(ty.as_ref()) {
|
||||||
&syn::Type::Reference(syn::TypeReference {
|
syn::Type::Reference(syn::TypeReference {
|
||||||
mutability: Some(_),
|
mutability: Some(_),
|
||||||
..
|
..
|
||||||
}) => true,
|
}) => true,
|
||||||
&syn::Type::Reference(syn::TypeReference {
|
syn::Type::Reference(syn::TypeReference {
|
||||||
mutability: None,
|
mutability: None,
|
||||||
ref elem,
|
ref elem,
|
||||||
..
|
..
|
||||||
}) => match flatten_type_groups(elem.as_ref()) {
|
}) => match flatten_type_groups(elem.as_ref()) {
|
||||||
&syn::Type::Path(ref p) if p.path == str_type_path => false,
|
syn::Type::Path(ref p) if p.path == str_type_path => false,
|
||||||
_ => {
|
_ => {
|
||||||
return Err(syn::Error::new(
|
return Err(syn::Error::new(
|
||||||
ty.span(),
|
ty.span(),
|
||||||
@ -285,18 +287,18 @@ impl Parse for ExportedFn {
|
|||||||
_ => panic!("internal error: receiver argument outside of first position!?"),
|
_ => panic!("internal error: receiver argument outside of first position!?"),
|
||||||
};
|
};
|
||||||
let is_ok = match flatten_type_groups(ty.as_ref()) {
|
let is_ok = match flatten_type_groups(ty.as_ref()) {
|
||||||
&syn::Type::Reference(syn::TypeReference {
|
syn::Type::Reference(syn::TypeReference {
|
||||||
mutability: Some(_),
|
mutability: Some(_),
|
||||||
..
|
..
|
||||||
}) => false,
|
}) => false,
|
||||||
&syn::Type::Reference(syn::TypeReference {
|
syn::Type::Reference(syn::TypeReference {
|
||||||
mutability: None,
|
mutability: None,
|
||||||
ref elem,
|
ref elem,
|
||||||
..
|
..
|
||||||
}) => {
|
}) => {
|
||||||
matches!(flatten_type_groups(elem.as_ref()), &syn::Type::Path(ref p) if p.path == str_type_path)
|
matches!(flatten_type_groups(elem.as_ref()), syn::Type::Path(ref p) if p.path == str_type_path)
|
||||||
}
|
}
|
||||||
&syn::Type::Verbatim(_) => false,
|
syn::Type::Verbatim(_) => false,
|
||||||
_ => true,
|
_ => true,
|
||||||
};
|
};
|
||||||
if !is_ok {
|
if !is_ok {
|
||||||
@ -308,21 +310,26 @@ impl Parse for ExportedFn {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// No returning references or pointers.
|
// Check return type.
|
||||||
if let syn::ReturnType::Type(_, ref rtype) = fn_all.sig.output {
|
if let syn::ReturnType::Type(_, ref rtype) = fn_all.sig.output {
|
||||||
match rtype.as_ref() {
|
match flatten_type_groups(rtype.as_ref()) {
|
||||||
&syn::Type::Ptr(_) => {
|
syn::Type::Ptr(_) => {
|
||||||
return Err(syn::Error::new(
|
return Err(syn::Error::new(
|
||||||
fn_all.sig.output.span(),
|
fn_all.sig.output.span(),
|
||||||
"cannot return a pointer to Rhai",
|
"Rhai functions cannot return pointers",
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
&syn::Type::Reference(_) => {
|
syn::Type::Reference(_) => {
|
||||||
return Err(syn::Error::new(
|
return Err(syn::Error::new(
|
||||||
fn_all.sig.output.span(),
|
fn_all.sig.output.span(),
|
||||||
"cannot return a reference to Rhai",
|
"Rhai functions cannot return references",
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
syn::Type::Path(p)
|
||||||
|
if p.path == dynamic_type_path1 || p.path == dynamic_type_path2 =>
|
||||||
|
{
|
||||||
|
return_dynamic = true
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -330,6 +337,7 @@ impl Parse for ExportedFn {
|
|||||||
entire_span,
|
entire_span,
|
||||||
signature: fn_all.sig,
|
signature: fn_all.sig,
|
||||||
is_public,
|
is_public,
|
||||||
|
return_dynamic,
|
||||||
mut_receiver,
|
mut_receiver,
|
||||||
params: ExportedFnParams::default(),
|
params: ExportedFnParams::default(),
|
||||||
})
|
})
|
||||||
@ -419,7 +427,7 @@ impl ExportedFn {
|
|||||||
|
|
||||||
pub(crate) fn return_type(&self) -> Option<&syn::Type> {
|
pub(crate) fn return_type(&self) -> Option<&syn::Type> {
|
||||||
if let syn::ReturnType::Type(_, ref rtype) = self.signature.output {
|
if let syn::ReturnType::Type(_, ref rtype) = self.signature.output {
|
||||||
Some(rtype)
|
Some(flatten_type_groups(rtype))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@ -437,7 +445,7 @@ impl ExportedFn {
|
|||||||
{
|
{
|
||||||
return Err(syn::Error::new(
|
return Err(syn::Error::new(
|
||||||
self.signature.span(),
|
self.signature.span(),
|
||||||
"return_raw functions must return Result<T>",
|
"return_raw functions must return Result<T, Box<EvalAltResult>>",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -467,7 +475,7 @@ impl ExportedFn {
|
|||||||
FnSpecialAccess::Property(Property::Set(_)) if self.return_type().is_some() => {
|
FnSpecialAccess::Property(Property::Set(_)) if self.return_type().is_some() => {
|
||||||
return Err(syn::Error::new(
|
return Err(syn::Error::new(
|
||||||
self.signature.span(),
|
self.signature.span(),
|
||||||
"property setter must return no value",
|
"property setter cannot return any value",
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
// 4a. Index getters must take the subject and the accessed "index" as arguments.
|
// 4a. Index getters must take the subject and the accessed "index" as arguments.
|
||||||
@ -495,7 +503,7 @@ impl ExportedFn {
|
|||||||
FnSpecialAccess::Index(Index::Set) if self.return_type().is_some() => {
|
FnSpecialAccess::Index(Index::Set) if self.return_type().is_some() => {
|
||||||
return Err(syn::Error::new(
|
return Err(syn::Error::new(
|
||||||
self.signature.span(),
|
self.signature.span(),
|
||||||
"index setter must return no value",
|
"index setter cannot return a value",
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
@ -532,7 +540,7 @@ impl ExportedFn {
|
|||||||
dynamic_signature.ident =
|
dynamic_signature.ident =
|
||||||
syn::Ident::new("dynamic_result_fn", proc_macro2::Span::call_site());
|
syn::Ident::new("dynamic_result_fn", proc_macro2::Span::call_site());
|
||||||
dynamic_signature.output = syn::parse2::<syn::ReturnType>(quote! {
|
dynamic_signature.output = syn::parse2::<syn::ReturnType>(quote! {
|
||||||
-> Result<Dynamic, EvalBox>
|
-> Result<Dynamic, Box<EvalAltResult>>
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let arguments: Vec<syn::Ident> = dynamic_signature
|
let arguments: Vec<syn::Ident> = dynamic_signature
|
||||||
@ -555,18 +563,22 @@ impl ExportedFn {
|
|||||||
.return_type()
|
.return_type()
|
||||||
.map(|r| r.span())
|
.map(|r| r.span())
|
||||||
.unwrap_or_else(|| proc_macro2::Span::call_site());
|
.unwrap_or_else(|| proc_macro2::Span::call_site());
|
||||||
if !self.params.return_raw {
|
if self.params.return_raw {
|
||||||
quote_spanned! { return_span=>
|
quote_spanned! { return_span=>
|
||||||
type EvalBox = Box<EvalAltResult>;
|
|
||||||
pub #dynamic_signature {
|
pub #dynamic_signature {
|
||||||
Ok(Dynamic::from(super::#name(#(#arguments),*)))
|
super::#name(#(#arguments),*)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if self.return_dynamic {
|
||||||
|
quote_spanned! { return_span=>
|
||||||
|
pub #dynamic_signature {
|
||||||
|
Ok(super::#name(#(#arguments),*))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
quote_spanned! { return_span=>
|
quote_spanned! { return_span=>
|
||||||
type EvalBox = Box<EvalAltResult>;
|
|
||||||
pub #dynamic_signature {
|
pub #dynamic_signature {
|
||||||
super::#name(#(#arguments),*)
|
Ok(Dynamic::from(super::#name(#(#arguments),*)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -620,8 +632,8 @@ impl ExportedFn {
|
|||||||
let var = syn::Ident::new("arg0", proc_macro2::Span::call_site());
|
let var = syn::Ident::new("arg0", proc_macro2::Span::call_site());
|
||||||
match first_arg {
|
match first_arg {
|
||||||
syn::FnArg::Typed(pattern) => {
|
syn::FnArg::Typed(pattern) => {
|
||||||
let arg_type: &syn::Type = match flatten_type_groups(pattern.ty.as_ref()) {
|
let arg_type = match flatten_type_groups(pattern.ty.as_ref()) {
|
||||||
&syn::Type::Reference(syn::TypeReference { ref elem, .. }) => elem.as_ref(),
|
syn::Type::Reference(syn::TypeReference { ref elem, .. }) => elem.as_ref(),
|
||||||
p => p,
|
p => p,
|
||||||
};
|
};
|
||||||
let downcast_span = quote_spanned!(
|
let downcast_span = quote_spanned!(
|
||||||
@ -658,14 +670,14 @@ impl ExportedFn {
|
|||||||
let is_ref;
|
let is_ref;
|
||||||
match arg {
|
match arg {
|
||||||
syn::FnArg::Typed(pattern) => {
|
syn::FnArg::Typed(pattern) => {
|
||||||
let arg_type: &syn::Type = pattern.ty.as_ref();
|
let arg_type = pattern.ty.as_ref();
|
||||||
let downcast_span = match flatten_type_groups(pattern.ty.as_ref()) {
|
let downcast_span = match flatten_type_groups(pattern.ty.as_ref()) {
|
||||||
&syn::Type::Reference(syn::TypeReference {
|
syn::Type::Reference(syn::TypeReference {
|
||||||
mutability: None,
|
mutability: None,
|
||||||
ref elem,
|
ref elem,
|
||||||
..
|
..
|
||||||
}) => match flatten_type_groups(elem.as_ref()) {
|
}) => match flatten_type_groups(elem.as_ref()) {
|
||||||
&syn::Type::Path(ref p) if p.path == str_type_path => {
|
syn::Type::Path(ref p) if p.path == str_type_path => {
|
||||||
is_string = true;
|
is_string = true;
|
||||||
is_ref = true;
|
is_ref = true;
|
||||||
quote_spanned!(arg_type.span()=>
|
quote_spanned!(arg_type.span()=>
|
||||||
@ -673,7 +685,7 @@ impl ExportedFn {
|
|||||||
}
|
}
|
||||||
_ => panic!("internal error: why wasn't this found earlier!?"),
|
_ => panic!("internal error: why wasn't this found earlier!?"),
|
||||||
},
|
},
|
||||||
&syn::Type::Path(ref p) if p.path == string_type_path => {
|
syn::Type::Path(ref p) if p.path == string_type_path => {
|
||||||
is_string = true;
|
is_string = true;
|
||||||
is_ref = false;
|
is_ref = false;
|
||||||
quote_spanned!(arg_type.span()=>
|
quote_spanned!(arg_type.span()=>
|
||||||
@ -734,8 +746,14 @@ impl ExportedFn {
|
|||||||
.map(|r| r.span())
|
.map(|r| r.span())
|
||||||
.unwrap_or_else(|| proc_macro2::Span::call_site());
|
.unwrap_or_else(|| proc_macro2::Span::call_site());
|
||||||
let return_expr = if !self.params.return_raw {
|
let return_expr = if !self.params.return_raw {
|
||||||
quote_spanned! { return_span=>
|
if self.return_dynamic {
|
||||||
Ok(Dynamic::from(#sig_name(#(#unpack_exprs),*)))
|
quote_spanned! { return_span=>
|
||||||
|
Ok(#sig_name(#(#unpack_exprs),*))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
quote_spanned! { return_span=>
|
||||||
|
Ok(Dynamic::from(#sig_name(#(#unpack_exprs),*)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
quote_spanned! { return_span=>
|
quote_spanned! { return_span=>
|
||||||
|
@ -3,6 +3,7 @@ use std::collections::HashMap;
|
|||||||
use quote::{quote, ToTokens};
|
use quote::{quote, ToTokens};
|
||||||
|
|
||||||
use crate::attrs::ExportScope;
|
use crate::attrs::ExportScope;
|
||||||
|
use crate::function::flatten_type_groups;
|
||||||
use crate::function::{ExportedFn, FnSpecialAccess};
|
use crate::function::{ExportedFn, FnSpecialAccess};
|
||||||
use crate::module::Module;
|
use crate::module::Module;
|
||||||
|
|
||||||
@ -37,7 +38,7 @@ pub(crate) fn generate_body(
|
|||||||
if itemmod.skipped() {
|
if itemmod.skipped() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let module_name: &syn::Ident = itemmod.module_name().unwrap();
|
let module_name = itemmod.module_name().unwrap();
|
||||||
let exported_name: syn::LitStr = if let Some(name) = itemmod.exported_name() {
|
let exported_name: syn::LitStr = if let Some(name) = itemmod.exported_name() {
|
||||||
syn::LitStr::new(&name, proc_macro2::Span::call_site())
|
syn::LitStr::new(&name, proc_macro2::Span::call_site())
|
||||||
} else {
|
} else {
|
||||||
@ -174,14 +175,6 @@ pub(crate) fn generate_body(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn flatten_type_groups(ty: &syn::Type) -> &syn::Type {
|
|
||||||
match ty {
|
|
||||||
syn::Type::Group(syn::TypeGroup { ref elem, .. })
|
|
||||||
| syn::Type::Paren(syn::TypeParen { ref elem, .. }) => flatten_type_groups(elem.as_ref()),
|
|
||||||
_ => ty,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn check_rename_collisions(fns: &Vec<ExportedFn>) -> Result<(), syn::Error> {
|
pub(crate) fn check_rename_collisions(fns: &Vec<ExportedFn>) -> Result<(), syn::Error> {
|
||||||
fn make_key(name: impl ToString, itemfn: &ExportedFn) -> String {
|
fn make_key(name: impl ToString, itemfn: &ExportedFn) -> String {
|
||||||
itemfn
|
itemfn
|
||||||
|
@ -88,7 +88,10 @@ mod function_tests {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let err = syn::parse2::<ExportedFn>(input_tokens).unwrap_err();
|
let err = syn::parse2::<ExportedFn>(input_tokens).unwrap_err();
|
||||||
assert_eq!(format!("{}", err), "cannot return a reference to Rhai");
|
assert_eq!(
|
||||||
|
format!("{}", err),
|
||||||
|
"Rhai functions cannot return references"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -98,7 +101,7 @@ mod function_tests {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let err = syn::parse2::<ExportedFn>(input_tokens).unwrap_err();
|
let err = syn::parse2::<ExportedFn>(input_tokens).unwrap_err();
|
||||||
assert_eq!(format!("{}", err), "cannot return a pointer to Rhai");
|
assert_eq!(format!("{}", err), "Rhai functions cannot return pointers");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -295,8 +298,7 @@ mod generate_tests {
|
|||||||
pub fn token_input_types() -> Box<[TypeId]> {
|
pub fn token_input_types() -> Box<[TypeId]> {
|
||||||
Token().input_types()
|
Token().input_types()
|
||||||
}
|
}
|
||||||
type EvalBox = Box<EvalAltResult>;
|
pub fn dynamic_result_fn() -> Result<Dynamic, Box<EvalAltResult> > {
|
||||||
pub fn dynamic_result_fn() -> Result<Dynamic, EvalBox> {
|
|
||||||
Ok(Dynamic::from(super::do_nothing()))
|
Ok(Dynamic::from(super::do_nothing()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -340,8 +342,7 @@ mod generate_tests {
|
|||||||
pub fn token_input_types() -> Box<[TypeId]> {
|
pub fn token_input_types() -> Box<[TypeId]> {
|
||||||
Token().input_types()
|
Token().input_types()
|
||||||
}
|
}
|
||||||
type EvalBox = Box<EvalAltResult>;
|
pub fn dynamic_result_fn(x: usize) -> Result<Dynamic, Box<EvalAltResult> > {
|
||||||
pub fn dynamic_result_fn(x: usize) -> Result<Dynamic, EvalBox> {
|
|
||||||
Ok(Dynamic::from(super::do_something(x)))
|
Ok(Dynamic::from(super::do_something(x)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -351,6 +352,51 @@ mod generate_tests {
|
|||||||
assert_streams_eq(item_fn.generate(), expected_tokens);
|
assert_streams_eq(item_fn.generate(), expected_tokens);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn return_dynamic() {
|
||||||
|
let input_tokens: TokenStream = quote! {
|
||||||
|
pub fn return_dynamic() -> (((rhai::Dynamic))) {
|
||||||
|
().into()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let expected_tokens = quote! {
|
||||||
|
#[allow(unused)]
|
||||||
|
pub mod rhai_fn_return_dynamic {
|
||||||
|
use super::*;
|
||||||
|
struct Token();
|
||||||
|
impl PluginFunction for Token {
|
||||||
|
fn call(&self,
|
||||||
|
args: &mut [&mut Dynamic]
|
||||||
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
|
debug_assert_eq!(args.len(), 0usize,
|
||||||
|
"wrong arg count: {} != {}", args.len(), 0usize);
|
||||||
|
Ok(return_dynamic())
|
||||||
|
}
|
||||||
|
|
||||||
|
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![].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() -> Result<Dynamic, Box<EvalAltResult> > {
|
||||||
|
Ok(super::return_dynamic())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let item_fn = syn::parse2::<ExportedFn>(input_tokens).unwrap();
|
||||||
|
assert_streams_eq(item_fn.generate(), expected_tokens);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn one_arg_usize_fn_impl() {
|
fn one_arg_usize_fn_impl() {
|
||||||
let input_tokens: TokenStream = quote! {
|
let input_tokens: TokenStream = quote! {
|
||||||
@ -417,8 +463,7 @@ mod generate_tests {
|
|||||||
pub fn token_input_types() -> Box<[TypeId]> {
|
pub fn token_input_types() -> Box<[TypeId]> {
|
||||||
Token().input_types()
|
Token().input_types()
|
||||||
}
|
}
|
||||||
type EvalBox = Box<EvalAltResult>;
|
pub fn dynamic_result_fn(x: usize, y: usize) -> Result<Dynamic, Box<EvalAltResult> > {
|
||||||
pub fn dynamic_result_fn(x: usize, y: usize) -> Result<Dynamic, EvalBox> {
|
|
||||||
Ok(Dynamic::from(super::add_together(x, y)))
|
Ok(Dynamic::from(super::add_together(x, y)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -464,8 +509,7 @@ mod generate_tests {
|
|||||||
pub fn token_input_types() -> Box<[TypeId]> {
|
pub fn token_input_types() -> Box<[TypeId]> {
|
||||||
Token().input_types()
|
Token().input_types()
|
||||||
}
|
}
|
||||||
type EvalBox = Box<EvalAltResult>;
|
pub fn dynamic_result_fn(x: &mut usize, y: usize) -> Result<Dynamic, Box<EvalAltResult> > {
|
||||||
pub fn dynamic_result_fn(x: &mut usize, y: usize) -> Result<Dynamic, EvalBox> {
|
|
||||||
Ok(Dynamic::from(super::increment(x, y)))
|
Ok(Dynamic::from(super::increment(x, y)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -510,8 +554,7 @@ mod generate_tests {
|
|||||||
pub fn token_input_types() -> Box<[TypeId]> {
|
pub fn token_input_types() -> Box<[TypeId]> {
|
||||||
Token().input_types()
|
Token().input_types()
|
||||||
}
|
}
|
||||||
type EvalBox = Box<EvalAltResult>;
|
pub fn dynamic_result_fn(message: &str) -> Result<Dynamic, Box<EvalAltResult> > {
|
||||||
pub fn dynamic_result_fn(message: &str) -> Result<Dynamic, EvalBox> {
|
|
||||||
Ok(Dynamic::from(super::special_print(message)))
|
Ok(Dynamic::from(super::special_print(message)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
error: return_raw functions must return Result<T>
|
error: return_raw functions must return Result<T, Box<EvalAltResult>>
|
||||||
--> $DIR/export_fn_raw_noreturn.rs:10:5
|
--> $DIR/export_fn_raw_noreturn.rs:10:5
|
||||||
|
|
|
|
||||||
10 | pub fn test_fn(input: &mut Point) {
|
10 | pub fn test_fn(input: &mut Point) {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
error: return_raw functions must return Result<T>
|
error: return_raw functions must return Result<T, Box<EvalAltResult>>
|
||||||
--> $DIR/export_mod_raw_noreturn.rs:12:5
|
--> $DIR/export_mod_raw_noreturn.rs:12:5
|
||||||
|
|
|
|
||||||
12 | pub fn test_fn(input: &mut Point) {
|
12 | pub fn test_fn(input: &mut Point) {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
error: cannot return a reference to Rhai
|
error: Rhai functions cannot return references
|
||||||
--> $DIR/return_mut_ref.rs:12:38
|
--> $DIR/return_mut_ref.rs:12:38
|
||||||
|
|
|
|
||||||
12 | pub fn test_fn(input: &mut Clonable) -> &mut bool {
|
12 | pub fn test_fn(input: &mut Clonable) -> &mut bool {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
error: cannot return a pointer to Rhai
|
error: Rhai functions cannot return pointers
|
||||||
--> $DIR/return_pointer.rs:12:33
|
--> $DIR/return_pointer.rs:12:33
|
||||||
|
|
|
|
||||||
12 | pub fn test_fn(input: Clonable) -> *const str {
|
12 | pub fn test_fn(input: Clonable) -> *const str {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
error: cannot return a reference to Rhai
|
error: Rhai functions cannot return references
|
||||||
--> $DIR/return_shared_ref.rs:12:33
|
--> $DIR/return_shared_ref.rs:12:33
|
||||||
|
|
|
|
||||||
12 | pub fn test_fn(input: Clonable) -> &'static str {
|
12 | pub fn test_fn(input: Clonable) -> &'static str {
|
||||||
|
@ -1,28 +0,0 @@
|
|||||||
use rhai::plugin::*;
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct Point {
|
|
||||||
x: f32,
|
|
||||||
y: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[export_module]
|
|
||||||
pub mod test_module {
|
|
||||||
pub use super::Point;
|
|
||||||
#[rhai_fn(name = "big$caching")]
|
|
||||||
pub fn test_fn(input: Point) -> bool {
|
|
||||||
input.x > input.y
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let n = Point {
|
|
||||||
x: 0.0,
|
|
||||||
y: 10.0,
|
|
||||||
};
|
|
||||||
if test_module::test_fn(n) {
|
|
||||||
println!("yes");
|
|
||||||
} else {
|
|
||||||
println!("no");
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
error: Rhai function names may not contain dollar sign
|
|
||||||
--> $DIR/rhai_fn_rename_dollar_sign.rs:12:22
|
|
||||||
|
|
|
||||||
12 | #[rhai_fn(name = "big$caching")]
|
|
||||||
| ^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
error[E0433]: failed to resolve: use of undeclared crate or module `test_module`
|
|
||||||
--> $DIR/rhai_fn_rename_dollar_sign.rs:23:8
|
|
||||||
|
|
|
||||||
23 | if test_module::test_fn(n) {
|
|
||||||
| ^^^^^^^^^^^ use of undeclared crate or module `test_module`
|
|
@ -1,28 +0,0 @@
|
|||||||
use rhai::plugin::*;
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct Point {
|
|
||||||
x: f32,
|
|
||||||
y: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[export_module]
|
|
||||||
pub mod test_module {
|
|
||||||
pub use super::Point;
|
|
||||||
#[rhai_fn(name = "foo.bar")]
|
|
||||||
pub fn test_fn(input: Point) -> bool {
|
|
||||||
input.x > input.y
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let n = Point {
|
|
||||||
x: 0.0,
|
|
||||||
y: 10.0,
|
|
||||||
};
|
|
||||||
if test_module::test_fn(n) {
|
|
||||||
println!("yes");
|
|
||||||
} else {
|
|
||||||
println!("no");
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
error: Rhai function names may not contain dot
|
|
||||||
--> $DIR/rhai_fn_rename_dot.rs:12:22
|
|
||||||
|
|
|
||||||
12 | #[rhai_fn(name = "foo.bar")]
|
|
||||||
| ^^^^^^^^^
|
|
||||||
|
|
||||||
error[E0433]: failed to resolve: use of undeclared crate or module `test_module`
|
|
||||||
--> $DIR/rhai_fn_rename_dot.rs:23:8
|
|
||||||
|
|
|
||||||
23 | if test_module::test_fn(n) {
|
|
||||||
| ^^^^^^^^^^^ use of undeclared crate or module `test_module`
|
|
@ -1,4 +1,4 @@
|
|||||||
error: property setter must return no value
|
error: property setter cannot return any value
|
||||||
--> $DIR/rhai_fn_setter_return.rs:13:9
|
--> $DIR/rhai_fn_setter_return.rs:13:9
|
||||||
|
|
|
|
||||||
13 | pub fn test_fn(input: &mut Point, value: f32) -> bool {
|
13 | pub fn test_fn(input: &mut Point, value: f32) -> bool {
|
||||||
|
@ -60,6 +60,7 @@ Symbols and Patterns
|
|||||||
| `--` | decrement | _reserved_ |
|
| `--` | decrement | _reserved_ |
|
||||||
| `..` | range | _reserved_ |
|
| `..` | range | _reserved_ |
|
||||||
| `...` | range | _reserved_ |
|
| `...` | range | _reserved_ |
|
||||||
|
| `**` | exponentiation | _reserved_ |
|
||||||
| `#` | hash | _reserved_ |
|
| `#` | hash | _reserved_ |
|
||||||
| `@` | at | _reserved_ |
|
| `@` | at | _reserved_ |
|
||||||
| `$` | dollar | _reserved_ |
|
| `$` | dollar | _reserved_ |
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"version": "0.20.0",
|
"version": "0.19.1",
|
||||||
"repoHome": "https://github.com/jonathandturner/rhai/blob/master",
|
"repoHome": "https://github.com/jonathandturner/rhai/blob/master",
|
||||||
"repoTree": "https://github.com/jonathandturner/rhai/tree/master",
|
"repoTree": "https://github.com/jonathandturner/rhai/tree/master",
|
||||||
"rootUrl": "",
|
"rootUrl": "",
|
||||||
|
@ -84,7 +84,11 @@ Operator Precedence
|
|||||||
|
|
||||||
All operators in Rhai has a _precedence_ indicating how tightly they bind.
|
All operators in Rhai has a _precedence_ indicating how tightly they bind.
|
||||||
|
|
||||||
The following _precedence table_ show the built-in precedence of standard Rhai operators:
|
A higher precedence binds more tightly than a lower precedence, so `*` and `/` binds before `+` and `-` etc.
|
||||||
|
|
||||||
|
When registering a custom operator, the operator's precedence must also be provided.
|
||||||
|
|
||||||
|
The following _precedence table_ shows the built-in precedence of standard Rhai operators:
|
||||||
|
|
||||||
| Category | Operators | Precedence (0-255) |
|
| Category | Operators | Precedence (0-255) |
|
||||||
| ------------------- | :-------------------------------------------------------------------------------------: | :----------------: |
|
| ------------------- | :-------------------------------------------------------------------------------------: | :----------------: |
|
||||||
@ -92,15 +96,11 @@ The following _precedence table_ show the built-in precedence of standard Rhai o
|
|||||||
| Logic and bit masks | <code>\|\|</code>, <code>\|</code>, `^` | 30 |
|
| Logic and bit masks | <code>\|\|</code>, <code>\|</code>, `^` | 30 |
|
||||||
| Logic and bit masks | `&`, `&&` | 60 |
|
| Logic and bit masks | `&`, `&&` | 60 |
|
||||||
| Comparisons | `==`, `!=` | 90 |
|
| Comparisons | `==`, `!=` | 90 |
|
||||||
| Comparisons | `>`, `>=`, `<`, `<=` | 110 |
|
| | `in` | 110 |
|
||||||
| | `in` | 130 |
|
| Comparisons | `>`, `>=`, `<`, `<=` | 130 |
|
||||||
| Arithmetic | `+`, `-` | 150 |
|
| Arithmetic | `+`, `-` | 150 |
|
||||||
| Arithmetic | `*`, `/` | 180 |
|
| Arithmetic | `*`, `/`, `%` | 180 |
|
||||||
| Arithmetic | `~`, `%` | 190 |
|
| Arithmetic | `~` | 190 |
|
||||||
| Bit-shifts | `<<`, `>>` | 210 |
|
| Bit-shifts | `<<`, `>>` | 210 |
|
||||||
| Object | `.` _(binds to right)_ | 240 |
|
| Object | `.` _(binds to right)_ | 240 |
|
||||||
| _Others_ | | 0 |
|
| Unary operators | unary `+`, `-`, `!` _(binds to right)_ | 255 |
|
||||||
|
|
||||||
A higher precedence binds more tightly than a lower precedence, so `*` and `/` binds before `+` and `-` etc.
|
|
||||||
|
|
||||||
When registering a custom operator, the operator's precedence must also be provided.
|
|
||||||
|
@ -114,7 +114,8 @@ Any custom syntax must include an _implementation_ of it.
|
|||||||
|
|
||||||
The function signature of an implementation is:
|
The function signature of an implementation is:
|
||||||
|
|
||||||
> `Fn(scope: &mut Scope, context: &mut EvalContext, inputs: &[Expression]) -> Result<Dynamic, Box<EvalAltResult>>`
|
> `Fn(scope: &mut Scope, context: &mut EvalContext, inputs: &[Expression])`
|
||||||
|
> `-> Result<Dynamic, Box<EvalAltResult>>`
|
||||||
|
|
||||||
where:
|
where:
|
||||||
|
|
||||||
|
@ -62,7 +62,8 @@ Function Signature
|
|||||||
|
|
||||||
The function signature passed to `Engine::on_var` takes the following form:
|
The function signature passed to `Engine::on_var` takes the following form:
|
||||||
|
|
||||||
> `Fn(name: &str, index: usize, scope: &Scope, context: &EvalContext) -> Result<Option<Dynamic>, Box<EvalAltResult>> + 'static`
|
> `Fn(name: &str, index: usize, scope: &Scope, context: &EvalContext)`
|
||||||
|
> `-> Result<Option<Dynamic>, Box<EvalAltResult>> + 'static`
|
||||||
|
|
||||||
where:
|
where:
|
||||||
|
|
||||||
|
@ -30,30 +30,33 @@ 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 |
|
||||||
| `+=` operator | 1) array<br/>2) array to append | concatenates the second array to the end of the first |
|
| `+=` operator | 1) array<br/>2) array to append | concatenates the second array to the end of the first |
|
||||||
| `+` operator | 1) first array<br/>2) second array | concatenates the first array with the second |
|
| `+` operator | 1) first array<br/>2) second array | concatenates the first array with the second |
|
||||||
| `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) |
|
||||||
| `remove` | index | removes an element at a particular index and returns it ([`()`] if the index is not valid) |
|
| `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 |
|
||||||
| `reverse` | _none_ | reverses the array |
|
| `remove` | index | removes an element at a particular index and returns it ([`()`] if the index is not valid) |
|
||||||
| `len` method and property | _none_ | returns the number of elements |
|
| `reverse` | _none_ | reverses the array |
|
||||||
| `pad` | 1) target length<br/>2) element to pad | pads the array with an element to at least a specified length |
|
| `len` method and property | _none_ | returns the number of elements |
|
||||||
| `clear` | _none_ | empties the array |
|
| `pad` | 1) target length<br/>2) element to pad | pads the array with an element to at least a specified length |
|
||||||
| `truncate` | target length | cuts off the array at exactly a specified length (discarding all subsequent elements) |
|
| `clear` | _none_ | empties the array |
|
||||||
| `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) |
|
| `truncate` | target length | cuts off the array at exactly a specified length (discarding all subsequent elements) |
|
||||||
| `filter` | [function pointer] to predicate (can be 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: offset index (optional) |
|
| `chop` | target length | cuts off the head of the array, leaving the tail at exactly a specified length |
|
||||||
| `map` | [function pointer] to conversion function (can be 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: offset index (optional) |
|
| `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) |
|
||||||
| `reduce` | [function pointer] to accumulator function (can be a [closure]) | constructs a new array with all items accumulated by the accumulator function:<br/>1st parameter: accumulated value ([`()`] initially),<br/>2nd parameter: array item,<br/>3rd parameter: offset index (optional) |
|
| `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 |
|
||||||
| `reduce_rev` | [function pointer] to accumulator function (can be a [closure]) | constructs a new array with all items (in reverse order) accumulated by the accumulator function:<br/>1st parameter: accumulated value ([`()`] initially),<br/>2nd parameter: array item,<br/>3rd parameter: offset index (optional) |
|
| `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 |
|
||||||
| `some` | [function pointer] to predicate (can be a [closure]) | returns `true` if any item returns `true` when called with the predicate function:<br/>1st parameter: array item,<br/>2nd parameter: offset index (optional) |
|
| `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 |
|
||||||
| `all` | [function pointer] to predicate (can be a [closure]) | returns `true` if all item returns `true` when called with the predicate function:<br/>1st parameter: array item,<br/>2nd parameter: offset index (optional) |
|
| `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 |
|
||||||
| `sort` | [function pointer] to a comparison function (can be a [closure]) | sorts the array with a comparison function:<br/>1st parameter: first item,<br/>2nd parameter: second item |
|
| `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 |
|
||||||
|
| `none` | [function pointer] to predicate (usually a [closure]) | returns `true` if no 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
|
||||||
@ -71,12 +74,13 @@ Examples
|
|||||||
--------
|
--------
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
let y = [2, 3]; // array literal with 2 elements
|
let y = [2, 3]; // y == [2, 3]
|
||||||
|
|
||||||
let y = [2, 3,]; // trailing comma is OK
|
let y = [2, 3,]; // y == [2, 3]
|
||||||
|
|
||||||
y.insert(0, 1); // insert element at the beginning
|
y.insert(0, 1); // y == [1, 2, 3]
|
||||||
y.insert(999, 4); // insert element at the end
|
|
||||||
|
y.insert(999, 4); // y == [1, 2, 3, 4]
|
||||||
|
|
||||||
y.len == 4;
|
y.len == 4;
|
||||||
|
|
||||||
@ -89,103 +93,111 @@ y[3] == 4;
|
|||||||
(42 in y) == false; // 'in' uses the '==' operator (which users can override)
|
(42 in y) == false; // 'in' uses the '==' operator (which users can override)
|
||||||
// to check if the target item exists in the array
|
// to check if the target item exists in the array
|
||||||
|
|
||||||
y[1] = 42; // array elements can be reassigned
|
y[1] = 42; // y == [1, 42, 3, 4]
|
||||||
|
|
||||||
(42 in y) == true;
|
(42 in y) == true;
|
||||||
|
|
||||||
y.remove(2) == 3; // remove element
|
y.remove(2) == 3; // y == [1, 42, 4]
|
||||||
|
|
||||||
y.len == 3;
|
y.len == 3;
|
||||||
|
|
||||||
y[2] == 4; // elements after the removed element are shifted
|
y[2] == 4; // elements after the removed element are shifted
|
||||||
|
|
||||||
ts.list = y; // arrays can be assigned completely (by value copy)
|
ts.list = y; // arrays can be assigned completely (by value copy)
|
||||||
let foo = ts.list[1];
|
|
||||||
foo == 42;
|
|
||||||
|
|
||||||
let foo = [1, 2, 3][0];
|
ts.list[1] == 42;
|
||||||
foo == 1;
|
|
||||||
|
[1, 2, 3][0] == 1; // indexing on array literal
|
||||||
|
|
||||||
fn abc() {
|
fn abc() {
|
||||||
[42, 43, 44] // a function returning an array
|
[42, 43, 44] // a function returning an array
|
||||||
}
|
}
|
||||||
|
|
||||||
let foo = abc()[0];
|
abc()[0] == 42;
|
||||||
foo == 42;
|
|
||||||
|
|
||||||
let foo = y[0];
|
y.push(4); // y == [1, 42, 4, 4]
|
||||||
foo == 1;
|
|
||||||
|
|
||||||
y.push(4); // 4 elements
|
y += 5; // y == [1, 42, 4, 4, 5]
|
||||||
y += 5; // 5 elements
|
|
||||||
|
|
||||||
y.len == 5;
|
y.len == 5;
|
||||||
|
|
||||||
let first = y.shift(); // remove the first element, 4 elements remaining
|
y.shift() == 1; // y == [42, 4, 4, 5]
|
||||||
first == 1;
|
|
||||||
|
|
||||||
let last = y.pop(); // remove the last element, 3 elements remaining
|
y.chop(3); // y == [4, 4, 5]
|
||||||
last == 5;
|
|
||||||
|
|
||||||
y.len == 3;
|
y.len == 3;
|
||||||
|
|
||||||
|
y.pop() == 5; // y == [4, 4]
|
||||||
|
|
||||||
|
y.len == 2;
|
||||||
|
|
||||||
for item in y { // arrays can be iterated with a 'for' statement
|
for item in y { // arrays can be iterated with a 'for' statement
|
||||||
print(item);
|
print(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
y.pad(10, "hello"); // pad the array up to 10 elements
|
y.pad(6, "hello"); // y == [4, 4, "hello", "hello", "hello", "hello"]
|
||||||
|
|
||||||
y.len == 10;
|
y.len == 6;
|
||||||
|
|
||||||
y.truncate(5); // truncate the array to 5 elements
|
y.truncate(4); // y == [4, 4, "hello", "hello"]
|
||||||
|
|
||||||
y.len == 5;
|
y.len == 4;
|
||||||
|
|
||||||
y.clear(); // empty the array
|
y.clear(); // y == []
|
||||||
|
|
||||||
y.len == 0;
|
y.len == 0;
|
||||||
|
|
||||||
let a = [42, 123, 99];
|
let a = [42, 123, 99];
|
||||||
|
|
||||||
a.map(|v| v + 1); // [43, 124, 100]
|
a.map(|v| v + 1); // returns [43, 124, 100]
|
||||||
|
|
||||||
a.map(|v, i| v + i); // [42, 124, 101]
|
a.map(|v, i| v + i); // returns [42, 124, 101]
|
||||||
|
|
||||||
a.filter(|v| v > 50); // [123, 99]
|
a.filter(|v| v > 50); // returns [123, 99]
|
||||||
|
|
||||||
a.filter(|v, i| i == 1); // [123]
|
a.filter(|v, i| i == 1); // returns [123]
|
||||||
|
|
||||||
a.reduce(|sum, v| {
|
// Use a closure to provide the initial value
|
||||||
// Detect the initial value of '()'
|
a.reduce(|sum, v| sum + v, || 0) == 264;
|
||||||
if sum.type_of() == "()" { v } else { sum + v }
|
|
||||||
|
// Detect the initial value of '()'
|
||||||
|
a.reduce(
|
||||||
|
|sum, v| if sum.type_of() == "()" { v } else { sum + v }
|
||||||
) == 264;
|
) == 264;
|
||||||
|
|
||||||
a.reduce(|sum, v, i| {
|
// Detect the initial value via index
|
||||||
|
a.reduce(|sum, v, i|
|
||||||
if i == 0 { v } else { sum + v }
|
if i == 0 { v } else { sum + v }
|
||||||
) == 264;
|
) == 264;
|
||||||
|
|
||||||
a.reduce_rev(|sum, v| {
|
// Use a closure to provide the initial value
|
||||||
// Detect the initial value of '()'
|
a.reduce_rev(|sum, v| sum + v, || 0) == 264;
|
||||||
if sum.type_of() == "()" { v } else { sum + v }
|
|
||||||
|
// Detect the initial value of '()'
|
||||||
|
a.reduce_rev(
|
||||||
|
|sum, v| if sum.type_of() == "()" { v } else { sum + v }
|
||||||
) == 264;
|
) == 264;
|
||||||
|
|
||||||
a.reduce_rev(|sum, v, i| {
|
// Detect the initial value via index
|
||||||
if i == 0 { v } else { sum + v }
|
a.reduce_rev(|sum, v, i|
|
||||||
|
if i == 2 { v } else { sum + v }
|
||||||
) == 264;
|
) == 264;
|
||||||
|
|
||||||
a.some(|v| v > 50) == true;
|
a.some(|v| v > 50); // returns true
|
||||||
|
|
||||||
a.some(|v, i| v < i) == false;
|
a.some(|v, i| v < i); // returns false
|
||||||
|
|
||||||
a.all(|v| v > 50) == false;
|
a.none(|v| v != 0); // returns false
|
||||||
|
|
||||||
a.all(|v, i| v > i) == true;
|
a.none(|v, i| v == i); // returns true
|
||||||
|
|
||||||
a.splice(1, 1, [1, 3, 2]);
|
a.all(|v| v > 50); // returns false
|
||||||
|
|
||||||
a == [42, 1, 3, 2, 99];
|
a.all(|v, i| v > i); // returns true
|
||||||
|
|
||||||
a.sort(|x, y| x - y);
|
a.splice(1, 1, [1, 3, 2]); // a == [42, 1, 3, 2, 99]
|
||||||
|
|
||||||
a == [1, 2, 3, 42, 99];
|
a.extract(1, 3); // returns [1, 3, 2]
|
||||||
|
|
||||||
|
a.sort(|x, y| x - y); // a == [1, 2, 3, 42, 99]
|
||||||
```
|
```
|
||||||
|
@ -13,7 +13,7 @@ Built-in methods
|
|||||||
----------------
|
----------------
|
||||||
|
|
||||||
The following standard methods (mostly defined in the [`BasicFnPackage`][packages] but excluded if
|
The following standard methods (mostly defined in the [`BasicFnPackage`][packages] but excluded if
|
||||||
using a [raw `Engine`]) operate on [strings]:
|
using a [raw `Engine`]) operate on function pointers:
|
||||||
|
|
||||||
| Function | Parameter(s) | Description |
|
| Function | Parameter(s) | Description |
|
||||||
| -------------------------- | ------------ | ---------------------------------------------------------------------------- |
|
| -------------------------- | ------------ | ---------------------------------------------------------------------------- |
|
||||||
@ -64,7 +64,7 @@ Global Namespace Only
|
|||||||
|
|
||||||
Because of their dynamic nature, function pointers cannot refer to functions in [`import`]-ed [modules].
|
Because of their dynamic nature, function pointers cannot refer to functions in [`import`]-ed [modules].
|
||||||
They can only refer to functions within the global [namespace][function namespace].
|
They can only refer to functions within the global [namespace][function namespace].
|
||||||
See [function namespaces] for more details.
|
See _[Function Namespaces]_ for more details.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
import "foo" as f; // assume there is 'f::do_work()'
|
import "foo" as f; // assume there is 'f::do_work()'
|
||||||
@ -135,13 +135,13 @@ map[func].call(42);
|
|||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
Binding the `this` Pointer
|
Bind the `this` Pointer
|
||||||
-------------------------
|
----------------------
|
||||||
|
|
||||||
When `call` is called as a _method_ but not on a `FnPtr` value, it is possible to dynamically dispatch
|
When `call` is called as a _method_ but not on a function pointer, it is possible to dynamically dispatch
|
||||||
to a function call while binding the object in the method call to the `this` pointer of the function.
|
to a function call while binding the object in the method call to the `this` pointer of the function.
|
||||||
|
|
||||||
To achieve this, pass the `FnPtr` value as the _first_ argument to `call`:
|
To achieve this, pass the function pointer as the _first_ argument to `call`:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
fn add(x) { // define function which uses 'this'
|
fn add(x) { // define function which uses 'this'
|
||||||
@ -167,3 +167,52 @@ Beware that this only works for _method-call_ style. Normal function-call style
|
|||||||
the `this` pointer (for syntactic reasons).
|
the `this` pointer (for syntactic reasons).
|
||||||
|
|
||||||
Therefore, obviously, binding the `this` pointer is unsupported under [`no_object`].
|
Therefore, obviously, binding the `this` pointer is unsupported under [`no_object`].
|
||||||
|
|
||||||
|
|
||||||
|
Call a Function Pointer in Rust
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
It is completely normal to register a Rust function with an [`Engine`] that takes parameters
|
||||||
|
whose types are function pointers. The Rust type in question is `rhai::FnPtr`.
|
||||||
|
|
||||||
|
A function pointer in Rhai is essentially syntactic sugar wrapping the _name_ of a function
|
||||||
|
to call in script. Therefore, the script's [`AST`] is required to call a function pointer,
|
||||||
|
as well as the entire _execution context_ that the script is running in.
|
||||||
|
|
||||||
|
For a rust function taking a function pointer as parameter, the [Low-Level API](../rust/register-raw.md)
|
||||||
|
must be used 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
|
||||||
|
current scripting [`Engine`], collection of script-defined functions, the `this` pointer,
|
||||||
|
and other necessary arguments.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use rhai::{Engine, Module, Dynamic, FnPtr};
|
||||||
|
|
||||||
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
|
// Define Rust function in required low-level API signature
|
||||||
|
fn call_fn_ptr_with_value(engine: &Engine, lib: &Module, args: &mut [&mut Dynamic])
|
||||||
|
-> Result<Dynamic, Box<EvalAltResult>>
|
||||||
|
{
|
||||||
|
// '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 value = args[2].clone(); // 3rd argument - function argument
|
||||||
|
let this_ptr = args.get_mut(0).unwrap(); // 1st argument - this pointer
|
||||||
|
|
||||||
|
// Use 'FnPtr::call_dynamic' to call the function pointer.
|
||||||
|
// Beware, private script-defined functions will not be found.
|
||||||
|
fp.call_dynamic(engine, lib, Some(this_ptr), [value])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register a Rust function using the low-level API
|
||||||
|
engine.register_raw_fn("super_call",
|
||||||
|
&[ // parameter types
|
||||||
|
std::any::TypeId::of::<i64>(),
|
||||||
|
std::any::TypeId::of::<FnPtr>(),
|
||||||
|
std::any::TypeId::of::<i64>()
|
||||||
|
],
|
||||||
|
call_fn_ptr_with_value
|
||||||
|
);
|
||||||
|
```
|
||||||
|
@ -79,7 +79,7 @@ for x in range(0, 50, 3) { // step by 3
|
|||||||
Iterate Through Object Maps
|
Iterate Through Object Maps
|
||||||
--------------------------
|
--------------------------
|
||||||
|
|
||||||
Two functions, `keys` and `values`, return [arrays] containing cloned _copies_
|
Two methods, `keys` and `values`, return [arrays] containing cloned _copies_
|
||||||
of all property names and values of an [object map], respectively.
|
of all property names and values of an [object map], respectively.
|
||||||
|
|
||||||
These [arrays] can be iterated.
|
These [arrays] can be iterated.
|
||||||
@ -88,7 +88,7 @@ These [arrays] can be iterated.
|
|||||||
let map = #{a:1, b:3, c:5, d:7, e:9};
|
let map = #{a:1, b:3, c:5, d:7, e:9};
|
||||||
|
|
||||||
// Property names are returned in unsorted, random order
|
// Property names are returned in unsorted, random order
|
||||||
for x in keys(map) {
|
for x in map.keys() {
|
||||||
if x > 10 { continue; } // skip to the next iteration
|
if x > 10 { continue; } // skip to the next iteration
|
||||||
|
|
||||||
print(x);
|
print(x);
|
||||||
@ -97,7 +97,7 @@ for x in keys(map) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Property values are returned in unsorted, random order
|
// Property values are returned in unsorted, random order
|
||||||
for val in values(map) {
|
for val in map.values() {
|
||||||
print(val);
|
print(val);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
@ -126,11 +126,11 @@ y.remove("a") == 1; // remove property
|
|||||||
y.len() == 2;
|
y.len() == 2;
|
||||||
y.has("a") == false;
|
y.has("a") == false;
|
||||||
|
|
||||||
for name in keys(y) { // get an array of all the property names via the 'keys' function
|
for name in y.keys() { // get an array of all the property names via 'keys'
|
||||||
print(name);
|
print(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
for val in values(y) { // get an array of all the property values via the 'values' function
|
for val in y.values() { // get an array of all the property values via 'values'
|
||||||
print(val);
|
print(val);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -138,3 +138,30 @@ y.clear(); // empty the object map
|
|||||||
|
|
||||||
y.len() == 0;
|
y.len() == 0;
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
No Support for Property Getters
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
In order not to affect the speed of accessing properties in an object map, new property
|
||||||
|
[getters][getters/setters] cannot be registered because they conflict with the syntax of
|
||||||
|
property access.
|
||||||
|
|
||||||
|
A property [getter][getters/setters] function registered via `Engine::register_get`, for example,
|
||||||
|
for a `Map` will never be found - instead, the property will be looked up in the object map.
|
||||||
|
|
||||||
|
Therefore, _method-call_ notation must be used for built-in properties:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
map.len // access property 'len', returns '()' if not found
|
||||||
|
|
||||||
|
map.len() // returns the number of properties
|
||||||
|
|
||||||
|
map.keys // access property 'keys', returns '()' if not found
|
||||||
|
|
||||||
|
map.keys() // returns array of all property names
|
||||||
|
|
||||||
|
map.values // access property 'values', returns '()' if not found
|
||||||
|
|
||||||
|
map.values() // returns array of all property values
|
||||||
|
```
|
||||||
|
@ -6,20 +6,20 @@ Built-in String Functions
|
|||||||
The following standard methods (mostly defined in the [`MoreStringPackage`][packages] but excluded if
|
The following standard methods (mostly defined in the [`MoreStringPackage`][packages] but excluded if
|
||||||
using a [raw `Engine`]) operate on [strings]:
|
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) character to pad<br/>2) target length | pads the string with an character 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 |
|
||||||
| `contains` | character/sub-string to search for | checks if a certain character or sub-string occurs in the string |
|
| `contains` | character/sub-string to search for | checks if a certain character or sub-string occurs in the string |
|
||||||
| `index_of` | 1) character/sub-string to search for<br/>2) start index _(optional)_ | returns the index that a certain character or sub-string occurs in the string, or -1 if not found |
|
| `index_of` | 1) character/sub-string to search for<br/>2) _(optional)_ start index | returns the index that a certain character or sub-string occurs in the string, or -1 if not found |
|
||||||
| `sub_string` | 1) start index<br/>2) length _(optional)_ | extracts a sub-string (to the end of the string if length is not specified) |
|
| `sub_string` | 1) start index<br/>2) _(optional)_ number of characters to extract, none if < 0 | extracts a sub-string (to the end of the string if length is not specified) |
|
||||||
| `split` | delimiter character/string | splits the string by the specified delimiter, returning an [array] of string segments; not available under [`no_index`] |
|
| `split` | delimiter character/string | splits the string by the specified delimiter, returning an [array] of string segments; not available under [`no_index`] |
|
||||||
| `crop` | 1) start index<br/>2) length _(optional)_ | retains only a portion of the string (to the end of the string if length is not specified) |
|
| `crop` | 1) start index<br/>2) _(optional)_ number of characters to retain, none if < 0 | retains only a portion of the string |
|
||||||
| `replace` | 1) target character/sub-string<br/>2) replacement character/string | replaces a sub-string with another |
|
| `replace` | 1) target character/sub-string<br/>2) replacement character/string | replaces a sub-string with another |
|
||||||
| `trim` | _none_ | trims the string of whitespace at the beginning and end |
|
| `trim` | _none_ | trims the string of whitespace at the beginning and end |
|
||||||
|
|
||||||
Examples
|
Examples
|
||||||
--------
|
--------
|
||||||
|
@ -59,7 +59,8 @@ 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]) -> Result<T, Box<EvalAltResult>> + 'static`
|
> `Fn(engine: &Engine, lib: &Module, args: &mut [&mut Dynamic])`
|
||||||
|
> `-> Result<T, Box<EvalAltResult>> + 'static`
|
||||||
|
|
||||||
where:
|
where:
|
||||||
|
|
||||||
@ -117,7 +118,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>(),
|
||||||
],
|
],
|
||||||
move |engine: &Engine, lib: &Module, args: &mut [&mut Dynamic]| {
|
|engine: &Engine, lib: &Module, args: &mut [&mut Dynamic]| {
|
||||||
// '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
|
||||||
|
12
src/api.rs
12
src/api.rs
@ -3,7 +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::{IteratorFn, SendSync};
|
use crate::fn_native::SendSync;
|
||||||
use crate::module::{FuncReturn, Module};
|
use crate::module::{FuncReturn, Module};
|
||||||
use crate::optimize::OptimizationLevel;
|
use crate::optimize::OptimizationLevel;
|
||||||
use crate::parser::AST;
|
use crate::parser::AST;
|
||||||
@ -174,11 +174,15 @@ impl Engine {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Register an iterator adapter for a type with the `Engine`.
|
/// Register an iterator adapter for an iterable type with the `Engine`.
|
||||||
/// This is an advanced feature.
|
/// This is an advanced feature.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn register_iterator<T: Variant + Clone>(&mut self, f: IteratorFn) -> &mut Self {
|
pub fn register_iterator<T>(&mut self) -> &mut Self
|
||||||
self.global_module.set_iter(TypeId::of::<T>(), f);
|
where
|
||||||
|
T: Variant + Clone + Iterator,
|
||||||
|
<T as Iterator>::Item: Variant + Clone,
|
||||||
|
{
|
||||||
|
self.global_module.set_iterable::<T>();
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
137
src/engine.rs
137
src/engine.rs
@ -129,6 +129,54 @@ pub enum ChainType {
|
|||||||
Dot,
|
Dot,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum IndexChainValue {
|
||||||
|
None,
|
||||||
|
FnCallArgs(StaticVec<Dynamic>),
|
||||||
|
Value(Dynamic),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||||
|
impl IndexChainValue {
|
||||||
|
/// Return the `Dynamic` value.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if not `IndexChainValue::Value`.
|
||||||
|
pub fn as_value(self) -> Dynamic {
|
||||||
|
match self {
|
||||||
|
Self::None | Self::FnCallArgs(_) => panic!("expecting IndexChainValue::Value"),
|
||||||
|
Self::Value(value) => value,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Return the `StaticVec<Dynamic>` value.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if not `IndexChainValue::FnCallArgs`.
|
||||||
|
pub fn as_fn_call_args(self) -> StaticVec<Dynamic> {
|
||||||
|
match self {
|
||||||
|
Self::None | Self::Value(_) => panic!("expecting IndexChainValue::FnCallArgs"),
|
||||||
|
Self::FnCallArgs(value) => value,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||||
|
impl From<StaticVec<Dynamic>> for IndexChainValue {
|
||||||
|
fn from(value: StaticVec<Dynamic>) -> Self {
|
||||||
|
Self::FnCallArgs(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||||
|
impl From<Dynamic> for IndexChainValue {
|
||||||
|
fn from(value: Dynamic) -> Self {
|
||||||
|
Self::Value(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// 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")))]
|
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -790,7 +838,7 @@ impl Engine {
|
|||||||
this_ptr: &mut Option<&mut Dynamic>,
|
this_ptr: &mut Option<&mut Dynamic>,
|
||||||
target: &mut Target,
|
target: &mut Target,
|
||||||
rhs: &Expr,
|
rhs: &Expr,
|
||||||
idx_values: &mut StaticVec<Dynamic>,
|
mut idx_values: StaticVec<IndexChainValue>,
|
||||||
chain_type: ChainType,
|
chain_type: ChainType,
|
||||||
level: usize,
|
level: usize,
|
||||||
new_val: Option<(Dynamic, Position)>,
|
new_val: Option<(Dynamic, Position)>,
|
||||||
@ -820,6 +868,7 @@ impl Engine {
|
|||||||
Expr::Dot(x) | Expr::Index(x) => {
|
Expr::Dot(x) | Expr::Index(x) => {
|
||||||
let (idx, expr, pos) = x.as_ref();
|
let (idx, expr, pos) = x.as_ref();
|
||||||
let idx_pos = idx.position();
|
let idx_pos = idx.position();
|
||||||
|
let idx_val = idx_val.as_value();
|
||||||
let obj_ptr = &mut self.get_indexed_mut(
|
let obj_ptr = &mut self.get_indexed_mut(
|
||||||
state, lib, target, idx_val, idx_pos, false, true, level,
|
state, lib, target, idx_val, idx_pos, false, true, level,
|
||||||
)?;
|
)?;
|
||||||
@ -832,6 +881,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
// xxx[rhs] = new_val
|
// xxx[rhs] = new_val
|
||||||
_ if new_val.is_some() => {
|
_ if new_val.is_some() => {
|
||||||
|
let idx_val = idx_val.as_value();
|
||||||
let mut idx_val2 = idx_val.clone();
|
let mut idx_val2 = idx_val.clone();
|
||||||
|
|
||||||
// `call_setter` is introduced to bypass double mutable borrowing of target
|
// `call_setter` is introduced to bypass double mutable borrowing of target
|
||||||
@ -876,9 +926,11 @@ impl Engine {
|
|||||||
Ok(Default::default())
|
Ok(Default::default())
|
||||||
}
|
}
|
||||||
// xxx[rhs]
|
// xxx[rhs]
|
||||||
_ => self
|
_ => {
|
||||||
.get_indexed_mut(state, lib, target, idx_val, pos, false, true, level)
|
let idx_val = idx_val.as_value();
|
||||||
.map(|v| (v.take_or_clone(), false)),
|
self.get_indexed_mut(state, lib, target, idx_val, pos, false, true, level)
|
||||||
|
.map(|v| (v.take_or_clone(), false))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -889,9 +941,9 @@ impl Engine {
|
|||||||
Expr::FnCall(x) if x.1.is_none() => {
|
Expr::FnCall(x) if x.1.is_none() => {
|
||||||
let ((name, native, _, pos), _, hash, _, def_val) = x.as_ref();
|
let ((name, native, _, pos), _, hash, _, def_val) = x.as_ref();
|
||||||
let def_val = def_val.map(Into::<Dynamic>::into);
|
let def_val = def_val.map(Into::<Dynamic>::into);
|
||||||
|
let args = idx_val.as_fn_call_args();
|
||||||
self.make_method_call(
|
self.make_method_call(
|
||||||
state, lib, name, *hash, target, idx_val, &def_val, *native, false,
|
state, lib, name, *hash, target, args, &def_val, *native, false, level,
|
||||||
level,
|
|
||||||
)
|
)
|
||||||
.map_err(|err| err.fill_position(*pos))
|
.map_err(|err| err.fill_position(*pos))
|
||||||
}
|
}
|
||||||
@ -956,10 +1008,11 @@ impl Engine {
|
|||||||
Expr::FnCall(x) if x.1.is_none() => {
|
Expr::FnCall(x) if x.1.is_none() => {
|
||||||
let ((name, native, _, pos), _, hash, _, def_val) = x.as_ref();
|
let ((name, native, _, pos), _, hash, _, def_val) = x.as_ref();
|
||||||
let def_val = def_val.map(Into::<Dynamic>::into);
|
let def_val = def_val.map(Into::<Dynamic>::into);
|
||||||
|
let args = idx_val.as_fn_call_args();
|
||||||
let (val, _) = self
|
let (val, _) = self
|
||||||
.make_method_call(
|
.make_method_call(
|
||||||
state, lib, name, *hash, target, idx_val, &def_val,
|
state, lib, name, *hash, target, args, &def_val, *native,
|
||||||
*native, false, level,
|
false, level,
|
||||||
)
|
)
|
||||||
.map_err(|err| err.fill_position(*pos))?;
|
.map_err(|err| err.fill_position(*pos))?;
|
||||||
val.into()
|
val.into()
|
||||||
@ -1034,10 +1087,11 @@ impl Engine {
|
|||||||
Expr::FnCall(x) if x.1.is_none() => {
|
Expr::FnCall(x) if x.1.is_none() => {
|
||||||
let ((name, native, _, pos), _, hash, _, def_val) = x.as_ref();
|
let ((name, native, _, pos), _, hash, _, def_val) = x.as_ref();
|
||||||
let def_val = def_val.map(Into::<Dynamic>::into);
|
let def_val = def_val.map(Into::<Dynamic>::into);
|
||||||
|
let args = idx_val.as_fn_call_args();
|
||||||
let (mut val, _) = self
|
let (mut val, _) = self
|
||||||
.make_method_call(
|
.make_method_call(
|
||||||
state, lib, name, *hash, target, idx_val, &def_val,
|
state, lib, name, *hash, target, args, &def_val, *native,
|
||||||
*native, false, level,
|
false, level,
|
||||||
)
|
)
|
||||||
.map_err(|err| err.fill_position(*pos))?;
|
.map_err(|err| err.fill_position(*pos))?;
|
||||||
let val = &mut val;
|
let val = &mut val;
|
||||||
@ -1083,10 +1137,19 @@ impl Engine {
|
|||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let idx_values = &mut StaticVec::new();
|
let mut idx_values = StaticVec::new();
|
||||||
|
|
||||||
self.eval_indexed_chain(
|
self.eval_indexed_chain(
|
||||||
scope, mods, state, lib, this_ptr, dot_rhs, chain_type, idx_values, 0, level,
|
scope,
|
||||||
|
mods,
|
||||||
|
state,
|
||||||
|
lib,
|
||||||
|
this_ptr,
|
||||||
|
dot_rhs,
|
||||||
|
chain_type,
|
||||||
|
&mut idx_values,
|
||||||
|
0,
|
||||||
|
level,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
match dot_lhs {
|
match dot_lhs {
|
||||||
@ -1133,11 +1196,8 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Evaluate a chain of indexes and store the results in a list.
|
/// Evaluate a chain of indexes and store the results in a StaticVec.
|
||||||
/// The first few results are stored in the array `list` which is of fixed length.
|
/// StaticVec is used to avoid an allocation in the overwhelming cases of just a few levels of indexing.
|
||||||
/// Any spill-overs are stored in `more`, which is dynamic.
|
|
||||||
/// The fixed length array is used to avoid an allocation in the overwhelming cases of just a few levels of indexing.
|
|
||||||
/// The total number of values is returned.
|
|
||||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||||
fn eval_indexed_chain(
|
fn eval_indexed_chain(
|
||||||
&self,
|
&self,
|
||||||
@ -1148,7 +1208,7 @@ impl Engine {
|
|||||||
this_ptr: &mut Option<&mut Dynamic>,
|
this_ptr: &mut Option<&mut Dynamic>,
|
||||||
expr: &Expr,
|
expr: &Expr,
|
||||||
chain_type: ChainType,
|
chain_type: ChainType,
|
||||||
idx_values: &mut StaticVec<Dynamic>,
|
idx_values: &mut StaticVec<IndexChainValue>,
|
||||||
size: usize,
|
size: usize,
|
||||||
level: usize,
|
level: usize,
|
||||||
) -> Result<(), Box<EvalAltResult>> {
|
) -> Result<(), Box<EvalAltResult>> {
|
||||||
@ -1162,31 +1222,30 @@ impl Engine {
|
|||||||
.map(|arg_expr| {
|
.map(|arg_expr| {
|
||||||
self.eval_expr(scope, mods, state, lib, this_ptr, arg_expr, level)
|
self.eval_expr(scope, mods, state, lib, this_ptr, arg_expr, level)
|
||||||
})
|
})
|
||||||
.collect::<Result<StaticVec<Dynamic>, _>>()?;
|
.collect::<Result<StaticVec<_>, _>>()?;
|
||||||
|
|
||||||
idx_values.push(Dynamic::from(arg_values));
|
idx_values.push(arg_values.into());
|
||||||
}
|
}
|
||||||
Expr::FnCall(_) => unreachable!(),
|
Expr::FnCall(_) => unreachable!(),
|
||||||
Expr::Property(_) => idx_values.push(().into()), // Store a placeholder - no need to copy the property name
|
Expr::Property(_) => idx_values.push(IndexChainValue::None),
|
||||||
Expr::Index(x) | Expr::Dot(x) => {
|
Expr::Index(x) | Expr::Dot(x) => {
|
||||||
let (lhs, rhs, _) = x.as_ref();
|
let (lhs, rhs, _) = x.as_ref();
|
||||||
|
|
||||||
// Evaluate in left-to-right order
|
// Evaluate in left-to-right order
|
||||||
let lhs_val = match lhs {
|
let lhs_val = match lhs {
|
||||||
Expr::Property(_) => Default::default(), // Store a placeholder in case of a property
|
Expr::Property(_) => IndexChainValue::None,
|
||||||
Expr::FnCall(x) if chain_type == ChainType::Dot && x.1.is_none() => {
|
Expr::FnCall(x) if chain_type == ChainType::Dot && x.1.is_none() => {
|
||||||
let arg_values = x
|
x.3.iter()
|
||||||
.3
|
|
||||||
.iter()
|
|
||||||
.map(|arg_expr| {
|
.map(|arg_expr| {
|
||||||
self.eval_expr(scope, mods, state, lib, this_ptr, arg_expr, level)
|
self.eval_expr(scope, mods, state, lib, this_ptr, arg_expr, level)
|
||||||
})
|
})
|
||||||
.collect::<Result<StaticVec<Dynamic>, _>>()?;
|
.collect::<Result<StaticVec<Dynamic>, _>>()?
|
||||||
|
.into()
|
||||||
Dynamic::from(arg_values)
|
|
||||||
}
|
}
|
||||||
Expr::FnCall(_) => unreachable!(),
|
Expr::FnCall(_) => unreachable!(),
|
||||||
_ => self.eval_expr(scope, mods, state, lib, this_ptr, lhs, level)?,
|
_ => self
|
||||||
|
.eval_expr(scope, mods, state, lib, this_ptr, lhs, level)?
|
||||||
|
.into(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Push in reverse order
|
// Push in reverse order
|
||||||
@ -1201,7 +1260,10 @@ impl Engine {
|
|||||||
|
|
||||||
idx_values.push(lhs_val);
|
idx_values.push(lhs_val);
|
||||||
}
|
}
|
||||||
_ => idx_values.push(self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?),
|
_ => idx_values.push(
|
||||||
|
self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?
|
||||||
|
.into(),
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -1782,21 +1844,22 @@ impl Engine {
|
|||||||
// For loop
|
// For loop
|
||||||
Stmt::For(x) => {
|
Stmt::For(x) => {
|
||||||
let (name, expr, stmt, _) = x.as_ref();
|
let (name, expr, stmt, _) = x.as_ref();
|
||||||
let iter_type = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?;
|
let iter_obj = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?;
|
||||||
let tid = iter_type.type_id();
|
let iter_type = iter_obj.type_id();
|
||||||
|
|
||||||
if let Some(func) = self
|
let func = self
|
||||||
.global_module
|
.global_module
|
||||||
.get_iter(tid)
|
.get_iter(iter_type)
|
||||||
.or_else(|| self.packages.get_iter(tid))
|
.or_else(|| self.packages.get_iter(iter_type));
|
||||||
{
|
|
||||||
|
if let Some(func) = func {
|
||||||
// Add the loop variable
|
// Add the loop variable
|
||||||
let var_name = unsafe_cast_var_name_to_lifetime(name, &state);
|
let var_name = unsafe_cast_var_name_to_lifetime(name, &state);
|
||||||
scope.push(var_name, ());
|
scope.push(var_name, ());
|
||||||
let index = scope.len() - 1;
|
let index = scope.len() - 1;
|
||||||
state.scope_level += 1;
|
state.scope_level += 1;
|
||||||
|
|
||||||
for iter_value in func(iter_type) {
|
for iter_value in func(iter_obj) {
|
||||||
let (loop_var, _) = scope.get_mut(index);
|
let (loop_var, _) = scope.get_mut(index);
|
||||||
|
|
||||||
let value = iter_value.flatten();
|
let value = iter_value.flatten();
|
||||||
|
131
src/fn_call.rs
131
src/fn_call.rs
@ -193,11 +193,9 @@ impl Engine {
|
|||||||
// Search for the native function
|
// Search for the native function
|
||||||
// First search registered functions (can override packages)
|
// First search registered functions (can override packages)
|
||||||
// Then search packages
|
// Then search packages
|
||||||
let func = self
|
let func = //lib.get_fn(hash_fn, pub_only)
|
||||||
.global_module
|
self.global_module.get_fn(hash_fn, pub_only)
|
||||||
.get_fn(hash_fn, pub_only)
|
.or_else(|| self.packages.get_fn(hash_fn, pub_only));
|
||||||
.or_else(|| lib.get_fn(hash_fn, pub_only))
|
|
||||||
.or_else(|| self.packages.get_fn(hash_fn, pub_only));
|
|
||||||
|
|
||||||
if let Some(func) = func {
|
if let Some(func) = func {
|
||||||
assert!(func.is_native());
|
assert!(func.is_native());
|
||||||
@ -462,9 +460,9 @@ impl Engine {
|
|||||||
|
|
||||||
// First check script-defined functions
|
// First check script-defined functions
|
||||||
lib.contains_fn(hash_script, pub_only)
|
lib.contains_fn(hash_script, pub_only)
|
||||||
|| lib.contains_fn(hash_fn, pub_only)
|
//|| lib.contains_fn(hash_fn, pub_only)
|
||||||
// Then check registered functions
|
// Then check registered functions
|
||||||
|| self.global_module.contains_fn(hash_script, pub_only)
|
//|| self.global_module.contains_fn(hash_script, pub_only)
|
||||||
|| self.global_module.contains_fn(hash_fn, pub_only)
|
|| self.global_module.contains_fn(hash_fn, pub_only)
|
||||||
// Then check packages
|
// Then check packages
|
||||||
|| self.packages.contains_fn(hash_script, pub_only)
|
|| self.packages.contains_fn(hash_script, pub_only)
|
||||||
@ -547,15 +545,14 @@ impl Engine {
|
|||||||
|
|
||||||
// Script-like function found
|
// Script-like function found
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
_ if self.global_module.contains_fn(hash_script, pub_only)
|
_ if lib.contains_fn(hash_script, pub_only)
|
||||||
|| lib.contains_fn(hash_script, pub_only)
|
//|| self.global_module.contains_fn(hash_script, pub_only)
|
||||||
|| self.packages.contains_fn(hash_script, pub_only) =>
|
|| self.packages.contains_fn(hash_script, pub_only) =>
|
||||||
{
|
{
|
||||||
// Get function
|
// Get function
|
||||||
let func = self
|
let func = lib
|
||||||
.global_module
|
|
||||||
.get_fn(hash_script, pub_only)
|
.get_fn(hash_script, pub_only)
|
||||||
.or_else(|| lib.get_fn(hash_script, pub_only))
|
//.or_else(|| self.global_module.get_fn(hash_script, pub_only))
|
||||||
.or_else(|| self.packages.get_fn(hash_script, pub_only))
|
.or_else(|| self.packages.get_fn(hash_script, pub_only))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@ -698,7 +695,7 @@ impl Engine {
|
|||||||
name: &str,
|
name: &str,
|
||||||
hash_script: u64,
|
hash_script: u64,
|
||||||
target: &mut Target,
|
target: &mut Target,
|
||||||
idx_val: Dynamic,
|
mut call_args: StaticVec<Dynamic>,
|
||||||
def_val: &Option<Dynamic>,
|
def_val: &Option<Dynamic>,
|
||||||
native: bool,
|
native: bool,
|
||||||
pub_only: bool,
|
pub_only: bool,
|
||||||
@ -708,7 +705,6 @@ impl Engine {
|
|||||||
|
|
||||||
// Get a reference to the mutation target Dynamic
|
// Get a reference to the mutation target Dynamic
|
||||||
let obj = target.as_mut();
|
let obj = target.as_mut();
|
||||||
let mut idx = idx_val.cast::<StaticVec<Dynamic>>();
|
|
||||||
let mut _fn_name = name;
|
let mut _fn_name = name;
|
||||||
|
|
||||||
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>() {
|
||||||
@ -721,12 +717,12 @@ impl Engine {
|
|||||||
let hash = if native {
|
let hash = if native {
|
||||||
0
|
0
|
||||||
} else {
|
} else {
|
||||||
calc_fn_hash(empty(), fn_name, curry.len() + idx.len(), empty())
|
calc_fn_hash(empty(), fn_name, curry.len() + call_args.len(), empty())
|
||||||
};
|
};
|
||||||
// Arguments are passed as-is, adding the curried arguments
|
// Arguments are passed as-is, adding the curried arguments
|
||||||
let mut arg_values = curry
|
let mut arg_values = curry
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
.chain(idx.iter_mut())
|
.chain(call_args.iter_mut())
|
||||||
.collect::<StaticVec<_>>();
|
.collect::<StaticVec<_>>();
|
||||||
let args = arg_values.as_mut();
|
let args = arg_values.as_mut();
|
||||||
|
|
||||||
@ -734,9 +730,12 @@ impl Engine {
|
|||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
state, lib, fn_name, hash, args, false, false, pub_only, None, def_val, level,
|
state, lib, fn_name, hash, args, false, false, pub_only, None, def_val, level,
|
||||||
)
|
)
|
||||||
} else if _fn_name == KEYWORD_FN_PTR_CALL && idx.len() > 0 && idx[0].is::<FnPtr>() {
|
} else if _fn_name == KEYWORD_FN_PTR_CALL
|
||||||
|
&& call_args.len() > 0
|
||||||
|
&& call_args[0].is::<FnPtr>()
|
||||||
|
{
|
||||||
// FnPtr call on object
|
// FnPtr call on object
|
||||||
let fn_ptr = idx.remove(0).cast::<FnPtr>();
|
let fn_ptr = call_args.remove(0).cast::<FnPtr>();
|
||||||
let mut curry = fn_ptr.curry().iter().cloned().collect::<StaticVec<_>>();
|
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.get_fn_name().clone();
|
||||||
@ -744,12 +743,12 @@ impl Engine {
|
|||||||
let hash = if native {
|
let hash = if native {
|
||||||
0
|
0
|
||||||
} else {
|
} else {
|
||||||
calc_fn_hash(empty(), &fn_name, curry.len() + idx.len(), empty())
|
calc_fn_hash(empty(), &fn_name, curry.len() + call_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 arg_values = once(obj)
|
let mut arg_values = once(obj)
|
||||||
.chain(curry.iter_mut())
|
.chain(curry.iter_mut())
|
||||||
.chain(idx.iter_mut())
|
.chain(call_args.iter_mut())
|
||||||
.collect::<StaticVec<_>>();
|
.collect::<StaticVec<_>>();
|
||||||
let args = arg_values.as_mut();
|
let args = arg_values.as_mut();
|
||||||
|
|
||||||
@ -767,7 +766,7 @@ impl Engine {
|
|||||||
.curry()
|
.curry()
|
||||||
.iter()
|
.iter()
|
||||||
.cloned()
|
.cloned()
|
||||||
.chain(idx.into_iter())
|
.chain(call_args.into_iter())
|
||||||
.collect(),
|
.collect(),
|
||||||
)
|
)
|
||||||
.into(),
|
.into(),
|
||||||
@ -776,7 +775,7 @@ impl Engine {
|
|||||||
} else if {
|
} else if {
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
{
|
{
|
||||||
_fn_name == KEYWORD_IS_SHARED && idx.is_empty()
|
_fn_name == KEYWORD_IS_SHARED && call_args.is_empty()
|
||||||
}
|
}
|
||||||
#[cfg(feature = "no_closure")]
|
#[cfg(feature = "no_closure")]
|
||||||
false
|
false
|
||||||
@ -802,13 +801,13 @@ impl Engine {
|
|||||||
.iter()
|
.iter()
|
||||||
.cloned()
|
.cloned()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.for_each(|(i, v)| idx.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
|
||||||
} else {
|
} else {
|
||||||
calc_fn_hash(empty(), _fn_name, idx.len(), empty())
|
calc_fn_hash(empty(), _fn_name, call_args.len(), empty())
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -819,7 +818,9 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Attached object pointer in front of the arguments
|
// Attached object pointer in front of the arguments
|
||||||
let mut arg_values = once(obj).chain(idx.iter_mut()).collect::<StaticVec<_>>();
|
let mut arg_values = once(obj)
|
||||||
|
.chain(call_args.iter_mut())
|
||||||
|
.collect::<StaticVec<_>>();
|
||||||
let args = arg_values.as_mut();
|
let args = arg_values.as_mut();
|
||||||
|
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
@ -876,28 +877,27 @@ impl Engine {
|
|||||||
// 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 expr = args_expr.get(0).unwrap();
|
||||||
let arg_value = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?;
|
let fn_ptr = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?;
|
||||||
|
|
||||||
if !arg_value.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(arg_value.type_name()),
|
self.map_type_name(fn_ptr.type_name()),
|
||||||
expr.position(),
|
expr.position(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let (fn_name, fn_curry) = arg_value.cast::<FnPtr>().take_data();
|
let (fn_name, mut fn_curry) = fn_ptr.cast::<FnPtr>().take_data();
|
||||||
|
|
||||||
let curry: StaticVec<_> = args_expr
|
// Append the new curried arguments to the existing list.
|
||||||
|
args_expr
|
||||||
.iter()
|
.iter()
|
||||||
.skip(1)
|
.skip(1)
|
||||||
.map(|expr| self.eval_expr(scope, mods, state, lib, this_ptr, expr, level))
|
.try_for_each(|expr| -> Result<(), Box<EvalAltResult>> {
|
||||||
.collect::<Result<_, _>>()?;
|
fn_curry.push(self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?);
|
||||||
|
Ok(())
|
||||||
|
})?;
|
||||||
|
|
||||||
return Ok(FnPtr::new_unchecked(
|
return Ok(FnPtr::new_unchecked(fn_name, fn_curry).into());
|
||||||
fn_name,
|
|
||||||
fn_curry.into_iter().chain(curry.into_iter()).collect(),
|
|
||||||
)
|
|
||||||
.into());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle is_shared()
|
// Handle is_shared()
|
||||||
@ -920,24 +920,27 @@ impl Engine {
|
|||||||
&& !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 expr = args_expr.get(0).unwrap();
|
||||||
let arg_value = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?;
|
let fn_ptr = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?;
|
||||||
|
|
||||||
if arg_value.is::<FnPtr>() {
|
if !fn_ptr.is::<FnPtr>() {
|
||||||
let fn_ptr = arg_value.cast::<FnPtr>();
|
|
||||||
curry = fn_ptr.curry().iter().cloned().collect();
|
|
||||||
// Redirect function name
|
|
||||||
redirected = fn_ptr.take_data().0;
|
|
||||||
name = &redirected;
|
|
||||||
// Skip the first argument
|
|
||||||
args_expr = &args_expr.as_ref()[1..];
|
|
||||||
// Recalculate hash
|
|
||||||
hash_script = calc_fn_hash(empty(), name, curry.len() + args_expr.len(), empty());
|
|
||||||
} else {
|
|
||||||
return Err(self.make_type_mismatch_err::<FnPtr>(
|
return Err(self.make_type_mismatch_err::<FnPtr>(
|
||||||
self.map_type_name(arg_value.type_name()),
|
self.map_type_name(fn_ptr.type_name()),
|
||||||
expr.position(),
|
expr.position(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let fn_ptr = fn_ptr.cast::<FnPtr>();
|
||||||
|
curry = fn_ptr.curry().iter().cloned().collect();
|
||||||
|
|
||||||
|
// Redirect function name
|
||||||
|
redirected = fn_ptr.take_data().0;
|
||||||
|
name = &redirected;
|
||||||
|
|
||||||
|
// Skip the first argument
|
||||||
|
args_expr = &args_expr.as_ref()[1..];
|
||||||
|
|
||||||
|
// Recalculate hash
|
||||||
|
hash_script = calc_fn_hash(empty(), name, curry.len() + args_expr.len(), empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle is_def_var()
|
// Handle is_def_var()
|
||||||
@ -946,8 +949,8 @@ 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 expr = args_expr.get(0).unwrap();
|
let expr = args_expr.get(0).unwrap();
|
||||||
let arg_value = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?;
|
let var_name = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?;
|
||||||
let var_name = arg_value.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, expr.position())
|
||||||
})?;
|
})?;
|
||||||
if var_name.is_empty() {
|
if var_name.is_empty() {
|
||||||
@ -970,20 +973,18 @@ 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 fn_name_expr = args_expr.get(0).unwrap();
|
let expr0 = args_expr.get(0).unwrap();
|
||||||
let num_params_expr = args_expr.get(1).unwrap();
|
let expr1 = args_expr.get(1).unwrap();
|
||||||
|
|
||||||
let arg0_value =
|
let fn_name = self.eval_expr(scope, mods, state, lib, this_ptr, expr0, level)?;
|
||||||
self.eval_expr(scope, mods, state, lib, this_ptr, fn_name_expr, level)?;
|
let num_params = self.eval_expr(scope, mods, state, lib, this_ptr, expr1, level)?;
|
||||||
let arg1_value =
|
|
||||||
self.eval_expr(scope, mods, state, lib, this_ptr, num_params_expr, level)?;
|
|
||||||
|
|
||||||
let fn_name = arg0_value.as_str().map_err(|err| {
|
let fn_name = fn_name.as_str().map_err(|err| {
|
||||||
self.make_type_mismatch_err::<ImmutableString>(err, fn_name_expr.position())
|
self.make_type_mismatch_err::<ImmutableString>(err, expr0.position())
|
||||||
})?;
|
|
||||||
let num_params = arg1_value.as_int().map_err(|err| {
|
|
||||||
self.make_type_mismatch_err::<INT>(err, num_params_expr.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());
|
||||||
@ -1002,8 +1003,8 @@ impl Engine {
|
|||||||
// 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 expr = args_expr.get(0).unwrap();
|
||||||
let arg_value = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?;
|
let script = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?;
|
||||||
let script = arg_value.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, expr.position())
|
||||||
})?;
|
})?;
|
||||||
let result = if !script.is_empty() {
|
let result = if !script.is_empty() {
|
||||||
|
@ -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, IteratorFn};
|
pub use fn_native::FnPtr;
|
||||||
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};
|
||||||
|
@ -549,36 +549,6 @@ impl Module {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set a raw function but with a signature that is a scripted function (meaning that the types
|
|
||||||
/// are not determined), but the implementation is in Rust.
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
|
||||||
#[inline]
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub(crate) fn set_raw_fn_as_scripted(
|
|
||||||
&mut self,
|
|
||||||
name: impl Into<String>,
|
|
||||||
num_params: usize,
|
|
||||||
func: impl Fn(&Engine, &Module, &mut [&mut Dynamic]) -> FuncReturn<Dynamic> + SendSync + 'static,
|
|
||||||
) -> u64 {
|
|
||||||
// None + function name + number of arguments.
|
|
||||||
let name = name.into();
|
|
||||||
let hash_script = calc_fn_hash(empty(), &name, num_params, empty());
|
|
||||||
let f = move |engine: &Engine, lib: &Module, args: &mut FnCallArgs| func(engine, lib, args);
|
|
||||||
self.functions.insert(
|
|
||||||
hash_script,
|
|
||||||
(
|
|
||||||
name,
|
|
||||||
FnAccess::Public,
|
|
||||||
num_params,
|
|
||||||
None,
|
|
||||||
CallableFunction::from_pure(Box::new(f)),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
self.indexed = false;
|
|
||||||
hash_script
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set a Rust function taking no parameters into the module, returning a hash key.
|
/// Set a Rust function taking no parameters into the module, returning a hash key.
|
||||||
///
|
///
|
||||||
/// If there is a similar existing Rust function, it is replaced.
|
/// If there is a similar existing Rust function, it is replaced.
|
||||||
@ -1498,6 +1468,28 @@ impl Module {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set a type iterator into the module.
|
||||||
|
pub fn set_iterable<T>(&mut self) -> &mut Self
|
||||||
|
where
|
||||||
|
T: Variant + Clone + IntoIterator,
|
||||||
|
<T as IntoIterator>::Item: Variant + Clone,
|
||||||
|
{
|
||||||
|
self.set_iter(TypeId::of::<T>(), |obj: Dynamic| {
|
||||||
|
Box::new(obj.cast::<T>().into_iter().map(Dynamic::from))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set an iterator type into the module as a type iterator.
|
||||||
|
pub fn set_iterator<T>(&mut self) -> &mut Self
|
||||||
|
where
|
||||||
|
T: Variant + Clone + Iterator,
|
||||||
|
<T as Iterator>::Item: Variant + Clone,
|
||||||
|
{
|
||||||
|
self.set_iter(TypeId::of::<T>(), |obj: Dynamic| {
|
||||||
|
Box::new(obj.cast::<T>().map(Dynamic::from))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the specified type iterator.
|
/// Get the specified type iterator.
|
||||||
pub(crate) fn get_iter(&self, id: TypeId) -> Option<IteratorFn> {
|
pub(crate) fn get_iter(&self, id: TypeId) -> Option<IteratorFn> {
|
||||||
self.type_iterators.get(&id).cloned()
|
self.type_iterators.get(&id).cloned()
|
||||||
|
@ -74,19 +74,19 @@ def_package!(crate:BasicArrayPackage:"Basic array utilities.", lib, {
|
|||||||
lib.set_raw_fn("map", &[TypeId::of::<Array>(), TypeId::of::<FnPtr>()], 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("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>()], 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>()], 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("some", &[TypeId::of::<Array>(), TypeId::of::<FnPtr>()], some);
|
||||||
lib.set_raw_fn("all", &[TypeId::of::<Array>(), TypeId::of::<FnPtr>()], all);
|
lib.set_raw_fn("all", &[TypeId::of::<Array>(), TypeId::of::<FnPtr>()], all);
|
||||||
|
lib.set_raw_fn("none", &[TypeId::of::<Array>(), TypeId::of::<FnPtr>()], none);
|
||||||
lib.set_raw_fn("sort", &[TypeId::of::<Array>(), TypeId::of::<FnPtr>()], sort);
|
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);
|
||||||
|
|
||||||
// Register array iterator
|
// Register array iterator
|
||||||
lib.set_iter(
|
lib.set_iterable::<Array>();
|
||||||
TypeId::of::<Array>(),
|
|
||||||
|arr| Box::new(arr.cast::<Array>().into_iter()) as Box<dyn Iterator<Item = Dynamic>>,
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
#[export_module]
|
#[export_module]
|
||||||
@ -131,6 +131,14 @@ mod array_functions {
|
|||||||
list.clear();
|
list.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub fn chop(list: &mut Array, len: INT) {
|
||||||
|
if len as usize >= list.len() {
|
||||||
|
} else if len >= 0 {
|
||||||
|
list.drain(0..list.len() - len as usize);
|
||||||
|
} else {
|
||||||
|
list.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
pub fn reverse(list: &mut Array) {
|
pub fn reverse(list: &mut Array) {
|
||||||
list.reverse();
|
list.reverse();
|
||||||
}
|
}
|
||||||
@ -153,6 +161,37 @@ mod array_functions {
|
|||||||
|
|
||||||
list.splice(start..start + len, replace.into_iter());
|
list.splice(start..start + len, replace.into_iter());
|
||||||
}
|
}
|
||||||
|
pub fn extract(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[start..start + len].iter().cloned().collect()
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "extract")]
|
||||||
|
pub fn extract_tail(list: &mut Array, start: INT) -> Array {
|
||||||
|
let start = if start < 0 {
|
||||||
|
0
|
||||||
|
} else if start as usize >= list.len() {
|
||||||
|
list.len() - 1
|
||||||
|
} else {
|
||||||
|
start as usize
|
||||||
|
};
|
||||||
|
|
||||||
|
list[start..].iter().cloned().collect()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pad<T: Variant + Clone>(
|
fn pad<T: Variant + Clone>(
|
||||||
@ -325,6 +364,40 @@ fn all(
|
|||||||
Ok(true.into())
|
Ok(true.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn none(
|
||||||
|
engine: &Engine,
|
||||||
|
lib: &Module,
|
||||||
|
args: &mut [&mut Dynamic],
|
||||||
|
) -> Result<bool, 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() {
|
||||||
|
if filter
|
||||||
|
.call_dynamic(engine, lib, None, [item.clone()])
|
||||||
|
.or_else(|err| match *err {
|
||||||
|
EvalAltResult::ErrorFunctionNotFound(_, _) => {
|
||||||
|
filter.call_dynamic(engine, lib, None, [item.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)
|
||||||
|
{
|
||||||
|
return Ok(false.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(true.into())
|
||||||
|
}
|
||||||
|
|
||||||
fn reduce(
|
fn reduce(
|
||||||
engine: &Engine,
|
engine: &Engine,
|
||||||
lib: &Module,
|
lib: &Module,
|
||||||
@ -359,6 +432,47 @@ fn reduce(
|
|||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn reduce_with_initial(
|
||||||
|
engine: &Engine,
|
||||||
|
lib: &Module,
|
||||||
|
args: &mut [&mut Dynamic],
|
||||||
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
|
let list = args[0].read_lock::<Array>().unwrap();
|
||||||
|
let reducer = args[1].read_lock::<FnPtr>().unwrap();
|
||||||
|
let initial = args[2].read_lock::<FnPtr>().unwrap();
|
||||||
|
|
||||||
|
let mut result = initial.call_dynamic(engine, lib, None, []).map_err(|err| {
|
||||||
|
Box::new(EvalAltResult::ErrorInFunctionCall(
|
||||||
|
"reduce".to_string(),
|
||||||
|
err,
|
||||||
|
Position::none(),
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
for (i, item) in list.iter().enumerate() {
|
||||||
|
result = reducer
|
||||||
|
.call_dynamic(engine, lib, None, [result.clone(), item.clone()])
|
||||||
|
.or_else(|err| match *err {
|
||||||
|
EvalAltResult::ErrorFunctionNotFound(_, _) => reducer.call_dynamic(
|
||||||
|
engine,
|
||||||
|
lib,
|
||||||
|
None,
|
||||||
|
[result, item.clone(), (i as INT).into()],
|
||||||
|
),
|
||||||
|
_ => Err(err),
|
||||||
|
})
|
||||||
|
.map_err(|err| {
|
||||||
|
Box::new(EvalAltResult::ErrorInFunctionCall(
|
||||||
|
"reduce".to_string(),
|
||||||
|
err,
|
||||||
|
Position::none(),
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
|
||||||
fn reduce_rev(
|
fn reduce_rev(
|
||||||
engine: &Engine,
|
engine: &Engine,
|
||||||
lib: &Module,
|
lib: &Module,
|
||||||
@ -393,6 +507,47 @@ fn reduce_rev(
|
|||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn reduce_rev_with_initial(
|
||||||
|
engine: &Engine,
|
||||||
|
lib: &Module,
|
||||||
|
args: &mut [&mut Dynamic],
|
||||||
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
|
let list = args[0].read_lock::<Array>().unwrap();
|
||||||
|
let reducer = args[1].read_lock::<FnPtr>().unwrap();
|
||||||
|
let initial = args[2].read_lock::<FnPtr>().unwrap();
|
||||||
|
|
||||||
|
let mut result = initial.call_dynamic(engine, lib, None, []).map_err(|err| {
|
||||||
|
Box::new(EvalAltResult::ErrorInFunctionCall(
|
||||||
|
"reduce".to_string(),
|
||||||
|
err,
|
||||||
|
Position::none(),
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
for (i, item) in list.iter().enumerate().rev() {
|
||||||
|
result = reducer
|
||||||
|
.call_dynamic(engine, lib, None, [result.clone(), item.clone()])
|
||||||
|
.or_else(|err| match *err {
|
||||||
|
EvalAltResult::ErrorFunctionNotFound(_, _) => reducer.call_dynamic(
|
||||||
|
engine,
|
||||||
|
lib,
|
||||||
|
None,
|
||||||
|
[result, item.clone(), (i as INT).into()],
|
||||||
|
),
|
||||||
|
_ => Err(err),
|
||||||
|
})
|
||||||
|
.map_err(|err| {
|
||||||
|
Box::new(EvalAltResult::ErrorInFunctionCall(
|
||||||
|
"reduce".to_string(),
|
||||||
|
err,
|
||||||
|
Position::none(),
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
|
||||||
fn sort(
|
fn sort(
|
||||||
engine: &Engine,
|
engine: &Engine,
|
||||||
lib: &Module,
|
lib: &Module,
|
||||||
|
@ -1,24 +1,9 @@
|
|||||||
use crate::any::{Dynamic, Variant};
|
use crate::any::Variant;
|
||||||
use crate::def_package;
|
use crate::def_package;
|
||||||
use crate::module::{FuncReturn, Module};
|
use crate::module::FuncReturn;
|
||||||
use crate::parser::INT;
|
use crate::parser::INT;
|
||||||
|
|
||||||
use crate::stdlib::{
|
use crate::stdlib::ops::{Add, Range};
|
||||||
any::TypeId,
|
|
||||||
boxed::Box,
|
|
||||||
ops::{Add, Range},
|
|
||||||
};
|
|
||||||
|
|
||||||
// Register range function
|
|
||||||
fn reg_range<T: Variant + Clone>(lib: &mut Module)
|
|
||||||
where
|
|
||||||
Range<T>: Iterator<Item = T>,
|
|
||||||
{
|
|
||||||
lib.set_iter(TypeId::of::<Range<T>>(), |source| {
|
|
||||||
Box::new(source.cast::<Range<T>>().map(|x| x.into_dynamic()))
|
|
||||||
as Box<dyn Iterator<Item = Dynamic>>
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_range<T: Variant + Clone>(from: T, to: T) -> FuncReturn<Range<T>> {
|
fn get_range<T: Variant + Clone>(from: T, to: T) -> FuncReturn<Range<T>> {
|
||||||
Ok(from..to)
|
Ok(from..to)
|
||||||
@ -49,18 +34,6 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reg_step<T>(lib: &mut Module)
|
|
||||||
where
|
|
||||||
for<'a> &'a T: Add<&'a T, Output = T>,
|
|
||||||
T: Variant + Clone + PartialOrd,
|
|
||||||
StepRange<T>: Iterator<Item = T>,
|
|
||||||
{
|
|
||||||
lib.set_iter(TypeId::of::<StepRange<T>>(), |source| {
|
|
||||||
Box::new(source.cast::<StepRange<T>>().map(|x| x.into_dynamic()))
|
|
||||||
as Box<dyn Iterator<Item = Dynamic>>
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
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) -> FuncReturn<StepRange<T>>
|
||||||
where
|
where
|
||||||
for<'a> &'a T: Add<&'a T, Output = T>,
|
for<'a> &'a T: Add<&'a T, Output = T>,
|
||||||
@ -70,14 +43,15 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
def_package!(crate:BasicIteratorPackage:"Basic range iterators.", lib, {
|
def_package!(crate:BasicIteratorPackage:"Basic range iterators.", lib, {
|
||||||
reg_range::<INT>(lib);
|
lib.set_iterator::<Range<INT>>();
|
||||||
|
|
||||||
lib.set_fn_2("range", get_range::<INT>);
|
lib.set_fn_2("range", get_range::<INT>);
|
||||||
|
|
||||||
if cfg!(not(feature = "only_i32")) && cfg!(not(feature = "only_i64")) {
|
if cfg!(not(feature = "only_i32")) && cfg!(not(feature = "only_i64")) {
|
||||||
macro_rules! reg_range {
|
macro_rules! reg_range {
|
||||||
($lib:expr, $x:expr, $( $y:ty ),*) => (
|
($lib:expr, $x:expr, $( $y:ty ),*) => (
|
||||||
$(
|
$(
|
||||||
reg_range::<$y>($lib);
|
$lib.set_iterator::<Range<$y>>();
|
||||||
$lib.set_fn_2($x, get_range::<$y>);
|
$lib.set_fn_2($x, get_range::<$y>);
|
||||||
)*
|
)*
|
||||||
)
|
)
|
||||||
@ -90,14 +64,14 @@ def_package!(crate:BasicIteratorPackage:"Basic range iterators.", lib, {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
reg_step::<INT>(lib);
|
lib.set_iterator::<StepRange<INT>>();
|
||||||
lib.set_fn_3("range", get_step_range::<INT>);
|
lib.set_fn_3("range", get_step_range::<INT>);
|
||||||
|
|
||||||
if cfg!(not(feature = "only_i32")) && cfg!(not(feature = "only_i64")) {
|
if cfg!(not(feature = "only_i32")) && cfg!(not(feature = "only_i64")) {
|
||||||
macro_rules! reg_step {
|
macro_rules! reg_step {
|
||||||
($lib:expr, $x:expr, $( $y:ty ),*) => (
|
($lib:expr, $x:expr, $( $y:ty ),*) => (
|
||||||
$(
|
$(
|
||||||
reg_step::<$y>($lib);
|
$lib.set_iterator::<StepRange<$y>>();
|
||||||
$lib.set_fn_3($x, get_step_range::<$y>);
|
$lib.set_fn_3($x, get_step_range::<$y>);
|
||||||
)*
|
)*
|
||||||
)
|
)
|
||||||
|
@ -6,7 +6,8 @@ use crate::engine::Map;
|
|||||||
use crate::parser::{ImmutableString, INT};
|
use crate::parser::{ImmutableString, INT};
|
||||||
use crate::plugin::*;
|
use crate::plugin::*;
|
||||||
|
|
||||||
use crate::stdlib::vec::Vec;
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
use crate::engine::Array;
|
||||||
|
|
||||||
def_package!(crate:BasicMapPackage:"Basic object map utilities.", lib, {
|
def_package!(crate:BasicMapPackage:"Basic object map utilities.", lib, {
|
||||||
combine_with_exported_module!(lib, "map", map_functions);
|
combine_with_exported_module!(lib, "map", map_functions);
|
||||||
@ -47,10 +48,10 @@ mod map_functions {
|
|||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
pub mod indexing {
|
pub mod indexing {
|
||||||
pub fn keys(map: &mut Map) -> Vec<Dynamic> {
|
pub fn keys(map: &mut Map) -> Array {
|
||||||
map.iter().map(|(k, _)| k.clone().into()).collect()
|
map.iter().map(|(k, _)| k.clone().into()).collect()
|
||||||
}
|
}
|
||||||
pub fn values(map: &mut Map) -> Vec<Dynamic> {
|
pub fn values(map: &mut Map) -> Array {
|
||||||
map.iter().map(|(_, v)| v.clone()).collect()
|
map.iter().map(|(_, v)| v.clone()).collect()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -108,9 +108,7 @@ def_package!(crate:MoreStringPackage:"Additional string utilities, including str
|
|||||||
// Register string iterator
|
// Register string iterator
|
||||||
lib.set_iter(
|
lib.set_iter(
|
||||||
TypeId::of::<ImmutableString>(),
|
TypeId::of::<ImmutableString>(),
|
||||||
|arr| Box::new(
|
|s: Dynamic| Box::new(s.cast::<ImmutableString>().chars().collect::<Vec<_>>().into_iter().map(Into::into))
|
||||||
arr.cast::<ImmutableString>().chars().collect::<Vec<_>>().into_iter().map(Into::into)
|
|
||||||
) as Box<dyn Iterator<Item = Dynamic>>,
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ mod inner {
|
|||||||
pub use core_error as error;
|
pub use core_error as error;
|
||||||
|
|
||||||
pub mod collections {
|
pub mod collections {
|
||||||
pub use hashbrown::{HashMap, HashSet};
|
pub use hashbrown::{hash_map, hash_set, HashMap, HashSet};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
12
src/token.rs
12
src/token.rs
@ -617,15 +617,15 @@ impl Token {
|
|||||||
|
|
||||||
EqualsTo | NotEqualsTo => 90,
|
EqualsTo | NotEqualsTo => 90,
|
||||||
|
|
||||||
LessThan | LessThanEqualsTo | GreaterThan | GreaterThanEqualsTo => 110,
|
In => 110,
|
||||||
|
|
||||||
In => 130,
|
LessThan | LessThanEqualsTo | GreaterThan | GreaterThanEqualsTo => 130,
|
||||||
|
|
||||||
Plus | Minus => 150,
|
Plus | Minus => 150,
|
||||||
|
|
||||||
Divide | Multiply => 180,
|
Divide | Multiply | Modulo => 180,
|
||||||
|
|
||||||
PowerOf | Modulo => 190,
|
PowerOf => 190,
|
||||||
|
|
||||||
LeftShift | RightShift => 210,
|
LeftShift | RightShift => 210,
|
||||||
|
|
||||||
@ -1231,6 +1231,10 @@ fn get_next_token_inner(
|
|||||||
('-', _) if !state.non_unary => return Some((Token::UnaryMinus, start_pos)),
|
('-', _) if !state.non_unary => return Some((Token::UnaryMinus, start_pos)),
|
||||||
('-', _) => return Some((Token::Minus, start_pos)),
|
('-', _) => return Some((Token::Minus, start_pos)),
|
||||||
|
|
||||||
|
('*', '*') => {
|
||||||
|
eat_next(stream, pos);
|
||||||
|
return Some((Token::Reserved("**".into()), start_pos));
|
||||||
|
}
|
||||||
('*', ')') => {
|
('*', ')') => {
|
||||||
eat_next(stream, pos);
|
eat_next(stream, pos);
|
||||||
return Some((Token::Reserved("*)".into()), start_pos));
|
return Some((Token::Reserved("*)".into()), start_pos));
|
||||||
|
@ -61,10 +61,10 @@ fn test_for_object() -> Result<(), Box<EvalAltResult>> {
|
|||||||
let keys = "";
|
let keys = "";
|
||||||
let map = #{a: 1, b: 2, c: 3};
|
let map = #{a: 1, b: 2, c: 3};
|
||||||
|
|
||||||
for key in keys(map) {
|
for key in map.keys() {
|
||||||
keys += key;
|
keys += key;
|
||||||
}
|
}
|
||||||
for value in values(map) {
|
for value in map.values() {
|
||||||
sum += value;
|
sum += value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user