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
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
use crate::machine::hook::{HookResult, PreExecuteHook};
use crate::machine::instruction::MachineInstruction;
use crate::machine::output_receiver::{NoopOutputReceiver, OutputReceiver};
use crate::machine::register::{HashMapRegister, MachineRegister};
use std::convert::TryFrom;

/// Hooks allow you to influence how the program is executed.
pub mod hook;

/// A machine instruction.
pub mod instruction;

/// Whenever your machine produces output.
pub mod output_receiver;

/// The registers of the machine.
pub mod register;

/// A machine which can run instructions.
pub struct Machine<I: MachineInstruction, R: MachineRegister, O: OutputReceiver> {
    instructions: Vec<I>,
    pub register: R,
    pub output_receiver: O,
    counter: i32,
}

impl<I: MachineInstruction> Machine<I, HashMapRegister, NoopOutputReceiver> {
    /// Constructs a new machine which uses a HashMapRegister and the NoopOutputReceiver.
    pub fn new_simple_machine(
        instructions: &[I],
    ) -> Machine<I, HashMapRegister, NoopOutputReceiver> {
        Machine {
            instructions: instructions.to_vec(),
            register: HashMapRegister::default(),
            output_receiver: NoopOutputReceiver,
            counter: 0,
        }
    }
}

impl<I, R, O> Machine<I, R, O>
where
    I: MachineInstruction,
    R: MachineRegister,
    O: OutputReceiver,
{
    /// Constructs a new machine where you specify your own register and output receiver.
    pub fn new_machine(instructions: &[I], register: R, output_receiver: O) -> Machine<I, R, O> {
        Machine {
            instructions: instructions.to_vec(),
            register,
            output_receiver,
            counter: 0,
        }
    }

    /// Let the program run.
    pub fn run<H: PreExecuteHook>(&mut self, pre_execute_hook: &mut H) {
        while let Some((idx, instruction)) = self.get_next_instruction() {
            match pre_execute_hook.run(self, &instruction, idx) {
                HookResult::Proceed => {
                    self.counter +=
                        instruction.execute(&mut self.register, &mut self.output_receiver);
                }
                HookResult::Skip => {}
                HookResult::Abort => {
                    return;
                }
            }
        }
    }

    /// Fetch the next instruction, if any.
    fn get_next_instruction(&self) -> Option<(usize, I)> {
        usize::try_from(self.counter).ok().and_then(|idx| {
            self.instructions
                .get(idx)
                .map(|instruction| (idx, instruction.clone()))
        })
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::error::ParseError;
    use crate::machine::hook::NoopHook;
    use crate::machine::instruction::ParsedMachineInstruction;

    #[test]
    fn test_machine() {
        let instructions = vec![MockInstruction; 3];
        let mut machine = Machine::new_simple_machine(&instructions);

        machine.run(&mut NoopHook);

        assert_eq!(machine.register.read('a'), 30);
    }

    #[derive(Clone)]
    struct MockInstruction;

    impl MachineInstruction for MockInstruction {
        fn execute<R: MachineRegister, O: OutputReceiver>(
            &self,
            register: &mut R,
            _output_receiver: &mut O,
        ) -> i32 {
            register.increment('a', 10);
            1
        }

        fn from_parsed_machine_instruction(
            _parsed: &ParsedMachineInstruction,
        ) -> Result<Self, ParseError> {
            unimplemented!()
        }
    }
}