diff --git a/src/engine.rs b/src/engine.rs index 2e239912..7d3bdb15 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -52,6 +52,9 @@ pub const FN_ANONYMOUS: &str = "anon$"; /// function to compare two [`Dynamic`] values. pub const OP_EQUALS: &str = Token::EqualsTo.literal_syntax(); +/// Standard not operator. +pub const OP_NOT: &str = Token::Bang.literal_syntax(); + /// Standard concatenation operator. /// /// Used primarily to build up interpolated strings. diff --git a/src/func/call.rs b/src/func/call.rs index fc70a214..bc2e60ad 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -325,9 +325,9 @@ impl Engine { } } - /// # Main Entry-Point + /// # Main Entry-Point (Native by Name) /// - /// Call a native Rust function registered with the [`Engine`]. + /// Call a native Rust function registered with the [`Engine`] by name. /// /// # WARNING /// @@ -543,9 +543,9 @@ impl Engine { } } - /// # Main Entry-Point + /// # Main Entry-Point (By Name) /// - /// Perform an actual function call, native Rust or scripted, taking care of special functions. + /// Perform an actual function call, native Rust or scripted, by name, taking care of special functions. /// /// # WARNING /// @@ -1463,6 +1463,8 @@ impl Engine { self.eval_global_statements(global, caches, scope, statements) } + /// # Main Entry-Point (`FnCallExpr`) + /// /// Evaluate a function call expression. pub(crate) fn eval_fn_call_expr( &self, @@ -1486,6 +1488,22 @@ impl Engine { let op_token = op_token.clone(); + // Short-circuit native unary operator call if under Fast Operators mode + if op_token == Token::Bang && self.fast_operators() && args.len() == 1 { + let mut value = self + .get_arg_value(global, caches, scope, this_ptr, &args[0])? + .0 + .flatten(); + + return value.as_bool().and_then(|r| Ok((!r).into())).or_else(|_| { + 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 != NO_TOKEN && self.fast_operators() && args.len() == 2 { let mut lhs = self diff --git a/src/optimizer.rs b/src/optimizer.rs index 67976cb7..1c503f89 100644 --- a/src/optimizer.rs +++ b/src/optimizer.rs @@ -4,7 +4,9 @@ use crate::ast::{ ASTFlags, Expr, OpAssignment, Stmt, StmtBlock, StmtBlockContainer, SwitchCasesCollection, }; -use crate::engine::{KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_PRINT, KEYWORD_TYPE_OF}; +use crate::engine::{ + KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_PRINT, KEYWORD_TYPE_OF, OP_NOT, +}; use crate::eval::{Caches, GlobalRuntimeState}; use crate::func::builtin::get_builtin_binary_op_fn; use crate::func::hashing::get_hasher; @@ -1065,6 +1067,20 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) { *expr = mem::take(&mut x.lhs); }, + // !true or !false + Expr::FnCall(x,..) + if x.name == OP_NOT + && x.args.len() == 1 + && matches!(x.args[0], Expr::BoolConstant(..)) + => { + state.set_dirty(); + if let Expr::BoolConstant(b, pos) = x.args[0] { + *expr = Expr::BoolConstant(!b, pos) + } else { + unreachable!() + } + } + // eval! Expr::FnCall(x, ..) if x.name == KEYWORD_EVAL => { state.propagate_constants = false;