diff --git a/src/api.rs b/src/api.rs index ba6f8bf4..ff57c51c 100644 --- a/src/api.rs +++ b/src/api.rs @@ -190,13 +190,15 @@ impl<'e> Engine<'e> { statements }; - let result = statements - .iter() - .try_fold(().into_dynamic(), |_, stmt| engine.eval_stmt(scope, stmt)); + let mut result = ().into_dynamic(); + + for stmt in statements { + result = engine.eval_stmt(scope, stmt)?; + } engine.clear_functions(); - result + Ok(result) } match eval_ast_internal(self, scope, ast) { diff --git a/src/engine.rs b/src/engine.rs index af3f4606..86c9a0d9 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -796,8 +796,8 @@ impl Engine<'_> { // name = rhs Expr::Variable(name, pos) => match scope.get(name) { Some((idx, _, VariableType::Normal, _)) => { - *scope.get_mut(name, idx) = rhs_val; - Ok(().into_dynamic()) + *scope.get_mut(name, idx) = rhs_val.clone(); + Ok(rhs_val) } Some((_, _, VariableType::Constant, _)) => Err( EvalAltResult::ErrorAssignmentToConstant(name.to_string(), *op_pos), @@ -947,7 +947,15 @@ impl Engine<'_> { Stmt::Noop(_) => Ok(().into_dynamic()), // Expression as statement - Stmt::Expr(expr) => self.eval_expr(scope, expr), + Stmt::Expr(expr) => { + let result = self.eval_expr(scope, expr)?; + + Ok(match expr.as_ref() { + // If it is an assignment, erase the result at the root + Expr::Assignment(_, _, _) => ().into_dynamic(), + _ => result, + }) + } // Block scope Stmt::Block(block, _) => { diff --git a/src/optimize.rs b/src/optimize.rs index 95f36186..2e2e7778 100644 --- a/src/optimize.rs +++ b/src/optimize.rs @@ -218,9 +218,30 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr { } stmt => Expr::Stmt(Box::new(stmt), pos), }, - Expr::Assignment(id, expr, pos) => { - Expr::Assignment(id, Box::new(optimize_expr(*expr, state)), pos) - } + Expr::Assignment(id1, expr1, pos1) => match *expr1 { + Expr::Assignment(id2, expr2, pos2) => match (*id1, *id2) { + (Expr::Variable(var1, _), Expr::Variable(var2, _)) if var1 == var2 => { + // Assignment to the same variable - fold + state.set_dirty(); + + Expr::Assignment( + Box::new(Expr::Variable(var1, pos1)), + Box::new(optimize_expr(*expr2, state)), + pos1, + ) + } + (id1, id2) => Expr::Assignment( + Box::new(id1), + Box::new(Expr::Assignment( + Box::new(id2), + Box::new(optimize_expr(*expr2, state)), + pos2, + )), + pos1, + ), + }, + expr => Expr::Assignment(id1, Box::new(optimize_expr(expr, state)), pos1), + }, Expr::Dot(lhs, rhs, pos) => Expr::Dot( Box::new(optimize_expr(*lhs, state)), Box::new(optimize_expr(*rhs, state)), diff --git a/src/parser.rs b/src/parser.rs index 89021d39..2934d0d7 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1214,7 +1214,7 @@ pub fn lex(input: &str) -> TokenIterator<'_> { } } -fn get_precedence(token: &Token) -> i8 { +fn get_precedence(token: &Token) -> u8 { match *token { Token::Equals | Token::PlusAssign @@ -1229,28 +1229,49 @@ fn get_precedence(token: &Token) -> i8 { | Token::ModuloAssign | Token::PowerOfAssign => 10, - Token::Or | Token::XOr | Token::Pipe => 11, + Token::Or | Token::XOr | Token::Pipe => 50, - Token::And | Token::Ampersand => 12, + Token::And | Token::Ampersand => 60, Token::LessThan | Token::LessThanEqualsTo | Token::GreaterThan | Token::GreaterThanEqualsTo | Token::EqualsTo - | Token::NotEqualsTo => 15, + | Token::NotEqualsTo => 70, - Token::Plus | Token::Minus => 20, + Token::Plus | Token::Minus => 80, - Token::Divide | Token::Multiply | Token::PowerOf => 40, + Token::Divide | Token::Multiply | Token::PowerOf => 90, - Token::LeftShift | Token::RightShift => 50, + Token::LeftShift | Token::RightShift => 100, - Token::Modulo => 60, + Token::Modulo => 110, - Token::Period => 100, + Token::Period => 120, - _ => -1, + _ => 0, + } +} + +fn is_bind_right(token: &Token) -> bool { + match *token { + Token::Equals + | Token::PlusAssign + | Token::MinusAssign + | Token::MultiplyAssign + | Token::DivideAssign + | Token::LeftShiftAssign + | Token::RightShiftAssign + | Token::AndAssign + | Token::OrAssign + | Token::XOrAssign + | Token::ModuloAssign + | Token::PowerOfAssign => true, + + Token::Period => true, + + _ => false, } } @@ -1673,39 +1694,47 @@ fn parse_op_assignment( fn parse_binary_op<'a>( input: &mut Peekable>, - precedence: i8, + parent_precedence: u8, lhs: Expr, ) -> Result { let mut current_lhs = lhs; loop { - let mut current_precedence = -1; + let (current_precedence, bind_right) = if let Some(&(ref current_op, _)) = input.peek() { + (get_precedence(current_op), is_bind_right(current_op)) + } else { + (0, false) + }; - if let Some(&(ref current_op, _)) = input.peek() { - current_precedence = get_precedence(current_op); - } - - if current_precedence < precedence { + // Bind left to the parent lhs expression if precedence is higher + // If same precedence, then check if the operator binds right + if current_precedence < parent_precedence + || (current_precedence == parent_precedence && !bind_right) + { return Ok(current_lhs); } if let Some((op_token, pos)) = input.next() { input.peek(); - let mut rhs = parse_unary(input)?; + let rhs = parse_unary(input)?; - let mut next_precedence = -1; + let next_precedence = if let Some(&(ref next_op, _)) = input.peek() { + get_precedence(next_op) + } else { + 0 + }; - if let Some(&(ref next_op, _)) = input.peek() { - next_precedence = get_precedence(next_op); - } - - if current_precedence < next_precedence { - rhs = parse_binary_op(input, current_precedence + 1, rhs)?; - } else if current_precedence >= 100 { - // Always bind right to left for precedence over 100 - rhs = parse_binary_op(input, current_precedence, rhs)?; - } + // Bind to right if the next operator has higher precedence + // If same precedence, then check if the operator binds right + let rhs = if (current_precedence == next_precedence && bind_right) + || current_precedence < next_precedence + { + parse_binary_op(input, current_precedence, rhs)? + } else { + // Otherwise bind to left (even if next operator has the same precedence) + rhs + }; current_lhs = match op_token { Token::Plus => Expr::FunctionCall("+".into(), vec![current_lhs, rhs], None, pos), @@ -1817,7 +1846,7 @@ fn parse_binary_op<'a>( fn parse_expr<'a>(input: &mut Peekable>) -> Result { let lhs = parse_unary(input)?; - parse_binary_op(input, 0, lhs) + parse_binary_op(input, 1, lhs) } fn parse_if<'a>(input: &mut Peekable>) -> Result {