use crate::compiler::{CompileCtx, Compiler, Label}; use oxide_bytecode::opcode::{self, OpCode}; use oxide_parser::Statement; impl Compiler { fn count_switch_statement(&self, stmt: &oxide_parser::SwitchStatement<'_>, ctx: &mut CompileCtx) { let id = ctx.next_label_id(); let end_label = Label::SwitchEnd(id); ctx.push_switch(end_label); self.count_expression(&stmt.discriminant, ctx); let compare_reg_checkpoint = ctx.reg_checkpoint(); let cases = &stmt.cases; for case in cases.iter() { if let Some(test) = &case.test { self.count_expression(test, ctx); ctx.projected_pc += 1; ctx.alloc_reg(); ctx.projected_pc += 1; ctx.restore_reg_checkpoint(compare_reg_checkpoint); } } let has_default = cases.iter().any(|c| c.test.is_none()); if !has_default { ctx.projected_pc += 1; } for (case_idx, case) in cases.iter().enumerate() { let case_label = Label::SwitchCase(id, case_idx as u32); ctx.labels.label_map.insert(case_label, ctx.projected_pc); for s in &case.consequent { self.count_statement(s, ctx); } } ctx.labels.label_map.insert(end_label, ctx.projected_pc); ctx.pop_switch(); } fn emit_switch_statement(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result, String> { let Statement::SwitchStatement(sw) = stmt else { return Ok(None); }; let id = ctx.next_label_id(); let end_label = Label::SwitchEnd(id); ctx.push_switch(end_label); let disc_reg = self.emit_expression(&sw.discriminant, ctx)?; let compare_reg_checkpoint = ctx.reg_checkpoint(); let cases = &sw.cases; for (case_idx, case) in cases.iter().enumerate() { if let Some(test) = &case.test { let test_reg = self.emit_expression(test, ctx)?; let eq_reg = ctx.alloc_reg(); ctx.emit(opcode::encode(OpCode::EQ, eq_reg, disc_reg, test_reg)); let case_label = Label::SwitchCase(id, case_idx as u32); ctx.emit_jmp_if_true_labeled(eq_reg, case_label); ctx.restore_reg_checkpoint(compare_reg_checkpoint); } } let has_default = cases.iter().any(|c| c.test.is_none()); if !has_default { ctx.emit_jmp_labeled(end_label); } for (case_idx, case) in cases.iter().enumerate() { let case_label = Label::SwitchCase(id, case_idx as u32); ctx.labels.label_map.insert(case_label, ctx.bytecode.len()); for s in &case.consequent { self.emit_statement(s, ctx)?; } } ctx.labels.label_map.insert(end_label, ctx.bytecode.len()); ctx.pop_switch(); Ok(None) } pub(crate) fn count_switch_domain(&self, stmt: &Statement, ctx: &mut CompileCtx) { if let Statement::SwitchStatement(sw) = stmt { self.count_switch_statement(sw, ctx); } } pub(crate) fn emit_switch_domain(&self, stmt: &Statement, ctx: &mut CompileCtx) -> Result, String> { match stmt { Statement::SwitchStatement(_) => self.emit_switch_statement(stmt, ctx), _ => Ok(None), } } }