use crate::{opcodes::Op, types::Value};
use alloc::{collections::BTreeMap, string::String, vec::Vec};
use core::convert::TryFrom;
use self::disassemble::disassemble;
#[derive(Debug, Clone)]
pub struct Chunk {
bytecode: Vec<u8>,
constants: Vec<[u8; 8]>,
src_lines_map: BTreeMap<usize, usize>,
}
impl Chunk {
pub fn new() -> Chunk {
Chunk {
bytecode: Vec::new(),
constants: Vec::new(),
src_lines_map: BTreeMap::new(),
}
}
pub fn push_op(&mut self, src_line: usize, opcode: Op) {
if !self.src_lines_map.contains_key(&src_line) {
self.src_lines_map.insert(src_line, self.bytecode.len());
}
self.bytecode.push(opcode.into());
}
pub fn push_const(&mut self, src_line: usize, value: Value) {
let index: u8 = u8::try_from(self.constants.len()).expect("Constants vec longer than 256");
self.constants.push(value.into());
self.push_op(src_line, Op::Constant);
self.bytecode.push(index);
}
pub fn disassemble(&self) -> String {
disassemble(self)
}
pub fn constants(&self) -> &[[u8; 8]] {
self.constants.as_slice()
}
pub fn bytecode(&self) -> &[u8] {
self.bytecode.as_slice()
}
}
pub mod disassemble {
use crate::{chunk::Chunk, opcodes::Op};
use alloc::{format, string::String};
use core::{convert::TryInto, iter::Enumerate, slice::Iter};
pub fn disassemble(chunk: &Chunk) -> String {
let mut line_list = chunk.src_lines_map.iter().peekable();
let mut code = chunk.bytecode.iter().enumerate();
let mut result = String::new();
while let Some((n_byte, bytes)) = code.next() {
let line = if let Some((&next_line, &src_line_byte)) = line_list.peek() {
if src_line_byte == n_byte {
line_list.next();
format!("{}", next_line)
} else {
String::from("|")
}
} else {
String::from("|")
};
result.push_str(&format!("{:0>4} {:>4} ", n_byte, line));
let opcode = (*bytes).try_into().unwrap();
match opcode {
Op::Constant => constant(chunk, &mut code, &mut result),
Op::Negate => negate(&mut result),
_ => result.push_str(&format!("{:?}", opcode)),
}
}
result
}
pub fn constant(chunk: &Chunk, code: &mut Enumerate<Iter<u8>>, output: &mut String) {
let (_, const_index) = code.next().expect("OpConstant not followed by an operand");
let constant = f64::from_be_bytes(
*(chunk
.constants()
.get(*const_index as usize)
.expect("Provided constant index out of bounds")),
);
output.push_str(&format!(
"{:?} {:>4} '{}'",
Op::Constant,
const_index,
constant
));
}
pub fn negate(output: &mut String) {
output.push_str(&format!("{:?}", Op::Negate));
}
}