use std::collections::HashMap; use crate::module::CompiledModule; use crate::opcode::{self, OpCode}; use crate::symbol_table::SymbolTable; pub use crate::hash::structural_hash; pub use crate::module::Constant; pub use oxide_parser::{AssignmentOperator, BinaryOperator, Expression, Statement, UnaryOperator}; pub struct Compiler; pub(crate) fn is_int_literal(value: f64) -> bool { value.fract() == 0.0 && value >= i32::MIN as f64 && value <= i32::MAX as f64 } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[allow(dead_code)] pub(crate) enum Label { IfElse(u32), IfEnd(u32), WhileStart(u32), WhileEnd(u32), ForStart(u32), ForUpdate(u32), ForEnd(u32), TernaryEnd(u32), TernaryElse(u32), BreakTarget(u32), ContinueTarget(u32), } #[allow(dead_code)] pub(crate) struct CompileCtx { pub(crate) bytecode: Vec, pub(crate) constants: Vec, next_reg: u8, pub(crate) max_regs: u8, symbols: SymbolTable, pub(crate) label_map: HashMap, pub(crate) loop_stack: Vec<(Label, Label)>, pub(crate) label_counter: u32, pub(crate) projected_pc: usize, } #[allow(dead_code)] impl CompileCtx { pub(crate) fn new() -> Self { Self { bytecode: Vec::new(), constants: Vec::new(), next_reg: 0, max_regs: 0, symbols: SymbolTable::new(), label_map: HashMap::new(), loop_stack: Vec::new(), label_counter: 0, projected_pc: 0, } } pub(crate) fn emit(&mut self, instr: opcode::Instr) { self.bytecode.push(instr); } pub(crate) fn alloc_reg(&mut self) -> u8 { let r = self.next_reg; self.next_reg += 1; if self.next_reg > self.max_regs { self.max_regs = self.next_reg; } r } pub(crate) fn reset_regs(&mut self) { self.next_reg = 0; self.projected_pc = 0; } pub(crate) fn add_constant(&mut self, c: Constant) -> u16 { if let Some(idx) = self.constants.iter().position(|x| x == &c) { return idx as u16; } let idx = self.constants.len(); self.constants.push(c); idx as u16 } pub(crate) fn push_scope(&mut self) { self.symbols.push_scope(); } pub(crate) fn pop_scope(&mut self) { self.symbols.pop_scope(); } pub(crate) fn declare(&mut self, name: &str, reg: u8) -> Result<(), String> { self.symbols.declare(name, reg) } pub(crate) fn lookup(&self, name: &str) -> Result { self.symbols.lookup(name) } pub(crate) fn lookup_or_global(&mut self, name: &str) -> u8 { let reg = self.alloc_reg(); self.symbols.lookup_or_global(name, reg) } pub(crate) fn init_var(&mut self, name: &str) { self.symbols.init_var(name); } pub(crate) fn next_label_id(&mut self) -> u32 { let id = self.label_counter; self.label_counter += 1; id } pub(crate) fn push_loop(&mut self, break_label: Label, continue_label: Label) { self.loop_stack.push((break_label, continue_label)); } pub(crate) fn pop_loop(&mut self) { self.loop_stack.pop(); } pub(crate) fn current_loop(&self) -> Option<&(Label, Label)> { self.loop_stack.last() } } impl Compiler { pub fn new() -> Self { Self } pub fn compile(&self, program: &oxide_parser::Program) -> Result { let mut ctx = CompileCtx::new(); for stmt in &program.body { self.count_statement(stmt, &mut ctx); } ctx.max_regs = ctx.max_regs.max(1); ctx.reset_regs(); let mut last_result = 0u8; for stmt in &program.body { if let Some(r) = self.emit_statement(stmt, &mut ctx)? { last_result = r; } } if last_result != 0 { ctx.emit(opcode::encode(OpCode::LOAD_VAR, 0, last_result, 0)); } ctx.emit(opcode::encode(OpCode::HALT, 0, 0, 0)); Ok(CompiledModule { bytecode: ctx.bytecode, constants: ctx.constants, n_registers: ctx.max_regs, }) } } impl Default for Compiler { fn default() -> Self { Self::new() } }