diff --git a/Cargo.toml b/Cargo.toml index d1ccb2d..9f4be2d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,6 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -clap = { version = "4.0.10", features = ["derive", "color"] } +clap = { version = "4.0.10", features = ["derive", "color", "cargo"] } log = "0.4.17" pretty_env_logger = "0.4.0" diff --git a/src/arguments.rs b/src/arguments.rs index 4f277c2..cbd591c 100644 --- a/src/arguments.rs +++ b/src/arguments.rs @@ -24,5 +24,4 @@ pub enum Feature { /// If the pointer at the end of the array, set the pointer to 0, otherwise increment the pointer. /// If the pointer at the beginning of the array, set the pointer to the end of the array, otherwise decrement the pointer. ReversePointer, - } \ No newline at end of file diff --git a/src/interpreter.rs b/src/interpreter.rs index 63fa55e..9af061b 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -1,21 +1,22 @@ -use std::io::{Read, Write}; +use std::io::{Write}; use std::usize; use crate::arguments; pub struct Interpreter { - pub array: Vec, + pub cells: Vec, pub pointer: usize, pub array_size: usize, pub bf_code: String, - pub brackets: Vec, + brackets: Vec, pub features: Vec, } + impl Interpreter { pub fn new(array_size: usize, bf_code: Option, features: Vec) -> Self { Self { - array: vec![0; array_size], + cells: vec![0; array_size], pointer: 0, array_size, bf_code: bf_code.unwrap_or_else(|| String::new()), @@ -24,65 +25,107 @@ impl Interpreter { } } - pub fn run(&mut self, bf_code: Option) { - let mut cells = vec![0u8; bf_arr_size]; - let mut ptr = 0; - let mut brackets = vec![]; + pub fn run(&mut self, bf_code: Option) -> Result { + let bf_code = match bf_code { + Some(bf_code) => { + self.bf_code.push_str(&*bf_code); + bf_code + } + None => self.bf_code.clone() + }; + match self.run_brainfuck_code(&bf_code) { + Ok(_) => Ok(0), + Err(e) => Err((e, 1)), + } + } + + // +[>++<-] + fn iterate(&mut self, code: String) -> Result<(), String> { + while self.cells[self.pointer] != 0 { + self.run_brainfuck_code(&code)?; + } + Ok(()) + } + + + fn run_brainfuck_code(&mut self, bf_code: &str) -> Result<(), String> { for (i, ch) in bf_code.chars().enumerate() { - trace!("Current character: {}", ch); - trace!("Current pointer: {}", ptr); - trace!("Current cell: {}", cells[ptr]); - match BfCommand::from_char(ch, i) { Some(cmd) => { trace!("Executing command: {:?}", cmd); - match cmd { - BfCommand::IncPtr => { - if ptr == bf_arr_size - 1 { - eprintln!("Error: pointer out of bounds"); - } else { - ptr += 1; - } - }, - BfCommand::DecPtr => ptr -= 1, - BfCommand::IncVal => { - cells[ptr] += 1 - }, - BfCommand::DecVal => cells[ptr] -= 1, - BfCommand::Print => { - trace!("Printing value: {}", cells[ptr]); - println!("{}", cells[ptr]); - std::io::stdout().flush().unwrap(); - }, - BfCommand::Read => cells[ptr] = std::io::stdin() - .bytes().next().unwrap().unwrap() as u8, - BfCommand::LoopStart(index) => brackets.push(cmd), - BfCommand::LoopEnd => { - if cells[ptr] != 0 { - let i = brackets - .iter() - .map(|cmd| match cmd { - BfCommand::LoopStart(index) => *index, - _ => unreachable!() - }) - .last(); - brackets.truncate(i); - ptr = index; - } else { - brackets.pop(); - } - } - } - }, + self.execute(cmd)? + } None => { - trace!("Ignoring character: {}", ch); - } // Ignore unknown characters + trace!("Skipping character: {}", ch); + } } } + Ok(()) + } + + fn execute(&mut self, cmd: BfCommand) -> Result<(), String> { + match cmd { + BfCommand::IncPtr => { + self.pointer += 1; + if self.pointer >= self.array_size { + if self.features.contains(&arguments::Feature::ReversePointer) { + self.pointer = 0; + } else { + return Err(format!("Pointer out of bounds: {}", self.pointer)); + } + } + } + BfCommand::DecPtr => { + if self.pointer == 0 { + if self.features.contains(&arguments::Feature::ReversePointer) { + self.pointer = self.array_size - 1; + } else { + return Err(format!("Pointer out of bounds: {}", self.pointer)); + } + } else { + self.pointer -= 1; + } + }, + BfCommand::IncVal => { + self.cells[self.pointer] = self.cells[self.pointer].wrapping_add(1); + }, + BfCommand::DecVal => { + self.cells[self.pointer] = self.cells[self.pointer].wrapping_sub(1); + }, + BfCommand::Print => { + print!("{}", self.cells[self.pointer] as char); + std::io::stdout().flush().unwrap(); + }, + BfCommand::Read => { + let mut input = String::new(); + // TODO: Handle errors, and read only one byte + std::io::stdin().read_line(&mut input).unwrap(); + self.cells[self.pointer] = input.chars().next().unwrap() as u8; + }, + BfCommand::LoopStart(i) => { + self.brackets.push(BfCommand::LoopStart(i)); + }, + BfCommand::LoopEnd(i) => { + let open_bracket = self.brackets.pop(); + match open_bracket { + Some(BfCommand::LoopStart(j)) => { + if self.cells[self.pointer] != 0 { + let code = self.bf_code[j..i].to_string(); + self.iterate(code)?; + } + }, + _ => { + return Err(format!("Unmatched closing bracket at position: {}", i)); + } + } + } + } + Ok(()) } } + #[derive(Debug, PartialEq)] enum BfCommand { IncPtr, @@ -92,7 +135,7 @@ enum BfCommand { Print, Read, LoopStart(usize), - LoopEnd, + LoopEnd(usize), } impl BfCommand { @@ -105,8 +148,8 @@ impl BfCommand { '.' => Some(BfCommand::Print), ',' => Some(BfCommand::Read), '[' => Some(BfCommand::LoopStart(index)), - ']' => Some(BfCommand::LoopEnd), + ']' => Some(BfCommand::LoopEnd(index)), _ => None, } } -} \ No newline at end of file +} diff --git a/src/main.rs b/src/main.rs index da54de4..e154553 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,12 +2,14 @@ mod arguments; mod interpreter; mod utils; +use std::io::Write; use clap::Parser; extern crate pretty_env_logger; #[macro_use] extern crate log; use arguments::Args; -use utils::; +use crate::utils::read_brainfuck_code_if_any; + fn main() { pretty_env_logger::init(); @@ -23,31 +25,45 @@ fn main() { info!("Initializing interpreter"); let mut interpreter = interpreter::Interpreter::new( args.array_size, - urils::read_brainfuck_code_if_any(args.source), + read_brainfuck_code_if_any(&args.source), args.features.unwrap_or_else(|| vec![])); match args.source { Some(source) => { info!("Running brainfuck source code from file: {}", source); - interpreter.run(None) + interpreter.run(None).unwrap(); }, None => { info!("Entering REPL mode"); println!("Welcome to the brainfuck REPL mode! :)"); + println!("Brainfuck interpreter v {}\nBy {}", + clap::crate_version!(), clap::crate_authors!()); println!("Enter your brainfuck code and press enter to run it."); println!("Enter !fuck to exit :D"); println!("Enter !help fuck to get more help"); loop { + print!("> "); + std::io::stdout().flush().unwrap(); let mut input = String::new(); std::io::stdin().read_line(&mut input).unwrap(); if input.starts_with("!") { - match input.trim() { - "!fuck" => { + match input.trim().get(1..).unwrap() { + "fuck" => { println!("Bye bye :D"); break; }, - "!help" => { + "array" | "a" => { + println!("Current array: {:#?}", interpreter.cells); + }, + "array_size" | "as" => { + println!("Current array size: {}", interpreter.array_size); + }, + "pointer" | "p" => { + println!("Current pointer: {}", interpreter.pointer); + }, + + "help" => { println!("!fuck: exit the REPL mode"); println!("!array, !a: print the current array"); println!("!array_size, !as: print the current array size"); diff --git a/src/utils.rs b/src/utils.rs index 29f532a..6be19a6 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,4 +1,4 @@ -fn read_brainfuck_code_if_any(source: Option) -> Option { +pub(crate) fn read_brainfuck_code_if_any(source: &Option) -> Option { match source { Some(source) => { info!("Reading brainfuck source code from file: {}", source); diff --git a/test_code/print_hi_yooo.brainfuck b/test_code/print_hi_yooo.brainfuck new file mode 100644 index 0000000..3e77384 --- /dev/null +++ b/test_code/print_hi_yooo.brainfuck @@ -0,0 +1 @@ +>+++++++++[<++++ ++++>-]<.>++++++++[<++++>-]<+.>+++++++++[<-------->-]<-.>++++++++++[<+++++++++>-]<-.>+++[<--->-]<-..............................................................>+++++++++[<--------->-]<+++.` diff --git a/test_code/read_name_and_print_hi.brainfuck b/test_code/read_name_and_print_hi.brainfuck new file mode 100644 index 0000000..b37c5e5 --- /dev/null +++ b/test_code/read_name_and_print_hi.brainfuck @@ -0,0 +1 @@ +>+++++++++[<++++ ++++>-]<.>++++++++[<++++>-]<+.>+++++++++[<-------->-]<-.>>,.[[-],.]