rdcl_aoc_helpers/machine/
mod.rs

1//! Machine that performs assembly-like instructions.
2use std::convert::TryFrom;
3
4use crate::machine::hook::{HookResult, PreExecuteHook};
5use crate::machine::instruction::MachineInstruction;
6use crate::machine::output_receiver::{NoopOutputReceiver, OutputReceiver};
7use crate::machine::register::{HashMapRegister, MachineRegister};
8
9/// Hooks allow you to influence how the program is executed.
10pub mod hook;
11
12/// A machine instruction.
13pub mod instruction;
14
15/// Whenever your machine produces output.
16pub mod output_receiver;
17
18/// The registers of the machine.
19pub mod register;
20
21/// A machine which can run instructions.
22pub struct Machine<I: MachineInstruction, R: MachineRegister, O: OutputReceiver<R>> {
23    instructions: Vec<I>,
24    pub register: R,
25    pub output_receiver: O,
26    counter: i64,
27}
28
29impl<I: MachineInstruction> Machine<I, HashMapRegister, NoopOutputReceiver> {
30    /// Constructs a new machine which uses a HashMapRegister and the NoopOutputReceiver.
31    pub fn new_simple_machine(
32        instructions: &[I],
33    ) -> Machine<I, HashMapRegister, NoopOutputReceiver> {
34        Machine {
35            instructions: instructions.to_vec(),
36            register: HashMapRegister::default(),
37            output_receiver: NoopOutputReceiver,
38            counter: 0,
39        }
40    }
41}
42
43impl<I, R, O> Machine<I, R, O>
44where
45    I: MachineInstruction,
46    R: MachineRegister,
47    O: OutputReceiver<R>,
48{
49    /// Constructs a new machine where you specify your own register and output receiver.
50    pub fn new_machine(instructions: &[I], register: R, output_receiver: O) -> Machine<I, R, O> {
51        Machine {
52            instructions: instructions.to_vec(),
53            register,
54            output_receiver,
55            counter: 0,
56        }
57    }
58
59    /// Let the program run.
60    pub fn run<H: PreExecuteHook<I>>(&mut self, pre_execute_hook: &mut H) {
61        while let Some((idx, instruction)) = self.get_next_instruction() {
62            match pre_execute_hook.run(self, &instruction, idx) {
63                HookResult::Proceed => {
64                    self.counter +=
65                        instruction.execute(&mut self.register, &mut self.output_receiver);
66                }
67                HookResult::Skip => {
68                    self.counter += 1;
69                }
70                HookResult::Goto(idx) => {
71                    self.counter = idx;
72                }
73                HookResult::Abort => {
74                    return;
75                }
76            }
77        }
78    }
79
80    /// Returns the program counter.
81    pub fn get_counter(&self) -> i64 {
82        self.counter
83    }
84
85    /// Fetch the instruction at position `idx`, if any.
86    pub fn get_instruction(&self, idx: i64) -> Option<(usize, I)> {
87        usize::try_from(idx).ok().and_then(|idx| {
88            self.instructions
89                .get(idx)
90                .map(|instruction| (idx, instruction.clone()))
91        })
92    }
93
94    /// Replaces the instruction at position `idx`, if any.
95    pub fn set_instruction(&mut self, idx: i64, new_instruction: &I) {
96        usize::try_from(idx).ok().and_then(|idx| {
97            self.instructions
98                .get_mut(idx)
99                .map(|instruction| *instruction = new_instruction.clone())
100        });
101    }
102
103    /// Fetch the next instruction, if any.
104    fn get_next_instruction(&self) -> Option<(usize, I)> {
105        self.get_instruction(self.counter)
106    }
107}
108
109#[cfg(test)]
110mod tests {
111    use crate::error::ParseError;
112    use crate::machine::hook::NoopHook;
113    use crate::machine::instruction::ParsedMachineInstruction;
114
115    use super::*;
116
117    #[test]
118    fn test_machine() {
119        let instructions = vec![MockInstruction; 3];
120        let mut machine = Machine::new_simple_machine(&instructions);
121
122        machine.run(&mut NoopHook::default());
123
124        assert_eq!(machine.register.read('a'), 30);
125    }
126
127    #[derive(Clone)]
128    struct MockInstruction;
129
130    impl MachineInstruction for MockInstruction {
131        fn execute<R: MachineRegister, O: OutputReceiver<R>>(
132            &self,
133            register: &mut R,
134            _output_receiver: &mut O,
135        ) -> i64 {
136            register.increment('a', 10);
137            1
138        }
139
140        fn from_parsed_machine_instruction(
141            _parsed: &ParsedMachineInstruction,
142        ) -> Result<Self, ParseError> {
143            unimplemented!()
144        }
145    }
146}