Allow chained assignments.
This commit is contained in:
parent
9844ae8665
commit
d5adee2209
10
src/api.rs
10
src/api.rs
@ -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) {
|
||||||
|
@ -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, _) => {
|
||||||
|
@ -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)),
|
||||||
|
@ -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> {
|
||||||
|
Loading…
Reference in New Issue
Block a user