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-10 13:26:05 +00:00
|
|
|
use crate::{
|
2024-09-10 22:19:59 +00:00
|
|
|
identifier_stack::Local, AnnotatedError, Identifier, IdentifierStack, Instruction, Span, Value,
|
|
|
|
ValueLocation,
|
2024-09-10 13:26:05 +00:00
|
|
|
};
|
2024-09-07 10:38:12 +00:00
|
|
|
|
2024-09-07 22:48:01 +00:00
|
|
|
#[derive(Clone, Eq, PartialEq, Serialize, Deserialize)]
|
2024-09-07 10:38:12 +00:00
|
|
|
pub struct Chunk {
|
|
|
|
code: Vec<(u8, Span)>,
|
|
|
|
constants: Vec<Value>,
|
2024-09-09 23:23:49 +00:00
|
|
|
identifiers: IdentifierStack,
|
2024-09-07 10:38:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Chunk {
|
|
|
|
pub fn new() -> Self {
|
|
|
|
Self {
|
|
|
|
code: Vec::new(),
|
|
|
|
constants: Vec::new(),
|
2024-09-09 23:23:49 +00:00
|
|
|
identifiers: IdentifierStack::new(),
|
2024-09-07 10:38:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-09-07 16:15:47 +00:00
|
|
|
pub fn with_data(
|
|
|
|
code: Vec<(u8, Span)>,
|
|
|
|
constants: Vec<Value>,
|
2024-09-09 23:23:49 +00:00
|
|
|
identifiers: Vec<Local>,
|
2024-09-07 16:15:47 +00:00
|
|
|
) -> Self {
|
|
|
|
Self {
|
|
|
|
code,
|
|
|
|
constants,
|
2024-09-09 23:23:49 +00:00
|
|
|
identifiers: IdentifierStack::with_data(identifiers, 0),
|
2024-09-07 16:15:47 +00:00
|
|
|
}
|
2024-09-07 10:38:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn len(&self) -> usize {
|
|
|
|
self.code.len()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn is_empty(&self) -> bool {
|
|
|
|
self.code.is_empty()
|
|
|
|
}
|
|
|
|
|
2024-09-10 22:19:59 +00:00
|
|
|
pub fn scope_depth(&self) -> usize {
|
|
|
|
self.identifiers.scope_depth()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get_code(&self, offset: usize, position: Span) -> Result<&(u8, Span), ChunkError> {
|
2024-09-07 16:15:47 +00:00
|
|
|
self.code
|
|
|
|
.get(offset)
|
2024-09-10 22:19:59 +00:00
|
|
|
.ok_or(ChunkError::CodeIndexOfBounds { offset, position })
|
2024-09-07 10:38:12 +00:00
|
|
|
}
|
|
|
|
|
2024-09-10 02:57:14 +00:00
|
|
|
pub fn push_code(&mut self, instruction: u8, position: Span) {
|
2024-09-07 10:38:12 +00:00
|
|
|
self.code.push((instruction, position));
|
|
|
|
}
|
|
|
|
|
2024-09-10 22:19:59 +00:00
|
|
|
pub fn get_constant(&self, index: u8, position: Span) -> Result<&Value, ChunkError> {
|
2024-09-07 10:38:12 +00:00
|
|
|
self.constants
|
2024-09-09 23:23:49 +00:00
|
|
|
.get(index as usize)
|
2024-09-10 22:19:59 +00:00
|
|
|
.ok_or(ChunkError::ConstantIndexOutOfBounds { index, position })
|
2024-09-07 10:38:12 +00:00
|
|
|
}
|
|
|
|
|
2024-09-10 22:19:59 +00:00
|
|
|
pub fn remove_constant(&mut self, index: u8, position: Span) -> Result<Value, ChunkError> {
|
2024-09-10 07:42:25 +00:00
|
|
|
let index = index as usize;
|
|
|
|
|
|
|
|
if index >= self.constants.len() {
|
2024-09-10 22:19:59 +00:00
|
|
|
Err(ChunkError::ConstantIndexOutOfBounds {
|
|
|
|
index: index as u8,
|
|
|
|
position,
|
|
|
|
})
|
2024-09-10 07:42:25 +00:00
|
|
|
} else {
|
|
|
|
Ok(self.constants.remove(index))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-09-07 10:38:12 +00:00
|
|
|
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) {
|
2024-09-07 16:15:47 +00:00
|
|
|
Err(ChunkError::ConstantOverflow)
|
2024-09-07 10:38:12 +00:00
|
|
|
} else {
|
|
|
|
self.constants.push(value);
|
|
|
|
|
|
|
|
Ok(starting_length as u8)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-09-09 23:23:49 +00:00
|
|
|
pub fn contains_identifier(&self, identifier: &Identifier) -> bool {
|
|
|
|
self.identifiers.contains(identifier)
|
|
|
|
}
|
|
|
|
|
2024-09-10 13:26:05 +00:00
|
|
|
pub fn get_local(&self, index: u8) -> Result<&Local, ChunkError> {
|
|
|
|
self.identifiers
|
|
|
|
.get(index as usize)
|
|
|
|
.ok_or(ChunkError::IdentifierIndexOutOfBounds(index))
|
|
|
|
}
|
|
|
|
|
2024-09-10 22:19:59 +00:00
|
|
|
pub fn resolve_local(&self, identifier: &Identifier) -> Option<u8> {
|
|
|
|
self.identifiers.resolve(self, identifier)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn resolve_local_index(&self, identifier: &Identifier) -> Option<u8> {
|
|
|
|
self.identifiers.resolve_index(identifier)
|
|
|
|
}
|
|
|
|
|
2024-09-09 23:23:49 +00:00
|
|
|
pub fn get_identifier(&self, index: u8) -> Result<&Identifier, ChunkError> {
|
2024-09-07 16:15:47 +00:00
|
|
|
self.identifiers
|
2024-09-09 23:23:49 +00:00
|
|
|
.get(index as usize)
|
|
|
|
.map(|local| &local.identifier)
|
2024-09-07 16:15:47 +00:00
|
|
|
.ok_or(ChunkError::IdentifierIndexOutOfBounds(index))
|
|
|
|
}
|
|
|
|
|
2024-09-10 22:19:59 +00:00
|
|
|
pub fn get_identifier_index(
|
|
|
|
&self,
|
|
|
|
identifier: &Identifier,
|
|
|
|
position: Span,
|
|
|
|
) -> Result<u8, ChunkError> {
|
2024-09-09 23:23:49 +00:00
|
|
|
self.identifiers
|
|
|
|
.get_index(identifier)
|
2024-09-10 22:19:59 +00:00
|
|
|
.map(|index| index as u8)
|
|
|
|
.ok_or(ChunkError::IdentifierNotFound {
|
|
|
|
identifier: identifier.clone(),
|
|
|
|
position,
|
|
|
|
})
|
2024-09-09 23:23:49 +00:00
|
|
|
}
|
|
|
|
|
2024-09-10 13:26:05 +00:00
|
|
|
pub fn push_constant_identifier(&mut self, identifier: Identifier) -> Result<u8, ChunkError> {
|
|
|
|
let starting_length = self.identifiers.local_count();
|
|
|
|
|
|
|
|
if starting_length + 1 > (u8::MAX as usize) {
|
|
|
|
Err(ChunkError::IdentifierOverflow)
|
|
|
|
} else {
|
|
|
|
self.identifiers
|
2024-09-10 22:19:59 +00:00
|
|
|
.define(identifier, ValueLocation::ConstantStack);
|
2024-09-10 13:26:05 +00:00
|
|
|
|
|
|
|
Ok(starting_length as u8)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn push_runtime_identifier(&mut self, identifier: Identifier) -> Result<u8, ChunkError> {
|
2024-09-09 23:23:49 +00:00
|
|
|
let starting_length = self.identifiers.local_count();
|
2024-09-07 16:15:47 +00:00
|
|
|
|
|
|
|
if starting_length + 1 > (u8::MAX as usize) {
|
|
|
|
Err(ChunkError::IdentifierOverflow)
|
|
|
|
} else {
|
2024-09-10 13:26:05 +00:00
|
|
|
self.identifiers
|
2024-09-10 22:19:59 +00:00
|
|
|
.define(identifier, ValueLocation::RuntimeStack);
|
2024-09-07 16:15:47 +00:00
|
|
|
|
|
|
|
Ok(starting_length as u8)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-09-10 22:19:59 +00:00
|
|
|
pub fn redefine_as_runtime_identifier(
|
|
|
|
&mut self,
|
|
|
|
identifier: &Identifier,
|
|
|
|
position: Span,
|
|
|
|
) -> Result<usize, ChunkError> {
|
|
|
|
self.identifiers
|
|
|
|
.redefine(identifier, ValueLocation::RuntimeStack)
|
|
|
|
.ok_or_else(|| ChunkError::IdentifierNotFound {
|
|
|
|
identifier: identifier.clone(),
|
|
|
|
position,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2024-09-10 14:44:15 +00:00
|
|
|
pub fn begin_scope(&mut self) {
|
|
|
|
self.identifiers.begin_scope();
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn end_scope(&mut self) {
|
|
|
|
self.identifiers.end_scope();
|
|
|
|
}
|
|
|
|
|
2024-09-07 10:38:12 +00:00
|
|
|
pub fn clear(&mut self) {
|
|
|
|
self.code.clear();
|
|
|
|
self.constants.clear();
|
2024-09-09 23:23:49 +00:00
|
|
|
self.identifiers.clear();
|
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 = 32_usize.saturating_sub(name_length + 2);
|
|
|
|
let name_buffer = " ".repeat(buffer_length / 2);
|
|
|
|
let name_line = format!("{name_buffer} {name} {name_buffer}\n");
|
|
|
|
|
|
|
|
output.push_str(&name_line);
|
|
|
|
output.push_str("\n Code \n");
|
2024-09-10 13:26:05 +00:00
|
|
|
output.push_str("------ ------------ ------------\n");
|
|
|
|
output.push_str("OFFSET POSITION INSTRUCTION\n");
|
|
|
|
output.push_str("------ ------------ ------------\n");
|
2024-09-07 10:38:12 +00:00
|
|
|
|
2024-09-07 21:16:14 +00:00
|
|
|
let mut previous = None;
|
2024-09-07 10:38:12 +00:00
|
|
|
|
|
|
|
for (offset, (byte, position)) in self.code.iter().enumerate() {
|
2024-09-07 21:16:14 +00:00
|
|
|
if let Some(
|
2024-09-10 03:24:22 +00:00
|
|
|
Instruction::Constant
|
2024-09-10 22:19:59 +00:00
|
|
|
| Instruction::DefineVariable
|
2024-09-10 03:24:22 +00:00
|
|
|
| Instruction::GetVariable
|
|
|
|
| Instruction::SetVariable,
|
2024-09-07 21:16:14 +00:00
|
|
|
) = previous
|
|
|
|
{
|
|
|
|
previous = None;
|
2024-09-07 10:38:12 +00:00
|
|
|
|
2024-09-07 21:16:14 +00:00
|
|
|
continue;
|
2024-09-07 10:38:12 +00:00
|
|
|
}
|
2024-09-07 21:16:14 +00:00
|
|
|
|
|
|
|
let instruction = Instruction::from_byte(*byte).unwrap();
|
2024-09-10 13:26:05 +00:00
|
|
|
let display = format!(
|
|
|
|
"{offset:4} {:12} {}\n",
|
|
|
|
position.to_string(),
|
|
|
|
instruction.disassemble(self, offset)
|
|
|
|
);
|
2024-09-10 00:55:00 +00:00
|
|
|
|
2024-09-10 13:26:05 +00:00
|
|
|
output.push_str(&display);
|
2024-09-10 22:19:59 +00:00
|
|
|
|
|
|
|
previous = Some(instruction);
|
2024-09-10 00:55:00 +00:00
|
|
|
}
|
|
|
|
|
2024-09-10 14:44:15 +00:00
|
|
|
output.push_str("\n Constants \n");
|
2024-09-10 13:26:05 +00:00
|
|
|
output.push_str("----- ---- -----\n");
|
2024-09-10 00:55:00 +00:00
|
|
|
output.push_str("INDEX KIND VALUE\n");
|
2024-09-10 13:26:05 +00:00
|
|
|
output.push_str("----- ---- -----\n");
|
2024-09-10 00:55:00 +00:00
|
|
|
|
|
|
|
for (index, value) in self.constants.iter().enumerate() {
|
|
|
|
let value_kind_display = match value {
|
|
|
|
Value::Raw(_) => "RAW ",
|
|
|
|
Value::Reference(_) => "REF ",
|
|
|
|
Value::Mutable(_) => "MUT ",
|
|
|
|
};
|
2024-09-10 13:26:05 +00:00
|
|
|
let display = format!("{index:3} {value_kind_display} {value}\n");
|
2024-09-10 00:55:00 +00:00
|
|
|
|
|
|
|
output.push_str(&display);
|
|
|
|
}
|
|
|
|
|
2024-09-10 14:44:15 +00:00
|
|
|
output.push_str("\n Identifiers \n");
|
2024-09-10 13:26:05 +00:00
|
|
|
output.push_str("----- ---------- -------- -----\n");
|
|
|
|
output.push_str("INDEX IDENTIFIER LOCATION DEPTH\n");
|
|
|
|
output.push_str("----- ---------- -------- -----\n");
|
|
|
|
|
|
|
|
for (
|
|
|
|
index,
|
|
|
|
Local {
|
|
|
|
identifier,
|
|
|
|
depth,
|
|
|
|
value_location,
|
|
|
|
},
|
|
|
|
) in self.identifiers.iter().enumerate()
|
|
|
|
{
|
|
|
|
let display = format!(
|
|
|
|
"{index:3} {:10} {value_location} {depth}\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 {
|
2024-09-10 05:04:30 +00:00
|
|
|
write!(f, "{}", self.disassemble("Chunk Disassembly"))
|
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 {
|
2024-09-10 05:04:30 +00:00
|
|
|
write!(f, "{}", self.disassemble("Chunk Disassembly"))
|
2024-09-07 22:48:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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,
|
|
|
|
},
|
2024-09-07 16:15:47 +00:00
|
|
|
ConstantOverflow,
|
2024-09-10 22:19:59 +00:00
|
|
|
ConstantIndexOutOfBounds {
|
|
|
|
index: u8,
|
|
|
|
position: Span,
|
|
|
|
},
|
2024-09-09 23:23:49 +00:00
|
|
|
IdentifierIndexOutOfBounds(u8),
|
2024-09-07 16:15:47 +00:00
|
|
|
IdentifierOverflow,
|
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::ConstantOverflow => "Constant overflow",
|
|
|
|
ChunkError::ConstantIndexOutOfBounds { .. } => "Constant index out of bounds",
|
|
|
|
ChunkError::IdentifierIndexOutOfBounds(_) => "Identifier index out of bounds",
|
|
|
|
ChunkError::IdentifierOverflow => "Identifier overflow",
|
|
|
|
ChunkError::IdentifierNotFound { .. } => "Identifier not found",
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn details(&self) -> Option<String> {
|
|
|
|
match self {
|
|
|
|
ChunkError::CodeIndexOfBounds { offset, .. } => Some(format!("Code index: {}", offset)),
|
|
|
|
ChunkError::ConstantIndexOutOfBounds { index, .. } => {
|
|
|
|
Some(format!("Constant index: {}", index))
|
|
|
|
}
|
|
|
|
ChunkError::IdentifierIndexOutOfBounds(index) => {
|
|
|
|
Some(format!("Identifier index: {}", index))
|
|
|
|
}
|
|
|
|
ChunkError::IdentifierNotFound { identifier, .. } => {
|
|
|
|
Some(format!("Identifier: {}", identifier))
|
|
|
|
}
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn position(&self) -> Span {
|
|
|
|
match self {
|
|
|
|
ChunkError::CodeIndexOfBounds { position, .. } => *position,
|
|
|
|
ChunkError::ConstantIndexOutOfBounds { position, .. } => *position,
|
|
|
|
ChunkError::IdentifierNotFound { position, .. } => *position,
|
|
|
|
_ => todo!(),
|
|
|
|
}
|
|
|
|
}
|
2024-09-07 10:38:12 +00:00
|
|
|
}
|
2024-09-10 05:04:30 +00:00
|
|
|
|
2024-09-10 13:26:05 +00:00
|
|
|
impl Display for ChunkError {
|
|
|
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
2024-09-10 05:04:30 +00:00
|
|
|
match self {
|
2024-09-10 22:19:59 +00:00
|
|
|
ChunkError::CodeIndexOfBounds { offset, .. } => {
|
2024-09-10 13:26:05 +00:00
|
|
|
write!(f, "Code index out of bounds: {}", offset)
|
2024-09-10 05:04:30 +00:00
|
|
|
}
|
2024-09-10 13:26:05 +00:00
|
|
|
ChunkError::ConstantOverflow => write!(f, "Constant overflow"),
|
2024-09-10 22:19:59 +00:00
|
|
|
ChunkError::ConstantIndexOutOfBounds { index, .. } => {
|
2024-09-10 13:26:05 +00:00
|
|
|
write!(f, "Constant index out of bounds: {}", index)
|
2024-09-10 05:04:30 +00:00
|
|
|
}
|
2024-09-10 13:26:05 +00:00
|
|
|
ChunkError::IdentifierIndexOutOfBounds(index) => {
|
|
|
|
write!(f, "Identifier index out of bounds: {}", index)
|
2024-09-10 05:04:30 +00:00
|
|
|
}
|
2024-09-10 13:26:05 +00:00
|
|
|
ChunkError::IdentifierOverflow => write!(f, "Identifier overflow"),
|
2024-09-10 22:19:59 +00:00
|
|
|
ChunkError::IdentifierNotFound { identifier, .. } => {
|
2024-09-10 13:26:05 +00:00
|
|
|
write!(f, "Identifier not found: {}", identifier)
|
2024-09-10 05:04:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|