Fix bugs in binary parsing and running
This commit is contained in:
parent
85241c04b9
commit
7afde989f9
@ -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::{instruction, AnnotatedError, Identifier, Instruction, Operation, Span, Value};
|
use crate::{AnnotatedError, Identifier, Instruction, Operation, Span, Value};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Chunk {
|
pub struct Chunk {
|
||||||
@ -76,12 +76,12 @@ impl Chunk {
|
|||||||
.ok_or(ChunkError::InstructionUnderflow { position })
|
.ok_or(ChunkError::InstructionUnderflow { position })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_previous(&self) -> Option<&(Instruction, Span)> {
|
pub fn get_last_instruction(&self) -> Option<&(Instruction, Span)> {
|
||||||
self.instructions.last()
|
self.instructions.last()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_last_operation(&self) -> Option<Operation> {
|
pub fn get_last_operation(&self) -> Option<Operation> {
|
||||||
self.get_previous()
|
self.get_last_instruction()
|
||||||
.map(|(instruction, _)| instruction.operation())
|
.map(|(instruction, _)| instruction.operation())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
use std::fmt::{self, format, Display, Formatter};
|
|
||||||
|
|
||||||
use crate::{Chunk, Operation, Span};
|
use crate::{Chunk, Operation, Span};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
@ -9,8 +7,8 @@ impl Instruction {
|
|||||||
pub fn r#move(to_register: u8, from_register: u8) -> Instruction {
|
pub fn r#move(to_register: u8, from_register: u8) -> Instruction {
|
||||||
let mut instruction = Instruction(Operation::Move as u32);
|
let mut instruction = Instruction(Operation::Move as u32);
|
||||||
|
|
||||||
instruction.set_destination(to_register);
|
instruction.set_a(to_register);
|
||||||
instruction.set_first_argument(from_register);
|
instruction.set_b(from_register);
|
||||||
|
|
||||||
instruction
|
instruction
|
||||||
}
|
}
|
||||||
@ -18,8 +16,8 @@ impl Instruction {
|
|||||||
pub fn close(from_register: u8, to_register: u8) -> Instruction {
|
pub fn close(from_register: u8, to_register: u8) -> Instruction {
|
||||||
let mut instruction = Instruction(Operation::Close as u32);
|
let mut instruction = Instruction(Operation::Close as u32);
|
||||||
|
|
||||||
instruction.set_first_argument(from_register);
|
instruction.set_b(from_register);
|
||||||
instruction.set_second_argument(to_register);
|
instruction.set_c(to_register);
|
||||||
|
|
||||||
instruction
|
instruction
|
||||||
}
|
}
|
||||||
@ -27,9 +25,9 @@ impl Instruction {
|
|||||||
pub fn load_boolean(to_register: u8, value: bool, skip: bool) -> Instruction {
|
pub fn load_boolean(to_register: u8, value: bool, skip: bool) -> Instruction {
|
||||||
let mut instruction = Instruction(Operation::LoadBoolean as u32);
|
let mut instruction = Instruction(Operation::LoadBoolean as u32);
|
||||||
|
|
||||||
instruction.set_destination(to_register);
|
instruction.set_a(to_register);
|
||||||
instruction.set_first_argument(if value { 1 } else { 0 });
|
instruction.set_b(if value { 1 } else { 0 });
|
||||||
instruction.set_second_argument(if skip { 1 } else { 0 });
|
instruction.set_c(if skip { 1 } else { 0 });
|
||||||
|
|
||||||
instruction
|
instruction
|
||||||
}
|
}
|
||||||
@ -37,8 +35,8 @@ impl Instruction {
|
|||||||
pub fn load_constant(to_register: u8, constant_index: u8) -> Instruction {
|
pub fn load_constant(to_register: u8, constant_index: u8) -> Instruction {
|
||||||
let mut instruction = Instruction(Operation::LoadConstant as u32);
|
let mut instruction = Instruction(Operation::LoadConstant as u32);
|
||||||
|
|
||||||
instruction.set_destination(to_register);
|
instruction.set_a(to_register);
|
||||||
instruction.set_first_argument(constant_index);
|
instruction.set_b(constant_index);
|
||||||
|
|
||||||
instruction
|
instruction
|
||||||
}
|
}
|
||||||
@ -46,9 +44,9 @@ impl Instruction {
|
|||||||
pub fn load_list(to_register: u8, start_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_a(to_register);
|
||||||
instruction.set_first_argument(start_register);
|
instruction.set_b(start_register);
|
||||||
instruction.set_second_argument(list_length);
|
instruction.set_c(list_length);
|
||||||
|
|
||||||
instruction
|
instruction
|
||||||
}
|
}
|
||||||
@ -56,9 +54,9 @@ impl Instruction {
|
|||||||
pub fn define_local(to_register: u8, local_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_a(to_register);
|
||||||
instruction.set_first_argument(local_index);
|
instruction.set_b(local_index);
|
||||||
instruction.set_second_argument(if is_mutable { 1 } else { 0 });
|
instruction.set_c(if is_mutable { 1 } else { 0 });
|
||||||
|
|
||||||
instruction
|
instruction
|
||||||
}
|
}
|
||||||
@ -66,8 +64,8 @@ impl Instruction {
|
|||||||
pub fn get_local(to_register: u8, local_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_a(to_register);
|
||||||
instruction.set_first_argument(local_index);
|
instruction.set_b(local_index);
|
||||||
|
|
||||||
instruction
|
instruction
|
||||||
}
|
}
|
||||||
@ -75,8 +73,8 @@ impl Instruction {
|
|||||||
pub fn set_local(from_register: u8, local_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_a(from_register);
|
||||||
instruction.set_first_argument(local_index);
|
instruction.set_b(local_index);
|
||||||
|
|
||||||
instruction
|
instruction
|
||||||
}
|
}
|
||||||
@ -84,9 +82,9 @@ impl Instruction {
|
|||||||
pub fn add(to_register: u8, left_index: u8, right_index: u8) -> Instruction {
|
pub fn add(to_register: u8, left_index: u8, right_index: u8) -> Instruction {
|
||||||
let mut instruction = Instruction(Operation::Add as u32);
|
let mut instruction = Instruction(Operation::Add as u32);
|
||||||
|
|
||||||
instruction.set_destination(to_register);
|
instruction.set_a(to_register);
|
||||||
instruction.set_first_argument(left_index);
|
instruction.set_b(left_index);
|
||||||
instruction.set_second_argument(right_index);
|
instruction.set_c(right_index);
|
||||||
|
|
||||||
instruction
|
instruction
|
||||||
}
|
}
|
||||||
@ -94,9 +92,9 @@ impl Instruction {
|
|||||||
pub fn subtract(to_register: u8, left_index: u8, right_index: u8) -> Instruction {
|
pub fn subtract(to_register: u8, left_index: u8, right_index: u8) -> Instruction {
|
||||||
let mut instruction = Instruction(Operation::Subtract as u32);
|
let mut instruction = Instruction(Operation::Subtract as u32);
|
||||||
|
|
||||||
instruction.set_destination(to_register);
|
instruction.set_a(to_register);
|
||||||
instruction.set_first_argument(left_index);
|
instruction.set_b(left_index);
|
||||||
instruction.set_second_argument(right_index);
|
instruction.set_c(right_index);
|
||||||
|
|
||||||
instruction
|
instruction
|
||||||
}
|
}
|
||||||
@ -104,9 +102,9 @@ impl Instruction {
|
|||||||
pub fn multiply(to_register: u8, left_index: u8, right_index: u8) -> Instruction {
|
pub fn multiply(to_register: u8, left_index: u8, right_index: u8) -> Instruction {
|
||||||
let mut instruction = Instruction(Operation::Multiply as u32);
|
let mut instruction = Instruction(Operation::Multiply as u32);
|
||||||
|
|
||||||
instruction.set_destination(to_register);
|
instruction.set_a(to_register);
|
||||||
instruction.set_first_argument(left_index);
|
instruction.set_b(left_index);
|
||||||
instruction.set_second_argument(right_index);
|
instruction.set_c(right_index);
|
||||||
|
|
||||||
instruction
|
instruction
|
||||||
}
|
}
|
||||||
@ -114,9 +112,9 @@ impl Instruction {
|
|||||||
pub fn divide(to_register: u8, left_index: u8, right_index: u8) -> Instruction {
|
pub fn divide(to_register: u8, left_index: u8, right_index: u8) -> Instruction {
|
||||||
let mut instruction = Instruction(Operation::Divide as u32);
|
let mut instruction = Instruction(Operation::Divide as u32);
|
||||||
|
|
||||||
instruction.set_destination(to_register);
|
instruction.set_a(to_register);
|
||||||
instruction.set_first_argument(left_index);
|
instruction.set_b(left_index);
|
||||||
instruction.set_second_argument(right_index);
|
instruction.set_c(right_index);
|
||||||
|
|
||||||
instruction
|
instruction
|
||||||
}
|
}
|
||||||
@ -124,9 +122,9 @@ impl Instruction {
|
|||||||
pub fn modulo(to_register: u8, left_index: u8, right_index: u8) -> Instruction {
|
pub fn modulo(to_register: u8, left_index: u8, right_index: u8) -> Instruction {
|
||||||
let mut instruction = Instruction(Operation::Modulo as u32);
|
let mut instruction = Instruction(Operation::Modulo as u32);
|
||||||
|
|
||||||
instruction.set_destination(to_register);
|
instruction.set_a(to_register);
|
||||||
instruction.set_first_argument(left_index);
|
instruction.set_b(left_index);
|
||||||
instruction.set_second_argument(right_index);
|
instruction.set_c(right_index);
|
||||||
|
|
||||||
instruction
|
instruction
|
||||||
}
|
}
|
||||||
@ -134,8 +132,8 @@ impl Instruction {
|
|||||||
pub fn test(to_register: u8, test_value: bool) -> Instruction {
|
pub fn test(to_register: u8, test_value: bool) -> Instruction {
|
||||||
let mut instruction = Instruction(Operation::Test as u32);
|
let mut instruction = Instruction(Operation::Test as u32);
|
||||||
|
|
||||||
instruction.set_destination(to_register);
|
instruction.set_a(to_register);
|
||||||
instruction.set_second_argument_to_boolean(test_value);
|
instruction.set_c_to_boolean(test_value);
|
||||||
|
|
||||||
instruction
|
instruction
|
||||||
}
|
}
|
||||||
@ -143,9 +141,9 @@ impl Instruction {
|
|||||||
pub fn test_set(to_register: u8, argument_index: u8, test_value: bool) -> Instruction {
|
pub fn test_set(to_register: u8, argument_index: u8, test_value: bool) -> Instruction {
|
||||||
let mut instruction = Instruction(Operation::TestSet as u32);
|
let mut instruction = Instruction(Operation::TestSet as u32);
|
||||||
|
|
||||||
instruction.set_destination(to_register);
|
instruction.set_a(to_register);
|
||||||
instruction.set_first_argument(argument_index);
|
instruction.set_b(argument_index);
|
||||||
instruction.set_second_argument_to_boolean(test_value);
|
instruction.set_c_to_boolean(test_value);
|
||||||
|
|
||||||
instruction
|
instruction
|
||||||
}
|
}
|
||||||
@ -153,9 +151,9 @@ impl Instruction {
|
|||||||
pub fn equal(comparison_boolean: bool, left_index: u8, right_index: u8) -> Instruction {
|
pub fn equal(comparison_boolean: bool, left_index: u8, right_index: u8) -> Instruction {
|
||||||
let mut instruction = Instruction(Operation::Equal as u32);
|
let mut instruction = Instruction(Operation::Equal as u32);
|
||||||
|
|
||||||
instruction.set_destination(if comparison_boolean { 1 } else { 0 });
|
instruction.set_a(if comparison_boolean { 1 } else { 0 });
|
||||||
instruction.set_first_argument(left_index);
|
instruction.set_b(left_index);
|
||||||
instruction.set_second_argument(right_index);
|
instruction.set_c(right_index);
|
||||||
|
|
||||||
instruction
|
instruction
|
||||||
}
|
}
|
||||||
@ -163,9 +161,9 @@ impl Instruction {
|
|||||||
pub fn less(comparison_boolean: bool, left_index: u8, right_index: u8) -> Instruction {
|
pub fn less(comparison_boolean: bool, left_index: u8, right_index: u8) -> Instruction {
|
||||||
let mut instruction = Instruction(Operation::Less as u32);
|
let mut instruction = Instruction(Operation::Less as u32);
|
||||||
|
|
||||||
instruction.set_destination(if comparison_boolean { 1 } else { 0 });
|
instruction.set_a(if comparison_boolean { 1 } else { 0 });
|
||||||
instruction.set_first_argument(left_index);
|
instruction.set_b(left_index);
|
||||||
instruction.set_second_argument(right_index);
|
instruction.set_c(right_index);
|
||||||
|
|
||||||
instruction
|
instruction
|
||||||
}
|
}
|
||||||
@ -173,9 +171,9 @@ impl Instruction {
|
|||||||
pub fn less_equal(comparison_boolean: bool, left_index: u8, right_index: u8) -> Instruction {
|
pub fn less_equal(comparison_boolean: bool, left_index: u8, right_index: u8) -> Instruction {
|
||||||
let mut instruction = Instruction(Operation::LessEqual as u32);
|
let mut instruction = Instruction(Operation::LessEqual as u32);
|
||||||
|
|
||||||
instruction.set_destination(if comparison_boolean { 1 } else { 0 });
|
instruction.set_a(if comparison_boolean { 1 } else { 0 });
|
||||||
instruction.set_first_argument(left_index);
|
instruction.set_b(left_index);
|
||||||
instruction.set_second_argument(right_index);
|
instruction.set_c(right_index);
|
||||||
|
|
||||||
instruction
|
instruction
|
||||||
}
|
}
|
||||||
@ -183,8 +181,8 @@ impl Instruction {
|
|||||||
pub fn negate(to_register: u8, from_index: u8) -> Instruction {
|
pub fn negate(to_register: u8, from_index: u8) -> Instruction {
|
||||||
let mut instruction = Instruction(Operation::Negate as u32);
|
let mut instruction = Instruction(Operation::Negate as u32);
|
||||||
|
|
||||||
instruction.set_destination(to_register);
|
instruction.set_a(to_register);
|
||||||
instruction.set_first_argument(from_index);
|
instruction.set_b(from_index);
|
||||||
|
|
||||||
instruction
|
instruction
|
||||||
}
|
}
|
||||||
@ -192,8 +190,8 @@ impl Instruction {
|
|||||||
pub fn not(to_register: u8, from_index: u8) -> Instruction {
|
pub fn not(to_register: u8, from_index: u8) -> Instruction {
|
||||||
let mut instruction = Instruction(Operation::Not as u32);
|
let mut instruction = Instruction(Operation::Not as u32);
|
||||||
|
|
||||||
instruction.set_destination(to_register);
|
instruction.set_a(to_register);
|
||||||
instruction.set_first_argument(from_index);
|
instruction.set_b(from_index);
|
||||||
|
|
||||||
instruction
|
instruction
|
||||||
}
|
}
|
||||||
@ -201,8 +199,8 @@ impl Instruction {
|
|||||||
pub fn jump(offset: u8, is_positive: bool) -> Instruction {
|
pub fn jump(offset: u8, is_positive: bool) -> Instruction {
|
||||||
let mut instruction = Instruction(Operation::Jump as u32);
|
let mut instruction = Instruction(Operation::Jump as u32);
|
||||||
|
|
||||||
instruction.set_first_argument(offset);
|
instruction.set_b(offset);
|
||||||
instruction.set_second_argument(if is_positive { 1 } else { 0 });
|
instruction.set_c(if is_positive { 1 } else { 0 });
|
||||||
|
|
||||||
instruction
|
instruction
|
||||||
}
|
}
|
||||||
@ -210,8 +208,8 @@ impl Instruction {
|
|||||||
pub fn r#return(from_register: u8, to_register: u8) -> Instruction {
|
pub fn r#return(from_register: u8, to_register: u8) -> Instruction {
|
||||||
let mut instruction = Instruction(Operation::Return as u32);
|
let mut instruction = Instruction(Operation::Return as u32);
|
||||||
|
|
||||||
instruction.set_destination(from_register);
|
instruction.set_a(from_register);
|
||||||
instruction.set_first_argument(to_register);
|
instruction.set_b(to_register);
|
||||||
|
|
||||||
instruction
|
instruction
|
||||||
}
|
}
|
||||||
@ -219,7 +217,7 @@ impl Instruction {
|
|||||||
pub fn end(returns_value: bool) -> Instruction {
|
pub fn end(returns_value: bool) -> Instruction {
|
||||||
let mut instruction = Instruction(Operation::End as u32);
|
let mut instruction = Instruction(Operation::End as u32);
|
||||||
|
|
||||||
instruction.set_destination_to_boolean(returns_value);
|
instruction.set_a_to_boolean(returns_value);
|
||||||
|
|
||||||
instruction
|
instruction
|
||||||
}
|
}
|
||||||
@ -232,92 +230,102 @@ impl Instruction {
|
|||||||
self.0 |= u8::from(operation) as u32;
|
self.0 |= u8::from(operation) as u32;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn destination(&self) -> u8 {
|
pub fn data(&self) -> (Operation, u8, u8, u8, bool, bool) {
|
||||||
|
(
|
||||||
|
self.operation(),
|
||||||
|
self.a(),
|
||||||
|
self.b(),
|
||||||
|
self.c(),
|
||||||
|
self.b_is_constant(),
|
||||||
|
self.c_is_constant(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn a(&self) -> u8 {
|
||||||
(self.0 >> 24) as u8
|
(self.0 >> 24) as u8
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn destination_as_boolean(&self) -> bool {
|
pub fn b(&self) -> u8 {
|
||||||
(self.0 >> 24) != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_destination_to_boolean(&mut self, boolean: bool) -> &mut Self {
|
|
||||||
self.set_destination(if boolean { 1 } else { 0 });
|
|
||||||
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_destination(&mut self, destination: u8) {
|
|
||||||
self.0 &= 0x00FFFFFF;
|
|
||||||
self.0 |= (destination as u32) << 24;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn first_argument(&self) -> u8 {
|
|
||||||
(self.0 >> 16) as u8
|
(self.0 >> 16) as u8
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn first_argument_is_constant(&self) -> bool {
|
pub fn c(&self) -> u8 {
|
||||||
self.0 & 0b1000_0000 != 0
|
(self.0 >> 8) as u8
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn first_argument_as_boolean(&self) -> bool {
|
pub fn a_as_boolean(&self) -> bool {
|
||||||
self.first_argument() != 0
|
self.a() != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_first_argument_to_boolean(&mut self, boolean: bool) -> &mut Self {
|
pub fn b_as_boolean(&self) -> bool {
|
||||||
self.set_first_argument(if boolean { 1 } else { 0 });
|
self.b() != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn c_as_boolean(&self) -> bool {
|
||||||
|
self.c() != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_a_to_boolean(&mut self, boolean: bool) -> &mut Self {
|
||||||
|
self.set_a(if boolean { 1 } else { 0 });
|
||||||
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_first_argument_to_constant(&mut self) -> &mut Self {
|
pub fn set_b_to_boolean(&mut self, boolean: bool) -> &mut Self {
|
||||||
|
self.set_b(if boolean { 1 } else { 0 });
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_c_to_boolean(&mut self, boolean: bool) -> &mut Self {
|
||||||
|
self.set_c(if boolean { 1 } else { 0 });
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_a(&mut self, destination: u8) {
|
||||||
|
self.0 |= (destination as u32) << 24;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_b(&mut self, argument: u8) {
|
||||||
|
self.0 |= (argument as u32) << 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_c(&mut self, argument: u8) {
|
||||||
|
self.0 |= (argument as u32) << 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn b_is_constant(&self) -> bool {
|
||||||
|
self.0 & 0b1000_0000 != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn c_is_constant(&self) -> bool {
|
||||||
|
self.0 & 0b0100_0000 != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_b_is_constant(&mut self) -> &mut Self {
|
||||||
self.0 |= 0b1000_0000;
|
self.0 |= 0b1000_0000;
|
||||||
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_first_argument(&mut self, argument: u8) {
|
pub fn set_c_is_constant(&mut self) -> &mut Self {
|
||||||
self.0 |= (argument as u32) << 16;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn second_argument(&self) -> u8 {
|
|
||||||
(self.0 >> 8) as u8
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn second_argument_is_constant(&self) -> bool {
|
|
||||||
self.0 & 0b0100_0000 != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn second_argument_as_boolean(&self) -> bool {
|
|
||||||
self.second_argument() != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_second_argument_to_boolean(&mut self, boolean: bool) -> &mut Self {
|
|
||||||
self.set_second_argument(if boolean { 1 } else { 0 });
|
|
||||||
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_second_argument_to_constant(&mut self) -> &mut Self {
|
|
||||||
self.0 |= 0b0100_0000;
|
self.0 |= 0b0100_0000;
|
||||||
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_second_argument(&mut self, argument: u8) {
|
|
||||||
self.0 |= (argument as u32) << 8;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn disassembly_info(&self, chunk: Option<&Chunk>) -> (Option<String>, Option<isize>) {
|
pub fn disassembly_info(&self, chunk: Option<&Chunk>) -> (Option<String>, Option<isize>) {
|
||||||
let format_arguments = || {
|
let format_arguments = || {
|
||||||
let first_argument = if self.first_argument_is_constant() {
|
let first_argument = if self.b_is_constant() {
|
||||||
format!("C{}", self.first_argument())
|
format!("C{}", self.b())
|
||||||
} else {
|
} else {
|
||||||
format!("R{}", self.first_argument())
|
format!("R{}", self.b())
|
||||||
};
|
};
|
||||||
let second_argument = if self.second_argument_is_constant() {
|
let second_argument = if self.c_is_constant() {
|
||||||
format!("C{}", self.second_argument())
|
format!("C{}", self.c())
|
||||||
} else {
|
} else {
|
||||||
format!("R{}", self.second_argument())
|
format!("R{}", self.c())
|
||||||
};
|
};
|
||||||
|
|
||||||
(first_argument, second_argument)
|
(first_argument, second_argument)
|
||||||
@ -325,21 +333,17 @@ impl Instruction {
|
|||||||
let mut jump_offset = None;
|
let mut jump_offset = None;
|
||||||
|
|
||||||
let info = match self.operation() {
|
let info = match self.operation() {
|
||||||
Operation::Move => Some(format!(
|
Operation::Move => Some(format!("R{} = R{}", self.a(), self.b())),
|
||||||
"R{} = R{}",
|
|
||||||
self.destination(),
|
|
||||||
self.first_argument()
|
|
||||||
)),
|
|
||||||
Operation::Close => {
|
Operation::Close => {
|
||||||
let from_register = self.first_argument();
|
let from_register = self.b();
|
||||||
let to_register = self.second_argument().saturating_sub(1);
|
let to_register = self.c().saturating_sub(1);
|
||||||
|
|
||||||
Some(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.a();
|
||||||
let boolean = self.first_argument_as_boolean();
|
let boolean = self.b_as_boolean();
|
||||||
let jump = self.second_argument_as_boolean();
|
let jump = self.c_as_boolean();
|
||||||
let info = if jump {
|
let info = if jump {
|
||||||
jump_offset = Some(1);
|
jump_offset = Some(1);
|
||||||
|
|
||||||
@ -351,30 +355,22 @@ impl Instruction {
|
|||||||
Some(info)
|
Some(info)
|
||||||
}
|
}
|
||||||
Operation::LoadConstant => {
|
Operation::LoadConstant => {
|
||||||
let constant_index = self.first_argument();
|
let constant_index = self.b();
|
||||||
|
|
||||||
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) => Some(format!(
|
Ok(value) => Some(format!("R{} = C{} {}", self.a(), constant_index, value)),
|
||||||
"R{} = C{} {}",
|
Err(error) => {
|
||||||
self.destination(),
|
Some(format!("R{} = C{} {:?}", self.a(), constant_index, error))
|
||||||
constant_index,
|
}
|
||||||
value
|
|
||||||
)),
|
|
||||||
Err(error) => Some(format!(
|
|
||||||
"R{} = C{} {:?}",
|
|
||||||
self.destination(),
|
|
||||||
constant_index,
|
|
||||||
error
|
|
||||||
)),
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Some(format!("R{} = C{}", self.destination(), constant_index))
|
Some(format!("R{} = C{}", self.a(), constant_index))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Operation::LoadList => {
|
Operation::LoadList => {
|
||||||
let destination = self.destination();
|
let destination = self.a();
|
||||||
let first_index = self.first_argument();
|
let first_index = self.b();
|
||||||
let last_index = destination.saturating_sub(1);
|
let last_index = destination.saturating_sub(1);
|
||||||
|
|
||||||
Some(format!(
|
Some(format!(
|
||||||
@ -383,8 +379,8 @@ impl Instruction {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
Operation::DefineLocal => {
|
Operation::DefineLocal => {
|
||||||
let destination = self.destination();
|
let destination = self.a();
|
||||||
let local_index = self.first_argument();
|
let local_index = self.b();
|
||||||
let identifier_display = if let Some(chunk) = chunk {
|
let identifier_display = if let Some(chunk) = chunk {
|
||||||
match chunk.get_identifier(local_index) {
|
match chunk.get_identifier(local_index) {
|
||||||
Some(identifier) => identifier.to_string(),
|
Some(identifier) => identifier.to_string(),
|
||||||
@ -393,23 +389,19 @@ impl Instruction {
|
|||||||
} else {
|
} else {
|
||||||
"???".to_string()
|
"???".to_string()
|
||||||
};
|
};
|
||||||
let mutable_display = if self.second_argument_as_boolean() {
|
let mutable_display = if self.c_as_boolean() { "mut " } else { "" };
|
||||||
"mut "
|
|
||||||
} else {
|
|
||||||
""
|
|
||||||
};
|
|
||||||
|
|
||||||
Some(format!(
|
Some(format!(
|
||||||
"L{local_index} = R{destination} {mutable_display}{identifier_display}"
|
"L{local_index} = R{destination} {mutable_display}{identifier_display}"
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
Operation::GetLocal => {
|
Operation::GetLocal => {
|
||||||
let local_index = self.first_argument();
|
let local_index = self.b();
|
||||||
|
|
||||||
Some(format!("R{} = L{}", self.destination(), local_index))
|
Some(format!("R{} = L{}", self.a(), local_index))
|
||||||
}
|
}
|
||||||
Operation::SetLocal => {
|
Operation::SetLocal => {
|
||||||
let local_index = self.first_argument();
|
let local_index = self.b();
|
||||||
let identifier_display = if let Some(chunk) = chunk {
|
let identifier_display = if let Some(chunk) = chunk {
|
||||||
match chunk.get_identifier(local_index) {
|
match chunk.get_identifier(local_index) {
|
||||||
Some(identifier) => identifier.to_string(),
|
Some(identifier) => identifier.to_string(),
|
||||||
@ -422,12 +414,12 @@ impl Instruction {
|
|||||||
Some(format!(
|
Some(format!(
|
||||||
"L{} = R{} {}",
|
"L{} = R{} {}",
|
||||||
local_index,
|
local_index,
|
||||||
self.destination(),
|
self.a(),
|
||||||
identifier_display
|
identifier_display
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
Operation::Add => {
|
Operation::Add => {
|
||||||
let destination = self.destination();
|
let destination = self.a();
|
||||||
let (first_argument, second_argument) = format_arguments();
|
let (first_argument, second_argument) = format_arguments();
|
||||||
|
|
||||||
Some(format!(
|
Some(format!(
|
||||||
@ -435,7 +427,7 @@ impl Instruction {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
Operation::Subtract => {
|
Operation::Subtract => {
|
||||||
let destination = self.destination();
|
let destination = self.a();
|
||||||
let (first_argument, second_argument) = format_arguments();
|
let (first_argument, second_argument) = format_arguments();
|
||||||
|
|
||||||
Some(format!(
|
Some(format!(
|
||||||
@ -443,7 +435,7 @@ impl Instruction {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
Operation::Multiply => {
|
Operation::Multiply => {
|
||||||
let destination = self.destination();
|
let destination = self.a();
|
||||||
let (first_argument, second_argument) = format_arguments();
|
let (first_argument, second_argument) = format_arguments();
|
||||||
|
|
||||||
Some(format!(
|
Some(format!(
|
||||||
@ -451,7 +443,7 @@ impl Instruction {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
Operation::Divide => {
|
Operation::Divide => {
|
||||||
let destination = self.destination();
|
let destination = self.a();
|
||||||
let (first_argument, second_argument) = format_arguments();
|
let (first_argument, second_argument) = format_arguments();
|
||||||
|
|
||||||
Some(format!(
|
Some(format!(
|
||||||
@ -459,7 +451,7 @@ impl Instruction {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
Operation::Modulo => {
|
Operation::Modulo => {
|
||||||
let destination = self.destination();
|
let destination = self.a();
|
||||||
let (first_argument, second_argument) = format_arguments();
|
let (first_argument, second_argument) = format_arguments();
|
||||||
|
|
||||||
Some(format!(
|
Some(format!(
|
||||||
@ -467,18 +459,17 @@ impl Instruction {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
Operation::Test => {
|
Operation::Test => {
|
||||||
let destination = self.destination();
|
let destination = self.a();
|
||||||
let test_value = self.second_argument_as_boolean();
|
let test_value = self.c_as_boolean();
|
||||||
let bang = if test_value { "" } else { "!" };
|
|
||||||
|
|
||||||
jump_offset = Some(1);
|
jump_offset = Some(1);
|
||||||
|
|
||||||
Some(format!("if {bang}R{destination}",))
|
Some(format!("if R{destination} != {test_value} {{ JUMP }}",))
|
||||||
}
|
}
|
||||||
Operation::TestSet => {
|
Operation::TestSet => {
|
||||||
let destination = self.destination();
|
let destination = self.a();
|
||||||
let argument = format!("R{}", self.first_argument());
|
let argument = format!("R{}", self.b());
|
||||||
let test_value = self.second_argument_as_boolean();
|
let test_value = self.c_as_boolean();
|
||||||
let bang = if test_value { "" } else { "!" };
|
let bang = if test_value { "" } else { "!" };
|
||||||
|
|
||||||
jump_offset = Some(1);
|
jump_offset = Some(1);
|
||||||
@ -488,11 +479,7 @@ impl Instruction {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
Operation::Equal => {
|
Operation::Equal => {
|
||||||
let comparison_symbol = if self.destination_as_boolean() {
|
let comparison_symbol = if self.a_as_boolean() { "==" } else { "!=" };
|
||||||
"=="
|
|
||||||
} else {
|
|
||||||
"!="
|
|
||||||
};
|
|
||||||
|
|
||||||
let (first_argument, second_argument) = format_arguments();
|
let (first_argument, second_argument) = format_arguments();
|
||||||
jump_offset = Some(1);
|
jump_offset = Some(1);
|
||||||
@ -502,11 +489,7 @@ impl Instruction {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
Operation::Less => {
|
Operation::Less => {
|
||||||
let comparison_symbol = if self.destination_as_boolean() {
|
let comparison_symbol = if self.a_as_boolean() { "<" } else { ">=" };
|
||||||
"<"
|
|
||||||
} else {
|
|
||||||
">="
|
|
||||||
};
|
|
||||||
let (first_argument, second_argument) = format_arguments();
|
let (first_argument, second_argument) = format_arguments();
|
||||||
jump_offset = Some(1);
|
jump_offset = Some(1);
|
||||||
|
|
||||||
@ -515,11 +498,7 @@ impl Instruction {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
Operation::LessEqual => {
|
Operation::LessEqual => {
|
||||||
let comparison_symbol = if self.destination_as_boolean() {
|
let comparison_symbol = if self.a_as_boolean() { "<=" } else { ">" };
|
||||||
"<="
|
|
||||||
} else {
|
|
||||||
">"
|
|
||||||
};
|
|
||||||
let (first_argument, second_argument) = format_arguments();
|
let (first_argument, second_argument) = format_arguments();
|
||||||
jump_offset = Some(1);
|
jump_offset = Some(1);
|
||||||
|
|
||||||
@ -528,28 +507,28 @@ impl Instruction {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
Operation::Negate => {
|
Operation::Negate => {
|
||||||
let destination = self.destination();
|
let destination = self.a();
|
||||||
let argument = if self.first_argument_is_constant() {
|
let argument = if self.b_is_constant() {
|
||||||
format!("C{}", self.first_argument())
|
format!("C{}", self.b())
|
||||||
} else {
|
} else {
|
||||||
format!("R{}", self.first_argument())
|
format!("R{}", self.b())
|
||||||
};
|
};
|
||||||
|
|
||||||
Some(format!("R{destination} = -{argument}"))
|
Some(format!("R{destination} = -{argument}"))
|
||||||
}
|
}
|
||||||
Operation::Not => {
|
Operation::Not => {
|
||||||
let destination = self.destination();
|
let destination = self.a();
|
||||||
let argument = if self.first_argument_is_constant() {
|
let argument = if self.b_is_constant() {
|
||||||
format!("C{}", self.first_argument())
|
format!("C{}", self.b())
|
||||||
} else {
|
} else {
|
||||||
format!("R{}", self.first_argument())
|
format!("R{}", self.b())
|
||||||
};
|
};
|
||||||
|
|
||||||
Some(format!("R{destination} = !{argument}"))
|
Some(format!("R{destination} = !{argument}"))
|
||||||
}
|
}
|
||||||
Operation::Jump => {
|
Operation::Jump => {
|
||||||
let offset = self.first_argument() as isize;
|
let offset = self.b() as isize;
|
||||||
let is_positive = self.second_argument_as_boolean();
|
let is_positive = self.c_as_boolean();
|
||||||
|
|
||||||
if is_positive {
|
if is_positive {
|
||||||
jump_offset = Some(offset);
|
jump_offset = Some(offset);
|
||||||
@ -560,13 +539,13 @@ impl Instruction {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
Operation::Return => {
|
Operation::Return => {
|
||||||
let from_register = self.destination();
|
let from_register = self.a();
|
||||||
let to_register = self.first_argument();
|
let to_register = self.b();
|
||||||
|
|
||||||
Some(format!("R{from_register}..=R{to_register}"))
|
Some(format!("R{from_register}..=R{to_register}"))
|
||||||
}
|
}
|
||||||
Operation::End => {
|
Operation::End => {
|
||||||
let return_value = self.destination_as_boolean();
|
let return_value = self.a_as_boolean();
|
||||||
|
|
||||||
if return_value {
|
if return_value {
|
||||||
Some("return".to_string())
|
Some("return".to_string())
|
||||||
@ -594,14 +573,14 @@ mod tests {
|
|||||||
fn r#move() {
|
fn r#move() {
|
||||||
let mut instruction = Instruction::r#move(0, 1);
|
let mut instruction = Instruction::r#move(0, 1);
|
||||||
|
|
||||||
instruction.set_first_argument_to_constant();
|
instruction.set_b_is_constant();
|
||||||
instruction.set_second_argument_to_constant();
|
instruction.set_c_is_constant();
|
||||||
|
|
||||||
assert_eq!(instruction.operation(), Operation::Move);
|
assert_eq!(instruction.operation(), Operation::Move);
|
||||||
assert_eq!(instruction.destination(), 0);
|
assert_eq!(instruction.a(), 0);
|
||||||
assert_eq!(instruction.first_argument(), 1);
|
assert_eq!(instruction.b(), 1);
|
||||||
assert!(instruction.first_argument_is_constant());
|
assert!(instruction.b_is_constant());
|
||||||
assert!(instruction.second_argument_is_constant());
|
assert!(instruction.b_is_constant());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -609,8 +588,8 @@ mod tests {
|
|||||||
let instruction = Instruction::close(1, 2);
|
let instruction = Instruction::close(1, 2);
|
||||||
|
|
||||||
assert_eq!(instruction.operation(), Operation::Close);
|
assert_eq!(instruction.operation(), Operation::Close);
|
||||||
assert_eq!(instruction.first_argument(), 1);
|
assert_eq!(instruction.b(), 1);
|
||||||
assert_eq!(instruction.second_argument(), 2);
|
assert_eq!(instruction.c(), 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -618,94 +597,94 @@ mod tests {
|
|||||||
let instruction = Instruction::load_boolean(4, true, true);
|
let instruction = Instruction::load_boolean(4, true, true);
|
||||||
|
|
||||||
assert_eq!(instruction.operation(), Operation::LoadBoolean);
|
assert_eq!(instruction.operation(), Operation::LoadBoolean);
|
||||||
assert_eq!(instruction.destination(), 4);
|
assert_eq!(instruction.a(), 4);
|
||||||
assert!(instruction.first_argument_as_boolean());
|
assert!(instruction.a_as_boolean());
|
||||||
assert!(instruction.second_argument_as_boolean());
|
assert!(instruction.c_as_boolean());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn load_constant() {
|
fn load_constant() {
|
||||||
let mut instruction = Instruction::load_constant(0, 1);
|
let mut instruction = Instruction::load_constant(0, 1);
|
||||||
|
|
||||||
instruction.set_first_argument_to_constant();
|
instruction.set_b_is_constant();
|
||||||
instruction.set_second_argument_to_constant();
|
instruction.set_c_is_constant();
|
||||||
|
|
||||||
assert_eq!(instruction.operation(), Operation::LoadConstant);
|
assert_eq!(instruction.operation(), Operation::LoadConstant);
|
||||||
assert_eq!(instruction.destination(), 0);
|
assert_eq!(instruction.a(), 0);
|
||||||
assert_eq!(instruction.first_argument(), 1);
|
assert_eq!(instruction.b(), 1);
|
||||||
assert!(instruction.first_argument_is_constant());
|
assert!(instruction.b_is_constant());
|
||||||
assert!(instruction.second_argument_is_constant());
|
assert!(instruction.b_is_constant());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn declare_local() {
|
fn declare_local() {
|
||||||
let mut instruction = Instruction::define_local(0, 1, true);
|
let mut instruction = Instruction::define_local(0, 1, true);
|
||||||
|
|
||||||
instruction.set_first_argument_to_constant();
|
instruction.set_b_is_constant();
|
||||||
|
|
||||||
assert_eq!(instruction.operation(), Operation::DefineLocal);
|
assert_eq!(instruction.operation(), Operation::DefineLocal);
|
||||||
assert_eq!(instruction.destination(), 0);
|
assert_eq!(instruction.a(), 0);
|
||||||
assert_eq!(instruction.first_argument(), 1);
|
assert_eq!(instruction.b(), 1);
|
||||||
assert_eq!(instruction.second_argument(), true as u8);
|
assert_eq!(instruction.c(), true as u8);
|
||||||
assert!(instruction.first_argument_is_constant());
|
assert!(instruction.b_is_constant());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn add() {
|
fn add() {
|
||||||
let mut instruction = Instruction::add(1, 1, 0);
|
let mut instruction = Instruction::add(1, 1, 0);
|
||||||
|
|
||||||
instruction.set_first_argument_to_constant();
|
instruction.set_b_is_constant();
|
||||||
|
|
||||||
assert_eq!(instruction.operation(), Operation::Add);
|
assert_eq!(instruction.operation(), Operation::Add);
|
||||||
assert_eq!(instruction.destination(), 1);
|
assert_eq!(instruction.a(), 1);
|
||||||
assert_eq!(instruction.first_argument(), 1);
|
assert_eq!(instruction.b(), 1);
|
||||||
assert_eq!(instruction.second_argument(), 0);
|
assert_eq!(instruction.c(), 0);
|
||||||
assert!(instruction.first_argument_is_constant());
|
assert!(instruction.b_is_constant());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn subtract() {
|
fn subtract() {
|
||||||
let mut instruction = Instruction::subtract(0, 1, 2);
|
let mut instruction = Instruction::subtract(0, 1, 2);
|
||||||
|
|
||||||
instruction.set_first_argument_to_constant();
|
instruction.set_b_is_constant();
|
||||||
instruction.set_second_argument_to_constant();
|
instruction.set_c_is_constant();
|
||||||
|
|
||||||
assert_eq!(instruction.operation(), Operation::Subtract);
|
assert_eq!(instruction.operation(), Operation::Subtract);
|
||||||
assert_eq!(instruction.destination(), 0);
|
assert_eq!(instruction.a(), 0);
|
||||||
assert_eq!(instruction.first_argument(), 1);
|
assert_eq!(instruction.b(), 1);
|
||||||
assert_eq!(instruction.second_argument(), 2);
|
assert_eq!(instruction.c(), 2);
|
||||||
assert!(instruction.first_argument_is_constant());
|
assert!(instruction.b_is_constant());
|
||||||
assert!(instruction.second_argument_is_constant());
|
assert!(instruction.b_is_constant());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn multiply() {
|
fn multiply() {
|
||||||
let mut instruction = Instruction::multiply(0, 1, 2);
|
let mut instruction = Instruction::multiply(0, 1, 2);
|
||||||
|
|
||||||
instruction.set_first_argument_to_constant();
|
instruction.set_b_is_constant();
|
||||||
instruction.set_second_argument_to_constant();
|
instruction.set_c_is_constant();
|
||||||
|
|
||||||
assert_eq!(instruction.operation(), Operation::Multiply);
|
assert_eq!(instruction.operation(), Operation::Multiply);
|
||||||
assert_eq!(instruction.destination(), 0);
|
assert_eq!(instruction.a(), 0);
|
||||||
assert_eq!(instruction.first_argument(), 1);
|
assert_eq!(instruction.b(), 1);
|
||||||
assert_eq!(instruction.second_argument(), 2);
|
assert_eq!(instruction.c(), 2);
|
||||||
assert!(instruction.first_argument_is_constant());
|
assert!(instruction.b_is_constant());
|
||||||
assert!(instruction.second_argument_is_constant());
|
assert!(instruction.b_is_constant());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn divide() {
|
fn divide() {
|
||||||
let mut instruction = Instruction::divide(0, 1, 2);
|
let mut instruction = Instruction::divide(0, 1, 2);
|
||||||
|
|
||||||
instruction.set_first_argument_to_constant();
|
instruction.set_b_is_constant();
|
||||||
instruction.set_second_argument_to_constant();
|
instruction.set_c_is_constant();
|
||||||
|
|
||||||
assert_eq!(instruction.operation(), Operation::Divide);
|
assert_eq!(instruction.operation(), Operation::Divide);
|
||||||
assert_eq!(instruction.destination(), 0);
|
assert_eq!(instruction.a(), 0);
|
||||||
assert_eq!(instruction.first_argument(), 1);
|
assert_eq!(instruction.b(), 1);
|
||||||
assert_eq!(instruction.second_argument(), 2);
|
assert_eq!(instruction.c(), 2);
|
||||||
assert!(instruction.first_argument_is_constant());
|
assert!(instruction.b_is_constant());
|
||||||
assert!(instruction.second_argument_is_constant());
|
assert!(instruction.b_is_constant());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -713,8 +692,8 @@ mod tests {
|
|||||||
let instruction = Instruction::test(4, true);
|
let instruction = Instruction::test(4, true);
|
||||||
|
|
||||||
assert_eq!(instruction.operation(), Operation::Test);
|
assert_eq!(instruction.operation(), Operation::Test);
|
||||||
assert_eq!(instruction.destination(), 4);
|
assert_eq!(instruction.a(), 4);
|
||||||
assert!(instruction.second_argument_as_boolean());
|
assert!(instruction.c_as_boolean());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -722,52 +701,52 @@ mod tests {
|
|||||||
let instruction = Instruction::test_set(4, 1, true);
|
let instruction = Instruction::test_set(4, 1, true);
|
||||||
|
|
||||||
assert_eq!(instruction.operation(), Operation::TestSet);
|
assert_eq!(instruction.operation(), Operation::TestSet);
|
||||||
assert_eq!(instruction.destination(), 4);
|
assert_eq!(instruction.a(), 4);
|
||||||
assert_eq!(instruction.first_argument(), 1);
|
assert_eq!(instruction.b(), 1);
|
||||||
assert!(instruction.second_argument_as_boolean());
|
assert!(instruction.c_as_boolean());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn equal() {
|
fn equal() {
|
||||||
let mut instruction = Instruction::equal(true, 1, 2);
|
let mut instruction = Instruction::equal(true, 1, 2);
|
||||||
|
|
||||||
instruction.set_first_argument_to_constant();
|
instruction.set_b_is_constant();
|
||||||
instruction.set_second_argument_to_constant();
|
instruction.set_c_is_constant();
|
||||||
|
|
||||||
assert_eq!(instruction.operation(), Operation::Equal);
|
assert_eq!(instruction.operation(), Operation::Equal);
|
||||||
assert!(instruction.destination_as_boolean());
|
assert!(instruction.a_as_boolean());
|
||||||
assert_eq!(instruction.first_argument(), 1);
|
assert_eq!(instruction.b(), 1);
|
||||||
assert_eq!(instruction.second_argument(), 2);
|
assert_eq!(instruction.c(), 2);
|
||||||
assert!(instruction.first_argument_is_constant());
|
assert!(instruction.b_is_constant());
|
||||||
assert!(instruction.second_argument_is_constant());
|
assert!(instruction.b_is_constant());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn negate() {
|
fn negate() {
|
||||||
let mut instruction = Instruction::negate(0, 1);
|
let mut instruction = Instruction::negate(0, 1);
|
||||||
|
|
||||||
instruction.set_first_argument_to_constant();
|
instruction.set_b_is_constant();
|
||||||
instruction.set_second_argument_to_constant();
|
instruction.set_c_is_constant();
|
||||||
|
|
||||||
assert_eq!(instruction.operation(), Operation::Negate);
|
assert_eq!(instruction.operation(), Operation::Negate);
|
||||||
assert_eq!(instruction.destination(), 0);
|
assert_eq!(instruction.a(), 0);
|
||||||
assert_eq!(instruction.first_argument(), 1);
|
assert_eq!(instruction.b(), 1);
|
||||||
assert!(instruction.first_argument_is_constant());
|
assert!(instruction.b_is_constant());
|
||||||
assert!(instruction.second_argument_is_constant());
|
assert!(instruction.b_is_constant());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn not() {
|
fn not() {
|
||||||
let mut instruction = Instruction::not(0, 1);
|
let mut instruction = Instruction::not(0, 1);
|
||||||
|
|
||||||
instruction.set_first_argument_to_constant();
|
instruction.set_b_is_constant();
|
||||||
instruction.set_second_argument_to_constant();
|
instruction.set_c_is_constant();
|
||||||
|
|
||||||
assert_eq!(instruction.operation(), Operation::Not);
|
assert_eq!(instruction.operation(), Operation::Not);
|
||||||
assert_eq!(instruction.destination(), 0);
|
assert_eq!(instruction.a(), 0);
|
||||||
assert_eq!(instruction.first_argument(), 1);
|
assert_eq!(instruction.b(), 1);
|
||||||
assert!(instruction.first_argument_is_constant());
|
assert!(instruction.b_is_constant());
|
||||||
assert!(instruction.second_argument_is_constant());
|
assert!(instruction.b_is_constant());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -775,8 +754,8 @@ mod tests {
|
|||||||
let instruction = Instruction::jump(4, true);
|
let instruction = Instruction::jump(4, true);
|
||||||
|
|
||||||
assert_eq!(instruction.operation(), Operation::Jump);
|
assert_eq!(instruction.operation(), Operation::Jump);
|
||||||
assert_eq!(instruction.first_argument(), 4);
|
assert_eq!(instruction.b(), 4);
|
||||||
assert!(instruction.first_argument_as_boolean());
|
assert!(instruction.c_as_boolean());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -784,8 +763,8 @@ mod tests {
|
|||||||
let instruction = Instruction::r#return(4, 8);
|
let instruction = Instruction::r#return(4, 8);
|
||||||
|
|
||||||
assert_eq!(instruction.operation(), Operation::Return);
|
assert_eq!(instruction.operation(), Operation::Return);
|
||||||
assert_eq!(instruction.destination(), 4);
|
assert_eq!(instruction.a(), 4);
|
||||||
assert_eq!(instruction.first_argument(), 8);
|
assert_eq!(instruction.b(), 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -793,6 +772,6 @@ mod tests {
|
|||||||
let instruction = Instruction::end(true);
|
let instruction = Instruction::end(true);
|
||||||
|
|
||||||
assert_eq!(instruction.operation(), Operation::End);
|
assert_eq!(instruction.operation(), Operation::End);
|
||||||
assert!(instruction.destination_as_boolean());
|
assert!(instruction.a_as_boolean());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -293,16 +293,16 @@ impl<'src> Parser<'src> {
|
|||||||
|
|
||||||
let (push_back, is_constant, argument) = {
|
let (push_back, is_constant, argument) = {
|
||||||
match previous_instruction.operation() {
|
match previous_instruction.operation() {
|
||||||
Operation::GetLocal => (false, false, previous_instruction.destination()),
|
Operation::GetLocal => (false, false, previous_instruction.a()),
|
||||||
Operation::LoadConstant => (false, true, previous_instruction.first_argument()),
|
Operation::LoadConstant => (false, true, previous_instruction.a()),
|
||||||
Operation::LoadBoolean => (true, false, previous_instruction.destination()),
|
Operation::LoadBoolean => (true, false, previous_instruction.a()),
|
||||||
Operation::Close => {
|
Operation::Close => {
|
||||||
return Err(ParseError::ExpectedExpression {
|
return Err(ParseError::ExpectedExpression {
|
||||||
found: self.previous_token.to_owned(),
|
found: self.previous_token.to_owned(),
|
||||||
position: self.previous_position,
|
position: self.previous_position,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
_ => (true, false, previous_instruction.destination()),
|
_ => (true, false, previous_instruction.a()),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -323,7 +323,7 @@ impl<'src> Parser<'src> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if is_constant {
|
if is_constant {
|
||||||
instruction.set_first_argument_to_constant();
|
instruction.set_b_is_constant();
|
||||||
}
|
}
|
||||||
|
|
||||||
if push_back {
|
if push_back {
|
||||||
@ -342,13 +342,13 @@ impl<'src> Parser<'src> {
|
|||||||
let mut push_back = false;
|
let mut push_back = false;
|
||||||
let mut is_constant = false;
|
let mut is_constant = false;
|
||||||
let argument = match instruction.operation() {
|
let argument = match instruction.operation() {
|
||||||
Operation::GetLocal => instruction.destination(),
|
Operation::GetLocal => instruction.a(),
|
||||||
Operation::LoadConstant => {
|
Operation::LoadConstant => {
|
||||||
is_constant = true;
|
is_constant = true;
|
||||||
|
|
||||||
instruction.first_argument()
|
instruction.b()
|
||||||
}
|
}
|
||||||
Operation::LoadBoolean => instruction.destination(),
|
Operation::LoadBoolean => instruction.a(),
|
||||||
Operation::Close => {
|
Operation::Close => {
|
||||||
return Err(ParseError::ExpectedExpression {
|
return Err(ParseError::ExpectedExpression {
|
||||||
found: self.previous_token.to_owned(),
|
found: self.previous_token.to_owned(),
|
||||||
@ -358,7 +358,7 @@ impl<'src> Parser<'src> {
|
|||||||
_ => {
|
_ => {
|
||||||
push_back = true;
|
push_back = true;
|
||||||
|
|
||||||
instruction.destination()
|
instruction.a()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -406,14 +406,14 @@ impl<'src> Parser<'src> {
|
|||||||
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)?;
|
||||||
|
|
||||||
new_instruction.set_second_argument(right);
|
new_instruction.set_c(right);
|
||||||
|
|
||||||
if left_is_constant {
|
if left_is_constant {
|
||||||
new_instruction.set_first_argument_to_constant();
|
new_instruction.set_b_is_constant();
|
||||||
}
|
}
|
||||||
|
|
||||||
if right_is_constant {
|
if right_is_constant {
|
||||||
new_instruction.set_second_argument_to_constant();
|
new_instruction.set_c_is_constant();
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut instructions = if !push_back_left && !push_back_right {
|
let mut instructions = if !push_back_left && !push_back_right {
|
||||||
@ -448,9 +448,9 @@ impl<'src> Parser<'src> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
instructions.sort_by_key(|(instruction, _)| instruction.destination());
|
instructions.sort_by_key(|(instruction, _)| instruction.a());
|
||||||
|
|
||||||
for (instruction, position) in instructions {
|
for (mut instruction, position) in instructions {
|
||||||
self.emit_instruction(instruction, position);
|
self.emit_instruction(instruction, position);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -512,14 +512,14 @@ impl<'src> Parser<'src> {
|
|||||||
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);
|
instruction.set_c(right);
|
||||||
|
|
||||||
if left_is_constant {
|
if left_is_constant {
|
||||||
instruction.set_first_argument_to_constant();
|
instruction.set_b_is_constant();
|
||||||
}
|
}
|
||||||
|
|
||||||
if right_is_constant {
|
if right_is_constant {
|
||||||
instruction.set_second_argument_to_constant();
|
instruction.set_c_is_constant();
|
||||||
}
|
}
|
||||||
|
|
||||||
if push_back_left || push_back_right {
|
if push_back_left || push_back_right {
|
||||||
@ -555,10 +555,9 @@ 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 test_register = left_instruction.destination();
|
|
||||||
let instruction = match operator.kind() {
|
let instruction = match operator.kind() {
|
||||||
TokenKind::DoubleAmpersand => Instruction::test(test_register, true),
|
TokenKind::DoubleAmpersand => Instruction::test(left_instruction.a(), false),
|
||||||
TokenKind::DoublePipe => Instruction::test(test_register, false),
|
TokenKind::DoublePipe => Instruction::test(left_instruction.a(), true),
|
||||||
_ => {
|
_ => {
|
||||||
return Err(ParseError::ExpectedTokenMultiple {
|
return Err(ParseError::ExpectedTokenMultiple {
|
||||||
expected: &[TokenKind::DoubleAmpersand, TokenKind::DoublePipe],
|
expected: &[TokenKind::DoubleAmpersand, TokenKind::DoublePipe],
|
||||||
@ -619,7 +618,7 @@ impl<'src> Parser<'src> {
|
|||||||
if let Some(register_index) = previous_register {
|
if let Some(register_index) = previous_register {
|
||||||
log::trace!("Condensing SET_LOCAL to binary expression");
|
log::trace!("Condensing SET_LOCAL to binary expression");
|
||||||
|
|
||||||
previous_instruction.set_destination(register_index);
|
previous_instruction.set_a(register_index);
|
||||||
self.emit_instruction(previous_instruction, self.current_position);
|
self.emit_instruction(previous_instruction, self.current_position);
|
||||||
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
@ -747,11 +746,11 @@ impl<'src> Parser<'src> {
|
|||||||
self.current_position
|
self.current_position
|
||||||
};
|
};
|
||||||
|
|
||||||
if let [Some(Operation::LoadBoolean)] = self.chunk.get_last_n_operations() {
|
if let Some(Operation::LoadBoolean) = self.chunk.get_last_operation() {
|
||||||
let (mut load_boolean, load_boolean_position) =
|
let (mut load_boolean, load_boolean_position) =
|
||||||
self.chunk.pop_instruction(self.current_position)?;
|
self.chunk.pop_instruction(self.current_position)?;
|
||||||
|
|
||||||
load_boolean.set_second_argument_to_boolean(true);
|
load_boolean.set_c_to_boolean(true);
|
||||||
|
|
||||||
self.emit_instruction(load_boolean, load_boolean_position);
|
self.emit_instruction(load_boolean, load_boolean_position);
|
||||||
self.increment_register()?;
|
self.increment_register()?;
|
||||||
@ -764,7 +763,7 @@ impl<'src> Parser<'src> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let jump_end = self.chunk.len();
|
let jump_end = self.chunk.len();
|
||||||
let jump_distance = jump_end - jump_start;
|
let jump_distance = jump_end.saturating_sub(jump_start);
|
||||||
|
|
||||||
self.chunk.insert_instruction(
|
self.chunk.insert_instruction(
|
||||||
jump_start,
|
jump_start,
|
||||||
@ -859,15 +858,14 @@ impl<'src> Parser<'src> {
|
|||||||
self.parse_expression()?;
|
self.parse_expression()?;
|
||||||
self.increment_register()?;
|
self.increment_register()?;
|
||||||
|
|
||||||
let (previous_instruction, previous_position) =
|
let (previous_instruction, previous_position) = *self
|
||||||
*self
|
.chunk
|
||||||
.chunk
|
.get_last_instruction()
|
||||||
.get_previous()
|
.ok_or_else(|| ParseError::ExpectedExpression {
|
||||||
.ok_or_else(|| ParseError::ExpectedExpression {
|
found: self.current_token.to_owned(),
|
||||||
found: self.current_token.to_owned(),
|
position,
|
||||||
position,
|
})?;
|
||||||
})?;
|
let register = previous_instruction.a();
|
||||||
let register = previous_instruction.destination();
|
|
||||||
let local_index =
|
let local_index =
|
||||||
self.chunk
|
self.chunk
|
||||||
.declare_local(identifier, is_mutable, register, previous_position)?;
|
.declare_local(identifier, is_mutable, register, previous_position)?;
|
||||||
|
@ -28,10 +28,7 @@ fn negate() {
|
|||||||
parse(source),
|
parse(source),
|
||||||
Ok(Chunk::with_data(
|
Ok(Chunk::with_data(
|
||||||
vec![
|
vec![
|
||||||
(
|
(*Instruction::negate(0, 0).set_b_is_constant(), Span(0, 1)),
|
||||||
*Instruction::negate(0, 0).set_first_argument_to_constant(),
|
|
||||||
Span(0, 1)
|
|
||||||
),
|
|
||||||
(Instruction::end(true), Span(5, 5))
|
(Instruction::end(true), Span(5, 5))
|
||||||
],
|
],
|
||||||
vec![Value::integer(42)],
|
vec![Value::integer(42)],
|
||||||
@ -50,8 +47,8 @@ fn greater_than_or_equal() {
|
|||||||
vec![
|
vec![
|
||||||
(
|
(
|
||||||
*Instruction::less(false, 0, 1)
|
*Instruction::less(false, 0, 1)
|
||||||
.set_first_argument_to_constant()
|
.set_b_is_constant()
|
||||||
.set_second_argument_to_constant(),
|
.set_c_is_constant(),
|
||||||
Span(2, 4)
|
Span(2, 4)
|
||||||
),
|
),
|
||||||
(Instruction::jump(1, true), Span(2, 4)),
|
(Instruction::jump(1, true), Span(2, 4)),
|
||||||
@ -75,8 +72,8 @@ fn greater() {
|
|||||||
vec![
|
vec![
|
||||||
(
|
(
|
||||||
*Instruction::less_equal(false, 0, 1)
|
*Instruction::less_equal(false, 0, 1)
|
||||||
.set_first_argument_to_constant()
|
.set_b_is_constant()
|
||||||
.set_second_argument_to_constant(),
|
.set_c_is_constant(),
|
||||||
Span(2, 3)
|
Span(2, 3)
|
||||||
),
|
),
|
||||||
(Instruction::jump(1, true), Span(2, 3)),
|
(Instruction::jump(1, true), Span(2, 3)),
|
||||||
@ -100,8 +97,8 @@ fn less_than_or_equal() {
|
|||||||
vec![
|
vec![
|
||||||
(
|
(
|
||||||
*Instruction::less_equal(true, 0, 1)
|
*Instruction::less_equal(true, 0, 1)
|
||||||
.set_first_argument_to_constant()
|
.set_b_is_constant()
|
||||||
.set_second_argument_to_constant(),
|
.set_c_is_constant(),
|
||||||
Span(2, 4)
|
Span(2, 4)
|
||||||
),
|
),
|
||||||
(Instruction::jump(1, true), Span(2, 4)),
|
(Instruction::jump(1, true), Span(2, 4)),
|
||||||
@ -125,8 +122,8 @@ fn less_than() {
|
|||||||
vec![
|
vec![
|
||||||
(
|
(
|
||||||
*Instruction::less(true, 0, 1)
|
*Instruction::less(true, 0, 1)
|
||||||
.set_first_argument_to_constant()
|
.set_b_is_constant()
|
||||||
.set_second_argument_to_constant(),
|
.set_c_is_constant(),
|
||||||
Span(2, 3)
|
Span(2, 3)
|
||||||
),
|
),
|
||||||
(Instruction::jump(1, true), Span(2, 3)),
|
(Instruction::jump(1, true), Span(2, 3)),
|
||||||
@ -150,8 +147,8 @@ fn not_equal() {
|
|||||||
vec![
|
vec![
|
||||||
(
|
(
|
||||||
*Instruction::equal(false, 0, 1)
|
*Instruction::equal(false, 0, 1)
|
||||||
.set_first_argument_to_constant()
|
.set_b_is_constant()
|
||||||
.set_second_argument_to_constant(),
|
.set_c_is_constant(),
|
||||||
Span(2, 4)
|
Span(2, 4)
|
||||||
),
|
),
|
||||||
(Instruction::jump(1, true), Span(2, 4)),
|
(Instruction::jump(1, true), Span(2, 4)),
|
||||||
@ -175,8 +172,8 @@ fn equal() {
|
|||||||
vec![
|
vec![
|
||||||
(
|
(
|
||||||
*Instruction::equal(true, 0, 1)
|
*Instruction::equal(true, 0, 1)
|
||||||
.set_first_argument_to_constant()
|
.set_b_is_constant()
|
||||||
.set_second_argument_to_constant(),
|
.set_c_is_constant(),
|
||||||
Span(2, 4)
|
Span(2, 4)
|
||||||
),
|
),
|
||||||
(Instruction::jump(1, true), Span(2, 4)),
|
(Instruction::jump(1, true), Span(2, 4)),
|
||||||
@ -200,8 +197,8 @@ fn equality_assignment_long() {
|
|||||||
vec![
|
vec![
|
||||||
(
|
(
|
||||||
*Instruction::equal(true, 0, 1)
|
*Instruction::equal(true, 0, 1)
|
||||||
.set_first_argument_to_constant()
|
.set_b_is_constant()
|
||||||
.set_second_argument_to_constant(),
|
.set_c_is_constant(),
|
||||||
Span(13, 15)
|
Span(13, 15)
|
||||||
),
|
),
|
||||||
(Instruction::jump(2, true), Span(13, 15)),
|
(Instruction::jump(2, true), Span(13, 15)),
|
||||||
@ -228,8 +225,8 @@ fn equality_assignment_short() {
|
|||||||
vec![
|
vec![
|
||||||
(
|
(
|
||||||
*Instruction::equal(true, 0, 1)
|
*Instruction::equal(true, 0, 1)
|
||||||
.set_first_argument_to_constant()
|
.set_b_is_constant()
|
||||||
.set_second_argument_to_constant(),
|
.set_c_is_constant(),
|
||||||
Span(10, 12)
|
Span(10, 12)
|
||||||
),
|
),
|
||||||
(Instruction::jump(1, true), Span(10, 12)),
|
(Instruction::jump(1, true), Span(10, 12)),
|
||||||
@ -254,8 +251,8 @@ fn if_expression() {
|
|||||||
vec![
|
vec![
|
||||||
(
|
(
|
||||||
*Instruction::equal(true, 0, 1)
|
*Instruction::equal(true, 0, 1)
|
||||||
.set_first_argument_to_constant()
|
.set_b_is_constant()
|
||||||
.set_second_argument_to_constant(),
|
.set_c_is_constant(),
|
||||||
Span(5, 7)
|
Span(5, 7)
|
||||||
),
|
),
|
||||||
(Instruction::jump(2, true), Span(5, 7)),
|
(Instruction::jump(2, true), Span(5, 7)),
|
||||||
@ -279,8 +276,8 @@ fn if_else_expression() {
|
|||||||
vec![
|
vec![
|
||||||
(
|
(
|
||||||
*Instruction::equal(true, 0, 1)
|
*Instruction::equal(true, 0, 1)
|
||||||
.set_first_argument_to_constant()
|
.set_b_is_constant()
|
||||||
.set_second_argument_to_constant(),
|
.set_c_is_constant(),
|
||||||
Span(5, 7)
|
Span(5, 7)
|
||||||
),
|
),
|
||||||
(Instruction::jump(2, true), Span(5, 7)),
|
(Instruction::jump(2, true), Span(5, 7)),
|
||||||
@ -312,14 +309,14 @@ fn list_with_complex_expression() {
|
|||||||
(Instruction::load_constant(0, 0), Span(1, 2)),
|
(Instruction::load_constant(0, 0), Span(1, 2)),
|
||||||
(
|
(
|
||||||
*Instruction::add(1, 1, 2)
|
*Instruction::add(1, 1, 2)
|
||||||
.set_first_argument_to_constant()
|
.set_b_is_constant()
|
||||||
.set_second_argument_to_constant(),
|
.set_c_is_constant(),
|
||||||
Span(6, 7)
|
Span(6, 7)
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
*Instruction::multiply(2, 3, 4)
|
*Instruction::multiply(2, 3, 4)
|
||||||
.set_first_argument_to_constant()
|
.set_b_is_constant()
|
||||||
.set_second_argument_to_constant(),
|
.set_c_is_constant(),
|
||||||
Span(14, 15)
|
Span(14, 15)
|
||||||
),
|
),
|
||||||
(Instruction::subtract(3, 1, 2), Span(10, 11)),
|
(Instruction::subtract(3, 1, 2), Span(10, 11)),
|
||||||
@ -350,8 +347,8 @@ fn list_with_simple_expression() {
|
|||||||
(Instruction::load_constant(0, 0), Span(1, 2)),
|
(Instruction::load_constant(0, 0), Span(1, 2)),
|
||||||
(
|
(
|
||||||
*Instruction::add(1, 1, 2)
|
*Instruction::add(1, 1, 2)
|
||||||
.set_first_argument_to_constant()
|
.set_b_is_constant()
|
||||||
.set_second_argument_to_constant(),
|
.set_c_is_constant(),
|
||||||
Span(6, 7)
|
Span(6, 7)
|
||||||
),
|
),
|
||||||
(Instruction::load_constant(2, 3), Span(11, 12)),
|
(Instruction::load_constant(2, 3), Span(11, 12)),
|
||||||
@ -470,12 +467,12 @@ fn parentheses_precedence() {
|
|||||||
vec![
|
vec![
|
||||||
(
|
(
|
||||||
*Instruction::add(0, 0, 1)
|
*Instruction::add(0, 0, 1)
|
||||||
.set_first_argument_to_constant()
|
.set_b_is_constant()
|
||||||
.set_second_argument_to_constant(),
|
.set_c_is_constant(),
|
||||||
Span(3, 4)
|
Span(3, 4)
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
*Instruction::multiply(1, 0, 2).set_second_argument_to_constant(),
|
*Instruction::multiply(1, 0, 2).set_c_is_constant(),
|
||||||
Span(8, 9)
|
Span(8, 9)
|
||||||
),
|
),
|
||||||
(Instruction::end(true), Span(11, 11)),
|
(Instruction::end(true), Span(11, 11)),
|
||||||
@ -494,18 +491,18 @@ fn math_operator_precedence() {
|
|||||||
vec![
|
vec![
|
||||||
(
|
(
|
||||||
*Instruction::add(0, 0, 1)
|
*Instruction::add(0, 0, 1)
|
||||||
.set_first_argument_to_constant()
|
.set_b_is_constant()
|
||||||
.set_second_argument_to_constant(),
|
.set_c_is_constant(),
|
||||||
Span(2, 3)
|
Span(2, 3)
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
*Instruction::multiply(1, 2, 3)
|
*Instruction::multiply(1, 2, 3)
|
||||||
.set_first_argument_to_constant()
|
.set_b_is_constant()
|
||||||
.set_second_argument_to_constant(),
|
.set_c_is_constant(),
|
||||||
Span(10, 11)
|
Span(10, 11)
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
*Instruction::divide(2, 1, 4).set_second_argument_to_constant(),
|
*Instruction::divide(2, 1, 4).set_c_is_constant(),
|
||||||
Span(14, 15)
|
Span(14, 15)
|
||||||
),
|
),
|
||||||
(Instruction::subtract(3, 0, 2), Span(6, 7)),
|
(Instruction::subtract(3, 0, 2), Span(6, 7)),
|
||||||
@ -546,7 +543,7 @@ fn or() {
|
|||||||
Ok(Chunk::with_data(
|
Ok(Chunk::with_data(
|
||||||
vec![
|
vec![
|
||||||
(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, true), Span(5, 7)),
|
||||||
(Instruction::jump(1, true), Span(5, 7)),
|
(Instruction::jump(1, true), Span(5, 7)),
|
||||||
(Instruction::load_boolean(1, false, false), Span(8, 13)),
|
(Instruction::load_boolean(1, false, false), Span(8, 13)),
|
||||||
(Instruction::end(true), Span(13, 13)),
|
(Instruction::end(true), Span(13, 13)),
|
||||||
@ -564,7 +561,7 @@ fn and() {
|
|||||||
Ok(Chunk::with_data(
|
Ok(Chunk::with_data(
|
||||||
vec![
|
vec![
|
||||||
(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, false), Span(5, 7)),
|
||||||
(Instruction::jump(1, true), Span(5, 7)),
|
(Instruction::jump(1, true), Span(5, 7)),
|
||||||
(Instruction::load_boolean(1, false, false), Span(8, 13)),
|
(Instruction::load_boolean(1, false, false), Span(8, 13)),
|
||||||
(Instruction::end(true), Span(13, 13)),
|
(Instruction::end(true), Span(13, 13)),
|
||||||
@ -586,7 +583,7 @@ fn variable_and() {
|
|||||||
(Instruction::load_boolean(1, false, false), Span(22, 27)),
|
(Instruction::load_boolean(1, false, false), Span(22, 27)),
|
||||||
(Instruction::define_local(1, 1, false), Span(18, 19)),
|
(Instruction::define_local(1, 1, false), Span(18, 19)),
|
||||||
(Instruction::get_local(2, 0), Span(29, 30)),
|
(Instruction::get_local(2, 0), Span(29, 30)),
|
||||||
(Instruction::test(2, true), Span(31, 33)),
|
(Instruction::test(2, false), 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)),
|
(Instruction::end(true), Span(35, 35)),
|
||||||
@ -608,8 +605,8 @@ fn divide() {
|
|||||||
vec![
|
vec![
|
||||||
(
|
(
|
||||||
*Instruction::divide(0, 0, 1)
|
*Instruction::divide(0, 0, 1)
|
||||||
.set_first_argument_to_constant()
|
.set_b_is_constant()
|
||||||
.set_second_argument_to_constant(),
|
.set_c_is_constant(),
|
||||||
Span(2, 3)
|
Span(2, 3)
|
||||||
),
|
),
|
||||||
(Instruction::end(true), Span(5, 5)),
|
(Instruction::end(true), Span(5, 5)),
|
||||||
@ -628,8 +625,8 @@ fn multiply() {
|
|||||||
vec![
|
vec![
|
||||||
(
|
(
|
||||||
*Instruction::multiply(0, 0, 1)
|
*Instruction::multiply(0, 0, 1)
|
||||||
.set_first_argument_to_constant()
|
.set_b_is_constant()
|
||||||
.set_second_argument_to_constant(),
|
.set_c_is_constant(),
|
||||||
Span(2, 3)
|
Span(2, 3)
|
||||||
),
|
),
|
||||||
(Instruction::end(true), Span(5, 5)),
|
(Instruction::end(true), Span(5, 5)),
|
||||||
@ -648,8 +645,8 @@ fn add() {
|
|||||||
vec![
|
vec![
|
||||||
(
|
(
|
||||||
*Instruction::add(0, 0, 1)
|
*Instruction::add(0, 0, 1)
|
||||||
.set_first_argument_to_constant()
|
.set_b_is_constant()
|
||||||
.set_second_argument_to_constant(),
|
.set_c_is_constant(),
|
||||||
Span(2, 3)
|
Span(2, 3)
|
||||||
),
|
),
|
||||||
(Instruction::end(true), Span(5, 5)),
|
(Instruction::end(true), Span(5, 5)),
|
||||||
@ -668,8 +665,8 @@ fn subtract() {
|
|||||||
vec![
|
vec![
|
||||||
(
|
(
|
||||||
*Instruction::subtract(0, 0, 1)
|
*Instruction::subtract(0, 0, 1)
|
||||||
.set_first_argument_to_constant()
|
.set_b_is_constant()
|
||||||
.set_second_argument_to_constant(),
|
.set_c_is_constant(),
|
||||||
Span(2, 3)
|
Span(2, 3)
|
||||||
),
|
),
|
||||||
(Instruction::end(true), Span(5, 5)),
|
(Instruction::end(true), Span(5, 5)),
|
||||||
|
@ -41,17 +41,15 @@ impl Vm {
|
|||||||
instruction: Instruction,
|
instruction: Instruction,
|
||||||
position: Span,
|
position: Span,
|
||||||
) -> Result<(&Value, &Value), VmError> {
|
) -> Result<(&Value, &Value), VmError> {
|
||||||
let left = if instruction.first_argument_is_constant() {
|
let left = if instruction.b_is_constant() {
|
||||||
vm.chunk
|
vm.chunk.get_constant(instruction.b(), position)?
|
||||||
.get_constant(instruction.first_argument(), position)?
|
|
||||||
} else {
|
} else {
|
||||||
vm.get(instruction.first_argument(), position)?
|
vm.get(instruction.b(), position)?
|
||||||
};
|
};
|
||||||
let right = if instruction.second_argument_is_constant() {
|
let right = if instruction.c_is_constant() {
|
||||||
vm.chunk
|
vm.chunk.get_constant(instruction.c(), position)?
|
||||||
.get_constant(instruction.second_argument(), position)?
|
|
||||||
} else {
|
} else {
|
||||||
vm.get(instruction.second_argument(), position)?
|
vm.get(instruction.c(), position)?
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok((left, right))
|
Ok((left, right))
|
||||||
@ -66,24 +64,24 @@ impl Vm {
|
|||||||
|
|
||||||
match instruction.operation() {
|
match instruction.operation() {
|
||||||
Operation::Move => {
|
Operation::Move => {
|
||||||
let from = instruction.first_argument();
|
let from = instruction.b();
|
||||||
let to = instruction.destination();
|
let to = instruction.a();
|
||||||
let value = self.take(from, position)?;
|
let value = self.take(from, position)?;
|
||||||
|
|
||||||
self.insert(value, to, position)?;
|
self.insert(value, to, position)?;
|
||||||
}
|
}
|
||||||
Operation::Close => {
|
Operation::Close => {
|
||||||
let from = instruction.first_argument();
|
let from = instruction.b();
|
||||||
let to = instruction.second_argument();
|
let to = instruction.c();
|
||||||
|
|
||||||
for register_index in from..to {
|
for register_index in from..to {
|
||||||
self.register_stack[register_index as usize] = None;
|
self.register_stack[register_index as usize] = None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Operation::LoadBoolean => {
|
Operation::LoadBoolean => {
|
||||||
let to_register = instruction.destination();
|
let to_register = instruction.a();
|
||||||
let boolean = instruction.first_argument_as_boolean();
|
let boolean = instruction.b_as_boolean();
|
||||||
let skip = instruction.second_argument_as_boolean();
|
let skip = instruction.c_as_boolean();
|
||||||
let value = Value::boolean(boolean);
|
let value = Value::boolean(boolean);
|
||||||
|
|
||||||
self.insert(value, to_register, position)?;
|
self.insert(value, to_register, position)?;
|
||||||
@ -93,20 +91,18 @@ impl Vm {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Operation::LoadConstant => {
|
Operation::LoadConstant => {
|
||||||
let to_register = instruction.destination();
|
let to_register = instruction.a();
|
||||||
let from_constant = instruction.first_argument();
|
let from_constant = instruction.b();
|
||||||
let value = self.chunk.take_constant(from_constant, position)?;
|
let value = self.chunk.take_constant(from_constant, position)?;
|
||||||
|
|
||||||
self.insert(value, to_register, position)?;
|
self.insert(value, to_register, position)?;
|
||||||
}
|
}
|
||||||
Operation::LoadList => {
|
Operation::LoadList => {
|
||||||
let to_register = instruction.destination();
|
let to_register = instruction.a();
|
||||||
let first_register = instruction.first_argument();
|
let first_register = instruction.b();
|
||||||
let length = instruction.second_argument();
|
let length = instruction.c();
|
||||||
let last_register = first_register + length + 1;
|
let last_register = first_register + length + 1;
|
||||||
|
|
||||||
println!("{first_register}..={last_register}");
|
|
||||||
|
|
||||||
let mut list = Vec::with_capacity(length as usize);
|
let mut list = Vec::with_capacity(length as usize);
|
||||||
|
|
||||||
for register_index in first_register..=last_register {
|
for register_index in first_register..=last_register {
|
||||||
@ -122,25 +118,25 @@ impl Vm {
|
|||||||
self.insert(Value::list(list), to_register, position)?;
|
self.insert(Value::list(list), to_register, position)?;
|
||||||
}
|
}
|
||||||
Operation::DefineLocal => {
|
Operation::DefineLocal => {
|
||||||
let from_register = instruction.destination();
|
let from_register = instruction.a();
|
||||||
let to_local = instruction.first_argument();
|
let to_local = instruction.b();
|
||||||
|
|
||||||
self.chunk.define_local(to_local, from_register, position)?;
|
self.chunk.define_local(to_local, from_register, position)?;
|
||||||
}
|
}
|
||||||
Operation::GetLocal => {
|
Operation::GetLocal => {
|
||||||
let register_index = instruction.destination();
|
let register_index = instruction.a();
|
||||||
let local_index = instruction.first_argument();
|
let local_index = instruction.b();
|
||||||
let local = self.chunk.get_local(local_index, position)?.clone();
|
let local = self.chunk.get_local(local_index, position)?.clone();
|
||||||
let value = self.clone_as_variable(local, position)?;
|
let value = self.clone_as_variable(local, position)?;
|
||||||
|
|
||||||
self.insert(value, register_index, position)?;
|
self.insert(value, register_index, position)?;
|
||||||
}
|
}
|
||||||
Operation::SetLocal => {
|
Operation::SetLocal => {
|
||||||
let register_index = instruction.destination();
|
let register_index = instruction.a();
|
||||||
let local_index = instruction.first_argument();
|
let local_index = instruction.b();
|
||||||
let local = self.chunk.get_local(local_index, position)?.clone();
|
let local = self.chunk.get_local(local_index, position)?.clone();
|
||||||
let value = self.clone_as_variable(local, position)?;
|
let value = self.clone_as_variable(local, position)?;
|
||||||
let new_value = if instruction.first_argument_is_constant() {
|
let new_value = if instruction.b_is_constant() {
|
||||||
self.chunk.take_constant(register_index, position)?
|
self.chunk.take_constant(register_index, position)?
|
||||||
} else {
|
} else {
|
||||||
self.clone(register_index, position)?
|
self.clone(register_index, position)?
|
||||||
@ -154,61 +150,76 @@ impl Vm {
|
|||||||
}
|
}
|
||||||
Operation::Add => {
|
Operation::Add => {
|
||||||
let (left, right) = get_arguments(self, instruction, position)?;
|
let (left, right) = get_arguments(self, instruction, position)?;
|
||||||
|
|
||||||
|
log::debug!("{left} + {right}");
|
||||||
|
|
||||||
let sum = left
|
let sum = left
|
||||||
.add(right)
|
.add(right)
|
||||||
.map_err(|error| VmError::Value { error, position })?;
|
.map_err(|error| VmError::Value { error, position })?;
|
||||||
|
|
||||||
self.insert(sum, instruction.destination(), position)?;
|
self.insert(sum, instruction.a(), position)?;
|
||||||
}
|
}
|
||||||
Operation::Subtract => {
|
Operation::Subtract => {
|
||||||
let (left, right) = get_arguments(self, instruction, position)?;
|
let (left, right) = get_arguments(self, instruction, position)?;
|
||||||
|
|
||||||
|
log::debug!("{left} - {right}");
|
||||||
|
|
||||||
let difference = left
|
let difference = left
|
||||||
.subtract(right)
|
.subtract(right)
|
||||||
.map_err(|error| VmError::Value { error, position })?;
|
.map_err(|error| VmError::Value { error, position })?;
|
||||||
|
|
||||||
self.insert(difference, instruction.destination(), position)?;
|
self.insert(difference, instruction.a(), position)?;
|
||||||
}
|
}
|
||||||
Operation::Multiply => {
|
Operation::Multiply => {
|
||||||
let (left, right) = get_arguments(self, instruction, position)?;
|
let (left, right) = get_arguments(self, instruction, position)?;
|
||||||
|
|
||||||
|
log::debug!("{left} * {right}");
|
||||||
|
|
||||||
let product = left
|
let product = left
|
||||||
.multiply(right)
|
.multiply(right)
|
||||||
.map_err(|error| VmError::Value { error, position })?;
|
.map_err(|error| VmError::Value { error, position })?;
|
||||||
|
|
||||||
self.insert(product, instruction.destination(), position)?;
|
self.insert(product, instruction.a(), position)?;
|
||||||
}
|
}
|
||||||
Operation::Divide => {
|
Operation::Divide => {
|
||||||
let (left, right) = get_arguments(self, instruction, position)?;
|
let (left, right) = get_arguments(self, instruction, position)?;
|
||||||
|
|
||||||
|
log::debug!("{left} / {right}");
|
||||||
|
|
||||||
let quotient = left
|
let quotient = left
|
||||||
.divide(right)
|
.divide(right)
|
||||||
.map_err(|error| VmError::Value { error, position })?;
|
.map_err(|error| VmError::Value { error, position })?;
|
||||||
|
|
||||||
self.insert(quotient, instruction.destination(), position)?;
|
self.insert(quotient, instruction.a(), position)?;
|
||||||
}
|
}
|
||||||
Operation::Modulo => {
|
Operation::Modulo => {
|
||||||
let (left, right) = get_arguments(self, instruction, position)?;
|
let (left, right) = get_arguments(self, instruction, position)?;
|
||||||
|
|
||||||
|
log::debug!("{left} % {right}");
|
||||||
|
|
||||||
let remainder = left
|
let remainder = left
|
||||||
.modulo(right)
|
.modulo(right)
|
||||||
.map_err(|error| VmError::Value { error, position })?;
|
.map_err(|error| VmError::Value { error, position })?;
|
||||||
|
|
||||||
self.insert(remainder, instruction.destination(), position)?;
|
self.insert(remainder, instruction.a(), position)?;
|
||||||
}
|
}
|
||||||
Operation::Test => {
|
Operation::Test => {
|
||||||
let register = instruction.destination();
|
let register = instruction.a();
|
||||||
let test_value = instruction.second_argument_as_boolean();
|
let test_value = instruction.c_as_boolean();
|
||||||
let value = self.get(register, position)?;
|
let value = self.get(register, position)?;
|
||||||
let boolean = value.as_boolean().ok_or_else(|| VmError::ExpectedBoolean {
|
let boolean = value.as_boolean().ok_or_else(|| VmError::ExpectedBoolean {
|
||||||
found: value.clone(),
|
found: value.clone(),
|
||||||
position,
|
position,
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
if boolean == test_value {
|
if boolean != test_value {
|
||||||
self.ip += 1;
|
self.ip += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Operation::TestSet => {
|
Operation::TestSet => {
|
||||||
let to_register = instruction.destination();
|
let to_register = instruction.a();
|
||||||
let argument = instruction.first_argument();
|
let argument = instruction.b();
|
||||||
let test_value = instruction.second_argument_as_boolean();
|
let test_value = instruction.c_as_boolean();
|
||||||
let value = self.clone(argument, position)?;
|
let value = self.clone(argument, position)?;
|
||||||
let boolean = value.as_boolean().ok_or_else(|| VmError::ExpectedBoolean {
|
let boolean = value.as_boolean().ok_or_else(|| VmError::ExpectedBoolean {
|
||||||
found: value.clone(),
|
found: value.clone(),
|
||||||
@ -235,13 +246,13 @@ impl Vm {
|
|||||||
found: left.clone(),
|
found: left.clone(),
|
||||||
position,
|
position,
|
||||||
})?;
|
})?;
|
||||||
let compare_to = instruction.destination_as_boolean();
|
let compare_to = instruction.a_as_boolean();
|
||||||
|
|
||||||
if boolean == compare_to {
|
if boolean == compare_to {
|
||||||
self.ip += 1;
|
self.ip += 1;
|
||||||
} else {
|
} else {
|
||||||
let jump_distance = jump.destination();
|
let jump_distance = jump.a();
|
||||||
let is_positive = jump.first_argument_as_boolean();
|
let is_positive = jump.a_as_boolean();
|
||||||
let new_ip = if is_positive {
|
let new_ip = if is_positive {
|
||||||
self.ip + jump_distance as usize
|
self.ip + jump_distance as usize
|
||||||
} else {
|
} else {
|
||||||
@ -265,13 +276,13 @@ impl Vm {
|
|||||||
found: left.clone(),
|
found: left.clone(),
|
||||||
position,
|
position,
|
||||||
})?;
|
})?;
|
||||||
let compare_to = instruction.destination_as_boolean();
|
let compare_to = instruction.a_as_boolean();
|
||||||
|
|
||||||
if boolean == compare_to {
|
if boolean == compare_to {
|
||||||
self.ip += 1;
|
self.ip += 1;
|
||||||
} else {
|
} else {
|
||||||
let jump_distance = jump.destination();
|
let jump_distance = jump.a();
|
||||||
let is_positive = jump.first_argument_as_boolean();
|
let is_positive = jump.a_as_boolean();
|
||||||
let new_ip = if is_positive {
|
let new_ip = if is_positive {
|
||||||
self.ip + jump_distance as usize
|
self.ip + jump_distance as usize
|
||||||
} else {
|
} else {
|
||||||
@ -295,13 +306,13 @@ impl Vm {
|
|||||||
found: left.clone(),
|
found: left.clone(),
|
||||||
position,
|
position,
|
||||||
})?;
|
})?;
|
||||||
let compare_to = instruction.destination_as_boolean();
|
let compare_to = instruction.a_as_boolean();
|
||||||
|
|
||||||
if boolean == compare_to {
|
if boolean == compare_to {
|
||||||
self.ip += 1;
|
self.ip += 1;
|
||||||
} else {
|
} else {
|
||||||
let jump_distance = jump.destination();
|
let jump_distance = jump.a();
|
||||||
let is_positive = jump.first_argument_as_boolean();
|
let is_positive = jump.a_as_boolean();
|
||||||
let new_ip = if is_positive {
|
let new_ip = if is_positive {
|
||||||
self.ip + jump_distance as usize
|
self.ip + jump_distance as usize
|
||||||
} else {
|
} else {
|
||||||
@ -312,34 +323,32 @@ impl Vm {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Operation::Negate => {
|
Operation::Negate => {
|
||||||
let value = if instruction.first_argument_is_constant() {
|
let value = if instruction.b_is_constant() {
|
||||||
self.chunk
|
self.chunk.get_constant(instruction.b(), position)?
|
||||||
.get_constant(instruction.first_argument(), position)?
|
|
||||||
} else {
|
} else {
|
||||||
self.get(instruction.first_argument(), position)?
|
self.get(instruction.b(), position)?
|
||||||
};
|
};
|
||||||
let negated = value
|
let negated = value
|
||||||
.negate()
|
.negate()
|
||||||
.map_err(|error| VmError::Value { error, position })?;
|
.map_err(|error| VmError::Value { error, position })?;
|
||||||
|
|
||||||
self.insert(negated, instruction.destination(), position)?;
|
self.insert(negated, instruction.a(), position)?;
|
||||||
}
|
}
|
||||||
Operation::Not => {
|
Operation::Not => {
|
||||||
let value = if instruction.first_argument_is_constant() {
|
let value = if instruction.b_is_constant() {
|
||||||
self.chunk
|
self.chunk.get_constant(instruction.b(), position)?
|
||||||
.get_constant(instruction.first_argument(), position)?
|
|
||||||
} else {
|
} else {
|
||||||
self.get(instruction.first_argument(), position)?
|
self.get(instruction.b(), position)?
|
||||||
};
|
};
|
||||||
let not = value
|
let not = value
|
||||||
.not()
|
.not()
|
||||||
.map_err(|error| VmError::Value { error, position })?;
|
.map_err(|error| VmError::Value { error, position })?;
|
||||||
|
|
||||||
self.insert(not, instruction.destination(), position)?;
|
self.insert(not, instruction.a(), position)?;
|
||||||
}
|
}
|
||||||
Operation::Jump => {
|
Operation::Jump => {
|
||||||
let offset = instruction.first_argument();
|
let offset = instruction.b();
|
||||||
let is_positive = instruction.second_argument_as_boolean();
|
let is_positive = instruction.c_as_boolean();
|
||||||
let new_ip = if is_positive {
|
let new_ip = if is_positive {
|
||||||
self.ip + offset as usize
|
self.ip + offset as usize
|
||||||
} else {
|
} else {
|
||||||
@ -349,8 +358,8 @@ impl Vm {
|
|||||||
self.ip = new_ip;
|
self.ip = new_ip;
|
||||||
}
|
}
|
||||||
Operation::Return => {
|
Operation::Return => {
|
||||||
let start_register = instruction.destination();
|
let start_register = instruction.a();
|
||||||
let end_register = instruction.first_argument();
|
let end_register = instruction.b();
|
||||||
let return_value_count = end_register - start_register;
|
let return_value_count = end_register - start_register;
|
||||||
|
|
||||||
if return_value_count == 1 {
|
if return_value_count == 1 {
|
||||||
@ -358,7 +367,7 @@ impl Vm {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Operation::End => {
|
Operation::End => {
|
||||||
let returns_value = instruction.destination_as_boolean();
|
let returns_value = instruction.a_as_boolean();
|
||||||
|
|
||||||
if returns_value {
|
if returns_value {
|
||||||
return Ok(Some(self.pop(position)?));
|
return Ok(Some(self.pop(position)?));
|
||||||
|
@ -1,7 +1,16 @@
|
|||||||
use dust_lang::*;
|
use dust_lang::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn if_expression() {
|
fn if_else() {
|
||||||
|
assert_eq!(run("if true { 1 } else { 2 }"), Ok(Some(Value::integer(1))));
|
||||||
|
assert_eq!(
|
||||||
|
run("if false { 1 } else { 2 }"),
|
||||||
|
Ok(Some(Value::integer(2)))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn r#if() {
|
||||||
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 }"),
|
Loading…
Reference in New Issue
Block a user