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};
|
||||
|
||||
#[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),
|
||||
|
@ -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)?;
|
||||
|
||||
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;";
|
||||
|
@ -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
|
||||
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
|
||||
|
Loading…
Reference in New Issue
Block a user