Make chunk debug print infallible
This commit is contained in:
parent
e9ec838b25
commit
32347ec512
@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize};
|
|||||||
|
|
||||||
use crate::{Identifier, Instruction, Span, Value};
|
use crate::{Identifier, Instruction, Span, Value};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
|
#[derive(Clone, Eq, PartialEq, Serialize, Deserialize)]
|
||||||
pub struct Chunk {
|
pub struct Chunk {
|
||||||
code: Vec<(u8, Span)>,
|
code: Vec<(u8, Span)>,
|
||||||
constants: Vec<Value>,
|
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)]
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
pub enum ChunkError {
|
pub enum ChunkError {
|
||||||
CodeIndextOfBounds(usize),
|
CodeIndextOfBounds(usize),
|
||||||
|
@ -243,20 +243,22 @@ impl<'src> Parser<'src> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn parse_statement(&mut self) -> Result<(), ParseError> {
|
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 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;
|
let end = self.previous_position.1;
|
||||||
|
|
||||||
self.emit_byte(Instruction::Pop as u8, Span(start, end));
|
self.emit_byte(Instruction::Pop as u8, Span(start, end));
|
||||||
@ -265,7 +267,7 @@ impl<'src> Parser<'src> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_let_assignment(&mut self) -> Result<(), ParseError> {
|
fn parse_let_assignment(&mut self, _allow_assignment: bool) -> Result<(), ParseError> {
|
||||||
self.expect(TokenKind::Let)?;
|
self.expect(TokenKind::Let)?;
|
||||||
|
|
||||||
let position = self.current_position;
|
let position = self.current_position;
|
||||||
@ -273,7 +275,6 @@ impl<'src> Parser<'src> {
|
|||||||
|
|
||||||
self.expect(TokenKind::Equal)?;
|
self.expect(TokenKind::Equal)?;
|
||||||
self.parse_expression()?;
|
self.parse_expression()?;
|
||||||
self.expect(TokenKind::Semicolon)?;
|
|
||||||
self.define_variable(identifier_index, position)
|
self.define_variable(identifier_index, position)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -430,7 +431,11 @@ impl From<&TokenKind> for ParseRule<'_> {
|
|||||||
TokenKind::FloatKeyword => todo!(),
|
TokenKind::FloatKeyword => todo!(),
|
||||||
TokenKind::If => todo!(),
|
TokenKind::If => todo!(),
|
||||||
TokenKind::Int => 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::Loop => todo!(),
|
||||||
TokenKind::Map => todo!(),
|
TokenKind::Map => todo!(),
|
||||||
TokenKind::Str => todo!(),
|
TokenKind::Str => todo!(),
|
||||||
@ -546,6 +551,44 @@ impl From<ChunkError> for ParseError {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
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]
|
#[test]
|
||||||
fn let_statement() {
|
fn let_statement() {
|
||||||
let source = "let x = 42;";
|
let source = "let x = 42;";
|
||||||
|
@ -294,13 +294,23 @@ impl Instruction {
|
|||||||
pub fn disassemble(&self, chunk: &Chunk, offset: usize) -> String {
|
pub fn disassemble(&self, chunk: &Chunk, offset: usize) -> String {
|
||||||
match self {
|
match self {
|
||||||
Instruction::Constant => {
|
Instruction::Constant => {
|
||||||
let (index, _) = chunk.read(offset + 1).unwrap();
|
let (index_display, value_display) = if let Ok((index, _)) = chunk.read(offset + 1)
|
||||||
let value_display = chunk
|
{
|
||||||
.get_constant(*index as usize)
|
let index_string = index.to_string();
|
||||||
.map(|value| value.to_string())
|
let value_string = chunk
|
||||||
.unwrap_or_else(|error| format!("{:?}", error));
|
.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::Return => format!("{offset:04} RETURN"),
|
||||||
Instruction::Pop => format!("{offset:04} POP"),
|
Instruction::Pop => format!("{offset:04} POP"),
|
||||||
@ -309,27 +319,45 @@ impl Instruction {
|
|||||||
Instruction::DefineGlobal => {
|
Instruction::DefineGlobal => {
|
||||||
let (index, _) = chunk.read(offset + 1).unwrap();
|
let (index, _) = chunk.read(offset + 1).unwrap();
|
||||||
let index = *index as usize;
|
let index = *index as usize;
|
||||||
let identifier = chunk.get_identifier(index).unwrap();
|
let identifier_display = chunk
|
||||||
let value = chunk.get_constant(index).unwrap();
|
.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 => {
|
Instruction::GetGlobal => {
|
||||||
let (index, _) = chunk.read(offset + 1).unwrap();
|
let (index, _) = chunk.read(offset + 1).unwrap();
|
||||||
let index = *index as usize;
|
let index = *index as usize;
|
||||||
let identifier = chunk.get_identifier(index).unwrap();
|
let identifier_display = chunk
|
||||||
let value = chunk.get_constant(index).unwrap();
|
.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 => {
|
Instruction::SetGlobal => {
|
||||||
let (index, _) = chunk.read(offset + 1).unwrap();
|
let (index, _) = chunk.read(offset + 1).unwrap();
|
||||||
let index = *index as usize;
|
let index = *index as usize;
|
||||||
let identifier = chunk.get_identifier(index).unwrap();
|
let identifier_display = chunk
|
||||||
let value = chunk.get_constant(index).unwrap();
|
.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
|
// Unary
|
||||||
|
Loading…
Reference in New Issue
Block a user