diff --git a/src/optimize.rs b/src/optimize.rs index ab8d863b..bf7eab8f 100644 --- a/src/optimize.rs +++ b/src/optimize.rs @@ -3,6 +3,26 @@ use crate::parser::{Expr, Stmt}; fn optimize_stmt(stmt: Stmt, changed: &mut bool, preserve_result: bool) -> Stmt { match stmt { + Stmt::IfElse(expr, stmt1, None) if stmt1.is_noop() => { + *changed = true; + + let pos = expr.position(); + let expr = optimize_expr(*expr, changed); + + match expr { + Expr::False(_) | Expr::True(_) => Stmt::Noop(stmt1.position()), + expr => { + let stmt = Stmt::Expr(Box::new(expr)); + + if preserve_result { + Stmt::Block(vec![stmt, *stmt1], pos) + } else { + stmt + } + } + } + } + Stmt::IfElse(expr, stmt1, None) => match *expr { Expr::False(pos) => { *changed = true; @@ -22,7 +42,10 @@ fn optimize_stmt(stmt: Stmt, changed: &mut bool, preserve_result: bool) -> Stmt expr => Stmt::IfElse( Box::new(optimize_expr(expr, changed)), Box::new(optimize_stmt(*stmt1, changed, true)), - Some(Box::new(optimize_stmt(*stmt2, changed, true))), + match optimize_stmt(*stmt2, changed, true) { + stmt if stmt.is_noop() => None, + stmt => Some(Box::new(stmt)), + }, ), }, @@ -274,10 +297,14 @@ pub(crate) fn optimize(statements: Vec) -> Vec { } } - // Eliminate No-op's but always keep the last statement + // Eliminate code that is pure but always keep the last statement let last_stmt = result.pop(); - result.retain(Stmt::is_op); // Remove all No-op's + // Remove all pure statements at top level + result.retain(|stmt| match stmt { + Stmt::Expr(expr) if expr.is_pure() => false, + _ => true, + }); if let Some(stmt) = last_stmt { result.push(stmt); // Add back the last statement diff --git a/src/parser.rs b/src/parser.rs index db2da373..66eec95f 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -186,6 +186,13 @@ pub enum Stmt { } impl Stmt { + pub fn is_noop(&self) -> bool { + match self { + Stmt::Noop(_) => true, + _ => false, + } + } + pub fn is_op(&self) -> bool { match self { Stmt::Noop(_) => false, @@ -265,6 +272,7 @@ impl Expr { pub fn is_pure(&self) -> bool { match self { Expr::Array(expressions, _) => expressions.iter().all(Expr::is_pure), + Expr::And(x, y) | Expr::Or(x, y) | Expr::Index(x, y, _) => x.is_pure() && y.is_pure(), expr => expr.is_constant() || expr.is_identifier(), } }