1#![deny(missing_docs)]
20#![allow(dead_code)]
22
23use std::{
24 collections::VecDeque,
25 error::Error,
26 fmt::Display,
27 io::{Read, Write},
28};
29
30pub const DEFAULT_TAPE_SIZE_LIMIT: usize = 30_000;
34
35pub const INSTRUCTION_MAPPINGS: &[u8] = b"[]+-,.<>";
37#[derive(Debug, Clone, PartialEq, Eq)]
38pub struct Interpreter<'a, I> {
40 code: &'a [u8],
41 instr_ptr: usize,
42 tape: Vec<i8>,
43 data_ptr: usize,
44 tape_size_limit: usize,
45 input: I,
46}
47
48impl<'a> Interpreter<'a, InputBuffer> {
49 pub fn from_ascii_with_input_buffer(code: &'a [u8]) -> Self {
52 Self::from_ascii_with_input_buffer_and_tape_limit(code, DEFAULT_TAPE_SIZE_LIMIT)
53 }
54
55 pub fn from_ascii_with_input_buffer_and_tape_limit(
58 code: &'a [u8],
59 tape_size_limit: usize,
60 ) -> Self {
61 Self {
62 code,
63 instr_ptr: 0,
64 tape: Vec::new(),
65 data_ptr: 0,
66 tape_size_limit,
67 input: InputBuffer(VecDeque::new()),
68 }
69 }
70
71 pub fn add_input(&mut self, input: i8) {
73 self.input.0.push_back(input);
74 }
75}
76
77impl<'a, I: BootlegRead> Interpreter<'a, I> {
78 pub fn from_ascii_with_tape_limit(code: &'a [u8], input: I, tape_size_limit: usize) -> Self {
80 Self {
81 code,
82 instr_ptr: 0,
83 tape: Vec::new(),
84 data_ptr: 0,
85 tape_size_limit,
86 input,
87 }
88 }
89
90 pub fn from_ascii(code: &'a [u8], input: I) -> Self {
92 Self::from_ascii_with_tape_limit(code, input, DEFAULT_TAPE_SIZE_LIMIT)
93 }
94
95 pub fn advance(&mut self) -> Result<Option<Status>, ProgramError> {
98 let &opcode = match self.code.get(self.instr_ptr) {
99 Some(opcode) => opcode,
100 None => return Ok(None),
101 };
102
103 match opcode {
104 b'>' => self.data_ptr += 1,
105
106 b'<' => {
107 self.data_ptr = self
108 .data_ptr
109 .checked_sub(1)
110 .ok_or(ProgramError::DataPointerUnderflow)?;
111 }
112
113 b'+' => {
114 let val = self
115 .get_or_resize_tape_mut()
116 .ok_or(ProgramError::TapeSizeExceededLimit)?;
117 *val = val.wrapping_add(1)
118 }
119
120 b'-' => {
121 let val = self
122 .get_or_resize_tape_mut()
123 .ok_or(ProgramError::TapeSizeExceededLimit)?;
124 *val = val.wrapping_sub(1)
125 }
126
127 b'.' => {
128 self.instr_ptr += 1;
129 return Ok(Some(Status::Output(self.get_at_data_ptr())));
130 }
131
132 b',' => match self.input.bootleg_read() {
133 Ok(Some(num)) => {
134 let cell = self
135 .get_or_resize_tape_mut()
136 .ok_or(ProgramError::TapeSizeExceededLimit)?;
137 *cell = num;
138 }
139 Ok(None) => return Ok(Some(Status::NeedsInput)),
140 Err(_) => return Err(ProgramError::InputReadError),
141 },
142
143 b'[' => {
144 if self.get_at_data_ptr() == 0 {
145 self.instr_ptr = self
146 .get_matching_closing_bracket(self.instr_ptr)
147 .ok_or(ProgramError::UnmatchedOpeningBracket)?
148 }
150 }
151
152 b']' => {
153 if self.get_at_data_ptr() != 0 {
154 self.instr_ptr = self
155 .get_matching_opening_bracket(self.instr_ptr)
156 .ok_or(ProgramError::UnmatchedClosingBracket)?
157 }
159 }
160
161 _ => {} }
163
164 self.instr_ptr += 1;
165
166 Ok(Some(Status::Continue))
167 }
168
169 pub fn advance_until_io(&mut self) -> Result<Option<IoStatus>, ProgramError> {
171 while let Some(status) = self.advance()? {
172 match status {
173 Status::NeedsInput => return Ok(Some(IoStatus::NeedsInput)),
174 Status::Output(out) => return Ok(Some(IoStatus::Output(out))),
175 Status::Continue => continue,
176 }
177 }
178 Ok(None)
179 }
180
181 pub fn interpret_with_output<O: Write>(&mut self, mut output: O) -> Result<(), InterpretError> {
184 while let Some(status) = self.advance_until_io()? {
185 match status {
186 IoStatus::NeedsInput => return Err(InterpretError::EndOfInput),
187 IoStatus::Output(out) => match output.write(&[out as u8]) {
188 Ok(0) => return Err(InterpretError::OutputBufferFull),
189 Ok(_) => continue,
190 Err(_) => return Err(InterpretError::OutputWriteError),
191 },
192 }
193 }
194 Ok(())
195 }
196
197 fn get_or_resize_tape_mut(&mut self) -> Option<&mut i8> {
198 if self.data_ptr > self.tape_size_limit {
199 return None;
200 }
201 if self.data_ptr >= self.tape.len() {
202 self.tape.resize(self.data_ptr + 1, 0);
203 }
204 Some(&mut self.tape[self.data_ptr])
205 }
206
207 fn get_at_data_ptr(&self) -> i8 {
208 self.tape.get(self.data_ptr).copied().unwrap_or(0)
210 }
211
212 fn get_matching_closing_bracket(&mut self, opening: usize) -> Option<usize> {
213 self.code[opening..]
214 .iter()
215 .zip(opening..)
216 .scan(0, |counter, (char, index)| {
217 match char {
218 b'[' => *counter += 1,
219 b']' => *counter -= 1,
220 _ => {}
221 };
222 Some((*counter, index))
223 })
224 .find_map(|(counter, index)| (counter == 0).then_some(index))
225 }
226
227 fn get_matching_opening_bracket(&mut self, closing: usize) -> Option<usize> {
228 self.code[..closing + 1]
229 .iter()
230 .zip(0..closing + 1)
231 .rev()
232 .scan(0, |counter, (char, index)| {
233 match char {
234 b']' => *counter += 1,
235 b'[' => *counter -= 1,
236 _ => {}
237 };
238 Some((*counter, index))
239 })
240 .find_map(|(counter, index)| (counter == 0).then_some(index))
241 }
242}
243
244pub fn interpret_with_io<I: BootlegRead, O: Write>(
247 code: &[u8],
248 input: I,
249 output: O,
250) -> Result<(), InterpretError> {
251 Interpreter::from_ascii(code, input).interpret_with_output(output)
252}
253
254#[derive(Debug, Clone, Copy, PartialEq, Eq)]
255pub enum Status {
257 NeedsInput,
258 Output(i8),
259 Continue,
260}
261
262#[derive(Debug, Clone, Copy, PartialEq, Eq)]
263pub enum IoStatus {
265 NeedsInput,
266 Output(i8),
267}
268
269#[derive(Debug, Clone, Copy, PartialEq, Eq)]
270pub enum ProgramError {
272 DataPointerUnderflow,
273 InputReadError,
274 UnmatchedOpeningBracket,
275 UnmatchedClosingBracket,
276 TapeSizeExceededLimit,
277}
278
279impl Display for ProgramError {
280 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
281 write!(
282 f,
283 "{}",
284 match self {
285 ProgramError::DataPointerUnderflow => "data pointer underflow",
286 ProgramError::InputReadError => "input read error",
287 ProgramError::UnmatchedOpeningBracket => "unmatched `[`",
288 ProgramError::UnmatchedClosingBracket => "unmatched `]`",
289 ProgramError::TapeSizeExceededLimit => "tape size exceeded",
290 }
291 )
292 }
293}
294impl Error for ProgramError {}
295
296#[derive(Debug, Clone, Copy, PartialEq, Eq)]
297pub enum InterpretError {
299 ProgramError(ProgramError),
300 EndOfInput,
301 OutputBufferFull,
302 OutputWriteError,
303}
304
305impl Display for InterpretError {
306 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
307 match self {
308 InterpretError::ProgramError(e) => write!(f, "program error: {}", e),
309 InterpretError::EndOfInput => write!(f, "unexpected end of input"),
310 InterpretError::OutputBufferFull => write!(f, "output buffer full"),
311 InterpretError::OutputWriteError => write!(f, "output write error"),
312 }
313 }
314}
315impl Error for InterpretError {}
316
317impl From<ProgramError> for InterpretError {
318 fn from(e: ProgramError) -> Self {
319 InterpretError::ProgramError(e)
320 }
321}
322
323pub trait BootlegRead {
326 type Error;
327 fn bootleg_read(&mut self) -> Result<Option<i8>, Self::Error>;
328}
329
330impl<T: Read> BootlegRead for T {
331 type Error = std::io::Error;
332 fn bootleg_read(&mut self) -> Result<Option<i8>, Self::Error> {
333 let mut buffer = [0];
334 match self.read(&mut buffer) {
335 Ok(0) => Ok(None),
336 Ok(_) => Ok(Some(buffer[0] as i8)),
337 Err(e) => Err(e),
338 }
339 }
340}
341
342struct InputBuffer(VecDeque<i8>);
344impl BootlegRead for InputBuffer {
345 type Error = std::convert::Infallible;
346 fn bootleg_read(&mut self) -> Result<Option<i8>, Self::Error> {
347 Ok(self.0.pop_front())
348 }
349}
350
351#[cfg(test)]
352mod tests {
353 use super::*;
354
355 #[test]
356 fn adder() {
357 let mut interpreter = Interpreter {
358 code: b"[->+<]", instr_ptr: 0,
360 tape: vec![10, 5],
361 data_ptr: 0,
362 tape_size_limit: DEFAULT_TAPE_SIZE_LIMIT,
363 input: std::io::empty(),
364 };
365
366 while let Some(status) = interpreter.advance_until_io().expect("Unexpected error") {
367 match status {
368 IoStatus::NeedsInput => panic!("Requested input in an IO-less program"),
369 IoStatus::Output(_) => panic!("Produced output in an IO-less program"),
370 }
371 }
372
373 assert_eq!(interpreter.tape, vec![0, 15]);
374 }
375
376 #[test]
377 fn hello_world() {
378 let mut interpreter = Interpreter::from_ascii(
379 b"++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.",
380 std::io::empty(),
381 );
382
383 let mut string = Vec::new();
384 interpreter
385 .interpret_with_output(&mut string)
386 .expect("Failed to write to output buffer");
387 assert_eq!(string, b"Hello World!\n");
388 }
389
390 #[test]
391 fn with_input_buffer() {
392 let mut interpreter = Interpreter::from_ascii_with_input_buffer(b"+++++.>,[-<->].");
393 let output = match interpreter
394 .advance_until_io()
395 .expect("Unexpected error")
396 .expect("Unexpected termination")
397 {
398 IoStatus::NeedsInput => panic!("Unexpected input request"),
399 IoStatus::Output(out) => out,
400 };
401
402 assert_eq!(
403 interpreter.advance_until_io(),
404 Ok(Some(IoStatus::NeedsInput))
405 );
406
407 interpreter.add_input(output);
408
409 assert_eq!(
410 interpreter.advance_until_io(),
411 Ok(Some(IoStatus::Output(0)))
412 );
413 assert_eq!(interpreter.advance_until_io(), Ok(None));
414 }
415
416 #[test]
417 fn hit_tape_size_limit() {
418 let mut interpreter =
419 Interpreter::from_ascii_with_tape_limit(b"+>+>+>+>+>", std::io::empty(), 1);
420 let result = interpreter.interpret_with_output(std::io::sink());
421 assert_eq!(
422 result,
423 Err(InterpretError::ProgramError(
424 ProgramError::TapeSizeExceededLimit
425 ))
426 );
427 }
428
429 #[test]
430 fn positive_integer_overflow() {
431 interpret_with_io(b"+[+]", std::io::empty(), std::io::sink()).unwrap();
432 }
433
434 #[test]
435 fn negative_integer_overflow() {
436 interpret_with_io(b"-", std::io::empty(), std::io::sink()).unwrap();
437 }
438}