Fix constant assignment.

This commit is contained in:
Stephen Chung 2020-11-03 13:08:19 +08:00
parent b9de8eaa7f
commit f74d947c6b
4 changed files with 43 additions and 11 deletions

View File

@ -156,11 +156,16 @@ fn call_fn_with_constant_arguments(
/// Optimize a statement.
fn optimize_stmt(stmt: Stmt, state: &mut State, preserve_result: bool) -> Stmt {
match stmt {
// id op= expr
Stmt::Assignment(x, pos) => Stmt::Assignment(
// expr op= expr
Stmt::Assignment(x, pos) => match x.0 {
Expr::Variable(_) => {
Stmt::Assignment(Box::new((x.0, x.1, optimize_expr(x.2, state))), pos)
}
_ => Stmt::Assignment(
Box::new((optimize_expr(x.0, state), x.1, optimize_expr(x.2, state))),
pos,
),
},
// if false { if_block } -> Noop
Stmt::IfThenElse(Expr::False(pos), x, _) if x.1.is_none() => {
state.set_dirty();
@ -462,6 +467,11 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr {
.map(|(_, mut expr)| { expr.set_position(pos); expr })
.unwrap_or_else(|| Expr::Unit(pos))
}
// var.rhs
(lhs @ Expr::Variable(_), rhs) => Expr::Dot(Box::new(BinaryExpr {
lhs,
rhs: optimize_expr(rhs, state),
}), dot_pos),
// lhs.rhs
(lhs, rhs) => Expr::Dot(Box::new(BinaryExpr {
lhs: optimize_expr(lhs, state),
@ -498,6 +508,11 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr {
state.set_dirty();
Expr::CharConstant(s.name.chars().nth(i as usize).unwrap(), s.pos)
}
// var[rhs]
(lhs @ Expr::Variable(_), rhs) => Expr::Index(Box::new(BinaryExpr {
lhs,
rhs: optimize_expr(rhs, state),
}), idx_pos),
// lhs[rhs]
(lhs, rhs) => Expr::Index(Box::new(BinaryExpr {
lhs: optimize_expr(lhs, state),

View File

@ -1087,7 +1087,7 @@ fn make_assignment_stmt<'a>(
) -> Result<Stmt, ParseError> {
match &lhs {
// var (non-indexed) = rhs
Expr::Variable(x) if x.1.is_none() => {
Expr::Variable(x) if x.0.is_none() => {
Ok(Stmt::Assignment(Box::new((lhs, fn_name.into(), rhs)), pos))
}
// var (indexed) = rhs
@ -1114,7 +1114,7 @@ fn make_assignment_stmt<'a>(
// xxx[???] = rhs, xxx.??? = rhs
Expr::Index(x, _) | Expr::Dot(x, _) => match &x.lhs {
// var[???] (non-indexed) = rhs, var.??? (non-indexed) = rhs
Expr::Variable(x) if x.1.is_none() => {
Expr::Variable(x) if x.0.is_none() => {
Ok(Stmt::Assignment(Box::new((lhs, fn_name.into(), rhs)), pos))
}
// var[???] (indexed) = rhs, var.??? (indexed) = rhs

View File

@ -127,7 +127,7 @@ impl EvalAltResult {
Self::ErrorVariableNotFound(_, _) => "Variable not found",
Self::ErrorModuleNotFound(_, _) => "Module not found",
Self::ErrorDataRace(_, _) => "Data race detected when accessing variable",
Self::ErrorAssignmentToConstant(_, _) => "Assignment to a constant variable",
Self::ErrorAssignmentToConstant(_, _) => "Cannot assign to a constant",
Self::ErrorMismatchOutputType(_, _, _) => "Output type is incorrect",
Self::ErrorInExpr(_) => "Malformed 'in' expression",
Self::ErrorDotExpr(_, _) => "Malformed dot expression",
@ -194,7 +194,9 @@ impl fmt::Display for EvalAltResult {
Self::ErrorRuntime(d, _) if d.is::<()>() => f.write_str(desc)?,
Self::ErrorRuntime(d, _) => write!(f, "{}: {}", desc, d)?,
Self::ErrorAssignmentToConstant(s, _) => write!(f, "{}: '{}'", desc, s)?,
Self::ErrorAssignmentToConstant(s, _) => {
write!(f, "Cannot assign to constant '{}'", s)?
}
Self::ErrorMismatchOutputType(r, s, _) => {
write!(f, "Output type is incorrect: {} (expecting {})", r, s)?
}

View File

@ -1,4 +1,4 @@
use rhai::{Engine, EvalAltResult, ParseErrorType, INT};
use rhai::{Engine, EvalAltResult, ParseErrorType, Scope, INT};
#[test]
fn test_constant() -> Result<(), Box<EvalAltResult>> {
@ -15,13 +15,28 @@ fn test_constant() -> Result<(), Box<EvalAltResult>> {
#[cfg(not(feature = "no_index"))]
assert!(matches!(
*engine.eval::<INT>("const x = [1, 2, 3, 4, 5]; x[2] = 42;").expect_err("expects error"),
*engine.consume("const x = [1, 2, 3, 4, 5]; x[2] = 42;").expect_err("expects error"),
EvalAltResult::ErrorParsing(ParseErrorType::AssignmentToConstant(x), _) if x == "x"
));
Ok(())
}
#[test]
fn test_constant_scope() -> Result<(), Box<EvalAltResult>> {
let engine = Engine::new();
let mut scope = Scope::new();
scope.push_constant("x", 42 as INT);
assert!(matches!(
*engine.consume_with_scope(&mut scope, "x = 1").expect_err("expects error"),
EvalAltResult::ErrorAssignmentToConstant(x, _) if x == "x"
));
Ok(())
}
#[test]
fn test_var_is_def() -> Result<(), Box<EvalAltResult>> {
let engine = Engine::new();