diff options
Diffstat (limited to '')
| -rw-r--r-- | Cargo.toml | 3 | ||||
| -rw-r--r-- | src/constants.rs | 5 | ||||
| -rw-r--r-- | src/engine.rs | 61 | ||||
| -rw-r--r-- | src/executor.rs | 124 | ||||
| -rw-r--r-- | src/lib.rs | 3 | ||||
| -rw-r--r-- | src/utility.rs | 6 |
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(()) } |