use std::{collections::HashMap, mem, vec};
use rush_analyzer::{ast::*, InfixOp, PrefixOp, Type};
use crate::{
instruction::{self, Instruction, Program},
value::{Pointer, Value},
};
#[derive(Default)]
pub struct Compiler<'src> {
functions: Vec<Vec<Instruction>>,
fn_names: HashMap<&'src str, usize>,
globals: HashMap<&'src str, usize>,
scopes: Vec<Scope<'src>>,
local_let_count: usize,
setmp_indices: Vec<usize>,
loops: Vec<Loop>,
}
type Scope<'src> = HashMap<&'src str, Variable>;
#[derive(Debug, Clone, Copy)]
enum Variable {
Unit,
Local { offset: isize },
Global { addr: usize },
}
#[derive(Default)]
struct Loop {
break_jmp_indices: Vec<usize>,
continue_jmp_indices: Vec<usize>,
}
impl<'src> Compiler<'src> {
pub(crate) fn new() -> Self {
Self {
functions: vec![vec![]],
..Default::default()
}
}
#[inline]
fn insert(&mut self, instruction: Instruction) {
self.functions
.last_mut()
.expect("there is always a function")
.push(instruction)
}
#[inline]
fn curr_fn(&self) -> &Vec<Instruction> {
self.functions.last().expect("there is always a function")
}
#[inline]
fn curr_fn_mut(&mut self) -> &mut Vec<Instruction> {
self.functions
.last_mut()
.expect("there is always a function")
}
#[inline]
fn scope_mut(&mut self) -> &mut Scope<'src> {
self.scopes.last_mut().expect("there is always a scope")
}
#[inline]
fn curr_loop_mut(&mut self) -> &mut Loop {
self.loops
.last_mut()
.expect("there is always a loop when called")
}
fn resolve_var(&self, name: &'src str) -> Variable {
for scope in self.scopes.iter().rev() {
if let Some(i) = scope.get(name) {
return *i;
};
}
Variable::Global {
addr: self.globals[name],
}
}
fn load_var(&mut self, name: &'src str) {
let var = self.resolve_var(name);
match var {
Variable::Unit => {} Variable::Local { offset, .. } => {
self.insert(Instruction::Push(Value::Ptr(Pointer::Rel(offset))));
self.insert(Instruction::GetVar)
}
Variable::Global { addr } => {
self.insert(Instruction::Push(Value::Ptr(Pointer::Abs(addr))));
self.insert(Instruction::GetVar)
}
}
}
pub(crate) fn compile(mut self, ast: AnalyzedProgram<'src>) -> Program {
for (idx, func) in ast.functions.iter().filter(|f| f.used).enumerate() {
self.fn_names.insert(func.name, idx + 2);
}
self.insert(Instruction::SetMp(ast.globals.len() as isize));
for var in ast.globals.into_iter().filter(|g| g.used) {
self.declare_global(var);
}
self.insert(Instruction::Call(1));
self.main_fn(ast.main_fn);
for func in ast.functions.into_iter().filter(|f| f.used) {
self.functions.push(vec![]);
self.fn_declaration(func);
}
Program(self.functions)
}
fn declare_global(&mut self, node: AnalyzedLetStmt<'src>) {
let addr = self.globals.len();
self.globals.insert(node.name, addr);
self.expression(node.expr);
self.insert(Instruction::SetVarImm(Pointer::Abs(addr)));
}
fn fn_declaration(&mut self, node: AnalyzedFunctionDefinition<'src>) {
self.local_let_count = 0;
self.scopes.push(Scope::default());
mem::take(&mut self.setmp_indices);
let setmp_idx = self.curr_fn().len();
self.insert(Instruction::SetMp(isize::MAX));
for param in node.params.iter().rev() {
let offset = -(self.local_let_count as isize);
let var = match param.type_ {
Type::Unit | Type::Never => Variable::Unit,
_ => {
self.insert(Instruction::SetVarImm(Pointer::Rel(offset)));
self.local_let_count += 1;
Variable::Local { offset }
}
};
self.scope_mut().insert(param.name, var);
}
self.block(node.block, false);
self.curr_fn_mut()[setmp_idx] = Instruction::SetMp(self.local_let_count as isize);
self.scopes.pop();
let pos = self.curr_fn().len();
self.setmp_indices.push(pos);
self.insert(Instruction::SetMp(isize::MIN));
self.insert(Instruction::Ret);
self.correct_setmp_values();
}
fn correct_setmp_values(&mut self) {
let offset = -(self.local_let_count as isize);
for idx in self.setmp_indices.clone() {
match (&mut self.curr_fn_mut()[idx], offset) {
(_, 0) => self.curr_fn_mut()[idx] = Instruction::Nop,
(Instruction::SetMp(o), _) => *o = offset,
other => unreachable!("other instructions do not modify mp: {other:?}"),
}
}
}
fn main_fn(&mut self, node: AnalyzedBlock<'src>) {
self.functions.push(vec![]);
self.local_let_count = 0;
self.fn_names.insert("main", 1);
let setmp_idx = self.curr_fn().len();
self.insert(Instruction::SetMp(isize::MAX));
self.block(node, true);
self.curr_fn_mut()[setmp_idx] = Instruction::SetMp(self.local_let_count as isize);
self.correct_setmp_values()
}
fn block(&mut self, node: AnalyzedBlock<'src>, new_scope: bool) {
if new_scope {
self.scopes.push(Scope::default());
}
for stmt in node.stmts {
self.statement(stmt);
}
if let Some(expr) = node.expr {
self.expression(expr);
}
if new_scope {
self.scopes.pop();
}
}
fn statement(&mut self, node: AnalyzedStatement<'src>) {
match node {
AnalyzedStatement::Let(node) => self.let_stmt(node),
AnalyzedStatement::Return(expr) => {
if let Some(expr) = expr {
self.expression(expr);
}
let pos = self.curr_fn().len();
self.setmp_indices.push(pos);
self.insert(Instruction::SetMp(isize::MIN));
self.insert(Instruction::Ret);
}
AnalyzedStatement::Loop(node) => self.loop_stmt(node),
AnalyzedStatement::While(node) => self.while_stmt(node),
AnalyzedStatement::For(node) => self.for_stmt(node),
AnalyzedStatement::Break => {
let pos = self.curr_fn().len();
self.curr_loop_mut().break_jmp_indices.push(pos);
self.insert(Instruction::Jmp(usize::MAX));
}
AnalyzedStatement::Continue => {
let pos = self.curr_fn().len();
self.curr_loop_mut().continue_jmp_indices.push(pos);
self.insert(Instruction::Jmp(usize::MAX));
}
AnalyzedStatement::Expr(node) => {
let expr_type = node.result_type();
self.expression(node);
if !matches!(expr_type, Type::Unit | Type::Never) {
self.insert(Instruction::Drop)
}
}
}
}
fn let_stmt(&mut self, node: AnalyzedLetStmt<'src>) {
match node.expr.result_type() {
Type::Unit | Type::Never => {
self.expression(node.expr);
self.scope_mut().insert(node.name, Variable::Unit);
}
_ => {
self.expression(node.expr);
let offset = -(self.local_let_count as isize);
self.insert(Instruction::SetVarImm(Pointer::Rel(offset)));
self.scope_mut()
.insert(node.name, Variable::Local { offset });
self.local_let_count += 1;
}
}
}
fn fill_blank_jmps(&mut self, jmps: &[usize], target: usize) {
for idx in jmps {
match &mut self.curr_fn_mut()[*idx] {
Instruction::Jmp(o) => *o = target,
Instruction::JmpFalse(o) => *o = target,
_ => unreachable!("other instructions do not jump"),
}
}
}
fn loop_stmt(&mut self, node: AnalyzedLoopStmt<'src>) {
let loop_head_pos = self.curr_fn().len();
self.loops.push(Loop::default());
let block_expr_type = node
.block
.expr
.as_ref()
.map_or(Type::Unit, |expr| expr.result_type());
self.block(node.block, true);
if !matches!(block_expr_type, Type::Unit | Type::Never) {
self.insert(Instruction::Drop);
}
self.insert(Instruction::Jmp(loop_head_pos));
let loop_ = self.loops.pop().expect("pushed above");
let pos = self.curr_fn().len();
self.fill_blank_jmps(&loop_.break_jmp_indices, pos);
self.fill_blank_jmps(&loop_.continue_jmp_indices, loop_head_pos);
}
fn while_stmt(&mut self, node: AnalyzedWhileStmt<'src>) {
let loop_head_pos = self.curr_fn().len();
self.expression(node.cond);
self.loops.push(Loop::default());
let end = self.curr_fn().len();
self.curr_loop_mut().break_jmp_indices.push(end);
self.insert(Instruction::JmpFalse(usize::MAX));
let block_expr_type = node
.block
.expr
.as_ref()
.map_or(Type::Unit, |expr| expr.result_type());
self.block(node.block, true);
if !matches!(block_expr_type, Type::Unit | Type::Never) {
self.insert(Instruction::Drop);
}
self.insert(Instruction::Jmp(loop_head_pos));
let loop_ = self.loops.pop().expect("pushed above");
let pos = self.curr_fn().len();
self.fill_blank_jmps(&loop_.break_jmp_indices, pos);
self.fill_blank_jmps(&loop_.continue_jmp_indices, loop_head_pos);
}
fn for_stmt(&mut self, node: AnalyzedForStmt<'src>) {
self.scopes.push(HashMap::new());
match node.initializer.result_type() {
Type::Unit | Type::Never => {
self.expression(node.initializer);
self.scope_mut().insert(node.ident, Variable::Unit);
}
_ => {
self.expression(node.initializer);
let offset = self.local_let_count as isize;
self.insert(Instruction::SetVarImm(Pointer::Rel(offset)));
self.scope_mut()
.insert(node.ident, Variable::Local { offset });
self.local_let_count += 1;
}
}
let loop_head_pos = self.curr_fn().len();
self.expression(node.cond);
self.loops.push(Loop::default());
let curr_pos = self.curr_fn().len();
self.curr_loop_mut().break_jmp_indices.push(curr_pos);
self.insert(Instruction::JmpFalse(usize::MAX));
let block_expr_type = node
.block
.expr
.as_ref()
.map_or(Type::Unit, |expr| expr.result_type());
self.block(node.block, true);
if !matches!(block_expr_type, Type::Unit | Type::Never) {
self.insert(Instruction::Drop);
}
let curr_pos = self.curr_fn().len();
let loop_ = self.loops.pop().expect("pushed above");
self.fill_blank_jmps(&loop_.continue_jmp_indices, curr_pos);
let update_type = node.update.result_type();
self.expression(node.update);
if !matches!(update_type, Type::Unit | Type::Never) {
self.insert(Instruction::Drop);
}
self.insert(Instruction::Jmp(loop_head_pos));
let pos = self.curr_fn().len();
self.fill_blank_jmps(&loop_.break_jmp_indices, pos);
self.scopes.pop();
}
fn expression(&mut self, node: AnalyzedExpression<'src>) {
match node {
AnalyzedExpression::Int(value) => self.insert(Instruction::Push(Value::Int(value))),
AnalyzedExpression::Float(value) => self.insert(Instruction::Push(Value::Float(value))),
AnalyzedExpression::Bool(value) => self.insert(Instruction::Push(Value::Bool(value))),
AnalyzedExpression::Char(value) => self.insert(Instruction::Push(Value::Char(value))),
AnalyzedExpression::Ident(node) => self.load_var(node.ident),
AnalyzedExpression::Block(node) => self.block(*node, true),
AnalyzedExpression::If(node) => self.if_expr(*node),
AnalyzedExpression::Prefix(node) => self.prefix_expr(*node),
AnalyzedExpression::Infix(node) => self.infix_expr(*node),
AnalyzedExpression::Assign(node) => self.assign_expr(*node),
AnalyzedExpression::Call(node) => self.call_expr(*node),
AnalyzedExpression::Cast(node) => self.cast_expr(*node),
AnalyzedExpression::Grouped(node) => self.expression(*node),
}
}
fn if_expr(&mut self, node: AnalyzedIfExpr<'src>) {
self.expression(node.cond);
let after_condition = self.curr_fn().len();
self.insert(Instruction::JmpFalse(usize::MAX));
self.block(node.then_block, true);
let after_then_idx = self.curr_fn().len();
if let Some(else_block) = node.else_block {
self.insert(Instruction::Jmp(usize::MAX));
self.curr_fn_mut()[after_condition] = Instruction::JmpFalse(after_then_idx + 1);
self.block(else_block, true);
let after_else = self.curr_fn().len();
self.curr_fn_mut()[after_then_idx] = Instruction::Jmp(after_else);
} else {
self.curr_fn_mut()[after_condition] = Instruction::JmpFalse(after_then_idx);
}
}
fn prefix_expr(&mut self, node: AnalyzedPrefixExpr<'src>) {
match Instruction::try_from(node.op) {
Ok(insruction) => {
self.expression(node.expr);
self.insert(insruction)
}
Err(_) => match node.op == PrefixOp::Ref {
true => {
if let AnalyzedExpression::Ident(ident) = node.expr {
match self.resolve_var(ident.ident) {
Variable::Local { offset, .. } => {
self.insert(Instruction::RelToAddr(offset))
}
Variable::Global { addr } => {
self.insert(Instruction::Push(Value::Ptr(Pointer::Abs(addr))));
}
Variable::Unit => unreachable!("unit values cannot be referenced"),
}
return;
}
unreachable!("the parser guarantees that only idents can be referenced")
}
false => {
self.expression(node.expr);
self.insert(Instruction::GetVar)
}
},
}
}
fn infix_expr(&mut self, node: AnalyzedInfixExpr<'src>) {
match node.op {
InfixOp::Or | InfixOp::And => {
self.expression(node.lhs);
if node.op == InfixOp::Or {
self.insert(Instruction::Not);
}
let merge_jmp_idx = self.curr_fn().len();
self.insert(Instruction::JmpFalse(usize::MAX));
self.expression(node.rhs);
let pos = self.curr_fn().len() + 2;
self.insert(Instruction::Jmp(pos));
self.insert(Instruction::Push(Value::Bool(node.op == InfixOp::Or)));
self.curr_fn_mut()[merge_jmp_idx] = Instruction::JmpFalse(self.curr_fn().len() - 1);
}
op => {
self.expression(node.lhs);
self.expression(node.rhs);
self.insert(Instruction::from(op));
}
}
}
fn assign_expr(&mut self, node: AnalyzedAssignExpr<'src>) {
let assignee = self.resolve_var(node.assignee);
let ptr = match assignee {
Variable::Local { offset } => Pointer::Rel(offset),
Variable::Global { addr } => Pointer::Abs(addr),
Variable::Unit => unreachable!("cannot assign to unit values"),
};
self.insert(Instruction::Push(Value::Ptr(ptr)));
let mut ptr_count = node.assignee_ptr_count;
while ptr_count > 0 {
self.insert(Instruction::GetVar);
ptr_count -= 1;
}
match node.op.try_into() {
Ok(instruction) => {
self.insert(Instruction::Clone);
match assignee {
Variable::Unit => {}
_ => self.insert(Instruction::GetVar),
};
self.expression(node.expr);
self.insert(instruction);
}
Err(()) => self.expression(node.expr),
}
match assignee {
Variable::Unit => {}
_ => self.insert(Instruction::SetVar),
};
}
fn call_expr(&mut self, node: AnalyzedCallExpr<'src>) {
for arg in node.args {
self.expression(arg);
}
match node.func {
"exit" => self.insert(Instruction::Exit),
func => {
let fn_idx = self.fn_names[func];
self.insert(Instruction::Call(fn_idx));
}
}
}
fn cast_expr(&mut self, node: AnalyzedCastExpr<'src>) {
let expr_type = node.expr.result_type();
self.expression(node.expr);
match (expr_type, node.type_) {
(from, to) if from == to => {}
(_, to) => self.insert(Instruction::Cast(instruction::Type::from(to))),
}
}
}