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 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 {
|
||||||
|
@ -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 {
|
||||||
|
@ -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";
|
||||||
|
@ -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;
|
||||||
|
@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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!(
|
||||||
|
@ -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 {
|
||||||
|
@ -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());
|
||||||
|
Loading…
Reference in New Issue
Block a user