bfy/src/repl.rs

231 lines
9.4 KiB
Rust
Raw Normal View History

2022-10-08 10:01:28 +00:00
use crate::bf_interpreter::interpreter::Interpreter;
2022-10-12 11:50:22 +00:00
use std::io::{Write, Read};
use colored::Colorize;
2022-10-07 22:28:14 +00:00
2022-10-12 11:50:22 +00:00
struct Repl<'a> {
interpreter: &'a mut Interpreter<'a>,
2022-10-07 22:28:14 +00:00
history: Vec<String>,
}
const PROMPT: &str = "bf-interpreter> ";
const HISTORY_FILE: &str = "bf-interpreter-history.bfr";
const COMMAND_PREFIX: &str = "!";
2022-10-12 11:50:22 +00:00
impl Repl<'_> {
pub fn new<'a>(interpreter: &'a mut Interpreter<'a>) -> Repl<'a> {
Repl {
2022-10-07 22:28:14 +00:00
interpreter,
history: Vec::new(),
}
}
2022-10-12 11:50:22 +00:00
pub fn run(mut self,
input: &mut impl Read,
output: &mut impl Write) -> Result<(), std::io::Error> {
2022-10-10 20:55:50 +00:00
let mut code_bat = String::new();
let mut is_loop = false;
2022-10-07 22:28:14 +00:00
loop {
2022-10-12 11:50:22 +00:00
output.write_all(
if is_loop {
format!("{}", "... ".yellow())
} else {
format!("{}", PROMPT)
}.as_bytes()
)?;
2022-10-10 20:55:50 +00:00
2022-10-12 11:50:22 +00:00
let mut user_input = String::new();
2022-10-07 22:28:14 +00:00
2022-10-12 11:50:22 +00:00
match input.read_to_string(&mut user_input) {
Ok(_) => {}
2022-10-07 22:28:14 +00:00
Err(e) => {
error!("Failed to read input: {}", e);
std::process::exit(1);
}
}
2022-10-12 11:50:22 +00:00
user_input = user_input.trim().to_string(); // Remove trailing newline
2022-10-07 22:28:14 +00:00
2022-10-12 11:50:22 +00:00
self.history.push(user_input.clone()); // Save input to history
2022-10-07 22:28:14 +00:00
2022-10-12 11:50:22 +00:00
if user_input.contains('[') && (!user_input.contains(']') && !is_loop) {
let loop_start_index = user_input.find('[').unwrap();
2022-10-10 20:55:50 +00:00
2022-10-12 11:50:22 +00:00
code_bat.push_str(&user_input[loop_start_index..]);
2022-10-10 20:55:50 +00:00
is_loop = true;
2022-10-12 11:50:22 +00:00
user_input = user_input[..loop_start_index].to_string();
2022-10-10 20:55:50 +00:00
}
2022-10-12 11:50:22 +00:00
if user_input.starts_with(COMMAND_PREFIX) {
self.run_repl_cmd(user_input, output)?;
2022-10-07 22:28:14 +00:00
} else {
2022-10-12 11:50:22 +00:00
match self.interpreter.run(user_input) {
2022-10-07 22:28:14 +00:00
Ok(_) => {
info!("Successfully ran brainfuck source code from REPL");
}
2022-10-07 23:05:21 +00:00
Err(e) => {
2022-10-07 22:28:14 +00:00
error!("Failed to run brainfuck source code from REPL: {}", e);
}
}
}
}
}
2022-10-12 11:50:22 +00:00
fn run_repl_cmd(&mut self, user_input: String, output: &mut impl Write) -> Result<(), std::io::Error> {
let mut cmd = user_input.split_whitespace();
match cmd.next() {
Some(repl_cmd) => {
match repl_cmd.get(COMMAND_PREFIX.len()..).unwrap_or("") {
"fuck" => {
2022-10-12 11:50:22 +00:00
output.write_all("Bye bye :D\n".green().as_bytes())?;
std::process::exit(0);
}
"array" | "a" => {
2022-10-12 11:50:22 +00:00
output.write_all(format!("Current array: {:?}\n", self.interpreter.cells).as_bytes())?;
}
"array_size" | "as" => {
2022-10-12 11:50:22 +00:00
output.write_all(format!("Current array size: {}\n",
self.interpreter.array_size.to_string().bold().green()).as_bytes())?;
}
"pointer" | "p" => {
2022-10-12 11:50:22 +00:00
output.write_all(format!("Current pointer: {}\n",
self.interpreter.pointer.to_string().bold().green()).as_bytes())?;
2022-10-07 22:28:14 +00:00
}
2022-10-08 10:01:28 +00:00
"pointer_value" | "pv" => {
2022-10-12 11:50:22 +00:00
format!(
"Current pointer value: {} = \'{}\' (char)\n",
2022-10-08 10:01:28 +00:00
self.interpreter.cells[self.interpreter.pointer],
self.interpreter.cells[self.interpreter.pointer] as char
);
}
"history" | "h" => {
2022-10-12 11:50:22 +00:00
output.write_all("History:\n".underline().green().as_bytes())?;
for (i, cmd) in self.history.iter().enumerate() {
2022-10-12 11:50:22 +00:00
output.write_all(format!("{}: {}", i, cmd).as_bytes())?;
}
2022-10-07 22:28:14 +00:00
}
"save" | "s" => {
let file_name = cmd.next().unwrap_or(HISTORY_FILE);
2022-10-12 11:50:22 +00:00
output.write_all(format!("Saving history to file: {file_name}").yellow().as_bytes())?;
match std::fs::write(file_name, self.history.join("\n")) {
Ok(_) => {
2022-10-12 11:50:22 +00:00
output.write_all(format!("Successfully saved history to file: {file_name}")
.green().as_bytes())?;
}
Err(e) => {
error!("Failed to save history to file: {}", e);
}
}
}
"load" | "l" => {
let file_name = cmd.next().unwrap_or(HISTORY_FILE);
2022-10-12 11:50:22 +00:00
output.write_all(format!("Loading history from file: {file_name}\n").yellow().as_bytes())?;
match std::fs::read_to_string(file_name) {
Ok(history) => {
2022-10-12 11:50:22 +00:00
output.write_all(format!("Successfully loaded history from file: {file_name}")
.green().as_bytes())?;
2022-10-07 23:06:35 +00:00
self.history = history.split("\n").map(|s| s.to_string()).collect();
// Run all commands in history
for cmd in self.history.iter() {
2022-10-10 20:55:50 +00:00
match self.interpreter.run(cmd.clone()) {
Ok(_) => {
2022-10-07 23:06:35 +00:00
info!(
"Successfully ran brainfuck source code from REPL"
);
}
2022-10-07 23:05:21 +00:00
Err(e) => {
2022-10-07 23:06:35 +00:00
error!(
"Failed to run brainfuck source code from REPL: {}",
e
);
}
}
}
}
Err(e) => {
error!("Failed to load history from file: {}", e);
}
}
}
"reset" | "r" => {
2022-10-12 11:50:22 +00:00
output.write_all("Resetting REPL\n".truecolor(56, 33, 102).as_bytes())?;
self.interpreter.reset();
2022-10-12 11:50:22 +00:00
self.history = Vec::new();
}
"help" => {
2022-10-12 11:50:22 +00:00
output.write_all("!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\n".as_bytes())?;
}
2022-10-12 11:50:22 +00:00
_ => output.write_all(format!("Unknown command: {}, type {} to show the help",
user_input, (COMMAND_PREFIX.to_string() + "help").green()
).red().as_bytes())?,
2022-10-07 22:28:14 +00:00
}
2022-10-07 23:06:35 +00:00
}
None => {}
2022-10-07 22:28:14 +00:00
}
2022-10-12 11:50:22 +00:00
Ok(())
2022-10-07 22:28:14 +00:00
}
}
/// Run the REPL
/// # Arguments
/// * `interpreter` - The interpreter to use
2022-10-12 11:50:22 +00:00
pub fn start<'a>(interpreter: &'a mut Interpreter<'a>,
input: &mut impl Read,
output: &mut impl Write) {
2022-10-07 22:28:14 +00:00
info!("Entering REPL mode");
2022-10-12 11:50:22 +00:00
output.write_all(
format!(
"{}\n\
Brainfuck interpreter v {}\nBy {}\n\
{}\n\
Type {} to exit :D\n\
type {} to get more fu*king help\n",
"Welcome to the brainfuck REPL mode! :)".green(),
clap::crate_version!().to_string().yellow(),
clap::crate_authors!().to_string().green(),
"Enter your brainfuck code and press enter to run it.".italic().blue(),
(COMMAND_PREFIX.to_string() + "fuck").bold().red(),
(COMMAND_PREFIX.to_string() + "help").bold().green(),
).as_bytes()).unwrap_or_else(|e| error!("Failed to write to output: {}", e));
match Repl::new(interpreter).run(input, output) {
Ok(_) => {
info!("Successfully ran REPL");
}
Err(e) => {
error!("Failed to run REPL: {}", e);
std::process::exit(1);
}
}
2022-10-07 22:28:14 +00:00
}
2022-10-10 20:55:50 +00:00
#[cfg(test)]
mod tests {
use super::*;
2022-10-12 11:50:22 +00:00
/*#[test]
2022-10-10 20:55:50 +00:00
fn nested_loop_level_1() {
let mut interpreter = Interpreter::new(
30000,
vec![],
);
assert_eq!(interpreter.run(String::from("++")), Ok(0));
assert_eq!(interpreter.run(String::from("[>++")), Ok(0));
assert_eq!(interpreter.run(String::from("[>+<-]")), Ok(0));
assert_eq!(interpreter.run(String::from("<-]")), Ok(0));
assert_eq!(interpreter.cells[2], 4);
2022-10-12 11:50:22 +00:00
}*/
2022-10-10 20:55:50 +00:00
}