1use std::ops::ControlFlow;
6use utils::prelude::*;
7
8#[derive(Clone, Debug)]
9pub(crate) struct Interpreter<const TGL: bool, const OUT: bool> {
10 instructions: Vec<Instruction>,
11}
12
13#[derive(Copy, Clone, PartialEq, Eq, Debug)]
14enum Register {
15 A,
16 B,
17 C,
18 D,
19}
20
21#[derive(Copy, Clone, PartialEq, Eq, Debug)]
22enum Value {
23 Register(Register),
24 Number(i32),
25}
26
27#[derive(Copy, Clone, PartialEq, Eq, Debug)]
28enum Instruction {
29 Copy(Value, Register),
30 Increment(Register),
31 Decrement(Register),
32 JumpIfNotZero(Value, Value),
33 Toggle(Register),
34 Out(Register),
35 Invalid2(Value, Value),
36}
37
38impl<const TGL: bool, const OUT: bool> Interpreter<TGL, OUT> {
39 pub fn new(input: &str) -> Result<Self, InputError> {
40 let register = parser::literal_map!(
41 "a" => Register::A,
42 "b" => Register::B,
43 "c" => Register::C,
44 "d" => Register::D,
45 );
46 let value = register
47 .map(Value::Register)
48 .or(parser::i32().map(Value::Number));
49
50 Ok(Self {
51 instructions: parser::parse_tree!(
52 ("inc ", r @ register) => Instruction::Increment(r),
53 ("dec ", r @ register) => Instruction::Decrement(r),
54 ("cpy ", v @ value, " ", r @ register) => Instruction::Copy(v, r),
55 ("jnz ", v @ value, " ", o @ value) => Instruction::JumpIfNotZero(v, o),
56 ("tgl ", r @ register) =?> {
57 if TGL {
58 Ok(Instruction::Toggle(r))
59 } else {
60 Err("tgl instruction not supported")
61 }
62 },
63 ("out ", r @ register) =?> {
64 if OUT {
65 Ok(Instruction::Out(r))
66 } else {
67 Err("out instruction not supported")
68 }
69 },
70 )
71 .parse_lines(input)?,
72 })
73 }
74}
75
76impl<const TGL: bool> Interpreter<TGL, false> {
77 pub fn execute(&self, reg: [i32; 4]) -> i32 {
78 execute(&self.instructions, reg, |_, _| unreachable!())
79 }
80}
81
82impl<const TGL: bool> Interpreter<TGL, true> {
83 pub fn execute(
84 &self,
85 reg: [i32; 4],
86 out_fn: impl FnMut(i32, (usize, [i32; 4])) -> ControlFlow<()>,
87 ) -> i32 {
88 execute(&self.instructions, reg, out_fn)
89 }
90}
91
92#[inline]
93fn execute(
94 instructions: &[Instruction],
95 mut reg: [i32; 4],
96 mut out_fn: impl FnMut(i32, (usize, [i32; 4])) -> ControlFlow<()>,
97) -> i32 {
98 let mut pc = 0;
99
100 let mut instructions = instructions.to_vec();
101 while pc < instructions.len() {
102 #[rustfmt::skip] match instructions[pc..] {
104 [
110 Instruction::Increment(x),
111 Instruction::Decrement(y),
112 Instruction::JumpIfNotZero(Value::Register(y2), Value::Number(-2)),
113 ..
114 ] if y == y2 => {
115 reg[x as usize] += reg[y as usize];
116 reg[y as usize] = 0;
117 pc += 3;
118 continue;
119 }
120 [
129 Instruction::Copy(Value::Register(w), x),
130 Instruction::Increment(y),
131 Instruction::Decrement(x2),
132 Instruction::JumpIfNotZero(Value::Register(x3), Value::Number(-2)),
133 Instruction::Decrement(z),
134 Instruction::JumpIfNotZero(Value::Register(z2), Value::Number(-5)),
135 ..
136 ] if x == x2 && x == x3 && z == z2 => {
137 reg[y as usize] = reg[w as usize] * reg[z as usize];
138 reg[x as usize] = 0;
139 reg[z as usize] = 0;
140 pc += 6;
141 continue;
142 }
143 [
154 Instruction::Copy(Value::Number(n), x),
155 Instruction::JumpIfNotZero(Value::Register(y), Value::Number(2)),
156 Instruction::JumpIfNotZero(Value::Number(1), Value::Number(6)),
157 Instruction::Decrement(y2),
158 Instruction::Decrement(x2),
159 Instruction::JumpIfNotZero(Value::Register(x3), Value::Number(-4)),
160 Instruction::Increment(z),
161 Instruction::JumpIfNotZero(Value::Number(1), Value::Number(-7)),
162 ..
163 ] if x == x2 && x == x3 && y == y2 => {
164 reg[z as usize] += reg[y as usize] / n;
165 reg[x as usize] = n - (reg[y as usize] % n);
166 reg[y as usize] = 0;
167 pc += 8;
168 continue;
169 }
170 _ => {}
171 };
172
173 match instructions[pc] {
174 Instruction::Copy(v, dst) => reg[dst as usize] = v.get(®),
175 Instruction::Increment(dst) => reg[dst as usize] += 1,
176 Instruction::Decrement(dst) => reg[dst as usize] -= 1,
177 Instruction::JumpIfNotZero(v, offset) => {
178 if v.get(®) != 0 {
179 let offset = offset.get(®);
180 let Some(new_pc) = pc.checked_add_signed(offset as isize) else {
181 break;
182 };
183 pc = new_pc;
184 continue;
185 }
186 }
187 Instruction::Toggle(r) => 'toggle: {
188 let Some(index) = pc.checked_add_signed(reg[r as usize] as isize) else {
189 break 'toggle;
190 };
191 if index >= instructions.len() {
192 break 'toggle;
193 }
194
195 instructions[index] = match instructions[index] {
196 Instruction::Increment(r) => Instruction::Decrement(r),
197 Instruction::Decrement(r) => Instruction::Increment(r),
198 Instruction::Toggle(r) => Instruction::Increment(r),
199 Instruction::Out(r) => Instruction::Increment(r),
200 Instruction::JumpIfNotZero(v, Value::Register(r)) => Instruction::Copy(v, r),
201 Instruction::JumpIfNotZero(v, o @ Value::Number(_)) => {
202 Instruction::Invalid2(v, o)
203 }
204 Instruction::Copy(v, r) => Instruction::JumpIfNotZero(v, Value::Register(r)),
205 Instruction::Invalid2(v1, v2) => {
206 Instruction::JumpIfNotZero(v1, v2)
208 }
209 };
210 }
211 Instruction::Out(r) => {
212 if out_fn(reg[r as usize], (pc, reg)).is_break() {
213 break;
214 }
215 }
216 Instruction::Invalid2(_, _) => {}
217 }
218
219 pc += 1;
220 }
221
222 reg[0]
223}
224
225impl Value {
226 #[inline]
227 fn get(self, registers: &[i32; 4]) -> i32 {
228 match self {
229 Value::Register(r) => registers[r as usize],
230 Value::Number(n) => n,
231 }
232 }
233}