Improve the REPL architecture yooo 💙😆🦀

This commit is contained in:
Anas Elgarhy 2022-10-13 17:40:05 +02:00
parent f14c618c24
commit 4af3c47a5d
8 changed files with 304 additions and 290 deletions

View file

@ -55,7 +55,7 @@ impl Cell {
pub fn increment(&mut self, no_reverse_value: bool) -> Result<(), InterpreterError> {
if self.get_value_utf8() == self.max_value() && no_reverse_value {
return Err(InterpreterErrorKind::ValueOutOfBounds.to_error())
return Err(InterpreterErrorKind::ValueOutOfBounds.to_error());
}
match self {
Self::Byte(value) => {
@ -78,7 +78,7 @@ impl Cell {
pub fn decrement(&mut self, no_reverse_value: bool) -> Result<(), InterpreterError> {
if self.get_value_utf8() == 0 && no_reverse_value {
return Err(InterpreterErrorKind::ValueOutOfBounds.to_error())
return Err(InterpreterErrorKind::ValueOutOfBounds.to_error());
}
match self {
Self::Byte(value) => {
@ -109,7 +109,7 @@ impl Cell {
pub fn to_char(&self) -> Result<char, InterpreterError> {
let c = match self {
Self::Byte(value) => Some(*value as char),
Self::Utf8(value) => char::from_u32(*value)
Self::Utf8(value) => char::from_u32(*value),
};
if let Some(c) = c {
@ -145,7 +145,10 @@ mod tests {
}
assert_eq!(cell, Cell::Byte(255));
assert_eq!(cell.increment(true).unwrap_err(), InterpreterErrorKind::ValueOutOfBounds.to_error());
assert_eq!(
cell.increment(true).unwrap_err(),
InterpreterErrorKind::ValueOutOfBounds.to_error()
);
assert_eq!(cell, Cell::Byte(255));
}
@ -160,7 +163,10 @@ mod tests {
}
assert_eq!(cell, Cell::Utf8(1114111));
assert_eq!(cell.increment(true).unwrap_err(), InterpreterErrorKind::ValueOutOfBounds.to_error());
assert_eq!(
cell.increment(true).unwrap_err(),
InterpreterErrorKind::ValueOutOfBounds.to_error()
);
assert_eq!(cell, Cell::Utf8(1114111));
}
@ -205,7 +211,10 @@ mod tests {
}
assert_eq!(cell, Cell::Byte(0));
assert_eq!(cell.decrement(true).unwrap_err(), InterpreterErrorKind::ValueOutOfBounds.to_error());
assert_eq!(
cell.decrement(true).unwrap_err(),
InterpreterErrorKind::ValueOutOfBounds.to_error()
);
assert_eq!(cell, Cell::Byte(0));
}
@ -220,7 +229,10 @@ mod tests {
}
assert_eq!(cell, Cell::Utf8(0));
assert_eq!(cell.decrement(true).unwrap_err(), InterpreterErrorKind::ValueOutOfBounds.to_error());
assert_eq!(
cell.decrement(true).unwrap_err(),
InterpreterErrorKind::ValueOutOfBounds.to_error()
);
assert_eq!(cell, Cell::Utf8(0));
}
@ -276,5 +288,4 @@ mod tests {
cell.set_value('🦀');
assert_eq!(cell, Cell::Utf8(129408));
}
}
}

View file

@ -1,8 +1,8 @@
use crate::arguments;
use crate::bf_interpreter::error::{InterpreterError, InterpreterErrorKind};
use std::io::{Write};
use std::{char, usize, vec};
use crate::bf_interpreter::cell::Cell;
use crate::bf_interpreter::error::{InterpreterError, InterpreterErrorKind};
use std::io::Write;
use std::{char, usize, vec};
pub struct Interpreter {
pub cells: Vec<Cell>,
@ -14,11 +14,7 @@ pub struct Interpreter {
}
impl Interpreter {
pub fn new(
array_size: usize,
features: Vec<arguments::Feature>,
term: console::Term,
) -> Self {
pub fn new(array_size: usize, features: Vec<arguments::Feature>, term: console::Term) -> Self {
Self {
cells: vec![Cell::default_cell(&features); array_size],
pointer: 0,
@ -92,15 +88,15 @@ impl Interpreter {
fn increment_value(&mut self) -> Result<(), InterpreterError> {
trace!("Increment value");
self.cells[self.pointer].increment(
!self.features.contains(&arguments::Feature::NoReverseValue))?;
self.cells[self.pointer]
.increment(!self.features.contains(&arguments::Feature::NoReverseValue))?;
Ok(())
}
fn decrement_value(&mut self) -> Result<(), InterpreterError> {
trace!("Decrement value");
self.cells[self.pointer].decrement(
!self.features.contains(&arguments::Feature::NoReverseValue))?;
self.cells[self.pointer]
.decrement(!self.features.contains(&arguments::Feature::NoReverseValue))?;
Ok(())
}
@ -126,7 +122,7 @@ impl Interpreter {
trace!("Input value");
match self.term.read_char() {
Ok(ch) => {
self.cells[self.pointer].set_value(ch);
self.cells[self.pointer].set_value(ch);
print!("{}", ch);
match std::io::stdout().flush() {
Ok(_) => Ok(()),
@ -207,9 +203,9 @@ impl BfCommand {
#[cfg(test)]
mod tests {
use console::Term;
use super::*;
use crate::utils;
use console::Term;
use pretty_assertions::assert_eq; // for testing only
#[test]
@ -226,8 +222,7 @@ mod tests {
#[test]
fn print_h_repl() {
let mut interpreter = Interpreter::new(30000, vec![],
Term::stdout());
let mut interpreter = Interpreter::new(30000, vec![], Term::stdout());
assert_eq!(interpreter.run(String::from(">+++++++++")), Ok(0));
assert_eq!(interpreter.run(String::from("[<++++ ++++>-]<.")), Ok(0));
@ -237,8 +232,7 @@ mod tests {
#[test]
fn nested_loop_level_1_combine() {
let mut interpreter = Interpreter::new(5, vec![],
Term::stdout());
let mut interpreter = Interpreter::new(5, vec![], Term::stdout());
assert_eq!(interpreter.run(String::from("++[>++[>+<-]<-]")), Ok(0));
assert_eq!(interpreter.cells[2], Cell::new(4, &vec![]));
@ -248,8 +242,7 @@ mod tests {
#[test]
fn execute_hello_world_from_file() {
let mut interpreter = Interpreter::new(30000, vec![],
Term::stdout());
let mut interpreter = Interpreter::new(30000, vec![], Term::stdout());
println!();
@ -263,8 +256,7 @@ mod tests {
#[test]
fn execute_print_hi_from_file() {
let mut interpreter = Interpreter::new(30000, vec![],
Term::stdout());
let mut interpreter = Interpreter::new(30000, vec![], Term::stdout());
println!();
@ -278,8 +270,7 @@ mod tests {
#[test]
fn execute_print_hi_yooo_from_file() {
let mut interpreter = Interpreter::new(30000, vec![],
Term::stdout());
let mut interpreter = Interpreter::new(30000, vec![], Term::stdout());
println!();
@ -293,8 +284,7 @@ mod tests {
#[test]
fn execute_print_my_first_name_from_formatted_file() {
let mut interpreter = Interpreter::new(30000, vec![],
Term::stdout());
let mut interpreter = Interpreter::new(30000, vec![], Term::stdout());
println!();
@ -308,8 +298,7 @@ mod tests {
#[test]
fn execute_print_my_first_name_from_file() {
let mut interpreter = Interpreter::new(30000, vec![],
Term::stdout());
let mut interpreter = Interpreter::new(30000, vec![], Term::stdout());
println!();
@ -320,7 +309,6 @@ mod tests {
Ok(0)
);
assert_eq!(interpreter.cells[0], Cell::default_cell(&vec![]));
assert_eq!(interpreter.cells[1], Cell::default_cell(&vec![]));
assert_eq!(interpreter.cells[2], Cell::new(115, &vec![]));
@ -331,8 +319,7 @@ mod tests {
#[test]
fn execute_print_my_first_name_and_last_name_from_formatted_file() {
let mut interpreter = Interpreter::new(30000, vec![],
Term::stdout());
let mut interpreter = Interpreter::new(30000, vec![], Term::stdout());
println!();
@ -346,8 +333,7 @@ mod tests {
#[test]
fn execute_print_my_first_name_and_last_name_from_file() {
let mut interpreter = Interpreter::new(30000, vec![],
Term::stdout());
let mut interpreter = Interpreter::new(30000, vec![], Term::stdout());
println!();
@ -361,8 +347,7 @@ mod tests {
#[test]
fn reset() {
let mut interpreter = Interpreter::new(30000, vec![],
Term::stdout());
let mut interpreter = Interpreter::new(30000, vec![], Term::stdout());
assert_eq!(interpreter.run(String::from(">++++")), Ok(0));

View file

@ -1,3 +1,3 @@
pub mod cell;
pub mod error;
pub mod interpreter;
pub mod cell;

View file

@ -22,10 +22,11 @@ fn main() {
let term = console::Term::stdout();
info!("Initializing interpreter");
let mut interpreter =
Interpreter::new(args.array_size,
args.features.unwrap_or_else(|| vec![]),
term);
let mut interpreter = Interpreter::new(
args.array_size,
args.features.unwrap_or_else(|| vec![]),
term,
);
match args.source {
Some(source) => {
@ -60,6 +61,6 @@ fn main() {
}
}
}
None => repl::start(interpreter),
None => repl::start_repl::start(interpreter),
}
}

View file

@ -1,19 +1,9 @@
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;
use console::{Term, Key};
struct Repl {
pub interpreter: Interpreter,
term: Term,
history: Vec<String>,
loop_body: String,
loop_depth: usize,
}
const PROMPT: &str = "bf-interpreter> ";
const HISTORY_FILE: &str = "bf-interpreter-history.bfr";
const COMMAND_PREFIX: &str = "!";
impl Repl {
pub fn new(interpreter: Interpreter) -> Repl {
@ -70,7 +60,9 @@ impl Repl {
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)
let last = self
.history
.get(self.history.len() - 1 - rev_index)
.unwrap();
rev_index += 1;
self.term.clear_line()?;
@ -81,8 +73,7 @@ impl Repl {
}
Key::ArrowDown => {
if !self.history.is_empty() && rev_index > 0 {
let first = self.history.get(self.history.len() - rev_index)
.unwrap();
let first = self.history.get(self.history.len() - rev_index).unwrap();
rev_index -= 1;
self.term.clear_line()?;
self.print_prompt();
@ -194,7 +185,9 @@ impl Repl {
println!(
"Current pointer value: {} = \'{}\' (char)",
self.interpreter.cells[self.interpreter.pointer],
self.interpreter.cells[self.interpreter.pointer].to_char().unwrap_or_else(|_| '?')
self.interpreter.cells[self.interpreter.pointer]
.to_char()
.unwrap_or_else(|_| '?')
);
}
"history" | "h" => {
@ -287,7 +280,7 @@ impl Repl {
user_input,
(COMMAND_PREFIX.to_string() + "help").green()
)
.red()
.red()
),
}
}
@ -295,225 +288,3 @@ impl Repl {
}
}
}
/// Run the REPL
/// # Arguments
/// * `interpreter` - The interpreter to use
pub fn start(interpreter: Interpreter) {
info!("Entering REPL mode");
println!(
"{}\n\
Brainfuck interpreter v {}\nBy {}\n\
{}\n\
Type {} to exit :D\n\
type {} to get more fu*king help",
"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(),
);
match Repl::new(interpreter).run() {
Ok(_) => {
info!("Successfully ran REPL");
}
Err(e) => {
error!("Failed to run REPL: {}", e);
std::process::exit(1);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use pretty_assertions::assert_eq;
use crate::bf_interpreter::cell::Cell;
#[test]
fn nested_loop_level_1() {
let term = Term::stdout();
let interpreter = Interpreter::new(4, vec![], term);
let mut repl = Repl::new(interpreter);
repl.process("++".to_string());
repl.process("[>++".to_string());
repl.process("[>+<-]".to_string());
repl.process("<-]".to_string());
let cells = &repl.interpreter.cells;
assert_eq!(cells[0], Cell::default_cell(&vec![]));
assert_eq!(cells[1], Cell::default_cell(&vec![]));
assert_eq!(cells[2], Cell::new(4, &vec![]));
}
#[test]
fn nested_loop_level_2() {
let term = Term::stdout();
let interpreter = Interpreter::new(4, vec![], term);
let mut repl = Repl::new(interpreter);
repl.process("++".to_string());
repl.process("[>++".to_string());
repl.process("[>+<-]".to_string());
repl.process("[>++".to_string());
repl.process("[>+<-]".to_string());
repl.process("<-]".to_string());
repl.process("<-]".to_string());
let cells = &repl.interpreter.cells;
assert_eq!(cells[0], Cell::default_cell(&vec![]));
assert_eq!(cells[1], Cell::default_cell(&vec![]));
assert_eq!(cells[2], Cell::new(4, &vec![]));
}
#[test]
fn print_my_first_name() {
let term = Term::stdout();
let interpreter = Interpreter::new(10, vec![], term);
let mut repl = Repl::new(interpreter);
let code = "++++ ++++ 8
[
>++++
[
>++ A
>+++ a
>++++
>+ space
<<<<-
]
>>>>>>++
[
<<<-
>>>-
]
<<<<<<<-
]
>>+. Print cell 2: A
<<++++
[
>+++
[
>+++
<-
]
>++
<<-
]
>>+. Print n
<<+++
[
>+++
[
>-
<-
]
>-
<<-
]
>>-. Print n
<<++++++
[
>>+++
<<-
]
>>. Print s"
.to_string()
.split("\n")
.map(|s| s.to_string())
.collect::<Vec<String>>();
for line in code {
repl.process(line);
}
assert_eq!(repl.interpreter.cells[0], Cell::default_cell(&vec![]));
assert_eq!(repl.interpreter.cells[1], Cell::default_cell(&vec![]));
assert_eq!(repl.interpreter.cells[2], Cell::new(115, &vec![]));
assert_eq!(repl.interpreter.cells[3], Cell::new(96, &vec![]));
assert_eq!(repl.interpreter.cells[4], Cell::new(112, &vec![]));
assert_eq!(repl.interpreter.cells[5], Cell::new(32, &vec![]));
}
#[test]
fn print_my_first_name_in_one_command() {
let term = Term::stdout();
let interpreter = Interpreter::new(10, vec![], term);
let mut repl = Repl::new(interpreter);
let code = "++++++++[>++++[>++>+++>++++>+<<<<-]>>>>>>++[<<<->>>-]<<<<<<<-]>>+.\
<<++++[>+++[>+++<-]>++<<-]>>+.<<+++[>+++[>-<-]>-<<-]>>-.<<++++++[>>+++<<-]>>."
.to_string();
repl.process(code);
assert_eq!(repl.interpreter.cells[0], Cell::default_cell(&vec![]));
assert_eq!(repl.interpreter.cells[1], Cell::default_cell(&vec![]));
assert_eq!(repl.interpreter.cells[2], Cell::new(115, &vec![]));
assert_eq!(repl.interpreter.cells[3], Cell::new(96, &vec![]));
assert_eq!(repl.interpreter.cells[4], Cell::new(112, &vec![]));
assert_eq!(repl.interpreter.cells[5], Cell::new(32, &vec![]));
}
#[test]
fn print_hello_world() {
let term = Term::stdout();
let interpreter = Interpreter::new(10, vec![], term);
let mut repl = Repl::new(interpreter);
let _ = "[ This program prints \"Hello World!\" and a newline to the screen, its
length is 106 active command characters. [It is not the shortest.]
]
++++++++ Set Cell #0 to 8
[
>++++ Add 4 to Cell #1; this will always set Cell #1 to 4
[ as the cell will be cleared by the loop
>++ Add 2 to Cell #2
>+++ Add 3 to Cell #3
>+++ Add 3 to Cell #4
>+ Add 1 to Cell #5
<<<<- Decrement the loop counter in Cell #1
] Loop until Cell #1 is zero; number of iterations is 4
>+ Add 1 to Cell #2
>+ Add 1 to Cell #3
>- Subtract 1 from Cell #4
>>+ Add 1 to Cell #6
[<] Move back to the first zero cell you find; this will
be Cell #1 which was cleared by the previous loop
<- Decrement the loop Counter in Cell #0
] Loop until Cell #0 is zero; number of iterations is 8
The result of this is:
Cell no : 0 1 2 3 4 5 6
Contents: 0 0 72 104 88 32 8
Pointer : ^
>>. Cell #2 has value 72 which is 'H'
>---. Subtract 3 from Cell #3 to get 101 which is 'e'
+++++++..+++. Likewise for 'llo' from Cell #3
>>. Cell #5 is 32 for the space
<-. Subtract 1 from Cell #4 for 87 to give a 'W'
<. Cell #3 was set to 'o' from the end of 'Hello'
+++.------.--------. Cell #3 for 'rl' and 'd'
>>+. Add 1 to Cell #5 gives us an exclamation point
>++. And finally a newline from Cell #6
"
.to_string()
.split("\n")
.for_each(|s| repl.process(s.to_string()));
}
}

3
src/repl/mod.rs Normal file
View file

@ -0,0 +1,3 @@
mod impl_repl;
mod repl;
pub mod start_repl;

208
src/repl/repl.rs Normal file
View file

@ -0,0 +1,208 @@
use crate::bf_interpreter::interpreter::Interpreter;
use console::Term;
pub struct Repl {
pub interpreter: Interpreter,
pub term: Term,
pub history: Vec<String>,
pub loop_body: String,
pub loop_depth: usize,
}
/// The REPL prompt
pub const PROMPT: &str = "bf-interpreter> ";
/// History file name
pub const HISTORY_FILE: &str = "bf-interpreter-history.bfr";
/// The command prefix
pub const COMMAND_PREFIX: &str = "!";
/// Tests :D
#[cfg(test)]
mod tests {
use super::*;
use crate::bf_interpreter::cell::Cell;
use pretty_assertions::assert_eq;
#[test]
fn nested_loop_level_1() {
let term = Term::stdout();
let interpreter = Interpreter::new(4, vec![], term);
let mut repl = Repl::new(interpreter);
repl.process("++".to_string());
repl.process("[>++".to_string());
repl.process("[>+<-]".to_string());
repl.process("<-]".to_string());
let cells = &repl.interpreter.cells;
assert_eq!(cells[0], Cell::default_cell(&vec![]));
assert_eq!(cells[1], Cell::default_cell(&vec![]));
assert_eq!(cells[2], Cell::new(4, &vec![]));
}
#[test]
fn nested_loop_level_2() {
let term = Term::stdout();
let interpreter = Interpreter::new(4, vec![], term);
let mut repl = Repl::new(interpreter);
repl.process("++".to_string());
repl.process("[>++".to_string());
repl.process("[>+<-]".to_string());
repl.process("[>++".to_string());
repl.process("[>+<-]".to_string());
repl.process("<-]".to_string());
repl.process("<-]".to_string());
let cells = &repl.interpreter.cells;
assert_eq!(cells[0], Cell::default_cell(&vec![]));
assert_eq!(cells[1], Cell::default_cell(&vec![]));
assert_eq!(cells[2], Cell::new(4, &vec![]));
}
#[test]
fn print_my_first_name() {
let term = Term::stdout();
let interpreter = Interpreter::new(10, vec![], term);
let mut repl = Repl::new(interpreter);
let code = "++++ ++++ 8
[
>++++
[
>++ A
>+++ a
>++++
>+ space
<<<<-
]
>>>>>>++
[
<<<-
>>>-
]
<<<<<<<-
]
>>+. Print cell 2: A
<<++++
[
>+++
[
>+++
<-
]
>++
<<-
]
>>+. Print n
<<+++
[
>+++
[
>-
<-
]
>-
<<-
]
>>-. Print n
<<++++++
[
>>+++
<<-
]
>>. Print s"
.to_string()
.split("\n")
.map(|s| s.to_string())
.collect::<Vec<String>>();
for line in code {
repl.process(line);
}
assert_eq!(repl.interpreter.cells[0], Cell::default_cell(&vec![]));
assert_eq!(repl.interpreter.cells[1], Cell::default_cell(&vec![]));
assert_eq!(repl.interpreter.cells[2], Cell::new(115, &vec![]));
assert_eq!(repl.interpreter.cells[3], Cell::new(96, &vec![]));
assert_eq!(repl.interpreter.cells[4], Cell::new(112, &vec![]));
assert_eq!(repl.interpreter.cells[5], Cell::new(32, &vec![]));
}
#[test]
fn print_my_first_name_in_one_command() {
let term = Term::stdout();
let interpreter = Interpreter::new(10, vec![], term);
let mut repl = Repl::new(interpreter);
let code = "++++++++[>++++[>++>+++>++++>+<<<<-]>>>>>>++[<<<->>>-]<<<<<<<-]>>+.\
<<++++[>+++[>+++<-]>++<<-]>>+.<<+++[>+++[>-<-]>-<<-]>>-.<<++++++[>>+++<<-]>>."
.to_string();
repl.process(code);
assert_eq!(repl.interpreter.cells[0], Cell::default_cell(&vec![]));
assert_eq!(repl.interpreter.cells[1], Cell::default_cell(&vec![]));
assert_eq!(repl.interpreter.cells[2], Cell::new(115, &vec![]));
assert_eq!(repl.interpreter.cells[3], Cell::new(96, &vec![]));
assert_eq!(repl.interpreter.cells[4], Cell::new(112, &vec![]));
assert_eq!(repl.interpreter.cells[5], Cell::new(32, &vec![]));
}
#[test]
fn print_hello_world() {
let term = Term::stdout();
let interpreter = Interpreter::new(10, vec![], term);
let mut repl = Repl::new(interpreter);
let _ = "[ This program prints \"Hello World!\" and a newline to the screen, its
length is 106 active command characters. [It is not the shortest.]
]
++++++++ Set Cell #0 to 8
[
>++++ Add 4 to Cell #1; this will always set Cell #1 to 4
[ as the cell will be cleared by the loop
>++ Add 2 to Cell #2
>+++ Add 3 to Cell #3
>+++ Add 3 to Cell #4
>+ Add 1 to Cell #5
<<<<- Decrement the loop counter in Cell #1
] Loop until Cell #1 is zero; number of iterations is 4
>+ Add 1 to Cell #2
>+ Add 1 to Cell #3
>- Subtract 1 from Cell #4
>>+ Add 1 to Cell #6
[<] Move back to the first zero cell you find; this will
be Cell #1 which was cleared by the previous loop
<- Decrement the loop Counter in Cell #0
] Loop until Cell #0 is zero; number of iterations is 8
The result of this is:
Cell no : 0 1 2 3 4 5 6
Contents: 0 0 72 104 88 32 8
Pointer : ^
>>. Cell #2 has value 72 which is 'H'
>---. Subtract 3 from Cell #3 to get 101 which is 'e'
+++++++..+++. Likewise for 'llo' from Cell #3
>>. Cell #5 is 32 for the space
<-. Subtract 1 from Cell #4 for 87 to give a 'W'
<. Cell #3 was set to 'o' from the end of 'Hello'
+++.------.--------. Cell #3 for 'rl' and 'd'
>>+. Add 1 to Cell #5 gives us an exclamation point
>++. And finally a newline from Cell #6
"
.to_string()
.split("\n")
.for_each(|s| repl.process(s.to_string()));
}
}

35
src/repl/start_repl.rs Normal file
View file

@ -0,0 +1,35 @@
use crate::bf_interpreter::interpreter::Interpreter;
use crate::repl::repl::{Repl, COMMAND_PREFIX};
use colored::Colorize;
/// Run the REPL
/// # Arguments
/// * `interpreter` - The interpreter to use
pub fn start(interpreter: Interpreter) {
info!("Entering REPL mode");
println!(
"{}\n\
Brainfuck interpreter v {}\nBy {}\n\
{}\n\
Type {} to exit :D\n\
type {} to get more fu*king help",
"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(),
);
match Repl::new(interpreter).run() {
Ok(_) => {
info!("Successfully ran REPL");
}
Err(e) => {
error!("Failed to run REPL: {}", e);
std::process::exit(1);
}
}
}