1
0

Refine equality implementation

This commit is contained in:
Jeff 2024-09-24 08:29:33 -04:00
parent 1d03876b89
commit 3df42f6a47
7 changed files with 302 additions and 199 deletions

View File

@ -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, Operation, Span, Value}; use crate::{instruction, AnnotatedError, Identifier, Instruction, Operation, Span, Value};
#[derive(Clone)] #[derive(Clone)]
pub struct Chunk { pub struct Chunk {
@ -307,8 +307,8 @@ impl<'a> ChunkDisassembler<'a> {
"", "",
"Instructions", "Instructions",
"------------", "------------",
"INDEX BYTECODE OPERATION INFO POSITION", "INDEX BYTECODE OPERATION INFO JUMP POSITION",
"----- -------- --------------- ------------------------------ --------", "----- -------- --------------- ------------------------- -------------- --------",
]; ];
const CONSTANT_HEADER: [&'static str; 5] = [ const CONSTANT_HEADER: [&'static str; 5] = [
@ -395,17 +395,24 @@ impl<'a> ChunkDisassembler<'a> {
for (index, (instruction, position)) in self.chunk.instructions.iter().enumerate() { for (index, (instruction, position)) in self.chunk.instructions.iter().enumerate() {
let position = position.to_string(); let position = position.to_string();
let operation = instruction.operation().to_string(); let operation = instruction.operation().to_string();
let info_option = instruction.disassembly_info(Some(self.chunk)); let (info, jump_offset) = instruction.disassembly_info(Some(self.chunk));
let bytecode = u32::from(instruction); let info = if let Some(info) = info {
info
let instruction_display = if let Some(info) = info_option {
format!("{index:<5} {bytecode:<08X} {operation:15} {info:30} {position:8}")
} else { } else {
format!( " ".to_string()
"{index:<5} {bytecode:<08X} {operation:15} {:30} {position:8}",
" "
)
}; };
let jump_offset = if let Some(jump_offset) = jump_offset {
let index = index as isize;
let jump_index = index + jump_offset + 1;
format!("{index} -> {jump_index}")
} else {
" ".to_string()
};
let bytecode = u32::from(instruction);
let instruction_display = format!(
"{index:<5} {bytecode:<08X} {operation:15} {info:25} {jump_offset:14} {position:8}"
);
disassembly.push_str(&center(&instruction_display)); disassembly.push_str(&center(&instruction_display));
} }

View File

@ -1,4 +1,4 @@
use std::fmt::{self, Display, Formatter}; use std::fmt::{self, format, Display, Formatter};
use crate::{Chunk, Operation, Span}; use crate::{Chunk, Operation, Span};
@ -216,6 +216,14 @@ impl Instruction {
instruction instruction
} }
pub fn end(returns_value: bool) -> Instruction {
let mut instruction = Instruction(Operation::End as u32);
instruction.set_destination_to_boolean(returns_value);
instruction
}
pub fn operation(&self) -> Operation { pub fn operation(&self) -> Operation {
Operation::from((self.0 & 0b0000_0000_0011_1111) as u8) Operation::from((self.0 & 0b0000_0000_0011_1111) as u8)
} }
@ -299,17 +307,7 @@ impl Instruction {
self.0 |= (argument as u32) << 8; self.0 |= (argument as u32) << 8;
} }
pub fn disassemble(&self, chunk: &Chunk) -> String { pub fn disassembly_info(&self, chunk: Option<&Chunk>) -> (Option<String>, Option<isize>) {
let mut disassembled = format!("{:16} ", self.operation().to_string());
if let Some(info) = self.disassembly_info(Some(chunk)) {
disassembled.push_str(&info);
}
disassembled
}
pub fn disassembly_info(&self, chunk: Option<&Chunk>) -> Option<String> {
let format_arguments = || { let format_arguments = || {
let first_argument = if self.first_argument_is_constant() { let first_argument = if self.first_argument_is_constant() {
format!("C{}", self.first_argument()) format!("C{}", self.first_argument())
@ -324,42 +322,54 @@ impl Instruction {
(first_argument, second_argument) (first_argument, second_argument)
}; };
let mut jump_offset = None;
let info = match self.operation() { let info = match self.operation() {
Operation::Move => { Operation::Move => Some(format!(
format!("R{} = R{}", self.destination(), self.first_argument()) "R{} = R{}",
} self.destination(),
self.first_argument()
)),
Operation::Close => { Operation::Close => {
let from_register = self.first_argument(); let from_register = self.first_argument();
let to_register = self.second_argument().saturating_sub(1); let to_register = self.second_argument().saturating_sub(1);
format!("R{from_register}..=R{to_register}") Some(format!("R{from_register}..=R{to_register}"))
} }
Operation::LoadBoolean => { Operation::LoadBoolean => {
let to_register = self.destination(); let to_register = self.destination();
let boolean = self.first_argument_as_boolean(); let boolean = self.first_argument_as_boolean();
let skip_display = if self.second_argument_as_boolean() { let jump = self.second_argument_as_boolean();
"IP++" let info = if jump {
jump_offset = Some(1);
format!("R{to_register} = {boolean} && JUMP")
} else { } else {
"" format!("R{to_register} = {boolean}")
}; };
format!("R{to_register} = {boolean} {skip_display}",) Some(info)
} }
Operation::LoadConstant => { Operation::LoadConstant => {
let constant_index = self.first_argument(); let constant_index = self.first_argument();
if let Some(chunk) = chunk { if let Some(chunk) = chunk {
match chunk.get_constant(constant_index, Span(0, 0)) { match chunk.get_constant(constant_index, Span(0, 0)) {
Ok(value) => { Ok(value) => Some(format!(
format!("R{} = C{} {}", self.destination(), constant_index, value) "R{} = C{} {}",
} self.destination(),
Err(error) => { constant_index,
format!("R{} = C{} {:?}", self.destination(), constant_index, error) value
} )),
Err(error) => Some(format!(
"R{} = C{} {:?}",
self.destination(),
constant_index,
error
)),
} }
} else { } else {
format!("R{} = C{}", self.destination(), constant_index) Some(format!("R{} = C{}", self.destination(), constant_index))
} }
} }
Operation::LoadList => { Operation::LoadList => {
@ -367,7 +377,10 @@ impl Instruction {
let first_index = self.first_argument(); let first_index = self.first_argument();
let last_index = destination.saturating_sub(1); let last_index = destination.saturating_sub(1);
format!("R{} = [R{}..=R{}]", destination, first_index, last_index) Some(format!(
"R{} = [R{}..=R{}]",
destination, first_index, last_index
))
} }
Operation::DefineLocal => { Operation::DefineLocal => {
let destination = self.destination(); let destination = self.destination();
@ -386,12 +399,14 @@ impl Instruction {
"" ""
}; };
format!("L{local_index} = R{destination} {mutable_display}{identifier_display}") Some(format!(
"L{local_index} = R{destination} {mutable_display}{identifier_display}"
))
} }
Operation::GetLocal => { Operation::GetLocal => {
let local_index = self.first_argument(); let local_index = self.first_argument();
format!("R{} = L{}", self.destination(), local_index) Some(format!("R{} = L{}", self.destination(), local_index))
} }
Operation::SetLocal => { Operation::SetLocal => {
let local_index = self.first_argument(); let local_index = self.first_argument();
@ -404,49 +419,61 @@ impl Instruction {
"???".to_string() "???".to_string()
}; };
format!( Some(format!(
"L{} = R{} {}", "L{} = R{} {}",
local_index, local_index,
self.destination(), self.destination(),
identifier_display identifier_display
) ))
} }
Operation::Add => { Operation::Add => {
let destination = self.destination(); let destination = self.destination();
let (first_argument, second_argument) = format_arguments(); let (first_argument, second_argument) = format_arguments();
format!("R{destination} = {first_argument} + {second_argument}",) Some(format!(
"R{destination} = {first_argument} + {second_argument}",
))
} }
Operation::Subtract => { Operation::Subtract => {
let destination = self.destination(); let destination = self.destination();
let (first_argument, second_argument) = format_arguments(); let (first_argument, second_argument) = format_arguments();
format!("R{destination} = {first_argument} - {second_argument}",) Some(format!(
"R{destination} = {first_argument} - {second_argument}",
))
} }
Operation::Multiply => { Operation::Multiply => {
let destination = self.destination(); let destination = self.destination();
let (first_argument, second_argument) = format_arguments(); let (first_argument, second_argument) = format_arguments();
format!("R{destination} = {first_argument} * {second_argument}",) Some(format!(
"R{destination} = {first_argument} * {second_argument}",
))
} }
Operation::Divide => { Operation::Divide => {
let destination = self.destination(); let destination = self.destination();
let (first_argument, second_argument) = format_arguments(); let (first_argument, second_argument) = format_arguments();
format!("R{destination} = {first_argument} / {second_argument}",) Some(format!(
"R{destination} = {first_argument} / {second_argument}",
))
} }
Operation::Modulo => { Operation::Modulo => {
let destination = self.destination(); let destination = self.destination();
let (first_argument, second_argument) = format_arguments(); let (first_argument, second_argument) = format_arguments();
format!("R{destination} = {first_argument} % {second_argument}",) Some(format!(
"R{destination} = {first_argument} % {second_argument}",
))
} }
Operation::Test => { Operation::Test => {
let destination = self.destination(); let destination = self.destination();
let test_value = self.second_argument_as_boolean(); let test_value = self.second_argument_as_boolean();
let bang = if test_value { "" } else { "!" }; let bang = if test_value { "" } else { "!" };
format!("if {bang}R{destination} {{ IP++ }}",) jump_offset = Some(1);
Some(format!("if {bang}R{destination}",))
} }
Operation::TestSet => { Operation::TestSet => {
let destination = self.destination(); let destination = self.destination();
@ -454,9 +481,11 @@ impl Instruction {
let test_value = self.second_argument_as_boolean(); let test_value = self.second_argument_as_boolean();
let bang = if test_value { "" } else { "!" }; let bang = if test_value { "" } else { "!" };
format!( jump_offset = Some(1);
"if {bang}R{destination} {{ R{destination} = R{argument} }} else {{ IP++ }}",
) Some(format!(
"if {bang}R{destination} {{ R{destination} = R{argument} }}",
))
} }
Operation::Equal => { Operation::Equal => {
let comparison_symbol = if self.destination_as_boolean() { let comparison_symbol = if self.destination_as_boolean() {
@ -466,8 +495,11 @@ impl Instruction {
}; };
let (first_argument, second_argument) = format_arguments(); let (first_argument, second_argument) = format_arguments();
jump_offset = Some(1);
format!("if {first_argument} {comparison_symbol} {second_argument} IP++",) Some(format!(
"if {first_argument} {comparison_symbol} {second_argument} {{ JUMP }}",
))
} }
Operation::Less => { Operation::Less => {
let comparison_symbol = if self.destination_as_boolean() { let comparison_symbol = if self.destination_as_boolean() {
@ -476,8 +508,11 @@ impl Instruction {
">=" ">="
}; };
let (first_argument, second_argument) = format_arguments(); let (first_argument, second_argument) = format_arguments();
jump_offset = Some(1);
format!("if {first_argument} {comparison_symbol} {second_argument} IP++",) Some(format!(
"if {first_argument} {comparison_symbol} {second_argument}",
))
} }
Operation::LessEqual => { Operation::LessEqual => {
let comparison_symbol = if self.destination_as_boolean() { let comparison_symbol = if self.destination_as_boolean() {
@ -486,8 +521,11 @@ impl Instruction {
">" ">"
}; };
let (first_argument, second_argument) = format_arguments(); let (first_argument, second_argument) = format_arguments();
jump_offset = Some(1);
format!("if {first_argument} {comparison_symbol} {second_argument} IP++",) Some(format!(
"if {first_argument} {comparison_symbol} {second_argument}",
))
} }
Operation::Negate => { Operation::Negate => {
let destination = self.destination(); let destination = self.destination();
@ -497,7 +535,7 @@ impl Instruction {
format!("R{}", self.first_argument()) format!("R{}", self.first_argument())
}; };
format!("R{destination} = -{argument}") Some(format!("R{destination} = -{argument}"))
} }
Operation::Not => { Operation::Not => {
let destination = self.destination(); let destination = self.destination();
@ -507,44 +545,38 @@ impl Instruction {
format!("R{}", self.first_argument()) format!("R{}", self.first_argument())
}; };
format!("R{destination} = !{argument}") Some(format!("R{destination} = !{argument}"))
} }
Operation::Jump => { Operation::Jump => {
let offset = self.first_argument(); let offset = self.first_argument() as isize;
let positive = self.second_argument() != 0; let is_positive = self.second_argument_as_boolean();
if positive { if is_positive {
format!("IP += {}", offset) jump_offset = Some(offset);
} else { } else {
format!("IP -= {}", offset) jump_offset = Some(-offset);
} }
None
} }
Operation::Return => { Operation::Return => {
let from_register = self.destination(); let from_register = self.destination();
let to_register = self.first_argument(); let to_register = self.first_argument();
format!("R{from_register}..=R{to_register}") Some(format!("R{from_register}..=R{to_register}"))
}
Operation::End => {
let return_value = self.destination_as_boolean();
if return_value {
Some("return".to_string())
} else {
Some("null".to_string())
}
} }
}; };
let trucated_length = 30;
let with_elipsis = trucated_length - 3;
let truncated_info = if info.len() > with_elipsis {
format!("{info:.<trucated_length$.with_elipsis$}")
} else {
info
};
Some(truncated_info) (info, jump_offset)
}
}
impl Display for Instruction {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
if let Some(info) = self.disassembly_info(None) {
write!(f, "{} {}", self.operation(), info)
} else {
write!(f, "{}", self.operation())
}
} }
} }
@ -755,4 +787,12 @@ mod tests {
assert_eq!(instruction.destination(), 4); assert_eq!(instruction.destination(), 4);
assert_eq!(instruction.first_argument(), 8); assert_eq!(instruction.first_argument(), 8);
} }
#[test]
fn end() {
let instruction = Instruction::end(true);
assert_eq!(instruction.operation(), Operation::End);
assert!(instruction.destination_as_boolean());
}
} }

View File

@ -29,6 +29,7 @@ const NOT: u8 = 0b0001_0011;
const JUMP: u8 = 0b0001_0100; const JUMP: u8 = 0b0001_0100;
const RETURN: u8 = 0b0001_0101; const RETURN: u8 = 0b0001_0101;
const END: u8 = 0b0001_0110;
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]
pub enum Operation { pub enum Operation {
@ -69,10 +70,11 @@ pub enum Operation {
// Control flow // Control flow
Jump = JUMP as isize, Jump = JUMP as isize,
Return = RETURN as isize, Return = RETURN as isize,
End = END as isize,
} }
impl Operation { impl Operation {
pub fn is_binary(&self) -> bool { pub fn is_math(&self) -> bool {
matches!( matches!(
self, self,
Operation::Add Operation::Add
@ -109,11 +111,12 @@ impl From<u8> for Operation {
NOT => Operation::Not, NOT => Operation::Not,
JUMP => Operation::Jump, JUMP => Operation::Jump,
RETURN => Operation::Return, RETURN => Operation::Return,
END => Operation::End,
_ => { _ => {
if cfg!(test) { if cfg!(test) {
panic!("Invalid operation byte: {}", byte) panic!("Invalid operation byte: {}", byte)
} else { } else {
Operation::Return Operation::End
} }
} }
} }
@ -145,6 +148,7 @@ impl From<Operation> for u8 {
Operation::Not => NOT, Operation::Not => NOT,
Operation::Jump => JUMP, Operation::Jump => JUMP,
Operation::Return => RETURN, Operation::Return => RETURN,
Operation::End => END,
} }
} }
} }
@ -174,6 +178,7 @@ impl Display for Operation {
Operation::Not => write!(f, "NOT"), Operation::Not => write!(f, "NOT"),
Operation::Jump => write!(f, "JUMP"), Operation::Jump => write!(f, "JUMP"),
Operation::Return => write!(f, "RETURN"), Operation::Return => write!(f, "RETURN"),
Operation::End => write!(f, "END"),
} }
} }
} }

View File

@ -375,7 +375,10 @@ impl<'src> Parser<'src> {
let operator_position = self.current_position; let operator_position = self.current_position;
let rule = ParseRule::from(&operator.kind()); let rule = ParseRule::from(&operator.kind());
let mut instruction = match operator.kind() { self.advance()?;
self.parse(rule.precedence.increment())?;
let mut new_instruction = match operator.kind() {
TokenKind::Plus => Instruction::add(self.current_register, left, 0), TokenKind::Plus => Instruction::add(self.current_register, left, 0),
TokenKind::Minus => Instruction::subtract(self.current_register, left, 0), TokenKind::Minus => Instruction::subtract(self.current_register, left, 0),
TokenKind::Star => Instruction::multiply(self.current_register, left, 0), TokenKind::Star => Instruction::multiply(self.current_register, left, 0),
@ -397,33 +400,59 @@ impl<'src> Parser<'src> {
}; };
self.increment_register()?; self.increment_register()?;
self.advance()?;
self.parse(rule.precedence.increment())?;
let (right_instruction, right_position) = let (right_instruction, right_position) =
self.chunk.pop_instruction(self.current_position)?; self.chunk.pop_instruction(self.current_position)?;
let (push_back_right, right_is_constant, right) = let (push_back_right, right_is_constant, right) =
self.handle_binary_argument(&right_instruction)?; self.handle_binary_argument(&right_instruction)?;
instruction.set_second_argument(right); new_instruction.set_second_argument(right);
if left_is_constant { if left_is_constant {
instruction.set_first_argument_to_constant(); new_instruction.set_first_argument_to_constant();
} }
if right_is_constant { if right_is_constant {
instruction.set_second_argument_to_constant(); new_instruction.set_second_argument_to_constant();
} }
if push_back_left { let mut instructions = if !push_back_left && !push_back_right {
self.emit_instruction(left_instruction, left_position); self.emit_instruction(new_instruction, operator_position);
return Ok(());
} else if push_back_right && !push_back_left {
vec![
(right_instruction, right_position),
(new_instruction, operator_position),
]
} else if push_back_left && !push_back_right {
vec![
(left_instruction, left_position),
(new_instruction, operator_position),
]
} else {
vec![
(new_instruction, operator_position),
(left_instruction, left_position),
(right_instruction, right_position),
]
};
while let Some(operation) = self.chunk.get_last_operation() {
if operation.is_math() {
let (instruction, position) = self.chunk.pop_instruction(self.current_position)?;
instructions.push((instruction, position));
} else {
break;
}
} }
if push_back_right { instructions.sort_by_key(|(instruction, _)| instruction.destination());
self.emit_instruction(right_instruction, right_position);
}
self.emit_instruction(instruction, operator_position); for (instruction, position) in instructions {
self.emit_instruction(instruction, position);
}
Ok(()) Ok(())
} }
@ -501,14 +530,8 @@ 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);
} }
}
self.emit_instruction(instruction, operator_position);
self.emit_instruction(Instruction::jump(1, true), operator_position);
self.emit_instruction(
Instruction::r#move(self.current_register, instruction.destination()),
operator_position,
);
} else {
self.emit_instruction(instruction, operator_position); self.emit_instruction(instruction, operator_position);
self.emit_instruction(Instruction::jump(1, true), operator_position); self.emit_instruction(Instruction::jump(1, true), operator_position);
self.emit_instruction( self.emit_instruction(
@ -519,7 +542,6 @@ impl<'src> Parser<'src> {
Instruction::load_boolean(self.current_register, false, false), Instruction::load_boolean(self.current_register, false, false),
operator_position, operator_position,
); );
}
Ok(()) Ok(())
} }
@ -549,13 +571,9 @@ impl<'src> Parser<'src> {
self.advance()?; self.advance()?;
self.parse(rule.precedence.increment())?; self.parse(rule.precedence.increment())?;
let (mut right_instruction, right_position) = let (right_instruction, right_position) =
self.chunk.pop_instruction(self.current_position)?; self.chunk.pop_instruction(self.current_position)?;
if let Operation::LoadBoolean = right_instruction.operation() {
right_instruction.set_second_argument_to_boolean(true);
}
self.emit_instruction(left_instruction, left_position); self.emit_instruction(left_instruction, left_position);
self.emit_instruction(instruction, operator_position); self.emit_instruction(instruction, operator_position);
self.emit_instruction(Instruction::jump(1, true), operator_position); self.emit_instruction(Instruction::jump(1, true), operator_position);
@ -591,7 +609,7 @@ impl<'src> Parser<'src> {
let (mut previous_instruction, previous_position) = let (mut previous_instruction, previous_position) =
self.chunk.pop_instruction(self.current_position)?; self.chunk.pop_instruction(self.current_position)?;
if previous_instruction.operation().is_binary() { if previous_instruction.operation().is_math() {
let previous_register = self let previous_register = self
.chunk .chunk
.get_local(local_index, start_position)? .get_local(local_index, start_position)?
@ -646,13 +664,13 @@ impl<'src> Parser<'src> {
fn parse_block( fn parse_block(
&mut self, &mut self,
_allow_assignment: bool, _allow_assignment: bool,
allow_return: bool, _allow_return: bool,
) -> Result<(), ParseError> { ) -> 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(allow_return)?; self.parse_statement(_allow_return)?;
} }
self.chunk.end_scope(); self.chunk.end_scope();
@ -712,43 +730,20 @@ impl<'src> Parser<'src> {
self.advance()?; self.advance()?;
self.parse_expression()?; self.parse_expression()?;
if self.allow(TokenKind::LeftCurlyBrace)? { if let Token::LeftCurlyBrace = self.current_token {
self.parse_block(allow_assignment, allow_return)?; self.parse_block(allow_assignment, allow_return)?;
} }
let jump_position = self.current_position;
let jump_start = self.current_register;
let jump_index = self.chunk.len();
if self.allow(TokenKind::Else)? { if self.allow(TokenKind::Else)? {
if self.allow(TokenKind::If)? { if let Token::If = self.current_token {
self.parse_if(allow_assignment, allow_return)?; self.parse_if(allow_assignment, allow_return)?;
} }
if self.allow(TokenKind::LeftCurlyBrace)? { if let Token::LeftCurlyBrace = self.current_token {
self.parse_block(allow_assignment, allow_return)?; self.parse_block(allow_assignment, allow_return)?;
} }
} }
if let [Some(Operation::LoadBoolean), Some(Operation::LoadBoolean)] =
self.chunk.get_last_n_operations()
{
// Do not emit a jump if the last two instructions were LoadBoolean operations.
} else if let [Some(Operation::LoadConstant), Some(Operation::LoadConstant)] =
self.chunk.get_last_n_operations()
{
// Do not emit a jump if the last two instructions were LoadConstant operations.
} else {
let jump_end = self.current_register;
let jump_distance = (jump_end - jump_start).max(1);
let jump = Instruction::jump(jump_distance, true);
if jump_distance > 1 {
self.chunk
.insert_instruction(jump_index, jump, jump_position);
}
}
Ok(()) Ok(())
} }
@ -769,19 +764,27 @@ impl<'src> Parser<'src> {
} }
fn parse_statement(&mut self, allow_return: bool) -> Result<(), ParseError> { fn parse_statement(&mut self, allow_return: bool) -> Result<(), ParseError> {
match self.current_token { let is_expression = match self.current_token {
Token::Let => { Token::Let => {
self.parse_let_statement(true, allow_return)?; self.parse_let_statement(true, allow_return)?;
self.allow(TokenKind::Semicolon)?
} }
Token::LeftCurlyBrace => { Token::LeftCurlyBrace => {
self.parse_block(true, true)?; self.parse_block(true, true)?;
!self.allow(TokenKind::Semicolon)?
} }
_ => { _ => {
self.parse_expression()?; self.parse_expression()?;
!self.allow(TokenKind::Semicolon)?
} }
}; };
self.allow(TokenKind::Semicolon)?; if self.current_token == Token::RightCurlyBrace || self.is_eof() {
self.emit_instruction(Instruction::end(is_expression), self.current_position);
}
Ok(()) Ok(())
} }

View File

@ -12,6 +12,7 @@ fn not() {
vec![ vec![
(Instruction::load_boolean(0, true, false), Span(1, 5)), (Instruction::load_boolean(0, true, false), Span(1, 5)),
(Instruction::not(1, 0), Span(0, 1)), (Instruction::not(1, 0), Span(0, 1)),
(Instruction::end(true), Span(5, 5)),
], ],
vec![], vec![],
vec![] vec![]
@ -26,10 +27,13 @@ fn negate() {
assert_eq!( assert_eq!(
parse(source), parse(source),
Ok(Chunk::with_data( Ok(Chunk::with_data(
vec![( vec![
(
*Instruction::negate(0, 0).set_first_argument_to_constant(), *Instruction::negate(0, 0).set_first_argument_to_constant(),
Span(0, 1) Span(0, 1)
),], ),
(Instruction::end(true), Span(5, 5))
],
vec![Value::integer(42)], vec![Value::integer(42)],
vec![] vec![]
)), )),
@ -53,6 +57,7 @@ fn greater_than_or_equal() {
(Instruction::jump(1, true), Span(2, 4)), (Instruction::jump(1, true), Span(2, 4)),
(Instruction::load_boolean(0, true, true), Span(2, 4)), (Instruction::load_boolean(0, true, true), Span(2, 4)),
(Instruction::load_boolean(0, false, false), Span(2, 4)), (Instruction::load_boolean(0, false, false), Span(2, 4)),
(Instruction::end(false), Span(7, 7)),
], ],
vec![Value::integer(1), Value::integer(2)], vec![Value::integer(1), Value::integer(2)],
vec![] vec![]
@ -77,6 +82,7 @@ fn greater() {
(Instruction::jump(1, true), Span(2, 3)), (Instruction::jump(1, true), Span(2, 3)),
(Instruction::load_boolean(0, true, true), Span(2, 3)), (Instruction::load_boolean(0, true, true), Span(2, 3)),
(Instruction::load_boolean(0, false, false), Span(2, 3)), (Instruction::load_boolean(0, false, false), Span(2, 3)),
(Instruction::end(false), Span(6, 6)),
], ],
vec![Value::integer(1), Value::integer(2)], vec![Value::integer(1), Value::integer(2)],
vec![] vec![]
@ -101,6 +107,7 @@ fn less_than_or_equal() {
(Instruction::jump(1, true), Span(2, 4)), (Instruction::jump(1, true), Span(2, 4)),
(Instruction::load_boolean(0, true, true), Span(2, 4)), (Instruction::load_boolean(0, true, true), Span(2, 4)),
(Instruction::load_boolean(0, false, false), Span(2, 4)), (Instruction::load_boolean(0, false, false), Span(2, 4)),
(Instruction::end(false), Span(7, 7)),
], ],
vec![Value::integer(1), Value::integer(2)], vec![Value::integer(1), Value::integer(2)],
vec![] vec![]
@ -125,6 +132,7 @@ fn less_than() {
(Instruction::jump(1, true), Span(2, 3)), (Instruction::jump(1, true), Span(2, 3)),
(Instruction::load_boolean(0, true, true), Span(2, 3)), (Instruction::load_boolean(0, true, true), Span(2, 3)),
(Instruction::load_boolean(0, false, false), Span(2, 3)), (Instruction::load_boolean(0, false, false), Span(2, 3)),
(Instruction::end(false), Span(6, 6)),
], ],
vec![Value::integer(1), Value::integer(2)], vec![Value::integer(1), Value::integer(2)],
vec![] vec![]
@ -149,6 +157,7 @@ fn not_equal() {
(Instruction::jump(1, true), Span(2, 4)), (Instruction::jump(1, true), Span(2, 4)),
(Instruction::load_boolean(0, true, true), Span(2, 4)), (Instruction::load_boolean(0, true, true), Span(2, 4)),
(Instruction::load_boolean(0, false, false), Span(2, 4)), (Instruction::load_boolean(0, false, false), Span(2, 4)),
(Instruction::end(false), Span(7, 7)),
], ],
vec![Value::integer(1), Value::integer(2)], vec![Value::integer(1), Value::integer(2)],
vec![] vec![]
@ -173,6 +182,7 @@ fn equal() {
(Instruction::jump(1, true), Span(2, 4)), (Instruction::jump(1, true), Span(2, 4)),
(Instruction::load_boolean(0, true, true), Span(2, 4)), (Instruction::load_boolean(0, true, true), Span(2, 4)),
(Instruction::load_boolean(0, false, false), Span(2, 4)), (Instruction::load_boolean(0, false, false), Span(2, 4)),
(Instruction::end(true), Span(6, 6)),
], ],
vec![Value::integer(1), Value::integer(2)], vec![Value::integer(1), Value::integer(2)],
vec![] vec![]
@ -198,6 +208,7 @@ fn equality_assignment_long() {
(Instruction::load_boolean(0, true, true), Span(13, 15)), (Instruction::load_boolean(0, true, true), Span(13, 15)),
(Instruction::load_boolean(0, false, false), Span(13, 15)), (Instruction::load_boolean(0, false, false), Span(13, 15)),
(Instruction::define_local(0, 0, false), Span(4, 5)), (Instruction::define_local(0, 0, false), Span(4, 5)),
(Instruction::end(false), Span(42, 42)),
], ],
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))]
@ -223,6 +234,7 @@ fn equality_assignment_short() {
(Instruction::load_boolean(0, true, true), Span(10, 12)), (Instruction::load_boolean(0, true, true), Span(10, 12)),
(Instruction::load_boolean(0, false, false), Span(10, 12)), (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)),
(Instruction::end(false), Span(15, 15)),
], ],
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))]
@ -247,6 +259,7 @@ fn if_expression() {
(Instruction::jump(1, true), Span(5, 7)), (Instruction::jump(1, true), Span(5, 7)),
(Instruction::load_boolean(0, true, true), Span(5, 7)), (Instruction::load_boolean(0, true, true), Span(5, 7)),
(Instruction::load_boolean(0, false, false), Span(5, 7)), (Instruction::load_boolean(0, false, false), Span(5, 7)),
(Instruction::end(true), Span(15, 15)),
], ],
vec![Value::integer(1), Value::integer(1)], vec![Value::integer(1), Value::integer(1)],
vec![] vec![]
@ -271,6 +284,7 @@ fn if_else_expression() {
(Instruction::jump(1, true), Span(5, 7)), (Instruction::jump(1, true), Span(5, 7)),
(Instruction::load_boolean(0, true, true), Span(5, 7)), (Instruction::load_boolean(0, true, true), Span(5, 7)),
(Instruction::load_boolean(0, false, false), Span(5, 7)), (Instruction::load_boolean(0, false, false), Span(5, 7)),
(Instruction::end(true), Span(26, 26)),
], ],
vec![Value::integer(1), Value::integer(1)], vec![Value::integer(1), Value::integer(1)],
vec![] vec![]
@ -295,6 +309,7 @@ fn list_with_expression() {
), ),
(Instruction::load_constant(2, 3), Span(11, 12)), (Instruction::load_constant(2, 3), Span(11, 12)),
(Instruction::load_list(3, 0, 3), Span(0, 13)), (Instruction::load_list(3, 0, 3), Span(0, 13)),
(Instruction::end(true), Span(13, 13)),
], ],
vec![ vec![
Value::integer(1), Value::integer(1),
@ -319,6 +334,7 @@ fn list() {
(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, 0, 3), Span(0, 9)), (Instruction::load_list(3, 0, 3), Span(0, 9)),
(Instruction::end(true), Span(9, 9)),
], ],
vec![Value::integer(1), Value::integer(2), Value::integer(3),], vec![Value::integer(1), Value::integer(2), Value::integer(3),],
vec![] vec![]
@ -350,10 +366,13 @@ fn block_scope() {
(Instruction::define_local(1, 1, false), Span(46, 47)), (Instruction::define_local(1, 1, false), Span(46, 47)),
(Instruction::load_constant(2, 2), Span(92, 93)), (Instruction::load_constant(2, 2), Span(92, 93)),
(Instruction::define_local(2, 2, false), Span(88, 89)), (Instruction::define_local(2, 2, false), Span(88, 89)),
(Instruction::end(false), Span(121, 124)),
(Instruction::load_constant(3, 3), Span(129, 130)), (Instruction::load_constant(3, 3), Span(129, 130)),
(Instruction::define_local(3, 3, false), Span(125, 126)), (Instruction::define_local(3, 3, false), Span(125, 126)),
(Instruction::end(false), Span(150, 159)),
(Instruction::load_constant(4, 4), Span(158, 159)), (Instruction::load_constant(4, 4), Span(158, 159)),
(Instruction::define_local(4, 4, false), Span(154, 155)), (Instruction::define_local(4, 4, false), Span(154, 155)),
(Instruction::end(false), Span(165, 165)),
], ],
vec![ vec![
Value::integer(0), Value::integer(0),
@ -388,6 +407,7 @@ fn set_local() {
(Instruction::define_local(0, 0, true), Span(8, 9)), (Instruction::define_local(0, 0, true), Span(8, 9)),
(Instruction::load_constant(1, 1), Span(20, 22)), (Instruction::load_constant(1, 1), Span(20, 22)),
(Instruction::set_local(1, 0), Span(16, 17)), (Instruction::set_local(1, 0), Span(16, 17)),
(Instruction::end(false), Span(23, 23)),
], ],
vec![Value::integer(41), Value::integer(42)], vec![Value::integer(41), Value::integer(42)],
vec![Local::new(Identifier::new("x"), true, 0, Some(0)),] vec![Local::new(Identifier::new("x"), true, 0, Some(0)),]
@ -411,6 +431,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::end(true), Span(11, 11)),
], ],
vec![Value::integer(1), Value::integer(2), Value::integer(3)], vec![Value::integer(1), Value::integer(2), Value::integer(3)],
vec![] vec![]
@ -424,12 +445,6 @@ fn math_operator_precedence() {
parse("1 + 2 - 3 * 4 / 5"), parse("1 + 2 - 3 * 4 / 5"),
Ok(Chunk::with_data( Ok(Chunk::with_data(
vec![ vec![
(
*Instruction::multiply(2, 2, 3)
.set_first_argument_to_constant()
.set_second_argument_to_constant(),
Span(10, 11)
),
( (
*Instruction::add(0, 0, 1) *Instruction::add(0, 0, 1)
.set_first_argument_to_constant() .set_first_argument_to_constant()
@ -437,10 +452,17 @@ fn math_operator_precedence() {
Span(2, 3) Span(2, 3)
), ),
( (
*Instruction::divide(3, 2, 4).set_second_argument_to_constant(), *Instruction::multiply(1, 2, 3)
.set_first_argument_to_constant()
.set_second_argument_to_constant(),
Span(10, 11)
),
(
*Instruction::divide(2, 1, 4).set_second_argument_to_constant(),
Span(14, 15) Span(14, 15)
), ),
(Instruction::subtract(1, 0, 3), Span(6, 7)), (Instruction::subtract(3, 0, 2), Span(6, 7)),
(Instruction::end(true), Span(17, 17)),
], ],
vec![ vec![
Value::integer(1), Value::integer(1),
@ -455,13 +477,14 @@ fn math_operator_precedence() {
} }
#[test] #[test]
fn declare_local() { fn define_local() {
assert_eq!( assert_eq!(
parse("let x = 42;"), parse("let x = 42;"),
Ok(Chunk::with_data( Ok(Chunk::with_data(
vec![ vec![
(Instruction::load_constant(0, 0), Span(8, 10)), (Instruction::load_constant(0, 0), Span(8, 10)),
(Instruction::define_local(0, 0, false), Span(4, 5)), (Instruction::define_local(0, 0, false), Span(4, 5)),
(Instruction::end(false), Span(11, 11)),
], ],
vec![Value::integer(42)], vec![Value::integer(42)],
vec![Local::new(Identifier::new("x"), false, 0, Some(0))] vec![Local::new(Identifier::new("x"), false, 0, Some(0))]
@ -478,7 +501,8 @@ fn or() {
(Instruction::load_boolean(0, true, false), Span(0, 4)), (Instruction::load_boolean(0, true, false), Span(0, 4)),
(Instruction::test(0, false), Span(5, 7)), (Instruction::test(0, false), Span(5, 7)),
(Instruction::jump(1, true), Span(5, 7)), (Instruction::jump(1, true), Span(5, 7)),
(Instruction::load_boolean(1, false, true), Span(8, 13)), (Instruction::load_boolean(1, false, false), Span(8, 13)),
(Instruction::end(true), Span(13, 13)),
], ],
vec![], vec![],
vec![] vec![]
@ -495,7 +519,8 @@ fn and() {
(Instruction::load_boolean(0, true, false), Span(0, 4)), (Instruction::load_boolean(0, true, false), Span(0, 4)),
(Instruction::test(0, true), Span(5, 7)), (Instruction::test(0, true), Span(5, 7)),
(Instruction::jump(1, true), Span(5, 7)), (Instruction::jump(1, true), Span(5, 7)),
(Instruction::load_boolean(1, false, true), Span(8, 13)), (Instruction::load_boolean(1, false, false), Span(8, 13)),
(Instruction::end(true), Span(13, 13)),
], ],
vec![], vec![],
vec![] vec![]
@ -517,6 +542,7 @@ fn variable_and() {
(Instruction::test(2, true), Span(31, 33)), (Instruction::test(2, true), Span(31, 33)),
(Instruction::jump(1, true), Span(31, 33)), (Instruction::jump(1, true), Span(31, 33)),
(Instruction::get_local(3, 1), Span(34, 35)), (Instruction::get_local(3, 1), Span(34, 35)),
(Instruction::end(true), Span(35, 35)),
], ],
vec![], vec![],
vec![ vec![
@ -532,12 +558,15 @@ fn divide() {
assert_eq!( assert_eq!(
parse("1 / 2"), parse("1 / 2"),
Ok(Chunk::with_data( Ok(Chunk::with_data(
vec![( vec![
(
*Instruction::divide(0, 0, 1) *Instruction::divide(0, 0, 1)
.set_first_argument_to_constant() .set_first_argument_to_constant()
.set_second_argument_to_constant(), .set_second_argument_to_constant(),
Span(2, 3) Span(2, 3)
),], ),
(Instruction::end(true), Span(5, 5)),
],
vec![Value::integer(1), Value::integer(2)], vec![Value::integer(1), Value::integer(2)],
vec![] vec![]
)) ))
@ -549,12 +578,15 @@ fn multiply() {
assert_eq!( assert_eq!(
parse("1 * 2"), parse("1 * 2"),
Ok(Chunk::with_data( Ok(Chunk::with_data(
vec![( vec![
(
*Instruction::multiply(0, 0, 1) *Instruction::multiply(0, 0, 1)
.set_first_argument_to_constant() .set_first_argument_to_constant()
.set_second_argument_to_constant(), .set_second_argument_to_constant(),
Span(2, 3) Span(2, 3)
),], ),
(Instruction::end(true), Span(5, 5)),
],
vec![Value::integer(1), Value::integer(2)], vec![Value::integer(1), Value::integer(2)],
vec![] vec![]
)) ))
@ -566,12 +598,15 @@ fn add() {
assert_eq!( assert_eq!(
parse("1 + 2"), parse("1 + 2"),
Ok(Chunk::with_data( Ok(Chunk::with_data(
vec![( vec![
(
*Instruction::add(0, 0, 1) *Instruction::add(0, 0, 1)
.set_first_argument_to_constant() .set_first_argument_to_constant()
.set_second_argument_to_constant(), .set_second_argument_to_constant(),
Span(2, 3) Span(2, 3)
),], ),
(Instruction::end(true), Span(5, 5)),
],
vec![Value::integer(1), Value::integer(2)], vec![Value::integer(1), Value::integer(2)],
vec![] vec![]
)) ))
@ -583,12 +618,15 @@ fn subtract() {
assert_eq!( assert_eq!(
parse("1 - 2"), parse("1 - 2"),
Ok(Chunk::with_data( Ok(Chunk::with_data(
vec![( vec![
(
*Instruction::subtract(0, 0, 1) *Instruction::subtract(0, 0, 1)
.set_first_argument_to_constant() .set_first_argument_to_constant()
.set_second_argument_to_constant(), .set_second_argument_to_constant(),
Span(2, 3) Span(2, 3)
),], ),
(Instruction::end(true), Span(5, 5)),
],
vec![Value::integer(1), Value::integer(2)], vec![Value::integer(1), Value::integer(2)],
vec![] vec![]
)) ))
@ -600,7 +638,10 @@ fn constant() {
assert_eq!( assert_eq!(
parse("42"), parse("42"),
Ok(Chunk::with_data( Ok(Chunk::with_data(
vec![(Instruction::load_constant(0, 0), Span(0, 2)),], vec![
(Instruction::load_constant(0, 0), Span(0, 2)),
(Instruction::end(true), Span(2, 2)),
],
vec![Value::integer(42)], vec![Value::integer(42)],
vec![] vec![]
)) ))

View File

@ -16,7 +16,6 @@ pub fn run(source: &str) -> Result<Option<Value>, DustError> {
pub struct Vm { pub struct Vm {
chunk: Chunk, chunk: Chunk,
ip: usize, ip: usize,
last_modified: u8,
register_stack: Vec<Option<Value>>, register_stack: Vec<Option<Value>>,
} }
@ -27,7 +26,6 @@ impl Vm {
Self { Self {
chunk, chunk,
ip: 0, ip: 0,
last_modified: 0,
register_stack: Vec::new(), register_stack: Vec::new(),
} }
} }
@ -60,7 +58,11 @@ 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 IP {} {instruction} at {position}", self.ip); log::trace!(
"Running IP {} {} at {position}",
self.ip,
instruction.operation()
);
match instruction.operation() { match instruction.operation() {
Operation::Move => { Operation::Move => {
@ -302,20 +304,25 @@ impl Vm {
return Ok(Some(self.take(start_register, position)?)); return Ok(Some(self.take(start_register, position)?));
} }
} }
Operation::End => {
let returns_value = instruction.destination_as_boolean();
return if returns_value {
Ok(Some(self.pop(position)?))
} else {
Ok(None)
};
}
} }
} }
let final_value = self.take(self.last_modified, Span(0, 0))?; Ok(None)
Ok(Some(final_value))
} }
fn insert(&mut self, value: Value, index: u8, position: Span) -> Result<(), VmError> { fn insert(&mut self, value: Value, index: u8, position: Span) -> Result<(), VmError> {
if self.register_stack.len() == Self::STACK_LIMIT { if self.register_stack.len() == Self::STACK_LIMIT {
Err(VmError::StackOverflow { position }) Err(VmError::StackOverflow { position })
} else { } else {
self.last_modified = index;
let index = index as usize; let index = index as usize;
while index >= self.register_stack.len() { while index >= self.register_stack.len() {

View File

@ -5,7 +5,7 @@ fn if_expression() {
assert_eq!(run("if true { 1 }"), Ok(Some(Value::integer(1)))); assert_eq!(run("if true { 1 }"), Ok(Some(Value::integer(1))));
assert_eq!( assert_eq!(
run("if 42 == 42 { 1 } else { 2 }"), run("if 42 == 42 { 1 } else { 2 }"),
Ok(Some(Value::integer(2))) Ok(Some(Value::integer(1)))
); );
} }