summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorSophie Forrest <git@sophieforrest.com>2024-08-30 23:35:45 +1200
committerSophie Forrest <git@sophieforrest.com>2024-08-30 23:35:45 +1200
commit3c163eabc78ddbd26bb250ef5ad6da28cd61adc6 (patch)
tree58e17534e1db18813554d4fb6e67020f898b655d /src
parent17b78f8cb127817b93f7e6ced7e55d8748806a80 (diff)
feat: split engine into crates
Diffstat (limited to 'src')
-rw-r--r--src/engine.rs118
-rw-r--r--src/executor.rs131
-rw-r--r--src/lexer.rs79
-rw-r--r--src/lib.rs204
-rw-r--r--src/main.rs128
-rw-r--r--src/parser.rs170
-rw-r--r--src/utility.rs50
7 files changed, 0 insertions, 880 deletions
diff --git a/src/engine.rs b/src/engine.rs
deleted file mode 100644
index 3269dff..0000000
--- a/src/engine.rs
+++ /dev/null
@@ -1,118 +0,0 @@
-//! Executor engine implementation for Brainfuck interpreter.
-//!
-//! This predominantly allows implementation of a [`u16`] executor.
-
-#[cfg(feature = "bigint-engine")]
-use std::io::Cursor;
-use std::io::Read;
-
-#[cfg(feature = "bigint-engine")]
-use byteorder::{BigEndian, ReadBytesExt};
-use num_traits::{One, Unsigned, WrappingAdd, WrappingSub, Zero};
-use thiserror::Error;
-
-use crate::executor;
-
-/// Error type for engine errors
-#[derive(Debug, Error)]
-pub enum Error {
-	/// Engine ran into an io Error
-	#[error(transparent)]
-	Io(#[from] std::io::Error),
-
-	/// Utf16 error from widestring
-	#[error("could not convert byte to Utf16Str")]
-	Utf16,
-
-	/// Utf32 error from widestring
-	#[error("could not convert byte to Utf32Str")]
-	Utf32,
-}
-
-/// Generic engine implementation for the Brainfuck interpreter.
-pub trait Engine {
-	/// Inner type of the Tape.
-	type TapeInner: Clone + Copy + Unsigned + WrappingAdd + WrappingSub + One + Zero;
-
-	/// Read one byte from stdin.
-	///
-	/// # Errors
-	///
-	/// This function will return an error if it is unable to read from stdin,
-	/// or if it indexes out of bounds.
-	fn read_byte() -> Result<Self::TapeInner, Error>;
-
-	/// Write the provided byte to stdout.
-	///
-	/// # Errors
-	///
-	/// This function will return an error if it is unable to write a byte to
-	/// stdout.
-	fn write_byte(byte: Self::TapeInner) -> Result<(), Error>;
-}
-
-impl Engine for executor::U8 {
-	type TapeInner = u8;
-
-	fn read_byte() -> Result<u8, Error> {
-		let mut input: [u8; 1] = [0; 1];
-
-		std::io::stdin().read_exact(&mut input)?;
-
-		Ok(input[0])
-	}
-
-	fn write_byte(byte: u8) -> Result<(), Error> {
-		print!("{}", char::from(byte));
-
-		Ok(())
-	}
-}
-
-#[cfg(feature = "engine-u16")]
-impl Engine for executor::U16 {
-	type TapeInner = u16;
-
-	fn read_byte() -> Result<u16, Error> {
-		let mut input: [u8; 2] = [0; 2];
-
-		std::io::stdin().read_exact(&mut input)?;
-
-		let mut reader = Cursor::new(input);
-
-		Ok(reader.read_u16::<BigEndian>()?)
-	}
-
-	fn write_byte(byte: u16) -> Result<(), Error> {
-		print!(
-			"{}",
-			widestring::Utf16Str::from_slice(&[byte]).map_err(|_err| Error::Utf16)?
-		);
-
-		Ok(())
-	}
-}
-
-#[cfg(feature = "engine-u32")]
-impl Engine for executor::U32 {
-	type TapeInner = u32;
-
-	fn read_byte() -> Result<u32, Error> {
-		let mut input: [u8; 4] = [0; 4];
-
-		std::io::stdin().read_exact(&mut input)?;
-
-		let mut reader = Cursor::new(input);
-
-		Ok(reader.read_u32::<BigEndian>()?)
-	}
-
-	fn write_byte(byte: u32) -> Result<(), Error> {
-		print!(
-			"{}",
-			widestring::Utf32Str::from_slice(&[byte]).map_err(|_err| Error::Utf32)?
-		);
-
-		Ok(())
-	}
-}
diff --git a/src/executor.rs b/src/executor.rs
deleted file mode 100644
index c5fff93..0000000
--- a/src/executor.rs
+++ /dev/null
@@ -1,131 +0,0 @@
-//! 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(())
-}
diff --git a/src/lexer.rs b/src/lexer.rs
deleted file mode 100644
index 786c873..0000000
--- a/src/lexer.rs
+++ /dev/null
@@ -1,79 +0,0 @@
-//! Lexer for Brainfuck
-
-/// List of operator codes for the lexer
-/// Note: Any input symbol that is not in this list is a comment
-#[derive(Clone, Copy, Debug)]
-pub enum OperatorCode {
-	/// `>`
-	///
-	/// Increment the data pointer by one (to point to the next cell to the
-	/// right).
-	IncrementPointer,
-
-	/// `<`
-	///
-	/// Decrement the data pointer by one (to point to the next cell to the
-	/// left).
-	DecrementPointer,
-
-	/// `+`
-	///
-	/// Increment the byte at the data pointer by one.
-	IncrementByte,
-
-	/// `-`
-	///
-	/// Decrement the byte at the data pointer by one.
-	DecrementByte,
-
-	/// `.`
-	///
-	/// Output the byte at the data pointer.
-	OutputByte,
-
-	/// `,`
-	///
-	/// Accept one byte of input, storing its value in the byte at the data
-	/// pointer.
-	InputByte,
-
-	/// `[`
-	///
-	/// If the byte at the data pointer is zero, then instead of moving the
-	/// instruction pointer forward to the next command, jump it forward to the
-	/// command after the matching ] command.
-	StartLoop {
-		/// Offset of the bracket in the source.
-		offset: usize,
-	},
-
-	/// `]`
-	///
-	/// If the byte at the data pointer is nonzero, then instead of moving the
-	/// instruction pointer forward to the next command, jump it back to the
-	/// command after the matching [ command.
-	EndLoop {
-		/// Offset of the bracket in the source.
-		offset: usize,
-	},
-}
-
-/// Perform lexical analysis on the input brainfuck code
-#[must_use]
-pub fn lex(input: &str) -> Vec<OperatorCode> {
-	input
-		.char_indices()
-		.filter_map(|(i, symbol)| match symbol {
-			'>' => Some(OperatorCode::IncrementPointer),
-			'<' => Some(OperatorCode::DecrementPointer),
-			'+' => Some(OperatorCode::IncrementByte),
-			'-' => Some(OperatorCode::DecrementByte),
-			'.' => Some(OperatorCode::OutputByte),
-			',' => Some(OperatorCode::InputByte),
-			'[' => Some(OperatorCode::StartLoop { offset: i }),
-			']' => Some(OperatorCode::EndLoop { offset: i }),
-			// Any symbol that does not match one of the above is a comment
-			_ => None,
-		})
-		.collect()
-}
diff --git a/src/lib.rs b/src/lib.rs
deleted file mode 100644
index 71c5a99..0000000
--- a/src/lib.rs
+++ /dev/null
@@ -1,204 +0,0 @@
-#![allow(incomplete_features)]
-#![feature(async_fn_in_trait)]
-#![feature(custom_inner_attributes)]
-#![feature(lint_reasons)]
-#![feature(never_type)]
-#![feature(test)]
-#![deny(clippy::complexity)]
-#![deny(clippy::nursery)]
-#![deny(clippy::pedantic)]
-#![deny(clippy::perf)]
-#![deny(clippy::suspicious)]
-#![deny(clippy::alloc_instead_of_core)]
-#![deny(clippy::as_underscore)]
-#![deny(clippy::clone_on_ref_ptr)]
-#![deny(clippy::create_dir)]
-#![warn(clippy::dbg_macro)]
-#![deny(clippy::default_numeric_fallback)]
-#![deny(clippy::default_union_representation)]
-#![deny(clippy::deref_by_slicing)]
-#![deny(clippy::empty_structs_with_brackets)]
-#![deny(clippy::exit)]
-#![deny(clippy::expect_used)]
-#![deny(clippy::filetype_is_file)]
-#![deny(clippy::fn_to_numeric_cast)]
-#![deny(clippy::format_push_string)]
-#![deny(clippy::get_unwrap)]
-#![deny(clippy::if_then_some_else_none)]
-#![allow(
-	clippy::implicit_return,
-	reason = "returns should be done implicitly, not explicitly"
-)]
-#![deny(clippy::indexing_slicing)]
-#![deny(clippy::large_include_file)]
-#![deny(clippy::let_underscore_must_use)]
-#![deny(clippy::lossy_float_literal)]
-#![deny(clippy::map_err_ignore)]
-#![deny(clippy::mem_forget)]
-#![deny(clippy::missing_docs_in_private_items)]
-#![deny(clippy::missing_trait_methods)]
-#![deny(clippy::mod_module_files)]
-#![deny(clippy::multiple_inherent_impl)]
-#![deny(clippy::mutex_atomic)]
-#![deny(clippy::needless_return)]
-#![deny(clippy::non_ascii_literal)]
-#![deny(clippy::panic_in_result_fn)]
-#![deny(clippy::pattern_type_mismatch)]
-#![deny(clippy::rc_buffer)]
-#![deny(clippy::rc_mutex)]
-#![deny(clippy::rest_pat_in_fully_bound_structs)]
-#![deny(clippy::same_name_method)]
-#![deny(clippy::separated_literal_suffix)]
-#![deny(clippy::str_to_string)]
-#![deny(clippy::string_add)]
-#![deny(clippy::string_slice)]
-#![deny(clippy::string_to_string)]
-#![allow(
-	clippy::tabs_in_doc_comments,
-	reason = "tabs are preferred for this project"
-)]
-#![deny(clippy::try_err)]
-#![deny(clippy::undocumented_unsafe_blocks)]
-#![deny(clippy::unnecessary_self_imports)]
-#![deny(clippy::unneeded_field_pattern)]
-#![deny(clippy::unwrap_in_result)]
-#![deny(clippy::unwrap_used)]
-#![warn(clippy::use_debug)]
-#![deny(clippy::verbose_file_reads)]
-#![deny(clippy::wildcard_dependencies)]
-#![deny(clippy::wildcard_enum_match_arm)]
-#![deny(missing_copy_implementations)]
-#![deny(missing_debug_implementations)]
-#![deny(missing_docs)]
-#![deny(single_use_lifetimes)]
-#![deny(unsafe_code)]
-#![deny(unused)]
-
-//! Brainfuck RS
-//!
-//! Implementation of a Brainfuck interpreter written in Rust.
-
-#[cfg(test)]
-extern crate test;
-
-mod engine;
-pub mod executor;
-pub mod lexer;
-pub mod parser;
-#[cfg(feature = "utilities")]
-pub mod utility;
-
-pub use executor::{U16 as ExecutorU16, U32 as ExecutorU32, U8 as ExecutorU8};
-pub use lexer::{lex, OperatorCode};
-use miette::Diagnostic;
-pub use parser::{parse, Instruction};
-use thiserror::Error;
-
-/// Top-level error type for Brainfuck interpreter.
-#[derive(Debug, Diagnostic, Error)]
-pub enum Error {
-	/// Error occurred when reading input from a file.
-	#[error(transparent)]
-	Io(#[from] std::io::Error),
-
-	/// An error that occurred while parsing Brainfuck code.
-	#[diagnostic(transparent)]
-	#[error(transparent)]
-	Parser(#[from] parser::Error),
-
-	/// An error that occurred during runtime.
-	#[error(transparent)]
-	Runtime(#[from] executor::Error),
-}
-
-#[cfg(test)]
-mod tests {
-	use test::Bencher;
-
-	use super::*;
-
-	#[test]
-	fn hello_world() -> Result<(), Error> {
-		let mut tape: Vec<u8> = vec![0; 1024];
-
-		utility::execute_from_file::<executor::U8>("./test_programs/hello_world.bf", &mut tape)?;
-
-		Ok(())
-	}
-
-	#[test]
-	fn hello_world_u16() -> Result<(), Error> {
-		let mut tape: Vec<u16> = vec![0; 1024];
-
-		utility::execute_from_file::<executor::U16>("./test_programs/hello_world.bf", &mut tape)?;
-
-		Ok(())
-	}
-
-	#[test]
-	fn hello_world_from_hell() -> Result<(), Error> {
-		let mut tape: Vec<u16> = vec![0; 1024];
-
-		utility::execute_from_file::<executor::U16>(
-			"./test_programs/hello_world_from_hell.bf",
-			&mut tape,
-		)?;
-
-		Ok(())
-	}
-
-	#[bench]
-	fn hello_world_from_hell_bench_u8(b: &mut Bencher) {
-		b.iter(|| {
-			let mut tape: Vec<u8> = vec![0; 1024];
-
-			#[allow(clippy::expect_used)]
-			utility::execute_from_file::<executor::U8>(
-				"./test_programs/hello_world_from_hell.bf",
-				&mut tape,
-			)
-			.expect("failed to run");
-		});
-	}
-
-	#[bench]
-	fn hello_world_from_hell_bench_u16(b: &mut Bencher) {
-		b.iter(|| {
-			let mut tape: Vec<u16> = vec![0; 1024];
-
-			#[allow(clippy::expect_used)]
-			utility::execute_from_file::<executor::U16>(
-				"./test_programs/hello_world_from_hell.bf",
-				&mut tape,
-			)
-			.expect("failed to run");
-		});
-	}
-
-	#[bench]
-	fn hello_world_from_hell_bench_u32(b: &mut Bencher) {
-		b.iter(|| {
-			let mut tape: Vec<u32> = vec![0; 1024];
-
-			#[allow(clippy::expect_used)]
-			utility::execute_from_file::<executor::U32>(
-				"./test_programs/hello_world_from_hell.bf",
-				&mut tape,
-			)
-			.expect("failed to run");
-		});
-	}
-
-	#[test]
-	fn hello_world_short() -> Result<(), Error> {
-		let mut tape: Vec<u8> = vec![0; 1024];
-
-		utility::execute_from_str::<executor::U8>(
-			"--[+++++++<---->>-->+>+>+<<<<]<.>++++[-<++++>>->--<<]>>-.>--..>+.<<<.<<-.>>+>->>.\
-			 +++[.<]",
-			&mut tape,
-		)?;
-
-		Ok(())
-	}
-}
diff --git a/src/main.rs b/src/main.rs
deleted file mode 100644
index 13868a6..0000000
--- a/src/main.rs
+++ /dev/null
@@ -1,128 +0,0 @@
-#![allow(incomplete_features)]
-#![feature(async_fn_in_trait)]
-#![feature(custom_inner_attributes)]
-#![feature(lint_reasons)]
-#![feature(never_type)]
-#![deny(clippy::complexity)]
-#![deny(clippy::nursery)]
-#![deny(clippy::pedantic)]
-#![deny(clippy::perf)]
-#![deny(clippy::suspicious)]
-#![deny(clippy::alloc_instead_of_core)]
-#![deny(clippy::as_underscore)]
-#![deny(clippy::clone_on_ref_ptr)]
-#![deny(clippy::create_dir)]
-#![warn(clippy::dbg_macro)]
-#![deny(clippy::default_numeric_fallback)]
-#![deny(clippy::default_union_representation)]
-#![deny(clippy::deref_by_slicing)]
-#![deny(clippy::empty_structs_with_brackets)]
-#![deny(clippy::exit)]
-#![deny(clippy::filetype_is_file)]
-#![deny(clippy::fn_to_numeric_cast)]
-#![deny(clippy::format_push_string)]
-#![deny(clippy::get_unwrap)]
-#![deny(clippy::if_then_some_else_none)]
-#![allow(
-	clippy::implicit_return,
-	reason = "returns should be done implicitly, not explicitly"
-)]
-#![deny(clippy::indexing_slicing)]
-#![deny(clippy::large_include_file)]
-#![deny(clippy::let_underscore_must_use)]
-#![deny(clippy::lossy_float_literal)]
-#![deny(clippy::map_err_ignore)]
-#![deny(clippy::mem_forget)]
-#![deny(clippy::missing_docs_in_private_items)]
-#![deny(clippy::missing_trait_methods)]
-#![deny(clippy::mod_module_files)]
-#![deny(clippy::multiple_inherent_impl)]
-#![deny(clippy::mutex_atomic)]
-#![deny(clippy::needless_return)]
-#![deny(clippy::non_ascii_literal)]
-#![deny(clippy::panic_in_result_fn)]
-#![deny(clippy::pattern_type_mismatch)]
-#![deny(clippy::rc_buffer)]
-#![deny(clippy::rc_mutex)]
-#![deny(clippy::rest_pat_in_fully_bound_structs)]
-#![deny(clippy::same_name_method)]
-#![deny(clippy::separated_literal_suffix)]
-#![deny(clippy::str_to_string)]
-#![deny(clippy::string_add)]
-#![deny(clippy::string_slice)]
-#![deny(clippy::string_to_string)]
-#![allow(
-	clippy::tabs_in_doc_comments,
-	reason = "tabs are preferred for this project"
-)]
-#![deny(clippy::try_err)]
-#![deny(clippy::undocumented_unsafe_blocks)]
-#![deny(clippy::unnecessary_self_imports)]
-#![deny(clippy::unneeded_field_pattern)]
-#![deny(clippy::unwrap_in_result)]
-#![deny(clippy::unwrap_used)]
-#![warn(clippy::use_debug)]
-#![deny(clippy::verbose_file_reads)]
-#![deny(clippy::wildcard_dependencies)]
-#![deny(clippy::wildcard_enum_match_arm)]
-#![deny(missing_copy_implementations)]
-#![deny(missing_debug_implementations)]
-#![deny(missing_docs)]
-#![deny(single_use_lifetimes)]
-#![deny(unsafe_code)]
-#![deny(unused)]
-
-//! Brainfuck RS
-//!
-//! Brainfuck interpreter written in Rust
-
-use std::path::PathBuf;
-
-use brainf_rs::{
-	executor,
-	utility::{execute_from_file, execute_from_str},
-};
-use clap::{Parser, Subcommand};
-use miette::Context;
-
-/// Representation of command line application for the Brainfuck interpreter.
-#[derive(Parser)]
-#[command(author, version, about, long_about = None)]
-#[command(propagate_version = true)]
-struct App {
-	/// Subcommands supported by the Brainfuck interpreter
-	#[command(subcommand)]
-	command: Command,
-}
-
-/// Implementation of subcommands for the Brainfuck interpreter.
-#[derive(Subcommand)]
-enum Command {
-	/// Interprets Brainfuck code from a provided file.
-	File {
-		/// Path to the file to interpret with Brainfuck.
-		path: PathBuf,
-	},
-
-	/// Interprets Brainfuck code from a text input on the command line.
-	Text {
-		/// Text input to be interpreted as Brainfuck.
-		input: String,
-	},
-}
-
-fn main() -> miette::Result<()> {
-	let app = App::parse();
-
-	let mut tape = vec![0u8; 1024];
-
-	match app.command {
-		Command::File { ref path } => {
-			execute_from_file::<executor::U8>(path, &mut tape)
-				.wrap_err("when executing from file")?;
-		}
-		Command::Text { ref input } => execute_from_str::<executor::U8>(input, &mut tape)?,
-	};
-
-	Ok(())
-}
diff --git a/src/parser.rs b/src/parser.rs
deleted file mode 100644
index e639516..0000000
--- a/src/parser.rs
+++ /dev/null
@@ -1,170 +0,0 @@
-//! Parser implementation for Brainfuck. Parses operator codes into instruction
-//! sets.
-
-use miette::{Diagnostic, SourceSpan};
-use thiserror::Error;
-
-use crate::lexer::OperatorCode;
-
-/// Parsed instructions for Brainfuck.
-#[derive(Clone, Debug)]
-pub enum Instruction {
-	/// `>`
-	///
-	/// Increment the data pointer by one (to point to the next cell to the
-	/// right).
-	IncrementPointer,
-
-	/// `<`
-	///
-	/// Decrement the data pointer by one (to point to the next cell to the
-	/// left).
-	DecrementPointer,
-
-	/// `+`
-	///
-	/// Increment the byte at the data pointer by one.
-	IncrementByte,
-
-	/// `-`
-	///
-	/// Decrement the byte at the data pointer by one.
-	DecrementByte,
-
-	/// `.`
-	///
-	/// Output the byte at the data pointer.
-	OutputByte,
-
-	/// `,`
-	///
-	/// Accept one byte of input, storing its value in the byte at the data
-	/// pointer.
-	InputByte,
-
-	/// `[]`
-	///
-	/// Loops through the inner instructions.
-	Loop(Vec<Instruction>),
-}
-
-/// Error type for errors that occur during parsing from [`OperatorCode`]s to
-/// [`Instruction`]s.
-#[derive(Debug, Diagnostic, Error)]
-pub enum Error {
-	/// Parser encountered a loop with no beginning.
-	#[diagnostic(
-		code(brainf_rs::parser::loop_with_no_beginning),
-		help("try closing the loop")
-	)]
-	#[error("loop closed at {loop_src:?} has no beginning")]
-	LoopWithNoBeginning {
-		/// Source code associated with diagnostic
-		#[source_code]
-		input: String,
-
-		/// SourceSpan of the loop bracket.
-		#[label("loop ending")]
-		loop_src: SourceSpan,
-	},
-
-	/// Parser encountered a loop with no ending.
-	#[diagnostic(
-		code(brainf_rs::parser::loop_with_no_ending),
-		help("try closing the loop")
-	)]
-	#[error("loop beginning at {loop_src:?} has no ending")]
-	LoopWithNoEnding {
-		/// Source code associated with diagnostic
-		#[source_code]
-		input: String,
-
-		/// SourceSpan of the loop bracket.
-		#[label("loop beginning")]
-		loop_src: SourceSpan,
-	},
-
-	/// Parser sliced out of bounds.
-	#[error("parser sliced out of bounds")]
-	SliceOutOfBounds(std::ops::Range<usize>),
-}
-
-/// Parses the operator codes into instruction codes.
-///
-/// # Parameters
-///
-/// * `src` - The source the operator codes originate from. This is used for
-///   error reporting.
-/// * `operator_codes` - The operator codes reveiced from the lexer.
-///
-/// # Errors
-///
-/// This function will return an error if a loop is encountered with no
-/// beginning, a loop is encountered with no ending, or if the parser attempts
-/// to slice out of bounds.
-pub fn parse(src: &str, operator_codes: &[OperatorCode]) -> Result<Vec<Instruction>, Error> {
-	let mut program: Vec<Instruction> = Vec::new();
-	let mut loop_stack: i32 = 0;
-	let mut loop_start = 0;
-	let mut loop_source_offset: usize = 0;
-
-	operator_codes
-		.iter()
-		.enumerate()
-		.try_for_each(|(i, operator_code)| -> Result<(), Error> {
-			match (loop_stack, *operator_code) {
-				(0i32, OperatorCode::StartLoop { offset }) => {
-					loop_start = i;
-					loop_source_offset = offset;
-					loop_stack += 1i32;
-				}
-				(0i32, _) => {
-					if let Some(instruction) = match *operator_code {
-						OperatorCode::IncrementPointer => Some(Instruction::IncrementPointer),
-						OperatorCode::DecrementPointer => Some(Instruction::DecrementPointer),
-						OperatorCode::IncrementByte => Some(Instruction::IncrementByte),
-						OperatorCode::DecrementByte => Some(Instruction::DecrementByte),
-						OperatorCode::OutputByte => Some(Instruction::OutputByte),
-						OperatorCode::InputByte => Some(Instruction::InputByte),
-						OperatorCode::EndLoop { offset } => {
-							return Err(Error::LoopWithNoBeginning {
-								input: src.to_owned(),
-								loop_src: (offset, 1).into(),
-							})
-						}
-						// We don't care about this variant as it is handled in a subsequent arm
-						OperatorCode::StartLoop { .. } => None,
-					} {
-						program.push(instruction);
-					}
-				}
-				(_, OperatorCode::StartLoop { .. }) => loop_stack += 1i32,
-				(_, OperatorCode::EndLoop { .. }) => {
-					loop_stack -= 1i32;
-					if loop_stack == 0i32 {
-						let loop_program = parse(
-							src,
-							match operator_codes.get(loop_start + 1..i) {
-								Some(value) => value,
-								None => return Err(Error::SliceOutOfBounds(loop_start + 1..i)),
-							},
-						)?;
-
-						program.push(Instruction::Loop(loop_program));
-					}
-				}
-				_ => (),
-			};
-
-			Ok(())
-		})?;
-
-	if loop_stack == 0i32 {
-		Ok(program)
-	} else {
-		Err(Error::LoopWithNoEnding {
-			input: src.to_owned(),
-			loop_src: (loop_source_offset, 1).into(),
-		})
-	}
-}
diff --git a/src/utility.rs b/src/utility.rs
deleted file mode 100644
index 514343c..0000000
--- a/src/utility.rs
+++ /dev/null
@@ -1,50 +0,0 @@
-//! Utility functions for working with the Brainfuck interpreter.
-
-use std::path::Path;
-
-use crate::{engine::Engine, executor::execute, lex, parse, Error};
-
-/// Utility function to execute a Brainfuck file. Lexes, parses and executes the
-/// input file.
-///
-/// # Errors
-///
-/// This function will return an error if reading the input file, parsing or
-/// execution fails. See documentation for [`crate::parser::parse`] and
-/// [`crate::executor::execute`].
-pub fn execute_from_file<E: Engine>(
-	path: impl AsRef<Path>,
-	tape: &mut [E::TapeInner],
-) -> Result<(), Error> {
-	let input = fs_err::read_to_string(path.as_ref())?;
-
-	let operator_codes = lex(&input);
-
-	let instructions = parse(&input, &operator_codes)?;
-
-	let mut data_pointer = 0;
-
-	execute::<E>(&instructions, tape, &mut data_pointer)?;
-
-	Ok(())
-}
-
-/// Utility function to execute Brainfuck code. Lexes, parses and executes the
-/// input.
-///
-/// # Errors
-///
-/// This function will return an error if parsing or
-/// execution fails. See documentation for [`crate::parser::parse`] and
-/// [`crate::executor::execute`].
-pub fn execute_from_str<E: Engine>(input: &str, tape: &mut [E::TapeInner]) -> Result<(), Error> {
-	let operator_codes = lex(input);
-
-	let instructions = parse(input, &operator_codes)?;
-
-	let mut data_pointer = 0;
-
-	execute::<E>(&instructions, tape, &mut data_pointer)?;
-
-	Ok(())
-}