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
|
//! Executor implementation for Brainfuck.
use miette::Diagnostic;
use num_traits::{One, WrappingAdd, WrappingSub, Zero};
use thiserror::Error;
use crate::{engine::Engine, parser::Instruction};
/// Runtime errors that can occur in brainfuck executor.
#[derive(Debug, Diagnostic, Error)]
pub enum Error {
/// Brainfuck engine ran into an error at runtime.
#[error(transparent)]
Engine(#[from] crate::engine::Error),
/// Brainfuck code performed an out of bounds index on the tape during
/// runtime.
#[error("tape indexed out of bounds, attempted index at `{0}`")]
IndexOutOfBounds(usize),
}
/// Struct for executor implementation, allows u8 Engine to be implemented.
#[derive(Clone, Copy, Debug)]
pub struct U8;
/// Struct for executor implementation, allows u16 Engine to be implemented.
#[derive(Clone, Copy, Debug)]
#[cfg(feature = "engine-u16")]
pub struct U16;
/// Struct for executor implementation, allows u32 Engine to be implemented.
#[derive(Clone, Copy, Debug)]
#[cfg(feature = "engine-u32")]
pub struct U32;
/// Trait that must be implemented by all executors.
pub trait Executor<E: Engine> {
/// Executes the provided instruction set, utilising the provided tape.
///
/// # Errors
///
/// This function will return an error if the Brainfuck code indexes out of
/// bounds of the tape, or if the executor cannot read an input byte from
/// stdin.
fn execute(
instructions: &[Instruction],
tape: &mut [E::TapeInner],
data_pointer: &mut usize,
) -> Result<(), Error>;
}
impl<E> Executor<E> for E
where
E: Engine,
{
#[inline]
fn execute(
instructions: &[Instruction],
tape: &mut [<E as Engine>::TapeInner],
data_pointer: &mut usize,
) -> Result<(), Error> {
execute::<E>(instructions, tape, data_pointer)
}
}
/// Executes the provided instruction set, utilising the provided tape. This
/// function allows specifying the Brainfuck engine implementation per call.
///
/// # Errors
///
/// This function will return an error if the Brainfuck code indexes out of
/// bounds of the tape, or if the executor cannot read an input byte from
/// stdin.
pub fn execute<E: Engine>(
instructions: &[Instruction],
tape: &mut [E::TapeInner],
data_pointer: &mut usize,
) -> Result<(), Error> {
for instruction in instructions {
match *instruction {
Instruction::IncrementPointer => {
let tape_len: usize = tape.len() - 1;
if *data_pointer == tape_len {
*data_pointer = 0;
} else {
*data_pointer += 1;
}
}
Instruction::DecrementPointer => {
*data_pointer = match *data_pointer {
0 => tape.len() - 1,
_ => *data_pointer - 1,
};
}
Instruction::IncrementByte => match tape.get_mut(*data_pointer) {
Some(value) => *value = value.wrapping_add(&E::TapeInner::one()),
None => return Err(Error::IndexOutOfBounds(*data_pointer)),
},
Instruction::DecrementByte => match tape.get_mut(*data_pointer) {
Some(value) => *value = value.wrapping_sub(&E::TapeInner::one()),
None => return Err(Error::IndexOutOfBounds(*data_pointer)),
},
Instruction::OutputByte => {
E::write_byte(match tape.get(*data_pointer) {
Some(value) => *value,
None => return Err(Error::IndexOutOfBounds(*data_pointer)),
})?;
}
Instruction::InputByte => {
let input = E::read_byte()?;
match tape.get_mut(*data_pointer) {
Some(value) => *value = input,
None => return Err(Error::IndexOutOfBounds(*data_pointer)),
};
}
Instruction::Loop(ref instructions) => {
while match tape.get(*data_pointer) {
Some(value) => value,
None => return Err(Error::IndexOutOfBounds(*data_pointer)),
} != &E::TapeInner::zero()
{
execute::<E>(instructions, tape, data_pointer)?;
}
}
}
}
Ok(())
}
|