use crate::SymbolError;
use crate::ast::{Expr, ExprKind};
use crate::error::IrError;
use crate::ir::Instr;
use crate::metadata::{SymbolKind, SymbolMetadata};
use std::borrow::Cow::Owned;
#[derive(Debug, Default)]
pub struct IrBuilder {
bytecode: Vec<Instr>,
symbols: Vec<SymbolMetadata>,
}
impl IrBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn build(mut self, ast: &Expr) -> Result<(Vec<Instr>, Vec<SymbolMetadata>), IrError> {
let body = if let ExprKind::Let { decls, body } = &ast.kind {
self.emit_let(decls)?;
body
} else {
ast
};
self.emit_expr(body);
Ok((self.bytecode, self.symbols))
}
fn emit_let(&mut self, decls: &[(&str, Expr)]) -> Result<(), IrError> {
for (name, value_expr) in decls {
if self.symbols.iter().any(|meta| meta.name == *name) {
return Err(SymbolError::DuplicateSymbol(name.to_string()).into());
}
self.emit_expr(value_expr);
let idx = self.symbols.len();
self.symbols.push(SymbolMetadata {
name: Owned(name.to_string()),
kind: SymbolKind::Const,
local: true,
index: None,
});
self.bytecode.push(Instr::Store(idx));
}
Ok(())
}
fn emit_expr(&mut self, expr: &Expr) {
match &expr.kind {
ExprKind::Literal(v) => {
self.bytecode.push(Instr::Push(*v));
}
ExprKind::Ident { name } => {
let idx = self.get_or_create_symbol(name, SymbolKind::Const);
self.bytecode.push(Instr::Load(idx));
}
ExprKind::Unary { op, expr } => {
self.emit_expr(expr);
self.bytecode.push((*op).into());
}
ExprKind::Binary { op, left, right } => {
self.emit_expr(left);
self.emit_expr(right);
self.bytecode.push((*op).into());
}
ExprKind::Call { name, args } => self.emit_call(name, args),
ExprKind::If {
cond,
then_branch,
else_branch,
} => self.emit_if(cond, then_branch, else_branch),
ExprKind::Let { body, .. } => self.emit_expr(body),
}
}
fn emit_if(&mut self, cond: &Expr, then_branch: &Expr, else_branch: &Expr) {
self.emit_expr(cond);
let jz_idx = self.bytecode.len();
self.bytecode.push(Instr::Jz(0));
self.emit_expr(then_branch);
let jmp_idx = self.bytecode.len();
self.bytecode.push(Instr::Jmp(0));
let else_start = self.bytecode.len();
self.emit_expr(else_branch);
let end = self.bytecode.len();
self.bytecode[jz_idx] = Instr::Jz(else_start);
self.bytecode[jmp_idx] = Instr::Jmp(end);
}
fn emit_call(&mut self, name: &str, args: &[Expr]) {
for arg in args {
self.emit_expr(arg);
}
let idx = self.get_or_create_symbol(
name,
SymbolKind::Func {
arity: args.len(),
variadic: false,
},
);
self.bytecode.push(Instr::Call(idx, args.len()));
}
fn get_or_create_symbol(&mut self, name: &str, kind: SymbolKind) -> usize {
if let Some(pos) = self.symbols.iter().position(|s| s.name == name) {
return pos;
}
self.symbols.push(SymbolMetadata {
name: name.to_string().into(),
kind,
local: false,
index: None,
});
self.symbols.len() - 1
}
}