Add short-circuits to op-assignments.
This commit is contained in:
parent
bb404a415d
commit
906ab3a295
@ -14,6 +14,7 @@ Bug fixes
|
||||
* Expressions such as `!inside` now parses correctly instead of as `!in` followed by `side`.
|
||||
* Custom syntax starting with symbols now works correctly and no longer raises a parse error.
|
||||
* Comparing different custom types now works correctly when the appropriate comparison operators are registered.
|
||||
* Op-assignments to bit flags or bit ranges now work correctly.
|
||||
|
||||
Potentially breaking changes
|
||||
----------------------------
|
||||
|
@ -7,6 +7,7 @@ use crate::ast::{
|
||||
SwitchCasesCollection,
|
||||
};
|
||||
use crate::func::{get_builtin_op_assignment_fn, get_hasher};
|
||||
use crate::tokenizer::Token;
|
||||
use crate::types::dynamic::{AccessMode, Union};
|
||||
use crate::{Dynamic, Engine, RhaiResult, RhaiResultOf, Scope, ERR, INT};
|
||||
use std::hash::{Hash, Hasher};
|
||||
@ -130,24 +131,102 @@ impl Engine {
|
||||
|
||||
if let Some((hash_x, hash, op_x, op_x_str, op, op_str)) = op_info.get_op_assignment_info() {
|
||||
let mut lock_guard = target.write_lock::<Dynamic>().unwrap();
|
||||
let args = &mut [&mut *lock_guard, &mut new_val];
|
||||
let mut done = false;
|
||||
|
||||
// Short-circuit built-in op-assignments if under Fast Operators mode
|
||||
if self.fast_operators() {
|
||||
if let Some((func, need_context)) =
|
||||
get_builtin_op_assignment_fn(op_x, args[0], args[1])
|
||||
{
|
||||
// Built-in found
|
||||
auto_restore! { let orig_level = global.level; global.level += 1 }
|
||||
#[allow(clippy::wildcard_imports)]
|
||||
use Token::*;
|
||||
|
||||
done = true;
|
||||
|
||||
// For extremely simple primary data operations, do it directly
|
||||
// to avoid the overhead of calling a function.
|
||||
match (&mut lock_guard.0, &mut new_val.0) {
|
||||
(Union::Bool(b1, ..), Union::Bool(b2, ..)) => match op_x {
|
||||
AndAssign => *b1 = *b1 && *b2,
|
||||
OrAssign => *b1 = *b1 || *b2,
|
||||
XOrAssign => *b1 = *b1 ^ *b2,
|
||||
_ => done = false,
|
||||
},
|
||||
(Union::Int(n1, ..), Union::Int(n2, ..)) => {
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
#[allow(clippy::wildcard_imports)]
|
||||
use crate::packages::arithmetic::arith_basic::INT::functions::*;
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
match op_x {
|
||||
PlusAssign => {
|
||||
*n1 = add(*n1, *n2).map_err(|err| err.fill_position(pos))?
|
||||
}
|
||||
MinusAssign => {
|
||||
*n1 = subtract(*n1, *n2).map_err(|err| err.fill_position(pos))?
|
||||
}
|
||||
MultiplyAssign => {
|
||||
*n1 = multiply(*n1, *n2).map_err(|err| err.fill_position(pos))?
|
||||
}
|
||||
DivideAssign => {
|
||||
*n1 = divide(*n1, *n2).map_err(|err| err.fill_position(pos))?
|
||||
}
|
||||
ModuloAssign => {
|
||||
*n1 = modulo(*n1, *n2).map_err(|err| err.fill_position(pos))?
|
||||
}
|
||||
_ => done = false,
|
||||
}
|
||||
#[cfg(feature = "unchecked")]
|
||||
match op_x {
|
||||
PlusAssign => *n1 += *n2,
|
||||
MinusAssign => *n1 -= *n2,
|
||||
MultiplyAssign => *n1 *= *n2,
|
||||
DivideAssign => *n1 /= *n2,
|
||||
ModuloAssign => *n1 %= *n2,
|
||||
_ => done = false,
|
||||
}
|
||||
}
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
(Union::Float(f1, ..), Union::Float(f2, ..)) => match op_x {
|
||||
PlusAssign => **f1 += **f2,
|
||||
MinusAssign => **f1 -= **f2,
|
||||
MultiplyAssign => **f1 *= **f2,
|
||||
DivideAssign => **f1 /= **f2,
|
||||
ModuloAssign => **f1 %= **f2,
|
||||
_ => done = false,
|
||||
},
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
(Union::Float(f1, ..), Union::Int(n2, ..)) => match op_x {
|
||||
PlusAssign => **f1 += *n2 as crate::FLOAT,
|
||||
MinusAssign => **f1 -= *n2 as crate::FLOAT,
|
||||
MultiplyAssign => **f1 *= *n2 as crate::FLOAT,
|
||||
DivideAssign => **f1 /= *n2 as crate::FLOAT,
|
||||
ModuloAssign => **f1 %= *n2 as crate::FLOAT,
|
||||
_ => done = false,
|
||||
},
|
||||
_ => done = false,
|
||||
}
|
||||
|
||||
if !done {
|
||||
if let Some((func, need_context)) =
|
||||
get_builtin_op_assignment_fn(op_x, &*lock_guard, &new_val)
|
||||
{
|
||||
// We may not need to bump the level because built-in's do not need it.
|
||||
//auto_restore! { let orig_level = global.level; global.level += 1 }
|
||||
|
||||
let args = &mut [&mut *lock_guard, &mut new_val];
|
||||
let context = need_context
|
||||
.then(|| (self, op_x_str, global.source(), &*global, pos).into());
|
||||
return func(context, args).map(|_| ());
|
||||
let _ = func(context, args).map_err(|err| err.fill_position(pos))?;
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !done {
|
||||
let opx = Some(op_x);
|
||||
let args = &mut [&mut *lock_guard, &mut new_val];
|
||||
|
||||
match self.exec_native_fn_call(global, caches, op_x_str, opx, hash_x, args, true, pos) {
|
||||
match self
|
||||
.exec_native_fn_call(global, caches, op_x_str, opx, hash_x, args, true, pos)
|
||||
{
|
||||
Ok(_) => (),
|
||||
Err(err) if matches!(*err, ERR::ErrorFunctionNotFound(ref f, ..) if f.starts_with(op_x_str)) =>
|
||||
{
|
||||
@ -162,6 +241,7 @@ impl Engine {
|
||||
}
|
||||
|
||||
self.check_data_size(&*args[0], root.position())?;
|
||||
}
|
||||
} else {
|
||||
// Normal assignment
|
||||
match target {
|
||||
|
@ -684,6 +684,7 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt
|
||||
return match op {
|
||||
AndAssign => impl_op!(bool = x && as_bool),
|
||||
OrAssign => impl_op!(bool = x || as_bool),
|
||||
XOrAssign => impl_op!(bool = x ^ as_bool),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
|
170
src/func/call.rs
170
src/func/call.rs
@ -1547,6 +1547,9 @@ impl Engine {
|
||||
/// # Main Entry-Point (`FnCallExpr`)
|
||||
///
|
||||
/// Evaluate a function call expression.
|
||||
///
|
||||
/// This method tries to short-circuit function resolution under Fast Operators mode if the
|
||||
/// function call is an operator.
|
||||
pub(crate) fn eval_fn_call_expr(
|
||||
&self,
|
||||
global: &mut GlobalRuntimeState,
|
||||
@ -1570,28 +1573,29 @@ impl Engine {
|
||||
let op_token = op_token.as_ref();
|
||||
|
||||
// Short-circuit native unary operator call if under Fast Operators mode
|
||||
if op_token == Some(&Token::Bang) && self.fast_operators() && args.len() == 1 {
|
||||
if self.fast_operators() && args.len() == 1 && op_token == Some(&Token::Bang) {
|
||||
let mut value = self
|
||||
.get_arg_value(global, caches, scope, this_ptr.as_deref_mut(), &args[0])?
|
||||
.0
|
||||
.flatten();
|
||||
|
||||
match value.0 {
|
||||
Union::Bool(b, ..) => return Ok((!b).into()),
|
||||
_ => (),
|
||||
}
|
||||
|
||||
return value.as_bool().map(|r| (!r).into()).or_else(|_| {
|
||||
return match value.0 {
|
||||
Union::Bool(b, ..) => Ok((!b).into()),
|
||||
_ => {
|
||||
let operand = &mut [&mut value];
|
||||
self.exec_fn_call(
|
||||
global, caches, None, name, op_token, *hashes, operand, false, false, pos,
|
||||
)
|
||||
.map(|(v, ..)| v)
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Short-circuit native binary operator call if under Fast Operators mode
|
||||
if op_token.is_some() && self.fast_operators() && args.len() == 2 {
|
||||
#[allow(clippy::wildcard_imports)]
|
||||
use Token::*;
|
||||
|
||||
let mut lhs = self
|
||||
.get_arg_value(global, caches, scope, this_ptr.as_deref_mut(), &args[0])?
|
||||
.0
|
||||
@ -1606,23 +1610,19 @@ impl Engine {
|
||||
// to avoid the overhead of calling a function.
|
||||
match (&lhs.0, &rhs.0) {
|
||||
(Union::Unit(..), Union::Unit(..)) => match op_token.unwrap() {
|
||||
Token::EqualsTo => return Ok(Dynamic::TRUE),
|
||||
Token::NotEqualsTo
|
||||
| Token::GreaterThan
|
||||
| Token::GreaterThanEqualsTo
|
||||
| Token::LessThan
|
||||
| Token::LessThanEqualsTo => return Ok(Dynamic::FALSE),
|
||||
EqualsTo => return Ok(Dynamic::TRUE),
|
||||
NotEqualsTo | GreaterThan | GreaterThanEqualsTo | LessThan
|
||||
| LessThanEqualsTo => return Ok(Dynamic::FALSE),
|
||||
_ => (),
|
||||
},
|
||||
(Union::Bool(b1, ..), Union::Bool(b2, ..)) => match op_token.unwrap() {
|
||||
Token::EqualsTo => return Ok((*b1 == *b2).into()),
|
||||
Token::NotEqualsTo => return Ok((*b1 != *b2).into()),
|
||||
Token::GreaterThan
|
||||
| Token::GreaterThanEqualsTo
|
||||
| Token::LessThan
|
||||
| Token::LessThanEqualsTo => return Ok(Dynamic::FALSE),
|
||||
Token::Pipe => return Ok((*b1 || *b2).into()),
|
||||
Token::Ampersand => return Ok((*b1 && *b2).into()),
|
||||
EqualsTo => return Ok((*b1 == *b2).into()),
|
||||
NotEqualsTo => return Ok((*b1 != *b2).into()),
|
||||
GreaterThan | GreaterThanEqualsTo | LessThan | LessThanEqualsTo => {
|
||||
return Ok(Dynamic::FALSE)
|
||||
}
|
||||
Pipe => return Ok((*b1 || *b2).into()),
|
||||
Ampersand => return Ok((*b1 && *b2).into()),
|
||||
_ => (),
|
||||
},
|
||||
(Union::Int(n1, ..), Union::Int(n2, ..)) => {
|
||||
@ -1632,87 +1632,87 @@ impl Engine {
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
match op_token.unwrap() {
|
||||
Token::EqualsTo => return Ok((*n1 == *n2).into()),
|
||||
Token::NotEqualsTo => return Ok((*n1 != *n2).into()),
|
||||
Token::GreaterThan => return Ok((*n1 > *n2).into()),
|
||||
Token::GreaterThanEqualsTo => return Ok((*n1 >= *n2).into()),
|
||||
Token::LessThan => return Ok((*n1 < *n2).into()),
|
||||
Token::LessThanEqualsTo => return Ok((*n1 <= *n2).into()),
|
||||
Token::Plus => return add(*n1, *n2).map(Into::into),
|
||||
Token::Minus => return subtract(*n1, *n2).map(Into::into),
|
||||
Token::Multiply => return multiply(*n1, *n2).map(Into::into),
|
||||
Token::Divide => return divide(*n1, *n2).map(Into::into),
|
||||
Token::Modulo => return modulo(*n1, *n2).map(Into::into),
|
||||
EqualsTo => return Ok((*n1 == *n2).into()),
|
||||
NotEqualsTo => return Ok((*n1 != *n2).into()),
|
||||
GreaterThan => return Ok((*n1 > *n2).into()),
|
||||
GreaterThanEqualsTo => return Ok((*n1 >= *n2).into()),
|
||||
LessThan => return Ok((*n1 < *n2).into()),
|
||||
LessThanEqualsTo => return Ok((*n1 <= *n2).into()),
|
||||
Plus => return add(*n1, *n2).map(Into::into),
|
||||
Minus => return subtract(*n1, *n2).map(Into::into),
|
||||
Multiply => return multiply(*n1, *n2).map(Into::into),
|
||||
Divide => return divide(*n1, *n2).map(Into::into),
|
||||
Modulo => return modulo(*n1, *n2).map(Into::into),
|
||||
_ => (),
|
||||
}
|
||||
#[cfg(feature = "unchecked")]
|
||||
match op_token.unwrap() {
|
||||
Token::EqualsTo => return Ok((*n1 == *n2).into()),
|
||||
Token::NotEqualsTo => return Ok((*n1 != *n2).into()),
|
||||
Token::GreaterThan => return Ok((*n1 > *n2).into()),
|
||||
Token::GreaterThanEqualsTo => return Ok((*n1 >= *n2).into()),
|
||||
Token::LessThan => return Ok((*n1 < *n2).into()),
|
||||
Token::LessThanEqualsTo => return Ok((*n1 <= *n2).into()),
|
||||
Token::Plus => return Ok((*n1 + *n2).into()),
|
||||
Token::Minus => return Ok((*n1 - *n2).into()),
|
||||
Token::Multiply => return Ok((*n1 * *n2).into()),
|
||||
Token::Divide => return Ok((*n1 / *n2).into()),
|
||||
Token::Modulo => return Ok((*n1 % *n2).into()),
|
||||
EqualsTo => return Ok((*n1 == *n2).into()),
|
||||
NotEqualsTo => return Ok((*n1 != *n2).into()),
|
||||
GreaterThan => return Ok((*n1 > *n2).into()),
|
||||
GreaterThanEqualsTo => return Ok((*n1 >= *n2).into()),
|
||||
LessThan => return Ok((*n1 < *n2).into()),
|
||||
LessThanEqualsTo => return Ok((*n1 <= *n2).into()),
|
||||
Plus => return Ok((*n1 + *n2).into()),
|
||||
Minus => return Ok((*n1 - *n2).into()),
|
||||
Multiply => return Ok((*n1 * *n2).into()),
|
||||
Divide => return Ok((*n1 / *n2).into()),
|
||||
Modulo => return Ok((*n1 % *n2).into()),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
(Union::Float(f1, ..), Union::Float(f2, ..)) => match op_token.unwrap() {
|
||||
Token::EqualsTo => return Ok((**f1 == **f2).into()),
|
||||
Token::NotEqualsTo => return Ok((**f1 != **f2).into()),
|
||||
Token::GreaterThan => return Ok((**f1 > **f2).into()),
|
||||
Token::GreaterThanEqualsTo => return Ok((**f1 >= **f2).into()),
|
||||
Token::LessThan => return Ok((**f1 < **f2).into()),
|
||||
Token::LessThanEqualsTo => return Ok((**f1 <= **f2).into()),
|
||||
Token::Plus => return Ok((**f1 + **f2).into()),
|
||||
Token::Minus => return Ok((**f1 - **f2).into()),
|
||||
Token::Multiply => return Ok((**f1 * **f2).into()),
|
||||
Token::Divide => return Ok((**f1 / **f2).into()),
|
||||
Token::Modulo => return Ok((**f1 % **f2).into()),
|
||||
EqualsTo => return Ok((**f1 == **f2).into()),
|
||||
NotEqualsTo => return Ok((**f1 != **f2).into()),
|
||||
GreaterThan => return Ok((**f1 > **f2).into()),
|
||||
GreaterThanEqualsTo => return Ok((**f1 >= **f2).into()),
|
||||
LessThan => return Ok((**f1 < **f2).into()),
|
||||
LessThanEqualsTo => return Ok((**f1 <= **f2).into()),
|
||||
Plus => return Ok((**f1 + **f2).into()),
|
||||
Minus => return Ok((**f1 - **f2).into()),
|
||||
Multiply => return Ok((**f1 * **f2).into()),
|
||||
Divide => return Ok((**f1 / **f2).into()),
|
||||
Modulo => return Ok((**f1 % **f2).into()),
|
||||
_ => (),
|
||||
},
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
(Union::Float(f1, ..), Union::Int(n2, ..)) => match op_token.unwrap() {
|
||||
Token::EqualsTo => return Ok((**f1 == (*n2 as crate::FLOAT)).into()),
|
||||
Token::NotEqualsTo => return Ok((**f1 != (*n2 as crate::FLOAT)).into()),
|
||||
Token::GreaterThan => return Ok((**f1 > (*n2 as crate::FLOAT)).into()),
|
||||
Token::GreaterThanEqualsTo => return Ok((**f1 >= (*n2 as crate::FLOAT)).into()),
|
||||
Token::LessThan => return Ok((**f1 < (*n2 as crate::FLOAT)).into()),
|
||||
Token::LessThanEqualsTo => return Ok((**f1 <= (*n2 as crate::FLOAT)).into()),
|
||||
Token::Plus => return Ok((**f1 + (*n2 as crate::FLOAT)).into()),
|
||||
Token::Minus => return Ok((**f1 - (*n2 as crate::FLOAT)).into()),
|
||||
Token::Multiply => return Ok((**f1 * (*n2 as crate::FLOAT)).into()),
|
||||
Token::Divide => return Ok((**f1 / (*n2 as crate::FLOAT)).into()),
|
||||
Token::Modulo => return Ok((**f1 % (*n2 as crate::FLOAT)).into()),
|
||||
EqualsTo => return Ok((**f1 == (*n2 as crate::FLOAT)).into()),
|
||||
NotEqualsTo => return Ok((**f1 != (*n2 as crate::FLOAT)).into()),
|
||||
GreaterThan => return Ok((**f1 > (*n2 as crate::FLOAT)).into()),
|
||||
GreaterThanEqualsTo => return Ok((**f1 >= (*n2 as crate::FLOAT)).into()),
|
||||
LessThan => return Ok((**f1 < (*n2 as crate::FLOAT)).into()),
|
||||
LessThanEqualsTo => return Ok((**f1 <= (*n2 as crate::FLOAT)).into()),
|
||||
Plus => return Ok((**f1 + (*n2 as crate::FLOAT)).into()),
|
||||
Minus => return Ok((**f1 - (*n2 as crate::FLOAT)).into()),
|
||||
Multiply => return Ok((**f1 * (*n2 as crate::FLOAT)).into()),
|
||||
Divide => return Ok((**f1 / (*n2 as crate::FLOAT)).into()),
|
||||
Modulo => return Ok((**f1 % (*n2 as crate::FLOAT)).into()),
|
||||
_ => (),
|
||||
},
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
(Union::Int(n1, ..), Union::Float(f2, ..)) => match op_token.unwrap() {
|
||||
Token::EqualsTo => return Ok(((*n1 as crate::FLOAT) == **f2).into()),
|
||||
Token::NotEqualsTo => return Ok(((*n1 as crate::FLOAT) != **f2).into()),
|
||||
Token::GreaterThan => return Ok(((*n1 as crate::FLOAT) > **f2).into()),
|
||||
Token::GreaterThanEqualsTo => return Ok(((*n1 as crate::FLOAT) >= **f2).into()),
|
||||
Token::LessThan => return Ok(((*n1 as crate::FLOAT) < **f2).into()),
|
||||
Token::LessThanEqualsTo => return Ok(((*n1 as crate::FLOAT) <= **f2).into()),
|
||||
Token::Plus => return Ok(((*n1 as crate::FLOAT) + **f2).into()),
|
||||
Token::Minus => return Ok(((*n1 as crate::FLOAT) - **f2).into()),
|
||||
Token::Multiply => return Ok(((*n1 as crate::FLOAT) * **f2).into()),
|
||||
Token::Divide => return Ok(((*n1 as crate::FLOAT) / **f2).into()),
|
||||
Token::Modulo => return Ok(((*n1 as crate::FLOAT) % **f2).into()),
|
||||
EqualsTo => return Ok(((*n1 as crate::FLOAT) == **f2).into()),
|
||||
NotEqualsTo => return Ok(((*n1 as crate::FLOAT) != **f2).into()),
|
||||
GreaterThan => return Ok(((*n1 as crate::FLOAT) > **f2).into()),
|
||||
GreaterThanEqualsTo => return Ok(((*n1 as crate::FLOAT) >= **f2).into()),
|
||||
LessThan => return Ok(((*n1 as crate::FLOAT) < **f2).into()),
|
||||
LessThanEqualsTo => return Ok(((*n1 as crate::FLOAT) <= **f2).into()),
|
||||
Plus => return Ok(((*n1 as crate::FLOAT) + **f2).into()),
|
||||
Minus => return Ok(((*n1 as crate::FLOAT) - **f2).into()),
|
||||
Multiply => return Ok(((*n1 as crate::FLOAT) * **f2).into()),
|
||||
Divide => return Ok(((*n1 as crate::FLOAT) / **f2).into()),
|
||||
Modulo => return Ok(((*n1 as crate::FLOAT) % **f2).into()),
|
||||
_ => (),
|
||||
},
|
||||
(Union::Str(s1, ..), Union::Str(s2, ..)) => match op_token.unwrap() {
|
||||
Token::EqualsTo => return Ok((s1 == s2).into()),
|
||||
Token::NotEqualsTo => return Ok((s1 != s2).into()),
|
||||
Token::GreaterThan => return Ok((s1 > s2).into()),
|
||||
Token::GreaterThanEqualsTo => return Ok((s1 >= s2).into()),
|
||||
Token::LessThan => return Ok((s1 < s2).into()),
|
||||
Token::LessThanEqualsTo => return Ok((s1 <= s2).into()),
|
||||
EqualsTo => return Ok((s1 == s2).into()),
|
||||
NotEqualsTo => return Ok((s1 != s2).into()),
|
||||
GreaterThan => return Ok((s1 > s2).into()),
|
||||
GreaterThanEqualsTo => return Ok((s1 >= s2).into()),
|
||||
LessThan => return Ok((s1 < s2).into()),
|
||||
LessThanEqualsTo => return Ok((s1 <= s2).into()),
|
||||
_ => (),
|
||||
},
|
||||
_ => (),
|
||||
@ -1723,8 +1723,8 @@ impl Engine {
|
||||
if let Some((func, need_context)) =
|
||||
get_builtin_binary_op_fn(op_token.as_ref().unwrap(), operands[0], operands[1])
|
||||
{
|
||||
// Built-in found
|
||||
auto_restore! { let orig_level = global.level; global.level += 1 }
|
||||
// We may not need to bump the level because built-in's do not need it.
|
||||
//auto_restore! { let orig_level = global.level; global.level += 1 }
|
||||
|
||||
let context =
|
||||
need_context.then(|| (self, name.as_str(), None, &*global, pos).into());
|
||||
|
Loading…
Reference in New Issue
Block a user