1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
use crate::exception::Exception;
use crate::{InstructionPointer, Machine};
use std::fmt::Debug;

#[derive(Clone)]
pub struct Instruction<Constant, Value: Debug> {
    pub op_code: u8,
    pub name: &'static str,
    pub instruction_fn: InstructionFn<Constant, Value>,
}

#[derive(Clone)]
pub enum InstructionFn<Constant, Value: Debug> {
    Raw {
        byte_arity: usize,
        instruction_fn: RawInstructionFn<Constant, Value>,
    },
    Const(fn() -> Value),
    UnaryOp(fn(value: Value) -> Result<Value, Exception>),
    BinaryOp(fn(left: Value, right: Value) -> Result<Value, Exception>),
}

pub type RawInstructionFn<Constant, Value> = fn(
    machine: &mut Machine<Constant, Value>,
    args_ip: InstructionPointer,
) -> Result<(), Exception>;

impl<Constant, Value: Debug> InstructionFn<Constant, Value> {
    pub fn byte_arity(&self) -> usize {
        if let InstructionFn::Raw { byte_arity, .. } = self {
            *byte_arity
        } else {
            0
        }
    }
    pub fn run(
        &self,
        machine: &mut Machine<Constant, Value>,
        args_ip: InstructionPointer,
    ) -> Result<(), Exception> {
        match self {
            InstructionFn::Raw { instruction_fn, .. } => {
                instruction_fn(machine, args_ip)?;
            }
            InstructionFn::Const(get_value) => {
                machine.push_operand(get_value());
            }
            InstructionFn::UnaryOp(operator) => {
                let operand = machine.pop_operand()?;
                let result = (*operator)(operand)?;
                machine.push_operand(result);
            }
            InstructionFn::BinaryOp(operator) => {
                let (left, right) = machine.pop_two_operands()?;
                let result = (*operator)(left, right)?;
                machine.push_operand(result);
            }
        };
        Ok(())
    }
}