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