diff --git a/dust-lang/src/chunk.rs b/dust-lang/src/chunk.rs index aad21d9..6da6edc 100644 --- a/dust-lang/src/chunk.rs +++ b/dust-lang/src/chunk.rs @@ -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, @@ -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), diff --git a/dust-lang/src/parser.rs b/dust-lang/src/parser.rs index 8fb9234..dd159ca 100644 --- a/dust-lang/src/parser.rs +++ b/dust-lang/src/parser.rs @@ -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 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;"; diff --git a/dust-lang/src/vm.rs b/dust-lang/src/vm.rs index e37a0fa..d1907a8 100644 --- a/dust-lang/src/vm.rs +++ b/dust-lang/src/vm.rs @@ -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