Pass all but one test
This commit is contained in:
parent
03113fdf5e
commit
be77d64c39
@ -3,7 +3,7 @@ use std::fmt::{self, Debug, Display, Formatter};
|
|||||||
use colored::Colorize;
|
use colored::Colorize;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{AnnotatedError, Identifier, Instruction, Span, Value};
|
use crate::{AnnotatedError, Identifier, Instruction, Operation, Span, Value};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Chunk {
|
pub struct Chunk {
|
||||||
@ -40,6 +40,14 @@ impl Chunk {
|
|||||||
self.scope_depth
|
self.scope_depth
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.instructions.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.instructions.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_instruction(
|
pub fn get_instruction(
|
||||||
&self,
|
&self,
|
||||||
offset: usize,
|
offset: usize,
|
||||||
@ -54,18 +62,27 @@ impl Chunk {
|
|||||||
self.instructions.push((instruction, position));
|
self.instructions.push((instruction, position));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn insert_instruction(&mut self, index: usize, instruction: Instruction, position: Span) {
|
||||||
|
self.instructions.insert(index, (instruction, position));
|
||||||
|
}
|
||||||
|
|
||||||
pub fn pop_instruction(&mut self, position: Span) -> Result<(Instruction, Span), ChunkError> {
|
pub fn pop_instruction(&mut self, position: Span) -> Result<(Instruction, Span), ChunkError> {
|
||||||
self.instructions
|
self.instructions
|
||||||
.pop()
|
.pop()
|
||||||
.ok_or(ChunkError::InstructionUnderflow { position })
|
.ok_or(ChunkError::InstructionUnderflow { position })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_last_instruction(&self, position: Span) -> Result<&(Instruction, Span), ChunkError> {
|
pub fn get_previous(&self, position: Span) -> Result<&(Instruction, Span), ChunkError> {
|
||||||
self.instructions
|
self.instructions
|
||||||
.last()
|
.last()
|
||||||
.ok_or(ChunkError::InstructionUnderflow { position })
|
.ok_or(ChunkError::InstructionUnderflow { position })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_last_operation(&self, position: Span) -> Result<Operation, ChunkError> {
|
||||||
|
self.get_previous(position)
|
||||||
|
.map(|(instruction, _)| instruction.operation())
|
||||||
|
}
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
|
@ -43,39 +43,40 @@ impl Instruction {
|
|||||||
instruction
|
instruction
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_list(to_register: u8, list_length: u8) -> Instruction {
|
pub fn load_list(to_register: u8, start_register: u8, list_length: u8) -> Instruction {
|
||||||
let mut instruction = Instruction(Operation::LoadList as u32);
|
let mut instruction = Instruction(Operation::LoadList as u32);
|
||||||
|
|
||||||
instruction.set_destination(to_register);
|
instruction.set_destination(to_register);
|
||||||
instruction.set_first_argument(list_length);
|
instruction.set_first_argument(start_register);
|
||||||
|
instruction.set_second_argument(list_length);
|
||||||
|
|
||||||
instruction
|
instruction
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn define_local(to_register: u8, variable_index: u8, is_mutable: bool) -> Instruction {
|
pub fn define_local(to_register: u8, local_index: u8, is_mutable: bool) -> Instruction {
|
||||||
let mut instruction = Instruction(Operation::DefineLocal as u32);
|
let mut instruction = Instruction(Operation::DefineLocal as u32);
|
||||||
|
|
||||||
instruction.set_destination(to_register);
|
instruction.set_destination(to_register);
|
||||||
instruction.set_first_argument(variable_index);
|
instruction.set_first_argument(local_index);
|
||||||
instruction.set_second_argument(if is_mutable { 1 } else { 0 });
|
instruction.set_second_argument(if is_mutable { 1 } else { 0 });
|
||||||
|
|
||||||
instruction
|
instruction
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_local(to_register: u8, variable_index: u8) -> Instruction {
|
pub fn get_local(to_register: u8, local_index: u8) -> Instruction {
|
||||||
let mut instruction = Instruction(Operation::GetLocal as u32);
|
let mut instruction = Instruction(Operation::GetLocal as u32);
|
||||||
|
|
||||||
instruction.set_destination(to_register);
|
instruction.set_destination(to_register);
|
||||||
instruction.set_first_argument(variable_index);
|
instruction.set_first_argument(local_index);
|
||||||
|
|
||||||
instruction
|
instruction
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_local(from_register: u8, variable_index: u8) -> Instruction {
|
pub fn set_local(from_register: u8, local_index: u8) -> Instruction {
|
||||||
let mut instruction = Instruction(Operation::SetLocal as u32);
|
let mut instruction = Instruction(Operation::SetLocal as u32);
|
||||||
|
|
||||||
instruction.set_destination(from_register);
|
instruction.set_destination(from_register);
|
||||||
instruction.set_first_argument(variable_index);
|
instruction.set_first_argument(local_index);
|
||||||
|
|
||||||
instruction
|
instruction
|
||||||
}
|
}
|
||||||
@ -130,22 +131,21 @@ impl Instruction {
|
|||||||
instruction
|
instruction
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn and(to_register: u8, left_index: u8, right_index: u8) -> Instruction {
|
pub fn test(to_register: u8, test_value: bool) -> Instruction {
|
||||||
let mut instruction = Instruction(Operation::And as u32);
|
let mut instruction = Instruction(Operation::Test as u32);
|
||||||
|
|
||||||
instruction.set_destination(to_register);
|
instruction.set_destination(to_register);
|
||||||
instruction.set_first_argument(left_index);
|
instruction.set_second_argument_to_boolean(test_value);
|
||||||
instruction.set_second_argument(right_index);
|
|
||||||
|
|
||||||
instruction
|
instruction
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn or(to_register: u8, left_index: u8, right_index: u8) -> Instruction {
|
pub fn test_set(to_register: u8, argument_index: u8, test_value: bool) -> Instruction {
|
||||||
let mut instruction = Instruction(Operation::Or as u32);
|
let mut instruction = Instruction(Operation::TestSet as u32);
|
||||||
|
|
||||||
instruction.set_destination(to_register);
|
instruction.set_destination(to_register);
|
||||||
instruction.set_first_argument(left_index);
|
instruction.set_first_argument(argument_index);
|
||||||
instruction.set_second_argument(right_index);
|
instruction.set_second_argument_to_boolean(test_value);
|
||||||
|
|
||||||
instruction
|
instruction
|
||||||
}
|
}
|
||||||
@ -187,8 +187,13 @@ impl Instruction {
|
|||||||
instruction
|
instruction
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn r#return() -> Instruction {
|
pub fn r#return(from_register: u8, to_register: u8) -> Instruction {
|
||||||
Instruction(Operation::Return as u32)
|
let mut instruction = Instruction(Operation::Return as u32);
|
||||||
|
|
||||||
|
instruction.set_destination(from_register);
|
||||||
|
instruction.set_first_argument(to_register);
|
||||||
|
|
||||||
|
instruction
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn operation(&self) -> Operation {
|
pub fn operation(&self) -> Operation {
|
||||||
@ -339,7 +344,7 @@ impl Instruction {
|
|||||||
}
|
}
|
||||||
Operation::LoadList => {
|
Operation::LoadList => {
|
||||||
let destination = self.destination();
|
let destination = self.destination();
|
||||||
let first_index = destination - (self.first_argument() - 1);
|
let first_index = self.first_argument();
|
||||||
let last_index = destination - 1;
|
let last_index = destination - 1;
|
||||||
|
|
||||||
format!("R{} = [R{}..=R{}]", destination, first_index, last_index)
|
format!("R{} = [R{}..=R{}]", destination, first_index, last_index)
|
||||||
@ -416,17 +421,22 @@ impl Instruction {
|
|||||||
|
|
||||||
format!("R{destination} = {first_argument} % {second_argument}",)
|
format!("R{destination} = {first_argument} % {second_argument}",)
|
||||||
}
|
}
|
||||||
Operation::And => {
|
Operation::Test => {
|
||||||
let destination = self.destination();
|
let destination = self.destination();
|
||||||
let (first_argument, second_argument) = format_arguments();
|
let test_value = self.second_argument_as_boolean();
|
||||||
|
let bang = if test_value { "" } else { "!" };
|
||||||
|
|
||||||
format!("R{destination} = {first_argument} && {second_argument}",)
|
format!("if {bang}R{destination} {{ IP++ }}",)
|
||||||
}
|
}
|
||||||
Operation::Or => {
|
Operation::TestSet => {
|
||||||
let destination = self.destination();
|
let destination = self.destination();
|
||||||
let (first_argument, second_argument) = format_arguments();
|
let argument = format!("R{}", self.first_argument());
|
||||||
|
let test_value = self.second_argument_as_boolean();
|
||||||
|
let bang = if test_value { "" } else { "!" };
|
||||||
|
|
||||||
format!("R{destination} = {first_argument} || {second_argument}",)
|
format!(
|
||||||
|
"if {bang}R{destination} {{ R{destination} = R{argument} }} else {{ IP++ }}",
|
||||||
|
)
|
||||||
}
|
}
|
||||||
Operation::Equal => {
|
Operation::Equal => {
|
||||||
let comparison_symbol = if self.destination_as_boolean() {
|
let comparison_symbol = if self.destination_as_boolean() {
|
||||||
@ -489,7 +499,12 @@ impl Instruction {
|
|||||||
format!("IP -= {}", offset)
|
format!("IP -= {}", offset)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Operation::Return => return None,
|
Operation::Return => {
|
||||||
|
let from_register = self.destination();
|
||||||
|
let to_register = self.first_argument();
|
||||||
|
|
||||||
|
format!("R{from_register}..=R{to_register}")
|
||||||
|
}
|
||||||
};
|
};
|
||||||
let trucated_length = 30;
|
let trucated_length = 30;
|
||||||
let with_elipsis = trucated_length - 3;
|
let with_elipsis = trucated_length - 3;
|
||||||
@ -637,32 +652,21 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn and() {
|
fn and() {
|
||||||
let mut instruction = Instruction::and(0, 1, 2);
|
let instruction = Instruction::test(4, true);
|
||||||
|
|
||||||
instruction.set_first_argument_to_constant();
|
assert_eq!(instruction.operation(), Operation::Test);
|
||||||
instruction.set_second_argument_to_constant();
|
assert_eq!(instruction.destination(), 4);
|
||||||
|
assert!(instruction.second_argument_as_boolean());
|
||||||
assert_eq!(instruction.operation(), Operation::And);
|
|
||||||
assert_eq!(instruction.destination(), 0);
|
|
||||||
assert_eq!(instruction.first_argument(), 1);
|
|
||||||
assert_eq!(instruction.second_argument(), 2);
|
|
||||||
assert!(instruction.first_argument_is_constant());
|
|
||||||
assert!(instruction.second_argument_is_constant());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn or() {
|
fn or() {
|
||||||
let mut instruction = Instruction::or(0, 1, 2);
|
let instruction = Instruction::test_set(4, 1, true);
|
||||||
|
|
||||||
instruction.set_first_argument_to_constant();
|
assert_eq!(instruction.operation(), Operation::TestSet);
|
||||||
instruction.set_second_argument_to_constant();
|
assert_eq!(instruction.destination(), 4);
|
||||||
|
|
||||||
assert_eq!(instruction.operation(), Operation::Or);
|
|
||||||
assert_eq!(instruction.destination(), 0);
|
|
||||||
assert_eq!(instruction.first_argument(), 1);
|
assert_eq!(instruction.first_argument(), 1);
|
||||||
assert_eq!(instruction.second_argument(), 2);
|
assert!(instruction.second_argument_as_boolean());
|
||||||
assert!(instruction.first_argument_is_constant());
|
|
||||||
assert!(instruction.second_argument_is_constant());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -710,10 +714,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn jump() {
|
fn jump() {
|
||||||
let mut instruction = Instruction::jump(4, true);
|
let instruction = Instruction::jump(4, true);
|
||||||
|
|
||||||
instruction.set_first_argument_to_constant();
|
|
||||||
instruction.set_second_argument_to_constant();
|
|
||||||
|
|
||||||
assert_eq!(instruction.operation(), Operation::Jump);
|
assert_eq!(instruction.operation(), Operation::Jump);
|
||||||
assert_eq!(instruction.first_argument(), 4);
|
assert_eq!(instruction.first_argument(), 4);
|
||||||
@ -722,13 +723,10 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn r#return() {
|
fn r#return() {
|
||||||
let mut instruction = Instruction::r#return();
|
let instruction = Instruction::r#return(4, 8);
|
||||||
|
|
||||||
instruction.set_first_argument_to_constant();
|
|
||||||
instruction.set_second_argument_to_constant();
|
|
||||||
|
|
||||||
assert_eq!(instruction.operation(), Operation::Return);
|
assert_eq!(instruction.operation(), Operation::Return);
|
||||||
assert!(instruction.first_argument_is_constant());
|
assert_eq!(instruction.destination(), 4);
|
||||||
assert!(instruction.second_argument_is_constant());
|
assert_eq!(instruction.first_argument(), 8);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,8 +16,9 @@ const SUBTRACT: u8 = 0b0000_1001;
|
|||||||
const MULTIPLY: u8 = 0b0000_1010;
|
const MULTIPLY: u8 = 0b0000_1010;
|
||||||
const DIVIDE: u8 = 0b0000_1011;
|
const DIVIDE: u8 = 0b0000_1011;
|
||||||
const MODULO: u8 = 0b0000_1100;
|
const MODULO: u8 = 0b0000_1100;
|
||||||
const AND: u8 = 0b0000_1101;
|
|
||||||
const OR: u8 = 0b0000_1110;
|
const TEST: u8 = 0b0000_1101;
|
||||||
|
const TEST_SET: u8 = 0b0000_1110;
|
||||||
|
|
||||||
const EQUAL: u8 = 0b0000_1111;
|
const EQUAL: u8 = 0b0000_1111;
|
||||||
const LESS: u8 = 0b0001_0000;
|
const LESS: u8 = 0b0001_0000;
|
||||||
@ -51,8 +52,10 @@ pub enum Operation {
|
|||||||
Multiply = MULTIPLY as isize,
|
Multiply = MULTIPLY as isize,
|
||||||
Divide = DIVIDE as isize,
|
Divide = DIVIDE as isize,
|
||||||
Modulo = MODULO as isize,
|
Modulo = MODULO as isize,
|
||||||
And = AND as isize,
|
|
||||||
Or = OR as isize,
|
// Logical operations
|
||||||
|
Test = TEST as isize,
|
||||||
|
TestSet = TEST_SET as isize,
|
||||||
|
|
||||||
// Relational operations
|
// Relational operations
|
||||||
Equal = EQUAL as isize,
|
Equal = EQUAL as isize,
|
||||||
@ -77,8 +80,6 @@ impl Operation {
|
|||||||
| Operation::Multiply
|
| Operation::Multiply
|
||||||
| Operation::Divide
|
| Operation::Divide
|
||||||
| Operation::Modulo
|
| Operation::Modulo
|
||||||
| Operation::And
|
|
||||||
| Operation::Or
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -99,8 +100,8 @@ impl From<u8> for Operation {
|
|||||||
MULTIPLY => Operation::Multiply,
|
MULTIPLY => Operation::Multiply,
|
||||||
DIVIDE => Operation::Divide,
|
DIVIDE => Operation::Divide,
|
||||||
MODULO => Operation::Modulo,
|
MODULO => Operation::Modulo,
|
||||||
AND => Operation::And,
|
TEST => Operation::Test,
|
||||||
OR => Operation::Or,
|
TEST_SET => Operation::TestSet,
|
||||||
EQUAL => Operation::Equal,
|
EQUAL => Operation::Equal,
|
||||||
LESS => Operation::Less,
|
LESS => Operation::Less,
|
||||||
LESS_EQUAL => Operation::LessEqual,
|
LESS_EQUAL => Operation::LessEqual,
|
||||||
@ -135,8 +136,8 @@ impl From<Operation> for u8 {
|
|||||||
Operation::Multiply => MULTIPLY,
|
Operation::Multiply => MULTIPLY,
|
||||||
Operation::Divide => DIVIDE,
|
Operation::Divide => DIVIDE,
|
||||||
Operation::Modulo => MODULO,
|
Operation::Modulo => MODULO,
|
||||||
Operation::And => AND,
|
Operation::Test => TEST,
|
||||||
Operation::Or => OR,
|
Operation::TestSet => TEST_SET,
|
||||||
Operation::Equal => EQUAL,
|
Operation::Equal => EQUAL,
|
||||||
Operation::Less => LESS,
|
Operation::Less => LESS,
|
||||||
Operation::LessEqual => LESS_EQUAL,
|
Operation::LessEqual => LESS_EQUAL,
|
||||||
@ -164,8 +165,8 @@ impl Display for Operation {
|
|||||||
Operation::Multiply => write!(f, "MULTIPLY"),
|
Operation::Multiply => write!(f, "MULTIPLY"),
|
||||||
Operation::Divide => write!(f, "DIVIDE"),
|
Operation::Divide => write!(f, "DIVIDE"),
|
||||||
Operation::Modulo => write!(f, "MODULO"),
|
Operation::Modulo => write!(f, "MODULO"),
|
||||||
Operation::And => write!(f, "AND"),
|
Operation::Test => write!(f, "TEST"),
|
||||||
Operation::Or => write!(f, "OR"),
|
Operation::TestSet => write!(f, "TEST_SET"),
|
||||||
Operation::Equal => write!(f, "EQUAL"),
|
Operation::Equal => write!(f, "EQUAL"),
|
||||||
Operation::Less => write!(f, "LESS"),
|
Operation::Less => write!(f, "LESS"),
|
||||||
Operation::LessEqual => write!(f, "LESS_EQUAL"),
|
Operation::LessEqual => write!(f, "LESS_EQUAL"),
|
||||||
|
@ -18,7 +18,7 @@ pub fn parse(source: &str) -> Result<Chunk, DustError> {
|
|||||||
|
|
||||||
while !parser.is_eof() {
|
while !parser.is_eof() {
|
||||||
parser
|
parser
|
||||||
.parse_statement()
|
.parse_statement(true)
|
||||||
.map_err(|error| DustError::Parse { error, source })?;
|
.map_err(|error| DustError::Parse { error, source })?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -143,31 +143,31 @@ impl<'src> Parser<'src> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_boolean(&mut self, _allow_assignment: bool) -> Result<(), ParseError> {
|
fn parse_boolean(
|
||||||
|
&mut self,
|
||||||
|
_allow_assignment: bool,
|
||||||
|
_allow_return: bool,
|
||||||
|
) -> Result<(), ParseError> {
|
||||||
if let Token::Boolean(text) = self.current_token {
|
if let Token::Boolean(text) = self.current_token {
|
||||||
|
let position = self.current_position;
|
||||||
|
|
||||||
self.advance()?;
|
self.advance()?;
|
||||||
|
|
||||||
let boolean = text.parse::<bool>().unwrap();
|
let boolean = text.parse::<bool>().unwrap();
|
||||||
|
|
||||||
if let Ok((last_instruction, _)) = self
|
if let Ok((last_instruction, _)) =
|
||||||
.chunk
|
self.chunk.get_previous(self.current_position).copied()
|
||||||
.get_last_instruction(self.current_position)
|
|
||||||
.copied()
|
|
||||||
{
|
{
|
||||||
let skip = last_instruction.operation() == Operation::Jump;
|
let skip = last_instruction.operation() == Operation::Jump;
|
||||||
|
|
||||||
self.emit_instruction(
|
self.emit_instruction(
|
||||||
Instruction::load_boolean(self.current_register, boolean, skip),
|
Instruction::load_boolean(self.current_register, boolean, skip),
|
||||||
self.previous_position,
|
position,
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Operation::LoadBoolean = last_instruction.operation() {
|
|
||||||
self.increment_register()?;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
self.emit_instruction(
|
self.emit_instruction(
|
||||||
Instruction::load_boolean(self.current_register, boolean, false),
|
Instruction::load_boolean(self.current_register, boolean, false),
|
||||||
self.previous_position,
|
position,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -175,7 +175,11 @@ impl<'src> Parser<'src> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_byte(&mut self, _allow_assignment: bool) -> Result<(), ParseError> {
|
fn parse_byte(
|
||||||
|
&mut self,
|
||||||
|
_allow_assignment: bool,
|
||||||
|
_allow_return: bool,
|
||||||
|
) -> Result<(), ParseError> {
|
||||||
if let Token::Byte(text) = self.current_token {
|
if let Token::Byte(text) = self.current_token {
|
||||||
self.advance()?;
|
self.advance()?;
|
||||||
|
|
||||||
@ -192,7 +196,11 @@ impl<'src> Parser<'src> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_character(&mut self, _allow_assignment: bool) -> Result<(), ParseError> {
|
fn parse_character(
|
||||||
|
&mut self,
|
||||||
|
_allow_assignment: bool,
|
||||||
|
_allow_return: bool,
|
||||||
|
) -> Result<(), ParseError> {
|
||||||
if let Token::Character(character) = self.current_token {
|
if let Token::Character(character) = self.current_token {
|
||||||
self.advance()?;
|
self.advance()?;
|
||||||
|
|
||||||
@ -204,7 +212,11 @@ impl<'src> Parser<'src> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_float(&mut self, _allow_assignment: bool) -> Result<(), ParseError> {
|
fn parse_float(
|
||||||
|
&mut self,
|
||||||
|
_allow_assignment: bool,
|
||||||
|
_allow_return: bool,
|
||||||
|
) -> Result<(), ParseError> {
|
||||||
if let Token::Float(text) = self.current_token {
|
if let Token::Float(text) = self.current_token {
|
||||||
self.advance()?;
|
self.advance()?;
|
||||||
|
|
||||||
@ -222,7 +234,11 @@ impl<'src> Parser<'src> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_integer(&mut self, _allow_assignment: bool) -> Result<(), ParseError> {
|
fn parse_integer(
|
||||||
|
&mut self,
|
||||||
|
_allow_assignment: bool,
|
||||||
|
_allow_return: bool,
|
||||||
|
) -> Result<(), ParseError> {
|
||||||
if let Token::Integer(text) = self.current_token {
|
if let Token::Integer(text) = self.current_token {
|
||||||
self.advance()?;
|
self.advance()?;
|
||||||
|
|
||||||
@ -240,7 +256,11 @@ impl<'src> Parser<'src> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_string(&mut self, _allow_assignment: bool) -> Result<(), ParseError> {
|
fn parse_string(
|
||||||
|
&mut self,
|
||||||
|
_allow_assignment: bool,
|
||||||
|
_allow_return: bool,
|
||||||
|
) -> Result<(), ParseError> {
|
||||||
if let Token::String(text) = self.current_token {
|
if let Token::String(text) = self.current_token {
|
||||||
self.advance()?;
|
self.advance()?;
|
||||||
|
|
||||||
@ -252,13 +272,21 @@ impl<'src> Parser<'src> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_grouped(&mut self, _allow_assignment: bool) -> Result<(), ParseError> {
|
fn parse_grouped(
|
||||||
|
&mut self,
|
||||||
|
_allow_assignment: bool,
|
||||||
|
_allow_return: bool,
|
||||||
|
) -> Result<(), ParseError> {
|
||||||
self.allow(TokenKind::LeftParenthesis)?;
|
self.allow(TokenKind::LeftParenthesis)?;
|
||||||
self.parse_expression()?;
|
self.parse_expression()?;
|
||||||
self.expect(TokenKind::RightParenthesis)
|
self.expect(TokenKind::RightParenthesis)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_unary(&mut self, _allow_assignment: bool) -> Result<(), ParseError> {
|
fn parse_unary(
|
||||||
|
&mut self,
|
||||||
|
_allow_assignment: bool,
|
||||||
|
_allow_return: bool,
|
||||||
|
) -> Result<(), ParseError> {
|
||||||
let operator = self.current_token;
|
let operator = self.current_token;
|
||||||
let operator_position = self.current_position;
|
let operator_position = self.current_position;
|
||||||
|
|
||||||
@ -308,89 +336,65 @@ impl<'src> Parser<'src> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn parse_binary(&mut self) -> Result<(), ParseError> {
|
fn parse_binary(&mut self) -> Result<(), ParseError> {
|
||||||
let (left_instruction, left_position) =
|
fn handle_argument(
|
||||||
self.chunk.pop_instruction(self.current_position)?;
|
parser: &mut Parser,
|
||||||
let mut push_back_left = false;
|
instruction: &Instruction,
|
||||||
let mut left_is_constant = false;
|
) -> Result<(bool, bool, u8), ParseError> {
|
||||||
let left = match left_instruction.operation() {
|
let mut push_back = false;
|
||||||
Operation::LoadConstant | Operation::GetLocal => {
|
let mut is_constant = false;
|
||||||
log::trace!(
|
let argument = match instruction.operation() {
|
||||||
"Condensing {} to binary expression",
|
Operation::GetLocal => {
|
||||||
left_instruction.operation()
|
parser.decrement_register()?;
|
||||||
);
|
instruction.destination()
|
||||||
|
}
|
||||||
|
Operation::LoadConstant => {
|
||||||
|
is_constant = true;
|
||||||
|
|
||||||
left_is_constant = matches!(left_instruction.operation(), Operation::LoadConstant);
|
parser.decrement_register()?;
|
||||||
|
instruction.first_argument()
|
||||||
self.decrement_register()?;
|
|
||||||
left_instruction.first_argument()
|
|
||||||
}
|
}
|
||||||
Operation::LoadBoolean => {
|
Operation::LoadBoolean => {
|
||||||
self.decrement_register()?;
|
is_constant = true;
|
||||||
left_instruction.destination()
|
push_back = true;
|
||||||
|
|
||||||
|
instruction.destination()
|
||||||
}
|
}
|
||||||
Operation::Close => {
|
Operation::Close => {
|
||||||
return Err(ParseError::ExpectedExpression {
|
return Err(ParseError::ExpectedExpression {
|
||||||
found: self.previous_token.to_owned(),
|
found: parser.previous_token.to_owned(),
|
||||||
position: self.previous_position,
|
position: parser.previous_position,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
push_back_left = true;
|
push_back = true;
|
||||||
|
|
||||||
left_instruction.destination()
|
instruction.destination()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Ok((push_back, is_constant, argument))
|
||||||
|
}
|
||||||
|
|
||||||
|
let (left_instruction, left_position) =
|
||||||
|
self.chunk.pop_instruction(self.current_position)?;
|
||||||
|
let (push_back_left, left_is_constant, left) = handle_argument(self, &left_instruction)?;
|
||||||
|
|
||||||
let operator = self.current_token;
|
let operator = self.current_token;
|
||||||
let operator_position = self.current_position;
|
let operator_position = self.current_position;
|
||||||
let rule = ParseRule::from(&operator.kind());
|
let rule = ParseRule::from(&operator.kind());
|
||||||
|
|
||||||
self.advance()?;
|
let (mut instruction, push_args_first) = match operator.kind() {
|
||||||
self.parse(rule.precedence.increment())?;
|
TokenKind::Plus => (Instruction::add(self.current_register, left, 0), true),
|
||||||
|
TokenKind::Minus => (Instruction::subtract(self.current_register, left, 0), true),
|
||||||
|
TokenKind::Star => (Instruction::multiply(self.current_register, left, 0), true),
|
||||||
|
TokenKind::Slash => (Instruction::divide(self.current_register, left, 0), true),
|
||||||
|
TokenKind::Percent => (Instruction::modulo(self.current_register, left, 0), true),
|
||||||
|
TokenKind::DoubleEqual => (Instruction::equal(true, left, 0), false),
|
||||||
|
TokenKind::DoubleAmpersand => {
|
||||||
|
let and_test = Instruction::test(self.current_register, false);
|
||||||
|
|
||||||
let (right_instruction, right_position) =
|
(and_test, false)
|
||||||
self.chunk.pop_instruction(self.current_position)?;
|
|
||||||
let mut push_back_right = false;
|
|
||||||
let mut right_is_constant = false;
|
|
||||||
let right = match right_instruction.operation() {
|
|
||||||
Operation::LoadConstant | Operation::GetLocal => {
|
|
||||||
log::trace!(
|
|
||||||
"Condensing {} to binary expression",
|
|
||||||
right_instruction.operation()
|
|
||||||
);
|
|
||||||
|
|
||||||
right_is_constant =
|
|
||||||
matches!(right_instruction.operation(), Operation::LoadConstant);
|
|
||||||
|
|
||||||
self.decrement_register()?;
|
|
||||||
right_instruction.first_argument()
|
|
||||||
}
|
}
|
||||||
Operation::LoadBoolean => {
|
|
||||||
self.decrement_register()?;
|
|
||||||
right_instruction.destination()
|
|
||||||
}
|
|
||||||
Operation::Close => {
|
|
||||||
return Err(ParseError::ExpectedExpression {
|
|
||||||
found: self.previous_token.to_owned(),
|
|
||||||
position: self.previous_position,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
push_back_right = true;
|
|
||||||
|
|
||||||
right_instruction.destination()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut instruction = match operator.kind() {
|
|
||||||
TokenKind::Plus => Instruction::add(self.current_register, left, right),
|
|
||||||
TokenKind::Minus => Instruction::subtract(self.current_register, left, right),
|
|
||||||
TokenKind::Star => Instruction::multiply(self.current_register, left, right),
|
|
||||||
TokenKind::Slash => Instruction::divide(self.current_register, left, right),
|
|
||||||
TokenKind::Percent => Instruction::modulo(self.current_register, left, right),
|
|
||||||
TokenKind::DoubleAmpersand => Instruction::and(self.current_register, left, right),
|
|
||||||
TokenKind::DoublePipe => Instruction::or(self.current_register, left, right),
|
|
||||||
TokenKind::DoubleEqual => Instruction::equal(true, left, right),
|
|
||||||
_ => {
|
_ => {
|
||||||
return Err(ParseError::ExpectedTokenMultiple {
|
return Err(ParseError::ExpectedTokenMultiple {
|
||||||
expected: &[
|
expected: &[
|
||||||
@ -409,6 +413,20 @@ impl<'src> Parser<'src> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if !(operator == Token::DoubleEqual) {
|
||||||
|
self.increment_register()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.advance()?;
|
||||||
|
self.parse(rule.precedence.increment())?;
|
||||||
|
|
||||||
|
let (right_instruction, right_position) =
|
||||||
|
self.chunk.pop_instruction(self.current_position)?;
|
||||||
|
let (push_back_right, right_is_constant, right) =
|
||||||
|
handle_argument(self, &right_instruction)?;
|
||||||
|
|
||||||
|
instruction.set_second_argument(right);
|
||||||
|
|
||||||
if left_is_constant {
|
if left_is_constant {
|
||||||
instruction.set_first_argument_to_constant();
|
instruction.set_first_argument_to_constant();
|
||||||
}
|
}
|
||||||
@ -417,6 +435,7 @@ impl<'src> Parser<'src> {
|
|||||||
instruction.set_second_argument_to_constant();
|
instruction.set_second_argument_to_constant();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if push_args_first {
|
||||||
if push_back_left {
|
if push_back_left {
|
||||||
self.emit_instruction(left_instruction, left_position);
|
self.emit_instruction(left_instruction, left_position);
|
||||||
}
|
}
|
||||||
@ -426,29 +445,37 @@ impl<'src> Parser<'src> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.emit_instruction(instruction, operator_position);
|
self.emit_instruction(instruction, operator_position);
|
||||||
|
}
|
||||||
|
|
||||||
if operator == Token::DoubleEqual {
|
if !push_args_first {
|
||||||
let distance = if push_back_left && push_back_right {
|
let push_left_first = self.current_register.saturating_sub(1) == left;
|
||||||
3
|
|
||||||
} else if push_back_left || push_back_right {
|
|
||||||
2
|
|
||||||
} else {
|
|
||||||
1
|
|
||||||
};
|
|
||||||
|
|
||||||
self.emit_instruction(Instruction::jump(distance, true), operator_position);
|
if push_back_left && push_left_first {
|
||||||
} else {
|
self.emit_instruction(left_instruction, left_position);
|
||||||
self.increment_register()?;
|
}
|
||||||
|
|
||||||
|
let jump_distance = if left_is_constant { 1 } else { 2 };
|
||||||
|
|
||||||
|
self.emit_instruction(instruction, operator_position);
|
||||||
|
self.emit_instruction(Instruction::jump(jump_distance, true), operator_position);
|
||||||
|
|
||||||
|
if push_back_left && !push_left_first {
|
||||||
|
self.emit_instruction(left_instruction, left_position);
|
||||||
|
}
|
||||||
|
|
||||||
|
if push_back_right {
|
||||||
|
self.emit_instruction(right_instruction, right_position);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_variable(&mut self, allow_assignment: bool) -> Result<(), ParseError> {
|
fn parse_variable(
|
||||||
self.parse_named_variable(allow_assignment)
|
&mut self,
|
||||||
}
|
allow_assignment: bool,
|
||||||
|
_allow_return: bool,
|
||||||
fn parse_named_variable(&mut self, allow_assignment: bool) -> Result<(), ParseError> {
|
) -> Result<(), ParseError> {
|
||||||
let token = self.current_token.to_owned();
|
let token = self.current_token.to_owned();
|
||||||
let start_position = self.current_position;
|
let start_position = self.current_position;
|
||||||
let local_index = self.parse_identifier_from(token, start_position)?;
|
let local_index = self.parse_identifier_from(token, start_position)?;
|
||||||
@ -482,6 +509,8 @@ impl<'src> Parser<'src> {
|
|||||||
|
|
||||||
previous_instruction.set_destination(register_index);
|
previous_instruction.set_destination(register_index);
|
||||||
self.emit_instruction(previous_instruction, self.current_position);
|
self.emit_instruction(previous_instruction, self.current_position);
|
||||||
|
|
||||||
|
return Ok(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -491,11 +520,11 @@ impl<'src> Parser<'src> {
|
|||||||
start_position,
|
start_position,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
self.increment_register()?;
|
||||||
self.emit_instruction(
|
self.emit_instruction(
|
||||||
Instruction::get_local(self.current_register, local_index),
|
Instruction::get_local(self.current_register, local_index),
|
||||||
self.previous_position,
|
self.previous_position,
|
||||||
);
|
);
|
||||||
self.increment_register()?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -526,12 +555,16 @@ impl<'src> Parser<'src> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_block(&mut self, _allow_assignment: bool) -> Result<(), ParseError> {
|
fn parse_block(
|
||||||
|
&mut self,
|
||||||
|
_allow_assignment: bool,
|
||||||
|
allow_return: bool,
|
||||||
|
) -> Result<(), ParseError> {
|
||||||
self.advance()?;
|
self.advance()?;
|
||||||
self.chunk.begin_scope();
|
self.chunk.begin_scope();
|
||||||
|
|
||||||
while !self.allow(TokenKind::RightCurlyBrace)? && !self.is_eof() {
|
while !self.allow(TokenKind::RightCurlyBrace)? && !self.is_eof() {
|
||||||
self.parse_statement()?;
|
self.parse_statement(allow_return)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.chunk.end_scope();
|
self.chunk.end_scope();
|
||||||
@ -539,11 +572,16 @@ impl<'src> Parser<'src> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_list(&mut self, _allow_assignment: bool) -> Result<(), ParseError> {
|
fn parse_list(
|
||||||
|
&mut self,
|
||||||
|
_allow_assignment: bool,
|
||||||
|
_allow_return: bool,
|
||||||
|
) -> Result<(), ParseError> {
|
||||||
let start = self.current_position.0;
|
let start = self.current_position.0;
|
||||||
|
|
||||||
self.advance()?;
|
self.advance()?;
|
||||||
|
|
||||||
|
let start_register = self.current_register;
|
||||||
let mut length = 0;
|
let mut length = 0;
|
||||||
|
|
||||||
while !self.allow(TokenKind::RightSquareBrace)? && !self.is_eof() {
|
while !self.allow(TokenKind::RightSquareBrace)? && !self.is_eof() {
|
||||||
@ -570,29 +608,39 @@ impl<'src> Parser<'src> {
|
|||||||
let end = self.current_position.1;
|
let end = self.current_position.1;
|
||||||
|
|
||||||
self.emit_instruction(
|
self.emit_instruction(
|
||||||
Instruction::load_list(self.current_register, length),
|
Instruction::load_list(self.current_register, start_register, length),
|
||||||
Span(start, end),
|
Span(start, end),
|
||||||
);
|
);
|
||||||
|
self.increment_register()?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_if(&mut self, allow_assignment: 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()?;
|
||||||
self.parse_block(allow_assignment)?;
|
|
||||||
|
self.parse_block(allow_assignment, false)?;
|
||||||
|
|
||||||
if self.allow(TokenKind::Else)? {
|
if self.allow(TokenKind::Else)? {
|
||||||
self.parse_block(allow_assignment)?;
|
if self.allow(TokenKind::If)? {
|
||||||
|
self.parse_if(allow_assignment, false)?;
|
||||||
|
} else {
|
||||||
|
self.parse_block(allow_assignment, false)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_while(&mut self, allow_assignment: bool) -> Result<(), ParseError> {
|
fn parse_while(
|
||||||
|
&mut self,
|
||||||
|
allow_assignment: bool,
|
||||||
|
allow_return: bool,
|
||||||
|
) -> Result<(), ParseError> {
|
||||||
self.advance()?;
|
self.advance()?;
|
||||||
self.parse_expression()?;
|
self.parse_expression()?;
|
||||||
self.parse_block(allow_assignment)?;
|
self.parse_block(allow_assignment, allow_return)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -601,18 +649,25 @@ impl<'src> Parser<'src> {
|
|||||||
self.parse(Precedence::None)
|
self.parse(Precedence::None)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_statement(&mut self) -> Result<(), ParseError> {
|
fn parse_statement(&mut self, allow_return: bool) -> Result<(), ParseError> {
|
||||||
let start = self.current_position.0;
|
let start = self.current_position.0;
|
||||||
let is_expression = match self.current_token {
|
let is_expression = match self.current_token {
|
||||||
Token::Let => {
|
Token::Let => {
|
||||||
self.parse_let_statement(true)?;
|
self.parse_let_statement(true, allow_return)?;
|
||||||
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
Token::LeftCurlyBrace => {
|
Token::LeftCurlyBrace => {
|
||||||
self.parse_block(true)?;
|
self.parse_block(true, true)?;
|
||||||
|
|
||||||
self.previous_token != Token::Semicolon
|
let last_operation = self.chunk.get_last_operation(self.current_position)?;
|
||||||
|
let ends_in_return = last_operation == Operation::Return;
|
||||||
|
|
||||||
|
if ends_in_return {
|
||||||
|
self.chunk.pop_instruction(self.current_position)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
ends_in_return
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
self.parse_expression()?;
|
self.parse_expression()?;
|
||||||
@ -620,18 +675,36 @@ impl<'src> Parser<'src> {
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if !allow_return || !is_expression {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let (last_instruction, _) = *self.chunk.get_previous(self.current_position)?;
|
||||||
|
let ends_in_assignment = matches!(
|
||||||
|
last_instruction.operation(),
|
||||||
|
Operation::DefineLocal | Operation::SetLocal
|
||||||
|
);
|
||||||
let has_semicolon = self.allow(TokenKind::Semicolon)?;
|
let has_semicolon = self.allow(TokenKind::Semicolon)?;
|
||||||
|
|
||||||
if (!has_semicolon && is_expression) && self.is_eof() {
|
if !ends_in_assignment && !has_semicolon {
|
||||||
let end = self.previous_position.1;
|
let end = self.previous_position.1;
|
||||||
|
let return_register = last_instruction.destination();
|
||||||
|
|
||||||
self.emit_instruction(Instruction::r#return(), Span(start, end));
|
self.emit_instruction(
|
||||||
|
Instruction::r#return(return_register, return_register),
|
||||||
|
Span(start, end),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_let_statement(&mut self, allow_assignment: bool) -> Result<(), ParseError> {
|
fn parse_let_statement(
|
||||||
|
&mut self,
|
||||||
|
allow_assignment: bool,
|
||||||
|
_allow_return: bool,
|
||||||
|
) -> Result<(), ParseError> {
|
||||||
if !allow_assignment {
|
if !allow_assignment {
|
||||||
return Err(ParseError::ExpectedExpression {
|
return Err(ParseError::ExpectedExpression {
|
||||||
found: self.current_token.to_owned(),
|
found: self.current_token.to_owned(),
|
||||||
@ -658,8 +731,7 @@ impl<'src> Parser<'src> {
|
|||||||
self.expect(TokenKind::Equal)?;
|
self.expect(TokenKind::Equal)?;
|
||||||
self.parse_expression()?;
|
self.parse_expression()?;
|
||||||
|
|
||||||
let (previous_instruction, previous_position) =
|
let (previous_instruction, previous_position) = *self.chunk.get_previous(position)?;
|
||||||
*self.chunk.get_last_instruction(position)?;
|
|
||||||
let register = previous_instruction.destination();
|
let register = previous_instruction.destination();
|
||||||
let local_index =
|
let local_index =
|
||||||
self.chunk
|
self.chunk
|
||||||
@ -671,7 +743,7 @@ impl<'src> Parser<'src> {
|
|||||||
|
|
||||||
if let Operation::Equal = self
|
if let Operation::Equal = self
|
||||||
.chunk
|
.chunk
|
||||||
.get_last_instruction(self.current_position)?
|
.get_previous(self.current_position)?
|
||||||
.0
|
.0
|
||||||
.operation()
|
.operation()
|
||||||
{
|
{
|
||||||
@ -700,6 +772,7 @@ impl<'src> Parser<'src> {
|
|||||||
|
|
||||||
fn parse(&mut self, precedence: Precedence) -> Result<(), ParseError> {
|
fn parse(&mut self, precedence: Precedence) -> Result<(), ParseError> {
|
||||||
let allow_assignment = precedence < Precedence::Assignment;
|
let allow_assignment = precedence < Precedence::Assignment;
|
||||||
|
let allow_return = precedence == Precedence::None;
|
||||||
|
|
||||||
if let Some(prefix_parser) = ParseRule::from(&self.current_token.kind()).prefix {
|
if let Some(prefix_parser) = ParseRule::from(&self.current_token.kind()).prefix {
|
||||||
log::trace!(
|
log::trace!(
|
||||||
@ -707,7 +780,7 @@ impl<'src> Parser<'src> {
|
|||||||
self.current_token,
|
self.current_token,
|
||||||
);
|
);
|
||||||
|
|
||||||
prefix_parser(self, allow_assignment)?;
|
prefix_parser(self, allow_assignment, allow_return)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut infix_rule = ParseRule::from(&self.current_token.kind());
|
let mut infix_rule = ParseRule::from(&self.current_token.kind());
|
||||||
@ -779,7 +852,7 @@ impl Display for Precedence {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type PrefixFunction<'a> = fn(&mut Parser<'a>, bool) -> Result<(), ParseError>;
|
type PrefixFunction<'a> = fn(&mut Parser<'a>, bool, bool) -> Result<(), ParseError>;
|
||||||
type InfixFunction<'a> = fn(&mut Parser<'a>) -> Result<(), ParseError>;
|
type InfixFunction<'a> = fn(&mut Parser<'a>) -> Result<(), ParseError>;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
@ -1057,7 +1130,14 @@ impl AnnotatedError for ParseError {
|
|||||||
} => Some(format!("Expected \"{expected}\", found \"{found}\"")),
|
} => Some(format!("Expected \"{expected}\", found \"{found}\"")),
|
||||||
Self::ExpectedTokenMultiple {
|
Self::ExpectedTokenMultiple {
|
||||||
expected, found, ..
|
expected, found, ..
|
||||||
} => Some(format!("Expected one of {expected:?}, found \"{found}\"")),
|
} => {
|
||||||
|
let expected = expected
|
||||||
|
.iter()
|
||||||
|
.map(|kind| kind.to_string() + ", ")
|
||||||
|
.collect::<String>();
|
||||||
|
|
||||||
|
Some(format!("Expected one of {expected}, found \"{found}\""))
|
||||||
|
}
|
||||||
Self::InvalidAssignmentTarget { found, .. } => {
|
Self::InvalidAssignmentTarget { found, .. } => {
|
||||||
Some(format!("Invalid assignment target, found \"{found}\""))
|
Some(format!("Invalid assignment target, found \"{found}\""))
|
||||||
}
|
}
|
||||||
|
@ -69,7 +69,7 @@ fn if_else_expression() {
|
|||||||
(Instruction::jump(1, true), Span(5, 7)),
|
(Instruction::jump(1, true), Span(5, 7)),
|
||||||
(Instruction::load_constant(0, 2), Span(12, 13)),
|
(Instruction::load_constant(0, 2), Span(12, 13)),
|
||||||
(Instruction::load_constant(1, 3), Span(23, 24)),
|
(Instruction::load_constant(1, 3), Span(23, 24)),
|
||||||
(Instruction::r#return(), Span(0, 26)),
|
(Instruction::r#return(1, 1), Span(0, 26)),
|
||||||
],
|
],
|
||||||
vec![
|
vec![
|
||||||
Value::integer(1),
|
Value::integer(1),
|
||||||
@ -98,8 +98,8 @@ fn list_with_expression() {
|
|||||||
Span(6, 7)
|
Span(6, 7)
|
||||||
),
|
),
|
||||||
(Instruction::load_constant(2, 3), Span(11, 12)),
|
(Instruction::load_constant(2, 3), Span(11, 12)),
|
||||||
(Instruction::load_list(3, 3), Span(0, 13)),
|
(Instruction::load_list(3, 0, 3), Span(0, 13)),
|
||||||
(Instruction::r#return(), Span(0, 13)),
|
(Instruction::r#return(3, 3), Span(0, 13)),
|
||||||
],
|
],
|
||||||
vec![
|
vec![
|
||||||
Value::integer(1),
|
Value::integer(1),
|
||||||
@ -123,8 +123,8 @@ fn list() {
|
|||||||
(Instruction::load_constant(0, 0), Span(1, 2)),
|
(Instruction::load_constant(0, 0), Span(1, 2)),
|
||||||
(Instruction::load_constant(1, 1), Span(4, 5)),
|
(Instruction::load_constant(1, 1), Span(4, 5)),
|
||||||
(Instruction::load_constant(2, 2), Span(7, 8)),
|
(Instruction::load_constant(2, 2), Span(7, 8)),
|
||||||
(Instruction::load_list(3, 3), Span(0, 9)),
|
(Instruction::load_list(3, 0, 3), Span(0, 9)),
|
||||||
(Instruction::r#return(), Span(0, 9)),
|
(Instruction::r#return(3, 3), Span(0, 9)),
|
||||||
],
|
],
|
||||||
vec![Value::integer(1), Value::integer(2), Value::integer(3),],
|
vec![Value::integer(1), Value::integer(2), Value::integer(3),],
|
||||||
vec![]
|
vec![]
|
||||||
@ -217,7 +217,7 @@ fn parentheses_precedence() {
|
|||||||
*Instruction::multiply(1, 0, 2).set_second_argument_to_constant(),
|
*Instruction::multiply(1, 0, 2).set_second_argument_to_constant(),
|
||||||
Span(8, 9)
|
Span(8, 9)
|
||||||
),
|
),
|
||||||
(Instruction::r#return(), Span(0, 11)),
|
(Instruction::r#return(1, 1), Span(0, 11)),
|
||||||
],
|
],
|
||||||
vec![Value::integer(1), Value::integer(2), Value::integer(3)],
|
vec![Value::integer(1), Value::integer(2), Value::integer(3)],
|
||||||
vec![]
|
vec![]
|
||||||
@ -232,7 +232,7 @@ fn math_operator_precedence() {
|
|||||||
Ok(Chunk::with_data(
|
Ok(Chunk::with_data(
|
||||||
vec![
|
vec![
|
||||||
(
|
(
|
||||||
*Instruction::multiply(1, 2, 3)
|
*Instruction::multiply(2, 2, 3)
|
||||||
.set_first_argument_to_constant()
|
.set_first_argument_to_constant()
|
||||||
.set_second_argument_to_constant(),
|
.set_second_argument_to_constant(),
|
||||||
Span(10, 11)
|
Span(10, 11)
|
||||||
@ -244,11 +244,11 @@ fn math_operator_precedence() {
|
|||||||
Span(2, 3)
|
Span(2, 3)
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
*Instruction::divide(2, 1, 4).set_second_argument_to_constant(),
|
*Instruction::divide(3, 2, 4).set_second_argument_to_constant(),
|
||||||
Span(14, 15)
|
Span(14, 15)
|
||||||
),
|
),
|
||||||
(Instruction::subtract(3, 0, 2), Span(6, 7)),
|
(Instruction::subtract(1, 0, 3), Span(6, 7)),
|
||||||
(Instruction::r#return(), Span(0, 17)),
|
(Instruction::r#return(1, 1), Span(0, 17)),
|
||||||
],
|
],
|
||||||
vec![
|
vec![
|
||||||
Value::integer(1),
|
Value::integer(1),
|
||||||
@ -280,18 +280,16 @@ fn declare_local() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn and() {
|
fn and() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse("1 && 2"),
|
parse("true && false"),
|
||||||
Ok(Chunk::with_data(
|
Ok(Chunk::with_data(
|
||||||
vec![
|
vec![
|
||||||
(
|
(Instruction::load_boolean(0, true, false), Span(0, 4)),
|
||||||
*Instruction::and(0, 0, 1)
|
(Instruction::test(0, true), Span(5, 7)),
|
||||||
.set_first_argument_to_constant()
|
(Instruction::jump(1, true), Span(5, 7)),
|
||||||
.set_second_argument_to_constant(),
|
(Instruction::load_boolean(1, false, false), Span(8, 13)),
|
||||||
Span(2, 4)
|
(Instruction::r#return(1, 1), Span(0, 13)),
|
||||||
),
|
|
||||||
(Instruction::r#return(), Span(0, 6)),
|
|
||||||
],
|
],
|
||||||
vec![Value::integer(1), Value::integer(2)],
|
vec![],
|
||||||
vec![]
|
vec![]
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
@ -309,7 +307,7 @@ fn divide() {
|
|||||||
.set_second_argument_to_constant(),
|
.set_second_argument_to_constant(),
|
||||||
Span(2, 3)
|
Span(2, 3)
|
||||||
),
|
),
|
||||||
(Instruction::r#return(), Span(0, 5)),
|
(Instruction::r#return(0, 0), Span(0, 5)),
|
||||||
],
|
],
|
||||||
vec![Value::integer(1), Value::integer(2)],
|
vec![Value::integer(1), Value::integer(2)],
|
||||||
vec![]
|
vec![]
|
||||||
@ -329,7 +327,7 @@ fn multiply() {
|
|||||||
.set_second_argument_to_constant(),
|
.set_second_argument_to_constant(),
|
||||||
Span(2, 3)
|
Span(2, 3)
|
||||||
),
|
),
|
||||||
(Instruction::r#return(), Span(0, 5)),
|
(Instruction::r#return(0, 0), Span(0, 5)),
|
||||||
],
|
],
|
||||||
vec![Value::integer(1), Value::integer(2)],
|
vec![Value::integer(1), Value::integer(2)],
|
||||||
vec![]
|
vec![]
|
||||||
@ -349,7 +347,7 @@ fn add() {
|
|||||||
.set_second_argument_to_constant(),
|
.set_second_argument_to_constant(),
|
||||||
Span(2, 3)
|
Span(2, 3)
|
||||||
),
|
),
|
||||||
(Instruction::r#return(), Span(0, 5)),
|
(Instruction::r#return(0, 0), Span(0, 5)),
|
||||||
],
|
],
|
||||||
vec![Value::integer(1), Value::integer(2)],
|
vec![Value::integer(1), Value::integer(2)],
|
||||||
vec![]
|
vec![]
|
||||||
@ -369,7 +367,7 @@ fn subtract() {
|
|||||||
.set_second_argument_to_constant(),
|
.set_second_argument_to_constant(),
|
||||||
Span(2, 3)
|
Span(2, 3)
|
||||||
),
|
),
|
||||||
(Instruction::r#return(), Span(0, 5)),
|
(Instruction::r#return(0, 0), Span(0, 5)),
|
||||||
],
|
],
|
||||||
vec![Value::integer(1), Value::integer(2)],
|
vec![Value::integer(1), Value::integer(2)],
|
||||||
vec![]
|
vec![]
|
||||||
@ -384,7 +382,7 @@ fn constant() {
|
|||||||
Ok(Chunk::with_data(
|
Ok(Chunk::with_data(
|
||||||
vec![
|
vec![
|
||||||
(Instruction::load_constant(0, 0), Span(0, 2)),
|
(Instruction::load_constant(0, 0), Span(0, 2)),
|
||||||
(Instruction::r#return(), Span(0, 2)),
|
(Instruction::r#return(0, 0), Span(0, 2)),
|
||||||
],
|
],
|
||||||
vec![Value::integer(42)],
|
vec![Value::integer(42)],
|
||||||
vec![]
|
vec![]
|
||||||
|
@ -186,21 +186,34 @@ impl Vm {
|
|||||||
|
|
||||||
self.insert(remainder, instruction.destination(), position)?;
|
self.insert(remainder, instruction.destination(), position)?;
|
||||||
}
|
}
|
||||||
Operation::And => {
|
Operation::Test => {
|
||||||
let (left, right) = take_constants_or_clone(self, instruction, position)?;
|
let register = instruction.destination();
|
||||||
let and = left
|
let test_value = instruction.second_argument_as_boolean();
|
||||||
.and(&right)
|
let value = self.clone(register, position)?;
|
||||||
.map_err(|error| VmError::Value { error, position })?;
|
let boolean = value.as_boolean().ok_or_else(|| VmError::ExpectedBoolean {
|
||||||
|
found: value,
|
||||||
|
position,
|
||||||
|
})?;
|
||||||
|
|
||||||
self.insert(and, instruction.destination(), position)?;
|
if boolean != test_value {
|
||||||
|
self.ip += 1;
|
||||||
}
|
}
|
||||||
Operation::Or => {
|
}
|
||||||
let (left, right) = take_constants_or_clone(self, instruction, position)?;
|
Operation::TestSet => {
|
||||||
let or = left
|
let to_register = instruction.destination();
|
||||||
.or(&right)
|
let argument = instruction.first_argument();
|
||||||
.map_err(|error| VmError::Value { error, position })?;
|
let test_value = instruction.second_argument_as_boolean();
|
||||||
|
let value = self.clone(argument, position)?;
|
||||||
|
let boolean = value.as_boolean().ok_or_else(|| VmError::ExpectedBoolean {
|
||||||
|
found: value.clone(),
|
||||||
|
position,
|
||||||
|
})?;
|
||||||
|
|
||||||
self.insert(or, instruction.destination(), position)?;
|
if boolean == test_value {
|
||||||
|
self.insert(value, to_register, position)?;
|
||||||
|
} else {
|
||||||
|
self.ip += 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Operation::Equal => {
|
Operation::Equal => {
|
||||||
let (left, right) = take_constants_or_clone(self, instruction, position)?;
|
let (left, right) = take_constants_or_clone(self, instruction, position)?;
|
||||||
@ -279,9 +292,9 @@ impl Vm {
|
|||||||
self.ip = new_ip;
|
self.ip = new_ip;
|
||||||
}
|
}
|
||||||
Operation::Return => {
|
Operation::Return => {
|
||||||
let value = self.pop(position)?;
|
let return_value = self.pop(position)?;
|
||||||
|
|
||||||
return Ok(Some(value));
|
return Ok(Some(return_value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -305,6 +318,20 @@ impl Vm {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn take(&mut self, index: u8, position: Span) -> Result<Value, VmError> {
|
||||||
|
let index = index as usize;
|
||||||
|
|
||||||
|
if let Some(register) = self.register_stack.get_mut(index) {
|
||||||
|
let value = register
|
||||||
|
.take()
|
||||||
|
.ok_or_else(|| VmError::EmptyRegister { index, position })?;
|
||||||
|
|
||||||
|
Ok(value)
|
||||||
|
} else {
|
||||||
|
Err(VmError::RegisterIndexOutOfBounds { position })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn clone(&mut self, index: u8, position: Span) -> Result<Value, VmError> {
|
fn clone(&mut self, index: u8, position: Span) -> Result<Value, VmError> {
|
||||||
let index = index as usize;
|
let index = index as usize;
|
||||||
|
|
||||||
@ -397,6 +424,10 @@ pub enum VmError {
|
|||||||
index: usize,
|
index: usize,
|
||||||
position: Span,
|
position: Span,
|
||||||
},
|
},
|
||||||
|
ExpectedBoolean {
|
||||||
|
found: Value,
|
||||||
|
position: Span,
|
||||||
|
},
|
||||||
RegisterIndexOutOfBounds {
|
RegisterIndexOutOfBounds {
|
||||||
position: Span,
|
position: Span,
|
||||||
},
|
},
|
||||||
@ -437,6 +468,7 @@ impl AnnotatedError for VmError {
|
|||||||
fn description(&self) -> &'static str {
|
fn description(&self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
Self::EmptyRegister { .. } => "Empty register",
|
Self::EmptyRegister { .. } => "Empty register",
|
||||||
|
Self::ExpectedBoolean { .. } => "Expected boolean",
|
||||||
Self::RegisterIndexOutOfBounds { .. } => "Register index out of bounds",
|
Self::RegisterIndexOutOfBounds { .. } => "Register index out of bounds",
|
||||||
Self::InvalidInstruction { .. } => "Invalid instruction",
|
Self::InvalidInstruction { .. } => "Invalid instruction",
|
||||||
Self::StackOverflow { .. } => "Stack overflow",
|
Self::StackOverflow { .. } => "Stack overflow",
|
||||||
@ -462,6 +494,7 @@ impl AnnotatedError for VmError {
|
|||||||
fn position(&self) -> Span {
|
fn position(&self) -> Span {
|
||||||
match self {
|
match self {
|
||||||
Self::EmptyRegister { position, .. } => *position,
|
Self::EmptyRegister { position, .. } => *position,
|
||||||
|
Self::ExpectedBoolean { position, .. } => *position,
|
||||||
Self::RegisterIndexOutOfBounds { position } => *position,
|
Self::RegisterIndexOutOfBounds { position } => *position,
|
||||||
Self::InvalidInstruction { position, .. } => *position,
|
Self::InvalidInstruction { position, .. } => *position,
|
||||||
Self::StackUnderflow { position } => *position,
|
Self::StackUnderflow { position } => *position,
|
||||||
|
Loading…
Reference in New Issue
Block a user