1
0
dust/dust-lang/src/chunk.rs

392 lines
11 KiB
Rust
Raw Normal View History

2024-09-10 02:57:14 +00:00
use std::fmt::{self, Debug, Display, Formatter};
2024-09-07 10:38:12 +00:00
use serde::{Deserialize, Serialize};
2024-09-11 07:10:12 +00:00
use crate::{AnnotatedError, Identifier, Instruction, Span, Value};
2024-09-07 10:38:12 +00:00
#[derive(Clone)]
2024-09-07 10:38:12 +00:00
pub struct Chunk {
instructions: Vec<(Instruction, Span)>,
constants: Vec<Option<Value>>,
locals: Vec<Local>,
2024-09-11 07:10:12 +00:00
scope_depth: usize,
2024-09-07 10:38:12 +00:00
}
impl Chunk {
pub fn new() -> Self {
Self {
instructions: Vec::new(),
2024-09-07 10:38:12 +00:00
constants: Vec::new(),
locals: Vec::new(),
2024-09-11 07:10:12 +00:00
scope_depth: 0,
2024-09-07 10:38:12 +00:00
}
}
2024-09-07 16:15:47 +00:00
pub fn with_data(
instructions: Vec<(Instruction, Span)>,
2024-09-07 16:15:47 +00:00
constants: Vec<Value>,
2024-09-09 23:23:49 +00:00
identifiers: Vec<Local>,
2024-09-07 16:15:47 +00:00
) -> Self {
Self {
instructions,
constants: constants.into_iter().map(Some).collect(),
locals: identifiers,
2024-09-11 07:10:12 +00:00
scope_depth: 0,
2024-09-07 16:15:47 +00:00
}
2024-09-07 10:38:12 +00:00
}
pub fn len(&self) -> usize {
self.instructions.len()
2024-09-07 10:38:12 +00:00
}
pub fn is_empty(&self) -> bool {
self.instructions.is_empty()
2024-09-07 10:38:12 +00:00
}
2024-09-10 22:19:59 +00:00
pub fn scope_depth(&self) -> usize {
2024-09-11 07:10:12 +00:00
self.scope_depth
2024-09-10 22:19:59 +00:00
}
pub fn get_instruction(
&self,
offset: usize,
position: Span,
) -> Result<&(Instruction, Span), ChunkError> {
self.instructions
2024-09-07 16:15:47 +00:00
.get(offset)
2024-09-10 22:19:59 +00:00
.ok_or(ChunkError::CodeIndexOfBounds { offset, position })
2024-09-07 10:38:12 +00:00
}
pub fn push_instruction(&mut self, instruction: Instruction, position: Span) {
self.instructions.push((instruction, position));
2024-09-07 10:38:12 +00:00
}
pub fn pop_instruction(&mut self) -> Option<(Instruction, Span)> {
self.instructions.pop()
}
pub fn get_constant(&self, index: usize, position: Span) -> Result<&Value, ChunkError> {
2024-09-07 10:38:12 +00:00
self.constants
.get(index)
2024-09-10 22:19:59 +00:00
.ok_or(ChunkError::ConstantIndexOutOfBounds { index, position })
.and_then(|value| {
value
.as_ref()
.ok_or(ChunkError::ConstantAlreadyUsed { index, position })
})
2024-09-07 10:38:12 +00:00
}
pub fn use_constant(&mut self, index: usize, position: Span) -> Result<Value, ChunkError> {
self.constants
.get_mut(index)
.ok_or_else(|| ChunkError::ConstantIndexOutOfBounds { index, position })?
.take()
.ok_or(ChunkError::ConstantAlreadyUsed { index, position })
}
pub fn push_constant(&mut self, value: Value, position: Span) -> Result<u16, ChunkError> {
2024-09-07 10:38:12 +00:00
let starting_length = self.constants.len();
if starting_length + 1 > (u8::MAX as usize) {
2024-09-11 07:10:12 +00:00
Err(ChunkError::ConstantOverflow { position })
2024-09-07 10:38:12 +00:00
} else {
self.constants.push(Some(value));
2024-09-07 10:38:12 +00:00
Ok(starting_length as u16)
2024-09-07 10:38:12 +00:00
}
}
2024-09-09 23:23:49 +00:00
pub fn contains_identifier(&self, identifier: &Identifier) -> bool {
self.locals
2024-09-11 07:10:12 +00:00
.iter()
.any(|local| &local.identifier == identifier)
2024-09-09 23:23:49 +00:00
}
pub fn get_local(&self, index: usize, position: Span) -> Result<&Local, ChunkError> {
self.locals
.get(index as usize)
.ok_or(ChunkError::LocalIndexOutOfBounds { index, position })
2024-09-10 22:19:59 +00:00
}
pub fn get_identifier(&self, index: u8) -> Option<&Identifier> {
if let Some(local) = self.locals.get(index as usize) {
Some(&local.identifier)
} else {
None
}
2024-09-07 16:15:47 +00:00
}
pub fn get_local_index(
2024-09-10 22:19:59 +00:00
&self,
identifier: &Identifier,
position: Span,
) -> Result<u16, ChunkError> {
self.locals
2024-09-11 07:10:12 +00:00
.iter()
.rev()
.enumerate()
2024-09-11 07:10:12 +00:00
.find_map(|(index, local)| {
if &local.identifier == identifier {
Some(index as u16)
2024-09-11 07:10:12 +00:00
} else {
None
}
})
2024-09-10 22:19:59 +00:00
.ok_or(ChunkError::IdentifierNotFound {
identifier: identifier.clone(),
position,
})
2024-09-09 23:23:49 +00:00
}
pub fn declare_local(
2024-09-11 07:10:12 +00:00
&mut self,
identifier: Identifier,
position: Span,
) -> Result<u16, ChunkError> {
let starting_length = self.locals.len();
2024-09-07 16:15:47 +00:00
if starting_length + 1 > (u8::MAX as usize) {
2024-09-11 07:10:12 +00:00
Err(ChunkError::IdentifierOverflow { position })
2024-09-07 16:15:47 +00:00
} else {
self.locals
.push(Local::new(identifier, self.scope_depth, None));
2024-09-07 16:15:47 +00:00
Ok(starting_length as u16)
2024-09-07 16:15:47 +00:00
}
}
pub fn define_local(
&mut self,
index: usize,
value: Value,
position: Span,
) -> Result<(), ChunkError> {
let local = self
.locals
.get_mut(index)
.ok_or_else(|| ChunkError::LocalIndexOutOfBounds { index, position })?;
local.value = Some(value);
Ok(())
}
2024-09-10 14:44:15 +00:00
pub fn begin_scope(&mut self) {
2024-09-11 07:10:12 +00:00
self.scope_depth += 1;
2024-09-10 14:44:15 +00:00
}
pub fn end_scope(&mut self) {
2024-09-11 07:10:12 +00:00
self.scope_depth -= 1;
2024-09-10 14:44:15 +00:00
}
2024-09-07 10:38:12 +00:00
pub fn clear(&mut self) {
self.instructions.clear();
2024-09-07 10:38:12 +00:00
self.constants.clear();
self.locals.clear();
2024-09-07 10:38:12 +00:00
}
2024-09-11 07:10:12 +00:00
pub fn identifiers(&self) -> &[Local] {
&self.locals
2024-09-11 07:10:12 +00:00
}
pub fn pop_identifier(&mut self) -> Option<Local> {
self.locals.pop()
2024-09-11 07:10:12 +00:00
}
2024-09-07 10:38:12 +00:00
pub fn disassemble(&self, name: &str) -> String {
let mut output = String::new();
2024-09-10 14:44:15 +00:00
let name_length = name.len();
let buffer_length = 51_usize.saturating_sub(name_length);
2024-09-10 14:44:15 +00:00
let name_buffer = " ".repeat(buffer_length / 2);
let underline = "-".repeat(name_length);
2024-09-10 14:44:15 +00:00
output.push_str(&format!("{name_buffer}{name}{name_buffer}\n"));
output.push_str(&format!("{name_buffer}{underline}{name_buffer}\n",));
output.push_str(" Code \n");
output.push_str("------ ---------------- -------------------- --------\n");
output.push_str("OFFSET INSTRUCTION INFO POSITION\n");
output.push_str("------ ---------------- -------------------- --------\n");
2024-09-07 10:38:12 +00:00
for (offset, (instruction, position)) in self.instructions.iter().enumerate() {
let display = format!(
"{offset:^6} {:37} {position}\n",
instruction.disassemble(self)
);
2024-09-10 00:55:00 +00:00
output.push_str(&display);
2024-09-10 00:55:00 +00:00
}
output.push_str("\n Constants\n");
output.push_str("----- ---- -----\n");
2024-09-10 00:55:00 +00:00
output.push_str("INDEX KIND VALUE\n");
output.push_str("----- ---- -----\n");
2024-09-10 00:55:00 +00:00
for (index, value_option) in self.constants.iter().enumerate() {
let value_kind_display = match value_option {
Some(Value::Raw(_)) => "RAW ",
Some(Value::Reference(_)) => "REF ",
Some(Value::Mutable(_)) => "MUT ",
None => "EMPTY",
2024-09-10 00:55:00 +00:00
};
let value_display = value_option
.as_ref()
.map(|value| value.to_string())
.unwrap_or_else(|| "EMPTY".to_string());
let display = format!("{index:3} {value_kind_display} {value_display}\n",);
2024-09-10 00:55:00 +00:00
output.push_str(&display);
}
output.push_str("\n Locals\n");
output.push_str("----- ---------- ----- -----\n");
output.push_str("INDEX NAME DEPTH VALUE\n");
output.push_str("----- ---------- ----- -----\n");
for (
index,
Local {
identifier,
depth,
value,
},
) in self.locals.iter().enumerate()
{
let value_display = value
.as_ref()
.map(|value| value.to_string())
.unwrap_or_else(|| "EMPTY".to_string());
let display = format!(
"{index:3} {:10} {depth:<5} {value_display}\n",
identifier.as_str()
);
2024-09-07 21:16:14 +00:00
output.push_str(&display);
2024-09-07 10:38:12 +00:00
}
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 Display"))
2024-09-07 10:38:12 +00:00
}
}
2024-09-07 22:48:01 +00:00
impl Debug for Chunk {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{}", self.disassemble("Chunk Debug Display"))
2024-09-07 22:48:01 +00:00
}
}
impl Eq for Chunk {}
impl PartialEq for Chunk {
fn eq(&self, other: &Self) -> bool {
self.instructions == other.instructions
&& self.constants == other.constants
&& self.locals == other.locals
}
}
2024-09-11 07:10:12 +00:00
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub struct Local {
pub identifier: Identifier,
pub depth: usize,
pub value: Option<Value>,
2024-09-11 07:10:12 +00:00
}
impl Local {
pub fn new(identifier: Identifier, depth: usize, value: Option<Value>) -> Self {
Self {
identifier,
depth,
value,
}
}
}
2024-09-09 23:23:49 +00:00
#[derive(Debug, Clone, PartialEq)]
2024-09-07 10:38:12 +00:00
pub enum ChunkError {
2024-09-10 22:19:59 +00:00
CodeIndexOfBounds {
offset: usize,
position: Span,
},
ConstantAlreadyUsed {
index: usize,
position: Span,
},
2024-09-11 07:10:12 +00:00
ConstantOverflow {
position: Span,
},
2024-09-10 22:19:59 +00:00
ConstantIndexOutOfBounds {
index: usize,
2024-09-10 22:19:59 +00:00
position: Span,
},
LocalIndexOutOfBounds {
index: usize,
2024-09-11 07:10:12 +00:00
position: Span,
},
IdentifierOverflow {
position: Span,
},
2024-09-10 22:19:59 +00:00
IdentifierNotFound {
identifier: Identifier,
position: Span,
},
}
impl AnnotatedError for ChunkError {
fn title() -> &'static str {
"Chunk Error"
}
fn description(&self) -> &'static str {
match self {
ChunkError::CodeIndexOfBounds { .. } => "Code index out of bounds",
ChunkError::ConstantAlreadyUsed { .. } => "Constant already used",
2024-09-11 07:10:12 +00:00
ChunkError::ConstantOverflow { .. } => "Constant overflow",
2024-09-10 22:19:59 +00:00
ChunkError::ConstantIndexOutOfBounds { .. } => "Constant index out of bounds",
ChunkError::LocalIndexOutOfBounds { .. } => "Identifier index out of bounds",
2024-09-11 07:10:12 +00:00
ChunkError::IdentifierOverflow { .. } => "Identifier overflow",
2024-09-10 22:19:59 +00:00
ChunkError::IdentifierNotFound { .. } => "Identifier not found",
}
}
fn details(&self) -> Option<String> {
match self {
ChunkError::CodeIndexOfBounds { offset, .. } => Some(format!("Code index: {}", offset)),
ChunkError::ConstantAlreadyUsed { index, .. } => {
Some(format!("Constant index: {}", index))
}
2024-09-10 22:19:59 +00:00
ChunkError::ConstantIndexOutOfBounds { index, .. } => {
Some(format!("Constant index: {}", index))
}
ChunkError::LocalIndexOutOfBounds { index, .. } => {
2024-09-10 22:19:59 +00:00
Some(format!("Identifier index: {}", index))
}
ChunkError::IdentifierNotFound { identifier, .. } => {
Some(format!("Identifier: {}", identifier))
}
_ => None,
}
}
fn position(&self) -> Span {
match self {
ChunkError::CodeIndexOfBounds { position, .. } => *position,
ChunkError::ConstantAlreadyUsed { position, .. } => *position,
2024-09-10 22:19:59 +00:00
ChunkError::ConstantIndexOutOfBounds { position, .. } => *position,
ChunkError::IdentifierNotFound { position, .. } => *position,
_ => todo!(),
}
}
2024-09-07 10:38:12 +00:00
}