Refactor and improve the VM, Parser, and Lexer
This commit is contained in:
parent
67e5de6664
commit
d4d58e793b
@ -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 {
|
||||
|
@ -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<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 {
|
||||
|
@ -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<String> {
|
||||
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";
|
||||
|
@ -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;
|
||||
|
@ -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<Chunk, DustError> {
|
||||
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<Self, ParseError> {
|
||||
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::<f64>().unwrap();
|
||||
let float = text
|
||||
.parse::<f64>()
|
||||
.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::<i64>().unwrap();
|
||||
let integer = text
|
||||
.parse::<i64>()
|
||||
.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,
|
||||
}
|
||||
}
|
||||
|
@ -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!(
|
||||
|
@ -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<Value, VmError> {
|
||||
fn take(&mut self, index: usize, position: Span) -> Result<Value, VmError> {
|
||||
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<Value, VmError> {
|
||||
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 {
|
||||
|
@ -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());
|
||||
|
Loading…
Reference in New Issue
Block a user