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