Extend parsing
This commit is contained in:
parent
573e5ae470
commit
57edf48e36
@ -58,6 +58,10 @@ impl Chunk {
|
||||
.ok_or(ChunkError::CodeIndexOfBounds { offset, position })
|
||||
}
|
||||
|
||||
pub fn remove_instruction(&mut self, index: usize) -> (Instruction, Span) {
|
||||
self.instructions.remove(index)
|
||||
}
|
||||
|
||||
pub fn push_instruction(&mut self, instruction: Instruction, position: Span) {
|
||||
self.instructions.push((instruction, position));
|
||||
}
|
||||
@ -72,17 +76,32 @@ impl Chunk {
|
||||
.ok_or(ChunkError::InstructionUnderflow { position })
|
||||
}
|
||||
|
||||
pub fn get_previous(&self, position: Span) -> Result<&(Instruction, Span), ChunkError> {
|
||||
self.instructions
|
||||
.last()
|
||||
.ok_or(ChunkError::InstructionUnderflow { position })
|
||||
pub fn get_previous(&self) -> Option<&(Instruction, Span)> {
|
||||
self.instructions.last()
|
||||
}
|
||||
|
||||
pub fn get_last_operation(&self, position: Span) -> Result<Operation, ChunkError> {
|
||||
self.get_previous(position)
|
||||
pub fn get_last_operation(&self) -> Option<Operation> {
|
||||
self.get_previous()
|
||||
.map(|(instruction, _)| instruction.operation())
|
||||
}
|
||||
|
||||
pub fn get_last_n_operations<const N: usize>(&self) -> [Option<Operation>; N] {
|
||||
let mut operations = [None; N];
|
||||
|
||||
for i in 0..N {
|
||||
let index = self.instructions.len().saturating_sub(i + 1);
|
||||
|
||||
let operation = self
|
||||
.instructions
|
||||
.get(index)
|
||||
.map(|(instruction, _)| instruction.operation());
|
||||
|
||||
operations[i] = operation;
|
||||
}
|
||||
|
||||
operations
|
||||
}
|
||||
|
||||
pub fn get_constant(&self, index: u8, position: Span) -> Result<&Value, ChunkError> {
|
||||
let index = index as usize;
|
||||
|
||||
|
@ -160,6 +160,26 @@ impl Instruction {
|
||||
instruction
|
||||
}
|
||||
|
||||
pub fn less(comparison_boolean: bool, left_index: u8, right_index: u8) -> Instruction {
|
||||
let mut instruction = Instruction(Operation::Less as u32);
|
||||
|
||||
instruction.set_destination(if comparison_boolean { 1 } else { 0 });
|
||||
instruction.set_first_argument(left_index);
|
||||
instruction.set_second_argument(right_index);
|
||||
|
||||
instruction
|
||||
}
|
||||
|
||||
pub fn less_equal(comparison_boolean: bool, left_index: u8, right_index: u8) -> Instruction {
|
||||
let mut instruction = Instruction(Operation::LessEqual as u32);
|
||||
|
||||
instruction.set_destination(if comparison_boolean { 1 } else { 0 });
|
||||
instruction.set_first_argument(left_index);
|
||||
instruction.set_second_argument(right_index);
|
||||
|
||||
instruction
|
||||
}
|
||||
|
||||
pub fn negate(to_register: u8, from_index: u8) -> Instruction {
|
||||
let mut instruction = Instruction(Operation::Negate as u32);
|
||||
|
||||
|
@ -309,7 +309,13 @@ impl<'src> Lexer<'src> {
|
||||
'!' => {
|
||||
self.position += 1;
|
||||
|
||||
(Token::Bang, Span(self.position - 1, self.position))
|
||||
if let Some('=') = self.peek_char() {
|
||||
self.position += 1;
|
||||
|
||||
(Token::BangEqual, Span(self.position - 2, self.position))
|
||||
} else {
|
||||
(Token::Bang, Span(self.position - 1, self.position))
|
||||
}
|
||||
}
|
||||
':' => {
|
||||
self.position += 1;
|
||||
@ -969,6 +975,21 @@ mod tests {
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn not_equal() {
|
||||
let input = "42 != 42";
|
||||
|
||||
assert_eq!(
|
||||
lex(input),
|
||||
Ok(vec![
|
||||
(Token::Integer("42"), Span(0, 2)),
|
||||
(Token::BangEqual, Span(3, 5)),
|
||||
(Token::Integer("42"), Span(6, 8)),
|
||||
(Token::Eof, Span(8, 8)),
|
||||
])
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn equal() {
|
||||
let input = "42 == 42";
|
||||
|
@ -8,8 +8,8 @@ use std::{
|
||||
};
|
||||
|
||||
use crate::{
|
||||
dust_error::AnnotatedError, Chunk, ChunkError, DustError, Identifier, Instruction, LexError,
|
||||
Lexer, Operation, Span, Token, TokenKind, TokenOwned, Value,
|
||||
AnnotatedError, Chunk, ChunkError, DustError, Identifier, Instruction, LexError, Lexer,
|
||||
Operation, Span, Token, TokenKind, TokenOwned, Value,
|
||||
};
|
||||
|
||||
pub fn parse(source: &str) -> Result<Chunk, DustError> {
|
||||
@ -150,26 +150,52 @@ impl<'src> Parser<'src> {
|
||||
) -> Result<(), ParseError> {
|
||||
if let Token::Boolean(text) = self.current_token {
|
||||
let position = self.current_position;
|
||||
let boolean = text.parse::<bool>().unwrap();
|
||||
|
||||
self.advance()?;
|
||||
|
||||
let boolean = text.parse::<bool>().unwrap();
|
||||
let previous_operations = self.chunk.get_last_n_operations::<2>();
|
||||
|
||||
if let Ok((last_instruction, _)) =
|
||||
self.chunk.get_previous(self.current_position).copied()
|
||||
if let [Some(Operation::LoadBoolean), Some(Operation::LoadBoolean)] =
|
||||
previous_operations
|
||||
{
|
||||
let skip = last_instruction.operation() == Operation::Jump;
|
||||
let (second_boolean, second_position) =
|
||||
self.chunk.pop_instruction(self.current_position)?;
|
||||
let (first_boolean, first_position) =
|
||||
self.chunk.pop_instruction(self.current_position)?;
|
||||
|
||||
self.emit_instruction(
|
||||
Instruction::load_boolean(self.current_register, boolean, skip),
|
||||
position,
|
||||
);
|
||||
} else {
|
||||
self.emit_instruction(
|
||||
Instruction::load_boolean(self.current_register, boolean, false),
|
||||
position,
|
||||
);
|
||||
if first_boolean.first_argument_as_boolean() == boolean {
|
||||
let skip = first_boolean.second_argument_as_boolean();
|
||||
|
||||
self.emit_instruction(
|
||||
Instruction::load_boolean(self.current_register, boolean, skip),
|
||||
position,
|
||||
);
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if second_boolean.first_argument_as_boolean() == boolean {
|
||||
let skip = second_boolean.second_argument_as_boolean();
|
||||
|
||||
self.emit_instruction(
|
||||
Instruction::load_boolean(self.current_register, boolean, skip),
|
||||
position,
|
||||
);
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
self.emit_instruction(first_boolean, first_position);
|
||||
self.emit_instruction(second_boolean, second_position);
|
||||
}
|
||||
|
||||
let skip = previous_operations[0] == Some(Operation::Jump);
|
||||
|
||||
self.emit_instruction(
|
||||
Instruction::load_boolean(self.current_register, boolean, skip),
|
||||
position,
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -390,11 +416,21 @@ impl<'src> Parser<'src> {
|
||||
TokenKind::Slash => (Instruction::divide(self.current_register, left, 0), false),
|
||||
TokenKind::Percent => (Instruction::modulo(self.current_register, left, 0), false),
|
||||
TokenKind::DoubleEqual => (Instruction::equal(true, left, 0), true),
|
||||
TokenKind::BangEqual => (Instruction::equal(false, left, 0), true),
|
||||
TokenKind::Less => (Instruction::less(true, left, 0), true),
|
||||
TokenKind::LessEqual => (Instruction::less_equal(true, left, 0), true),
|
||||
TokenKind::Greater => (Instruction::less_equal(false, left, 0), true),
|
||||
TokenKind::GreaterEqual => (Instruction::less(false, left, 0), true),
|
||||
TokenKind::DoubleAmpersand => {
|
||||
let and_test = Instruction::test(self.current_register, false);
|
||||
|
||||
(and_test, true)
|
||||
}
|
||||
TokenKind::DoublePipe => {
|
||||
let or_test = Instruction::test(self.current_register, true);
|
||||
|
||||
(or_test, true)
|
||||
}
|
||||
_ => {
|
||||
return Err(ParseError::ExpectedTokenMultiple {
|
||||
expected: &[
|
||||
@ -403,9 +439,14 @@ impl<'src> Parser<'src> {
|
||||
TokenKind::Star,
|
||||
TokenKind::Slash,
|
||||
TokenKind::Percent,
|
||||
TokenKind::DoubleEqual,
|
||||
TokenKind::BangEqual,
|
||||
TokenKind::Less,
|
||||
TokenKind::LessEqual,
|
||||
TokenKind::Greater,
|
||||
TokenKind::GreaterEqual,
|
||||
TokenKind::DoubleAmpersand,
|
||||
TokenKind::DoublePipe,
|
||||
TokenKind::DoubleEqual,
|
||||
],
|
||||
found: operator.to_owned(),
|
||||
position: operator_position,
|
||||
@ -466,6 +507,21 @@ impl<'src> Parser<'src> {
|
||||
if push_back_right {
|
||||
self.emit_instruction(right_instruction, right_position);
|
||||
}
|
||||
|
||||
if !push_back_left && !push_back_right {
|
||||
if self.current_register > 0 {
|
||||
self.decrement_register()?;
|
||||
}
|
||||
|
||||
self.emit_instruction(
|
||||
Instruction::load_boolean(self.current_register, true, true),
|
||||
operator_position,
|
||||
);
|
||||
self.emit_instruction(
|
||||
Instruction::load_boolean(self.current_register, false, false),
|
||||
operator_position,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -482,16 +538,17 @@ impl<'src> Parser<'src> {
|
||||
self.advance()?;
|
||||
|
||||
let local_index = self.parse_identifier_from(token, start_position)?;
|
||||
let is_mutable = self.chunk.get_local(local_index, start_position)?.mutable;
|
||||
|
||||
if !is_mutable {
|
||||
return Err(ParseError::CannotMutateImmutableVariable {
|
||||
identifier: self.chunk.get_identifier(local_index).cloned().unwrap(),
|
||||
position: start_position,
|
||||
});
|
||||
}
|
||||
|
||||
if allow_assignment && self.allow(TokenKind::Equal)? {
|
||||
let is_mutable = self.chunk.get_local(local_index, start_position)?.mutable;
|
||||
|
||||
if !is_mutable {
|
||||
return Err(ParseError::CannotMutateImmutableVariable {
|
||||
identifier: self.chunk.get_identifier(local_index).cloned().unwrap(),
|
||||
position: start_position,
|
||||
});
|
||||
}
|
||||
|
||||
self.parse_expression()?;
|
||||
|
||||
let (mut previous_instruction, previous_position) =
|
||||
@ -614,6 +671,13 @@ impl<'src> Parser<'src> {
|
||||
fn parse_if(&mut self, allow_assignment: bool, allow_return: bool) -> Result<(), ParseError> {
|
||||
self.advance()?;
|
||||
self.parse_expression()?;
|
||||
|
||||
let (second_load_boolean, second_position) =
|
||||
self.chunk.pop_instruction(self.current_position)?;
|
||||
let (first_load_boolean, first_position) =
|
||||
self.chunk.pop_instruction(self.current_position)?;
|
||||
let length_after_expression = self.chunk.len();
|
||||
|
||||
self.parse_block(allow_assignment, allow_return)?;
|
||||
|
||||
let jump_start = self.current_register;
|
||||
@ -627,12 +691,22 @@ impl<'src> Parser<'src> {
|
||||
}
|
||||
}
|
||||
|
||||
let jump_end = self.current_register;
|
||||
let jump_distance = (jump_end - jump_start).max(1);
|
||||
let jump = Instruction::jump(jump_distance, true);
|
||||
if self.chunk.len() == length_after_expression {
|
||||
self.emit_instruction(first_load_boolean, first_position);
|
||||
self.emit_instruction(second_load_boolean, second_position);
|
||||
}
|
||||
|
||||
self.chunk
|
||||
.insert_instruction(jump_index, jump, self.current_position);
|
||||
if let Some(Operation::LoadBoolean) = self.chunk.get_last_operation() {
|
||||
// Skip the jump if the last instruction was a LoadBoolean operation. A LoadBoolean can
|
||||
// skip the following instruction, so a jump is unnecessary.
|
||||
} else {
|
||||
let jump_end = self.current_register;
|
||||
let jump_distance = (jump_end - jump_start).max(1);
|
||||
let jump = Instruction::jump(jump_distance, true);
|
||||
|
||||
self.chunk
|
||||
.insert_instruction(jump_index, jump, self.current_position);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -702,35 +776,37 @@ impl<'src> Parser<'src> {
|
||||
self.expect(TokenKind::Equal)?;
|
||||
self.parse_expression()?;
|
||||
|
||||
let (previous_instruction, previous_position) = *self.chunk.get_previous(position)?;
|
||||
let (previous_instruction, previous_position) =
|
||||
*self
|
||||
.chunk
|
||||
.get_previous()
|
||||
.ok_or_else(|| ParseError::ExpectedExpression {
|
||||
found: self.current_token.to_owned(),
|
||||
position,
|
||||
})?;
|
||||
let register = previous_instruction.destination();
|
||||
let local_index =
|
||||
self.chunk
|
||||
.declare_local(identifier, is_mutable, register, previous_position)?;
|
||||
|
||||
// Optimize for assignment to a comparison
|
||||
if let Operation::Jump = previous_instruction.operation() {
|
||||
let (jump, jump_position) = self.chunk.pop_instruction(self.current_position)?;
|
||||
// if let Operation::Jump = previous_instruction.operation() {
|
||||
// let (jump, jump_position) = self.chunk.pop_instruction(self.current_position)?;
|
||||
|
||||
if let Operation::Equal = self
|
||||
.chunk
|
||||
.get_previous(self.current_position)?
|
||||
.0
|
||||
.operation()
|
||||
{
|
||||
self.emit_instruction(jump, jump_position);
|
||||
self.emit_instruction(
|
||||
Instruction::load_boolean(self.current_register, true, true),
|
||||
self.current_position,
|
||||
);
|
||||
self.emit_instruction(
|
||||
Instruction::load_boolean(self.current_register, false, false),
|
||||
self.current_position,
|
||||
);
|
||||
} else {
|
||||
self.emit_instruction(jump, jump_position);
|
||||
}
|
||||
}
|
||||
// if let Some(Operation::Equal) = self.chunk.get_last_operation() {
|
||||
// self.emit_instruction(jump, jump_position);
|
||||
// self.emit_instruction(
|
||||
// Instruction::load_boolean(self.current_register, true, true),
|
||||
// self.current_position,
|
||||
// );
|
||||
// self.emit_instruction(
|
||||
// Instruction::load_boolean(self.current_register, false, false),
|
||||
// self.current_position,
|
||||
// );
|
||||
// } else {
|
||||
// self.emit_instruction(jump, jump_position);
|
||||
// }
|
||||
// }
|
||||
|
||||
self.emit_instruction(
|
||||
Instruction::define_local(register, local_index, is_mutable),
|
||||
@ -836,21 +912,24 @@ pub struct ParseRule<'a> {
|
||||
impl From<&TokenKind> for ParseRule<'_> {
|
||||
fn from(token_kind: &TokenKind) -> Self {
|
||||
match token_kind {
|
||||
TokenKind::Eof => ParseRule {
|
||||
TokenKind::Async => todo!(),
|
||||
TokenKind::Bang => ParseRule {
|
||||
prefix: Some(Parser::parse_unary),
|
||||
infix: None,
|
||||
precedence: Precedence::Unary,
|
||||
},
|
||||
TokenKind::BangEqual => ParseRule {
|
||||
prefix: None,
|
||||
infix: None,
|
||||
precedence: Precedence::None,
|
||||
},
|
||||
TokenKind::Identifier => ParseRule {
|
||||
prefix: Some(Parser::parse_variable),
|
||||
infix: None,
|
||||
precedence: Precedence::None,
|
||||
infix: Some(Parser::parse_binary),
|
||||
precedence: Precedence::Equality,
|
||||
},
|
||||
TokenKind::Bool => todo!(),
|
||||
TokenKind::Boolean => ParseRule {
|
||||
prefix: Some(Parser::parse_boolean),
|
||||
infix: None,
|
||||
precedence: Precedence::None,
|
||||
},
|
||||
TokenKind::Break => todo!(),
|
||||
TokenKind::Byte => ParseRule {
|
||||
prefix: Some(Parser::parse_byte),
|
||||
infix: None,
|
||||
@ -861,55 +940,6 @@ impl From<&TokenKind> for ParseRule<'_> {
|
||||
infix: None,
|
||||
precedence: Precedence::None,
|
||||
},
|
||||
TokenKind::Float => ParseRule {
|
||||
prefix: Some(Parser::parse_float),
|
||||
infix: None,
|
||||
precedence: Precedence::None,
|
||||
},
|
||||
TokenKind::Integer => ParseRule {
|
||||
prefix: Some(Parser::parse_integer),
|
||||
infix: None,
|
||||
precedence: Precedence::None,
|
||||
},
|
||||
TokenKind::String => ParseRule {
|
||||
prefix: Some(Parser::parse_string),
|
||||
infix: None,
|
||||
precedence: Precedence::None,
|
||||
},
|
||||
TokenKind::Async => todo!(),
|
||||
TokenKind::Bool => todo!(),
|
||||
TokenKind::Break => todo!(),
|
||||
TokenKind::Else => ParseRule {
|
||||
prefix: None,
|
||||
infix: None,
|
||||
precedence: Precedence::None,
|
||||
},
|
||||
TokenKind::FloatKeyword => todo!(),
|
||||
TokenKind::If => ParseRule {
|
||||
prefix: Some(Parser::parse_if),
|
||||
infix: None,
|
||||
precedence: Precedence::None,
|
||||
},
|
||||
TokenKind::Int => todo!(),
|
||||
TokenKind::Let => ParseRule {
|
||||
prefix: Some(Parser::parse_let_statement),
|
||||
infix: None,
|
||||
precedence: Precedence::None,
|
||||
},
|
||||
TokenKind::Loop => todo!(),
|
||||
TokenKind::Map => todo!(),
|
||||
TokenKind::Str => todo!(),
|
||||
TokenKind::While => ParseRule {
|
||||
prefix: Some(Parser::parse_while),
|
||||
infix: None,
|
||||
precedence: Precedence::None,
|
||||
},
|
||||
TokenKind::BangEqual => todo!(),
|
||||
TokenKind::Bang => ParseRule {
|
||||
prefix: Some(Parser::parse_unary),
|
||||
infix: None,
|
||||
precedence: Precedence::Unary,
|
||||
},
|
||||
TokenKind::Colon => todo!(),
|
||||
TokenKind::Comma => ParseRule {
|
||||
prefix: None,
|
||||
@ -922,7 +952,6 @@ impl From<&TokenKind> for ParseRule<'_> {
|
||||
infix: Some(Parser::parse_binary),
|
||||
precedence: Precedence::LogicalAnd,
|
||||
},
|
||||
TokenKind::DoubleDot => todo!(),
|
||||
TokenKind::DoubleEqual => ParseRule {
|
||||
prefix: None,
|
||||
infix: Some(Parser::parse_binary),
|
||||
@ -933,13 +962,54 @@ impl From<&TokenKind> for ParseRule<'_> {
|
||||
infix: Some(Parser::parse_binary),
|
||||
precedence: Precedence::LogicalOr,
|
||||
},
|
||||
TokenKind::DoubleDot => todo!(),
|
||||
TokenKind::Eof => ParseRule {
|
||||
prefix: None,
|
||||
infix: None,
|
||||
precedence: Precedence::None,
|
||||
},
|
||||
TokenKind::Equal => ParseRule {
|
||||
prefix: None,
|
||||
infix: None,
|
||||
precedence: Precedence::Assignment,
|
||||
},
|
||||
TokenKind::Greater => todo!(),
|
||||
TokenKind::GreaterOrEqual => todo!(),
|
||||
TokenKind::Else => ParseRule {
|
||||
prefix: None,
|
||||
infix: None,
|
||||
precedence: Precedence::None,
|
||||
},
|
||||
TokenKind::Float => ParseRule {
|
||||
prefix: Some(Parser::parse_float),
|
||||
infix: None,
|
||||
precedence: Precedence::None,
|
||||
},
|
||||
TokenKind::FloatKeyword => todo!(),
|
||||
TokenKind::Greater => ParseRule {
|
||||
prefix: None,
|
||||
infix: Some(Parser::parse_binary),
|
||||
precedence: Precedence::Comparison,
|
||||
},
|
||||
TokenKind::GreaterEqual => ParseRule {
|
||||
prefix: None,
|
||||
infix: Some(Parser::parse_binary),
|
||||
precedence: Precedence::Comparison,
|
||||
},
|
||||
TokenKind::Identifier => ParseRule {
|
||||
prefix: Some(Parser::parse_variable),
|
||||
infix: None,
|
||||
precedence: Precedence::None,
|
||||
},
|
||||
TokenKind::If => ParseRule {
|
||||
prefix: Some(Parser::parse_if),
|
||||
infix: None,
|
||||
precedence: Precedence::None,
|
||||
},
|
||||
TokenKind::Int => todo!(),
|
||||
TokenKind::Integer => ParseRule {
|
||||
prefix: Some(Parser::parse_integer),
|
||||
infix: None,
|
||||
precedence: Precedence::None,
|
||||
},
|
||||
TokenKind::LeftCurlyBrace => ParseRule {
|
||||
prefix: Some(Parser::parse_block),
|
||||
infix: None,
|
||||
@ -955,8 +1025,23 @@ impl From<&TokenKind> for ParseRule<'_> {
|
||||
infix: None,
|
||||
precedence: Precedence::None,
|
||||
},
|
||||
TokenKind::Less => todo!(),
|
||||
TokenKind::LessOrEqual => todo!(),
|
||||
TokenKind::Less => ParseRule {
|
||||
prefix: None,
|
||||
infix: Some(Parser::parse_binary),
|
||||
precedence: Precedence::Comparison,
|
||||
},
|
||||
TokenKind::LessEqual => ParseRule {
|
||||
prefix: None,
|
||||
infix: Some(Parser::parse_binary),
|
||||
precedence: Precedence::Comparison,
|
||||
},
|
||||
TokenKind::Let => ParseRule {
|
||||
prefix: Some(Parser::parse_let_statement),
|
||||
infix: None,
|
||||
precedence: Precedence::None,
|
||||
},
|
||||
TokenKind::Loop => todo!(),
|
||||
TokenKind::Map => todo!(),
|
||||
TokenKind::Minus => ParseRule {
|
||||
prefix: Some(Parser::parse_unary),
|
||||
infix: Some(Parser::parse_binary),
|
||||
@ -999,16 +1084,27 @@ impl From<&TokenKind> for ParseRule<'_> {
|
||||
infix: None,
|
||||
precedence: Precedence::None,
|
||||
},
|
||||
TokenKind::Slash => ParseRule {
|
||||
prefix: None,
|
||||
infix: Some(Parser::parse_binary),
|
||||
precedence: Precedence::Factor,
|
||||
},
|
||||
TokenKind::Star => ParseRule {
|
||||
prefix: None,
|
||||
infix: Some(Parser::parse_binary),
|
||||
precedence: Precedence::Factor,
|
||||
},
|
||||
TokenKind::Str => todo!(),
|
||||
TokenKind::String => ParseRule {
|
||||
prefix: Some(Parser::parse_string),
|
||||
infix: None,
|
||||
precedence: Precedence::None,
|
||||
},
|
||||
TokenKind::Struct => todo!(),
|
||||
TokenKind::Slash => ParseRule {
|
||||
prefix: None,
|
||||
infix: Some(Parser::parse_binary),
|
||||
precedence: Precedence::Factor,
|
||||
TokenKind::While => ParseRule {
|
||||
prefix: Some(Parser::parse_while),
|
||||
infix: None,
|
||||
precedence: Precedence::None,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,184 @@ use crate::Local;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn not() {
|
||||
let source = "!true";
|
||||
|
||||
assert_eq!(
|
||||
parse(source),
|
||||
Ok(Chunk::with_data(
|
||||
vec![
|
||||
(Instruction::load_boolean(0, true, true), Span(1, 5)),
|
||||
(Instruction::not(0, 0), Span(1, 5)),
|
||||
],
|
||||
vec![],
|
||||
vec![]
|
||||
)),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn negate() {
|
||||
let source = "-(42)";
|
||||
|
||||
assert_eq!(
|
||||
parse(source),
|
||||
Ok(Chunk::with_data(
|
||||
vec![
|
||||
(Instruction::load_constant(0, 0), Span(2, 4)),
|
||||
(Instruction::negate(0, 0), Span(1, 5)),
|
||||
],
|
||||
vec![Value::integer(42),],
|
||||
vec![]
|
||||
)),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn greater_than_or_equal() {
|
||||
let source = "1 >= 2;";
|
||||
|
||||
assert_eq!(
|
||||
parse(source),
|
||||
Ok(Chunk::with_data(
|
||||
vec![
|
||||
(
|
||||
*Instruction::less(false, 0, 1)
|
||||
.set_first_argument_to_constant()
|
||||
.set_second_argument_to_constant(),
|
||||
Span(2, 4)
|
||||
),
|
||||
(Instruction::jump(1, true), Span(2, 4)),
|
||||
(Instruction::load_boolean(0, true, true), Span(2, 4)),
|
||||
(Instruction::load_boolean(0, false, false), Span(2, 4)),
|
||||
],
|
||||
vec![Value::integer(1), Value::integer(2)],
|
||||
vec![]
|
||||
)),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn greater() {
|
||||
let source = "1 > 2;";
|
||||
|
||||
assert_eq!(
|
||||
parse(source),
|
||||
Ok(Chunk::with_data(
|
||||
vec![
|
||||
(
|
||||
*Instruction::less_equal(false, 0, 1)
|
||||
.set_first_argument_to_constant()
|
||||
.set_second_argument_to_constant(),
|
||||
Span(2, 3)
|
||||
),
|
||||
(Instruction::jump(1, true), Span(2, 3)),
|
||||
(Instruction::load_boolean(0, true, true), Span(2, 3)),
|
||||
(Instruction::load_boolean(0, false, false), Span(2, 3)),
|
||||
],
|
||||
vec![Value::integer(1), Value::integer(2)],
|
||||
vec![]
|
||||
)),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn less_than_or_equal() {
|
||||
let source = "1 <= 2;";
|
||||
|
||||
assert_eq!(
|
||||
parse(source),
|
||||
Ok(Chunk::with_data(
|
||||
vec![
|
||||
(
|
||||
*Instruction::less_equal(true, 0, 1)
|
||||
.set_first_argument_to_constant()
|
||||
.set_second_argument_to_constant(),
|
||||
Span(2, 4)
|
||||
),
|
||||
(Instruction::jump(1, true), Span(2, 4)),
|
||||
(Instruction::load_boolean(0, true, true), Span(2, 4)),
|
||||
(Instruction::load_boolean(0, false, false), Span(2, 4)),
|
||||
],
|
||||
vec![Value::integer(1), Value::integer(2)],
|
||||
vec![]
|
||||
)),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn less_than() {
|
||||
let source = "1 < 2;";
|
||||
|
||||
assert_eq!(
|
||||
parse(source),
|
||||
Ok(Chunk::with_data(
|
||||
vec![
|
||||
(
|
||||
*Instruction::less(true, 0, 1)
|
||||
.set_first_argument_to_constant()
|
||||
.set_second_argument_to_constant(),
|
||||
Span(2, 3)
|
||||
),
|
||||
(Instruction::jump(1, true), Span(2, 3)),
|
||||
(Instruction::load_boolean(0, true, true), Span(2, 3)),
|
||||
(Instruction::load_boolean(0, false, false), Span(2, 3)),
|
||||
],
|
||||
vec![Value::integer(1), Value::integer(2)],
|
||||
vec![]
|
||||
)),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn not_equal() {
|
||||
let source = "1 != 2;";
|
||||
|
||||
assert_eq!(
|
||||
parse(source),
|
||||
Ok(Chunk::with_data(
|
||||
vec![
|
||||
(
|
||||
*Instruction::equal(false, 0, 1)
|
||||
.set_first_argument_to_constant()
|
||||
.set_second_argument_to_constant(),
|
||||
Span(2, 4)
|
||||
),
|
||||
(Instruction::jump(1, true), Span(2, 4)),
|
||||
(Instruction::load_boolean(0, true, true), Span(2, 4)),
|
||||
(Instruction::load_boolean(0, false, false), Span(2, 4)),
|
||||
],
|
||||
vec![Value::integer(1), Value::integer(2)],
|
||||
vec![]
|
||||
)),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn equal() {
|
||||
let source = "1 == 2";
|
||||
|
||||
assert_eq!(
|
||||
parse(source),
|
||||
Ok(Chunk::with_data(
|
||||
vec![
|
||||
(
|
||||
*Instruction::equal(true, 0, 1)
|
||||
.set_first_argument_to_constant()
|
||||
.set_second_argument_to_constant(),
|
||||
Span(2, 4)
|
||||
),
|
||||
(Instruction::jump(1, true), Span(2, 4)),
|
||||
(Instruction::load_boolean(0, true, true), Span(2, 4)),
|
||||
(Instruction::load_boolean(0, false, false), Span(2, 4)),
|
||||
],
|
||||
vec![Value::integer(1), Value::integer(2)],
|
||||
vec![]
|
||||
)),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn equality_assignment_long() {
|
||||
let source = "let a = if 4 == 4 { true } else { false };";
|
||||
@ -18,12 +196,11 @@ fn equality_assignment_long() {
|
||||
),
|
||||
(Instruction::jump(1, true), Span(13, 15)),
|
||||
(Instruction::load_boolean(0, true, true), Span(20, 24)),
|
||||
(Instruction::jump(1, true), Span(41, 42)),
|
||||
(Instruction::load_boolean(0, false, false), Span(34, 39)),
|
||||
(Instruction::define_local(0, 0, false), Span(4, 5)),
|
||||
],
|
||||
vec![Value::integer(4), Value::integer(4),],
|
||||
vec![Local::new(Identifier::new("a"), false, 0, Some(0)),]
|
||||
vec![Value::integer(4), Value::integer(4)],
|
||||
vec![Local::new(Identifier::new("a"), false, 0, Some(0))]
|
||||
)),
|
||||
);
|
||||
}
|
||||
@ -43,12 +220,12 @@ fn equality_assignment_short() {
|
||||
Span(10, 12)
|
||||
),
|
||||
(Instruction::jump(1, true), Span(10, 12)),
|
||||
(Instruction::load_boolean(0, true, true), Span(14, 15)),
|
||||
(Instruction::load_boolean(0, false, false), Span(14, 15)),
|
||||
(Instruction::load_boolean(0, true, true), Span(10, 12)),
|
||||
(Instruction::load_boolean(0, false, false), Span(10, 12)),
|
||||
(Instruction::define_local(0, 0, false), Span(4, 5)),
|
||||
],
|
||||
vec![Value::integer(4), Value::integer(4),],
|
||||
vec![Local::new(Identifier::new("a"), false, 0, Some(0)),]
|
||||
vec![Value::integer(4), Value::integer(4)],
|
||||
vec![Local::new(Identifier::new("a"), false, 0, Some(0))]
|
||||
)),
|
||||
);
|
||||
}
|
||||
@ -274,13 +451,34 @@ fn declare_local() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn or() {
|
||||
assert_eq!(
|
||||
parse("true || false"),
|
||||
Ok(Chunk::with_data(
|
||||
vec![
|
||||
(
|
||||
*Instruction::test(0, false)
|
||||
.set_second_argument_to_constant()
|
||||
.set_first_argument_to_constant(),
|
||||
Span(5, 7)
|
||||
),
|
||||
(Instruction::jump(1, true), Span(5, 7)),
|
||||
(Instruction::load_boolean(0, true, false), Span(0, 4)),
|
||||
(Instruction::load_boolean(1, false, false), Span(8, 13)),
|
||||
],
|
||||
vec![],
|
||||
vec![]
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn and() {
|
||||
assert_eq!(
|
||||
parse("true && false"),
|
||||
Ok(Chunk::with_data(
|
||||
vec![
|
||||
(Instruction::load_boolean(0, true, false), Span(0, 4)),
|
||||
(
|
||||
*Instruction::test(0, true)
|
||||
.set_second_argument_to_constant()
|
||||
@ -288,6 +486,7 @@ fn and() {
|
||||
Span(5, 7)
|
||||
),
|
||||
(Instruction::jump(1, true), Span(5, 7)),
|
||||
(Instruction::load_boolean(0, true, false), Span(0, 4)),
|
||||
(Instruction::load_boolean(1, false, false), Span(8, 13)),
|
||||
],
|
||||
vec![],
|
||||
|
@ -198,7 +198,7 @@ impl<'src> Token<'src> {
|
||||
Token::Float(_) => TokenKind::Float,
|
||||
Token::FloatKeyword => TokenKind::FloatKeyword,
|
||||
Token::Greater => TokenKind::Greater,
|
||||
Token::GreaterEqual => TokenKind::GreaterOrEqual,
|
||||
Token::GreaterEqual => TokenKind::GreaterEqual,
|
||||
Token::Identifier(_) => TokenKind::Identifier,
|
||||
Token::If => TokenKind::If,
|
||||
Token::Int => TokenKind::Int,
|
||||
@ -208,7 +208,7 @@ impl<'src> Token<'src> {
|
||||
Token::LeftSquareBrace => TokenKind::LeftSquareBrace,
|
||||
Token::Let => TokenKind::Let,
|
||||
Token::Less => TokenKind::Less,
|
||||
Token::LessEqual => TokenKind::LessOrEqual,
|
||||
Token::LessEqual => TokenKind::LessEqual,
|
||||
Token::Loop => TokenKind::Loop,
|
||||
Token::Map => TokenKind::Map,
|
||||
Token::Minus => TokenKind::Minus,
|
||||
@ -450,12 +450,12 @@ pub enum TokenKind {
|
||||
DoublePipe,
|
||||
Equal,
|
||||
Greater,
|
||||
GreaterOrEqual,
|
||||
GreaterEqual,
|
||||
LeftCurlyBrace,
|
||||
LeftParenthesis,
|
||||
LeftSquareBrace,
|
||||
Less,
|
||||
LessOrEqual,
|
||||
LessEqual,
|
||||
Minus,
|
||||
MinusEqual,
|
||||
Mut,
|
||||
@ -495,7 +495,7 @@ impl Display for TokenKind {
|
||||
TokenKind::Float => write!(f, "float value"),
|
||||
TokenKind::FloatKeyword => Token::FloatKeyword.fmt(f),
|
||||
TokenKind::Greater => Token::Greater.fmt(f),
|
||||
TokenKind::GreaterOrEqual => Token::GreaterEqual.fmt(f),
|
||||
TokenKind::GreaterEqual => Token::GreaterEqual.fmt(f),
|
||||
TokenKind::Identifier => write!(f, "identifier"),
|
||||
TokenKind::If => Token::If.fmt(f),
|
||||
TokenKind::Int => Token::Int.fmt(f),
|
||||
@ -505,7 +505,7 @@ impl Display for TokenKind {
|
||||
TokenKind::LeftSquareBrace => Token::LeftSquareBrace.fmt(f),
|
||||
TokenKind::Let => Token::Let.fmt(f),
|
||||
TokenKind::Less => Token::Less.fmt(f),
|
||||
TokenKind::LessOrEqual => Token::LessEqual.fmt(f),
|
||||
TokenKind::LessEqual => Token::LessEqual.fmt(f),
|
||||
TokenKind::Loop => Token::Loop.fmt(f),
|
||||
TokenKind::Map => Token::Map.fmt(f),
|
||||
TokenKind::Minus => Token::Minus.fmt(f),
|
||||
|
@ -60,7 +60,7 @@ impl Vm {
|
||||
}
|
||||
|
||||
while let Ok((instruction, position)) = self.read(Span(0, 0)).copied() {
|
||||
log::trace!("Running instruction {instruction} at {position}");
|
||||
log::trace!("Running IP {} {instruction} at {position}", self.ip);
|
||||
|
||||
match instruction.operation() {
|
||||
Operation::Move => {
|
||||
|
Loading…
Reference in New Issue
Block a user