use std::ops::Index;
use super::opcode::OpCode;
use super::value;
// In the book, this type is a hand-rolled dynamic array
// implementation in C. The main benefit of following that approach
// would be avoiding issues with OpCode variants not having equal
// sizes, but for the purpose of this I'm going to ignore that
// problem.
#[derive(Debug, Default)]
pub struct Chunk {
pub code: Vec<OpCode>,
lines: Vec<Span>,
constants: Vec<value::Value>,
}
#[derive(Debug)]
struct Span {
/// Source code line
line: usize,
/// Number of instructions derived from this line
count: usize,
}
impl Chunk {
pub fn add_op(&mut self, data: OpCode, line: usize) -> usize {
let idx = self.code.len();
self.code.push(data);
self.add_line(line);
idx
}
pub fn add_constant(&mut self, data: value::Value) -> usize {
let idx = self.constants.len();
self.constants.push(data);
idx
}
pub fn constant(&self, idx: usize) -> &value::Value {
self.constants.index(idx)
}
fn add_line(&mut self, line: usize) {
match self.lines.last_mut() {
Some(span) if span.line == line => span.count += 1,
_ => self.lines.push(Span { line, count: 1 }),
}
}
fn get_line(&self, offset: usize) -> usize {
let mut pos = 0;
for span in &self.lines {
pos += span.count;
if pos > offset {
return span.line;
}
}
panic!("invalid chunk state: line missing for offset {}", offset);
}
}
// Disassembler
/// Print a single disassembled instruction at the specified offset.
/// Some instructions are printed "raw", others have special handling.
#[cfg(feature = "disassemble")]
pub fn disassemble_instruction(chunk: &Chunk, offset: usize) {
print!("{:04} ", offset);
let line = chunk.get_line(offset);
if offset > 0 && line == chunk.get_line(offset - 1) {
print!(" | ");
} else {
print!("{:4} ", line);
}
match chunk.code.index(offset) {
OpCode::OpConstant(idx) => {
println!("OpConstant({}) '{:?}'", *idx, chunk.constant(*idx))
}
op => println!("{:?}", op),
}
}