summaryrefslogtreecommitdiff
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
commitf752160c6ebcee747a61e6febc4789d0b04ccd12 (patch)
tree76e123279abbd1797ba281f35cbcba61136ef7bd
parent98d7946709032aae5e1d0d35cff78efc55550900 (diff)
feat: implement generic engine for executor
-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(())
}