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 = match chunk.functions.iter().find(|f| f.entry as usize == i) {
29            Some(f) => format!("  ; {}", f.name),
30            None => String::new(),
31        };
32        let _ = writeln!(out, "{i:04}  {instr}{label}");
33    }
34    out
35}
36
37#[cfg(test)]
38mod tests {
39    use crate::bytecode::builder::ChunkBuilder;
40
41
42    #[test]
43    fn lists_main() {
44        let mut b = ChunkBuilder::new("d");
45        b.begin_function("main", 0, 1);
46        b.emit_load_imm(0, 1);
47        b.emit_return(0);
48        let text = super::disassemble(&b.finish());
49        assert!(text.contains("LoadImm r0, 1"));
50        assert!(text.contains("main"));
51    }
52}