Allow chained assignments.

This commit is contained in:
Stephen Chung 2020-03-14 11:51:45 +08:00
parent 9844ae8665
commit d5adee2209
4 changed files with 100 additions and 40 deletions

View File

@ -171,13 +171,15 @@ impl<'e> Engine<'e> {
statements statements
}; };
let result = statements let mut result = ().into_dynamic();
.iter()
.try_fold(().into_dynamic(), |_, stmt| engine.eval_stmt(scope, stmt)); for stmt in statements {
result = engine.eval_stmt(scope, stmt)?;
}
engine.clear_functions(); engine.clear_functions();
result Ok(result)
} }
match eval_ast_internal(self, scope, ast) { match eval_ast_internal(self, scope, ast) {

View File

@ -796,8 +796,8 @@ impl Engine<'_> {
// name = rhs // name = rhs
Expr::Variable(name, pos) => match scope.get(name) { Expr::Variable(name, pos) => match scope.get(name) {
Some((idx, _, VariableType::Normal, _)) => { Some((idx, _, VariableType::Normal, _)) => {
*scope.get_mut(name, idx) = rhs_val; *scope.get_mut(name, idx) = rhs_val.clone();
Ok(().into_dynamic()) Ok(rhs_val)
} }
Some((_, _, VariableType::Constant, _)) => Err( Some((_, _, VariableType::Constant, _)) => Err(
EvalAltResult::ErrorAssignmentToConstant(name.to_string(), *op_pos), EvalAltResult::ErrorAssignmentToConstant(name.to_string(), *op_pos),
@ -947,7 +947,15 @@ impl Engine<'_> {
Stmt::Noop(_) => Ok(().into_dynamic()), Stmt::Noop(_) => Ok(().into_dynamic()),
// Expression as statement // 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 // Block scope
Stmt::Block(block, _) => { Stmt::Block(block, _) => {

View File

@ -217,9 +217,30 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr {
} }
stmt => Expr::Stmt(Box::new(stmt), pos), stmt => Expr::Stmt(Box::new(stmt), pos),
}, },
Expr::Assignment(id, expr, pos) => { Expr::Assignment(id1, expr1, pos1) => match *expr1 {
Expr::Assignment(id, Box::new(optimize_expr(*expr, state)), pos) 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( Expr::Dot(lhs, rhs, pos) => Expr::Dot(
Box::new(optimize_expr(*lhs, state)), Box::new(optimize_expr(*lhs, state)),
Box::new(optimize_expr(*rhs, state)), Box::new(optimize_expr(*rhs, state)),

View File

@ -1177,7 +1177,7 @@ pub fn lex(input: &str) -> TokenIterator<'_> {
} }
} }
fn get_precedence(token: &Token) -> i8 { fn get_precedence(token: &Token) -> u8 {
match *token { match *token {
Token::Equals Token::Equals
| Token::PlusAssign | Token::PlusAssign
@ -1192,28 +1192,49 @@ fn get_precedence(token: &Token) -> i8 {
| Token::ModuloAssign | Token::ModuloAssign
| Token::PowerOfAssign => 10, | 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::LessThan
| Token::LessThanEqualsTo | Token::LessThanEqualsTo
| Token::GreaterThan | Token::GreaterThan
| Token::GreaterThanEqualsTo | Token::GreaterThanEqualsTo
| Token::EqualsTo | 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,
} }
} }
@ -1636,39 +1657,47 @@ fn parse_op_assignment(
fn parse_binary_op<'a>( fn parse_binary_op<'a>(
input: &mut Peekable<TokenIterator<'a>>, input: &mut Peekable<TokenIterator<'a>>,
precedence: i8, parent_precedence: u8,
lhs: Expr, lhs: Expr,
) -> Result<Expr, ParseError> { ) -> Result<Expr, ParseError> {
let mut current_lhs = lhs; let mut current_lhs = lhs;
loop { 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() { // Bind left to the parent lhs expression if precedence is higher
current_precedence = get_precedence(current_op); // If same precedence, then check if the operator binds right
} if current_precedence < parent_precedence
|| (current_precedence == parent_precedence && !bind_right)
if current_precedence < precedence { {
return Ok(current_lhs); return Ok(current_lhs);
} }
if let Some((op_token, pos)) = input.next() { if let Some((op_token, pos)) = input.next() {
input.peek(); 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() { // Bind to right if the next operator has higher precedence
next_precedence = get_precedence(next_op); // If same precedence, then check if the operator binds right
} let rhs = if (current_precedence == next_precedence && bind_right)
|| current_precedence < next_precedence
if current_precedence < next_precedence { {
rhs = parse_binary_op(input, current_precedence + 1, rhs)?; parse_binary_op(input, current_precedence, rhs)?
} else if current_precedence >= 100 { } else {
// Always bind right to left for precedence over 100 // Otherwise bind to left (even if next operator has the same precedence)
rhs = parse_binary_op(input, current_precedence, rhs)?; rhs
} };
current_lhs = match op_token { current_lhs = match op_token {
Token::Plus => Expr::FunctionCall("+".into(), vec![current_lhs, rhs], None, pos), Token::Plus => Expr::FunctionCall("+".into(), vec![current_lhs, rhs], None, pos),
@ -1780,7 +1809,7 @@ fn parse_binary_op<'a>(
fn parse_expr<'a>(input: &mut Peekable<TokenIterator<'a>>) -> Result<Expr, ParseError> { fn parse_expr<'a>(input: &mut Peekable<TokenIterator<'a>>) -> Result<Expr, ParseError> {
let lhs = parse_unary(input)?; let lhs = parse_unary(input)?;
parse_binary_op(input, 0, lhs) parse_binary_op(input, 1, lhs)
} }
fn parse_if<'a>(input: &mut Peekable<TokenIterator<'a>>) -> Result<Stmt, ParseError> { fn parse_if<'a>(input: &mut Peekable<TokenIterator<'a>>) -> Result<Stmt, ParseError> {