summary refs log tree commit diff
path: root/src/executor.rs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/executor.rs89
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(())
+}