This repository has been archived on 2026-09-15. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
OxideJS/crates/oxide_compiler/src/stmt/switch.rs
T
2026-08-05 00:24:12 +08:00

86 lines
3.4 KiB
Rust

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<Option<u8>, 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<Option<u8>, String> {
match stmt {
Statement::SwitchStatement(_) => self.emit_switch_statement(stmt, ctx),
_ => Ok(None),
}
}
}