diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/engine.rs | 118 | ||||
| -rw-r--r-- | src/executor.rs | 131 | ||||
| -rw-r--r-- | src/lexer.rs | 79 | ||||
| -rw-r--r-- | src/lib.rs | 204 | ||||
| -rw-r--r-- | src/main.rs | 128 | ||||
| -rw-r--r-- | src/parser.rs | 170 | ||||
| -rw-r--r-- | src/utility.rs | 50 |
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(()) -} |