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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
//! Machine that performs assembly-like instructions.
use std::convert::TryFrom;

use crate::machine::hook::{HookResult, PreExecuteHook};
use crate::machine::instruction::MachineInstruction;
use crate::machine::output_receiver::{NoopOutputReceiver, OutputReceiver};
use crate::machine::register::{HashMapRegister, MachineRegister};

/// 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<R>> {
    instructions: Vec<I>,
    pub register: R,
    pub output_receiver: O,
    counter: i64,
}

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<R>,
{
    /// 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<I>>(&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 => {
                    self.counter += 1;
                }
                HookResult::Goto(idx) => {
                    self.counter = idx;
                }
                HookResult::Abort => {
                    return;
                }
            }
        }
    }

    /// Returns the program counter.
    pub fn get_counter(&self) -> i64 {
        self.counter
    }

    /// Fetch the instruction at position `idx`, if any.
    pub fn get_instruction(&self, idx: i64) -> Option<(usize, I)> {
        usize::try_from(idx).ok().and_then(|idx| {
            self.instructions
                .get(idx)
                .map(|instruction| (idx, instruction.clone()))
        })
    }

    /// Replaces the instruction at position `idx`, if any.
    pub fn set_instruction(&mut self, idx: i64, new_instruction: &I) {
        usize::try_from(idx).ok().and_then(|idx| {
            self.instructions
                .get_mut(idx)
                .map(|instruction| *instruction = new_instruction.clone())
        });
    }

    /// Fetch the next instruction, if any.
    fn get_next_instruction(&self) -> Option<(usize, I)> {
        self.get_instruction(self.counter)
    }
}

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

    use super::*;

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

        machine.run(&mut NoopHook::default());

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

    #[derive(Clone)]
    struct MockInstruction;

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

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