2024-12-08 06:04:01 -05:00
|
|
|
//! Part of an [Instruction][crate::Instruction] that is encoded as a single byte.
|
|
|
|
|
|
|
|
use std::fmt::{self, Debug, Display, Formatter};
|
|
|
|
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
|
2024-12-09 07:01:07 -05:00
|
|
|
pub const MOVE_BYTE: u8 = 0;
|
|
|
|
pub const CLOSE_BYTE: u8 = 1;
|
|
|
|
pub const LOAD_BOOLEAN_BYTE: u8 = 2;
|
|
|
|
pub const LOAD_CONSTANT_BYTE: u8 = 3;
|
|
|
|
pub const LOAD_LIST_BYTE: u8 = 4;
|
2024-12-11 08:39:48 -05:00
|
|
|
pub const LOAD_SELF_BYTE: u8 = 5;
|
|
|
|
pub const GET_LOCAL_BYTE: u8 = 6;
|
|
|
|
pub const SET_LOCAL_BYTE: u8 = 7;
|
|
|
|
pub const ADD_BYTE: u8 = 8;
|
|
|
|
pub const SUBTRACT_BYTE: u8 = 9;
|
|
|
|
pub const MULTIPLY_BYTE: u8 = 10;
|
|
|
|
pub const DIVIDE_BYTE: u8 = 11;
|
|
|
|
pub const MODULO_BYTE: u8 = 12;
|
|
|
|
pub const TEST_BYTE: u8 = 13;
|
|
|
|
pub const TEST_SET_BYTE: u8 = 14;
|
|
|
|
pub const EQUAL_BYTE: u8 = 15;
|
|
|
|
pub const LESS_BYTE: u8 = 16;
|
|
|
|
pub const LESS_EQUAL_BYTE: u8 = 17;
|
|
|
|
pub const NEGATE_BYTE: u8 = 18;
|
|
|
|
pub const NOT_BYTE: u8 = 19;
|
|
|
|
pub const CALL_BYTE: u8 = 20;
|
|
|
|
pub const CALL_NATIVE_BYTE: u8 = 21;
|
|
|
|
pub const JUMP_BYTE: u8 = 22;
|
|
|
|
pub const RETURN_BYTE: u8 = 23;
|
2024-12-08 06:04:01 -05:00
|
|
|
|
2024-12-09 07:01:07 -05:00
|
|
|
/// Part of an [Instruction][crate::Instruction] that is encoded as a single byte.
|
|
|
|
#[derive(Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
|
|
|
#[repr(u8)]
|
|
|
|
pub enum Operation {
|
|
|
|
Move = MOVE_BYTE,
|
|
|
|
Close = CLOSE_BYTE,
|
|
|
|
LoadBoolean = LOAD_BOOLEAN_BYTE,
|
|
|
|
LoadConstant = LOAD_CONSTANT_BYTE,
|
|
|
|
LoadList = LOAD_LIST_BYTE,
|
|
|
|
LoadSelf = LOAD_SELF_BYTE,
|
|
|
|
GetLocal = GET_LOCAL_BYTE,
|
|
|
|
SetLocal = SET_LOCAL_BYTE,
|
|
|
|
Add = ADD_BYTE,
|
|
|
|
Subtract = SUBTRACT_BYTE,
|
|
|
|
Multiply = MULTIPLY_BYTE,
|
|
|
|
Divide = DIVIDE_BYTE,
|
|
|
|
Modulo = MODULO_BYTE,
|
|
|
|
Test = TEST_BYTE,
|
|
|
|
TestSet = TEST_SET_BYTE,
|
|
|
|
Equal = EQUAL_BYTE,
|
|
|
|
Less = LESS_BYTE,
|
|
|
|
LessEqual = LESS_EQUAL_BYTE,
|
|
|
|
Negate = NEGATE_BYTE,
|
|
|
|
Not = NOT_BYTE,
|
|
|
|
Call = CALL_BYTE,
|
|
|
|
CallNative = CALL_NATIVE_BYTE,
|
|
|
|
Jump = JUMP_BYTE,
|
|
|
|
Return = RETURN_BYTE,
|
|
|
|
}
|
2024-12-08 06:04:01 -05:00
|
|
|
|
2024-12-09 07:01:07 -05:00
|
|
|
impl From<u8> for Operation {
|
|
|
|
fn from(byte: u8) -> Self {
|
|
|
|
match byte {
|
|
|
|
MOVE_BYTE => Self::Move,
|
|
|
|
CLOSE_BYTE => Self::Close,
|
|
|
|
LOAD_BOOLEAN_BYTE => Self::LoadBoolean,
|
|
|
|
LOAD_CONSTANT_BYTE => Self::LoadConstant,
|
|
|
|
LOAD_LIST_BYTE => Self::LoadList,
|
|
|
|
LOAD_SELF_BYTE => Self::LoadSelf,
|
|
|
|
GET_LOCAL_BYTE => Self::GetLocal,
|
|
|
|
SET_LOCAL_BYTE => Self::SetLocal,
|
|
|
|
ADD_BYTE => Self::Add,
|
|
|
|
SUBTRACT_BYTE => Self::Subtract,
|
|
|
|
MULTIPLY_BYTE => Self::Multiply,
|
|
|
|
DIVIDE_BYTE => Self::Divide,
|
|
|
|
MODULO_BYTE => Self::Modulo,
|
|
|
|
TEST_BYTE => Self::Test,
|
|
|
|
TEST_SET_BYTE => Self::TestSet,
|
|
|
|
EQUAL_BYTE => Self::Equal,
|
|
|
|
LESS_BYTE => Self::Less,
|
|
|
|
LESS_EQUAL_BYTE => Self::LessEqual,
|
|
|
|
NEGATE_BYTE => Self::Negate,
|
|
|
|
NOT_BYTE => Self::Not,
|
|
|
|
CALL_BYTE => Self::Call,
|
|
|
|
CALL_NATIVE_BYTE => Self::CallNative,
|
|
|
|
JUMP_BYTE => Self::Jump,
|
|
|
|
RETURN_BYTE => Self::Return,
|
2024-12-08 06:04:01 -05:00
|
|
|
_ => {
|
|
|
|
if cfg!(debug_assertions) {
|
2024-12-09 07:01:07 -05:00
|
|
|
panic!("Invalid operation byte: {}", byte)
|
2024-12-08 06:04:01 -05:00
|
|
|
} else {
|
2024-12-09 07:01:07 -05:00
|
|
|
Self::Return
|
2024-12-08 06:04:01 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-09 07:01:07 -05:00
|
|
|
impl Operation {
|
|
|
|
pub fn name(self) -> &'static str {
|
|
|
|
match self {
|
|
|
|
Self::Move => "MOVE",
|
|
|
|
Self::Close => "CLOSE",
|
|
|
|
Self::LoadBoolean => "LOAD_BOOLEAN",
|
|
|
|
Self::LoadConstant => "LOAD_CONSTANT",
|
|
|
|
Self::LoadList => "LOAD_LIST",
|
|
|
|
Self::LoadSelf => "LOAD_SELF",
|
|
|
|
Self::GetLocal => "GET_LOCAL",
|
|
|
|
Self::SetLocal => "SET_LOCAL",
|
|
|
|
Self::Add => "ADD",
|
|
|
|
Self::Subtract => "SUBTRACT",
|
|
|
|
Self::Multiply => "MULTIPLY",
|
|
|
|
Self::Divide => "DIVIDE",
|
|
|
|
Self::Modulo => "MODULO",
|
|
|
|
Self::Test => "TEST",
|
|
|
|
Self::TestSet => "TEST_SET",
|
|
|
|
Self::Equal => "EQUAL",
|
|
|
|
Self::Less => "LESS",
|
|
|
|
Self::LessEqual => "LESS_EQUAL",
|
|
|
|
Self::Negate => "NEGATE",
|
|
|
|
Self::Not => "NOT",
|
|
|
|
Self::Call => "CALL",
|
|
|
|
Self::CallNative => "CALL_NATIVE",
|
|
|
|
Self::Jump => "JUMP",
|
|
|
|
Self::Return => "RETURN",
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-08 06:04:01 -05:00
|
|
|
impl Debug for Operation {
|
|
|
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
2024-12-09 07:01:07 -05:00
|
|
|
write!(f, "{}", self.name())
|
2024-12-08 06:04:01 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Display for Operation {
|
|
|
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
|
|
write!(f, "{}", self.name())
|
|
|
|
}
|
|
|
|
}
|
2024-12-09 07:01:07 -05:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
|
2024-12-11 08:39:48 -05:00
|
|
|
const ALL_OPERATIONS: [Operation; 24] = [
|
2024-12-09 07:01:07 -05:00
|
|
|
Operation::Move,
|
|
|
|
Operation::Close,
|
|
|
|
Operation::LoadBoolean,
|
|
|
|
Operation::LoadConstant,
|
|
|
|
Operation::LoadList,
|
|
|
|
Operation::LoadSelf,
|
|
|
|
Operation::GetLocal,
|
|
|
|
Operation::SetLocal,
|
|
|
|
Operation::Add,
|
|
|
|
Operation::Subtract,
|
|
|
|
Operation::Multiply,
|
|
|
|
Operation::Divide,
|
|
|
|
Operation::Modulo,
|
|
|
|
Operation::Test,
|
|
|
|
Operation::TestSet,
|
|
|
|
Operation::Equal,
|
|
|
|
Operation::Less,
|
|
|
|
Operation::LessEqual,
|
|
|
|
Operation::Negate,
|
|
|
|
Operation::Not,
|
|
|
|
Operation::Call,
|
|
|
|
Operation::CallNative,
|
|
|
|
Operation::Jump,
|
|
|
|
Operation::Return,
|
|
|
|
];
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn operations_are_unique() {
|
|
|
|
for (i, operation) in ALL_OPERATIONS.into_iter().enumerate() {
|
|
|
|
assert_eq!(i, operation as usize);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn operation_uses_five_bits() {
|
|
|
|
for operation in ALL_OPERATIONS {
|
|
|
|
assert_eq!(operation as u8 & 0b1110_0000, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn operation_is_one_byte() {
|
|
|
|
assert_eq!(size_of::<Operation>(), 1);
|
|
|
|
}
|
|
|
|
}
|