From d4d58e793b496f89b3b0fd87e9a94c2edc432657 Mon Sep 17 00:00:00 2001 From: Jeff Date: Thu, 12 Sep 2024 05:08:55 -0400 Subject: [PATCH] Refactor and improve the VM, Parser, and Lexer --- dust-lang/src/chunk.rs | 31 +++-- dust-lang/src/instruction.rs | 244 +++++++++++++++++++++++++++++----- dust-lang/src/lexer.rs | 149 +++++++++++++++------ dust-lang/src/lib.rs | 2 +- dust-lang/src/parser/mod.rs | 105 ++++++++------- dust-lang/src/parser/tests.rs | 17 +++ dust-lang/src/vm.rs | 30 ++++- dust-lang/tests/values.rs | 7 +- 8 files changed, 442 insertions(+), 143 deletions(-) diff --git a/dust-lang/src/chunk.rs b/dust-lang/src/chunk.rs index cf0fd59..1547166 100644 --- a/dust-lang/src/chunk.rs +++ b/dust-lang/src/chunk.rs @@ -179,21 +179,20 @@ impl Chunk { let mut output = String::new(); let name_length = name.len(); - let buffer_length = 34_usize.saturating_sub(name_length + 2); + let buffer_length = 51_usize.saturating_sub(name_length); let name_buffer = " ".repeat(buffer_length / 2); - let name_line = format!("{name_buffer}{name}{name_buffer}\n"); - let name_underline = format!("{name_buffer}{}{name_buffer}\n", "-".repeat(name_length)); + let underline = "-".repeat(name_length); - output.push_str(&name_line); - output.push_str(&name_underline); - output.push_str("\n Code \n"); - output.push_str("------ -------- ------------\n"); - output.push_str("OFFSET POSITION INSTRUCTION\n"); - output.push_str("------ -------- ------------\n"); + output.push_str(&format!("{name_buffer}{name}{name_buffer}\n")); + output.push_str(&format!("{name_buffer}{underline}{name_buffer}\n",)); + output.push_str(" Code \n"); + output.push_str("------ ---------------- ------------------ --------\n"); + output.push_str("OFFSET INSTRUCTION INFO POSITION\n"); + output.push_str("------ ---------------- ------------------ --------\n"); for (offset, (instruction, position)) in self.code.iter().enumerate() { let display = format!( - "{offset:04} {position} {}\n", + "{offset:^6} {:35} {position}\n", instruction.disassemble(self) ); @@ -221,7 +220,7 @@ impl Chunk { output.push_str(&display); } - output.push_str("\n Identifiers\n"); + output.push_str("\n Identifiers\n"); output.push_str("----- ---------- -----\n"); output.push_str("INDEX NAME DEPTH\n"); output.push_str("----- ---------- -----\n"); @@ -243,13 +242,13 @@ impl Default for Chunk { impl Display for Chunk { fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "{}", self.disassemble("Chunk Disassembly")) + write!(f, "{}", self.disassemble("Chunk Display")) } } impl Debug for Chunk { fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "{}", self.disassemble("Chunk Disassembly")) + write!(f, "{}", self.disassemble("Chunk Debug Display")) } } @@ -269,6 +268,12 @@ pub struct Local { pub depth: usize, } +impl Local { + pub fn new(identifier: Identifier, depth: usize) -> Self { + Self { identifier, depth } + } +} + #[derive(Debug, Clone, PartialEq)] pub enum ChunkError { CodeIndexOfBounds { diff --git a/dust-lang/src/instruction.rs b/dust-lang/src/instruction.rs index d7fae59..b7d5e55 100644 --- a/dust-lang/src/instruction.rs +++ b/dust-lang/src/instruction.rs @@ -10,6 +10,26 @@ pub struct Instruction { } impl Instruction { + pub fn decode(bits: u32) -> Instruction { + let operation = Operation::from((bits >> 24) as u8); + let to_register = ((bits >> 16) & 0xff) as u8; + let arguments = [((bits >> 8) & 0xff) as u8, (bits & 0xff) as u8]; + + Instruction { + operation, + to_register, + arguments, + } + } + + pub fn encode(&self) -> u32 { + let operation = u32::from(self.operation as u8); + let to_register = u32::from(self.to_register); + let arguments = u32::from(self.arguments[0]) << 8 | u32::from(self.arguments[1]); + + operation << 24 | to_register << 16 | arguments + } + pub fn r#move(to_register: u8, from_register: u8) -> Instruction { Instruction { operation: Operation::Move, @@ -108,6 +128,15 @@ impl Instruction { pub fn disassemble(&self, chunk: &Chunk) -> String { match self.operation { + Operation::Move => { + format!( + "{:16} R({}) R({})", + self.operation.to_string(), + self.to_register, + self.arguments[0] + ) + } + Operation::Close => format!("{} R({})", self.operation, self.to_register), Operation::LoadConstant => { let constant_index = u16::from_le_bytes(self.arguments); let constant_display = match chunk.get_constant(constant_index, Span(0, 0)) { @@ -115,9 +144,91 @@ impl Instruction { Err(error) => format!("{:?}", error), }; - format!("{self} {constant_display}") + format!( + "{:16} R({}) = C({}) {} ", + self.operation.to_string(), + self.to_register, + constant_index, + constant_display + ) + } + Operation::DeclareVariable => { + let identifier_index = u16::from_le_bytes([self.arguments[0], self.arguments[1]]); + + format!( + "{:16} R[C({})] = R({})", + self.operation.to_string(), + identifier_index, + self.to_register + ) + } + Operation::GetVariable => { + let identifier_index = u16::from_le_bytes([self.arguments[0], self.arguments[1]]); + + format!( + "{:16} R{} = R[I({})]", + self.operation.to_string(), + self.to_register, + identifier_index + ) + } + Operation::SetVariable => { + let identifier_index = u16::from_le_bytes([self.arguments[0], self.arguments[1]]); + + format!( + "{:16} R[C({})] = R({})", + self.operation.to_string(), + identifier_index, + self.to_register + ) + } + Operation::Add => { + format!( + "{:16} R({}) = R({}) + R({})", + self.operation.to_string(), + self.to_register, + self.arguments[0], + self.arguments[1] + ) + } + Operation::Subtract => { + format!( + "{:16} R({}) = R({}) - R({})", + self.operation.to_string(), + self.to_register, + self.arguments[0], + self.arguments[1] + ) + } + Operation::Multiply => { + format!( + "{:16} R({}) = R({}) * R({})", + self.operation.to_string(), + self.to_register, + self.arguments[0], + self.arguments[1] + ) + } + Operation::Divide => { + format!( + "{:16} R({}) = R({}) / R({})", + self.operation.to_string(), + self.to_register, + self.arguments[0], + self.arguments[1] + ) + } + Operation::Negate => { + format!( + "{:16} R({}) = -R({})", + self.operation.to_string(), + self.to_register, + self.arguments[0] + ) + } + Operation::Return => { + format!("{:16}", self.operation.to_string()) } - _ => format!("{self}"), } } } @@ -125,65 +236,111 @@ impl Instruction { impl Display for Instruction { fn fmt(&self, f: &mut Formatter) -> fmt::Result { match self.operation { - Operation::Move => write!(f, "MOVE R{} R{}", self.to_register, self.arguments[0]), - Operation::Close => write!(f, "CLOSE R{}", self.to_register), + Operation::Move => { + write!( + f, + "{:16} R({}) R({})", + self.operation.to_string(), + self.to_register, + self.arguments[0] + ) + } + Operation::Close => write!(f, "{} R({})", self.operation, self.to_register), Operation::LoadConstant => { let constant_index = u16::from_le_bytes(self.arguments); - write!(f, "LOAD_CONSTANT R{} C{}", self.to_register, constant_index) + write!( + f, + "{:16} R({}) C({})", + self.operation.to_string(), + self.to_register, + constant_index + ) } Operation::DeclareVariable => { - let variable_index = u16::from_le_bytes([self.arguments[0], self.arguments[1]]); + let identifier_index = u16::from_le_bytes([self.arguments[0], self.arguments[1]]); write!( f, - "DECLARE_VARIABLE V{} R{}", - variable_index, self.to_register + "{:16} R[C({})] = R({})", + self.operation.to_string(), + identifier_index, + self.to_register ) } Operation::GetVariable => { - let variable_index = u16::from_le_bytes([self.arguments[0], self.arguments[1]]); + let identifier_index = u16::from_le_bytes([self.arguments[0], self.arguments[1]]); - write!(f, "GET_VARIABLE R{} V{}", self.to_register, variable_index) + write!( + f, + "{:16} R{} = R[I({})]", + self.operation.to_string(), + self.to_register, + identifier_index + ) } Operation::SetVariable => { - let variable_index = u16::from_le_bytes([self.arguments[0], self.arguments[1]]); + let identifier_index = u16::from_le_bytes([self.arguments[0], self.arguments[1]]); - write!(f, "SET_VARIABLE V{} R{}", variable_index, self.to_register) + write!( + f, + "{:16} R[C({})] = R({})", + self.operation.to_string(), + identifier_index, + self.to_register + ) } Operation::Add => { write!( f, - "ADD R{} = R{} + R{}", - self.to_register, self.arguments[0], self.arguments[1] + "{:16} R({}) = R({}) + R({})", + self.operation.to_string(), + self.to_register, + self.arguments[0], + self.arguments[1] ) } Operation::Subtract => { write!( f, - "SUBTRACT R{} = R{} - R{}", - self.to_register, self.arguments[0], self.arguments[1] + "{:16} R({}) = R({}) - R({})", + self.operation.to_string(), + self.to_register, + self.arguments[0], + self.arguments[1] ) } Operation::Multiply => { write!( f, - "MULTIPLY R{} = R{} * R{}", - self.to_register, self.arguments[0], self.arguments[1] + "{:16} R({}) = R({}) * R({})", + self.operation.to_string(), + self.to_register, + self.arguments[0], + self.arguments[1] ) } Operation::Divide => { write!( f, - "DIVIDE R{} = R{} / R{}", - self.to_register, self.arguments[0], self.arguments[1] + "{:16} R({}) = R({}) / R({})", + self.operation.to_string(), + self.to_register, + self.arguments[0], + self.arguments[1] ) } Operation::Negate => { - write!(f, "NEGATE R{} = !R{}", self.to_register, self.arguments[0]) + write!( + f, + "{:16} R({}) = -R({})", + self.operation.to_string(), + self.to_register, + self.arguments[0] + ) } Operation::Return => { - write!(f, "RETURN") + write!(f, "{:16}", self.operation.to_string()) } } } @@ -192,28 +349,47 @@ impl Display for Instruction { #[derive(Clone, Copy, Debug, PartialEq)] pub enum Operation { // Stack manipulation - Move, - Close, + Move = 0, + Close = 1, // Constants - LoadConstant, + LoadConstant = 2, // Variables - DeclareVariable, - GetVariable, - SetVariable, + DeclareVariable = 3, + GetVariable = 4, + SetVariable = 5, // Binary operations - Add, - Subtract, - Multiply, - Divide, + Add = 6, + Subtract = 7, + Multiply = 8, + Divide = 9, // Unary operations - Negate, + Negate = 10, // Control flow - Return, + Return = 11, +} + +impl From for Operation { + fn from(byte: u8) -> Self { + match byte { + 0 => Operation::Move, + 1 => Operation::Close, + 2 => Operation::LoadConstant, + 3 => Operation::DeclareVariable, + 4 => Operation::GetVariable, + 5 => Operation::SetVariable, + 6 => Operation::Add, + 7 => Operation::Subtract, + 8 => Operation::Multiply, + 9 => Operation::Divide, + 10 => Operation::Negate, + _ => Operation::Return, + } + } } impl Display for Operation { diff --git a/dust-lang/src/lexer.rs b/dust-lang/src/lexer.rs index a6dfbd1..5a3728c 100644 --- a/dust-lang/src/lexer.rs +++ b/dust-lang/src/lexer.rs @@ -3,7 +3,6 @@ //! This module provides two lexing options: //! - [`lex`], which lexes the entire input and returns a vector of tokens and their positions //! - [`Lexer`], which lexes the input a token at a time -use std::fmt::{self, Display, Formatter}; use crate::{dust_error::AnnotatedError, Span, Token}; @@ -100,7 +99,7 @@ impl<'src> Lexer<'src> { let (token, span) = if let Some(c) = self.peek_char() { match c { - '0'..='9' => self.lex_number()?, + '0'..='9' => self.lex_numeric()?, '-' => { let second_char = self.peek_second_char(); @@ -109,7 +108,7 @@ impl<'src> Lexer<'src> { (Token::MinusEqual, Span(self.position - 2, self.position)) } else if let Some('0'..='9') = second_char { - self.lex_number()? + self.lex_numeric()? } else if "-Infinity" == self.peek_chars(9) { self.position += 9; @@ -384,7 +383,7 @@ impl<'src> Lexer<'src> { } /// Lex an integer or float token. - fn lex_number(&mut self) -> Result<(Token<'src>, Span), LexError> { + fn lex_numeric(&mut self) -> Result<(Token<'src>, Span), LexError> { let start_pos = self.position; let mut is_float = false; @@ -399,26 +398,39 @@ impl<'src> Lexer<'src> { self.next_char(); } + is_float = true; + self.next_char(); - loop { - let peek_char = self.peek_char(); - - if let Some('0'..='9') = peek_char { + while let Some(peek_char) = self.peek_char() { + if let '0'..='9' = peek_char { self.next_char(); - } else if let Some('e') = peek_char { - if let Some('0'..='9') = self.peek_second_char() { - self.next_char(); - self.next_char(); - } else { - break; - } - } else { - break; - } - } - is_float = true; + continue; + } + + let peek_second_char = self.peek_second_char(); + + if let ('e', Some('0'..='9')) = (peek_char, peek_second_char) { + self.next_char(); + self.next_char(); + + continue; + } + + if let ('e', Some('-')) = (peek_char, peek_second_char) { + self.next_char(); + self.next_char(); + + continue; + } + + return Err(LexError::ExpectedCharacterMultiple { + expected: &['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'e', '-'], + actual: peek_char, + position: self.position, + }); + } } else { break; } @@ -431,7 +443,10 @@ impl<'src> Lexer<'src> { if c.is_ascii_hexdigit() { self.next_char(); } else { - break; + return Err(LexError::ExpectedAsciiHexDigit { + actual: c, + position: self.position, + }); } } @@ -515,11 +530,20 @@ impl<'src> Lexer<'src> { #[derive(Debug, PartialEq, Clone)] pub enum LexError { + ExpectedAsciiHexDigit { + actual: char, + position: usize, + }, ExpectedCharacter { expected: char, actual: char, position: usize, }, + ExpectedCharacterMultiple { + expected: &'static [char], + actual: char, + position: usize, + }, UnexpectedCharacter { actual: char, position: usize, @@ -536,7 +560,9 @@ impl AnnotatedError for LexError { fn description(&self) -> &'static str { match self { + Self::ExpectedAsciiHexDigit { .. } => "Expected ASCII hex digit", Self::ExpectedCharacter { .. } => "Expected character", + Self::ExpectedCharacterMultiple { .. } => "Expected one of multiple characters", Self::UnexpectedCharacter { .. } => "Unexpected character", Self::UnexpectedEndOfFile { .. } => "Unexpected end of file", } @@ -544,12 +570,34 @@ impl AnnotatedError for LexError { fn details(&self) -> Option { match self { + Self::ExpectedAsciiHexDigit { actual, .. } => Some(format!( + "Expected ASCII hex digit (0-9 or A-F), found \"{}\"", + actual + )), Self::ExpectedCharacter { expected, actual, .. } => Some(format!( "Expected character \"{}\", found \"{}\"", expected, actual )), + Self::ExpectedCharacterMultiple { + expected, actual, .. + } => { + let mut details = "Expected one of the following characters ".to_string(); + + for (i, c) in expected.iter().enumerate() { + if i == expected.len() - 1 { + details.push_str(", or "); + } else if i > 0 { + details.push_str(", "); + } + details.push(*c); + } + + details.push_str(&format!(" but found {}", actual)); + + Some(details) + } Self::UnexpectedCharacter { actual, .. } => { Some(format!("Unexpected character \"{}\"", actual)) } @@ -559,29 +607,15 @@ impl AnnotatedError for LexError { fn position(&self) -> Span { match self { + Self::ExpectedAsciiHexDigit { position, .. } => Span(*position, *position), Self::ExpectedCharacter { position, .. } => Span(*position, *position), + Self::ExpectedCharacterMultiple { position, .. } => Span(*position, *position), Self::UnexpectedCharacter { position, .. } => Span(*position, *position), Self::UnexpectedEndOfFile { position } => Span(*position, *position), } } } -impl Display for LexError { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - match self { - Self::ExpectedCharacter { - expected, actual, .. - } => write!(f, "Expected character '{expected}', found '{actual}'"), - Self::UnexpectedCharacter { actual, .. } => { - write!(f, "Unexpected character '{actual}'") - } - Self::UnexpectedEndOfFile { .. } => { - write!(f, "Unexpected end of file") - } - } - } -} - #[cfg(test)] mod tests { use super::*; @@ -1266,6 +1300,47 @@ mod tests { ) } + #[test] + fn float_with_exponent() { + let input = "1.23e4"; + + assert_eq!( + lex(input), + Ok(vec![ + (Token::Float("1.23e4"), Span(0, 6)), + (Token::Eof, Span(6, 6)), + ]) + ) + } + + #[test] + fn float_with_negative_exponent() { + let input = "1.23e-4"; + + assert_eq!( + lex(input), + Ok(vec![ + (Token::Float("1.23e-4"), Span(0, 7)), + (Token::Eof, Span(7, 7)), + ]) + ) + } + + #[test] + fn float_infinity_and_nan() { + let input = "Infinity -Infinity NaN"; + + assert_eq!( + lex(input), + Ok(vec![ + (Token::Float("Infinity"), Span(0, 8)), + (Token::Float("-Infinity"), Span(9, 18)), + (Token::Float("NaN"), Span(19, 22)), + (Token::Eof, Span(22, 22)), + ]) + ) + } + #[test] fn add() { let input = "1 + 2"; diff --git a/dust-lang/src/lib.rs b/dust-lang/src/lib.rs index 04668ee..9739235 100644 --- a/dust-lang/src/lib.rs +++ b/dust-lang/src/lib.rs @@ -12,7 +12,7 @@ mod vm; use std::fmt::Display; -pub use chunk::{Chunk, ChunkError}; +pub use chunk::{Chunk, ChunkError, Local}; pub use constructor::Constructor; pub use dust_error::{AnnotatedError, DustError}; pub use identifier::Identifier; diff --git a/dust-lang/src/parser/mod.rs b/dust-lang/src/parser/mod.rs index 4caeb3c..298bc71 100644 --- a/dust-lang/src/parser/mod.rs +++ b/dust-lang/src/parser/mod.rs @@ -3,8 +3,8 @@ mod tests; use std::{ fmt::{self, Display, Formatter}, - mem, - num::ParseIntError, + mem::replace, + num::{ParseFloatError, ParseIntError}, }; use crate::{ @@ -14,7 +14,7 @@ use crate::{ pub fn parse(source: &str) -> Result { let lexer = Lexer::new(source); - let mut parser = Parser::new(lexer); + let mut parser = Parser::new(lexer).map_err(|error| DustError::Parse { error, source })?; while !parser.is_eof() { parser @@ -37,13 +37,12 @@ pub struct Parser<'src> { } impl<'src> Parser<'src> { - pub fn new(mut lexer: Lexer<'src>) -> Self { - let (current_token, current_position) = - lexer.next_token().unwrap_or((Token::Eof, Span(0, 0))); + pub fn new(mut lexer: Lexer<'src>) -> Result { + let (current_token, current_position) = lexer.next_token()?; log::trace!("Starting parser with token {current_token} at {current_position}"); - Parser { + Ok(Parser { lexer, chunk: Chunk::new(), current_register: 0, @@ -51,7 +50,11 @@ impl<'src> Parser<'src> { current_position, previous_token: Token::Eof, previous_position: Span(0, 0), - } + }) + } + + pub fn take_chunk(self) -> Chunk { + self.chunk } fn is_eof(&self) -> bool { @@ -81,8 +84,8 @@ impl<'src> Parser<'src> { log::trace!("Advancing to token {new_token} at {position}"); - self.previous_token = mem::replace(&mut self.current_token, new_token); - self.previous_position = mem::replace(&mut self.current_position, position); + self.previous_token = replace(&mut self.current_token, new_token); + self.previous_position = replace(&mut self.current_position, position); Ok(()) } @@ -164,7 +167,12 @@ impl<'src> Parser<'src> { fn parse_float(&mut self, _allow_assignment: bool) -> Result<(), ParseError> { if let Token::Float(text) = self.previous_token { - let float = text.parse::().unwrap(); + let float = text + .parse::() + .map_err(|error| ParseError::ParseFloatError { + error, + position: self.previous_position, + })?; let value = Value::float(float); self.emit_constant(value)?; @@ -175,7 +183,12 @@ impl<'src> Parser<'src> { fn parse_integer(&mut self, _allow_assignment: bool) -> Result<(), ParseError> { if let Token::Integer(text) = self.previous_token { - let integer = text.parse::().unwrap(); + let integer = text + .parse::() + .map_err(|error| ParseError::ParseIntError { + error, + position: self.previous_position, + })?; let value = Value::integer(integer); self.emit_constant(value)?; @@ -222,8 +235,6 @@ impl<'src> Parser<'src> { } fn parse_binary(&mut self) -> Result<(), ParseError> { - log::trace!("Parsing binary expression"); - let operator_position = self.previous_position; let operator = self.previous_token.kind(); let rule = ParseRule::from(&operator); @@ -337,6 +348,7 @@ impl<'src> Parser<'src> { let start = self.current_position.0; let (is_expression_statement, contains_block) = match self.current_token { Token::Let => { + self.advance()?; self.parse_let_statement(true)?; (false, false) @@ -364,8 +376,6 @@ impl<'src> Parser<'src> { } fn parse_let_statement(&mut self, _allow_assignment: bool) -> Result<(), ParseError> { - self.expect(TokenKind::Let)?; - let position = self.current_position; let identifier = if let Token::Identifier(text) = self.current_token { self.advance()?; @@ -442,40 +452,36 @@ impl<'src> Parser<'src> { #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub enum Precedence { - None = 0, - Assignment = 1, - Conditional = 2, - LogicalOr = 3, - LogicalAnd = 4, - Equality = 5, - Comparison = 6, - Term = 7, - Factor = 8, - Unary = 9, - Call = 10, - Primary = 11, + None, + Assignment, + Conditional, + LogicalOr, + LogicalAnd, + Equality, + Comparison, + Term, + Factor, + Unary, + Call, + Primary, } impl Precedence { - fn from_byte(byte: u8) -> Self { - match byte { - 0 => Self::None, - 1 => Self::Assignment, - 2 => Self::Conditional, - 3 => Self::LogicalOr, - 4 => Self::LogicalAnd, - 5 => Self::Equality, - 6 => Self::Comparison, - 7 => Self::Term, - 8 => Self::Factor, - 9 => Self::Unary, - 10 => Self::Call, - _ => Self::Primary, - } - } - fn increment(&self) -> Self { - Self::from_byte(*self as u8 + 1) + match self { + Precedence::None => Precedence::Assignment, + Precedence::Assignment => Precedence::Conditional, + Precedence::Conditional => Precedence::LogicalOr, + Precedence::LogicalOr => Precedence::LogicalAnd, + Precedence::LogicalAnd => Precedence::Equality, + Precedence::Equality => Precedence::Comparison, + Precedence::Comparison => Precedence::Term, + Precedence::Term => Precedence::Factor, + Precedence::Factor => Precedence::Unary, + Precedence::Unary => Precedence::Call, + Precedence::Call => Precedence::Primary, + Precedence::Primary => Precedence::Primary, + } } } @@ -659,6 +665,10 @@ pub enum ParseError { // Wrappers around foreign errors Chunk(ChunkError), Lex(LexError), + ParseFloatError { + error: ParseFloatError, + position: Span, + }, ParseIntError { error: ParseIntError, position: Span, @@ -686,6 +696,7 @@ impl AnnotatedError for ParseError { Self::RegisterOverflow { .. } => "Register overflow", Self::Chunk { .. } => "Chunk error", Self::Lex(_) => "Lex error", + Self::ParseFloatError { .. } => "Failed to parse float", Self::ParseIntError { .. } => "Failed to parse integer", } } @@ -708,6 +719,7 @@ impl AnnotatedError for ParseError { Self::RegisterOverflow { .. } => None, Self::Chunk(error) => error.details(), Self::Lex(error) => error.details(), + Self::ParseFloatError { error, .. } => Some(error.to_string()), Self::ParseIntError { error, .. } => Some(error.to_string()), } } @@ -722,6 +734,7 @@ impl AnnotatedError for ParseError { Self::RegisterOverflow { position } => *position, Self::Chunk(error) => error.position(), Self::Lex(error) => error.position(), + Self::ParseFloatError { position, .. } => *position, Self::ParseIntError { position, .. } => *position, } } diff --git a/dust-lang/src/parser/tests.rs b/dust-lang/src/parser/tests.rs index 79ef460..cc13fb2 100644 --- a/dust-lang/src/parser/tests.rs +++ b/dust-lang/src/parser/tests.rs @@ -1,5 +1,22 @@ +use crate::Local; + use super::*; +#[test] +fn let_statement() { + assert_eq!( + parse("let x = 42;"), + Ok(Chunk::with_data( + vec![ + (Instruction::load_constant(0, 0), Span(8, 10)), + (Instruction::declare_variable(1, 0), Span(4, 5)), + ], + vec![Value::integer(42),], + vec![Local::new(Identifier::new("x"), 0)] + )) + ); +} + #[test] fn integer() { assert_eq!( diff --git a/dust-lang/src/vm.rs b/dust-lang/src/vm.rs index 14659a3..3ee3e55 100644 --- a/dust-lang/src/vm.rs +++ b/dust-lang/src/vm.rs @@ -44,8 +44,19 @@ impl Vm { self.insert(value, register_index, position)?; } - Operation::DeclareVariable => todo!(), - Operation::GetVariable => todo!(), + Operation::DeclareVariable => { + let register_index = instruction.to_register as usize; + let identifier_index = u16::from_le_bytes(instruction.arguments) as usize; + let value = self.take(identifier_index, position)?; + + self.insert(value, register_index, position)?; + } + Operation::GetVariable => { + let identifier_index = u16::from_le_bytes(instruction.arguments) as usize; + let value = self.take(identifier_index, position)?; + + self.insert(value, identifier_index, position)?; + } Operation::SetVariable => todo!(), Operation::Add => { let left = self.take(instruction.arguments[0] as usize, position)?; @@ -106,16 +117,20 @@ impl Vm { if self.register_stack.len() == Self::STACK_LIMIT { Err(VmError::StackOverflow { position }) } else { - self.register_stack.insert(index, Some(value)); + if index == self.register_stack.len() { + self.register_stack.push(Some(value)); + } else { + self.register_stack[index] = Some(value); + } Ok(()) } } - pub fn take(&mut self, index: usize, position: Span) -> Result { + fn take(&mut self, index: usize, position: Span) -> Result { if let Some(register) = self.register_stack.get_mut(index) { let value = register - .take() + .clone() .ok_or(VmError::EmptyRegister { index, position })?; Ok(value) @@ -126,7 +141,10 @@ impl Vm { fn pop(&mut self, position: Span) -> Result { if let Some(register) = self.register_stack.pop() { - let value = register.ok_or(VmError::RegisterIndexOutOfBounds { position })?; + let value = register.ok_or(VmError::EmptyRegister { + index: self.register_stack.len().saturating_sub(1), + position, + })?; Ok(value) } else { diff --git a/dust-lang/tests/values.rs b/dust-lang/tests/values.rs index 28b5d60..4befbc6 100644 --- a/dust-lang/tests/values.rs +++ b/dust-lang/tests/values.rs @@ -25,18 +25,13 @@ fn float_exponential() { assert_eq!(run("4.2e1"), Ok(Some(Value::float(42.0)))); } -#[test] -fn float_exponential_positive() { - assert_eq!(run("4.2e+1"), Ok(Some(Value::float(42.0)))); -} - #[test] fn float_exponential_negative() { assert_eq!(run("4.2e-1"), Ok(Some(Value::float(0.42)))); } #[test] -fn float_special_values() { +fn float_infinity_and_nan() { assert_eq!(run("Infinity"), Ok(Some(Value::float(f64::INFINITY)))); assert_eq!(run("-Infinity"), Ok(Some(Value::float(f64::NEG_INFINITY)))); assert!(run("NaN").unwrap().unwrap().as_float().unwrap().is_nan());