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 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)
);
@ -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 {

View File

@ -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 {

View File

@ -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();
}
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;
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 {
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";

View File

@ -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;

View File

@ -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,
}
}

View File

@ -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!(

View File

@ -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 {

View File

@ -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());