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::{BinaryOperator, Expression, Statement, UnaryOperator}; pub struct Compiler; fn is_int_literal(value: f64) -> bool { value.fract() == 0.0 && value >= i32::MIN as f64 && value <= i32::MAX as f64 } struct CompileCtx { bytecode: Vec, constants: Vec, next_reg: u8, max_regs: u8, #[allow(dead_code)] symbols: SymbolTable, } impl CompileCtx { fn new() -> Self { Self { bytecode: Vec::new(), constants: Vec::new(), next_reg: 0, max_regs: 0, symbols: SymbolTable::new(), } } fn emit(&mut self, instr: opcode::Instr) { self.bytecode.push(instr); } 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 } fn reset_regs(&mut self) { self.next_reg = 0; } 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 } #[allow(dead_code)] fn push_scope(&mut self) { self.symbols.push_scope(); } #[allow(dead_code)] fn pop_scope(&mut self) { self.symbols.pop_scope(); } #[allow(dead_code)] fn declare(&mut self, name: &str, reg: u8) -> Result<(), String> { self.symbols.declare(name, reg) } #[allow(dead_code)] fn lookup(&self, name: &str) -> Result { self.symbols.lookup(name) } #[allow(dead_code)] fn lookup_or_global(&mut self, name: &str) -> u8 { let reg = self.alloc_reg(); self.symbols.lookup_or_global(name, reg) } #[allow(dead_code)] fn init_var(&mut self, name: &str) { self.symbols.init_var(name); } } 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() } } // ── Pass 1: Counter ── impl Compiler { fn count_statement(&self, stmt: &Statement, ctx: &mut CompileCtx) { match stmt { Statement::ExpressionStatement(es) => { self.count_expression(&es.expression, ctx); } Statement::VariableDeclaration(decl) => { for d in &decl.declarations { if let Some(init) = &d.init { self.count_expression(init, ctx); } } } Statement::ReturnStatement(ret) => { if let Some(arg) = &ret.argument { self.count_expression(arg, ctx); } } Statement::IfStatement(ifs) => { self.count_expression(&ifs.test, ctx); self.count_statement(&ifs.consequent, ctx); if let Some(alt) = &ifs.alternate { self.count_statement(alt, ctx); } } Statement::WhileStatement(wh) => { self.count_expression(&wh.test, ctx); self.count_statement(&wh.body, ctx); } Statement::ForStatement(fr) => { if let Some(init) = &fr.init { if let Some(expr) = init.as_expression() { self.count_expression(expr, ctx); } } if let Some(test) = &fr.test { self.count_expression(test, ctx); } if let Some(update) = &fr.update { self.count_expression(update, ctx); } self.count_statement(&fr.body, ctx); } Statement::BlockStatement(block) => { for s in &block.body { self.count_statement(s, ctx); } } _ => {} } } fn count_expression(&self, expr: &Expression, ctx: &mut CompileCtx) { match expr { Expression::BinaryExpression(bin) => { self.count_expression(&bin.left, ctx); self.count_expression(&bin.right, ctx); ctx.alloc_reg(); } Expression::UnaryExpression(un) => { self.count_expression(&un.argument, ctx); ctx.alloc_reg(); } Expression::CallExpression(call) => { self.count_expression(&call.callee, ctx); ctx.alloc_reg(); } Expression::AssignmentExpression(assign) => { if let oxide_parser::AssignmentTarget::StaticMemberExpression(member) = &assign.left { self.count_expression(&member.object, ctx); self.count_expression(&assign.right, ctx); ctx.alloc_reg(); } else if let oxide_parser::AssignmentTarget::ComputedMemberExpression(member) = &assign.left { self.count_expression(&member.object, ctx); self.count_expression(&member.expression, ctx); self.count_expression(&assign.right, ctx); ctx.alloc_reg(); } else { self.count_expression(&assign.right, ctx); ctx.alloc_reg(); } } Expression::ConditionalExpression(cond) => { self.count_expression(&cond.test, ctx); self.count_expression(&cond.consequent, ctx); self.count_expression(&cond.alternate, ctx); ctx.alloc_reg(); } Expression::SequenceExpression(seq) => { for e in &seq.expressions { self.count_expression(e, ctx); } } Expression::LogicalExpression(log) => { self.count_expression(&log.left, ctx); self.count_expression(&log.right, ctx); ctx.alloc_reg(); } Expression::ObjectExpression(obj) => { ctx.alloc_reg(); for prop in &obj.properties { if let oxide_parser::ObjectPropertyKind::ObjectProperty(p) = prop { ctx.alloc_reg(); self.count_expression(&p.value, ctx); } } } Expression::ArrayExpression(arr) => { ctx.alloc_reg(); for elem in &arr.elements { if let Some(e) = elem.as_expression() { self.count_expression(e, ctx); ctx.alloc_reg(); } } } Expression::StaticMemberExpression(member) => { self.count_expression(&member.object, ctx); ctx.alloc_reg(); ctx.alloc_reg(); } Expression::ComputedMemberExpression(member) => { self.count_expression(&member.object, ctx); self.count_expression(&member.expression, ctx); ctx.alloc_reg(); } Expression::ParenthesizedExpression(p) => { self.count_expression(&p.expression, ctx); } _ => { ctx.alloc_reg(); } } } } // ── Pass 2: Emitter ── impl Compiler { fn emit_statement(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result, String> { match stmt { Statement::ExpressionStatement(es) => { Ok(Some(self.emit_expression(&es.expression, ctx)?)) } Statement::VariableDeclaration(decl) => { let mut r = None; for d in &decl.declarations { if let Some(init) = &d.init { r = Some(self.emit_expression(init, ctx)?); } } Ok(r) } Statement::ReturnStatement(ret) => { match &ret.argument { Some(expr) => { let r = self.emit_expression(expr, ctx)?; ctx.emit(opcode::encode(OpCode::RETURN, r, 0, 0)); } None => { ctx.emit(opcode::encode(OpCode::RETURN, 0, 0, 0)); } } Ok(None) } Statement::BlockStatement(block) => { let mut r = None; for s in &block.body { if let Some(rr) = self.emit_statement(s, ctx)? { r = Some(rr); } } Ok(r) } _ => Ok(None), } } fn emit_expression(&self, expr: &Expression, ctx: &mut CompileCtx) -> Result { match expr { Expression::NumericLiteral(n) => { let idx = if is_int_literal(n.value) { ctx.add_constant(Constant::Int(n.value as i32)) } else { ctx.add_constant(Constant::Number(n.value)) }; let r = ctx.alloc_reg(); ctx.emit(opcode::encode( OpCode::LOAD_CONST, r, (idx & 0xFF) as u8, ((idx >> 8) & 0xFF) as u8, )); Ok(r) } Expression::StringLiteral(s) => { let idx = ctx.add_constant(Constant::String(s.value.to_string())); let r = ctx.alloc_reg(); ctx.emit(opcode::encode( OpCode::LOAD_CONST, r, (idx & 0xFF) as u8, ((idx >> 8) & 0xFF) as u8, )); Ok(r) } Expression::BooleanLiteral(b) => { let idx = ctx.add_constant(Constant::Boolean(b.value)); let r = ctx.alloc_reg(); ctx.emit(opcode::encode( OpCode::LOAD_CONST, r, (idx & 0xFF) as u8, ((idx >> 8) & 0xFF) as u8, )); Ok(r) } Expression::NullLiteral(_) => { let idx = ctx.add_constant(Constant::Null); let r = ctx.alloc_reg(); ctx.emit(opcode::encode( OpCode::LOAD_CONST, r, (idx & 0xFF) as u8, ((idx >> 8) & 0xFF) as u8, )); Ok(r) } Expression::BinaryExpression(bin) => { let left = self.emit_expression(&bin.left, ctx)?; let right = self.emit_expression(&bin.right, ctx)?; let op = match bin.operator { BinaryOperator::Addition => OpCode::ADD, BinaryOperator::Subtraction => OpCode::SUB, BinaryOperator::Multiplication => OpCode::MUL, BinaryOperator::Division => OpCode::DIV, BinaryOperator::Remainder => OpCode::MOD, BinaryOperator::Equality => OpCode::EQ, BinaryOperator::Inequality => OpCode::NEQ, BinaryOperator::LessThan => OpCode::LT, BinaryOperator::GreaterThan => OpCode::GT, BinaryOperator::LessEqualThan => OpCode::LTE, BinaryOperator::GreaterEqualThan => OpCode::GTE, BinaryOperator::In => OpCode::IN, _ => return Err(format!("unsupported binary operator: {:?}", bin.operator)), }; let r = ctx.alloc_reg(); ctx.emit(opcode::encode(op, r, left, right)); Ok(r) } Expression::UnaryExpression(un) => { let arg = self.emit_expression(&un.argument, ctx)?; match un.operator { UnaryOperator::UnaryNegation => { let r = ctx.alloc_reg(); ctx.emit(opcode::encode(OpCode::NEG, r, arg, 0)); Ok(r) } UnaryOperator::Typeof => { let r = ctx.alloc_reg(); ctx.emit(opcode::encode(OpCode::TYPEOF, r, arg, 0)); Ok(r) } UnaryOperator::Void => { let r = ctx.alloc_reg(); ctx.emit(opcode::encode(OpCode::VOID, r, arg, 0)); Ok(r) } _ => Err(format!("unsupported unary operator: {:?}", un.operator)), } } Expression::StaticMemberExpression(member) => { let obj_reg = self.emit_expression(&member.object, ctx)?; let prop_name = member.property.name.as_str(); let idx = ctx.add_constant(Constant::String(prop_name.to_string())); let key_reg = ctx.alloc_reg(); ctx.emit(opcode::encode( OpCode::LOAD_CONST, key_reg, (idx & 0xFF) as u8, ((idx >> 8) & 0xFF) as u8, )); let result_reg = ctx.alloc_reg(); ctx.emit(opcode::encode( OpCode::IC_GET_PROP, result_reg, obj_reg, key_reg, )); ctx.emit(0); Ok(result_reg) } Expression::ComputedMemberExpression(member) => { let obj_reg = self.emit_expression(&member.object, ctx)?; let key_reg = self.emit_expression(&member.expression, ctx)?; let r = ctx.alloc_reg(); ctx.emit(opcode::encode( OpCode::GET_PROP_DYNAMIC, obj_reg, key_reg, r, )); Ok(r) } Expression::ObjectExpression(obj) => { let obj_reg = ctx.alloc_reg(); ctx.emit(opcode::encode(OpCode::NEW_OBJECT, obj_reg, 0, 0)); for prop in &obj.properties { let oxide_parser::ObjectPropertyKind::ObjectProperty(p) = prop else { return Err("spread properties not yet supported".into()); }; let prop_name = match &p.key { oxide_parser::PropertyKey::StaticIdentifier(ident) => { ident.name.as_str().to_string() } oxide_parser::PropertyKey::StringLiteral(s) => s.value.to_string(), _ => return Err("unsupported object property key type".into()), }; let idx = ctx.add_constant(Constant::String(prop_name.to_string())); let key_reg = ctx.alloc_reg(); ctx.emit(opcode::encode( OpCode::LOAD_CONST, key_reg, (idx & 0xFF) as u8, ((idx >> 8) & 0xFF) as u8, )); let val_reg = self.emit_expression(&p.value, ctx)?; ctx.emit(opcode::encode(OpCode::SET_PROP, obj_reg, val_reg, key_reg)); } Ok(obj_reg) } Expression::ArrayExpression(arr) => { let arr_reg = ctx.alloc_reg(); let n = arr.elements.len() as u16; ctx.emit(opcode::encode( OpCode::NEW_ARRAY, arr_reg, (n & 0xFF) as u8, ((n >> 8) & 0xFF) as u8, )); for (i, elem) in arr.elements.iter().enumerate() { let Some(e) = elem.as_expression() else { return Err("spread not supported".into()); }; let val_reg = self.emit_expression(e, ctx)?; let idx_reg = ctx.alloc_reg(); let idx = ctx.add_constant(Constant::Number(i as f64)); ctx.emit(opcode::encode( OpCode::LOAD_CONST, idx_reg, (idx & 0xFF) as u8, ((idx >> 8) & 0xFF) as u8, )); ctx.emit(opcode::encode(OpCode::SET_ELEM, arr_reg, idx_reg, val_reg)); } Ok(arr_reg) } Expression::AssignmentExpression(assign) => { if let oxide_parser::AssignmentTarget::StaticMemberExpression(member) = &assign.left { let obj_reg = self.emit_expression(&member.object, ctx)?; let val_reg = self.emit_expression(&assign.right, ctx)?; let prop_name = member.property.name.as_str(); let idx = ctx.add_constant(Constant::String(prop_name.to_string())); let key_reg = ctx.alloc_reg(); ctx.emit(opcode::encode( OpCode::LOAD_CONST, key_reg, (idx & 0xFF) as u8, ((idx >> 8) & 0xFF) as u8, )); ctx.emit(opcode::encode( OpCode::IC_SET_PROP, obj_reg, val_reg, key_reg, )); ctx.emit(0); Ok(val_reg) } else if let oxide_parser::AssignmentTarget::ComputedMemberExpression(member) = &assign.left { let obj_reg = self.emit_expression(&member.object, ctx)?; let key_reg = self.emit_expression(&member.expression, ctx)?; let val_reg = self.emit_expression(&assign.right, ctx)?; ctx.emit(opcode::encode( OpCode::SET_PROP_DYNAMIC, obj_reg, key_reg, val_reg, )); Ok(val_reg) } else { Err("assignment target not supported".into()) } } Expression::ParenthesizedExpression(p) => self.emit_expression(&p.expression, ctx), _ => Err(format!("unsupported expression type: {:?}", expr)), } } }