1
0

Refactor and improve the VM, Parser, and Lexer

This commit is contained in:
Jeff 2024-09-12 05:08:55 -04:00
parent 67e5de6664
commit d4d58e793b
8 changed files with 442 additions and 143 deletions

View File

@ -179,21 +179,20 @@ impl Chunk {
let mut output = String::new(); let mut output = String::new();
let name_length = name.len(); 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_buffer = " ".repeat(buffer_length / 2);
let name_line = format!("{name_buffer}{name}{name_buffer}\n"); let underline = "-".repeat(name_length);
let name_underline = format!("{name_buffer}{}{name_buffer}\n", "-".repeat(name_length));
output.push_str(&name_line); output.push_str(&format!("{name_buffer}{name}{name_buffer}\n"));
output.push_str(&name_underline); output.push_str(&format!("{name_buffer}{underline}{name_buffer}\n",));
output.push_str("\n Code \n"); output.push_str(" Code \n");
output.push_str("------ -------- ------------\n"); output.push_str("------ ---------------- ------------------ --------\n");
output.push_str("OFFSET POSITION INSTRUCTION\n"); output.push_str("OFFSET INSTRUCTION INFO POSITION\n");
output.push_str("------ -------- ------------\n"); output.push_str("------ ---------------- ------------------ --------\n");
for (offset, (instruction, position)) in self.code.iter().enumerate() { for (offset, (instruction, position)) in self.code.iter().enumerate() {
let display = format!( let display = format!(
"{offset:04} {position} {}\n", "{offset:^6} {:35} {position}\n",
instruction.disassemble(self) instruction.disassemble(self)
); );
@ -243,13 +242,13 @@ impl Default for Chunk {
impl Display for Chunk { impl Display for Chunk {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{}", self.disassemble("Chunk Disassembly")) write!(f, "{}", self.disassemble("Chunk Display"))
} }
} }
impl Debug for Chunk { impl Debug for Chunk {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { 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, pub depth: usize,
} }
impl Local {
pub fn new(identifier: Identifier, depth: usize) -> Self {
Self { identifier, depth }
}
}
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum ChunkError { pub enum ChunkError {
CodeIndexOfBounds { CodeIndexOfBounds {

View File

@ -10,6 +10,26 @@ pub struct Instruction {
} }
impl 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 { pub fn r#move(to_register: u8, from_register: u8) -> Instruction {
Instruction { Instruction {
operation: Operation::Move, operation: Operation::Move,
@ -108,6 +128,15 @@ impl Instruction {
pub fn disassemble(&self, chunk: &Chunk) -> String { pub fn disassemble(&self, chunk: &Chunk) -> String {
match self.operation { 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 => { Operation::LoadConstant => {
let constant_index = u16::from_le_bytes(self.arguments); let constant_index = u16::from_le_bytes(self.arguments);
let constant_display = match chunk.get_constant(constant_index, Span(0, 0)) { let constant_display = match chunk.get_constant(constant_index, Span(0, 0)) {
@ -115,9 +144,91 @@ impl Instruction {
Err(error) => format!("{:?}", error), 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 { impl Display for Instruction {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self.operation { match self.operation {
Operation::Move => write!(f, "MOVE R{} R{}", self.to_register, self.arguments[0]), Operation::Move => {
Operation::Close => write!(f, "CLOSE R{}", self.to_register), 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 => { Operation::LoadConstant => {
let constant_index = u16::from_le_bytes(self.arguments); 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 => { 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!( write!(
f, f,
"DECLARE_VARIABLE V{} R{}", "{:16} R[C({})] = R({})",
variable_index, self.to_register self.operation.to_string(),
identifier_index,
self.to_register
) )
} }
Operation::GetVariable => { 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 => { 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 => { Operation::Add => {
write!( write!(
f, f,
"ADD R{} = R{} + R{}", "{:16} R({}) = R({}) + R({})",
self.to_register, self.arguments[0], self.arguments[1] self.operation.to_string(),
self.to_register,
self.arguments[0],
self.arguments[1]
) )
} }
Operation::Subtract => { Operation::Subtract => {
write!( write!(
f, f,
"SUBTRACT R{} = R{} - R{}", "{:16} R({}) = R({}) - R({})",
self.to_register, self.arguments[0], self.arguments[1] self.operation.to_string(),
self.to_register,
self.arguments[0],
self.arguments[1]
) )
} }
Operation::Multiply => { Operation::Multiply => {
write!( write!(
f, f,
"MULTIPLY R{} = R{} * R{}", "{:16} R({}) = R({}) * R({})",
self.to_register, self.arguments[0], self.arguments[1] self.operation.to_string(),
self.to_register,
self.arguments[0],
self.arguments[1]
) )
} }
Operation::Divide => { Operation::Divide => {
write!( write!(
f, f,
"DIVIDE R{} = R{} / R{}", "{:16} R({}) = R({}) / R({})",
self.to_register, self.arguments[0], self.arguments[1] self.operation.to_string(),
self.to_register,
self.arguments[0],
self.arguments[1]
) )
} }
Operation::Negate => { 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 => { 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)] #[derive(Clone, Copy, Debug, PartialEq)]
pub enum Operation { pub enum Operation {
// Stack manipulation // Stack manipulation
Move, Move = 0,
Close, Close = 1,
// Constants // Constants
LoadConstant, LoadConstant = 2,
// Variables // Variables
DeclareVariable, DeclareVariable = 3,
GetVariable, GetVariable = 4,
SetVariable, SetVariable = 5,
// Binary operations // Binary operations
Add, Add = 6,
Subtract, Subtract = 7,
Multiply, Multiply = 8,
Divide, Divide = 9,
// Unary operations // Unary operations
Negate, Negate = 10,
// Control flow // Control flow
Return, Return = 11,
}
impl From<u8> 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 { impl Display for Operation {

View File

@ -3,7 +3,6 @@
//! This module provides two lexing options: //! This module provides two lexing options:
//! - [`lex`], which lexes the entire input and returns a vector of tokens and their positions //! - [`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 //! - [`Lexer`], which lexes the input a token at a time
use std::fmt::{self, Display, Formatter};
use crate::{dust_error::AnnotatedError, Span, Token}; 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() { let (token, span) = if let Some(c) = self.peek_char() {
match c { match c {
'0'..='9' => self.lex_number()?, '0'..='9' => self.lex_numeric()?,
'-' => { '-' => {
let second_char = self.peek_second_char(); let second_char = self.peek_second_char();
@ -109,7 +108,7 @@ impl<'src> Lexer<'src> {
(Token::MinusEqual, Span(self.position - 2, self.position)) (Token::MinusEqual, Span(self.position - 2, self.position))
} else if let Some('0'..='9') = second_char { } else if let Some('0'..='9') = second_char {
self.lex_number()? self.lex_numeric()?
} else if "-Infinity" == self.peek_chars(9) { } else if "-Infinity" == self.peek_chars(9) {
self.position += 9; self.position += 9;
@ -384,7 +383,7 @@ impl<'src> Lexer<'src> {
} }
/// Lex an integer or float token. /// 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 start_pos = self.position;
let mut is_float = false; let mut is_float = false;
@ -399,26 +398,39 @@ impl<'src> Lexer<'src> {
self.next_char(); self.next_char();
} }
self.next_char();
loop {
let peek_char = self.peek_char();
if let Some('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; is_float = true;
self.next_char();
while let Some(peek_char) = self.peek_char() {
if let '0'..='9' = peek_char {
self.next_char();
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 { } else {
break; break;
} }
@ -431,7 +443,10 @@ impl<'src> Lexer<'src> {
if c.is_ascii_hexdigit() { if c.is_ascii_hexdigit() {
self.next_char(); self.next_char();
} else { } else {
break; return Err(LexError::ExpectedAsciiHexDigit {
actual: c,
position: self.position,
});
} }
} }
@ -515,11 +530,20 @@ impl<'src> Lexer<'src> {
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
pub enum LexError { pub enum LexError {
ExpectedAsciiHexDigit {
actual: char,
position: usize,
},
ExpectedCharacter { ExpectedCharacter {
expected: char, expected: char,
actual: char, actual: char,
position: usize, position: usize,
}, },
ExpectedCharacterMultiple {
expected: &'static [char],
actual: char,
position: usize,
},
UnexpectedCharacter { UnexpectedCharacter {
actual: char, actual: char,
position: usize, position: usize,
@ -536,7 +560,9 @@ impl AnnotatedError for LexError {
fn description(&self) -> &'static str { fn description(&self) -> &'static str {
match self { match self {
Self::ExpectedAsciiHexDigit { .. } => "Expected ASCII hex digit",
Self::ExpectedCharacter { .. } => "Expected character", Self::ExpectedCharacter { .. } => "Expected character",
Self::ExpectedCharacterMultiple { .. } => "Expected one of multiple characters",
Self::UnexpectedCharacter { .. } => "Unexpected character", Self::UnexpectedCharacter { .. } => "Unexpected character",
Self::UnexpectedEndOfFile { .. } => "Unexpected end of file", Self::UnexpectedEndOfFile { .. } => "Unexpected end of file",
} }
@ -544,12 +570,34 @@ impl AnnotatedError for LexError {
fn details(&self) -> Option<String> { fn details(&self) -> Option<String> {
match self { match self {
Self::ExpectedAsciiHexDigit { actual, .. } => Some(format!(
"Expected ASCII hex digit (0-9 or A-F), found \"{}\"",
actual
)),
Self::ExpectedCharacter { Self::ExpectedCharacter {
expected, actual, .. expected, actual, ..
} => Some(format!( } => Some(format!(
"Expected character \"{}\", found \"{}\"", "Expected character \"{}\", found \"{}\"",
expected, actual 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, .. } => { Self::UnexpectedCharacter { actual, .. } => {
Some(format!("Unexpected character \"{}\"", actual)) Some(format!("Unexpected character \"{}\"", actual))
} }
@ -559,29 +607,15 @@ impl AnnotatedError for LexError {
fn position(&self) -> Span { fn position(&self) -> Span {
match self { match self {
Self::ExpectedAsciiHexDigit { position, .. } => Span(*position, *position),
Self::ExpectedCharacter { position, .. } => Span(*position, *position), Self::ExpectedCharacter { position, .. } => Span(*position, *position),
Self::ExpectedCharacterMultiple { position, .. } => Span(*position, *position),
Self::UnexpectedCharacter { position, .. } => Span(*position, *position), Self::UnexpectedCharacter { position, .. } => Span(*position, *position),
Self::UnexpectedEndOfFile { 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)] #[cfg(test)]
mod tests { mod tests {
use super::*; 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] #[test]
fn add() { fn add() {
let input = "1 + 2"; let input = "1 + 2";

View File

@ -12,7 +12,7 @@ mod vm;
use std::fmt::Display; use std::fmt::Display;
pub use chunk::{Chunk, ChunkError}; pub use chunk::{Chunk, ChunkError, Local};
pub use constructor::Constructor; pub use constructor::Constructor;
pub use dust_error::{AnnotatedError, DustError}; pub use dust_error::{AnnotatedError, DustError};
pub use identifier::Identifier; pub use identifier::Identifier;

View File

@ -3,8 +3,8 @@ mod tests;
use std::{ use std::{
fmt::{self, Display, Formatter}, fmt::{self, Display, Formatter},
mem, mem::replace,
num::ParseIntError, num::{ParseFloatError, ParseIntError},
}; };
use crate::{ use crate::{
@ -14,7 +14,7 @@ use crate::{
pub fn parse(source: &str) -> Result<Chunk, DustError> { pub fn parse(source: &str) -> Result<Chunk, DustError> {
let lexer = Lexer::new(source); 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() { while !parser.is_eof() {
parser parser
@ -37,13 +37,12 @@ pub struct Parser<'src> {
} }
impl<'src> Parser<'src> { impl<'src> Parser<'src> {
pub fn new(mut lexer: Lexer<'src>) -> Self { pub fn new(mut lexer: Lexer<'src>) -> Result<Self, ParseError> {
let (current_token, current_position) = let (current_token, current_position) = lexer.next_token()?;
lexer.next_token().unwrap_or((Token::Eof, Span(0, 0)));
log::trace!("Starting parser with token {current_token} at {current_position}"); log::trace!("Starting parser with token {current_token} at {current_position}");
Parser { Ok(Parser {
lexer, lexer,
chunk: Chunk::new(), chunk: Chunk::new(),
current_register: 0, current_register: 0,
@ -51,7 +50,11 @@ impl<'src> Parser<'src> {
current_position, current_position,
previous_token: Token::Eof, previous_token: Token::Eof,
previous_position: Span(0, 0), previous_position: Span(0, 0),
})
} }
pub fn take_chunk(self) -> Chunk {
self.chunk
} }
fn is_eof(&self) -> bool { fn is_eof(&self) -> bool {
@ -81,8 +84,8 @@ impl<'src> Parser<'src> {
log::trace!("Advancing to token {new_token} at {position}"); log::trace!("Advancing to token {new_token} at {position}");
self.previous_token = mem::replace(&mut self.current_token, new_token); self.previous_token = replace(&mut self.current_token, new_token);
self.previous_position = mem::replace(&mut self.current_position, position); self.previous_position = replace(&mut self.current_position, position);
Ok(()) Ok(())
} }
@ -164,7 +167,12 @@ impl<'src> Parser<'src> {
fn parse_float(&mut self, _allow_assignment: bool) -> Result<(), ParseError> { fn parse_float(&mut self, _allow_assignment: bool) -> Result<(), ParseError> {
if let Token::Float(text) = self.previous_token { if let Token::Float(text) = self.previous_token {
let float = text.parse::<f64>().unwrap(); let float = text
.parse::<f64>()
.map_err(|error| ParseError::ParseFloatError {
error,
position: self.previous_position,
})?;
let value = Value::float(float); let value = Value::float(float);
self.emit_constant(value)?; self.emit_constant(value)?;
@ -175,7 +183,12 @@ impl<'src> Parser<'src> {
fn parse_integer(&mut self, _allow_assignment: bool) -> Result<(), ParseError> { fn parse_integer(&mut self, _allow_assignment: bool) -> Result<(), ParseError> {
if let Token::Integer(text) = self.previous_token { if let Token::Integer(text) = self.previous_token {
let integer = text.parse::<i64>().unwrap(); let integer = text
.parse::<i64>()
.map_err(|error| ParseError::ParseIntError {
error,
position: self.previous_position,
})?;
let value = Value::integer(integer); let value = Value::integer(integer);
self.emit_constant(value)?; self.emit_constant(value)?;
@ -222,8 +235,6 @@ impl<'src> Parser<'src> {
} }
fn parse_binary(&mut self) -> Result<(), ParseError> { fn parse_binary(&mut self) -> Result<(), ParseError> {
log::trace!("Parsing binary expression");
let operator_position = self.previous_position; let operator_position = self.previous_position;
let operator = self.previous_token.kind(); let operator = self.previous_token.kind();
let rule = ParseRule::from(&operator); let rule = ParseRule::from(&operator);
@ -337,6 +348,7 @@ impl<'src> Parser<'src> {
let start = self.current_position.0; let start = self.current_position.0;
let (is_expression_statement, contains_block) = match self.current_token { let (is_expression_statement, contains_block) = match self.current_token {
Token::Let => { Token::Let => {
self.advance()?;
self.parse_let_statement(true)?; self.parse_let_statement(true)?;
(false, false) (false, false)
@ -364,8 +376,6 @@ impl<'src> Parser<'src> {
} }
fn parse_let_statement(&mut self, _allow_assignment: bool) -> Result<(), ParseError> { fn parse_let_statement(&mut self, _allow_assignment: bool) -> Result<(), ParseError> {
self.expect(TokenKind::Let)?;
let position = self.current_position; let position = self.current_position;
let identifier = if let Token::Identifier(text) = self.current_token { let identifier = if let Token::Identifier(text) = self.current_token {
self.advance()?; self.advance()?;
@ -442,40 +452,36 @@ impl<'src> Parser<'src> {
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum Precedence { pub enum Precedence {
None = 0, None,
Assignment = 1, Assignment,
Conditional = 2, Conditional,
LogicalOr = 3, LogicalOr,
LogicalAnd = 4, LogicalAnd,
Equality = 5, Equality,
Comparison = 6, Comparison,
Term = 7, Term,
Factor = 8, Factor,
Unary = 9, Unary,
Call = 10, Call,
Primary = 11, Primary,
} }
impl Precedence { 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 { 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 // Wrappers around foreign errors
Chunk(ChunkError), Chunk(ChunkError),
Lex(LexError), Lex(LexError),
ParseFloatError {
error: ParseFloatError,
position: Span,
},
ParseIntError { ParseIntError {
error: ParseIntError, error: ParseIntError,
position: Span, position: Span,
@ -686,6 +696,7 @@ impl AnnotatedError for ParseError {
Self::RegisterOverflow { .. } => "Register overflow", Self::RegisterOverflow { .. } => "Register overflow",
Self::Chunk { .. } => "Chunk error", Self::Chunk { .. } => "Chunk error",
Self::Lex(_) => "Lex error", Self::Lex(_) => "Lex error",
Self::ParseFloatError { .. } => "Failed to parse float",
Self::ParseIntError { .. } => "Failed to parse integer", Self::ParseIntError { .. } => "Failed to parse integer",
} }
} }
@ -708,6 +719,7 @@ impl AnnotatedError for ParseError {
Self::RegisterOverflow { .. } => None, Self::RegisterOverflow { .. } => None,
Self::Chunk(error) => error.details(), Self::Chunk(error) => error.details(),
Self::Lex(error) => error.details(), Self::Lex(error) => error.details(),
Self::ParseFloatError { error, .. } => Some(error.to_string()),
Self::ParseIntError { 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::RegisterOverflow { position } => *position,
Self::Chunk(error) => error.position(), Self::Chunk(error) => error.position(),
Self::Lex(error) => error.position(), Self::Lex(error) => error.position(),
Self::ParseFloatError { position, .. } => *position,
Self::ParseIntError { position, .. } => *position, Self::ParseIntError { position, .. } => *position,
} }
} }

View File

@ -1,5 +1,22 @@
use crate::Local;
use super::*; 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] #[test]
fn integer() { fn integer() {
assert_eq!( assert_eq!(

View File

@ -44,8 +44,19 @@ impl Vm {
self.insert(value, register_index, position)?; self.insert(value, register_index, position)?;
} }
Operation::DeclareVariable => todo!(), Operation::DeclareVariable => {
Operation::GetVariable => todo!(), 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::SetVariable => todo!(),
Operation::Add => { Operation::Add => {
let left = self.take(instruction.arguments[0] as usize, position)?; let left = self.take(instruction.arguments[0] as usize, position)?;
@ -106,16 +117,20 @@ impl Vm {
if self.register_stack.len() == Self::STACK_LIMIT { if self.register_stack.len() == Self::STACK_LIMIT {
Err(VmError::StackOverflow { position }) Err(VmError::StackOverflow { position })
} else { } 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(()) Ok(())
} }
} }
pub fn take(&mut self, index: usize, position: Span) -> Result<Value, VmError> { fn take(&mut self, index: usize, position: Span) -> Result<Value, VmError> {
if let Some(register) = self.register_stack.get_mut(index) { if let Some(register) = self.register_stack.get_mut(index) {
let value = register let value = register
.take() .clone()
.ok_or(VmError::EmptyRegister { index, position })?; .ok_or(VmError::EmptyRegister { index, position })?;
Ok(value) Ok(value)
@ -126,7 +141,10 @@ impl Vm {
fn pop(&mut self, position: Span) -> Result<Value, VmError> { fn pop(&mut self, position: Span) -> Result<Value, VmError> {
if let Some(register) = self.register_stack.pop() { 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) Ok(value)
} else { } else {

View File

@ -25,18 +25,13 @@ fn float_exponential() {
assert_eq!(run("4.2e1"), Ok(Some(Value::float(42.0)))); 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] #[test]
fn float_exponential_negative() { fn float_exponential_negative() {
assert_eq!(run("4.2e-1"), Ok(Some(Value::float(0.42)))); assert_eq!(run("4.2e-1"), Ok(Some(Value::float(0.42))));
} }
#[test] #[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::INFINITY))));
assert_eq!(run("-Infinity"), Ok(Some(Value::float(f64::NEG_INFINITY)))); assert_eq!(run("-Infinity"), Ok(Some(Value::float(f64::NEG_INFINITY))));
assert!(run("NaN").unwrap().unwrap().as_float().unwrap().is_nan()); assert!(run("NaN").unwrap().unwrap().as_float().unwrap().is_nan());