use crate::arguments; use crate::bf_interpreter::error::{InterpreterError, InterpreterErrorKind}; use std::io::{Read, Write}; use std::{char, usize, vec}; pub struct Interpreter<'a> { pub cells: Vec, pub pointer: usize, pub bf_commands: Vec, brackets: Vec, pub input: &'a Box, pub output: &'a Box, pub features: Vec, } impl<'a> Interpreter<'a> { pub fn new( array_size: usize, input: &'a mut Box, output: &'a mut Box, features: Vec, ) -> Self { Self { cells: vec![0; array_size], pointer: 0, bf_commands: vec![], brackets: Vec::new(), input, output, features, } } pub fn run(&mut self, bf_code: String) -> Result { self.bf_commands = to_bf_commands(bf_code.chars().collect())?; match self.run_brainfuck_code(&self.bf_commands.clone()) { Ok(_) => Ok(0), Err(e) => Err(e), } } // +[>++<-] fn iterate(&mut self, code: &Vec) -> Result<(), InterpreterError> { trace!("Iterate: {:?}", code); while self.cells[self.pointer] != 0 { self.run_brainfuck_code(code)?; } Ok(()) } fn run_brainfuck_code(&mut self, bf_code: &Vec) -> Result<(), InterpreterError> { for command in bf_code { match command { BfCommand::IncPtr => self.increment_pointer()?, BfCommand::DecPtr => self.decrement_pointer()?, BfCommand::IncVal => self.increment_value()?, BfCommand::DecVal => self.decrement_value()?, BfCommand::Print => self.output_value()?, BfCommand::Read => self.input_value()?, BfCommand::Loop(loop_body) => self.iterate(loop_body)?, } } Ok(()) } fn increment_pointer(&mut self) -> Result<(), InterpreterError> { trace!("Increment pointer"); self.pointer += 1; if self.pointer >= self.cells.len() { if self.features.contains(&arguments::Feature::ReversePointer) { self.pointer = 0; } else { return Err(InterpreterErrorKind::PointerOutOfBounds(self.pointer).to_error()); } } Ok(()) } fn decrement_pointer(&mut self) -> Result<(), InterpreterError> { trace!("Decrement pointer"); if self.pointer == 0 { if self.features.contains(&arguments::Feature::ReversePointer) { self.pointer = self.cells.len() - 1; } else { return Err(InterpreterErrorKind::PointerOutOfBounds(self.pointer).to_error()); } } else { self.pointer -= 1; } Ok(()) } fn increment_value(&mut self) -> Result<(), InterpreterError> { trace!("Increment value"); if self.cells[self.pointer] == 255 { if !self.features.contains(&arguments::Feature::NoReverseValue) { self.cells[self.pointer] = 0; } else { return Err(InterpreterErrorKind::ValueOutOfBounds.to_error()); } } else { self.cells[self.pointer] += 1; } Ok(()) } fn decrement_value(&mut self) -> Result<(), InterpreterError> { trace!("Decrement value"); if self.cells[self.pointer] == 0 { if !self.features.contains(&arguments::Feature::NoReverseValue) { self.cells[self.pointer] = 255; } else { return Err(InterpreterErrorKind::ValueOutOfBounds.to_error()); } } else { self.cells[self.pointer] -= 1; } Ok(()) } fn output_value(&mut self) -> Result<(), InterpreterError> { trace!("Output value"); if self.features.contains(&arguments::Feature::AllowUtf8) { let c = char::from_u32(self.cells[self.pointer] as u32); match c { Some(c) => print!("{}", c), None => return Err(InterpreterErrorKind::InvalidUtf8.to_error()), } } else { print!("{}", self.cells[self.pointer] as char); } match std::io::stdout().flush() { Ok(_) => Ok(()), Err(e) => Err(InterpreterErrorKind::FlushError(e).to_error()), } } fn input_value(&mut self) -> Result<(), InterpreterError> { trace!("Input value"); let mut input = [0; 1]; match std::io::stdin().read_exact(&mut input) { Ok(_) => { self.cells[self.pointer] = input[0]; Ok(()) } Err(e) => Err(InterpreterErrorKind::IoError(e).to_error()), } } pub fn reset(&mut self) { self.cells = vec![0; self.cells.len()]; self.pointer = 0; self.brackets = Vec::new(); self.bf_commands = Vec::new(); } } #[derive(Debug, PartialEq, Clone)] pub enum BfCommand { IncPtr, DecPtr, IncVal, DecVal, Print, Read, Loop(Vec), } fn to_bf_commands(bf_code: Vec) -> Result, InterpreterError> { let mut bf_commands = Vec::new(); let mut i = 0; while i < bf_code.len() { match bf_code[i] { '[' => { let mut bracket_count = 1; let mut j = i + 1; while j < bf_code.len() { match bf_code[j] { '[' => bracket_count += 1, ']' => bracket_count -= 1, _ => (), } if bracket_count == 0 { break; } j += 1; } if bracket_count != 0 { return Err(InterpreterErrorKind::UnmatchedBracket.to_error()); } bf_commands.push(BfCommand::Loop(to_bf_commands(bf_code[i + 1..j].to_vec())?)); i = j; } _ => { match BfCommand::from(bf_code[i]) { Some(command) => bf_commands.push(command), None => (), } }, } i += 1; } Ok(bf_commands) } impl BfCommand { fn from(c: char) -> Option { match c { '>' => Some(BfCommand::IncPtr), '<' => Some(BfCommand::DecPtr), '+' => Some(BfCommand::IncVal), '-' => Some(BfCommand::DecVal), '.' => Some(BfCommand::Print), ',' => Some(BfCommand::Read), _ => None, } } } #[cfg(test)] mod tests { use super::*; use pretty_assertions::assert_eq; // for testing only use crate::utils; #[test] fn print_h_combine_repl() { let mut interpreter = Interpreter::new( 30000, vec![], ); assert_eq!(interpreter.run(String::from(">+++++++++[<++++ ++++>-]<.")), Ok(0)); println!(); } #[test] fn print_h_repl() { let mut interpreter = Interpreter::new( 30000, vec![], ); assert_eq!(interpreter.run(String::from(">+++++++++")), Ok(0)); assert_eq!(interpreter.run(String::from("[<++++ ++++>-]<.")), Ok(0)); println!(); } #[test] fn nested_loop_level_1_combine() { let mut interpreter = Interpreter::new( 5, vec![], ); assert_eq!(interpreter.run(String::from("++[>++[>+<-]<-]")), Ok(0)); assert_eq!(interpreter.cells[2], 4); println!(); } #[test] fn execute_hello_world_from_file() { let mut interpreter = Interpreter::new( 30000, vec![], ); println!(); assert_eq!(interpreter.run( utils::read_brainfuck_code( &String::from("test_code/hello_world.bf"))), Ok(0)); } #[test] fn execute_print_hi_from_file() { let mut interpreter = Interpreter::new( 30000, vec![], ); println!(); assert_eq!(interpreter.run( utils::read_brainfuck_code(&String::from("test_code/print_hi.bf"))), Ok(0)); } #[test] fn execute_print_hi_yooo_from_file() { let mut interpreter = Interpreter::new( 30000, vec![], ); println!(); assert_eq!(interpreter.run( utils::read_brainfuck_code(&String::from("test_code/print_hi_yooo.bf"))), Ok(0)); } #[test] fn execute_print_my_first_name_from_formatted_file() { let mut interpreter = Interpreter::new( 30000, vec![], ); println!(); assert_eq!(interpreter.run( utils::read_brainfuck_code(&String::from("test_code/print_my_first_name_formatted.bf"))), Ok(0)); } #[test] fn execute_print_my_first_name_from_file() { let mut interpreter = Interpreter::new( 30000, vec![], ); println!(); assert_eq!(interpreter.run( utils::read_brainfuck_code(&String::from("test_code/print_my_first_name.bf"))), Ok(0)); } #[test] fn execute_print_my_first_name_and_last_name_from_formatted_file() { let mut interpreter = Interpreter::new( 30000, vec![], ); println!(); assert_eq!(interpreter.run( utils::read_brainfuck_code( &String::from("test_code/print_my_first_name_and_last_name_formatted.bf"))), Ok(0)); } #[test] fn execute_print_my_first_name_and_last_name_from_file() { let mut interpreter = Interpreter::new( 30000, vec![], ); println!(); assert_eq!(interpreter.run(utils::read_brainfuck_code( &String::from("test_code/print_my_first_name_and_last_name.bf"))), Ok(0)); } #[test] fn reset() { let mut interpreter = Interpreter::new( 30000, vec![], ); assert_eq!(interpreter.run(String::from(">++++")), Ok(0)); assert_eq!(interpreter.pointer, 1); assert_eq!(interpreter.cells[0], 0); assert_eq!(interpreter.cells[1], 4); // assert_eq!(interpreter.commands, vec!['>', '+', '+', '+', '+']); // reset interpreter.reset(); assert_eq!(interpreter.pointer, 0); assert_eq!(interpreter.cells[0], 0); assert_eq!(interpreter.cells[1], 0); assert_eq!(interpreter.bf_commands, Vec::::new()); } }