diff --git a/src/optimize.rs b/src/optimize.rs index e9b36d27..3e40d019 100644 --- a/src/optimize.rs +++ b/src/optimize.rs @@ -1,10 +1,11 @@ //! Module implementing the [`AST`] optimizer. -use crate::ast::{Expr, Stmt}; +use crate::ast::{Expr, OpAssignment, Stmt}; use crate::dynamic::AccessMode; use crate::engine::{KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_PRINT, KEYWORD_TYPE_OF}; use crate::fn_builtin::get_builtin_binary_op_fn; use crate::parser::map_dynamic_to_expr; +use crate::token::Token; use crate::utils::get_hasher; use crate::{ calc_fn_hash, calc_fn_params_hash, combine_hashes, Dynamic, Engine, FnPtr, ImmutableString, @@ -381,6 +382,33 @@ fn optimize_stmt_block( /// Optimize a [statement][Stmt]. fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) { match stmt { + // var = var op expr => var op= expr + Stmt::Assignment(x, _) + if x.1.is_none() + && x.0.is_variable_access(true) + && matches!(&x.2, Expr::FnCall(x2, _) + if Token::lookup_from_syntax(&x2.name).and_then(|t| t.make_op_assignment()).is_some() + && x2.args_count() == 2 && x2.args.len() >= 1 + && x2.args[0].get_variable_name(true) == x.0.get_variable_name(true) + ) => + { + match &mut x.2 { + Expr::FnCall(x2, _) => { + state.set_dirty(); + let op = Token::lookup_from_syntax(&x2.name).unwrap(); + let op_assignment = op.make_op_assignment().unwrap(); + x.1 = Some(OpAssignment::new(op_assignment.keyword_syntax())); + x.2 = if x2.args.len() > 1 { + mem::take(&mut x2.args[1]) + } else { + let (value, pos) = mem::take(&mut x2.constant_args[0]); + Expr::DynamicConstant(Box::new(value), pos) + }; + } + _ => unreachable!(), + } + } + // expr op= expr Stmt::Assignment(x, _) => match x.0 { Expr::Variable(_, _, _) => optimize_expr(&mut x.2, state), diff --git a/src/parser.rs b/src/parser.rs index 87082f7e..b587fb6b 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1518,19 +1518,7 @@ fn parse_op_assignment_stmt( let op = match token { Token::Equals => "", - - Token::PlusAssign - | Token::MinusAssign - | Token::MultiplyAssign - | Token::DivideAssign - | Token::LeftShiftAssign - | Token::RightShiftAssign - | Token::ModuloAssign - | Token::PowerOfAssign - | Token::AndAssign - | Token::OrAssign - | Token::XOrAssign => token.keyword_syntax(), - + _ if token.map_op_assignment().is_some() => token.keyword_syntax(), _ => return Ok(Stmt::Expr(lhs)), }; diff --git a/src/token.rs b/src/token.rs index c777e4d7..20a4bc98 100644 --- a/src/token.rs +++ b/src/token.rs @@ -578,6 +578,42 @@ impl Token { } } + /// Get the corresponding operator of the token if it is an op-assignment operator. + pub fn map_op_assignment(&self) -> Option { + Some(match self { + Self::PlusAssign => Self::Plus, + Self::MinusAssign => Self::Minus, + Self::MultiplyAssign => Self::Multiply, + Self::DivideAssign => Self::Divide, + Self::LeftShiftAssign => Self::LeftShift, + Self::RightShiftAssign => Self::RightShift, + Self::ModuloAssign => Self::Modulo, + Self::PowerOfAssign => Self::PowerOf, + Self::AndAssign => Self::Ampersand, + Self::OrAssign => Self::Pipe, + Self::XOrAssign => Self::XOr, + _ => return None, + }) + } + + /// Get the corresponding op-assignment operator of the token. + pub fn make_op_assignment(&self) -> Option { + Some(match self { + Self::Plus => Self::PlusAssign, + Self::Minus => Self::MinusAssign, + Self::Multiply => Self::MultiplyAssign, + Self::Divide => Self::DivideAssign, + Self::LeftShift => Self::LeftShiftAssign, + Self::RightShift => Self::RightShiftAssign, + Self::Modulo => Self::ModuloAssign, + Self::PowerOf => Self::PowerOfAssign, + Self::Ampersand => Self::AndAssign, + Self::Pipe => Self::OrAssign, + Self::XOr => Self::XOrAssign, + _ => return None, + }) + } + /// Reverse lookup a token from a piece of syntax. pub fn lookup_from_syntax(syntax: &str) -> Option { use Token::*;