2024-09-12 04:39:31 +00:00
|
|
|
use std::fmt::{self, Display, Formatter};
|
|
|
|
|
2024-09-10 22:19:59 +00:00
|
|
|
use crate::{Chunk, Span};
|
2024-09-10 03:24:22 +00:00
|
|
|
|
2024-09-15 05:24:04 +00:00
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
|
|
pub struct Instruction(u32);
|
2024-09-10 03:24:22 +00:00
|
|
|
|
|
|
|
impl Instruction {
|
2024-09-12 03:07:20 +00:00
|
|
|
pub fn r#move(to_register: u8, from_register: u8) -> Instruction {
|
2024-09-15 05:24:04 +00:00
|
|
|
let mut instruction = Instruction(Operation::Move as u32);
|
|
|
|
|
|
|
|
instruction.set_destination(to_register);
|
|
|
|
instruction.set_first_argument(from_register);
|
|
|
|
|
|
|
|
instruction
|
2024-09-10 03:24:22 +00:00
|
|
|
}
|
|
|
|
|
2024-09-18 03:06:58 +00:00
|
|
|
pub fn close(from_register: u8, to_register: u8) -> Instruction {
|
2024-09-15 05:24:04 +00:00
|
|
|
let mut instruction = Instruction(Operation::Close as u32);
|
|
|
|
|
2024-09-18 03:06:58 +00:00
|
|
|
instruction.set_first_argument(from_register);
|
|
|
|
instruction.set_second_argument(to_register);
|
2024-09-15 05:24:04 +00:00
|
|
|
|
|
|
|
instruction
|
2024-09-12 03:07:20 +00:00
|
|
|
}
|
2024-09-10 22:19:59 +00:00
|
|
|
|
2024-09-15 01:05:03 +00:00
|
|
|
pub fn load_constant(to_register: u8, constant_index: u8) -> Instruction {
|
2024-09-15 05:24:04 +00:00
|
|
|
let mut instruction = Instruction(Operation::LoadConstant as u32);
|
|
|
|
|
|
|
|
instruction.set_destination(to_register);
|
|
|
|
instruction.set_first_argument(constant_index);
|
|
|
|
|
|
|
|
instruction
|
2024-09-12 03:07:20 +00:00
|
|
|
}
|
2024-09-10 13:26:05 +00:00
|
|
|
|
2024-09-17 23:35:33 +00:00
|
|
|
pub fn load_list(to_register: u8, list_length: u8) -> Instruction {
|
|
|
|
let mut instruction = Instruction(Operation::LoadList as u32);
|
|
|
|
|
|
|
|
instruction.set_destination(to_register);
|
|
|
|
instruction.set_first_argument(list_length);
|
|
|
|
|
|
|
|
instruction
|
|
|
|
}
|
|
|
|
|
2024-09-15 01:05:03 +00:00
|
|
|
pub fn declare_local(to_register: u8, variable_index: u8) -> Instruction {
|
2024-09-15 05:24:04 +00:00
|
|
|
let mut instruction = Instruction(Operation::DeclareLocal as u32);
|
|
|
|
|
|
|
|
instruction.set_destination(to_register);
|
|
|
|
instruction.set_first_argument(variable_index);
|
|
|
|
|
|
|
|
instruction
|
2024-09-12 03:07:20 +00:00
|
|
|
}
|
|
|
|
|
2024-09-15 01:05:03 +00:00
|
|
|
pub fn get_local(to_register: u8, variable_index: u8) -> Instruction {
|
2024-09-15 05:24:04 +00:00
|
|
|
let mut instruction = Instruction(Operation::GetLocal as u32);
|
|
|
|
|
|
|
|
instruction.set_destination(to_register);
|
|
|
|
instruction.set_first_argument(variable_index);
|
|
|
|
|
|
|
|
instruction
|
2024-09-12 03:07:20 +00:00
|
|
|
}
|
|
|
|
|
2024-09-15 01:05:03 +00:00
|
|
|
pub fn set_local(from_register: u8, variable_index: u8) -> Instruction {
|
2024-09-15 05:24:04 +00:00
|
|
|
let mut instruction = Instruction(Operation::SetLocal as u32);
|
|
|
|
|
|
|
|
instruction.set_destination(from_register);
|
|
|
|
instruction.set_first_argument(variable_index);
|
|
|
|
|
|
|
|
instruction
|
2024-09-12 03:07:20 +00:00
|
|
|
}
|
|
|
|
|
2024-09-15 05:24:04 +00:00
|
|
|
pub fn add(to_register: u8, left_index: u8, right_index: u8) -> Instruction {
|
|
|
|
let mut instruction = Instruction(Operation::Add as u32);
|
|
|
|
|
|
|
|
instruction.set_destination(to_register);
|
|
|
|
instruction.set_first_argument(left_index);
|
|
|
|
instruction.set_second_argument(right_index);
|
|
|
|
|
|
|
|
instruction
|
2024-09-12 03:07:20 +00:00
|
|
|
}
|
|
|
|
|
2024-09-15 05:24:04 +00:00
|
|
|
pub fn subtract(to_register: u8, left_index: u8, right_index: u8) -> Instruction {
|
|
|
|
let mut instruction = Instruction(Operation::Subtract as u32);
|
|
|
|
|
|
|
|
instruction.set_destination(to_register);
|
|
|
|
instruction.set_first_argument(left_index);
|
|
|
|
instruction.set_second_argument(right_index);
|
|
|
|
|
|
|
|
instruction
|
2024-09-12 03:07:20 +00:00
|
|
|
}
|
|
|
|
|
2024-09-15 05:24:04 +00:00
|
|
|
pub fn multiply(to_register: u8, left_index: u8, right_index: u8) -> Instruction {
|
|
|
|
let mut instruction = Instruction(Operation::Multiply as u32);
|
|
|
|
|
|
|
|
instruction.set_destination(to_register);
|
|
|
|
instruction.set_first_argument(left_index);
|
|
|
|
instruction.set_second_argument(right_index);
|
|
|
|
|
|
|
|
instruction
|
2024-09-12 03:07:20 +00:00
|
|
|
}
|
|
|
|
|
2024-09-15 05:24:04 +00:00
|
|
|
pub fn divide(to_register: u8, left_index: u8, right_index: u8) -> Instruction {
|
|
|
|
let mut instruction = Instruction(Operation::Divide as u32);
|
|
|
|
|
|
|
|
instruction.set_destination(to_register);
|
|
|
|
instruction.set_first_argument(left_index);
|
|
|
|
instruction.set_second_argument(right_index);
|
|
|
|
|
|
|
|
instruction
|
2024-09-12 03:07:20 +00:00
|
|
|
}
|
|
|
|
|
2024-09-17 21:51:39 +00:00
|
|
|
pub fn modulo(to_register: u8, left_index: u8, right_index: u8) -> Instruction {
|
|
|
|
let mut instruction = Instruction(Operation::Modulo as u32);
|
|
|
|
|
|
|
|
instruction.set_destination(to_register);
|
|
|
|
instruction.set_first_argument(left_index);
|
|
|
|
instruction.set_second_argument(right_index);
|
|
|
|
|
|
|
|
instruction
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn and(to_register: u8, left_index: u8, right_index: u8) -> Instruction {
|
|
|
|
let mut instruction = Instruction(Operation::And as u32);
|
|
|
|
|
|
|
|
instruction.set_destination(to_register);
|
|
|
|
instruction.set_first_argument(left_index);
|
|
|
|
instruction.set_second_argument(right_index);
|
|
|
|
|
|
|
|
instruction
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn or(to_register: u8, left_index: u8, right_index: u8) -> Instruction {
|
|
|
|
let mut instruction = Instruction(Operation::Or as u32);
|
|
|
|
|
|
|
|
instruction.set_destination(to_register);
|
|
|
|
instruction.set_first_argument(left_index);
|
|
|
|
instruction.set_second_argument(right_index);
|
|
|
|
|
|
|
|
instruction
|
|
|
|
}
|
|
|
|
|
2024-09-15 05:24:04 +00:00
|
|
|
pub fn negate(to_register: u8, from_index: u8) -> Instruction {
|
|
|
|
let mut instruction = Instruction(Operation::Negate as u32);
|
|
|
|
|
|
|
|
instruction.set_destination(to_register);
|
|
|
|
instruction.set_first_argument(from_index);
|
|
|
|
|
|
|
|
instruction
|
2024-09-12 03:07:20 +00:00
|
|
|
}
|
|
|
|
|
2024-09-17 21:51:39 +00:00
|
|
|
pub fn not(to_register: u8, from_index: u8) -> Instruction {
|
|
|
|
let mut instruction = Instruction(Operation::Not as u32);
|
|
|
|
|
|
|
|
instruction.set_destination(to_register);
|
|
|
|
instruction.set_first_argument(from_index);
|
|
|
|
|
|
|
|
instruction
|
|
|
|
}
|
|
|
|
|
2024-09-12 03:07:20 +00:00
|
|
|
pub fn r#return() -> Instruction {
|
2024-09-15 05:24:04 +00:00
|
|
|
Instruction(Operation::Return as u32)
|
|
|
|
}
|
|
|
|
|
2024-09-15 06:03:54 +00:00
|
|
|
pub fn set_first_argument_to_constant(&mut self) -> &mut Self {
|
2024-09-15 05:24:04 +00:00
|
|
|
self.0 |= 0b1000_0000;
|
2024-09-15 06:03:54 +00:00
|
|
|
|
|
|
|
self
|
2024-09-15 05:24:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn first_argument_is_constant(&self) -> bool {
|
|
|
|
self.0 & 0b1000_0000 != 0
|
|
|
|
}
|
|
|
|
|
2024-09-15 06:03:54 +00:00
|
|
|
pub fn set_second_argument_to_constant(&mut self) -> &mut Self {
|
2024-09-15 05:24:04 +00:00
|
|
|
self.0 |= 0b0100_0000;
|
2024-09-15 06:03:54 +00:00
|
|
|
|
|
|
|
self
|
2024-09-15 05:24:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn second_argument_is_constant(&self) -> bool {
|
|
|
|
self.0 & 0b0100_0000 != 0
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn destination(&self) -> u8 {
|
|
|
|
(self.0 >> 24) as u8
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_destination(&mut self, destination: u8) {
|
2024-09-15 14:20:43 +00:00
|
|
|
self.0 &= 0x00FFFFFF;
|
2024-09-15 05:24:04 +00:00
|
|
|
self.0 |= (destination as u32) << 24;
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn first_argument(&self) -> u8 {
|
|
|
|
(self.0 >> 16) as u8
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_first_argument(&mut self, argument: u8) {
|
|
|
|
self.0 |= (argument as u32) << 16;
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn second_argument(&self) -> u8 {
|
|
|
|
(self.0 >> 8) as u8
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_second_argument(&mut self, argument: u8) {
|
|
|
|
self.0 |= (argument as u32) << 8;
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn operation(&self) -> Operation {
|
|
|
|
Operation::from((self.0 & 0b0000_0000_0011_1111) as u8)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_operation(&mut self, operation: Operation) {
|
|
|
|
self.0 |= u8::from(operation) as u32;
|
2024-09-12 03:07:20 +00:00
|
|
|
}
|
|
|
|
|
2024-09-12 04:39:31 +00:00
|
|
|
pub fn disassemble(&self, chunk: &Chunk) -> String {
|
2024-09-15 05:24:04 +00:00
|
|
|
let mut disassembled = format!("{:16} ", self.operation().to_string());
|
2024-09-12 17:03:24 +00:00
|
|
|
|
|
|
|
if let Some(info) = self.disassembly_info(Some(chunk)) {
|
|
|
|
disassembled.push_str(&info);
|
|
|
|
}
|
|
|
|
|
|
|
|
disassembled
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn disassembly_info(&self, chunk: Option<&Chunk>) -> Option<String> {
|
2024-09-18 01:10:44 +00:00
|
|
|
let format_arguments = || {
|
|
|
|
let first_argument = if self.first_argument_is_constant() {
|
|
|
|
format!("C({})", self.first_argument())
|
|
|
|
} else {
|
|
|
|
format!("R({})", self.first_argument())
|
|
|
|
};
|
|
|
|
let second_argument = if self.second_argument_is_constant() {
|
|
|
|
format!("C({})", self.second_argument())
|
|
|
|
} else {
|
|
|
|
format!("R({})", self.second_argument())
|
|
|
|
};
|
|
|
|
|
|
|
|
(first_argument, second_argument)
|
|
|
|
};
|
|
|
|
|
2024-09-15 05:24:04 +00:00
|
|
|
let info = match self.operation() {
|
2024-09-12 09:08:55 +00:00
|
|
|
Operation::Move => {
|
2024-09-15 05:24:04 +00:00
|
|
|
format!("R({}) = R({})", self.destination(), self.first_argument())
|
2024-09-12 09:08:55 +00:00
|
|
|
}
|
2024-09-18 03:06:58 +00:00
|
|
|
Operation::Close => {
|
|
|
|
let from_register = self.first_argument();
|
|
|
|
let to_register = self.second_argument().saturating_sub(1);
|
|
|
|
|
|
|
|
format!("R({from_register})..=R({to_register})")
|
|
|
|
}
|
2024-09-12 04:39:31 +00:00
|
|
|
Operation::LoadConstant => {
|
2024-09-15 05:24:04 +00:00
|
|
|
let constant_index = self.first_argument();
|
2024-09-12 03:07:20 +00:00
|
|
|
|
2024-09-12 17:03:24 +00:00
|
|
|
if let Some(chunk) = chunk {
|
|
|
|
match chunk.get_constant(constant_index, Span(0, 0)) {
|
|
|
|
Ok(value) => {
|
2024-09-15 05:24:04 +00:00
|
|
|
format!(
|
|
|
|
"R({}) = C({}) {}",
|
|
|
|
self.destination(),
|
|
|
|
constant_index,
|
|
|
|
value
|
|
|
|
)
|
2024-09-12 17:03:24 +00:00
|
|
|
}
|
|
|
|
Err(error) => format!(
|
|
|
|
"R({}) = C({}) {:?}",
|
2024-09-15 05:24:04 +00:00
|
|
|
self.destination(),
|
|
|
|
constant_index,
|
|
|
|
error
|
2024-09-12 17:03:24 +00:00
|
|
|
),
|
|
|
|
}
|
|
|
|
} else {
|
2024-09-15 05:24:04 +00:00
|
|
|
format!("R({}) = C({})", self.destination(), constant_index)
|
2024-09-12 17:03:24 +00:00
|
|
|
}
|
2024-09-12 09:08:55 +00:00
|
|
|
}
|
2024-09-17 23:35:33 +00:00
|
|
|
Operation::LoadList => {
|
|
|
|
let destination = self.destination();
|
|
|
|
let first_index = destination - self.first_argument();
|
|
|
|
let last_index = destination - 1;
|
|
|
|
|
|
|
|
format!(
|
2024-09-18 03:06:58 +00:00
|
|
|
"R({}) = [R({})..=R({})]",
|
2024-09-17 23:35:33 +00:00
|
|
|
destination, first_index, last_index
|
|
|
|
)
|
|
|
|
}
|
2024-09-12 17:03:24 +00:00
|
|
|
Operation::DeclareLocal => {
|
2024-09-15 05:24:04 +00:00
|
|
|
let local_index = self.first_argument();
|
2024-09-12 23:25:20 +00:00
|
|
|
let identifier_display = if let Some(chunk) = chunk {
|
2024-09-15 01:05:03 +00:00
|
|
|
match chunk.get_identifier(local_index) {
|
2024-09-12 23:25:20 +00:00
|
|
|
Some(identifier) => identifier.to_string(),
|
|
|
|
None => "???".to_string(),
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
"???".to_string()
|
|
|
|
};
|
2024-09-12 09:08:55 +00:00
|
|
|
|
2024-09-12 23:25:20 +00:00
|
|
|
format!(
|
|
|
|
"L({}) = R({}) {}",
|
2024-09-15 05:24:04 +00:00
|
|
|
local_index,
|
|
|
|
self.destination(),
|
|
|
|
identifier_display
|
2024-09-12 23:25:20 +00:00
|
|
|
)
|
2024-09-12 09:08:55 +00:00
|
|
|
}
|
2024-09-12 17:03:24 +00:00
|
|
|
Operation::GetLocal => {
|
2024-09-15 05:24:04 +00:00
|
|
|
let local_index = self.first_argument();
|
2024-09-12 09:08:55 +00:00
|
|
|
|
2024-09-15 05:24:04 +00:00
|
|
|
format!("R({}) = L({})", self.destination(), local_index)
|
2024-09-12 09:08:55 +00:00
|
|
|
}
|
2024-09-12 17:03:24 +00:00
|
|
|
Operation::SetLocal => {
|
2024-09-15 05:24:04 +00:00
|
|
|
let local_index = self.first_argument();
|
2024-09-12 23:25:20 +00:00
|
|
|
let identifier_display = if let Some(chunk) = chunk {
|
2024-09-15 01:05:03 +00:00
|
|
|
match chunk.get_identifier(local_index) {
|
2024-09-12 23:25:20 +00:00
|
|
|
Some(identifier) => identifier.to_string(),
|
|
|
|
None => "???".to_string(),
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
"???".to_string()
|
|
|
|
};
|
2024-09-12 09:08:55 +00:00
|
|
|
|
2024-09-12 23:25:20 +00:00
|
|
|
format!(
|
|
|
|
"L({}) = R({}) {}",
|
2024-09-15 05:24:04 +00:00
|
|
|
local_index,
|
|
|
|
self.destination(),
|
|
|
|
identifier_display
|
2024-09-12 23:25:20 +00:00
|
|
|
)
|
2024-09-12 09:08:55 +00:00
|
|
|
}
|
|
|
|
Operation::Add => {
|
2024-09-15 05:24:04 +00:00
|
|
|
let destination = self.destination();
|
2024-09-18 01:10:44 +00:00
|
|
|
let (first_argument, second_argument) = format_arguments();
|
2024-09-15 05:24:04 +00:00
|
|
|
|
|
|
|
format!("R({destination}) = {first_argument} + {second_argument}",)
|
2024-09-12 09:08:55 +00:00
|
|
|
}
|
|
|
|
Operation::Subtract => {
|
2024-09-15 05:24:04 +00:00
|
|
|
let destination = self.destination();
|
2024-09-18 01:10:44 +00:00
|
|
|
let (first_argument, second_argument) = format_arguments();
|
2024-09-15 05:24:04 +00:00
|
|
|
|
|
|
|
format!("R({destination}) = {first_argument} - {second_argument}",)
|
2024-09-12 09:08:55 +00:00
|
|
|
}
|
|
|
|
Operation::Multiply => {
|
2024-09-15 05:24:04 +00:00
|
|
|
let destination = self.destination();
|
2024-09-18 01:10:44 +00:00
|
|
|
let (first_argument, second_argument) = format_arguments();
|
2024-09-15 05:24:04 +00:00
|
|
|
|
|
|
|
format!("R({destination}) = {first_argument} * {second_argument}",)
|
2024-09-12 09:08:55 +00:00
|
|
|
}
|
|
|
|
Operation::Divide => {
|
2024-09-15 05:24:04 +00:00
|
|
|
let destination = self.destination();
|
2024-09-18 01:10:44 +00:00
|
|
|
let (first_argument, second_argument) = format_arguments();
|
2024-09-15 05:24:04 +00:00
|
|
|
|
|
|
|
format!("R({destination}) = {first_argument} / {second_argument}",)
|
2024-09-12 09:08:55 +00:00
|
|
|
}
|
2024-09-17 21:51:39 +00:00
|
|
|
Operation::Modulo => {
|
|
|
|
let destination = self.destination();
|
2024-09-18 01:10:44 +00:00
|
|
|
let (first_argument, second_argument) = format_arguments();
|
2024-09-17 21:51:39 +00:00
|
|
|
|
|
|
|
format!("R({destination}) = {first_argument} % {second_argument}",)
|
|
|
|
}
|
|
|
|
Operation::And => {
|
|
|
|
let destination = self.destination();
|
2024-09-18 01:10:44 +00:00
|
|
|
let (first_argument, second_argument) = format_arguments();
|
2024-09-17 21:51:39 +00:00
|
|
|
|
|
|
|
format!("R({destination}) = {first_argument} && {second_argument}",)
|
|
|
|
}
|
|
|
|
Operation::Or => {
|
|
|
|
let destination = self.destination();
|
2024-09-18 01:10:44 +00:00
|
|
|
let (first_argument, second_argument) = format_arguments();
|
2024-09-17 21:51:39 +00:00
|
|
|
|
|
|
|
format!("R({destination}) = {first_argument} || {second_argument}",)
|
|
|
|
}
|
2024-09-12 09:08:55 +00:00
|
|
|
Operation::Negate => {
|
2024-09-17 21:23:37 +00:00
|
|
|
let destination = self.destination();
|
|
|
|
let argument = if self.first_argument_is_constant() {
|
|
|
|
format!("C({})", self.first_argument())
|
|
|
|
} else {
|
|
|
|
format!("R({})", self.first_argument())
|
|
|
|
};
|
|
|
|
|
|
|
|
format!("R({destination}) = -{argument}")
|
2024-09-12 09:08:55 +00:00
|
|
|
}
|
2024-09-17 21:51:39 +00:00
|
|
|
Operation::Not => {
|
|
|
|
let destination = self.destination();
|
|
|
|
let argument = if self.first_argument_is_constant() {
|
|
|
|
format!("C({})", self.first_argument())
|
|
|
|
} else {
|
|
|
|
format!("R({})", self.first_argument())
|
|
|
|
};
|
|
|
|
|
|
|
|
format!("R({destination}) = !{argument}")
|
|
|
|
}
|
2024-09-12 17:03:24 +00:00
|
|
|
Operation::Return => return None,
|
|
|
|
};
|
|
|
|
|
|
|
|
Some(info)
|
2024-09-12 04:39:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Display for Instruction {
|
|
|
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
2024-09-12 17:03:24 +00:00
|
|
|
if let Some(info) = self.disassembly_info(None) {
|
2024-09-15 05:24:04 +00:00
|
|
|
write!(f, "{} {}", self.operation(), info)
|
2024-09-12 17:03:24 +00:00
|
|
|
} else {
|
2024-09-15 05:24:04 +00:00
|
|
|
write!(f, "{}", self.operation())
|
2024-09-10 03:24:22 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-09-15 05:24:04 +00:00
|
|
|
const MOVE: u8 = 0b0000_0000;
|
|
|
|
const CLOSE: u8 = 0b000_0001;
|
|
|
|
const LOAD_CONSTANT: u8 = 0b0000_0010;
|
2024-09-17 23:35:33 +00:00
|
|
|
const LOAD_LIST: u8 = 0b0000_0011;
|
|
|
|
const DECLARE_LOCAL: u8 = 0b0000_0100;
|
|
|
|
const GET_LOCAL: u8 = 0b0000_0101;
|
|
|
|
const SET_LOCAL: u8 = 0b0000_0110;
|
|
|
|
const ADD: u8 = 0b0000_0111;
|
|
|
|
const SUBTRACT: u8 = 0b0000_1000;
|
|
|
|
const MULTIPLY: u8 = 0b0000_1001;
|
|
|
|
const MODULO: u8 = 0b0000_1010;
|
|
|
|
const AND: u8 = 0b0000_1011;
|
|
|
|
const OR: u8 = 0b0000_1100;
|
|
|
|
const DIVIDE: u8 = 0b0000_1101;
|
|
|
|
const NEGATE: u8 = 0b0000_1110;
|
|
|
|
const NOT: u8 = 0b0000_1111;
|
|
|
|
const RETURN: u8 = 0b0001_0000;
|
2024-09-15 05:24:04 +00:00
|
|
|
|
2024-09-12 03:07:20 +00:00
|
|
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
2024-09-12 04:39:31 +00:00
|
|
|
pub enum Operation {
|
2024-09-12 03:07:20 +00:00
|
|
|
// Stack manipulation
|
2024-09-15 05:24:04 +00:00
|
|
|
Move = MOVE as isize,
|
|
|
|
Close = CLOSE as isize,
|
2024-09-12 03:07:20 +00:00
|
|
|
|
2024-09-17 23:35:33 +00:00
|
|
|
// Value loading
|
2024-09-15 05:24:04 +00:00
|
|
|
LoadConstant = LOAD_CONSTANT as isize,
|
2024-09-17 23:35:33 +00:00
|
|
|
LoadList = LOAD_LIST as isize,
|
2024-09-12 03:07:20 +00:00
|
|
|
|
|
|
|
// Variables
|
2024-09-15 05:24:04 +00:00
|
|
|
DeclareLocal = DECLARE_LOCAL as isize,
|
|
|
|
GetLocal = GET_LOCAL as isize,
|
|
|
|
SetLocal = SET_LOCAL as isize,
|
2024-09-12 03:07:20 +00:00
|
|
|
|
|
|
|
// Binary operations
|
2024-09-15 05:24:04 +00:00
|
|
|
Add = ADD as isize,
|
|
|
|
Subtract = SUBTRACT as isize,
|
|
|
|
Multiply = MULTIPLY as isize,
|
|
|
|
Divide = DIVIDE as isize,
|
2024-09-17 21:51:39 +00:00
|
|
|
Modulo = MODULO as isize,
|
|
|
|
And = AND as isize,
|
|
|
|
Or = OR as isize,
|
2024-09-12 03:07:20 +00:00
|
|
|
|
|
|
|
// Unary operations
|
2024-09-15 05:24:04 +00:00
|
|
|
Negate = NEGATE as isize,
|
2024-09-17 21:51:39 +00:00
|
|
|
Not = NOT as isize,
|
2024-09-12 03:07:20 +00:00
|
|
|
|
|
|
|
// Control flow
|
2024-09-15 05:24:04 +00:00
|
|
|
Return = RETURN as isize,
|
2024-09-12 09:08:55 +00:00
|
|
|
}
|
|
|
|
|
2024-09-15 01:05:03 +00:00
|
|
|
impl Operation {
|
|
|
|
pub fn is_binary(&self) -> bool {
|
|
|
|
matches!(
|
|
|
|
self,
|
2024-09-17 21:51:39 +00:00
|
|
|
Operation::Add
|
|
|
|
| Operation::Subtract
|
|
|
|
| Operation::Multiply
|
|
|
|
| Operation::Divide
|
|
|
|
| Operation::Modulo
|
|
|
|
| Operation::And
|
|
|
|
| Operation::Or
|
2024-09-15 01:05:03 +00:00
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-09-12 09:08:55 +00:00
|
|
|
impl From<u8> for Operation {
|
|
|
|
fn from(byte: u8) -> Self {
|
|
|
|
match byte {
|
2024-09-15 05:24:04 +00:00
|
|
|
MOVE => Operation::Move,
|
|
|
|
CLOSE => Operation::Close,
|
|
|
|
LOAD_CONSTANT => Operation::LoadConstant,
|
2024-09-17 23:35:33 +00:00
|
|
|
LOAD_LIST => Operation::LoadList,
|
2024-09-15 05:24:04 +00:00
|
|
|
DECLARE_LOCAL => Operation::DeclareLocal,
|
|
|
|
GET_LOCAL => Operation::GetLocal,
|
|
|
|
SET_LOCAL => Operation::SetLocal,
|
|
|
|
ADD => Operation::Add,
|
|
|
|
SUBTRACT => Operation::Subtract,
|
|
|
|
MULTIPLY => Operation::Multiply,
|
|
|
|
DIVIDE => Operation::Divide,
|
2024-09-17 21:51:39 +00:00
|
|
|
MODULO => Operation::Modulo,
|
|
|
|
AND => Operation::And,
|
|
|
|
OR => Operation::Or,
|
2024-09-15 05:24:04 +00:00
|
|
|
NEGATE => Operation::Negate,
|
2024-09-17 21:51:39 +00:00
|
|
|
NOT => Operation::Not,
|
|
|
|
RETURN => Operation::Return,
|
|
|
|
_ => panic!("Invalid operation byte: {}", byte),
|
2024-09-12 09:08:55 +00:00
|
|
|
}
|
|
|
|
}
|
2024-09-10 03:24:22 +00:00
|
|
|
}
|
2024-09-10 14:44:15 +00:00
|
|
|
|
2024-09-15 05:24:04 +00:00
|
|
|
impl From<Operation> for u8 {
|
|
|
|
fn from(operation: Operation) -> Self {
|
|
|
|
match operation {
|
|
|
|
Operation::Move => MOVE,
|
|
|
|
Operation::Close => CLOSE,
|
|
|
|
Operation::LoadConstant => LOAD_CONSTANT,
|
2024-09-17 23:35:33 +00:00
|
|
|
Operation::LoadList => LOAD_LIST,
|
2024-09-15 05:24:04 +00:00
|
|
|
Operation::DeclareLocal => DECLARE_LOCAL,
|
|
|
|
Operation::GetLocal => GET_LOCAL,
|
|
|
|
Operation::SetLocal => SET_LOCAL,
|
|
|
|
Operation::Add => ADD,
|
|
|
|
Operation::Subtract => SUBTRACT,
|
|
|
|
Operation::Multiply => MULTIPLY,
|
|
|
|
Operation::Divide => DIVIDE,
|
2024-09-17 21:51:39 +00:00
|
|
|
Operation::Modulo => MODULO,
|
|
|
|
Operation::And => AND,
|
|
|
|
Operation::Or => OR,
|
2024-09-15 05:24:04 +00:00
|
|
|
Operation::Negate => NEGATE,
|
2024-09-17 21:51:39 +00:00
|
|
|
Operation::Not => NOT,
|
2024-09-15 05:24:04 +00:00
|
|
|
Operation::Return => RETURN,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-09-12 04:39:31 +00:00
|
|
|
impl Display for Operation {
|
|
|
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
|
|
match self {
|
|
|
|
Operation::Move => write!(f, "MOVE"),
|
|
|
|
Operation::Close => write!(f, "CLOSE"),
|
|
|
|
Operation::LoadConstant => write!(f, "LOAD_CONSTANT"),
|
2024-09-17 23:35:33 +00:00
|
|
|
Operation::LoadList => write!(f, "LOAD_LIST"),
|
2024-09-12 17:03:24 +00:00
|
|
|
Operation::DeclareLocal => write!(f, "DECLARE_LOCAL"),
|
|
|
|
Operation::GetLocal => write!(f, "GET_LOCAL"),
|
|
|
|
Operation::SetLocal => write!(f, "SET_LOCAL"),
|
2024-09-12 04:39:31 +00:00
|
|
|
Operation::Add => write!(f, "ADD"),
|
|
|
|
Operation::Subtract => write!(f, "SUBTRACT"),
|
|
|
|
Operation::Multiply => write!(f, "MULTIPLY"),
|
|
|
|
Operation::Divide => write!(f, "DIVIDE"),
|
2024-09-17 21:51:39 +00:00
|
|
|
Operation::Modulo => write!(f, "MODULO"),
|
|
|
|
Operation::And => write!(f, "AND"),
|
|
|
|
Operation::Or => write!(f, "OR"),
|
2024-09-12 04:39:31 +00:00
|
|
|
Operation::Negate => write!(f, "NEGATE"),
|
2024-09-17 21:51:39 +00:00
|
|
|
Operation::Not => write!(f, "NOT"),
|
2024-09-12 04:39:31 +00:00
|
|
|
Operation::Return => write!(f, "RETURN"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-09-12 03:07:20 +00:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[test]
|
2024-09-15 05:24:04 +00:00
|
|
|
fn r#move() {
|
2024-09-15 06:03:54 +00:00
|
|
|
let mut instruction = Instruction::r#move(0, 1);
|
2024-09-15 05:24:04 +00:00
|
|
|
|
|
|
|
instruction.set_first_argument_to_constant();
|
|
|
|
instruction.set_second_argument_to_constant();
|
|
|
|
|
|
|
|
assert_eq!(instruction.operation(), Operation::Move);
|
2024-09-15 06:03:54 +00:00
|
|
|
assert_eq!(instruction.destination(), 0);
|
|
|
|
assert_eq!(instruction.first_argument(), 1);
|
2024-09-15 05:24:04 +00:00
|
|
|
assert!(instruction.first_argument_is_constant());
|
|
|
|
assert!(instruction.second_argument_is_constant());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn close() {
|
2024-09-18 03:06:58 +00:00
|
|
|
let instruction = Instruction::close(1, 2);
|
2024-09-15 05:24:04 +00:00
|
|
|
|
|
|
|
assert_eq!(instruction.operation(), Operation::Close);
|
2024-09-18 03:06:58 +00:00
|
|
|
assert_eq!(instruction.first_argument(), 1);
|
|
|
|
assert_eq!(instruction.second_argument(), 2);
|
2024-09-15 05:24:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn load_constant() {
|
2024-09-15 06:03:54 +00:00
|
|
|
let mut instruction = Instruction::load_constant(0, 1);
|
2024-09-15 05:24:04 +00:00
|
|
|
|
|
|
|
instruction.set_first_argument_to_constant();
|
|
|
|
instruction.set_second_argument_to_constant();
|
|
|
|
|
|
|
|
assert_eq!(instruction.operation(), Operation::LoadConstant);
|
2024-09-15 06:03:54 +00:00
|
|
|
assert_eq!(instruction.destination(), 0);
|
|
|
|
assert_eq!(instruction.first_argument(), 1);
|
2024-09-15 05:24:04 +00:00
|
|
|
assert!(instruction.first_argument_is_constant());
|
|
|
|
assert!(instruction.second_argument_is_constant());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn declare_local() {
|
2024-09-15 06:03:54 +00:00
|
|
|
let mut instruction = Instruction::declare_local(0, 1);
|
2024-09-15 05:24:04 +00:00
|
|
|
|
|
|
|
instruction.set_first_argument_to_constant();
|
|
|
|
instruction.set_second_argument_to_constant();
|
|
|
|
|
|
|
|
assert_eq!(instruction.operation(), Operation::DeclareLocal);
|
2024-09-15 06:03:54 +00:00
|
|
|
assert_eq!(instruction.destination(), 0);
|
|
|
|
assert_eq!(instruction.first_argument(), 1);
|
2024-09-15 05:24:04 +00:00
|
|
|
assert!(instruction.first_argument_is_constant());
|
|
|
|
assert!(instruction.second_argument_is_constant());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn add() {
|
2024-09-15 14:20:43 +00:00
|
|
|
let mut instruction = Instruction::add(1, 1, 0);
|
2024-09-15 05:24:04 +00:00
|
|
|
|
|
|
|
instruction.set_operation(Operation::Add);
|
|
|
|
|
|
|
|
instruction.set_first_argument_to_constant();
|
|
|
|
|
|
|
|
assert_eq!(instruction.operation(), Operation::Add);
|
2024-09-15 14:20:43 +00:00
|
|
|
assert_eq!(instruction.destination(), 1);
|
2024-09-15 06:03:54 +00:00
|
|
|
assert_eq!(instruction.first_argument(), 1);
|
2024-09-15 14:20:43 +00:00
|
|
|
assert_eq!(instruction.second_argument(), 0);
|
2024-09-15 05:24:04 +00:00
|
|
|
assert!(instruction.first_argument_is_constant());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn subtract() {
|
2024-09-15 06:03:54 +00:00
|
|
|
let mut instruction = Instruction::subtract(0, 1, 2);
|
2024-09-15 05:24:04 +00:00
|
|
|
|
|
|
|
instruction.set_operation(Operation::Subtract);
|
|
|
|
|
|
|
|
instruction.set_first_argument_to_constant();
|
|
|
|
instruction.set_second_argument_to_constant();
|
|
|
|
|
|
|
|
assert_eq!(instruction.operation(), Operation::Subtract);
|
2024-09-15 06:03:54 +00:00
|
|
|
assert_eq!(instruction.destination(), 0);
|
|
|
|
assert_eq!(instruction.first_argument(), 1);
|
|
|
|
assert_eq!(instruction.second_argument(), 2);
|
2024-09-15 05:24:04 +00:00
|
|
|
assert!(instruction.first_argument_is_constant());
|
|
|
|
assert!(instruction.second_argument_is_constant());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn multiply() {
|
2024-09-15 06:03:54 +00:00
|
|
|
let mut instruction = Instruction::multiply(0, 1, 2);
|
2024-09-15 05:24:04 +00:00
|
|
|
|
|
|
|
instruction.set_operation(Operation::Multiply);
|
|
|
|
|
|
|
|
instruction.set_first_argument_to_constant();
|
|
|
|
instruction.set_second_argument_to_constant();
|
|
|
|
|
|
|
|
assert_eq!(instruction.operation(), Operation::Multiply);
|
2024-09-15 06:03:54 +00:00
|
|
|
assert_eq!(instruction.destination(), 0);
|
|
|
|
assert_eq!(instruction.first_argument(), 1);
|
|
|
|
assert_eq!(instruction.second_argument(), 2);
|
2024-09-15 05:24:04 +00:00
|
|
|
assert!(instruction.first_argument_is_constant());
|
|
|
|
assert!(instruction.second_argument_is_constant());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn divide() {
|
2024-09-15 06:03:54 +00:00
|
|
|
let mut instruction = Instruction::divide(0, 1, 2);
|
2024-09-15 05:24:04 +00:00
|
|
|
|
|
|
|
instruction.set_operation(Operation::Divide);
|
|
|
|
|
|
|
|
instruction.set_first_argument_to_constant();
|
|
|
|
instruction.set_second_argument_to_constant();
|
|
|
|
|
|
|
|
assert_eq!(instruction.operation(), Operation::Divide);
|
2024-09-15 06:03:54 +00:00
|
|
|
assert_eq!(instruction.destination(), 0);
|
|
|
|
assert_eq!(instruction.first_argument(), 1);
|
|
|
|
assert_eq!(instruction.second_argument(), 2);
|
2024-09-15 05:24:04 +00:00
|
|
|
assert!(instruction.first_argument_is_constant());
|
|
|
|
assert!(instruction.second_argument_is_constant());
|
|
|
|
}
|
|
|
|
|
2024-09-17 21:51:39 +00:00
|
|
|
#[test]
|
|
|
|
fn and() {
|
|
|
|
let mut instruction = Instruction::and(0, 1, 2);
|
|
|
|
|
|
|
|
instruction.set_operation(Operation::And);
|
|
|
|
|
|
|
|
instruction.set_first_argument_to_constant();
|
|
|
|
instruction.set_second_argument_to_constant();
|
|
|
|
|
|
|
|
assert_eq!(instruction.operation(), Operation::And);
|
|
|
|
assert_eq!(instruction.destination(), 0);
|
|
|
|
assert_eq!(instruction.first_argument(), 1);
|
|
|
|
assert_eq!(instruction.second_argument(), 2);
|
|
|
|
assert!(instruction.first_argument_is_constant());
|
|
|
|
assert!(instruction.second_argument_is_constant());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn or() {
|
|
|
|
let mut instruction = Instruction::or(0, 1, 2);
|
|
|
|
|
|
|
|
instruction.set_operation(Operation::Or);
|
|
|
|
|
|
|
|
instruction.set_first_argument_to_constant();
|
|
|
|
instruction.set_second_argument_to_constant();
|
|
|
|
|
|
|
|
assert_eq!(instruction.operation(), Operation::Or);
|
|
|
|
assert_eq!(instruction.destination(), 0);
|
|
|
|
assert_eq!(instruction.first_argument(), 1);
|
|
|
|
assert_eq!(instruction.second_argument(), 2);
|
|
|
|
assert!(instruction.first_argument_is_constant());
|
|
|
|
assert!(instruction.second_argument_is_constant());
|
|
|
|
}
|
|
|
|
|
2024-09-15 05:24:04 +00:00
|
|
|
#[test]
|
|
|
|
fn negate() {
|
2024-09-15 06:03:54 +00:00
|
|
|
let mut instruction = Instruction::negate(0, 1);
|
2024-09-15 05:24:04 +00:00
|
|
|
|
|
|
|
instruction.set_operation(Operation::Negate);
|
|
|
|
|
|
|
|
instruction.set_first_argument_to_constant();
|
|
|
|
instruction.set_second_argument_to_constant();
|
|
|
|
|
|
|
|
assert_eq!(instruction.operation(), Operation::Negate);
|
2024-09-15 06:03:54 +00:00
|
|
|
assert_eq!(instruction.destination(), 0);
|
|
|
|
assert_eq!(instruction.first_argument(), 1);
|
2024-09-15 05:24:04 +00:00
|
|
|
assert!(instruction.first_argument_is_constant());
|
|
|
|
assert!(instruction.second_argument_is_constant());
|
|
|
|
}
|
|
|
|
|
2024-09-17 21:51:39 +00:00
|
|
|
#[test]
|
|
|
|
fn not() {
|
|
|
|
let mut instruction = Instruction::not(0, 1);
|
|
|
|
|
|
|
|
instruction.set_operation(Operation::Not);
|
|
|
|
|
|
|
|
instruction.set_first_argument_to_constant();
|
|
|
|
instruction.set_second_argument_to_constant();
|
|
|
|
|
|
|
|
assert_eq!(instruction.operation(), Operation::Not);
|
|
|
|
assert_eq!(instruction.destination(), 0);
|
|
|
|
assert_eq!(instruction.first_argument(), 1);
|
|
|
|
assert!(instruction.first_argument_is_constant());
|
|
|
|
assert!(instruction.second_argument_is_constant());
|
|
|
|
}
|
|
|
|
|
2024-09-15 05:24:04 +00:00
|
|
|
#[test]
|
|
|
|
fn r#return() {
|
|
|
|
let mut instruction = Instruction::r#return();
|
|
|
|
|
|
|
|
instruction.set_operation(Operation::Return);
|
|
|
|
|
|
|
|
instruction.set_first_argument_to_constant();
|
|
|
|
instruction.set_second_argument_to_constant();
|
|
|
|
|
|
|
|
assert_eq!(instruction.operation(), Operation::Return);
|
|
|
|
assert!(instruction.first_argument_is_constant());
|
|
|
|
assert!(instruction.second_argument_is_constant());
|
2024-09-10 14:44:15 +00:00
|
|
|
}
|
|
|
|
}
|