use crate::op::Op;
use crate::value::Value;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct Chunk {
pub ops: Vec<Op>,
pub constants: Vec<Value>,
pub names: Vec<String>,
pub lines: Vec<u32>,
pub sub_entries: Vec<(u16, usize)>,
pub block_ranges: Vec<(usize, usize)>,
pub source: String,
}
impl Chunk {
pub fn new() -> Self {
Self::default()
}
pub fn find_sub(&self, name_idx: u16) -> Option<usize> {
self.sub_entries
.iter()
.find(|(n, _)| *n == name_idx)
.map(|(_, ip)| *ip)
}
}
pub struct ChunkBuilder {
chunk: Chunk,
name_map: std::collections::HashMap<String, u16>,
}
impl ChunkBuilder {
pub fn new() -> Self {
Self {
chunk: Chunk::new(),
name_map: std::collections::HashMap::new(),
}
}
pub fn emit(&mut self, op: Op, line: u32) -> usize {
let idx = self.chunk.ops.len();
self.chunk.ops.push(op);
self.chunk.lines.push(line);
idx
}
pub fn add_constant(&mut self, val: Value) -> u16 {
let idx = self.chunk.constants.len();
self.chunk.constants.push(val);
idx as u16
}
pub fn add_name(&mut self, name: &str) -> u16 {
if let Some(&idx) = self.name_map.get(name) {
return idx;
}
let idx = self.chunk.names.len() as u16;
self.chunk.names.push(name.to_string());
self.name_map.insert(name.to_string(), idx);
idx
}
pub fn current_pos(&self) -> usize {
self.chunk.ops.len()
}
pub fn patch_jump(&mut self, op_idx: usize, target: usize) {
match &mut self.chunk.ops[op_idx] {
Op::Jump(t)
| Op::JumpIfTrue(t)
| Op::JumpIfFalse(t)
| Op::JumpIfTrueKeep(t)
| Op::JumpIfFalseKeep(t) => *t = target,
_ => panic!("patch_jump on non-jump op at {}", op_idx),
}
}
pub fn add_sub_entry(&mut self, name_idx: u16, ip: usize) {
self.chunk.sub_entries.push((name_idx, ip));
}
pub fn add_block_range(&mut self, start: usize, end: usize) -> u16 {
let idx = self.chunk.block_ranges.len();
self.chunk.block_ranges.push((start, end));
idx as u16
}
pub fn set_source(&mut self, source: impl Into<String>) {
self.chunk.source = source.into();
}
pub fn build(self) -> Chunk {
self.chunk
}
}
impl Default for ChunkBuilder {
fn default() -> Self {
Self::new()
}
}