brainfoamkit_lib/instruction.rs
1// SPDX-FileCopyrightText: 2023 - 2024 Ali Sajid Imami
2//
3// SPDX-License-Identifier: Apache-2.0
4// SPDX-License-Identifier: MIT
5
6use std::fmt::{
7 self,
8 Display,
9 Formatter,
10};
11
12/// All possible instructions that can be understood by the interpreter
13///
14/// This enum is at the heart of the interpreter. This enumerates
15/// all eight instructions that a brainfuck program can be composed of
16/// in addition to a ninth No-Op instruction
17///
18/// # Examples
19///
20/// ```
21/// use brainfoamkit_lib::Instruction;
22///
23/// let incrptr = Instruction::from_char('>');
24/// let decrptr = Instruction::from_char('<');
25///
26/// assert_eq!(incrptr, Instruction::IncrementPointer);
27/// assert_eq!(decrptr, Instruction::DecrementPointer);
28/// ```
29#[derive(Debug, PartialEq, Eq, Clone, Copy)]
30pub enum Instruction {
31 /// Instruction to Increment the Pointer
32 ///
33 /// Internal representation of the `>` instruction.
34 IncrementPointer,
35 /// Instruction to Decrement the Pointer
36 ///
37 /// Internal representation of the `<` instruction.
38 DecrementPointer,
39 /// Instruction to Increment the Value stored in memory location
40 ///
41 /// Internal representation of the `+` instruction.
42 IncrementValue,
43 /// Instruction to Increment the Value stored in memory location
44 ///
45 /// Internal representation of the `-` instruction.
46 DecrementValue,
47 /// Instruction to Output the currently stored value to the external
48 /// interface
49 ///
50 /// Internal representation of the `.` instruction.
51 OutputValue,
52 /// Instruction to Input the currently available value at the external
53 /// interface
54 ///
55 /// Internal representation of the `,` instruction.
56 InputValue,
57 /// Instruction to Start a loop if the current value is non-zero
58 ///
59 /// Internal representation of the `[` instruction.
60 JumpForward,
61 /// Instruction to Restart a loop if the current value is non-zero
62 ///
63 /// Internal representation of the `]` instruction.
64 JumpBackward,
65 /// Instruction to do nothing
66 ///
67 /// This does not have a corresponding instruction in `BrainFuck`
68 NoOp,
69}
70
71impl Instruction {
72 /// Convert a char to an Instruction
73 ///
74 /// This method takes in a a single instruction (character
75 /// and converts that into the instruction.
76 ///
77 /// This ignores any instructions not in the standard alphabet
78 /// of `BrainFuck` and counts them as No-Ops
79 ///
80 /// The following table and [associated github repository](https://github.com/AliSajid/BrainFoamKit/tree/alpha/lang#brainfuck-syntax)
81 /// show the entire syntax.
82 ///
83 /// | Symbol | Effect |
84 /// | :------: | :------ |
85 /// | `+` | Increment the value in the current memory cell|
86 /// | `-` | Decrement the value in the current memory cell|
87 /// | `>` | Move the memory pointer to the right|
88 /// | `<` | Move the memory pointer to the left|
89 /// | `[` | Begin a loop: continue if value in memory cell is nonzero|
90 /// | `]` | End a loop: jump back to corresponding `[` if value in memory cell is nonzero|
91 /// | `.` | Output the ASCII value in the current memory cell|
92 /// | `,` | Read a single ASCII character and store it in the current memory cell|
93 /// | _ | Everything else is a No-Up|
94 ///
95 /// # Argument
96 ///
97 /// * `c` - A single character from the `BrainFuck` list of command
98 /// characters.
99 ///
100 /// # Examples
101 ///
102 /// ```
103 /// use brainfoamkit_lib::Instruction;
104 ///
105 /// let instruction = Instruction::from_char('+');
106 ///
107 /// assert_eq!(instruction, Instruction::IncrementValue)
108 /// ```
109 ///
110 /// # Returns
111 ///
112 /// The appropriate variant of the `Instruction` enum
113 ///
114 /// # Notes
115 ///
116 /// This version of `Instruction` treats every character other than the
117 /// eight specific characters as `NoOp`s
118 #[must_use]
119 pub const fn from_char(c: char) -> Self {
120 match c {
121 '>' => Self::IncrementPointer,
122 '<' => Self::DecrementPointer,
123 '+' => Self::IncrementValue,
124 '-' => Self::DecrementValue,
125 '.' => Self::OutputValue,
126 ',' => Self::InputValue,
127 '[' => Self::JumpForward,
128 ']' => Self::JumpBackward,
129 _ => Self::NoOp,
130 }
131 }
132}
133
134/// Convert an instruction to a String
135///
136/// This method converts a given instruction to a human-readable
137/// instruction.
138///
139/// # Examples
140///
141/// ```
142/// use brainfoamkit_lib::{
143/// Instruction,
144/// Nybble,
145/// };
146///
147/// let instruction = Instruction::from_char('>');
148///
149/// assert_eq!(instruction.to_string(), "INCPTR");
150/// ```
151///
152/// # Returns
153///
154/// The instruction as a string.
155///
156/// # See Also
157///
158/// * [`from_char()`](#method.from_char): Creates a new Instruction from a
159/// string.
160impl Display for Instruction {
161 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
162 match *self {
163 Self::IncrementPointer => write!(f, "INCPTR"),
164 Self::DecrementPointer => write!(f, "DECPTR"),
165 Self::IncrementValue => write!(f, "INCVAL"),
166 Self::DecrementValue => write!(f, "DECVAL"),
167 Self::OutputValue => write!(f, "OUTVAL"),
168 Self::InputValue => write!(f, "INPVAL"),
169 Self::JumpForward => write!(f, "JMPFWD"),
170 Self::JumpBackward => write!(f, "JMPBCK"),
171 Self::NoOp => write!(f, "NOOP"),
172 }
173 }
174}
175
176#[cfg(test)]
177mod tests {
178 use super::*;
179
180 #[test]
181 fn test_instruction_from_char() {
182 assert_eq!(Instruction::from_char('>'), Instruction::IncrementPointer);
183 assert_eq!(Instruction::from_char('<'), Instruction::DecrementPointer);
184 assert_eq!(Instruction::from_char('+'), Instruction::IncrementValue);
185 assert_eq!(Instruction::from_char('-'), Instruction::DecrementValue);
186 assert_eq!(Instruction::from_char('.'), Instruction::OutputValue);
187 assert_eq!(Instruction::from_char(','), Instruction::InputValue);
188 assert_eq!(Instruction::from_char('['), Instruction::JumpForward);
189 assert_eq!(Instruction::from_char(']'), Instruction::JumpBackward);
190 assert_eq!(Instruction::from_char(' '), Instruction::NoOp);
191 }
192
193 #[test]
194 fn test_instruction_display() {
195 assert_eq!(format!("{}", Instruction::IncrementPointer), "INCPTR");
196 assert_eq!(format!("{}", Instruction::DecrementPointer), "DECPTR");
197 assert_eq!(format!("{}", Instruction::IncrementValue), "INCVAL");
198 assert_eq!(format!("{}", Instruction::DecrementValue), "DECVAL");
199 assert_eq!(format!("{}", Instruction::OutputValue), "OUTVAL");
200 assert_eq!(format!("{}", Instruction::InputValue), "INPVAL");
201 assert_eq!(format!("{}", Instruction::JumpForward), "JMPFWD");
202 assert_eq!(format!("{}", Instruction::JumpBackward), "JMPBCK");
203 assert_eq!(format!("{}", Instruction::NoOp), "NOOP");
204 }
205}