commit
8805f02a8f
@ -10,6 +10,8 @@ Bug fixes
|
|||||||
* Integer numbers that are too large to deserialize into `INT` now fall back to `Decimal` or `FLOAT` instead of silently truncating.
|
* Integer numbers that are too large to deserialize into `INT` now fall back to `Decimal` or `FLOAT` instead of silently truncating.
|
||||||
* Parsing deeply-nested closures (e.g. `||{||{||{||{||{||{||{...}}}}}}}`) no longer panics but will be confined to the nesting limit.
|
* Parsing deeply-nested closures (e.g. `||{||{||{||{||{||{||{...}}}}}}}`) no longer panics but will be confined to the nesting limit.
|
||||||
* Closures containing a single expression are now allowed in `Engine::eval_expression` etc.
|
* Closures containing a single expression are now allowed in `Engine::eval_expression` etc.
|
||||||
|
* Strings interpolation now works under `Engine::new_raw` without any standard package.
|
||||||
|
* `Fn` now throws an error if the name is a reserved keyword as it cannot possibly map to such a function. This also disallows creating function pointers to custom operators which are defined as disabled keywords (a mouthful), but such custom operators are designed primarily to be used as operators.
|
||||||
|
|
||||||
Breaking API changes
|
Breaking API changes
|
||||||
--------------------
|
--------------------
|
||||||
@ -57,8 +59,10 @@ Net features
|
|||||||
|
|
||||||
### Enhanced array API
|
### Enhanced array API
|
||||||
|
|
||||||
* Array methods that take a function pointer, usually a closure (e.g. `map`, `filter`, `index_of` etc.), can now provide a closure with one few parameter but binds the first parameter to `this`.
|
* Array methods that take a function pointer, usually a closure (e.g. `map`, `filter`, `index_of`, `reduce` etc.), can now bind the array element to `this` when calling a closure.
|
||||||
* This vastly improves performance when working with arrays of object maps by avoiding unnecessary cloning of large types.
|
* This vastly improves performance when working with arrays of large types (e.g. object maps) by avoiding unnecessary cloning.
|
||||||
|
* `find` and `find_map` are added for arrays.
|
||||||
|
* `for_each` is also added for arrays, allowing a closure to mutate array elements (bound to `this`) in turn.
|
||||||
|
|
||||||
Enhancements
|
Enhancements
|
||||||
------------
|
------------
|
||||||
@ -77,7 +81,6 @@ Enhancements
|
|||||||
* `FnPtr::iter_curry` and `FnPtr::iter_curry_mut` are added.
|
* `FnPtr::iter_curry` and `FnPtr::iter_curry_mut` are added.
|
||||||
* `Dynamic::deep_scan` is added to recursively scan for `Dynamic` values.
|
* `Dynamic::deep_scan` is added to recursively scan for `Dynamic` values.
|
||||||
* `>>` and `<<` operators on integers no longer throw errors when the number of bits to shift is out of bounds. Shifting by a negative number of bits simply reverses the shift direction.
|
* `>>` and `<<` operators on integers no longer throw errors when the number of bits to shift is out of bounds. Shifting by a negative number of bits simply reverses the shift direction.
|
||||||
* `find` and `find_map` are added for arrays.
|
|
||||||
|
|
||||||
|
|
||||||
Version 1.11.0
|
Version 1.11.0
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
use crate::ast::Expr;
|
use crate::ast::Expr;
|
||||||
use crate::func::SendSync;
|
use crate::func::SendSync;
|
||||||
use crate::parser::ParseResult;
|
use crate::parser::ParseResult;
|
||||||
use crate::tokenizer::{is_valid_identifier, Token, NO_TOKEN};
|
use crate::tokenizer::{is_reserved_keyword_or_symbol, is_valid_identifier, Token};
|
||||||
use crate::types::dynamic::Variant;
|
use crate::types::dynamic::Variant;
|
||||||
use crate::{
|
use crate::{
|
||||||
Dynamic, Engine, EvalContext, Identifier, ImmutableString, LexError, Position, RhaiResult,
|
Dynamic, Engine, EvalContext, Identifier, ImmutableString, LexError, Position, RhaiResult,
|
||||||
@ -231,11 +231,11 @@ impl Engine {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let token = Token::lookup_symbol_from_syntax(s).unwrap_or_else(|| {
|
let token = Token::lookup_symbol_from_syntax(s).or_else(|| {
|
||||||
if Token::is_reserved_keyword(s) {
|
if is_reserved_keyword_or_symbol(s) {
|
||||||
Token::Reserved(Box::new(s.into()))
|
Some(Token::Reserved(Box::new(s.into())))
|
||||||
} else {
|
} else {
|
||||||
NO_TOKEN
|
None
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -256,13 +256,13 @@ impl Engine {
|
|||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
CUSTOM_SYNTAX_MARKER_FLOAT if !segments.is_empty() => s.into(),
|
CUSTOM_SYNTAX_MARKER_FLOAT if !segments.is_empty() => s.into(),
|
||||||
// Standard or reserved keyword/symbol not in first position
|
// Standard or reserved keyword/symbol not in first position
|
||||||
_ if !segments.is_empty() && token != NO_TOKEN => {
|
_ if !segments.is_empty() && token.is_some() => {
|
||||||
// Make it a custom keyword/symbol if it is disabled or reserved
|
// Make it a custom keyword/symbol if it is disabled or reserved
|
||||||
if (self
|
if (self
|
||||||
.disabled_symbols
|
.disabled_symbols
|
||||||
.as_deref()
|
.as_deref()
|
||||||
.map_or(false, |m| m.contains(s))
|
.map_or(false, |m| m.contains(s))
|
||||||
|| token.is_reserved())
|
|| token.as_ref().map_or(false, Token::is_reserved))
|
||||||
&& !self
|
&& !self
|
||||||
.custom_keywords
|
.custom_keywords
|
||||||
.as_deref()
|
.as_deref()
|
||||||
@ -276,7 +276,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
// Standard keyword in first position but not disabled
|
// Standard keyword in first position but not disabled
|
||||||
_ if segments.is_empty()
|
_ if segments.is_empty()
|
||||||
&& token.is_standard_keyword()
|
&& token.as_ref().map_or(false, Token::is_standard_keyword)
|
||||||
&& !self
|
&& !self
|
||||||
.disabled_symbols
|
.disabled_symbols
|
||||||
.as_deref()
|
.as_deref()
|
||||||
@ -298,7 +298,7 @@ impl Engine {
|
|||||||
.disabled_symbols
|
.disabled_symbols
|
||||||
.as_deref()
|
.as_deref()
|
||||||
.map_or(false, |m| m.contains(s))
|
.map_or(false, |m| m.contains(s))
|
||||||
|| (token.is_reserved()
|
|| (token.as_ref().map_or(false, Token::is_reserved)
|
||||||
&& !self
|
&& !self
|
||||||
.custom_keywords
|
.custom_keywords
|
||||||
.as_deref()
|
.as_deref()
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
use super::{ASTFlags, ASTNode, Ident, Namespace, Stmt, StmtBlock};
|
use super::{ASTFlags, ASTNode, Ident, Namespace, Stmt, StmtBlock};
|
||||||
use crate::engine::{KEYWORD_FN_PTR, OP_EXCLUSIVE_RANGE, OP_INCLUSIVE_RANGE};
|
use crate::engine::{KEYWORD_FN_PTR, OP_EXCLUSIVE_RANGE, OP_INCLUSIVE_RANGE};
|
||||||
use crate::func::hashing::ALT_ZERO_HASH;
|
use crate::func::hashing::ALT_ZERO_HASH;
|
||||||
use crate::tokenizer::{Token, NO_TOKEN};
|
use crate::tokenizer::Token;
|
||||||
use crate::types::dynamic::Union;
|
use crate::types::dynamic::Union;
|
||||||
use crate::{
|
use crate::{
|
||||||
calc_fn_hash, Dynamic, FnPtr, Identifier, ImmutableString, Position, SmartString, StaticVec,
|
calc_fn_hash, Dynamic, FnPtr, Identifier, ImmutableString, Position, SmartString, StaticVec,
|
||||||
@ -207,8 +207,7 @@ pub struct FnCallExpr {
|
|||||||
/// Does this function call capture the parent scope?
|
/// Does this function call capture the parent scope?
|
||||||
pub capture_parent_scope: bool,
|
pub capture_parent_scope: bool,
|
||||||
/// Is this function call a native operator?
|
/// Is this function call a native operator?
|
||||||
/// Otherwise set to [`Token::NONE`].
|
pub op_token: Option<Token>,
|
||||||
pub op_token: Token,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for FnCallExpr {
|
impl fmt::Debug for FnCallExpr {
|
||||||
@ -222,7 +221,7 @@ impl fmt::Debug for FnCallExpr {
|
|||||||
ff.field("hash", &self.hashes)
|
ff.field("hash", &self.hashes)
|
||||||
.field("name", &self.name)
|
.field("name", &self.name)
|
||||||
.field("args", &self.args);
|
.field("args", &self.args);
|
||||||
if self.op_token != NO_TOKEN {
|
if self.op_token.is_some() {
|
||||||
ff.field("op_token", &self.op_token);
|
ff.field("op_token", &self.op_token);
|
||||||
}
|
}
|
||||||
if self.capture_parent_scope {
|
if self.capture_parent_scope {
|
||||||
@ -582,7 +581,7 @@ impl Expr {
|
|||||||
hashes: calc_fn_hash(None, f.fn_name(), 1).into(),
|
hashes: calc_fn_hash(None, f.fn_name(), 1).into(),
|
||||||
args: once(Self::StringConstant(f.fn_name().into(), pos)).collect(),
|
args: once(Self::StringConstant(f.fn_name().into(), pos)).collect(),
|
||||||
capture_parent_scope: false,
|
capture_parent_scope: false,
|
||||||
op_token: NO_TOKEN,
|
op_token: None,
|
||||||
}
|
}
|
||||||
.into(),
|
.into(),
|
||||||
pos,
|
pos,
|
||||||
|
@ -52,11 +52,6 @@ pub const FN_ANONYMOUS: &str = "anon$";
|
|||||||
/// function to compare two [`Dynamic`] values.
|
/// function to compare two [`Dynamic`] values.
|
||||||
pub const OP_EQUALS: &str = Token::EqualsTo.literal_syntax();
|
pub const OP_EQUALS: &str = Token::EqualsTo.literal_syntax();
|
||||||
|
|
||||||
/// Standard concatenation operator.
|
|
||||||
///
|
|
||||||
/// Used primarily to build up interpolated strings.
|
|
||||||
pub const OP_CONCAT: &str = Token::PlusAssign.literal_syntax();
|
|
||||||
|
|
||||||
/// Standard containment testing function.
|
/// Standard containment testing function.
|
||||||
///
|
///
|
||||||
/// The `in` operator is implemented as a call to this function.
|
/// The `in` operator is implemented as a call to this function.
|
||||||
|
@ -5,7 +5,6 @@ use super::{Caches, GlobalRuntimeState, Target};
|
|||||||
use crate::ast::{ASTFlags, BinaryExpr, Expr, OpAssignment};
|
use crate::ast::{ASTFlags, BinaryExpr, Expr, OpAssignment};
|
||||||
use crate::config::hashing::SusLock;
|
use crate::config::hashing::SusLock;
|
||||||
use crate::engine::{FN_IDX_GET, FN_IDX_SET};
|
use crate::engine::{FN_IDX_GET, FN_IDX_SET};
|
||||||
use crate::tokenizer::NO_TOKEN;
|
|
||||||
use crate::types::dynamic::Union;
|
use crate::types::dynamic::Union;
|
||||||
use crate::{
|
use crate::{
|
||||||
calc_fn_hash, Dynamic, Engine, FnArgsVec, Position, RhaiResult, RhaiResultOf, Scope, ERR,
|
calc_fn_hash, Dynamic, Engine, FnArgsVec, Position, RhaiResult, RhaiResultOf, Scope, ERR,
|
||||||
@ -73,7 +72,7 @@ impl Engine {
|
|||||||
let hash = hash_idx().0;
|
let hash = hash_idx().0;
|
||||||
let args = &mut [target, idx];
|
let args = &mut [target, idx];
|
||||||
|
|
||||||
self.exec_native_fn_call(global, caches, FN_IDX_GET, NO_TOKEN, hash, args, true, pos)
|
self.exec_native_fn_call(global, caches, FN_IDX_GET, None, hash, args, true, pos)
|
||||||
.map(|(r, ..)| r)
|
.map(|(r, ..)| r)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,7 +94,7 @@ impl Engine {
|
|||||||
let args = &mut [target, idx, new_val];
|
let args = &mut [target, idx, new_val];
|
||||||
|
|
||||||
self.exec_native_fn_call(
|
self.exec_native_fn_call(
|
||||||
global, caches, FN_IDX_SET, NO_TOKEN, hash, args, is_ref_mut, pos,
|
global, caches, FN_IDX_SET, None, hash, args, is_ref_mut, pos,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -751,8 +750,7 @@ impl Engine {
|
|||||||
|
|
||||||
let (mut orig_val, ..) = self
|
let (mut orig_val, ..) = self
|
||||||
.exec_native_fn_call(
|
.exec_native_fn_call(
|
||||||
global, caches, getter, NO_TOKEN, *hash_get, args, is_ref_mut,
|
global, caches, getter, None, *hash_get, args, is_ref_mut, *pos,
|
||||||
*pos,
|
|
||||||
)
|
)
|
||||||
.or_else(|err| match *err {
|
.or_else(|err| match *err {
|
||||||
// Try an indexer if property does not exist
|
// Try an indexer if property does not exist
|
||||||
@ -786,7 +784,7 @@ impl Engine {
|
|||||||
let args = &mut [target.as_mut(), &mut new_val];
|
let args = &mut [target.as_mut(), &mut new_val];
|
||||||
|
|
||||||
self.exec_native_fn_call(
|
self.exec_native_fn_call(
|
||||||
global, caches, setter, NO_TOKEN, *hash_set, args, is_ref_mut, *pos,
|
global, caches, setter, None, *hash_set, args, is_ref_mut, *pos,
|
||||||
)
|
)
|
||||||
.or_else(|err| match *err {
|
.or_else(|err| match *err {
|
||||||
// Try an indexer if property does not exist
|
// Try an indexer if property does not exist
|
||||||
@ -813,7 +811,7 @@ impl Engine {
|
|||||||
let args = &mut [target.as_mut()];
|
let args = &mut [target.as_mut()];
|
||||||
|
|
||||||
self.exec_native_fn_call(
|
self.exec_native_fn_call(
|
||||||
global, caches, getter, NO_TOKEN, *hash_get, args, is_ref_mut, *pos,
|
global, caches, getter, None, *hash_get, args, is_ref_mut, *pos,
|
||||||
)
|
)
|
||||||
.map_or_else(
|
.map_or_else(
|
||||||
|err| match *err {
|
|err| match *err {
|
||||||
@ -904,8 +902,8 @@ impl Engine {
|
|||||||
// Assume getters are always pure
|
// Assume getters are always pure
|
||||||
let (mut val, ..) = self
|
let (mut val, ..) = self
|
||||||
.exec_native_fn_call(
|
.exec_native_fn_call(
|
||||||
global, caches, getter, NO_TOKEN, *hash_get, args,
|
global, caches, getter, None, *hash_get, args, is_ref_mut,
|
||||||
is_ref_mut, pos,
|
pos,
|
||||||
)
|
)
|
||||||
.or_else(|err| match *err {
|
.or_else(|err| match *err {
|
||||||
// Try an indexer if property does not exist
|
// Try an indexer if property does not exist
|
||||||
@ -940,7 +938,7 @@ impl Engine {
|
|||||||
// The return value is thrown away and not used.
|
// The return value is thrown away and not used.
|
||||||
let _ = self
|
let _ = self
|
||||||
.exec_native_fn_call(
|
.exec_native_fn_call(
|
||||||
global, caches, setter, NO_TOKEN, *hash_set, args,
|
global, caches, setter, None, *hash_set, args,
|
||||||
is_ref_mut, pos,
|
is_ref_mut, pos,
|
||||||
)
|
)
|
||||||
.or_else(|err| match *err {
|
.or_else(|err| match *err {
|
||||||
|
@ -9,18 +9,18 @@ use std::borrow::Borrow;
|
|||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
|
|
||||||
impl Dynamic {
|
impl Dynamic {
|
||||||
/// Recursively calculate the sizes of a value.
|
/// Recursively calculate the sizes of an array.
|
||||||
///
|
///
|
||||||
/// Sizes returned are `(` [`Array`][crate::Array], [`Map`][crate::Map] and [`String`] `)`.
|
/// Sizes returned are `(` [`Array`][crate::Array], [`Map`][crate::Map] and [`String`] `)`.
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// Panics if any interior data is shared (should never happen).
|
/// Panics if any interior data is shared (should never happen).
|
||||||
pub(crate) fn calc_data_sizes(&self, _top: bool) -> (usize, usize, usize) {
|
|
||||||
match self.0 {
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Union::Array(ref arr, ..) => {
|
#[inline]
|
||||||
arr.iter()
|
pub(crate) fn calc_array_sizes(array: &crate::Array, _top: bool) -> (usize, usize, usize) {
|
||||||
|
array
|
||||||
|
.iter()
|
||||||
.fold((0, 0, 0), |(ax, mx, sx), value| match value.0 {
|
.fold((0, 0, 0), |(ax, mx, sx), value| match value.0 {
|
||||||
Union::Array(..) => {
|
Union::Array(..) => {
|
||||||
let (a, m, s) = value.calc_data_sizes(false);
|
let (a, m, s) = value.calc_data_sizes(false);
|
||||||
@ -36,10 +36,16 @@ impl Dynamic {
|
|||||||
_ => (ax + 1, mx, sx),
|
_ => (ax + 1, mx, sx),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "no_index"))]
|
/// Recursively calculate the sizes of a map.
|
||||||
Union::Blob(ref blob, ..) => (blob.len(), 0, 0),
|
///
|
||||||
|
/// Sizes returned are `(` [`Array`][crate::Array], [`Map`][crate::Map] and [`String`] `)`.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if any interior data is shared (should never happen).
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Union::Map(ref map, ..) => {
|
#[inline]
|
||||||
|
pub(crate) fn calc_map_sizes(map: &crate::Map, _top: bool) -> (usize, usize, usize) {
|
||||||
map.values()
|
map.values()
|
||||||
.fold((0, 0, 0), |(ax, mx, sx), value| match value.0 {
|
.fold((0, 0, 0), |(ax, mx, sx), value| match value.0 {
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
@ -57,6 +63,22 @@ impl Dynamic {
|
|||||||
_ => (ax, mx + 1, sx),
|
_ => (ax, mx + 1, sx),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
/// Recursively calculate the sizes of a value.
|
||||||
|
///
|
||||||
|
/// Sizes returned are `(` [`Array`][crate::Array], [`Map`][crate::Map] and [`String`] `)`.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if any interior data is shared (should never happen).
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn calc_data_sizes(&self, _top: bool) -> (usize, usize, usize) {
|
||||||
|
match self.0 {
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
Union::Array(ref arr, ..) => Self::calc_array_sizes(&**arr, _top),
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
Union::Blob(ref blob, ..) => (blob.len(), 0, 0),
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
Union::Map(ref map, ..) => Self::calc_map_sizes(&**map, _top),
|
||||||
Union::Str(ref s, ..) => (0, 0, s.len()),
|
Union::Str(ref s, ..) => (0, 0, s.len()),
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Union::Shared(..) if _top => self.read_lock::<Self>().unwrap().calc_data_sizes(true),
|
Union::Shared(..) if _top => self.read_lock::<Self>().unwrap().calc_data_sizes(true),
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
//! Module defining functions for evaluating an expression.
|
//! Module defining functions for evaluating an expression.
|
||||||
|
|
||||||
use super::{Caches, EvalContext, GlobalRuntimeState, Target};
|
use super::{Caches, EvalContext, GlobalRuntimeState, Target};
|
||||||
use crate::ast::{Expr, OpAssignment};
|
use crate::ast::Expr;
|
||||||
use crate::engine::{KEYWORD_THIS, OP_CONCAT};
|
use crate::engine::KEYWORD_THIS;
|
||||||
|
use crate::packages::string_basic::{print_with_func, FUNC_TO_STRING};
|
||||||
use crate::types::dynamic::AccessMode;
|
use crate::types::dynamic::AccessMode;
|
||||||
use crate::{Dynamic, Engine, Position, RhaiResult, RhaiResultOf, Scope, ERR};
|
use crate::{Dynamic, Engine, RhaiResult, RhaiResultOf, Scope, SmartString, ERR};
|
||||||
use std::num::NonZeroUsize;
|
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
|
use std::{fmt::Write, num::NonZeroUsize};
|
||||||
|
|
||||||
impl Engine {
|
impl Engine {
|
||||||
/// Search for a module within an imports stack.
|
/// Search for a module within an imports stack.
|
||||||
@ -283,42 +284,43 @@ impl Engine {
|
|||||||
|
|
||||||
// `... ${...} ...`
|
// `... ${...} ...`
|
||||||
Expr::InterpolatedString(x, _) => {
|
Expr::InterpolatedString(x, _) => {
|
||||||
let mut concat = self.const_empty_string().into();
|
let mut concat = SmartString::new_const();
|
||||||
let target = &mut concat;
|
|
||||||
|
|
||||||
let mut op_info = OpAssignment::new_op_assignment(OP_CONCAT, Position::NONE);
|
x.iter().try_for_each(|expr| -> RhaiResultOf<()> {
|
||||||
|
let item = &mut self
|
||||||
x.iter()
|
|
||||||
.try_for_each(|expr| {
|
|
||||||
let item = self
|
|
||||||
.eval_expr(global, caches, scope, this_ptr.as_deref_mut(), expr)?
|
.eval_expr(global, caches, scope, this_ptr.as_deref_mut(), expr)?
|
||||||
.flatten();
|
.flatten();
|
||||||
|
let pos = expr.position();
|
||||||
|
|
||||||
op_info.pos = expr.start_position();
|
if item.is_string() {
|
||||||
|
write!(concat, "{item}").unwrap();
|
||||||
|
} else {
|
||||||
|
let source = global.source();
|
||||||
|
let context = &(self, FUNC_TO_STRING, source, &*global, pos).into();
|
||||||
|
let display = print_with_func(FUNC_TO_STRING, context, item);
|
||||||
|
write!(concat, "{}", display).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
self.eval_op_assignment(global, caches, &op_info, expr, target, item)
|
#[cfg(not(feature = "unchecked"))]
|
||||||
})
|
self.throw_on_size((0, 0, concat.len()))
|
||||||
.map(|_| concat.take_or_clone())
|
.map_err(|err| err.fill_position(pos))?;
|
||||||
.and_then(|r| self.check_data_size(r, expr.start_position()))
|
|
||||||
|
Ok(())
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(self.get_interned_string(concat).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Expr::Array(x, ..) => {
|
Expr::Array(x, ..) => {
|
||||||
|
let mut array = crate::Array::with_capacity(x.len());
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
let mut total_data_sizes = (0, 0, 0);
|
let mut total_data_sizes = (0, 0, 0);
|
||||||
|
|
||||||
x.iter()
|
x.iter().try_for_each(|item_expr| -> RhaiResultOf<()> {
|
||||||
.try_fold(
|
|
||||||
crate::Array::with_capacity(x.len()),
|
|
||||||
|mut array, item_expr| {
|
|
||||||
let value = self
|
let value = self
|
||||||
.eval_expr(
|
.eval_expr(global, caches, scope, this_ptr.as_deref_mut(), item_expr)?
|
||||||
global,
|
|
||||||
caches,
|
|
||||||
scope,
|
|
||||||
this_ptr.as_deref_mut(),
|
|
||||||
item_expr,
|
|
||||||
)?
|
|
||||||
.flatten();
|
.flatten();
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
@ -326,7 +328,7 @@ impl Engine {
|
|||||||
let val_sizes = value.calc_data_sizes(true);
|
let val_sizes = value.calc_data_sizes(true);
|
||||||
|
|
||||||
total_data_sizes = (
|
total_data_sizes = (
|
||||||
total_data_sizes.0 + val_sizes.0,
|
total_data_sizes.0 + val_sizes.0 + 1,
|
||||||
total_data_sizes.1 + val_sizes.1,
|
total_data_sizes.1 + val_sizes.1,
|
||||||
total_data_sizes.2 + val_sizes.2,
|
total_data_sizes.2 + val_sizes.2,
|
||||||
);
|
);
|
||||||
@ -336,19 +338,21 @@ impl Engine {
|
|||||||
|
|
||||||
array.push(value);
|
array.push(value);
|
||||||
|
|
||||||
Ok(array)
|
Ok(())
|
||||||
},
|
})?;
|
||||||
)
|
|
||||||
.map(Into::into)
|
Ok(Dynamic::from_array(array))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Expr::Map(x, ..) => {
|
Expr::Map(x, ..) => {
|
||||||
|
let mut map = x.1.clone();
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
let mut total_data_sizes = (0, 0, 0);
|
let mut total_data_sizes = (0, 0, 0);
|
||||||
|
|
||||||
x.0.iter()
|
x.0.iter()
|
||||||
.try_fold(x.1.clone(), |mut map, (key, value_expr)| {
|
.try_for_each(|(key, value_expr)| -> RhaiResultOf<()> {
|
||||||
let value = self
|
let value = self
|
||||||
.eval_expr(global, caches, scope, this_ptr.as_deref_mut(), value_expr)?
|
.eval_expr(global, caches, scope, this_ptr.as_deref_mut(), value_expr)?
|
||||||
.flatten();
|
.flatten();
|
||||||
@ -358,7 +362,7 @@ impl Engine {
|
|||||||
let delta = value.calc_data_sizes(true);
|
let delta = value.calc_data_sizes(true);
|
||||||
total_data_sizes = (
|
total_data_sizes = (
|
||||||
total_data_sizes.0 + delta.0,
|
total_data_sizes.0 + delta.0,
|
||||||
total_data_sizes.1 + delta.1,
|
total_data_sizes.1 + delta.1 + 1,
|
||||||
total_data_sizes.2 + delta.2,
|
total_data_sizes.2 + delta.2,
|
||||||
);
|
);
|
||||||
self.throw_on_size(total_data_sizes)
|
self.throw_on_size(total_data_sizes)
|
||||||
@ -367,9 +371,10 @@ impl Engine {
|
|||||||
|
|
||||||
*map.get_mut(key.as_str()).unwrap() = value;
|
*map.get_mut(key.as_str()).unwrap() = value;
|
||||||
|
|
||||||
Ok(map)
|
Ok(())
|
||||||
})
|
})?;
|
||||||
.map(Into::into)
|
|
||||||
|
Ok(Dynamic::from_map(map))
|
||||||
}
|
}
|
||||||
|
|
||||||
Expr::And(x, ..) => Ok((self
|
Expr::And(x, ..) => Ok((self
|
||||||
|
@ -140,15 +140,16 @@ impl Engine {
|
|||||||
let args = &mut [&mut *lock_guard, &mut new_val];
|
let args = &mut [&mut *lock_guard, &mut new_val];
|
||||||
|
|
||||||
if self.fast_operators() {
|
if self.fast_operators() {
|
||||||
if let Some((func, ctx)) =
|
if let Some((func, need_context)) =
|
||||||
get_builtin_op_assignment_fn(op_assign_token.clone(), args[0], args[1])
|
get_builtin_op_assignment_fn(op_assign_token.clone(), args[0], args[1])
|
||||||
{
|
{
|
||||||
// Built-in found
|
// Built-in found
|
||||||
let op = op_assign_token.literal_syntax();
|
let op = op_assign_token.literal_syntax();
|
||||||
auto_restore! { let orig_level = global.level; global.level += 1 }
|
auto_restore! { let orig_level = global.level; global.level += 1 }
|
||||||
|
|
||||||
let context = if ctx {
|
let context = if need_context {
|
||||||
Some((self, op, None, &*global, *op_pos).into())
|
let source = global.source();
|
||||||
|
Some((self, op, source, &*global, *op_pos).into())
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
@ -158,7 +159,7 @@ impl Engine {
|
|||||||
|
|
||||||
let op_assign = op_assign_token.literal_syntax();
|
let op_assign = op_assign_token.literal_syntax();
|
||||||
let op = op_token.literal_syntax();
|
let op = op_token.literal_syntax();
|
||||||
let token = op_assign_token.clone();
|
let token = Some(op_assign_token.clone());
|
||||||
|
|
||||||
match self
|
match self
|
||||||
.exec_native_fn_call(global, caches, op_assign, token, hash, args, true, *op_pos)
|
.exec_native_fn_call(global, caches, op_assign, token, hash, args, true, *op_pos)
|
||||||
@ -167,7 +168,7 @@ impl Engine {
|
|||||||
Err(err) if matches!(*err, ERR::ErrorFunctionNotFound(ref f, ..) if f.starts_with(op_assign)) =>
|
Err(err) if matches!(*err, ERR::ErrorFunctionNotFound(ref f, ..) if f.starts_with(op_assign)) =>
|
||||||
{
|
{
|
||||||
// Expand to `var = var op rhs`
|
// Expand to `var = var op rhs`
|
||||||
let token = op_token.clone();
|
let token = Some(op_token.clone());
|
||||||
|
|
||||||
*args[0] = self
|
*args[0] = self
|
||||||
.exec_native_fn_call(
|
.exec_native_fn_call(
|
||||||
|
@ -91,65 +91,65 @@ pub fn get_builtin_binary_op_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Option<F
|
|||||||
let type2 = y.type_id();
|
let type2 = y.type_id();
|
||||||
|
|
||||||
macro_rules! impl_op {
|
macro_rules! impl_op {
|
||||||
($xx:ident $op:tt $yy:ident) => { (|_, args| {
|
($xx:ident $op:tt $yy:ident) => { Some((|_, args| {
|
||||||
let x = &*args[0].read_lock::<$xx>().unwrap();
|
let x = &*args[0].read_lock::<$xx>().unwrap();
|
||||||
let y = &*args[1].read_lock::<$yy>().unwrap();
|
let y = &*args[1].read_lock::<$yy>().unwrap();
|
||||||
Ok((x $op y).into())
|
Ok((x $op y).into())
|
||||||
}, false) };
|
}, false)) };
|
||||||
($xx:ident . $func:ident ( $yy:ty )) => { (|_, args| {
|
($xx:ident . $func:ident ( $yy:ty )) => { Some((|_, args| {
|
||||||
let x = &*args[0].read_lock::<$xx>().unwrap();
|
let x = &*args[0].read_lock::<$xx>().unwrap();
|
||||||
let y = &*args[1].read_lock::<$yy>().unwrap();
|
let y = &*args[1].read_lock::<$yy>().unwrap();
|
||||||
Ok(x.$func(y).into())
|
Ok(x.$func(y).into())
|
||||||
}, false) };
|
}, false)) };
|
||||||
($xx:ident . $func:ident ( $yy:ident . $yyy:ident () )) => { (|_, args| {
|
($xx:ident . $func:ident ( $yy:ident . $yyy:ident () )) => { Some((|_, args| {
|
||||||
let x = &*args[0].read_lock::<$xx>().unwrap();
|
let x = &*args[0].read_lock::<$xx>().unwrap();
|
||||||
let y = &*args[1].read_lock::<$yy>().unwrap();
|
let y = &*args[1].read_lock::<$yy>().unwrap();
|
||||||
Ok(x.$func(y.$yyy()).into())
|
Ok(x.$func(y.$yyy()).into())
|
||||||
}, false) };
|
}, false)) };
|
||||||
($func:ident ( $op:tt )) => { (|_, args| {
|
($func:ident ( $op:tt )) => { Some((|_, args| {
|
||||||
let (x, y) = $func(args);
|
let (x, y) = $func(args);
|
||||||
Ok((x $op y).into())
|
Ok((x $op y).into())
|
||||||
}, false) };
|
}, false)) };
|
||||||
($base:ty => $xx:ident $op:tt $yy:ident) => { (|_, args| {
|
($base:ty => $xx:ident $op:tt $yy:ident) => { Some((|_, args| {
|
||||||
let x = args[0].$xx().unwrap() as $base;
|
let x = args[0].$xx().unwrap() as $base;
|
||||||
let y = args[1].$yy().unwrap() as $base;
|
let y = args[1].$yy().unwrap() as $base;
|
||||||
Ok((x $op y).into())
|
Ok((x $op y).into())
|
||||||
}, false) };
|
}, false)) };
|
||||||
($base:ty => $xx:ident . $func:ident ( $yy:ident as $yyy:ty)) => { (|_, args| {
|
($base:ty => $xx:ident . $func:ident ( $yy:ident as $yyy:ty)) => { Some((|_, args| {
|
||||||
let x = args[0].$xx().unwrap() as $base;
|
let x = args[0].$xx().unwrap() as $base;
|
||||||
let y = args[1].$yy().unwrap() as $base;
|
let y = args[1].$yy().unwrap() as $base;
|
||||||
Ok(x.$func(y as $yyy).into())
|
Ok(x.$func(y as $yyy).into())
|
||||||
}, false) };
|
}, false)) };
|
||||||
($base:ty => Ok($func:ident ( $xx:ident, $yy:ident ))) => { (|_, args| {
|
($base:ty => Ok($func:ident ( $xx:ident, $yy:ident ))) => { Some((|_, args| {
|
||||||
let x = args[0].$xx().unwrap() as $base;
|
let x = args[0].$xx().unwrap() as $base;
|
||||||
let y = args[1].$yy().unwrap() as $base;
|
let y = args[1].$yy().unwrap() as $base;
|
||||||
Ok($func(x, y).into())
|
Ok($func(x, y).into())
|
||||||
}, false) };
|
}, false)) };
|
||||||
($base:ty => $func:ident ( $xx:ident, $yy:ident )) => { (|_, args| {
|
($base:ty => $func:ident ( $xx:ident, $yy:ident )) => { Some((|_, args| {
|
||||||
let x = args[0].$xx().unwrap() as $base;
|
let x = args[0].$xx().unwrap() as $base;
|
||||||
let y = args[1].$yy().unwrap() as $base;
|
let y = args[1].$yy().unwrap() as $base;
|
||||||
$func(x, y).map(Into::into)
|
$func(x, y).map(Into::into)
|
||||||
}, false) };
|
}, false)) };
|
||||||
(from $base:ty => $xx:ident $op:tt $yy:ident) => { (|_, args| {
|
(from $base:ty => $xx:ident $op:tt $yy:ident) => { Some((|_, args| {
|
||||||
let x = <$base>::from(args[0].$xx().unwrap());
|
let x = <$base>::from(args[0].$xx().unwrap());
|
||||||
let y = <$base>::from(args[1].$yy().unwrap());
|
let y = <$base>::from(args[1].$yy().unwrap());
|
||||||
Ok((x $op y).into())
|
Ok((x $op y).into())
|
||||||
}, false) };
|
}, false)) };
|
||||||
(from $base:ty => $xx:ident . $func:ident ( $yy:ident )) => { (|_, args| {
|
(from $base:ty => $xx:ident . $func:ident ( $yy:ident )) => { Some((|_, args| {
|
||||||
let x = <$base>::from(args[0].$xx().unwrap());
|
let x = <$base>::from(args[0].$xx().unwrap());
|
||||||
let y = <$base>::from(args[1].$yy().unwrap());
|
let y = <$base>::from(args[1].$yy().unwrap());
|
||||||
Ok(x.$func(y).into())
|
Ok(x.$func(y).into())
|
||||||
}, false) };
|
}, false)) };
|
||||||
(from $base:ty => Ok($func:ident ( $xx:ident, $yy:ident ))) => { (|_, args| {
|
(from $base:ty => Ok($func:ident ( $xx:ident, $yy:ident ))) => { Some((|_, args| {
|
||||||
let x = <$base>::from(args[0].$xx().unwrap());
|
let x = <$base>::from(args[0].$xx().unwrap());
|
||||||
let y = <$base>::from(args[1].$yy().unwrap());
|
let y = <$base>::from(args[1].$yy().unwrap());
|
||||||
Ok($func(x, y).into())
|
Ok($func(x, y).into())
|
||||||
}, false) };
|
}, false)) };
|
||||||
(from $base:ty => $func:ident ( $xx:ident, $yy:ident )) => { (|_, args| {
|
(from $base:ty => $func:ident ( $xx:ident, $yy:ident )) => { Some((|_, args| {
|
||||||
let x = <$base>::from(args[0].$xx().unwrap());
|
let x = <$base>::from(args[0].$xx().unwrap());
|
||||||
let y = <$base>::from(args[1].$yy().unwrap());
|
let y = <$base>::from(args[1].$yy().unwrap());
|
||||||
$func(x, y).map(Into::into)
|
$func(x, y).map(Into::into)
|
||||||
}, false) };
|
}, false)) };
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for common patterns
|
// Check for common patterns
|
||||||
@ -161,25 +161,25 @@ pub fn get_builtin_binary_op_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Option<F
|
|||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
match op {
|
match op {
|
||||||
Plus => return Some(impl_op!(INT => add(as_int, as_int))),
|
Plus => return impl_op!(INT => add(as_int, as_int)),
|
||||||
Minus => return Some(impl_op!(INT => subtract(as_int, as_int))),
|
Minus => return impl_op!(INT => subtract(as_int, as_int)),
|
||||||
Multiply => return Some(impl_op!(INT => multiply(as_int, as_int))),
|
Multiply => return impl_op!(INT => multiply(as_int, as_int)),
|
||||||
Divide => return Some(impl_op!(INT => divide(as_int, as_int))),
|
Divide => return impl_op!(INT => divide(as_int, as_int)),
|
||||||
Modulo => return Some(impl_op!(INT => modulo(as_int, as_int))),
|
Modulo => return impl_op!(INT => modulo(as_int, as_int)),
|
||||||
PowerOf => return Some(impl_op!(INT => power(as_int, as_int))),
|
PowerOf => return impl_op!(INT => power(as_int, as_int)),
|
||||||
RightShift => return Some(impl_op!(INT => Ok(shift_right(as_int, as_int)))),
|
RightShift => return impl_op!(INT => Ok(shift_right(as_int, as_int))),
|
||||||
LeftShift => return Some(impl_op!(INT => Ok(shift_left(as_int, as_int)))),
|
LeftShift => return impl_op!(INT => Ok(shift_left(as_int, as_int))),
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "unchecked")]
|
#[cfg(feature = "unchecked")]
|
||||||
match op {
|
match op {
|
||||||
Plus => return Some(impl_op!(INT => as_int + as_int)),
|
Plus => return impl_op!(INT => as_int + as_int),
|
||||||
Minus => return Some(impl_op!(INT => as_int - as_int)),
|
Minus => return impl_op!(INT => as_int - as_int),
|
||||||
Multiply => return Some(impl_op!(INT => as_int * as_int)),
|
Multiply => return impl_op!(INT => as_int * as_int),
|
||||||
Divide => return Some(impl_op!(INT => as_int / as_int)),
|
Divide => return impl_op!(INT => as_int / as_int),
|
||||||
Modulo => return Some(impl_op!(INT => as_int % as_int)),
|
Modulo => return impl_op!(INT => as_int % as_int),
|
||||||
PowerOf => return Some(impl_op!(INT => as_int.pow(as_int as u32))),
|
PowerOf => return impl_op!(INT => as_int.pow(as_int as u32)),
|
||||||
RightShift => {
|
RightShift => {
|
||||||
return Some((
|
return Some((
|
||||||
|_, args| {
|
|_, args| {
|
||||||
@ -204,32 +204,32 @@ pub fn get_builtin_binary_op_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Option<F
|
|||||||
}
|
}
|
||||||
|
|
||||||
return match op {
|
return match op {
|
||||||
EqualsTo => Some(impl_op!(INT => as_int == as_int)),
|
EqualsTo => impl_op!(INT => as_int == as_int),
|
||||||
NotEqualsTo => Some(impl_op!(INT => as_int != as_int)),
|
NotEqualsTo => impl_op!(INT => as_int != as_int),
|
||||||
GreaterThan => Some(impl_op!(INT => as_int > as_int)),
|
GreaterThan => impl_op!(INT => as_int > as_int),
|
||||||
GreaterThanEqualsTo => Some(impl_op!(INT => as_int >= as_int)),
|
GreaterThanEqualsTo => impl_op!(INT => as_int >= as_int),
|
||||||
LessThan => Some(impl_op!(INT => as_int < as_int)),
|
LessThan => impl_op!(INT => as_int < as_int),
|
||||||
LessThanEqualsTo => Some(impl_op!(INT => as_int <= as_int)),
|
LessThanEqualsTo => impl_op!(INT => as_int <= as_int),
|
||||||
Ampersand => Some(impl_op!(INT => as_int & as_int)),
|
Ampersand => impl_op!(INT => as_int & as_int),
|
||||||
Pipe => Some(impl_op!(INT => as_int | as_int)),
|
Pipe => impl_op!(INT => as_int | as_int),
|
||||||
XOr => Some(impl_op!(INT => as_int ^ as_int)),
|
XOr => impl_op!(INT => as_int ^ as_int),
|
||||||
ExclusiveRange => Some(impl_op!(INT => as_int .. as_int)),
|
ExclusiveRange => impl_op!(INT => as_int .. as_int),
|
||||||
InclusiveRange => Some(impl_op!(INT => as_int ..= as_int)),
|
InclusiveRange => impl_op!(INT => as_int ..= as_int),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if type1 == TypeId::of::<bool>() {
|
if type1 == TypeId::of::<bool>() {
|
||||||
return match op {
|
return match op {
|
||||||
EqualsTo => Some(impl_op!(bool => as_bool == as_bool)),
|
EqualsTo => impl_op!(bool => as_bool == as_bool),
|
||||||
NotEqualsTo => Some(impl_op!(bool => as_bool != as_bool)),
|
NotEqualsTo => impl_op!(bool => as_bool != as_bool),
|
||||||
GreaterThan => Some(impl_op!(bool => as_bool > as_bool)),
|
GreaterThan => impl_op!(bool => as_bool > as_bool),
|
||||||
GreaterThanEqualsTo => Some(impl_op!(bool => as_bool >= as_bool)),
|
GreaterThanEqualsTo => impl_op!(bool => as_bool >= as_bool),
|
||||||
LessThan => Some(impl_op!(bool => as_bool < as_bool)),
|
LessThan => impl_op!(bool => as_bool < as_bool),
|
||||||
LessThanEqualsTo => Some(impl_op!(bool => as_bool <= as_bool)),
|
LessThanEqualsTo => impl_op!(bool => as_bool <= as_bool),
|
||||||
Ampersand => Some(impl_op!(bool => as_bool & as_bool)),
|
Ampersand => impl_op!(bool => as_bool & as_bool),
|
||||||
Pipe => Some(impl_op!(bool => as_bool | as_bool)),
|
Pipe => impl_op!(bool => as_bool | as_bool),
|
||||||
XOr => Some(impl_op!(bool => as_bool ^ as_bool)),
|
XOr => impl_op!(bool => as_bool ^ as_bool),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -250,13 +250,13 @@ pub fn get_builtin_binary_op_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Option<F
|
|||||||
},
|
},
|
||||||
CHECKED_BUILD,
|
CHECKED_BUILD,
|
||||||
)),
|
)),
|
||||||
Minus => Some(impl_op!(ImmutableString - ImmutableString)),
|
Minus => impl_op!(ImmutableString - ImmutableString),
|
||||||
EqualsTo => Some(impl_op!(ImmutableString == ImmutableString)),
|
EqualsTo => impl_op!(ImmutableString == ImmutableString),
|
||||||
NotEqualsTo => Some(impl_op!(ImmutableString != ImmutableString)),
|
NotEqualsTo => impl_op!(ImmutableString != ImmutableString),
|
||||||
GreaterThan => Some(impl_op!(ImmutableString > ImmutableString)),
|
GreaterThan => impl_op!(ImmutableString > ImmutableString),
|
||||||
GreaterThanEqualsTo => Some(impl_op!(ImmutableString >= ImmutableString)),
|
GreaterThanEqualsTo => impl_op!(ImmutableString >= ImmutableString),
|
||||||
LessThan => Some(impl_op!(ImmutableString < ImmutableString)),
|
LessThan => impl_op!(ImmutableString < ImmutableString),
|
||||||
LessThanEqualsTo => Some(impl_op!(ImmutableString <= ImmutableString)),
|
LessThanEqualsTo => impl_op!(ImmutableString <= ImmutableString),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -279,12 +279,12 @@ pub fn get_builtin_binary_op_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Option<F
|
|||||||
},
|
},
|
||||||
CHECKED_BUILD,
|
CHECKED_BUILD,
|
||||||
)),
|
)),
|
||||||
EqualsTo => Some(impl_op!(char => as_char == as_char)),
|
EqualsTo => impl_op!(char => as_char == as_char),
|
||||||
NotEqualsTo => Some(impl_op!(char => as_char != as_char)),
|
NotEqualsTo => impl_op!(char => as_char != as_char),
|
||||||
GreaterThan => Some(impl_op!(char => as_char > as_char)),
|
GreaterThan => impl_op!(char => as_char > as_char),
|
||||||
GreaterThanEqualsTo => Some(impl_op!(char => as_char >= as_char)),
|
GreaterThanEqualsTo => impl_op!(char => as_char >= as_char),
|
||||||
LessThan => Some(impl_op!(char => as_char < as_char)),
|
LessThan => impl_op!(char => as_char < as_char),
|
||||||
LessThanEqualsTo => Some(impl_op!(char => as_char <= as_char)),
|
LessThanEqualsTo => impl_op!(char => as_char <= as_char),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -316,8 +316,8 @@ pub fn get_builtin_binary_op_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Option<F
|
|||||||
},
|
},
|
||||||
CHECKED_BUILD,
|
CHECKED_BUILD,
|
||||||
)),
|
)),
|
||||||
EqualsTo => Some(impl_op!(Blob == Blob)),
|
EqualsTo => impl_op!(Blob == Blob),
|
||||||
NotEqualsTo => Some(impl_op!(Blob != Blob)),
|
NotEqualsTo => impl_op!(Blob != Blob),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -338,18 +338,18 @@ pub fn get_builtin_binary_op_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Option<F
|
|||||||
($x:ty, $xx:ident, $y:ty, $yy:ident) => {
|
($x:ty, $xx:ident, $y:ty, $yy:ident) => {
|
||||||
if (type1, type2) == (TypeId::of::<$x>(), TypeId::of::<$y>()) {
|
if (type1, type2) == (TypeId::of::<$x>(), TypeId::of::<$y>()) {
|
||||||
return match op {
|
return match op {
|
||||||
Plus => Some(impl_op!(FLOAT => $xx + $yy)),
|
Plus => impl_op!(FLOAT => $xx + $yy),
|
||||||
Minus => Some(impl_op!(FLOAT => $xx - $yy)),
|
Minus => impl_op!(FLOAT => $xx - $yy),
|
||||||
Multiply => Some(impl_op!(FLOAT => $xx * $yy)),
|
Multiply => impl_op!(FLOAT => $xx * $yy),
|
||||||
Divide => Some(impl_op!(FLOAT => $xx / $yy)),
|
Divide => impl_op!(FLOAT => $xx / $yy),
|
||||||
Modulo => Some(impl_op!(FLOAT => $xx % $yy)),
|
Modulo => impl_op!(FLOAT => $xx % $yy),
|
||||||
PowerOf => Some(impl_op!(FLOAT => $xx.powf($yy as FLOAT))),
|
PowerOf => impl_op!(FLOAT => $xx.powf($yy as FLOAT)),
|
||||||
EqualsTo => Some(impl_op!(FLOAT => $xx == $yy)),
|
EqualsTo => impl_op!(FLOAT => $xx == $yy),
|
||||||
NotEqualsTo => Some(impl_op!(FLOAT => $xx != $yy)),
|
NotEqualsTo => impl_op!(FLOAT => $xx != $yy),
|
||||||
GreaterThan => Some(impl_op!(FLOAT => $xx > $yy)),
|
GreaterThan => impl_op!(FLOAT => $xx > $yy),
|
||||||
GreaterThanEqualsTo => Some(impl_op!(FLOAT => $xx >= $yy)),
|
GreaterThanEqualsTo => impl_op!(FLOAT => $xx >= $yy),
|
||||||
LessThan => Some(impl_op!(FLOAT => $xx < $yy)),
|
LessThan => impl_op!(FLOAT => $xx < $yy),
|
||||||
LessThanEqualsTo => Some(impl_op!(FLOAT => $xx <= $yy)),
|
LessThanEqualsTo => impl_op!(FLOAT => $xx <= $yy),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -373,12 +373,12 @@ pub fn get_builtin_binary_op_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Option<F
|
|||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
match op {
|
match op {
|
||||||
Plus => return Some(impl_op!(from Decimal => add($xx, $yy))),
|
Plus => return impl_op!(from Decimal => add($xx, $yy)),
|
||||||
Minus => return Some(impl_op!(from Decimal => subtract($xx, $yy))),
|
Minus => return impl_op!(from Decimal => subtract($xx, $yy)),
|
||||||
Multiply => return Some(impl_op!(from Decimal => multiply($xx, $yy))),
|
Multiply => return impl_op!(from Decimal => multiply($xx, $yy)),
|
||||||
Divide => return Some(impl_op!(from Decimal => divide($xx, $yy))),
|
Divide => return impl_op!(from Decimal => divide($xx, $yy)),
|
||||||
Modulo => return Some(impl_op!(from Decimal => modulo($xx, $yy))),
|
Modulo => return impl_op!(from Decimal => modulo($xx, $yy)),
|
||||||
PowerOf => return Some(impl_op!(from Decimal => power($xx, $yy))),
|
PowerOf => return impl_op!(from Decimal => power($xx, $yy)),
|
||||||
_ => ()
|
_ => ()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -387,22 +387,22 @@ pub fn get_builtin_binary_op_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Option<F
|
|||||||
|
|
||||||
#[cfg(feature = "unchecked")]
|
#[cfg(feature = "unchecked")]
|
||||||
match op {
|
match op {
|
||||||
Plus => return Some(impl_op!(from Decimal => $xx + $yy)),
|
Plus => return impl_op!(from Decimal => $xx + $yy),
|
||||||
Minus => return Some(impl_op!(from Decimal => $xx - $yy)),
|
Minus => return impl_op!(from Decimal => $xx - $yy),
|
||||||
Multiply => return Some(impl_op!(from Decimal => $xx * $yy)),
|
Multiply => return impl_op!(from Decimal => $xx * $yy),
|
||||||
Divide => return Some(impl_op!(from Decimal => $xx / $yy)),
|
Divide => return impl_op!(from Decimal => $xx / $yy),
|
||||||
Modulo => return Some(impl_op!(from Decimal => $xx % $yy)),
|
Modulo => return impl_op!(from Decimal => $xx % $yy),
|
||||||
PowerOf => return Some(impl_op!(from Decimal => $xx.powd($yy))),
|
PowerOf => return impl_op!(from Decimal => $xx.powd($yy)),
|
||||||
_ => ()
|
_ => ()
|
||||||
}
|
}
|
||||||
|
|
||||||
return match op {
|
return match op {
|
||||||
EqualsTo => Some(impl_op!(from Decimal => $xx == $yy)),
|
EqualsTo => impl_op!(from Decimal => $xx == $yy),
|
||||||
NotEqualsTo => Some(impl_op!(from Decimal => $xx != $yy)),
|
NotEqualsTo => impl_op!(from Decimal => $xx != $yy),
|
||||||
GreaterThan => Some(impl_op!(from Decimal => $xx > $yy)),
|
GreaterThan => impl_op!(from Decimal => $xx > $yy),
|
||||||
GreaterThanEqualsTo => Some(impl_op!(from Decimal => $xx >= $yy)),
|
GreaterThanEqualsTo => impl_op!(from Decimal => $xx >= $yy),
|
||||||
LessThan => Some(impl_op!(from Decimal => $xx < $yy)),
|
LessThan => impl_op!(from Decimal => $xx < $yy),
|
||||||
LessThanEqualsTo => Some(impl_op!(from Decimal => $xx <= $yy)),
|
LessThanEqualsTo => impl_op!(from Decimal => $xx <= $yy),
|
||||||
_ => None
|
_ => None
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -444,12 +444,12 @@ pub fn get_builtin_binary_op_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Option<F
|
|||||||
},
|
},
|
||||||
CHECKED_BUILD,
|
CHECKED_BUILD,
|
||||||
)),
|
)),
|
||||||
EqualsTo => Some(impl_op!(get_s1s2(==))),
|
EqualsTo => impl_op!(get_s1s2(==)),
|
||||||
NotEqualsTo => Some(impl_op!(get_s1s2(!=))),
|
NotEqualsTo => impl_op!(get_s1s2(!=)),
|
||||||
GreaterThan => Some(impl_op!(get_s1s2(>))),
|
GreaterThan => impl_op!(get_s1s2(>)),
|
||||||
GreaterThanEqualsTo => Some(impl_op!(get_s1s2(>=))),
|
GreaterThanEqualsTo => impl_op!(get_s1s2(>=)),
|
||||||
LessThan => Some(impl_op!(get_s1s2(<))),
|
LessThan => impl_op!(get_s1s2(<)),
|
||||||
LessThanEqualsTo => Some(impl_op!(get_s1s2(<=))),
|
LessThanEqualsTo => impl_op!(get_s1s2(<=)),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -486,12 +486,12 @@ pub fn get_builtin_binary_op_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Option<F
|
|||||||
},
|
},
|
||||||
false,
|
false,
|
||||||
)),
|
)),
|
||||||
EqualsTo => Some(impl_op!(get_s1s2(==))),
|
EqualsTo => impl_op!(get_s1s2(==)),
|
||||||
NotEqualsTo => Some(impl_op!(get_s1s2(!=))),
|
NotEqualsTo => impl_op!(get_s1s2(!=)),
|
||||||
GreaterThan => Some(impl_op!(get_s1s2(>))),
|
GreaterThan => impl_op!(get_s1s2(>)),
|
||||||
GreaterThanEqualsTo => Some(impl_op!(get_s1s2(>=))),
|
GreaterThanEqualsTo => impl_op!(get_s1s2(>=)),
|
||||||
LessThan => Some(impl_op!(get_s1s2(<))),
|
LessThan => impl_op!(get_s1s2(<)),
|
||||||
LessThanEqualsTo => Some(impl_op!(get_s1s2(<=))),
|
LessThanEqualsTo => impl_op!(get_s1s2(<=)),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -568,16 +568,16 @@ pub fn get_builtin_binary_op_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Option<F
|
|||||||
// Handle ranges here because ranges are implemented as custom type
|
// Handle ranges here because ranges are implemented as custom type
|
||||||
if type1 == TypeId::of::<ExclusiveRange>() && type1 == type2 {
|
if type1 == TypeId::of::<ExclusiveRange>() && type1 == type2 {
|
||||||
return match op {
|
return match op {
|
||||||
EqualsTo => Some(impl_op!(ExclusiveRange == ExclusiveRange)),
|
EqualsTo => impl_op!(ExclusiveRange == ExclusiveRange),
|
||||||
NotEqualsTo => Some(impl_op!(ExclusiveRange != ExclusiveRange)),
|
NotEqualsTo => impl_op!(ExclusiveRange != ExclusiveRange),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if type1 == TypeId::of::<InclusiveRange>() && type1 == type2 {
|
if type1 == TypeId::of::<InclusiveRange>() && type1 == type2 {
|
||||||
return match op {
|
return match op {
|
||||||
EqualsTo => Some(impl_op!(InclusiveRange == InclusiveRange)),
|
EqualsTo => impl_op!(InclusiveRange == InclusiveRange),
|
||||||
NotEqualsTo => Some(impl_op!(InclusiveRange != InclusiveRange)),
|
NotEqualsTo => impl_op!(InclusiveRange != InclusiveRange),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -626,54 +626,54 @@ pub fn get_builtin_op_assignment_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Opti
|
|||||||
let type2 = y.type_id();
|
let type2 = y.type_id();
|
||||||
|
|
||||||
macro_rules! impl_op {
|
macro_rules! impl_op {
|
||||||
($x:ty = x $op:tt $yy:ident) => { (|_, args| {
|
($x:ty = x $op:tt $yy:ident) => { Some((|_, args| {
|
||||||
let x = args[0].$yy().unwrap();
|
let x = args[0].$yy().unwrap();
|
||||||
let y = args[1].$yy().unwrap() as $x;
|
let y = args[1].$yy().unwrap() as $x;
|
||||||
Ok((*args[0].write_lock::<$x>().unwrap() = x $op y).into())
|
Ok((*args[0].write_lock::<$x>().unwrap() = x $op y).into())
|
||||||
}, false) };
|
}, false)) };
|
||||||
($x:ident $op:tt $yy:ident) => { (|_, args| {
|
($x:ident $op:tt $yy:ident) => { Some((|_, args| {
|
||||||
let y = args[1].$yy().unwrap() as $x;
|
let y = args[1].$yy().unwrap() as $x;
|
||||||
Ok((*args[0].write_lock::<$x>().unwrap() $op y).into())
|
Ok((*args[0].write_lock::<$x>().unwrap() $op y).into())
|
||||||
}, false) };
|
}, false)) };
|
||||||
($x:ident $op:tt $yy:ident as $yyy:ty) => { (|_, args| {
|
($x:ident $op:tt $yy:ident as $yyy:ty) => { Some((|_, args| {
|
||||||
let y = args[1].$yy().unwrap() as $yyy;
|
let y = args[1].$yy().unwrap() as $yyy;
|
||||||
Ok((*args[0].write_lock::<$x>().unwrap() $op y).into())
|
Ok((*args[0].write_lock::<$x>().unwrap() $op y).into())
|
||||||
}, false) };
|
}, false)) };
|
||||||
($x:ty => $xx:ident . $func:ident ( $yy:ident as $yyy:ty )) => { (|_, args| {
|
($x:ty => $xx:ident . $func:ident ( $yy:ident as $yyy:ty )) => { Some((|_, args| {
|
||||||
let x = args[0].$xx().unwrap();
|
let x = args[0].$xx().unwrap();
|
||||||
let y = args[1].$yy().unwrap() as $x;
|
let y = args[1].$yy().unwrap() as $x;
|
||||||
Ok((*args[0].write_lock::<$x>().unwrap() = x.$func(y as $yyy)).into())
|
Ok((*args[0].write_lock::<$x>().unwrap() = x.$func(y as $yyy)).into())
|
||||||
}, false) };
|
}, false)) };
|
||||||
($x:ty => Ok($func:ident ( $xx:ident, $yy:ident ))) => { (|_, args| {
|
($x:ty => Ok($func:ident ( $xx:ident, $yy:ident ))) => { Some((|_, args| {
|
||||||
let x = args[0].$xx().unwrap();
|
let x = args[0].$xx().unwrap();
|
||||||
let y = args[1].$yy().unwrap() as $x;
|
let y = args[1].$yy().unwrap() as $x;
|
||||||
let v: Dynamic = $func(x, y).into();
|
let v: Dynamic = $func(x, y).into();
|
||||||
Ok((*args[0].write_lock().unwrap() = v).into())
|
Ok((*args[0].write_lock().unwrap() = v).into())
|
||||||
}, false) };
|
}, false)) };
|
||||||
($x:ty => $func:ident ( $xx:ident, $yy:ident )) => { (|_, args| {
|
($x:ty => $func:ident ( $xx:ident, $yy:ident )) => { Some((|_, args| {
|
||||||
let x = args[0].$xx().unwrap();
|
let x = args[0].$xx().unwrap();
|
||||||
let y = args[1].$yy().unwrap() as $x;
|
let y = args[1].$yy().unwrap() as $x;
|
||||||
Ok((*args[0].write_lock().unwrap() = $func(x, y)?).into())
|
Ok((*args[0].write_lock().unwrap() = $func(x, y)?).into())
|
||||||
}, false) };
|
}, false)) };
|
||||||
(from $x:ident $op:tt $yy:ident) => { (|_, args| {
|
(from $x:ident $op:tt $yy:ident) => { Some((|_, args| {
|
||||||
let y = <$x>::from(args[1].$yy().unwrap());
|
let y = <$x>::from(args[1].$yy().unwrap());
|
||||||
Ok((*args[0].write_lock::<$x>().unwrap() $op y).into())
|
Ok((*args[0].write_lock::<$x>().unwrap() $op y).into())
|
||||||
}, false) };
|
}, false)) };
|
||||||
(from $x:ty => $xx:ident . $func:ident ( $yy:ident )) => { (|_, args| {
|
(from $x:ty => $xx:ident . $func:ident ( $yy:ident )) => { Some((|_, args| {
|
||||||
let x = args[0].$xx().unwrap();
|
let x = args[0].$xx().unwrap();
|
||||||
let y = <$x>::from(args[1].$yy().unwrap());
|
let y = <$x>::from(args[1].$yy().unwrap());
|
||||||
Ok((*args[0].write_lock::<$x>().unwrap() = x.$func(y)).into())
|
Ok((*args[0].write_lock::<$x>().unwrap() = x.$func(y)).into())
|
||||||
}, false) };
|
}, false)) };
|
||||||
(from $x:ty => Ok($func:ident ( $xx:ident, $yy:ident ))) => { (|_, args| {
|
(from $x:ty => Ok($func:ident ( $xx:ident, $yy:ident ))) => { Some((|_, args| {
|
||||||
let x = args[0].$xx().unwrap();
|
let x = args[0].$xx().unwrap();
|
||||||
let y = <$x>::from(args[1].$yy().unwrap());
|
let y = <$x>::from(args[1].$yy().unwrap());
|
||||||
Ok((*args[0].write_lock().unwrap() = $func(x, y).into()).into())
|
Ok((*args[0].write_lock().unwrap() = $func(x, y).into()).into())
|
||||||
}, false) };
|
}, false)) };
|
||||||
(from $x:ty => $func:ident ( $xx:ident, $yy:ident )) => { (|_, args| {
|
(from $x:ty => $func:ident ( $xx:ident, $yy:ident )) => { Some((|_, args| {
|
||||||
let x = args[0].$xx().unwrap();
|
let x = args[0].$xx().unwrap();
|
||||||
let y = <$x>::from(args[1].$yy().unwrap());
|
let y = <$x>::from(args[1].$yy().unwrap());
|
||||||
Ok((*args[0].write_lock().unwrap() = $func(x, y)?).into())
|
Ok((*args[0].write_lock().unwrap() = $func(x, y)?).into())
|
||||||
}, false) };
|
}, false)) };
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for common patterns
|
// Check for common patterns
|
||||||
@ -685,25 +685,25 @@ pub fn get_builtin_op_assignment_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Opti
|
|||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
match op {
|
match op {
|
||||||
PlusAssign => return Some(impl_op!(INT => add(as_int, as_int))),
|
PlusAssign => return impl_op!(INT => add(as_int, as_int)),
|
||||||
MinusAssign => return Some(impl_op!(INT => subtract(as_int, as_int))),
|
MinusAssign => return impl_op!(INT => subtract(as_int, as_int)),
|
||||||
MultiplyAssign => return Some(impl_op!(INT => multiply(as_int, as_int))),
|
MultiplyAssign => return impl_op!(INT => multiply(as_int, as_int)),
|
||||||
DivideAssign => return Some(impl_op!(INT => divide(as_int, as_int))),
|
DivideAssign => return impl_op!(INT => divide(as_int, as_int)),
|
||||||
ModuloAssign => return Some(impl_op!(INT => modulo(as_int, as_int))),
|
ModuloAssign => return impl_op!(INT => modulo(as_int, as_int)),
|
||||||
PowerOfAssign => return Some(impl_op!(INT => power(as_int, as_int))),
|
PowerOfAssign => return impl_op!(INT => power(as_int, as_int)),
|
||||||
RightShiftAssign => return Some(impl_op!(INT => Ok(shift_right(as_int, as_int)))),
|
RightShiftAssign => return impl_op!(INT => Ok(shift_right(as_int, as_int))),
|
||||||
LeftShiftAssign => return Some(impl_op!(INT => Ok(shift_left(as_int, as_int)))),
|
LeftShiftAssign => return impl_op!(INT => Ok(shift_left(as_int, as_int))),
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "unchecked")]
|
#[cfg(feature = "unchecked")]
|
||||||
match op {
|
match op {
|
||||||
PlusAssign => return Some(impl_op!(INT += as_int)),
|
PlusAssign => return impl_op!(INT += as_int),
|
||||||
MinusAssign => return Some(impl_op!(INT -= as_int)),
|
MinusAssign => return impl_op!(INT -= as_int),
|
||||||
MultiplyAssign => return Some(impl_op!(INT *= as_int)),
|
MultiplyAssign => return impl_op!(INT *= as_int),
|
||||||
DivideAssign => return Some(impl_op!(INT /= as_int)),
|
DivideAssign => return impl_op!(INT /= as_int),
|
||||||
ModuloAssign => return Some(impl_op!(INT %= as_int)),
|
ModuloAssign => return impl_op!(INT %= as_int),
|
||||||
PowerOfAssign => return Some(impl_op!(INT => as_int.pow(as_int as u32))),
|
PowerOfAssign => return impl_op!(INT => as_int.pow(as_int as u32)),
|
||||||
RightShiftAssign => {
|
RightShiftAssign => {
|
||||||
return Some((
|
return Some((
|
||||||
|_, args| {
|
|_, args| {
|
||||||
@ -730,17 +730,17 @@ pub fn get_builtin_op_assignment_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Opti
|
|||||||
}
|
}
|
||||||
|
|
||||||
return match op {
|
return match op {
|
||||||
AndAssign => Some(impl_op!(INT &= as_int)),
|
AndAssign => impl_op!(INT &= as_int),
|
||||||
OrAssign => Some(impl_op!(INT |= as_int)),
|
OrAssign => impl_op!(INT |= as_int),
|
||||||
XOrAssign => Some(impl_op!(INT ^= as_int)),
|
XOrAssign => impl_op!(INT ^= as_int),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if type1 == TypeId::of::<bool>() {
|
if type1 == TypeId::of::<bool>() {
|
||||||
return match op {
|
return match op {
|
||||||
AndAssign => Some(impl_op!(bool = x && as_bool)),
|
AndAssign => impl_op!(bool = x && as_bool),
|
||||||
OrAssign => Some(impl_op!(bool = x || as_bool)),
|
OrAssign => impl_op!(bool = x || as_bool),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -861,12 +861,12 @@ pub fn get_builtin_op_assignment_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Opti
|
|||||||
($x:ident, $xx:ident, $y:ty, $yy:ident) => {
|
($x:ident, $xx:ident, $y:ty, $yy:ident) => {
|
||||||
if (type1, type2) == (TypeId::of::<$x>(), TypeId::of::<$y>()) {
|
if (type1, type2) == (TypeId::of::<$x>(), TypeId::of::<$y>()) {
|
||||||
return match op {
|
return match op {
|
||||||
PlusAssign => Some(impl_op!($x += $yy)),
|
PlusAssign => impl_op!($x += $yy),
|
||||||
MinusAssign => Some(impl_op!($x -= $yy)),
|
MinusAssign => impl_op!($x -= $yy),
|
||||||
MultiplyAssign => Some(impl_op!($x *= $yy)),
|
MultiplyAssign => impl_op!($x *= $yy),
|
||||||
DivideAssign => Some(impl_op!($x /= $yy)),
|
DivideAssign => impl_op!($x /= $yy),
|
||||||
ModuloAssign => Some(impl_op!($x %= $yy)),
|
ModuloAssign => impl_op!($x %= $yy),
|
||||||
PowerOfAssign => Some(impl_op!($x => $xx.powf($yy as $x))),
|
PowerOfAssign => impl_op!($x => $xx.powf($yy as $x)),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -889,12 +889,12 @@ pub fn get_builtin_op_assignment_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Opti
|
|||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
return match op {
|
return match op {
|
||||||
PlusAssign => Some(impl_op!(from $x => add($xx, $yy))),
|
PlusAssign => impl_op!(from $x => add($xx, $yy)),
|
||||||
MinusAssign => Some(impl_op!(from $x => subtract($xx, $yy))),
|
MinusAssign => impl_op!(from $x => subtract($xx, $yy)),
|
||||||
MultiplyAssign => Some(impl_op!(from $x => multiply($xx, $yy))),
|
MultiplyAssign => impl_op!(from $x => multiply($xx, $yy)),
|
||||||
DivideAssign => Some(impl_op!(from $x => divide($xx, $yy))),
|
DivideAssign => impl_op!(from $x => divide($xx, $yy)),
|
||||||
ModuloAssign => Some(impl_op!(from $x => modulo($xx, $yy))),
|
ModuloAssign => impl_op!(from $x => modulo($xx, $yy)),
|
||||||
PowerOfAssign => Some(impl_op!(from $x => power($xx, $yy))),
|
PowerOfAssign => impl_op!(from $x => power($xx, $yy)),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -903,12 +903,12 @@ pub fn get_builtin_op_assignment_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Opti
|
|||||||
|
|
||||||
#[cfg(feature = "unchecked")]
|
#[cfg(feature = "unchecked")]
|
||||||
return match op {
|
return match op {
|
||||||
PlusAssign => Some(impl_op!(from $x += $yy)),
|
PlusAssign => impl_op!(from $x += $yy),
|
||||||
MinusAssign => Some(impl_op!(from $x -= $yy)),
|
MinusAssign => impl_op!(from $x -= $yy),
|
||||||
MultiplyAssign => Some(impl_op!(from $x *= $yy)),
|
MultiplyAssign => impl_op!(from $x *= $yy),
|
||||||
DivideAssign => Some(impl_op!(from $x /= $yy)),
|
DivideAssign => impl_op!(from $x /= $yy),
|
||||||
ModuloAssign => Some(impl_op!(from $x %= $yy)),
|
ModuloAssign => impl_op!(from $x %= $yy),
|
||||||
PowerOfAssign => Some(impl_op!(from $x => $xx.powd($yy))),
|
PowerOfAssign => impl_op!(from $x => $xx.powd($yy)),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -939,7 +939,7 @@ pub fn get_builtin_op_assignment_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Opti
|
|||||||
},
|
},
|
||||||
CHECKED_BUILD,
|
CHECKED_BUILD,
|
||||||
)),
|
)),
|
||||||
MinusAssign => Some(impl_op!(ImmutableString -= as_char as char)),
|
MinusAssign => impl_op!(ImmutableString -= as_char as char),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ use crate::engine::{
|
|||||||
KEYWORD_IS_DEF_VAR, KEYWORD_PRINT, KEYWORD_TYPE_OF,
|
KEYWORD_IS_DEF_VAR, KEYWORD_PRINT, KEYWORD_TYPE_OF,
|
||||||
};
|
};
|
||||||
use crate::eval::{Caches, FnResolutionCacheEntry, GlobalRuntimeState};
|
use crate::eval::{Caches, FnResolutionCacheEntry, GlobalRuntimeState};
|
||||||
use crate::tokenizer::{is_valid_function_name, Token, NO_TOKEN};
|
use crate::tokenizer::{is_valid_function_name, Token};
|
||||||
use crate::{
|
use crate::{
|
||||||
calc_fn_hash, calc_fn_hash_full, Dynamic, Engine, FnArgsVec, FnPtr, ImmutableString,
|
calc_fn_hash, calc_fn_hash_full, Dynamic, Engine, FnArgsVec, FnPtr, ImmutableString,
|
||||||
OptimizationLevel, Position, RhaiError, RhaiResult, RhaiResultOf, Scope, Shared, ERR,
|
OptimizationLevel, Position, RhaiError, RhaiResult, RhaiResultOf, Scope, Shared, ERR,
|
||||||
@ -164,7 +164,7 @@ impl Engine {
|
|||||||
_global: &GlobalRuntimeState,
|
_global: &GlobalRuntimeState,
|
||||||
caches: &'s mut Caches,
|
caches: &'s mut Caches,
|
||||||
local_entry: &'s mut Option<FnResolutionCacheEntry>,
|
local_entry: &'s mut Option<FnResolutionCacheEntry>,
|
||||||
op_token: Token,
|
op_token: Option<Token>,
|
||||||
hash_base: u64,
|
hash_base: u64,
|
||||||
args: Option<&mut FnCallArgs>,
|
args: Option<&mut FnCallArgs>,
|
||||||
allow_dynamic: bool,
|
allow_dynamic: bool,
|
||||||
@ -270,30 +270,29 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Try to find a built-in version
|
// Try to find a built-in version
|
||||||
let builtin = args.and_then(|args| match op_token {
|
let builtin =
|
||||||
Token::NONE => None,
|
args.and_then(|args| match op_token {
|
||||||
token if token.is_op_assignment() => {
|
None => None,
|
||||||
|
Some(token) if token.is_op_assignment() => {
|
||||||
let (first_arg, rest_args) = args.split_first().unwrap();
|
let (first_arg, rest_args) = args.split_first().unwrap();
|
||||||
|
|
||||||
get_builtin_op_assignment_fn(token, first_arg, rest_args[0]).map(
|
get_builtin_op_assignment_fn(token, first_arg, rest_args[0])
|
||||||
|(f, has_context)| FnResolutionCacheEntry {
|
.map(|(f, has_context)| FnResolutionCacheEntry {
|
||||||
func: CallableFunction::Method {
|
func: CallableFunction::Method {
|
||||||
func: Shared::new(f),
|
func: Shared::new(f),
|
||||||
has_context,
|
has_context,
|
||||||
},
|
},
|
||||||
source: None,
|
source: None,
|
||||||
},
|
})
|
||||||
)
|
|
||||||
}
|
}
|
||||||
token => get_builtin_binary_op_fn(token, args[0], args[1]).map(
|
Some(token) => get_builtin_binary_op_fn(token, args[0], args[1])
|
||||||
|(f, has_context)| FnResolutionCacheEntry {
|
.map(|(f, has_context)| FnResolutionCacheEntry {
|
||||||
func: CallableFunction::Method {
|
func: CallableFunction::Method {
|
||||||
func: Shared::new(f),
|
func: Shared::new(f),
|
||||||
has_context,
|
has_context,
|
||||||
},
|
},
|
||||||
source: None,
|
source: None,
|
||||||
},
|
}),
|
||||||
),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return if cache.filter.is_absent_and_set(hash) {
|
return if cache.filter.is_absent_and_set(hash) {
|
||||||
@ -346,7 +345,7 @@ impl Engine {
|
|||||||
global: &mut GlobalRuntimeState,
|
global: &mut GlobalRuntimeState,
|
||||||
caches: &mut Caches,
|
caches: &mut Caches,
|
||||||
name: &str,
|
name: &str,
|
||||||
op_token: Token,
|
op_token: Option<Token>,
|
||||||
hash: u64,
|
hash: u64,
|
||||||
args: &mut FnCallArgs,
|
args: &mut FnCallArgs,
|
||||||
is_ref_mut: bool,
|
is_ref_mut: bool,
|
||||||
@ -568,21 +567,13 @@ impl Engine {
|
|||||||
caches: &mut Caches,
|
caches: &mut Caches,
|
||||||
_scope: Option<&mut Scope>,
|
_scope: Option<&mut Scope>,
|
||||||
fn_name: &str,
|
fn_name: &str,
|
||||||
op_token: Token,
|
op_token: Option<Token>,
|
||||||
hashes: FnCallHashes,
|
hashes: FnCallHashes,
|
||||||
mut _args: &mut FnCallArgs,
|
mut _args: &mut FnCallArgs,
|
||||||
is_ref_mut: bool,
|
is_ref_mut: bool,
|
||||||
_is_method_call: bool,
|
_is_method_call: bool,
|
||||||
pos: Position,
|
pos: Position,
|
||||||
) -> RhaiResultOf<(Dynamic, bool)> {
|
) -> RhaiResultOf<(Dynamic, bool)> {
|
||||||
fn no_method_err(name: &str, pos: Position) -> RhaiResultOf<(Dynamic, bool)> {
|
|
||||||
Err(ERR::ErrorRuntime(
|
|
||||||
format!("'{name}' should not be called this way. Try {name}(...);").into(),
|
|
||||||
pos,
|
|
||||||
)
|
|
||||||
.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for data race.
|
// Check for data race.
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
ensure_no_data_race(fn_name, _args, is_ref_mut)?;
|
ensure_no_data_race(fn_name, _args, is_ref_mut)?;
|
||||||
@ -622,16 +613,13 @@ impl Engine {
|
|||||||
|
|
||||||
// Handle is_shared()
|
// Handle is_shared()
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
crate::engine::KEYWORD_IS_SHARED if _args.len() == 1 => {
|
crate::engine::KEYWORD_IS_SHARED => {
|
||||||
return no_method_err(fn_name, pos)
|
unreachable!("{} called as method", fn_name)
|
||||||
}
|
}
|
||||||
|
|
||||||
KEYWORD_FN_PTR | KEYWORD_EVAL | KEYWORD_IS_DEF_VAR if _args.len() == 1 => {
|
KEYWORD_FN_PTR | KEYWORD_EVAL | KEYWORD_IS_DEF_VAR | KEYWORD_FN_PTR_CALL
|
||||||
return no_method_err(fn_name, pos)
|
| KEYWORD_FN_PTR_CURRY => {
|
||||||
}
|
unreachable!("{} called as method", fn_name)
|
||||||
|
|
||||||
KEYWORD_FN_PTR_CALL | KEYWORD_FN_PTR_CURRY if !_args.is_empty() => {
|
|
||||||
return no_method_err(fn_name, pos)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => (),
|
_ => (),
|
||||||
@ -645,7 +633,7 @@ impl Engine {
|
|||||||
let local_entry = &mut None;
|
let local_entry = &mut None;
|
||||||
|
|
||||||
if let Some(FnResolutionCacheEntry { func, ref source }) = self
|
if let Some(FnResolutionCacheEntry { func, ref source }) = self
|
||||||
.resolve_fn(global, caches, local_entry, NO_TOKEN, hash, None, false)
|
.resolve_fn(global, caches, local_entry, None, hash, None, false)
|
||||||
.cloned()
|
.cloned()
|
||||||
{
|
{
|
||||||
// Script function call
|
// Script function call
|
||||||
@ -812,7 +800,7 @@ impl Engine {
|
|||||||
caches,
|
caches,
|
||||||
None,
|
None,
|
||||||
fn_name,
|
fn_name,
|
||||||
NO_TOKEN,
|
None,
|
||||||
new_hash,
|
new_hash,
|
||||||
args,
|
args,
|
||||||
false,
|
false,
|
||||||
@ -899,7 +887,7 @@ impl Engine {
|
|||||||
caches,
|
caches,
|
||||||
None,
|
None,
|
||||||
&fn_name,
|
&fn_name,
|
||||||
NO_TOKEN,
|
None,
|
||||||
new_hash,
|
new_hash,
|
||||||
args,
|
args,
|
||||||
is_ref_mut,
|
is_ref_mut,
|
||||||
@ -986,7 +974,7 @@ impl Engine {
|
|||||||
caches,
|
caches,
|
||||||
None,
|
None,
|
||||||
fn_name,
|
fn_name,
|
||||||
NO_TOKEN,
|
None,
|
||||||
hash,
|
hash,
|
||||||
&mut args,
|
&mut args,
|
||||||
is_ref_mut,
|
is_ref_mut,
|
||||||
@ -1012,7 +1000,7 @@ impl Engine {
|
|||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
mut this_ptr: Option<&mut Dynamic>,
|
mut this_ptr: Option<&mut Dynamic>,
|
||||||
fn_name: &str,
|
fn_name: &str,
|
||||||
op_token: Token,
|
op_token: Option<Token>,
|
||||||
first_arg: Option<&Expr>,
|
first_arg: Option<&Expr>,
|
||||||
args_expr: &[Expr],
|
args_expr: &[Expr],
|
||||||
hashes: FnCallHashes,
|
hashes: FnCallHashes,
|
||||||
@ -1028,7 +1016,7 @@ impl Engine {
|
|||||||
let redirected; // Handle call() - Redirect function call
|
let redirected; // Handle call() - Redirect function call
|
||||||
|
|
||||||
match name {
|
match name {
|
||||||
_ if op_token != NO_TOKEN => (),
|
_ if op_token.is_some() => (),
|
||||||
|
|
||||||
// Handle call(fn_ptr, ...)
|
// Handle call(fn_ptr, ...)
|
||||||
KEYWORD_FN_PTR_CALL if total_args >= 1 => {
|
KEYWORD_FN_PTR_CALL if total_args >= 1 => {
|
||||||
@ -1582,7 +1570,7 @@ impl Engine {
|
|||||||
let op_token = op_token.clone();
|
let op_token = op_token.clone();
|
||||||
|
|
||||||
// Short-circuit native unary operator call if under Fast Operators mode
|
// Short-circuit native unary operator call if under Fast Operators mode
|
||||||
if op_token == Token::Bang && self.fast_operators() && args.len() == 1 {
|
if op_token == Some(Token::Bang) && self.fast_operators() && args.len() == 1 {
|
||||||
let mut value = self
|
let mut value = self
|
||||||
.get_arg_value(global, caches, scope, this_ptr.as_deref_mut(), &args[0])?
|
.get_arg_value(global, caches, scope, this_ptr.as_deref_mut(), &args[0])?
|
||||||
.0
|
.0
|
||||||
@ -1598,7 +1586,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Short-circuit native binary operator call if under Fast Operators mode
|
// Short-circuit native binary operator call if under Fast Operators mode
|
||||||
if op_token != NO_TOKEN && self.fast_operators() && args.len() == 2 {
|
if op_token.is_some() && self.fast_operators() && args.len() == 2 {
|
||||||
let mut lhs = self
|
let mut lhs = self
|
||||||
.get_arg_value(global, caches, scope, this_ptr.as_deref_mut(), &args[0])?
|
.get_arg_value(global, caches, scope, this_ptr.as_deref_mut(), &args[0])?
|
||||||
.0
|
.0
|
||||||
@ -1611,13 +1599,13 @@ impl Engine {
|
|||||||
|
|
||||||
let operands = &mut [&mut lhs, &mut rhs];
|
let operands = &mut [&mut lhs, &mut rhs];
|
||||||
|
|
||||||
if let Some((func, ctx)) =
|
if let Some((func, need_context)) =
|
||||||
get_builtin_binary_op_fn(op_token.clone(), operands[0], operands[1])
|
get_builtin_binary_op_fn(op_token.clone().unwrap(), operands[0], operands[1])
|
||||||
{
|
{
|
||||||
// Built-in found
|
// Built-in found
|
||||||
auto_restore! { let orig_level = global.level; global.level += 1 }
|
auto_restore! { let orig_level = global.level; global.level += 1 }
|
||||||
|
|
||||||
let context = if ctx {
|
let context = if need_context {
|
||||||
Some((self, name.as_str(), None, &*global, pos).into())
|
Some((self, name.as_str(), None, &*global, pos).into())
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
@ -4,7 +4,7 @@ use super::call::FnCallArgs;
|
|||||||
use crate::ast::FnCallHashes;
|
use crate::ast::FnCallHashes;
|
||||||
use crate::eval::{Caches, GlobalRuntimeState};
|
use crate::eval::{Caches, GlobalRuntimeState};
|
||||||
use crate::plugin::PluginFunction;
|
use crate::plugin::PluginFunction;
|
||||||
use crate::tokenizer::{is_valid_function_name, Token, TokenizeState, NO_TOKEN};
|
use crate::tokenizer::{is_valid_function_name, Token, TokenizeState};
|
||||||
use crate::types::dynamic::Variant;
|
use crate::types::dynamic::Variant;
|
||||||
use crate::{
|
use crate::{
|
||||||
calc_fn_hash, Dynamic, Engine, EvalContext, FuncArgs, Position, RhaiResult, RhaiResultOf,
|
calc_fn_hash, Dynamic, Engine, EvalContext, FuncArgs, Position, RhaiResult, RhaiResultOf,
|
||||||
@ -436,7 +436,7 @@ impl<'a> NativeCallContext<'a> {
|
|||||||
let caches = &mut Caches::new();
|
let caches = &mut Caches::new();
|
||||||
|
|
||||||
let fn_name = fn_name.as_ref();
|
let fn_name = fn_name.as_ref();
|
||||||
let op_token = Token::lookup_symbol_from_syntax(fn_name).unwrap_or(NO_TOKEN);
|
let op_token = Token::lookup_symbol_from_syntax(fn_name);
|
||||||
let args_len = args.len();
|
let args_len = args.len();
|
||||||
|
|
||||||
if native_only {
|
if native_only {
|
||||||
|
@ -147,7 +147,7 @@ impl<'a> OptimizerState<'a> {
|
|||||||
pub fn call_fn_with_constant_arguments(
|
pub fn call_fn_with_constant_arguments(
|
||||||
&mut self,
|
&mut self,
|
||||||
fn_name: &str,
|
fn_name: &str,
|
||||||
op_token: Token,
|
op_token: Option<Token>,
|
||||||
arg_values: &mut [Dynamic],
|
arg_values: &mut [Dynamic],
|
||||||
) -> Option<Dynamic> {
|
) -> Option<Dynamic> {
|
||||||
self.engine
|
self.engine
|
||||||
@ -1137,8 +1137,8 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Overloaded operators can override built-in.
|
// Overloaded operators can override built-in.
|
||||||
_ if x.args.len() == 2 && x.op_token != Token::NONE && (state.engine.fast_operators() || !state.engine.has_native_fn_override(x.hashes.native(), &arg_types)) => {
|
_ if x.args.len() == 2 && x.op_token.is_some() && (state.engine.fast_operators() || !state.engine.has_native_fn_override(x.hashes.native(), &arg_types)) => {
|
||||||
if let Some(result) = get_builtin_binary_op_fn(x.op_token.clone(), &arg_values[0], &arg_values[1])
|
if let Some(result) = get_builtin_binary_op_fn(x.op_token.clone().unwrap(), &arg_values[0], &arg_values[1])
|
||||||
.and_then(|(f, ctx)| {
|
.and_then(|(f, ctx)| {
|
||||||
let context = if ctx {
|
let context = if ctx {
|
||||||
Some((state.engine, x.name.as_str(), None, &state.global, *pos).into())
|
Some((state.engine, x.name.as_str(), None, &state.global, *pos).into())
|
||||||
|
@ -235,52 +235,15 @@ pub mod array_functions {
|
|||||||
|
|
||||||
// Check if array will be over max size limit
|
// Check if array will be over max size limit
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
{
|
if _ctx.engine().max_array_size() > 0 {
|
||||||
use crate::types::dynamic::Union;
|
let pad = len - array.len();
|
||||||
|
let (a, m, s) = Dynamic::calc_array_sizes(array, true);
|
||||||
|
let (ax, mx, sx) = item.calc_data_sizes(true);
|
||||||
|
|
||||||
if _ctx.engine().max_array_size() > 0 && len > _ctx.engine().max_array_size() {
|
_ctx.engine()
|
||||||
return Err(
|
.throw_on_size((a + pad + ax * pad, m + mx * pad, s + sx * pad))?;
|
||||||
ERR::ErrorDataTooLarge("Size of array".to_string(), Position::NONE).into(),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let check_sizes = match item.0 {
|
|
||||||
Union::Str(..) => true,
|
|
||||||
Union::Array(..) => true,
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
Union::Map(..) => true,
|
|
||||||
_ => false,
|
|
||||||
};
|
|
||||||
|
|
||||||
if check_sizes {
|
|
||||||
let mut arr_len = array.len();
|
|
||||||
let mut arr = Dynamic::from_array(mem::take(array));
|
|
||||||
|
|
||||||
let (mut a1, mut m1, mut s1) = arr.calc_data_sizes(true);
|
|
||||||
let (a2, m2, s2) = item.calc_data_sizes(true);
|
|
||||||
|
|
||||||
{
|
|
||||||
let mut guard = arr.write_lock::<Array>().unwrap();
|
|
||||||
|
|
||||||
while arr_len < len {
|
|
||||||
a1 += a2;
|
|
||||||
m1 += m2;
|
|
||||||
s1 += s2;
|
|
||||||
|
|
||||||
_ctx.engine().throw_on_size((a1, m1, s1))?;
|
|
||||||
|
|
||||||
guard.push(item.clone());
|
|
||||||
arr_len += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
*array = arr.into_array().unwrap();
|
|
||||||
} else {
|
|
||||||
array.resize(len, item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "unchecked")]
|
|
||||||
array.resize(len, item);
|
array.resize(len, item);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -644,6 +607,43 @@ pub mod array_functions {
|
|||||||
result
|
result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Iterate through all the elements in the array, applying a `process` function to each element in turn.
|
||||||
|
/// Each element is bound to `this` before calling the function.
|
||||||
|
///
|
||||||
|
/// # Function Parameters
|
||||||
|
///
|
||||||
|
/// * `this`: bound to array element (mutable)
|
||||||
|
/// * `index` _(optional)_: current index in the array
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let x = [1, 2, 3, 4, 5];
|
||||||
|
///
|
||||||
|
/// x.for_each(|| this *= this);
|
||||||
|
///
|
||||||
|
/// print(x); // prints "[1, 4, 9, 16, 25]"
|
||||||
|
///
|
||||||
|
/// x.for_each(|i| this *= i);
|
||||||
|
///
|
||||||
|
/// print(x); // prints "[0, 2, 6, 12, 20]"
|
||||||
|
/// ```
|
||||||
|
#[rhai_fn(return_raw)]
|
||||||
|
pub fn for_each(ctx: NativeCallContext, array: &mut Array, map: FnPtr) -> RhaiResultOf<()> {
|
||||||
|
if array.is_empty() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i, item) in array.iter_mut().enumerate() {
|
||||||
|
let ex = [(i as INT).into()];
|
||||||
|
|
||||||
|
let _ = map.call_raw_with_extra_args("map", &ctx, Some(item), [], ex, None)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Iterate through all the elements in the array, applying a `mapper` function to each element
|
/// Iterate through all the elements in the array, applying a `mapper` function to each element
|
||||||
/// in turn, and return the results as a new array.
|
/// in turn, and return the results as a new array.
|
||||||
///
|
///
|
||||||
@ -679,8 +679,7 @@ pub mod array_functions {
|
|||||||
|
|
||||||
for (i, item) in array.iter_mut().enumerate() {
|
for (i, item) in array.iter_mut().enumerate() {
|
||||||
let ex = [(i as INT).into()];
|
let ex = [(i as INT).into()];
|
||||||
|
ar.push(map.call_raw_with_extra_args("map", &ctx, Some(item), [], ex, Some(0))?);
|
||||||
ar.push(map.call_raw_with_extra_args("map", &ctx, Some(item), [], ex)?);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(ar)
|
Ok(ar)
|
||||||
@ -723,7 +722,7 @@ pub mod array_functions {
|
|||||||
let ex = [(i as INT).into()];
|
let ex = [(i as INT).into()];
|
||||||
|
|
||||||
if filter
|
if filter
|
||||||
.call_raw_with_extra_args("filter", &ctx, Some(item), [], ex)?
|
.call_raw_with_extra_args("filter", &ctx, Some(item), [], ex, Some(0))?
|
||||||
.as_bool()
|
.as_bool()
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
{
|
{
|
||||||
@ -962,7 +961,7 @@ pub mod array_functions {
|
|||||||
let ex = [(i as INT).into()];
|
let ex = [(i as INT).into()];
|
||||||
|
|
||||||
if filter
|
if filter
|
||||||
.call_raw_with_extra_args("index_of", &ctx, Some(item), [], ex)?
|
.call_raw_with_extra_args("index_of", &ctx, Some(item), [], ex, Some(0))?
|
||||||
.as_bool()
|
.as_bool()
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
{
|
{
|
||||||
@ -1128,7 +1127,8 @@ pub mod array_functions {
|
|||||||
for (i, item) in array.iter_mut().enumerate().skip(start) {
|
for (i, item) in array.iter_mut().enumerate().skip(start) {
|
||||||
let ex = [(i as INT).into()];
|
let ex = [(i as INT).into()];
|
||||||
|
|
||||||
let value = filter.call_raw_with_extra_args("find_map", &ctx, Some(item), [], ex)?;
|
let value =
|
||||||
|
filter.call_raw_with_extra_args("find_map", &ctx, Some(item), [], ex, Some(0))?;
|
||||||
|
|
||||||
if !value.is_unit() {
|
if !value.is_unit() {
|
||||||
return Ok(value);
|
return Ok(value);
|
||||||
@ -1169,7 +1169,7 @@ pub mod array_functions {
|
|||||||
let ex = [(i as INT).into()];
|
let ex = [(i as INT).into()];
|
||||||
|
|
||||||
if filter
|
if filter
|
||||||
.call_raw_with_extra_args("some", &ctx, Some(item), [], ex)?
|
.call_raw_with_extra_args("some", &ctx, Some(item), [], ex, Some(0))?
|
||||||
.as_bool()
|
.as_bool()
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
{
|
{
|
||||||
@ -1211,7 +1211,7 @@ pub mod array_functions {
|
|||||||
let ex = [(i as INT).into()];
|
let ex = [(i as INT).into()];
|
||||||
|
|
||||||
if !filter
|
if !filter
|
||||||
.call_raw_with_extra_args("all", &ctx, Some(item), [], ex)?
|
.call_raw_with_extra_args("all", &ctx, Some(item), [], ex, Some(0))?
|
||||||
.as_bool()
|
.as_bool()
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
{
|
{
|
||||||
@ -1334,11 +1334,11 @@ pub mod array_functions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
array
|
array
|
||||||
.iter()
|
.iter_mut()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.try_fold(initial, |result, (i, item)| {
|
.try_fold(initial, |result, (i, item)| {
|
||||||
let ex = [(i as INT).into()];
|
let ex = [(i as INT).into()];
|
||||||
reducer.call_raw_with_extra_args("reduce", &ctx, None, [result, item.clone()], ex)
|
reducer.call_raw_with_extra_args("reduce", &ctx, Some(item), [result], ex, Some(1))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
/// Reduce an array by iterating through all elements, in _reverse_ order,
|
/// Reduce an array by iterating through all elements, in _reverse_ order,
|
||||||
@ -1400,19 +1400,22 @@ pub mod array_functions {
|
|||||||
return Ok(initial);
|
return Ok(initial);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let len = array.len();
|
||||||
|
|
||||||
array
|
array
|
||||||
.iter()
|
.iter_mut()
|
||||||
.rev()
|
.rev()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.try_fold(initial, |result, (i, item)| {
|
.try_fold(initial, |result, (i, item)| {
|
||||||
let ex = [((array.len() - 1 - i) as INT).into()];
|
let ex = [((len - 1 - i) as INT).into()];
|
||||||
|
|
||||||
reducer.call_raw_with_extra_args(
|
reducer.call_raw_with_extra_args(
|
||||||
"reduce_rev",
|
"reduce_rev",
|
||||||
&ctx,
|
&ctx,
|
||||||
None,
|
Some(item),
|
||||||
[result, item.clone()],
|
[result],
|
||||||
ex,
|
ex,
|
||||||
|
Some(1),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -1602,7 +1605,7 @@ pub mod array_functions {
|
|||||||
let ex = [(i as INT).into()];
|
let ex = [(i as INT).into()];
|
||||||
|
|
||||||
if filter
|
if filter
|
||||||
.call_raw_with_extra_args("drain", &ctx, Some(&mut array[x]), [], ex)?
|
.call_raw_with_extra_args("drain", &ctx, Some(&mut array[x]), [], ex, Some(0))?
|
||||||
.as_bool()
|
.as_bool()
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
{
|
{
|
||||||
@ -1749,7 +1752,7 @@ pub mod array_functions {
|
|||||||
let ex = [(i as INT).into()];
|
let ex = [(i as INT).into()];
|
||||||
|
|
||||||
if filter
|
if filter
|
||||||
.call_raw_with_extra_args("retain", &ctx, Some(&mut array[x]), [], ex)?
|
.call_raw_with_extra_args("retain", &ctx, Some(&mut array[x]), [], ex, Some(0))?
|
||||||
.as_bool()
|
.as_bool()
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
{
|
{
|
||||||
|
@ -44,7 +44,14 @@ pub fn print_with_func(
|
|||||||
result.into_immutable_string().expect("`ImmutableString`")
|
result.into_immutable_string().expect("`ImmutableString`")
|
||||||
}
|
}
|
||||||
Ok(result) => ctx.engine().map_type_name(result.type_name()).into(),
|
Ok(result) => ctx.engine().map_type_name(result.type_name()).into(),
|
||||||
Err(_) => ctx.engine().map_type_name(value.type_name()).into(),
|
Err(_) => {
|
||||||
|
let mut buf = SmartString::new_const();
|
||||||
|
match fn_name {
|
||||||
|
FUNC_TO_DEBUG => write!(&mut buf, "{value:?}").unwrap(),
|
||||||
|
_ => write!(&mut buf, "{value}").unwrap(),
|
||||||
|
}
|
||||||
|
ctx.engine().map_type_name(&buf).into()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,7 +65,9 @@ mod print_debug_functions {
|
|||||||
/// Convert the value of the `item` into a string.
|
/// Convert the value of the `item` into a string.
|
||||||
#[rhai_fn(name = "to_string", pure)]
|
#[rhai_fn(name = "to_string", pure)]
|
||||||
pub fn to_string_generic(ctx: NativeCallContext, item: &mut Dynamic) -> ImmutableString {
|
pub fn to_string_generic(ctx: NativeCallContext, item: &mut Dynamic) -> ImmutableString {
|
||||||
ctx.engine().map_type_name(&item.to_string()).into()
|
let mut buf = SmartString::new_const();
|
||||||
|
write!(&mut buf, "{item}").unwrap();
|
||||||
|
ctx.engine().map_type_name(&buf).into()
|
||||||
}
|
}
|
||||||
/// Convert the value of the `item` into a string in debug format.
|
/// Convert the value of the `item` into a string in debug format.
|
||||||
#[rhai_fn(name = "debug", pure)]
|
#[rhai_fn(name = "debug", pure)]
|
||||||
@ -95,7 +104,9 @@ mod print_debug_functions {
|
|||||||
/// Return the character into a string.
|
/// Return the character into a string.
|
||||||
#[rhai_fn(name = "print", name = "to_string")]
|
#[rhai_fn(name = "print", name = "to_string")]
|
||||||
pub fn print_char(character: char) -> ImmutableString {
|
pub fn print_char(character: char) -> ImmutableString {
|
||||||
character.to_string().into()
|
let mut buf = SmartString::new_const();
|
||||||
|
buf.push(character);
|
||||||
|
buf.into()
|
||||||
}
|
}
|
||||||
/// Convert the string into debug format.
|
/// Convert the string into debug format.
|
||||||
#[rhai_fn(name = "debug", name = "to_debug")]
|
#[rhai_fn(name = "debug", name = "to_debug")]
|
||||||
@ -108,13 +119,17 @@ mod print_debug_functions {
|
|||||||
/// Convert the function pointer into a string in debug format.
|
/// Convert the function pointer into a string in debug format.
|
||||||
#[rhai_fn(name = "debug", name = "to_debug", pure)]
|
#[rhai_fn(name = "debug", name = "to_debug", pure)]
|
||||||
pub fn debug_fn_ptr(f: &mut FnPtr) -> ImmutableString {
|
pub fn debug_fn_ptr(f: &mut FnPtr) -> ImmutableString {
|
||||||
f.to_string().into()
|
let mut buf = SmartString::new_const();
|
||||||
|
write!(&mut buf, "{f}").unwrap();
|
||||||
|
buf.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the boolean value into a string.
|
/// Return the boolean value into a string.
|
||||||
#[rhai_fn(name = "print", name = "to_string")]
|
#[rhai_fn(name = "print", name = "to_string")]
|
||||||
pub fn print_bool(value: bool) -> ImmutableString {
|
pub fn print_bool(value: bool) -> ImmutableString {
|
||||||
value.to_string().into()
|
let mut buf = SmartString::new_const();
|
||||||
|
write!(&mut buf, "{value}").unwrap();
|
||||||
|
buf.into()
|
||||||
}
|
}
|
||||||
/// Convert the boolean value into a string in debug format.
|
/// Convert the boolean value into a string in debug format.
|
||||||
#[rhai_fn(name = "debug", name = "to_debug")]
|
#[rhai_fn(name = "debug", name = "to_debug")]
|
||||||
@ -141,30 +156,32 @@ mod print_debug_functions {
|
|||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
#[rhai_fn(name = "print", name = "to_string")]
|
#[rhai_fn(name = "print", name = "to_string")]
|
||||||
pub fn print_f64(number: f64) -> ImmutableString {
|
pub fn print_f64(number: f64) -> ImmutableString {
|
||||||
crate::types::FloatWrapper::new(number).to_string().into()
|
let mut buf = SmartString::new_const();
|
||||||
|
write!(&mut buf, "{}", crate::types::FloatWrapper::new(number)).unwrap();
|
||||||
|
buf.into()
|
||||||
}
|
}
|
||||||
/// Convert the value of `number` into a string.
|
/// Convert the value of `number` into a string.
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
#[rhai_fn(name = "print", name = "to_string")]
|
#[rhai_fn(name = "print", name = "to_string")]
|
||||||
pub fn print_f32(number: f32) -> ImmutableString {
|
pub fn print_f32(number: f32) -> ImmutableString {
|
||||||
crate::types::FloatWrapper::new(number).to_string().into()
|
let mut buf = SmartString::new_const();
|
||||||
|
write!(&mut buf, "{}", crate::types::FloatWrapper::new(number)).unwrap();
|
||||||
|
buf.into()
|
||||||
}
|
}
|
||||||
/// Convert the value of `number` into a string.
|
/// Convert the value of `number` into a string.
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
#[rhai_fn(name = "debug", name = "to_debug")]
|
#[rhai_fn(name = "debug", name = "to_debug")]
|
||||||
pub fn debug_f64(number: f64) -> ImmutableString {
|
pub fn debug_f64(number: f64) -> ImmutableString {
|
||||||
let number = crate::types::FloatWrapper::new(number);
|
|
||||||
let mut buf = SmartString::new_const();
|
let mut buf = SmartString::new_const();
|
||||||
write!(&mut buf, "{number:?}").unwrap();
|
write!(&mut buf, "{:?}", crate::types::FloatWrapper::new(number)).unwrap();
|
||||||
buf.into()
|
buf.into()
|
||||||
}
|
}
|
||||||
/// Convert the value of `number` into a string.
|
/// Convert the value of `number` into a string.
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
#[rhai_fn(name = "debug", name = "to_debug")]
|
#[rhai_fn(name = "debug", name = "to_debug")]
|
||||||
pub fn debug_f32(number: f32) -> ImmutableString {
|
pub fn debug_f32(number: f32) -> ImmutableString {
|
||||||
let number = crate::types::FloatWrapper::new(number);
|
|
||||||
let mut buf = SmartString::new_const();
|
let mut buf = SmartString::new_const();
|
||||||
write!(&mut buf, "{number:?}").unwrap();
|
write!(&mut buf, "{:?}", crate::types::FloatWrapper::new(number)).unwrap();
|
||||||
buf.into()
|
buf.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -179,7 +196,7 @@ mod print_debug_functions {
|
|||||||
)]
|
)]
|
||||||
pub fn format_array(ctx: NativeCallContext, array: &mut Array) -> ImmutableString {
|
pub fn format_array(ctx: NativeCallContext, array: &mut Array) -> ImmutableString {
|
||||||
let len = array.len();
|
let len = array.len();
|
||||||
let mut result = String::with_capacity(len * 5 + 2);
|
let mut result = SmartString::new_const();
|
||||||
result.push('[');
|
result.push('[');
|
||||||
|
|
||||||
array.iter_mut().enumerate().for_each(|(i, x)| {
|
array.iter_mut().enumerate().for_each(|(i, x)| {
|
||||||
@ -204,12 +221,10 @@ mod print_debug_functions {
|
|||||||
)]
|
)]
|
||||||
pub fn format_map(ctx: NativeCallContext, map: &mut Map) -> ImmutableString {
|
pub fn format_map(ctx: NativeCallContext, map: &mut Map) -> ImmutableString {
|
||||||
let len = map.len();
|
let len = map.len();
|
||||||
let mut result = String::with_capacity(len * 5 + 3);
|
let mut result = SmartString::new_const();
|
||||||
result.push_str("#{");
|
result.push_str("#{");
|
||||||
|
|
||||||
map.iter_mut().enumerate().for_each(|(i, (k, v))| {
|
map.iter_mut().enumerate().for_each(|(i, (k, v))| {
|
||||||
use std::fmt::Write;
|
|
||||||
|
|
||||||
write!(
|
write!(
|
||||||
result,
|
result,
|
||||||
"{:?}: {}{}",
|
"{:?}: {}{}",
|
||||||
|
@ -12,7 +12,7 @@ use crate::eval::{Caches, GlobalRuntimeState};
|
|||||||
use crate::func::{hashing::get_hasher, StraightHashMap};
|
use crate::func::{hashing::get_hasher, StraightHashMap};
|
||||||
use crate::tokenizer::{
|
use crate::tokenizer::{
|
||||||
is_keyword_function, is_valid_function_name, is_valid_identifier, Token, TokenStream,
|
is_keyword_function, is_valid_function_name, is_valid_identifier, Token, TokenStream,
|
||||||
TokenizerControl, NO_TOKEN,
|
TokenizerControl,
|
||||||
};
|
};
|
||||||
use crate::types::dynamic::AccessMode;
|
use crate::types::dynamic::AccessMode;
|
||||||
use crate::types::StringsInterner;
|
use crate::types::StringsInterner;
|
||||||
@ -627,7 +627,7 @@ impl Engine {
|
|||||||
return Ok(FnCallExpr {
|
return Ok(FnCallExpr {
|
||||||
name: state.get_interned_string(id),
|
name: state.get_interned_string(id),
|
||||||
capture_parent_scope,
|
capture_parent_scope,
|
||||||
op_token: NO_TOKEN,
|
op_token: None,
|
||||||
namespace: _namespace,
|
namespace: _namespace,
|
||||||
hashes,
|
hashes,
|
||||||
args,
|
args,
|
||||||
@ -702,7 +702,7 @@ impl Engine {
|
|||||||
return Ok(FnCallExpr {
|
return Ok(FnCallExpr {
|
||||||
name: state.get_interned_string(id),
|
name: state.get_interned_string(id),
|
||||||
capture_parent_scope,
|
capture_parent_scope,
|
||||||
op_token: NO_TOKEN,
|
op_token: None,
|
||||||
namespace: _namespace,
|
namespace: _namespace,
|
||||||
hashes,
|
hashes,
|
||||||
args,
|
args,
|
||||||
@ -1657,7 +1657,7 @@ impl Engine {
|
|||||||
|
|
||||||
match input.peek().expect(NEVER_ENDS).0 {
|
match input.peek().expect(NEVER_ENDS).0 {
|
||||||
// Function call is allowed to have reserved keyword
|
// Function call is allowed to have reserved keyword
|
||||||
Token::LeftParen | Token::Bang | Token::Unit if is_keyword_function(&s) => {
|
Token::LeftParen | Token::Bang | Token::Unit if is_keyword_function(&s).0 => {
|
||||||
Expr::Variable(
|
Expr::Variable(
|
||||||
(None, ns, 0, state.get_interned_string(*s)).into(),
|
(None, ns, 0, state.get_interned_string(*s)).into(),
|
||||||
None,
|
None,
|
||||||
@ -1800,7 +1800,10 @@ impl Engine {
|
|||||||
state.allow_capture = false;
|
state.allow_capture = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(Token::Reserved(s), ..) if is_keyword_function(s) => (),
|
(Token::Reserved(s), ..) if is_keyword_function(s).1 => (),
|
||||||
|
(Token::Reserved(s), pos) => {
|
||||||
|
return Err(PERR::Reserved(s.to_string()).into_err(*pos))
|
||||||
|
}
|
||||||
(.., pos) => return Err(PERR::PropertyExpected.into_err(*pos)),
|
(.., pos) => return Err(PERR::PropertyExpected.into_err(*pos)),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1924,7 +1927,7 @@ impl Engine {
|
|||||||
name: state.get_interned_string("-"),
|
name: state.get_interned_string("-"),
|
||||||
hashes: FnCallHashes::from_native(calc_fn_hash(None, "-", 1)),
|
hashes: FnCallHashes::from_native(calc_fn_hash(None, "-", 1)),
|
||||||
args,
|
args,
|
||||||
op_token: token,
|
op_token: Some(token),
|
||||||
capture_parent_scope: false,
|
capture_parent_scope: false,
|
||||||
}
|
}
|
||||||
.into_fn_call_expr(pos))
|
.into_fn_call_expr(pos))
|
||||||
@ -1952,7 +1955,7 @@ impl Engine {
|
|||||||
name: state.get_interned_string("+"),
|
name: state.get_interned_string("+"),
|
||||||
hashes: FnCallHashes::from_native(calc_fn_hash(None, "+", 1)),
|
hashes: FnCallHashes::from_native(calc_fn_hash(None, "+", 1)),
|
||||||
args,
|
args,
|
||||||
op_token: token,
|
op_token: Some(token),
|
||||||
capture_parent_scope: false,
|
capture_parent_scope: false,
|
||||||
}
|
}
|
||||||
.into_fn_call_expr(pos))
|
.into_fn_call_expr(pos))
|
||||||
@ -1973,7 +1976,7 @@ impl Engine {
|
|||||||
name: state.get_interned_string("!"),
|
name: state.get_interned_string("!"),
|
||||||
hashes: FnCallHashes::from_native(calc_fn_hash(None, "!", 1)),
|
hashes: FnCallHashes::from_native(calc_fn_hash(None, "!", 1)),
|
||||||
args,
|
args,
|
||||||
op_token: token,
|
op_token: Some(token),
|
||||||
capture_parent_scope: false,
|
capture_parent_scope: false,
|
||||||
}
|
}
|
||||||
.into_fn_call_expr(pos))
|
.into_fn_call_expr(pos))
|
||||||
@ -1987,7 +1990,7 @@ impl Engine {
|
|||||||
|
|
||||||
/// Make an assignment statement.
|
/// Make an assignment statement.
|
||||||
fn make_assignment_stmt(
|
fn make_assignment_stmt(
|
||||||
op: Token,
|
op: Option<Token>,
|
||||||
state: &mut ParseState,
|
state: &mut ParseState,
|
||||||
lhs: Expr,
|
lhs: Expr,
|
||||||
rhs: Expr,
|
rhs: Expr,
|
||||||
@ -2020,10 +2023,10 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let op_info = if op == NO_TOKEN {
|
let op_info = if let Some(op) = op {
|
||||||
OpAssignment::new_assignment(op_pos)
|
|
||||||
} else {
|
|
||||||
OpAssignment::new_op_assignment_from_token(op, op_pos)
|
OpAssignment::new_op_assignment_from_token(op, op_pos)
|
||||||
|
} else {
|
||||||
|
OpAssignment::new_assignment(op_pos)
|
||||||
};
|
};
|
||||||
|
|
||||||
match lhs {
|
match lhs {
|
||||||
@ -2109,9 +2112,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
// lhs.module::id - syntax error
|
// lhs.module::id - syntax error
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
(.., Expr::Variable(x, ..)) if !x.1.is_empty() => {
|
(.., Expr::Variable(x, ..)) if !x.1.is_empty() => unreachable!("lhs.ns::id"),
|
||||||
Err(PERR::PropertyExpected.into_err(x.1.position()))
|
|
||||||
}
|
|
||||||
// lhs.id
|
// lhs.id
|
||||||
(lhs, var_expr @ Expr::Variable(..)) => {
|
(lhs, var_expr @ Expr::Variable(..)) => {
|
||||||
let rhs = var_expr.into_property(state);
|
let rhs = var_expr.into_property(state);
|
||||||
@ -2125,9 +2126,7 @@ impl Engine {
|
|||||||
)),
|
)),
|
||||||
// lhs.nnn::func(...) - syntax error
|
// lhs.nnn::func(...) - syntax error
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
(.., Expr::FnCall(f, ..)) if f.is_qualified() => {
|
(.., Expr::FnCall(f, ..)) if f.is_qualified() => unreachable!("lhs.ns::func()"),
|
||||||
Err(PERR::PropertyExpected.into_err(f.namespace.position()))
|
|
||||||
}
|
|
||||||
// lhs.Fn() or lhs.eval()
|
// lhs.Fn() or lhs.eval()
|
||||||
(.., Expr::FnCall(f, func_pos))
|
(.., Expr::FnCall(f, func_pos))
|
||||||
if f.args.is_empty()
|
if f.args.is_empty()
|
||||||
@ -2174,13 +2173,11 @@ impl Engine {
|
|||||||
match x.lhs {
|
match x.lhs {
|
||||||
// lhs.module::id.dot_rhs or lhs.module::id[idx_rhs] - syntax error
|
// lhs.module::id.dot_rhs or lhs.module::id[idx_rhs] - syntax error
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
Expr::Variable(x, ..) if !x.1.is_empty() => {
|
Expr::Variable(x, ..) if !x.1.is_empty() => unreachable!("lhs.ns::id..."),
|
||||||
Err(PERR::PropertyExpected.into_err(x.1.position()))
|
|
||||||
}
|
|
||||||
// lhs.module::func().dot_rhs or lhs.module::func()[idx_rhs] - syntax error
|
// lhs.module::func().dot_rhs or lhs.module::func()[idx_rhs] - syntax error
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
Expr::FnCall(f, ..) if f.is_qualified() => {
|
Expr::FnCall(f, ..) if f.is_qualified() => {
|
||||||
Err(PERR::PropertyExpected.into_err(f.namespace.position()))
|
unreachable!("lhs.ns::func()...")
|
||||||
}
|
}
|
||||||
// lhs.id.dot_rhs or lhs.id[idx_rhs]
|
// lhs.id.dot_rhs or lhs.id[idx_rhs]
|
||||||
Expr::Variable(..) | Expr::Property(..) => {
|
Expr::Variable(..) | Expr::Property(..) => {
|
||||||
@ -2310,9 +2307,9 @@ impl Engine {
|
|||||||
let hash = calc_fn_hash(None, &op, 2);
|
let hash = calc_fn_hash(None, &op, 2);
|
||||||
let is_valid_script_function = is_valid_function_name(&op);
|
let is_valid_script_function = is_valid_function_name(&op);
|
||||||
let operator_token = if is_valid_script_function {
|
let operator_token = if is_valid_script_function {
|
||||||
NO_TOKEN
|
None
|
||||||
} else {
|
} else {
|
||||||
op_token.clone()
|
Some(op_token.clone())
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut args = StaticVec::new_const();
|
let mut args = StaticVec::new_const();
|
||||||
@ -2383,7 +2380,7 @@ impl Engine {
|
|||||||
name: state.get_interned_string(op),
|
name: state.get_interned_string(op),
|
||||||
hashes: FnCallHashes::from_native(calc_fn_hash(None, op, 1)),
|
hashes: FnCallHashes::from_native(calc_fn_hash(None, op, 1)),
|
||||||
args,
|
args,
|
||||||
op_token: Token::Bang,
|
op_token: Some(Token::Bang),
|
||||||
capture_parent_scope: false,
|
capture_parent_scope: false,
|
||||||
};
|
};
|
||||||
not_base.into_fn_call_expr(pos)
|
not_base.into_fn_call_expr(pos)
|
||||||
@ -3186,11 +3183,12 @@ impl Engine {
|
|||||||
|
|
||||||
let (op, pos) = match input.peek().expect(NEVER_ENDS) {
|
let (op, pos) = match input.peek().expect(NEVER_ENDS) {
|
||||||
// var = ...
|
// var = ...
|
||||||
(Token::Equals, ..) => (NO_TOKEN, eat_token(input, Token::Equals)),
|
(Token::Equals, ..) => (None, eat_token(input, Token::Equals)),
|
||||||
// var op= ...
|
// var op= ...
|
||||||
(token, ..) if token.is_op_assignment() => {
|
(token, ..) if token.is_op_assignment() => input
|
||||||
input.next().map(|(op, pos)| (op, pos)).expect(NEVER_ENDS)
|
.next()
|
||||||
}
|
.map(|(op, pos)| (Some(op), pos))
|
||||||
|
.expect(NEVER_ENDS),
|
||||||
// Not op-assignment
|
// Not op-assignment
|
||||||
_ => return Ok(Stmt::Expr(expr.into())),
|
_ => return Ok(Stmt::Expr(expr.into())),
|
||||||
};
|
};
|
||||||
@ -3686,7 +3684,7 @@ impl Engine {
|
|||||||
num_externals + 1,
|
num_externals + 1,
|
||||||
)),
|
)),
|
||||||
args,
|
args,
|
||||||
op_token: NO_TOKEN,
|
op_token: None,
|
||||||
capture_parent_scope: false,
|
capture_parent_scope: false,
|
||||||
}
|
}
|
||||||
.into_fn_call_expr(pos);
|
.into_fn_call_expr(pos);
|
||||||
|
@ -55,9 +55,6 @@ type LERR = LexError;
|
|||||||
/// Separator character for numbers.
|
/// Separator character for numbers.
|
||||||
const NUMBER_SEPARATOR: char = '_';
|
const NUMBER_SEPARATOR: char = '_';
|
||||||
|
|
||||||
/// No token.
|
|
||||||
pub const NO_TOKEN: Token = Token::NONE;
|
|
||||||
|
|
||||||
/// A stream of tokens.
|
/// A stream of tokens.
|
||||||
pub type TokenStream<'a> = Peekable<TokenIterator<'a>>;
|
pub type TokenStream<'a> = Peekable<TokenIterator<'a>>;
|
||||||
|
|
||||||
@ -631,41 +628,6 @@ impl Token {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Is a piece of syntax a reserved keyword?
|
|
||||||
#[must_use]
|
|
||||||
pub fn is_reserved_keyword(syntax: &str) -> bool {
|
|
||||||
match syntax {
|
|
||||||
#[cfg(feature = "no_object")]
|
|
||||||
"?." => true,
|
|
||||||
#[cfg(feature = "no_index")]
|
|
||||||
"?[" => true,
|
|
||||||
#[cfg(feature = "no_function")]
|
|
||||||
"fn" | "private" => true,
|
|
||||||
#[cfg(feature = "no_module")]
|
|
||||||
"import" | "export" | "as" => true,
|
|
||||||
|
|
||||||
// List of reserved operators
|
|
||||||
"===" | "!==" | "->" | "<-" | "?" | ":=" | ":;" | "~" | "!." | "::<" | "(*" | "*)"
|
|
||||||
| "#" | "#!" | "@" | "$" | "++" | "--" | "..." | "<|" | "|>" => true,
|
|
||||||
|
|
||||||
// List of reserved keywords
|
|
||||||
"public" | "protected" | "super" | "new" | "use" | "module" | "package" | "var"
|
|
||||||
| "static" | "shared" | "with" | "is" | "goto" | "exit" | "match" | "case"
|
|
||||||
| "default" | "void" | "null" | "nil" | "spawn" | "thread" | "go" | "sync"
|
|
||||||
| "async" | "await" | "yield" => true,
|
|
||||||
|
|
||||||
KEYWORD_PRINT | KEYWORD_DEBUG | KEYWORD_TYPE_OF | KEYWORD_EVAL | KEYWORD_FN_PTR
|
|
||||||
| KEYWORD_FN_PTR_CALL | KEYWORD_FN_PTR_CURRY | KEYWORD_THIS | KEYWORD_IS_DEF_VAR => {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
|
||||||
crate::engine::KEYWORD_IS_DEF_FN => true,
|
|
||||||
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// If another operator is after these, it's probably a unary operator
|
/// If another operator is after these, it's probably a unary operator
|
||||||
/// (not sure about `fn` name).
|
/// (not sure about `fn` name).
|
||||||
#[must_use]
|
#[must_use]
|
||||||
@ -1994,7 +1956,8 @@ fn parse_identifier_token(
|
|||||||
if let Some(token) = Token::lookup_symbol_from_syntax(&identifier) {
|
if let Some(token) = Token::lookup_symbol_from_syntax(&identifier) {
|
||||||
return (token, start_pos);
|
return (token, start_pos);
|
||||||
}
|
}
|
||||||
if Token::is_reserved_keyword(&identifier) {
|
|
||||||
|
if is_reserved_keyword_or_symbol(&identifier) {
|
||||||
return (Token::Reserved(Box::new(identifier)), start_pos);
|
return (Token::Reserved(Box::new(identifier)), start_pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2008,18 +1971,27 @@ fn parse_identifier_token(
|
|||||||
(Token::Identifier(identifier.into()), start_pos)
|
(Token::Identifier(identifier.into()), start_pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Is a keyword allowed as a function?
|
/// Can a keyword be called like a function?
|
||||||
|
///
|
||||||
|
/// # Return values
|
||||||
|
///
|
||||||
|
/// The first `bool` indicates whether the keyword can be called normally as a function.
|
||||||
|
///
|
||||||
|
/// The second `bool` indicates whether the keyword can be called in method-call style.
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn is_keyword_function(name: &str) -> bool {
|
pub fn is_keyword_function(name: &str) -> (bool, bool) {
|
||||||
match name {
|
match name {
|
||||||
KEYWORD_PRINT | KEYWORD_DEBUG | KEYWORD_TYPE_OF | KEYWORD_EVAL | KEYWORD_FN_PTR
|
KEYWORD_TYPE_OF | KEYWORD_FN_PTR_CALL | KEYWORD_FN_PTR_CURRY => (true, true),
|
||||||
| KEYWORD_FN_PTR_CALL | KEYWORD_FN_PTR_CURRY | KEYWORD_IS_DEF_VAR => true,
|
|
||||||
|
KEYWORD_PRINT | KEYWORD_DEBUG | KEYWORD_EVAL | KEYWORD_FN_PTR | KEYWORD_IS_DEF_VAR => {
|
||||||
|
(true, false)
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
crate::engine::KEYWORD_IS_DEF_FN => true,
|
crate::engine::KEYWORD_IS_DEF_FN => (true, false),
|
||||||
|
|
||||||
_ => false,
|
_ => (false, false),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2047,7 +2019,9 @@ pub fn is_valid_identifier(name: &str) -> bool {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn is_valid_function_name(name: &str) -> bool {
|
pub fn is_valid_function_name(name: &str) -> bool {
|
||||||
is_valid_identifier(name) && !is_keyword_function(name)
|
is_valid_identifier(name)
|
||||||
|
&& !is_reserved_keyword_or_symbol(name)
|
||||||
|
&& Token::lookup_symbol_from_syntax(name).is_none()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Is a character valid to start an identifier?
|
/// Is a character valid to start an identifier?
|
||||||
@ -2082,6 +2056,39 @@ pub const fn is_id_continue(x: char) -> bool {
|
|||||||
x.is_ascii_alphanumeric() || x == '_'
|
x.is_ascii_alphanumeric() || x == '_'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Is a piece of syntax a reserved keyword or symbol?
|
||||||
|
#[must_use]
|
||||||
|
pub fn is_reserved_keyword_or_symbol(syntax: &str) -> bool {
|
||||||
|
match syntax {
|
||||||
|
#[cfg(feature = "no_object")]
|
||||||
|
"?." => true,
|
||||||
|
#[cfg(feature = "no_index")]
|
||||||
|
"?[" => true,
|
||||||
|
#[cfg(feature = "no_function")]
|
||||||
|
"fn" | "private" => true,
|
||||||
|
#[cfg(feature = "no_module")]
|
||||||
|
"import" | "export" | "as" => true,
|
||||||
|
|
||||||
|
// List of reserved operators
|
||||||
|
"===" | "!==" | "->" | "<-" | "?" | ":=" | ":;" | "~" | "!." | "::<" | "(*" | "*)"
|
||||||
|
| "#" | "#!" | "@" | "$" | "++" | "--" | "..." | "<|" | "|>" => true,
|
||||||
|
|
||||||
|
// List of reserved keywords
|
||||||
|
"public" | "protected" | "super" | "new" | "use" | "module" | "package" | "var"
|
||||||
|
| "static" | "shared" | "with" | "is" | "goto" | "exit" | "match" | "case" | "default"
|
||||||
|
| "void" | "null" | "nil" | "spawn" | "thread" | "go" | "sync" | "async" | "await"
|
||||||
|
| "yield" => true,
|
||||||
|
|
||||||
|
KEYWORD_PRINT | KEYWORD_DEBUG | KEYWORD_TYPE_OF | KEYWORD_EVAL | KEYWORD_FN_PTR
|
||||||
|
| KEYWORD_FN_PTR_CALL | KEYWORD_FN_PTR_CURRY | KEYWORD_THIS | KEYWORD_IS_DEF_VAR => true,
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
crate::engine::KEYWORD_IS_DEF_FN => true,
|
||||||
|
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// _(internals)_ A type that implements the [`InputStream`] trait.
|
/// _(internals)_ A type that implements the [`InputStream`] trait.
|
||||||
/// Exported under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
///
|
///
|
||||||
|
@ -2,11 +2,11 @@
|
|||||||
|
|
||||||
use crate::eval::GlobalRuntimeState;
|
use crate::eval::GlobalRuntimeState;
|
||||||
use crate::func::EncapsulatedEnviron;
|
use crate::func::EncapsulatedEnviron;
|
||||||
use crate::tokenizer::is_valid_function_name;
|
use crate::tokenizer::{is_reserved_keyword_or_symbol, is_valid_function_name, Token};
|
||||||
use crate::types::dynamic::Variant;
|
use crate::types::dynamic::Variant;
|
||||||
use crate::{
|
use crate::{
|
||||||
Dynamic, Engine, FnArgsVec, FuncArgs, ImmutableString, NativeCallContext, Position, RhaiError,
|
Dynamic, Engine, FnArgsVec, FuncArgs, ImmutableString, NativeCallContext, ParseErrorType,
|
||||||
RhaiResult, RhaiResultOf, Shared, StaticVec, AST, ERR,
|
Position, RhaiError, RhaiResult, RhaiResultOf, Shared, StaticVec, AST, ERR,
|
||||||
};
|
};
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
@ -360,7 +360,9 @@ impl FnPtr {
|
|||||||
/// arguments attached.
|
/// arguments attached.
|
||||||
///
|
///
|
||||||
/// If `this_ptr` is provided, it is first provided to script-defined functions bound to `this`.
|
/// If `this_ptr` is provided, it is first provided to script-defined functions bound to `this`.
|
||||||
/// When an appropriate function is not found, it is then removed and mapped to the first parameter.
|
///
|
||||||
|
/// When an appropriate function is not found and `move_this_ptr_to_args` is `Some`, `this_ptr`
|
||||||
|
/// is removed and inserted as the appropriate parameter number.
|
||||||
///
|
///
|
||||||
/// This is useful for calling predicate closures within an iteration loop where the extra argument
|
/// This is useful for calling predicate closures within an iteration loop where the extra argument
|
||||||
/// is the current element's index.
|
/// is the current element's index.
|
||||||
@ -374,17 +376,19 @@ impl FnPtr {
|
|||||||
fn_name: &str,
|
fn_name: &str,
|
||||||
ctx: &NativeCallContext,
|
ctx: &NativeCallContext,
|
||||||
this_ptr: Option<&mut Dynamic>,
|
this_ptr: Option<&mut Dynamic>,
|
||||||
items: [Dynamic; N],
|
args: [Dynamic; N],
|
||||||
extras: [Dynamic; E],
|
extras: [Dynamic; E],
|
||||||
|
move_this_ptr_to_args: Option<usize>,
|
||||||
) -> RhaiResult {
|
) -> RhaiResult {
|
||||||
self._call_with_extra_args(fn_name, ctx, this_ptr, items, extras)
|
self._call_with_extra_args(fn_name, ctx, this_ptr, args, extras, move_this_ptr_to_args)
|
||||||
}
|
}
|
||||||
/// _(internals)_ Make a call to a function pointer with either a specified number of arguments,
|
/// _(internals)_ Make a call to a function pointer with either a specified number of arguments,
|
||||||
/// or with extra arguments attached.
|
/// or with extra arguments attached. Exported under the `internals` feature only.
|
||||||
/// Exported under the `internals` feature only.
|
|
||||||
///
|
///
|
||||||
/// If `this_ptr` is provided, it is first provided to script-defined functions bound to `this`.
|
/// If `this_ptr` is provided, it is first provided to script-defined functions bound to `this`.
|
||||||
/// When an appropriate function is not found, it is then removed and mapped to the first parameter.
|
///
|
||||||
|
/// When an appropriate function is not found and `move_this_ptr_to_args` is `Some`, `this_ptr`
|
||||||
|
/// is removed and inserted as the appropriate parameter number.
|
||||||
///
|
///
|
||||||
/// This is useful for calling predicate closures within an iteration loop where the extra
|
/// This is useful for calling predicate closures within an iteration loop where the extra
|
||||||
/// argument is the current element's index.
|
/// argument is the current element's index.
|
||||||
@ -398,10 +402,11 @@ impl FnPtr {
|
|||||||
fn_name: &str,
|
fn_name: &str,
|
||||||
ctx: &NativeCallContext,
|
ctx: &NativeCallContext,
|
||||||
this_ptr: Option<&mut Dynamic>,
|
this_ptr: Option<&mut Dynamic>,
|
||||||
items: [Dynamic; N],
|
args: [Dynamic; N],
|
||||||
extras: [Dynamic; E],
|
extras: [Dynamic; E],
|
||||||
|
move_this_ptr_to_args: Option<usize>,
|
||||||
) -> RhaiResult {
|
) -> RhaiResult {
|
||||||
self._call_with_extra_args(fn_name, ctx, this_ptr, items, extras)
|
self._call_with_extra_args(fn_name, ctx, this_ptr, args, extras, move_this_ptr_to_args)
|
||||||
}
|
}
|
||||||
/// Make a call to a function pointer with either a specified number of arguments, or with extra
|
/// Make a call to a function pointer with either a specified number of arguments, or with extra
|
||||||
/// arguments attached.
|
/// arguments attached.
|
||||||
@ -410,51 +415,94 @@ impl FnPtr {
|
|||||||
fn_name: &str,
|
fn_name: &str,
|
||||||
ctx: &NativeCallContext,
|
ctx: &NativeCallContext,
|
||||||
mut this_ptr: Option<&mut Dynamic>,
|
mut this_ptr: Option<&mut Dynamic>,
|
||||||
items: [Dynamic; N],
|
args: [Dynamic; N],
|
||||||
extras: [Dynamic; E],
|
extras: [Dynamic; E],
|
||||||
|
move_this_ptr_to_args: Option<usize>,
|
||||||
) -> RhaiResult {
|
) -> RhaiResult {
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
if let Some(arity) = self.fn_def().map(|f| f.params.len()) {
|
if let Some(arity) = self.fn_def().map(|f| f.params.len()) {
|
||||||
if arity == N + 1 && this_ptr.is_some() {
|
if arity == N + self.curry().len() {
|
||||||
let mut args = FnArgsVec::with_capacity(items.len() + 1);
|
return self.call_raw(ctx, this_ptr, args);
|
||||||
args.push(this_ptr.as_mut().unwrap().clone());
|
|
||||||
args.extend(items);
|
|
||||||
return self.call_raw(ctx, None, args);
|
|
||||||
}
|
}
|
||||||
if arity == N {
|
if let Some(move_to_args) = move_this_ptr_to_args {
|
||||||
return self.call_raw(ctx, this_ptr, items);
|
if this_ptr.is_some() {
|
||||||
|
if arity == N + 1 + self.curry().len() {
|
||||||
|
let mut args2 = FnArgsVec::with_capacity(args.len() + 1);
|
||||||
|
if move_to_args == 0 {
|
||||||
|
args2.push(this_ptr.as_mut().unwrap().clone());
|
||||||
|
args2.extend(args);
|
||||||
|
} else {
|
||||||
|
args2.extend(args);
|
||||||
|
args2.insert(move_to_args, this_ptr.as_mut().unwrap().clone());
|
||||||
}
|
}
|
||||||
if arity == N + E {
|
return self.call_raw(ctx, None, args2);
|
||||||
let mut items2 = FnArgsVec::with_capacity(items.len() + extras.len());
|
}
|
||||||
items2.extend(IntoIterator::into_iter(items));
|
if arity == N + E + 1 + self.curry().len() {
|
||||||
items2.extend(IntoIterator::into_iter(extras));
|
let mut args2 = FnArgsVec::with_capacity(args.len() + extras.len() + 1);
|
||||||
return self.call_raw(ctx, this_ptr, items2);
|
if move_to_args == 0 {
|
||||||
|
args2.push(this_ptr.as_mut().unwrap().clone());
|
||||||
|
args2.extend(args);
|
||||||
|
args2.extend(extras);
|
||||||
|
} else {
|
||||||
|
args2.extend(args);
|
||||||
|
args2.insert(move_to_args, this_ptr.as_mut().unwrap().clone());
|
||||||
|
args2.extend(extras);
|
||||||
|
}
|
||||||
|
return self.call_raw(ctx, None, args2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if arity == N + E + self.curry().len() {
|
||||||
|
let mut args2 = FnArgsVec::with_capacity(args.len() + extras.len());
|
||||||
|
args2.extend(args);
|
||||||
|
args2.extend(extras);
|
||||||
|
return self.call_raw(ctx, this_ptr, args2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.call_raw(ctx, this_ptr.as_deref_mut(), items.clone())
|
self.call_raw(ctx, this_ptr.as_deref_mut(), args.clone())
|
||||||
.or_else(|err| match *err {
|
.or_else(|err| match *err {
|
||||||
ERR::ErrorFunctionNotFound(sig, ..)
|
ERR::ErrorFunctionNotFound(sig, ..)
|
||||||
if this_ptr.is_some() && sig.starts_with(self.fn_name()) =>
|
if move_this_ptr_to_args.is_some()
|
||||||
|
&& this_ptr.is_some()
|
||||||
|
&& sig.starts_with(self.fn_name()) =>
|
||||||
{
|
{
|
||||||
let mut args = FnArgsVec::with_capacity(items.len() + 1);
|
let mut args2 = FnArgsVec::with_capacity(args.len() + 1);
|
||||||
args.push(this_ptr.as_mut().unwrap().clone());
|
let move_to_args = move_this_ptr_to_args.unwrap();
|
||||||
args.extend(IntoIterator::into_iter(items.clone()));
|
if move_to_args == 0 {
|
||||||
self.call_raw(ctx, this_ptr.as_deref_mut(), args)
|
args2.push(this_ptr.as_mut().unwrap().clone());
|
||||||
|
args2.extend(args.clone());
|
||||||
|
} else {
|
||||||
|
args2.extend(args.clone());
|
||||||
|
args2.insert(move_to_args, this_ptr.as_mut().unwrap().clone());
|
||||||
|
}
|
||||||
|
self.call_raw(ctx, None, args2)
|
||||||
}
|
}
|
||||||
_ => Err(err),
|
_ => Err(err),
|
||||||
})
|
})
|
||||||
.or_else(|err| match *err {
|
.or_else(|err| match *err {
|
||||||
ERR::ErrorFunctionNotFound(sig, ..) if sig.starts_with(self.fn_name()) => {
|
ERR::ErrorFunctionNotFound(sig, ..) if sig.starts_with(self.fn_name()) => {
|
||||||
let mut args = FnArgsVec::with_capacity(
|
if let Some(move_to_args) = move_this_ptr_to_args {
|
||||||
items.len() + extras.len() + if this_ptr.is_some() { 1 } else { 0 },
|
|
||||||
);
|
|
||||||
if let Some(ref mut this_ptr) = this_ptr {
|
if let Some(ref mut this_ptr) = this_ptr {
|
||||||
args.push(this_ptr.clone());
|
let mut args2 = FnArgsVec::with_capacity(args.len() + extras.len() + 1);
|
||||||
|
if move_to_args == 0 {
|
||||||
|
args2.push(this_ptr.clone());
|
||||||
|
args2.extend(args);
|
||||||
|
args2.extend(extras);
|
||||||
|
} else {
|
||||||
|
args2.extend(args);
|
||||||
|
args2.extend(extras);
|
||||||
|
args2.insert(move_to_args, this_ptr.clone());
|
||||||
}
|
}
|
||||||
args.extend(IntoIterator::into_iter(items));
|
return self.call_raw(ctx, None, args2);
|
||||||
args.extend(IntoIterator::into_iter(extras));
|
}
|
||||||
self.call_raw(ctx, this_ptr, args)
|
}
|
||||||
|
|
||||||
|
let mut args2 = FnArgsVec::with_capacity(args.len() + extras.len());
|
||||||
|
args2.extend(args);
|
||||||
|
args2.extend(extras);
|
||||||
|
|
||||||
|
self.call_raw(ctx, this_ptr, args2)
|
||||||
}
|
}
|
||||||
_ => Err(err),
|
_ => Err(err),
|
||||||
})
|
})
|
||||||
@ -488,6 +536,13 @@ impl TryFrom<ImmutableString> for FnPtr {
|
|||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
fn_def: None,
|
fn_def: None,
|
||||||
})
|
})
|
||||||
|
} else if is_reserved_keyword_or_symbol(&value)
|
||||||
|
|| Token::lookup_symbol_from_syntax(&value).is_some()
|
||||||
|
{
|
||||||
|
Err(
|
||||||
|
ERR::ErrorParsing(ParseErrorType::Reserved(value.to_string()), Position::NONE)
|
||||||
|
.into(),
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
Err(ERR::ErrorFunctionNotFound(value.to_string(), Position::NONE).into())
|
Err(ERR::ErrorFunctionNotFound(value.to_string(), Position::NONE).into())
|
||||||
}
|
}
|
||||||
|
@ -299,11 +299,31 @@ fn test_arrays_map_reduce() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
assert_eq!(engine.eval::<INT>("[1].map(|x| x + 41)[0]")?, 42);
|
assert_eq!(engine.eval::<INT>("[1].map(|x| x + 41)[0]")?, 42);
|
||||||
assert_eq!(engine.eval::<INT>("[1].map(|| this + 41)[0]")?, 42);
|
assert_eq!(engine.eval::<INT>("[1].map(|| this + 41)[0]")?, 42);
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<INT>("let x = [1, 2, 3]; x.for_each(|| this += 41); x[0]")?,
|
||||||
|
42
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<INT>(
|
||||||
|
"
|
||||||
|
let x = [1, 2, 3];
|
||||||
|
let sum = 0;
|
||||||
|
let factor = 2;
|
||||||
|
x.for_each(|| sum += this * factor);
|
||||||
|
sum
|
||||||
|
"
|
||||||
|
)?,
|
||||||
|
12
|
||||||
|
);
|
||||||
assert_eq!(engine.eval::<INT>("([1].map(|x| x + 41))[0]")?, 42);
|
assert_eq!(engine.eval::<INT>("([1].map(|x| x + 41))[0]")?, 42);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<INT>("let c = 40; let y = 1; [1].map(|x, i| c + x + y + i)[0]")?,
|
engine.eval::<INT>("let c = 40; let y = 1; [1].map(|x, i| c + x + y + i)[0]")?,
|
||||||
42
|
42
|
||||||
);
|
);
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<INT>("let x = [1, 2, 3]; x.for_each(|i| this += i); x[2]")?,
|
||||||
|
5
|
||||||
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine
|
engine
|
||||||
@ -387,18 +407,31 @@ fn test_arrays_map_reduce() -> Result<(), Box<EvalAltResult>> {
|
|||||||
14
|
14
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
// assert_eq!(
|
||||||
engine.eval::<INT>(
|
// engine.eval::<INT>(
|
||||||
"
|
// "
|
||||||
let x = [1, 2, 3];
|
// let x = [1, 2, 3];
|
||||||
x.reduce(|sum, v, i| {
|
// x.reduce(|sum, v, i| {
|
||||||
if i == 0 { sum = 10 }
|
// if i == 0 { sum = 10 }
|
||||||
sum + v * v
|
// sum + v * v
|
||||||
})
|
// })
|
||||||
"
|
// "
|
||||||
)?,
|
// )?,
|
||||||
24
|
// 24
|
||||||
);
|
// );
|
||||||
|
|
||||||
|
// assert_eq!(
|
||||||
|
// engine.eval::<INT>(
|
||||||
|
// "
|
||||||
|
// let x = [1, 2, 3];
|
||||||
|
// x.reduce(|sum, i| {
|
||||||
|
// if i == 0 { sum = 10 }
|
||||||
|
// sum + this * this
|
||||||
|
// })
|
||||||
|
// "
|
||||||
|
// )?,
|
||||||
|
// 24
|
||||||
|
// );
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<INT>(
|
engine.eval::<INT>(
|
||||||
|
@ -175,7 +175,7 @@ fn test_max_array_size() -> Result<(), Box<EvalAltResult>> {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<INT>(
|
engine.eval::<INT>(
|
||||||
"
|
"
|
||||||
let x = [1,2,3];
|
let x = [1,2];
|
||||||
len([x, x, x])
|
len([x, x, x])
|
||||||
"
|
"
|
||||||
)?,
|
)?,
|
||||||
|
@ -332,7 +332,8 @@ fn test_string_split() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_string_interpolated() -> Result<(), Box<EvalAltResult>> {
|
fn test_string_interpolated() -> Result<(), Box<EvalAltResult>> {
|
||||||
let engine = Engine::new();
|
// Make sure strings interpolation works even under raw
|
||||||
|
let engine = Engine::new_raw();
|
||||||
|
|
||||||
assert_eq!(engine.eval::<String>("`${}`")?, "");
|
assert_eq!(engine.eval::<String>("`${}`")?, "");
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user