use crate::bytecode::bytecode_interpreter::Opcode::{ADD, DIVIDE, EXP, MULTIPLY, PUSH, SUBTRACT};
pub enum Opcode {
PUSH,
ADD,
SUBTRACT,
MULTIPLY,
DIVIDE,
EXP,
}
impl Opcode {
pub fn byte_from_opcode(&self) -> u8 {
match self {
PUSH => 0,
ADD => 1,
SUBTRACT => 2,
MULTIPLY => 3,
DIVIDE => 4,
EXP => 5,
}
}
pub fn opcode_from_byte(byte: u8) -> Self {
match byte {
0 => PUSH,
1 => ADD,
2 => SUBTRACT,
3 => MULTIPLY,
4 => DIVIDE,
5 => EXP,
_ => panic!("Unknown opcode {}", byte),
}
}
}
const IMM_LEN: usize = 8;
pub fn interpret(bytes: &Vec<u8>) -> Result<f64, String> {
let mut index = 0;
let mut stack: Vec<f64> = Vec::new();
let mut errors = String::new();
while index < bytes.len() {
let operation = bytes[index];
match Opcode::opcode_from_byte(operation) {
PUSH => {
index += 1;
let mut immediate_bytes = [0u8; IMM_LEN];
immediate_bytes[..IMM_LEN].copy_from_slice(&bytes[index..(index + IMM_LEN)]);
stack.push(f64::from_le_bytes(immediate_bytes));
index += 8; },
ADD | SUBTRACT | MULTIPLY | DIVIDE | EXP => {
index += 1;
if stack.len() < 2 {
errors.push_str("Binary operation attempted with insufficient operands!\n");
continue
}
let right = stack.pop().unwrap();
let left = stack.pop().unwrap();
match Opcode::opcode_from_byte(operation) {
ADD => stack.push(left + right),
SUBTRACT => stack.push(left - right),
MULTIPLY => stack.push(left * right),
DIVIDE => {
if right == 0.0 {
errors.push_str("Cannot divide by zero.\n");
continue
}
stack.push(left / right)
},
EXP => stack.push(left.powf(right)),
_ => errors.push_str(&format!("Unknown binary operation: {}\n", operation)),
}
}
}
}
if stack.is_empty() {
errors.push_str("No result.\n");
}
if errors.is_empty() {
Ok(stack.pop().unwrap())
} else {
Err(errors)
}
}
#[cfg(test)]
mod tests {
use crate::bytecode::bytecode_interpreter::interpret;
#[test]
fn test_add() {
let mut code = Vec::new();
code.push(0u8);
code.extend_from_slice(&f64::to_le_bytes(1.0));
code.push(0u8);
code.extend_from_slice(&f64::to_le_bytes(f64::MAX - 1.0));
code.push(1u8);
assert_eq!(interpret(&code).unwrap(), f64::MAX);
}
#[test]
fn test_subtract() {
let mut code = Vec::new();
code.push(0u8);
code.extend_from_slice(&f64::to_le_bytes(1.0));
code.push(0u8);
code.extend_from_slice(&f64::to_le_bytes(2.0));
code.push(2u8);
assert_eq!(interpret(&code).unwrap(), -1.0);
}
#[test]
fn test_multiply() {
let mut code = Vec::new();
code.push(0u8);
code.extend_from_slice(&f64::to_le_bytes(2.0));
code.push(0u8);
code.extend_from_slice(&f64::to_le_bytes(128.0));
code.push(3u8);
assert_eq!(interpret(&code).unwrap(), 256.0);
}
#[test]
fn test_divide() {
let mut code = Vec::new();
code.push(0u8);
code.extend_from_slice(&f64::to_le_bytes(2.0));
code.push(0u8);
code.extend_from_slice(&f64::to_le_bytes(4.0));
code.push(4u8);
assert_eq!(interpret(&code).unwrap(), 0.5);
}
#[test]
fn test_divide_zero() {
let mut code = Vec::new();
code.push(0u8);
code.extend_from_slice(&f64::to_le_bytes(2.0));
code.push(0u8);
code.extend_from_slice(&f64::to_le_bytes(0.0));
code.push(4u8);
assert_eq!(interpret(&code), Err("Cannot divide by zero.\nNo result.\n".to_string()));
}
#[test]
fn test_pow() {
let mut code = Vec::new();
code.push(0u8);
code.extend_from_slice(&f64::to_le_bytes(2.0));
code.push(0u8);
code.extend_from_slice(&f64::to_le_bytes(2.0));
code.push(5u8);
assert_eq!(interpret(&code).unwrap(), 4.0);
}
#[test]
fn test_double_exp() {
let mut code = Vec::new();
code.push(0u8);
code.extend_from_slice(&f64::to_le_bytes(2.0));
code.push(0u8);
code.extend_from_slice(&f64::to_le_bytes(3.0));
code.push(0u8);
code.extend_from_slice(&f64::to_le_bytes(2.0));
code.push(5u8); code.push(5u8);
assert_eq!(interpret(&code).unwrap(), 512.0);
}
}