summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--Cargo.toml3
-rw-r--r--src/constants.rs5
-rw-r--r--src/engine.rs61
-rw-r--r--src/executor.rs124
-rw-r--r--src/lib.rs3
-rw-r--r--src/utility.rs6
6 files changed, 136 insertions, 66 deletions
diff --git a/Cargo.toml b/Cargo.toml
index b9c05cb..3d83881 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -11,5 +11,6 @@ miette = { features = ["fancy"], version = "5.10.0" }
 thiserror = "1.0.44"
 
 [features]
-default = ["utilities"]
+default = ["utilities", "u8-engine"]
 utilities = []
+u8-engine = []
diff --git a/src/constants.rs b/src/constants.rs
index 6651b22..6ca1bc5 100644
--- a/src/constants.rs
+++ b/src/constants.rs
@@ -1,4 +1,9 @@
 //! Constants and types used throughout the Brainfuck interpreter.
 
 /// The inner type used by a Brainfuck tape.
+#[cfg(feature = "u8-engine")]
 pub type TapeInner = u8;
+
+/// The inner type used by a Brainfuck tape.
+#[cfg(feature = "u16-engine")]
+pub type TapeInner = u16;
diff --git a/src/engine.rs b/src/engine.rs
new file mode 100644
index 0000000..d57d6a4
--- /dev/null
+++ b/src/engine.rs
@@ -0,0 +1,61 @@
+//! Executor engine implementation for Brainfuck interpreter.
+//!
+//! This predominantly allows implementation of a [`u16`] executor.
+
+use std::io::Read;
+
+use crate::executor::{Error, Executor};
+
+/// Generic engine implementation for the Brainfuck interpreter.
+pub trait Engine<T> {
+	/// 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<T, 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: T) -> Result<(), Error>;
+}
+
+impl Engine<u8> for Executor {
+	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(())
+	}
+}
+
+impl Engine<u16> for Executor {
+	fn read_byte() -> Result<u16, Error> {
+		let mut input: [u8; 2] = [0; 2];
+
+		std::io::stdin().read_exact(&mut input)?;
+
+		#[expect(clippy::pedantic)]
+		let number = ((input[0] as u16) << 8i32) | input[1] as u16;
+
+		Ok(number)
+	}
+
+	fn write_byte(byte: u16) -> Result<(), Error> {
+		print!("{}", String::from_utf16_lossy(&[byte]));
+
+		Ok(())
+	}
+}
diff --git a/src/executor.rs b/src/executor.rs
index f368ea9..24b2f47 100644
--- a/src/executor.rs
+++ b/src/executor.rs
@@ -1,11 +1,9 @@
 //! Executor implementation for Brainfuck.
 
-use std::io::Read;
-
 use miette::Diagnostic;
 use thiserror::Error;
 
-use crate::{constants::TapeInner, parser::Instruction};
+use crate::{constants::TapeInner, engine::Engine, parser::Instruction};
 
 /// Runtime errors that can occur in brainfuck executor.
 #[derive(Debug, Diagnostic, Error)]
@@ -20,70 +18,74 @@ pub enum Error {
 	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;
+/// Struct for executor implementation, allows Engine to be implemented.
+#[derive(Clone, Copy, Debug)]
+pub struct Executor;
 
-				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)?;
+impl Executor {
+	/// 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;
 
-				match tape.get_mut(*data_pointer) {
-					Some(value) => *value = input[0],
+					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::Loop(ref instructions) => {
-				while match tape.get(*data_pointer) {
-					Some(value) => *value,
+				},
+				Instruction::DecrementByte => match tape.get_mut(*data_pointer) {
+					Some(value) => *value = value.overflowing_sub(1).0,
 					None => return Err(Error::IndexOutOfBounds(*data_pointer)),
-				} != 0
-				{
-					execute(instructions, tape, data_pointer)?;
+				},
+				Instruction::OutputByte => {
+					Self::write_byte(match tape.get(*data_pointer) {
+						Some(value) => *value,
+						None => return Err(Error::IndexOutOfBounds(*data_pointer)),
+					})?;
+				}
+				Instruction::InputByte => {
+					let input = Self::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)),
+					} != 0
+					{
+						Self::execute(instructions, tape, data_pointer)?;
+					}
 				}
 			}
 		}
-	}
 
-	Ok(())
+		Ok(())
+	}
 }
diff --git a/src/lib.rs b/src/lib.rs
index 725084a..4a7950a 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -83,13 +83,14 @@
 extern crate test;
 
 mod constants;
+mod engine;
 pub mod executor;
 pub mod lexer;
 pub mod parser;
 #[cfg(feature = "utilities")]
 pub mod utility;
 
-pub use executor::execute;
+pub use executor::Executor;
 pub use lexer::{lex, OperatorCode};
 use miette::Diagnostic;
 pub use parser::{parse, Instruction};
diff --git a/src/utility.rs b/src/utility.rs
index a903273..58ac3e2 100644
--- a/src/utility.rs
+++ b/src/utility.rs
@@ -2,7 +2,7 @@
 
 use std::path::Path;
 
-use crate::{constants::TapeInner, execute, lex, parse, Error};
+use crate::{constants::TapeInner, lex, parse, Error, Executor};
 
 /// Utility function to execute a Brainfuck file. Lexes, parses and executes the
 /// input file.
@@ -21,7 +21,7 @@ pub fn execute_from_file(path: impl AsRef<Path>, tape: &mut [TapeInner]) -> Resu
 
 	let mut data_pointer = 0;
 
-	execute(&instructions, tape, &mut data_pointer)?;
+	Executor::execute(&instructions, tape, &mut data_pointer)?;
 
 	Ok(())
 }
@@ -41,7 +41,7 @@ pub fn execute_from_str(input: &str, tape: &mut [TapeInner]) -> Result<(), Error
 
 	let mut data_pointer = 0;
 
-	execute(&instructions, tape, &mut data_pointer)?;
+	Executor::execute(&instructions, tape, &mut data_pointer)?;
 
 	Ok(())
 }