1
0
dust/dust-lang/src/chunk.rs
2024-09-07 17:16:14 -04:00

160 lines
4.0 KiB
Rust

use std::fmt::{self, Debug, Display, Formatter};
use serde::{Deserialize, Serialize};
use crate::{Identifier, Instruction, Span, Value};
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub struct Chunk {
code: Vec<(u8, Span)>,
constants: Vec<Value>,
identifiers: Vec<Identifier>,
}
impl Chunk {
pub fn new() -> Self {
Self {
code: Vec::new(),
constants: Vec::new(),
identifiers: Vec::new(),
}
}
pub fn with_data(
code: Vec<(u8, Span)>,
constants: Vec<Value>,
identifiers: Vec<Identifier>,
) -> Self {
Self {
code,
constants,
identifiers,
}
}
pub fn len(&self) -> usize {
self.code.len()
}
pub fn is_empty(&self) -> bool {
self.code.is_empty()
}
pub fn capacity(&self) -> usize {
self.code.capacity()
}
pub fn read(&self, offset: usize) -> Result<&(u8, Span), ChunkError> {
self.code
.get(offset)
.ok_or(ChunkError::CodeIndextOfBounds(offset))
}
pub fn write(&mut self, instruction: u8, position: Span) {
self.code.push((instruction, position));
}
pub fn get_constant(&self, index: usize) -> Result<&Value, ChunkError> {
self.constants
.get(index)
.ok_or(ChunkError::ConstantIndexOutOfBounds(index))
}
pub fn push_constant(&mut self, value: Value) -> Result<u8, ChunkError> {
let starting_length = self.constants.len();
if starting_length + 1 > (u8::MAX as usize) {
Err(ChunkError::ConstantOverflow)
} else {
self.constants.push(value);
Ok(starting_length as u8)
}
}
pub fn get_identifier(&self, index: usize) -> Result<&Identifier, ChunkError> {
self.identifiers
.get(index)
.ok_or(ChunkError::IdentifierIndexOutOfBounds(index))
}
pub fn push_identifier(&mut self, identifier: Identifier) -> Result<u8, ChunkError> {
let starting_length = self.constants.len();
if starting_length + 1 > (u8::MAX as usize) {
Err(ChunkError::IdentifierOverflow)
} else {
self.identifiers.push(identifier);
Ok(starting_length as u8)
}
}
pub fn clear(&mut self) {
self.code.clear();
self.constants.clear();
}
pub fn disassemble(&self, name: &str) -> String {
let mut output = String::new();
output.push_str("== ");
output.push_str(name);
output.push_str(" ==\n");
let mut previous = None;
for (offset, (byte, position)) in self.code.iter().enumerate() {
if let Some(Instruction::Constant) = previous {
let display = format!("{position} {offset:04} CONSTANT_INDEX {byte}\n");
previous = None;
output.push_str(&display);
continue;
}
if let Some(
Instruction::DefineGlobal | Instruction::GetGlobal | Instruction::SetGlobal,
) = previous
{
let display = format!("{position} {offset:04} IDENTIFIER_INDEX {byte}\n");
previous = None;
output.push_str(&display);
continue;
}
let instruction = Instruction::from_byte(*byte).unwrap();
let display = format!("{} {}\n", position, instruction.disassemble(self, offset));
previous = Some(instruction);
output.push_str(&display);
}
output
}
}
impl Default for Chunk {
fn default() -> Self {
Self::new()
}
}
impl Display for Chunk {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{}", self.disassemble("Chunk"))
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ChunkError {
CodeIndextOfBounds(usize),
ConstantOverflow,
ConstantIndexOutOfBounds(usize),
IdentifierIndexOutOfBounds(usize),
IdentifierOverflow,
}