diff options
Diffstat (limited to 'src/executor.rs')
| -rw-r--r-- | src/executor.rs | 89 |
1 files changed, 89 insertions, 0 deletions
diff --git a/src/executor.rs b/src/executor.rs new file mode 100644 index 0000000..f368ea9 --- /dev/null +++ b/src/executor.rs @@ -0,0 +1,89 @@ +//! Executor implementation for Brainfuck. + +use std::io::Read; + +use miette::Diagnostic; +use thiserror::Error; + +use crate::{constants::TapeInner, parser::Instruction}; + +/// Runtime errors that can occur in brainfuck executor. +#[derive(Debug, Diagnostic, Error)] +pub enum 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), + + /// Executor was unable to read an input byte from stdin. + #[error("could not read input from stdin")] + ReadInput(#[from] std::io::Error), +} + +/// 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. +pub fn execute( + instructions: &[Instruction], + tape: &mut [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.overflowing_add(1).0, + None => return Err(Error::IndexOutOfBounds(*data_pointer)), + }, + Instruction::DecrementByte => match tape.get_mut(*data_pointer) { + Some(value) => *value = value.overflowing_sub(1).0, + None => return Err(Error::IndexOutOfBounds(*data_pointer)), + }, + Instruction::OutputByte => print!( + "{}", + char::from(match tape.get(*data_pointer) { + Some(value) => *value, + None => return Err(Error::IndexOutOfBounds(*data_pointer)), + }) + ), + Instruction::InputByte => { + let mut input: [TapeInner; 1] = [0; 1]; + + std::io::stdin().read_exact(&mut input)?; + + match tape.get_mut(*data_pointer) { + Some(value) => *value = input[0], + 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)), + } != 0 + { + execute(instructions, tape, data_pointer)?; + } + } + } + } + + Ok(()) +} |