Skip to main content

byteflow/bytecode/
disasm.rs

1use std::fmt::Write as _;
2
3use super::chunk::{Chunk, ABI_VERSION};
4
5/// Human-readable listing of a [`Chunk`], used by `byteflow-cli disasm`.
6pub fn disassemble(chunk: &Chunk) -> String {
7    let mut out = String::new();
8    let _ = writeln!(
9        out,
10        "; chunk {:?} abi={} constants={} functions={} code={}",
11        chunk.name,
12        ABI_VERSION,
13        chunk.constants.len(),
14        chunk.functions.len(),
15        chunk.code.len()
16    );
17    for (i, c) in chunk.constants.iter().enumerate() {
18        let _ = writeln!(out, ";   const[{i}] = {c}");
19    }
20    for (i, def) in chunk.functions.iter().enumerate() {
21        let _ = writeln!(
22            out,
23            "; fn[{i}] {} entry={} arity={} regs={}",
24            def.name, def.entry, def.arity, def.num_registers
25        );
26    }
27    for (i, instr) in chunk.code.iter().enumerate() {
28        let label = chunk
29            .functions
30            .iter()
31            .find(|f| f.entry as usize == i)
32            .map(|f| format!("  ; {}", f.name))
33            .unwrap_or_default();
34        let _ = writeln!(out, "{i:04}  {instr}{label}");
35    }
36    out
37}
38
39#[cfg(test)]
40mod tests {
41    use crate::bytecode::builder::ChunkBuilder;
42
43
44    #[test]
45    fn lists_main() {
46        let mut b = ChunkBuilder::new("d");
47        b.begin_function("main", 0, 1);
48        b.emit_load_imm(0, 1);
49        b.emit_return(0);
50        let text = super::disassemble(&b.finish());
51        assert!(text.contains("LoadImm r0, 1"));
52        assert!(text.contains("main"));
53    }
54}