dust/dust-lang/src/instruction.rs

591 lines
19 KiB
Rust
Raw Normal View History

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 {
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
}
pub fn close(to_register: u8) -> Instruction {
2024-09-15 05:24:04 +00:00
let mut instruction = Instruction(Operation::Close as u32);
instruction.set_destination(to_register);
instruction
}
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-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-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-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-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-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-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-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-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
}
pub fn r#return() -> Instruction {
2024-09-15 05:24:04 +00:00
Instruction(Operation::Return as u32)
}
pub fn set_first_argument_to_constant(&mut self) -> &mut Self {
2024-09-15 05:24:04 +00:00
self.0 |= 0b1000_0000;
self
2024-09-15 05:24:04 +00:00
}
pub fn first_argument_is_constant(&self) -> bool {
self.0 & 0b1000_0000 != 0
}
pub fn set_second_argument_to_constant(&mut self) -> &mut Self {
2024-09-15 05:24:04 +00:00
self.0 |= 0b0100_0000;
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) {
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;
}
pub fn disassemble(&self, chunk: &Chunk) -> String {
2024-09-15 05:24:04 +00:00
let mut disassembled = format!("{:16} ", self.operation().to_string());
if let Some(info) = self.disassembly_info(Some(chunk)) {
disassembled.push_str(&info);
}
disassembled
}
pub fn disassembly_info(&self, chunk: Option<&Chunk>) -> Option<String> {
2024-09-15 05:24:04 +00:00
let info = match self.operation() {
Operation::Move => {
2024-09-15 05:24:04 +00:00
format!("R({}) = R({})", self.destination(), self.first_argument())
}
2024-09-15 05:24:04 +00:00
Operation::Close => format!("R({})", self.destination()),
Operation::LoadConstant => {
2024-09-15 05:24:04 +00:00
let constant_index = self.first_argument();
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
)
}
Err(error) => format!(
"R({}) = C({}) {:?}",
2024-09-15 05:24:04 +00:00
self.destination(),
constant_index,
error
),
}
} else {
2024-09-15 05:24:04 +00:00
format!("R({}) = C({})", self.destination(), constant_index)
}
}
Operation::DeclareLocal => {
2024-09-15 05:24:04 +00:00
let local_index = self.first_argument();
let identifier_display = if let Some(chunk) = chunk {
2024-09-15 01:05:03 +00:00
match chunk.get_identifier(local_index) {
Some(identifier) => identifier.to_string(),
None => "???".to_string(),
}
} else {
"???".to_string()
};
format!(
"L({}) = R({}) {}",
2024-09-15 05:24:04 +00:00
local_index,
self.destination(),
identifier_display
)
}
Operation::GetLocal => {
2024-09-15 05:24:04 +00:00
let local_index = self.first_argument();
2024-09-15 05:24:04 +00:00
format!("R({}) = L({})", self.destination(), local_index)
}
Operation::SetLocal => {
2024-09-15 05:24:04 +00:00
let local_index = self.first_argument();
let identifier_display = if let Some(chunk) = chunk {
2024-09-15 01:05:03 +00:00
match chunk.get_identifier(local_index) {
Some(identifier) => identifier.to_string(),
None => "???".to_string(),
}
} else {
"???".to_string()
};
format!(
"L({}) = R({}) {}",
2024-09-15 05:24:04 +00:00
local_index,
self.destination(),
identifier_display
)
}
Operation::Add => {
2024-09-15 05:24:04 +00:00
let destination = self.destination();
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())
};
format!("R({destination}) = {first_argument} + {second_argument}",)
}
Operation::Subtract => {
2024-09-15 05:24:04 +00:00
let destination = self.destination();
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())
};
format!("R({destination}) = {first_argument} - {second_argument}",)
}
Operation::Multiply => {
2024-09-15 05:24:04 +00:00
let destination = self.destination();
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())
};
format!("R({destination}) = {first_argument} * {second_argument}",)
}
Operation::Divide => {
2024-09-15 05:24:04 +00:00
let destination = self.destination();
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())
};
format!("R({destination}) = {first_argument} / {second_argument}",)
}
Operation::Negate => {
2024-09-15 05:24:04 +00:00
format!("R({}) = -RC({})", self.destination(), self.first_argument())
}
Operation::Return => return None,
};
Some(info)
}
}
impl Display for Instruction {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
if let Some(info) = self.disassembly_info(None) {
2024-09-15 05:24:04 +00:00
write!(f, "{} {}", self.operation(), info)
} 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;
const DECLARE_LOCAL: u8 = 0b0000_0011;
const GET_LOCAL: u8 = 0b0000_0100;
const SET_LOCAL: u8 = 0b0000_0101;
const ADD: u8 = 0b0000_0110;
const SUBTRACT: u8 = 0b0000_0111;
const MULTIPLY: u8 = 0b0000_1000;
const DIVIDE: u8 = 0b0000_1001;
const NEGATE: u8 = 0b0000_1010;
const RETURN: u8 = 0b0000_1011;
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Operation {
// Stack manipulation
2024-09-15 05:24:04 +00:00
Move = MOVE as isize,
Close = CLOSE as isize,
// Constants
2024-09-15 05:24:04 +00:00
LoadConstant = LOAD_CONSTANT as isize,
// Variables
2024-09-15 05:24:04 +00:00
DeclareLocal = DECLARE_LOCAL as isize,
GetLocal = GET_LOCAL as isize,
SetLocal = SET_LOCAL as isize,
// 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,
// Unary operations
2024-09-15 05:24:04 +00:00
Negate = NEGATE as isize,
// Control flow
2024-09-15 05:24:04 +00:00
Return = RETURN as isize,
}
2024-09-15 01:05:03 +00:00
impl Operation {
pub fn is_binary(&self) -> bool {
matches!(
self,
Operation::Add | Operation::Subtract | Operation::Multiply | Operation::Divide
)
}
}
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,
DECLARE_LOCAL => Operation::DeclareLocal,
GET_LOCAL => Operation::GetLocal,
SET_LOCAL => Operation::SetLocal,
ADD => Operation::Add,
SUBTRACT => Operation::Subtract,
MULTIPLY => Operation::Multiply,
DIVIDE => Operation::Divide,
NEGATE => Operation::Negate,
_ => Operation::Return,
}
}
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,
Operation::DeclareLocal => DECLARE_LOCAL,
Operation::GetLocal => GET_LOCAL,
Operation::SetLocal => SET_LOCAL,
Operation::Add => ADD,
Operation::Subtract => SUBTRACT,
Operation::Multiply => MULTIPLY,
Operation::Divide => DIVIDE,
Operation::Negate => NEGATE,
Operation::Return => RETURN,
}
}
}
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"),
Operation::DeclareLocal => write!(f, "DECLARE_LOCAL"),
Operation::GetLocal => write!(f, "GET_LOCAL"),
Operation::SetLocal => write!(f, "SET_LOCAL"),
Operation::Add => write!(f, "ADD"),
Operation::Subtract => write!(f, "SUBTRACT"),
Operation::Multiply => write!(f, "MULTIPLY"),
Operation::Divide => write!(f, "DIVIDE"),
Operation::Negate => write!(f, "NEGATE"),
Operation::Return => write!(f, "RETURN"),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
2024-09-15 05:24:04 +00:00
fn r#move() {
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);
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() {
let mut instruction = Instruction::close(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::Close);
assert_eq!(instruction.destination(), 1);
2024-09-15 05:24:04 +00:00
assert!(instruction.first_argument_is_constant());
assert!(instruction.second_argument_is_constant());
}
#[test]
fn load_constant() {
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);
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() {
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);
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() {
let mut instruction = Instruction::add(0, 1, 2);
2024-09-15 05:24:04 +00:00
instruction.set_operation(Operation::Add);
instruction.set_first_argument_to_constant();
instruction.set_second_argument_to_constant();
assert_eq!(instruction.operation(), Operation::Add);
assert_eq!(instruction.destination(), 0);
assert_eq!(instruction.first_argument(), 1);
2024-09-15 05:24:04 +00:00
assert_eq!(instruction.second_argument(), 2);
assert!(instruction.first_argument_is_constant());
assert!(instruction.second_argument_is_constant());
}
#[test]
fn subtract() {
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);
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() {
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);
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() {
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);
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 negate() {
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);
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 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
}
}