use super::repl::Repl; use crate::bf_interpreter::interpreter::Interpreter; use crate::repl::repl::{COMMAND_PREFIX, HISTORY_FILE, PROMPT}; use colored::Colorize; use console::Key; use std::io::Write; impl Repl { pub fn new(interpreter: Interpreter) -> Repl { Repl { term: interpreter.term.clone(), interpreter, history: Vec::new(), loop_body: String::new(), loop_depth: 0, } } // #[no_panic] pub fn run(mut self) -> Result<(), std::io::Error> { loop { self.print_prompt(); std::io::stdout().flush()?; match self.read_input() { Ok(input) => { let user_input = input.trim().to_string(); // Remove trailing newline if !user_input.is_empty() && user_input.len() > 0 { self.history.push(user_input.clone()); // Save input to history self.process(user_input); // Process the input } } Err(e) => { eprintln!("Error: {}", e); } } } } fn print_prompt(&self) { print!( "{}", if self.loop_depth != 0 { "............... ".yellow() } else { PROMPT.to_string().truecolor(54, 76, 76) } ); } fn read_input(&mut self) -> Result { let mut input = String::new(); let mut rev_index = 0; loop { let key = self.term.read_key()?; // Read key from terminal match key { Key::ArrowUp => { if !self.history.is_empty() && rev_index < self.history.len() { let last = self .history .get(self.history.len() - 1 - rev_index) .unwrap(); rev_index += 1; self.term.clear_line()?; self.print_prompt(); self.term.write_str(last)?; input = last.clone(); } } Key::ArrowDown => { if !self.history.is_empty() && rev_index > 0 { let first = self.history.get(self.history.len() - rev_index).unwrap(); rev_index -= 1; self.term.clear_line()?; self.print_prompt(); self.term.write_str(first)?; input = first.clone(); } } Key::Char(c) => { self.term.write_str(&c.to_string())?; input.push(c); } Key::Backspace if !input.is_empty() => { self.term.clear_line()?; self.print_prompt(); self.term.write_str(&input[0..input.len() - 1])?; input.pop(); } Key::Enter => { self.term.write_str("\n")?; break; } _ => {} } } Ok(input) } pub fn process(&mut self, mut user_input: String) { let mut print = false; user_input.chars().for_each(|ch| { if ch == '[' { self.loop_depth += 1; } else if ch == ']' { self.loop_depth -= 1; } else if ch == '.' { print = true; } }); match user_input.find('[') { Some(index) if self.loop_depth != 0 && self.loop_body.is_empty() => { self.loop_body.push_str(&user_input[index..]); user_input = user_input[..index].to_string(); } Some(_) if !self.loop_body.is_empty() => { self.loop_body.push_str(&user_input); return; } _ => { if user_input.contains(']') { if self.loop_depth == 0 { self.loop_body.push_str(&user_input); user_input = self.loop_body.clone(); self.loop_body = String::new(); } } if self.loop_depth != 0 { self.loop_body.push_str(&user_input); return; } } } if user_input.is_empty() || user_input.len() == 0 { return; } if user_input.starts_with(COMMAND_PREFIX) { self.run_repl_cmd(user_input); } else { match self.interpreter.run(user_input) { Ok(_) => { info!("Successfully ran brainfuck source code from REPL"); if print { println!(); // Print newline after output, if user requested print any output } } Err(e) => { error!("Failed to run brainfuck source code from REPL: {}", e); } } } } fn run_repl_cmd(&mut self, user_input: String) { let mut cmd = user_input.split_whitespace(); match cmd.next() { Some(repl_cmd) => { match repl_cmd.get(COMMAND_PREFIX.len()..).unwrap_or("") { "fuck" => { println!("{}", "Bye bye :D".green()); std::process::exit(0); } "array" | "a" => { println!("{}", format!("Current array: {:?}", self.interpreter.cells)); } "array_size" | "as" => { println!( "{}", format!( "Current array size: {}", self.interpreter.cells.len().to_string().bold().green() ) ); } "pointer" | "p" => { println!( "{}", format!( "Current pointer: {}", self.interpreter.pointer.to_string().bold().green() ) ); } "pointer_value" | "pv" => { println!( "Current pointer value: {} = \'{}\' (char)", self.interpreter.cells[self.interpreter.pointer], self.interpreter.cells[self.interpreter.pointer] .to_char() .unwrap_or_else(|_| '?') ); } "history" | "h" => { println!("{}", "History:".underline().green()); for (i, cmd) in self.history.iter().enumerate() { println!("{}", format!("{}: {}", i, cmd)); } } "save" | "s" => { let file_name = cmd.next().unwrap_or(HISTORY_FILE); println!( "{}", format!("Saving history to file: {file_name}").yellow() ); match std::fs::write(file_name, self.history.join("\n")) { Ok(_) => { println!( "{}", format!("Successfully saved history to file: {file_name}") .green() ); } Err(e) => { error!("Failed to save history to file: {}", e); } } } "load" | "l" => { let file_name = cmd.next().unwrap_or(HISTORY_FILE); println!( "{}", format!("Loading history from file: {file_name}").yellow() ); match std::fs::read_to_string(file_name) { Ok(history) => { println!( "{}", format!("Successfully loaded history from file: {file_name}") .green() ); self.history = history.split("\n").map(|s| s.to_string()).collect(); // Run all commands in history for cmd in self.history.iter() { match self.interpreter.run(cmd.clone()) { Ok(_) => { info!( "Successfully ran brainfuck source code from REPL" ); } Err(e) => { error!( "Failed to run brainfuck source code from REPL: {}", e ); } } } } Err(e) => { error!("Failed to load history from file: {}", e); } } } "reset" | "r" => { println!("{}", "Resetting REPL".truecolor(56, 33, 102)); self.interpreter.reset(); self.history = Vec::new(); } "help" => { println!( "!array, !a: print the current array\n\ !array_size, !as: print the current array size\n\ !pointer, !p: print the current pointer\n\ !pointer_value, !pv: print the current pointer value\n\ !history, !h: print the REPL history\n\ !save, !s: save the REPL history to a file\n\ !load, !l: load the REPL history from a file\n\ !reset, !r: reset the REPL\n\ !help: print this help message\n\ !fuck: exit the REPL" ); } _ => println!( "{}", format!( "Unknown command: {}, type {} to show the help", user_input, (COMMAND_PREFIX.to_string() + "help").green() ) .red() ), } } None => {} } } }