Improve the REPL architecture yooo 💙😆🦀
This commit is contained in:
parent
f14c618c24
commit
4af3c47a5d
8 changed files with 304 additions and 290 deletions
|
@ -55,7 +55,7 @@ impl Cell {
|
||||||
|
|
||||||
pub fn increment(&mut self, no_reverse_value: bool) -> Result<(), InterpreterError> {
|
pub fn increment(&mut self, no_reverse_value: bool) -> Result<(), InterpreterError> {
|
||||||
if self.get_value_utf8() == self.max_value() && no_reverse_value {
|
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 {
|
match self {
|
||||||
Self::Byte(value) => {
|
Self::Byte(value) => {
|
||||||
|
@ -78,7 +78,7 @@ impl Cell {
|
||||||
|
|
||||||
pub fn decrement(&mut self, no_reverse_value: bool) -> Result<(), InterpreterError> {
|
pub fn decrement(&mut self, no_reverse_value: bool) -> Result<(), InterpreterError> {
|
||||||
if self.get_value_utf8() == 0 && no_reverse_value {
|
if self.get_value_utf8() == 0 && no_reverse_value {
|
||||||
return Err(InterpreterErrorKind::ValueOutOfBounds.to_error())
|
return Err(InterpreterErrorKind::ValueOutOfBounds.to_error());
|
||||||
}
|
}
|
||||||
match self {
|
match self {
|
||||||
Self::Byte(value) => {
|
Self::Byte(value) => {
|
||||||
|
@ -109,7 +109,7 @@ impl Cell {
|
||||||
pub fn to_char(&self) -> Result<char, InterpreterError> {
|
pub fn to_char(&self) -> Result<char, InterpreterError> {
|
||||||
let c = match self {
|
let c = match self {
|
||||||
Self::Byte(value) => Some(*value as char),
|
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 {
|
if let Some(c) = c {
|
||||||
|
@ -145,7 +145,10 @@ mod tests {
|
||||||
}
|
}
|
||||||
assert_eq!(cell, Cell::Byte(255));
|
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));
|
assert_eq!(cell, Cell::Byte(255));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -160,7 +163,10 @@ mod tests {
|
||||||
}
|
}
|
||||||
assert_eq!(cell, Cell::Utf8(1114111));
|
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));
|
assert_eq!(cell, Cell::Utf8(1114111));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -205,7 +211,10 @@ mod tests {
|
||||||
}
|
}
|
||||||
assert_eq!(cell, Cell::Byte(0));
|
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));
|
assert_eq!(cell, Cell::Byte(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -220,7 +229,10 @@ mod tests {
|
||||||
}
|
}
|
||||||
assert_eq!(cell, Cell::Utf8(0));
|
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));
|
assert_eq!(cell, Cell::Utf8(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -276,5 +288,4 @@ mod tests {
|
||||||
cell.set_value('🦀');
|
cell.set_value('🦀');
|
||||||
assert_eq!(cell, Cell::Utf8(129408));
|
assert_eq!(cell, Cell::Utf8(129408));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use crate::arguments;
|
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::cell::Cell;
|
||||||
|
use crate::bf_interpreter::error::{InterpreterError, InterpreterErrorKind};
|
||||||
|
use std::io::Write;
|
||||||
|
use std::{char, usize, vec};
|
||||||
|
|
||||||
pub struct Interpreter {
|
pub struct Interpreter {
|
||||||
pub cells: Vec<Cell>,
|
pub cells: Vec<Cell>,
|
||||||
|
@ -14,11 +14,7 @@ pub struct Interpreter {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Interpreter {
|
impl Interpreter {
|
||||||
pub fn new(
|
pub fn new(array_size: usize, features: Vec<arguments::Feature>, term: console::Term) -> Self {
|
||||||
array_size: usize,
|
|
||||||
features: Vec<arguments::Feature>,
|
|
||||||
term: console::Term,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
Self {
|
||||||
cells: vec![Cell::default_cell(&features); array_size],
|
cells: vec![Cell::default_cell(&features); array_size],
|
||||||
pointer: 0,
|
pointer: 0,
|
||||||
|
@ -92,15 +88,15 @@ impl Interpreter {
|
||||||
|
|
||||||
fn increment_value(&mut self) -> Result<(), InterpreterError> {
|
fn increment_value(&mut self) -> Result<(), InterpreterError> {
|
||||||
trace!("Increment value");
|
trace!("Increment value");
|
||||||
self.cells[self.pointer].increment(
|
self.cells[self.pointer]
|
||||||
!self.features.contains(&arguments::Feature::NoReverseValue))?;
|
.increment(!self.features.contains(&arguments::Feature::NoReverseValue))?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decrement_value(&mut self) -> Result<(), InterpreterError> {
|
fn decrement_value(&mut self) -> Result<(), InterpreterError> {
|
||||||
trace!("Decrement value");
|
trace!("Decrement value");
|
||||||
self.cells[self.pointer].decrement(
|
self.cells[self.pointer]
|
||||||
!self.features.contains(&arguments::Feature::NoReverseValue))?;
|
.decrement(!self.features.contains(&arguments::Feature::NoReverseValue))?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,7 +122,7 @@ impl Interpreter {
|
||||||
trace!("Input value");
|
trace!("Input value");
|
||||||
match self.term.read_char() {
|
match self.term.read_char() {
|
||||||
Ok(ch) => {
|
Ok(ch) => {
|
||||||
self.cells[self.pointer].set_value(ch);
|
self.cells[self.pointer].set_value(ch);
|
||||||
print!("{}", ch);
|
print!("{}", ch);
|
||||||
match std::io::stdout().flush() {
|
match std::io::stdout().flush() {
|
||||||
Ok(_) => Ok(()),
|
Ok(_) => Ok(()),
|
||||||
|
@ -207,9 +203,9 @@ impl BfCommand {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use console::Term;
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::utils;
|
use crate::utils;
|
||||||
|
use console::Term;
|
||||||
use pretty_assertions::assert_eq; // for testing only
|
use pretty_assertions::assert_eq; // for testing only
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -226,8 +222,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn print_h_repl() {
|
fn print_h_repl() {
|
||||||
let mut interpreter = Interpreter::new(30000, vec![],
|
let mut interpreter = Interpreter::new(30000, vec![], Term::stdout());
|
||||||
Term::stdout());
|
|
||||||
|
|
||||||
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));
|
||||||
|
@ -237,8 +232,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn nested_loop_level_1_combine() {
|
fn nested_loop_level_1_combine() {
|
||||||
let mut interpreter = Interpreter::new(5, vec![],
|
let mut interpreter = Interpreter::new(5, vec![], Term::stdout());
|
||||||
Term::stdout());
|
|
||||||
|
|
||||||
assert_eq!(interpreter.run(String::from("++[>++[>+<-]<-]")), Ok(0));
|
assert_eq!(interpreter.run(String::from("++[>++[>+<-]<-]")), Ok(0));
|
||||||
assert_eq!(interpreter.cells[2], Cell::new(4, &vec![]));
|
assert_eq!(interpreter.cells[2], Cell::new(4, &vec![]));
|
||||||
|
@ -248,8 +242,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn execute_hello_world_from_file() {
|
fn execute_hello_world_from_file() {
|
||||||
let mut interpreter = Interpreter::new(30000, vec![],
|
let mut interpreter = Interpreter::new(30000, vec![], Term::stdout());
|
||||||
Term::stdout());
|
|
||||||
|
|
||||||
println!();
|
println!();
|
||||||
|
|
||||||
|
@ -263,8 +256,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn execute_print_hi_from_file() {
|
fn execute_print_hi_from_file() {
|
||||||
let mut interpreter = Interpreter::new(30000, vec![],
|
let mut interpreter = Interpreter::new(30000, vec![], Term::stdout());
|
||||||
Term::stdout());
|
|
||||||
|
|
||||||
println!();
|
println!();
|
||||||
|
|
||||||
|
@ -278,8 +270,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn execute_print_hi_yooo_from_file() {
|
fn execute_print_hi_yooo_from_file() {
|
||||||
let mut interpreter = Interpreter::new(30000, vec![],
|
let mut interpreter = Interpreter::new(30000, vec![], Term::stdout());
|
||||||
Term::stdout());
|
|
||||||
|
|
||||||
println!();
|
println!();
|
||||||
|
|
||||||
|
@ -293,8 +284,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn execute_print_my_first_name_from_formatted_file() {
|
fn execute_print_my_first_name_from_formatted_file() {
|
||||||
let mut interpreter = Interpreter::new(30000, vec![],
|
let mut interpreter = Interpreter::new(30000, vec![], Term::stdout());
|
||||||
Term::stdout());
|
|
||||||
|
|
||||||
println!();
|
println!();
|
||||||
|
|
||||||
|
@ -308,8 +298,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn execute_print_my_first_name_from_file() {
|
fn execute_print_my_first_name_from_file() {
|
||||||
let mut interpreter = Interpreter::new(30000, vec![],
|
let mut interpreter = Interpreter::new(30000, vec![], Term::stdout());
|
||||||
Term::stdout());
|
|
||||||
|
|
||||||
println!();
|
println!();
|
||||||
|
|
||||||
|
@ -320,7 +309,6 @@ mod tests {
|
||||||
Ok(0)
|
Ok(0)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
assert_eq!(interpreter.cells[0], Cell::default_cell(&vec![]));
|
assert_eq!(interpreter.cells[0], Cell::default_cell(&vec![]));
|
||||||
assert_eq!(interpreter.cells[1], Cell::default_cell(&vec![]));
|
assert_eq!(interpreter.cells[1], Cell::default_cell(&vec![]));
|
||||||
assert_eq!(interpreter.cells[2], Cell::new(115, &vec![]));
|
assert_eq!(interpreter.cells[2], Cell::new(115, &vec![]));
|
||||||
|
@ -331,8 +319,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn execute_print_my_first_name_and_last_name_from_formatted_file() {
|
fn execute_print_my_first_name_and_last_name_from_formatted_file() {
|
||||||
let mut interpreter = Interpreter::new(30000, vec![],
|
let mut interpreter = Interpreter::new(30000, vec![], Term::stdout());
|
||||||
Term::stdout());
|
|
||||||
|
|
||||||
println!();
|
println!();
|
||||||
|
|
||||||
|
@ -346,8 +333,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn execute_print_my_first_name_and_last_name_from_file() {
|
fn execute_print_my_first_name_and_last_name_from_file() {
|
||||||
let mut interpreter = Interpreter::new(30000, vec![],
|
let mut interpreter = Interpreter::new(30000, vec![], Term::stdout());
|
||||||
Term::stdout());
|
|
||||||
|
|
||||||
println!();
|
println!();
|
||||||
|
|
||||||
|
@ -361,8 +347,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn reset() {
|
fn reset() {
|
||||||
let mut interpreter = Interpreter::new(30000, vec![],
|
let mut interpreter = Interpreter::new(30000, vec![], Term::stdout());
|
||||||
Term::stdout());
|
|
||||||
|
|
||||||
assert_eq!(interpreter.run(String::from(">++++")), Ok(0));
|
assert_eq!(interpreter.run(String::from(">++++")), Ok(0));
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
|
pub mod cell;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod interpreter;
|
pub mod interpreter;
|
||||||
pub mod cell;
|
|
||||||
|
|
11
src/main.rs
11
src/main.rs
|
@ -22,10 +22,11 @@ fn main() {
|
||||||
let term = console::Term::stdout();
|
let term = console::Term::stdout();
|
||||||
|
|
||||||
info!("Initializing interpreter");
|
info!("Initializing interpreter");
|
||||||
let mut interpreter =
|
let mut interpreter = Interpreter::new(
|
||||||
Interpreter::new(args.array_size,
|
args.array_size,
|
||||||
args.features.unwrap_or_else(|| vec![]),
|
args.features.unwrap_or_else(|| vec![]),
|
||||||
term);
|
term,
|
||||||
|
);
|
||||||
|
|
||||||
match args.source {
|
match args.source {
|
||||||
Some(source) => {
|
Some(source) => {
|
||||||
|
@ -60,6 +61,6 @@ fn main() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => repl::start(interpreter),
|
None => repl::start_repl::start(interpreter),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +1,9 @@
|
||||||
|
use super::repl::Repl;
|
||||||
use crate::bf_interpreter::interpreter::Interpreter;
|
use crate::bf_interpreter::interpreter::Interpreter;
|
||||||
|
use crate::repl::repl::{COMMAND_PREFIX, HISTORY_FILE, PROMPT};
|
||||||
use colored::Colorize;
|
use colored::Colorize;
|
||||||
|
use console::Key;
|
||||||
use std::io::Write;
|
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 {
|
impl Repl {
|
||||||
pub fn new(interpreter: Interpreter) -> Repl {
|
pub fn new(interpreter: Interpreter) -> Repl {
|
||||||
|
@ -70,7 +60,9 @@ impl Repl {
|
||||||
match key {
|
match key {
|
||||||
Key::ArrowUp => {
|
Key::ArrowUp => {
|
||||||
if !self.history.is_empty() && rev_index < self.history.len() {
|
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();
|
.unwrap();
|
||||||
rev_index += 1;
|
rev_index += 1;
|
||||||
self.term.clear_line()?;
|
self.term.clear_line()?;
|
||||||
|
@ -81,8 +73,7 @@ impl Repl {
|
||||||
}
|
}
|
||||||
Key::ArrowDown => {
|
Key::ArrowDown => {
|
||||||
if !self.history.is_empty() && rev_index > 0 {
|
if !self.history.is_empty() && rev_index > 0 {
|
||||||
let first = self.history.get(self.history.len() - rev_index)
|
let first = self.history.get(self.history.len() - rev_index).unwrap();
|
||||||
.unwrap();
|
|
||||||
rev_index -= 1;
|
rev_index -= 1;
|
||||||
self.term.clear_line()?;
|
self.term.clear_line()?;
|
||||||
self.print_prompt();
|
self.print_prompt();
|
||||||
|
@ -194,7 +185,9 @@ impl Repl {
|
||||||
println!(
|
println!(
|
||||||
"Current pointer value: {} = \'{}\' (char)",
|
"Current pointer value: {} = \'{}\' (char)",
|
||||||
self.interpreter.cells[self.interpreter.pointer],
|
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" => {
|
"history" | "h" => {
|
||||||
|
@ -287,7 +280,7 @@ impl Repl {
|
||||||
user_input,
|
user_input,
|
||||||
(COMMAND_PREFIX.to_string() + "help").green()
|
(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
3
src/repl/mod.rs
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
mod impl_repl;
|
||||||
|
mod repl;
|
||||||
|
pub mod start_repl;
|
208
src/repl/repl.rs
Normal file
208
src/repl/repl.rs
Normal 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
35
src/repl/start_repl.rs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue