Make chunk debug print infallible

This commit is contained in:
Jeff 2024-09-07 18:48:01 -04:00
parent e9ec838b25
commit 32347ec512
3 changed files with 107 additions and 30 deletions

View File

@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize};
use crate::{Identifier, Instruction, Span, Value};
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Eq, PartialEq, Serialize, Deserialize)]
pub struct Chunk {
code: Vec<(u8, Span)>,
constants: Vec<Value>,
@ -149,6 +149,12 @@ impl Display for Chunk {
}
}
impl Debug 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),

View File

@ -243,20 +243,22 @@ impl<'src> Parser<'src> {
}
fn parse_statement(&mut self) -> Result<(), ParseError> {
match self.current_token {
Token::Let => self.parse_let_assignment()?,
_ => self.parse_expression_statement()?,
}
Ok(())
}
fn parse_expression_statement(&mut self) -> Result<(), ParseError> {
let start = self.current_position.0;
let is_expression_statement = match self.current_token {
Token::Let => {
self.parse_let_assignment(true)?;
self.parse_expression()?;
false
}
_ => {
self.parse_expression()?;
if self.allow(TokenKind::Semicolon)? {
true
}
};
let has_semicolon = self.allow(TokenKind::Semicolon)?;
if is_expression_statement && has_semicolon {
let end = self.previous_position.1;
self.emit_byte(Instruction::Pop as u8, Span(start, end));
@ -265,7 +267,7 @@ impl<'src> Parser<'src> {
Ok(())
}
fn parse_let_assignment(&mut self) -> Result<(), ParseError> {
fn parse_let_assignment(&mut self, _allow_assignment: bool) -> Result<(), ParseError> {
self.expect(TokenKind::Let)?;
let position = self.current_position;
@ -273,7 +275,6 @@ impl<'src> Parser<'src> {
self.expect(TokenKind::Equal)?;
self.parse_expression()?;
self.expect(TokenKind::Semicolon)?;
self.define_variable(identifier_index, position)
}
@ -430,7 +431,11 @@ impl From<&TokenKind> for ParseRule<'_> {
TokenKind::FloatKeyword => todo!(),
TokenKind::If => todo!(),
TokenKind::Int => todo!(),
TokenKind::Let => todo!(),
TokenKind::Let => ParseRule {
prefix: Some(Parser::parse_let_assignment),
infix: None,
precedence: Precedence::None,
},
TokenKind::Loop => todo!(),
TokenKind::Map => todo!(),
TokenKind::Str => todo!(),
@ -546,6 +551,44 @@ impl From<ChunkError> for ParseError {
mod tests {
use super::*;
#[test]
fn add_variables() {
let source = "
let x = 42
let y = 42
x + y
";
let test_chunk = parse(source);
assert_eq!(
test_chunk,
Ok(Chunk::with_data(
vec![
(Instruction::Constant as u8, Span(21, 23)),
(0, Span(21, 23)),
(Instruction::DefineGlobal as u8, Span(17, 18)),
(0, Span(17, 18)),
(Instruction::Constant as u8, Span(44, 46)),
(1, Span(44, 46)),
(Instruction::DefineGlobal as u8, Span(40, 41)),
(1, Span(40, 41)),
(Instruction::GetGlobal as u8, Span(61, 62)),
(0, Span(61, 62)),
(Instruction::GetGlobal as u8, Span(52, 53)),
(1, Span(52, 53)),
(Instruction::Add as u8, Span(48, 53))
],
vec![Value::integer(42), Value::integer(42)],
vec![
Identifier::new("x"),
Identifier::new("y"),
Identifier::new("x"),
Identifier::new("y")
]
))
);
}
#[test]
fn let_statement() {
let source = "let x = 42;";

View File

@ -294,13 +294,23 @@ impl Instruction {
pub fn disassemble(&self, chunk: &Chunk, offset: usize) -> String {
match self {
Instruction::Constant => {
let (index, _) = chunk.read(offset + 1).unwrap();
let value_display = chunk
.get_constant(*index as usize)
.map(|value| value.to_string())
.unwrap_or_else(|error| format!("{:?}", error));
let (index_display, value_display) = if let Ok((index, _)) = chunk.read(offset + 1)
{
let index_string = index.to_string();
let value_string = chunk
.get_constant(*index as usize)
.map(|value| value.to_string())
.unwrap_or_else(|error| format!("{:?}", error));
format!("{offset:04} CONSTANT {index} {value_display}")
(index_string, value_string)
} else {
let index = "ERROR".to_string();
let value = "ERROR".to_string();
(index, value)
};
format!("{offset:04} CONSTANT {index_display} {value_display}")
}
Instruction::Return => format!("{offset:04} RETURN"),
Instruction::Pop => format!("{offset:04} POP"),
@ -309,27 +319,45 @@ impl Instruction {
Instruction::DefineGlobal => {
let (index, _) = chunk.read(offset + 1).unwrap();
let index = *index as usize;
let identifier = chunk.get_identifier(index).unwrap();
let value = chunk.get_constant(index).unwrap();
let identifier_display = chunk
.get_identifier(index)
.map(|identifier| identifier.to_string())
.unwrap_or_else(|error| format!("{:?}", error));
let value_display = chunk
.get_constant(index)
.map(|value| value.to_string())
.unwrap_or_else(|error| format!("{:?}", error));
format!("{offset:04} DEFINE_GLOBAL {identifier} {value}")
format!("{offset:04} DEFINE_GLOBAL {identifier_display} {value_display}")
}
Instruction::GetGlobal => {
let (index, _) = chunk.read(offset + 1).unwrap();
let index = *index as usize;
let identifier = chunk.get_identifier(index).unwrap();
let value = chunk.get_constant(index).unwrap();
let identifier_display = chunk
.get_identifier(index)
.map(|identifier| identifier.to_string())
.unwrap_or_else(|error| format!("{:?}", error));
let value_display = chunk
.get_constant(index)
.map(|value| value.to_string())
.unwrap_or_else(|error| format!("{:?}", error));
format!("{offset:04} GET_GLOBAL {identifier} {value}")
format!("{offset:04} GET_GLOBAL {identifier_display} {value_display}")
}
Instruction::SetGlobal => {
let (index, _) = chunk.read(offset + 1).unwrap();
let index = *index as usize;
let identifier = chunk.get_identifier(index).unwrap();
let value = chunk.get_constant(index).unwrap();
let identifier_display = chunk
.get_identifier(index)
.map(|identifier| identifier.to_string())
.unwrap_or_else(|error| format!("{:?}", error));
let value_display = chunk
.get_constant(index)
.map(|value| value.to_string())
.unwrap_or_else(|error| format!("{:?}", error));
format!("{offset:04} SET_GLOBAL {identifier} {value}")
format!("{offset:04} SET_GLOBAL {identifier_display} {value_display}")
}
// Unary