nek-lang/src/astoptimizer.rs
Kai-Philipp Nosper 8b67c4d59c Implement block scopes (code inside braces)
- Putting code in between braces will create a new scope
2022-02-04 17:30:23 +01:00

106 lines
4.5 KiB
Rust

use crate::ast::{Ast, BlockScope, Expression, If, Loop, Statement, BinOpType, UnOpType};
pub trait AstOptimizer {
fn optimize(ast: Ast) -> Ast;
}
pub struct SimpleAstOptimizer;
impl AstOptimizer for SimpleAstOptimizer {
fn optimize(mut ast: Ast) -> Ast {
Self::optimize_block(&mut ast.main);
ast
}
}
impl SimpleAstOptimizer {
fn optimize_block(block: &mut BlockScope) {
for stmt in block {
match stmt {
Statement::Expr(expr) => Self::optimize_expr(expr),
Statement::Block(block) => Self::optimize_block(block),
Statement::Loop(Loop {
condition,
advancement,
body,
}) => {
Self::optimize_expr(condition);
if let Some(advancement) = advancement {
Self::optimize_expr(advancement)
}
Self::optimize_block(body);
}
Statement::If(If {
condition,
body_true,
body_false,
}) => {
Self::optimize_expr(condition);
Self::optimize_block(body_true);
Self::optimize_block(body_false);
}
Statement::Print(expr) => Self::optimize_expr(expr),
}
}
}
fn optimize_expr(expr: &mut Expression) {
match expr {
Expression::I64(_) | Expression::String(_) | Expression::Var(_, _) => (),
Expression::BinOp(bo, lhs, rhs) => {
Self::optimize_expr(lhs);
Self::optimize_expr(rhs);
// Precalculate binary operations that consist of 2 literals. No need to do this at
// runtime, as all parts of the calculation are known at *compiletime* / parsetime.
match (lhs.as_mut(), rhs.as_mut()) {
(Expression::I64(lhs), Expression::I64(rhs)) => {
let new_expr = match bo {
BinOpType::Add => Expression::I64(*lhs + *rhs),
BinOpType::Mul => Expression::I64(*lhs * *rhs),
BinOpType::Sub => Expression::I64(*lhs - *rhs),
BinOpType::Div => Expression::I64(*lhs / *rhs),
BinOpType::Mod => Expression::I64(*lhs % *rhs),
BinOpType::BOr => Expression::I64(*lhs | *rhs),
BinOpType::BAnd => Expression::I64(*lhs & *rhs),
BinOpType::BXor => Expression::I64(*lhs ^ *rhs),
BinOpType::LAnd => Expression::I64(if (*lhs != 0) && (*rhs != 0) { 1 } else { 0 }),
BinOpType::LOr => Expression::I64(if (*lhs != 0) || (*rhs != 0) { 1 } else { 0 }),
BinOpType::Shr => Expression::I64(*lhs >> *rhs),
BinOpType::Shl => Expression::I64(*lhs << *rhs),
BinOpType::EquEqu => Expression::I64(if lhs == rhs { 1 } else { 0 }),
BinOpType::NotEqu => Expression::I64(if lhs != rhs { 1 } else { 0 }),
BinOpType::Less => Expression::I64(if lhs < rhs { 1 } else { 0 }),
BinOpType::LessEqu => Expression::I64(if lhs <= rhs { 1 } else { 0 }),
BinOpType::Greater => Expression::I64(if lhs > rhs { 1 } else { 0 }),
BinOpType::GreaterEqu => Expression::I64(if lhs >= rhs { 1 } else { 0 }),
BinOpType::Declare | BinOpType::Assign => unreachable!(),
};
*expr = new_expr;
},
_ => ()
}
}
Expression::UnOp(uo, operand) => {
Self::optimize_expr(operand);
// Precalculate unary operations just like binary ones
match operand.as_mut() {
Expression::I64(val) => {
let new_expr = match uo {
UnOpType::Negate => Expression::I64(-*val),
UnOpType::BNot => Expression::I64(!*val),
UnOpType::LNot => Expression::I64(if *val == 0 { 1 } else { 0 }),
};
*expr = new_expr;
}
_ => (),
}
}
}
}
}