1
0

Extend parsing

This commit is contained in:
Jeff 2024-09-22 18:17:21 -04:00
parent 573e5ae470
commit 57edf48e36
7 changed files with 494 additions and 139 deletions

View File

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

View File

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

View File

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

View File

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

View File

@ -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![],

View File

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

View File

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