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}