Fix the repl and improve it yo 🤪
This commit is contained in:
parent
639c39c9f6
commit
fa39f358ce
4 changed files with 224 additions and 100 deletions
|
@ -38,6 +38,7 @@ clap = { version = "4.0.10", features = ["derive", "color", "cargo"] }
|
||||||
log = "0.4.17"
|
log = "0.4.17"
|
||||||
pretty_env_logger = "0.4.0"
|
pretty_env_logger = "0.4.0"
|
||||||
colored = "2.0.0"
|
colored = "2.0.0"
|
||||||
|
no-panic = "0.1.16"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
pretty_assertions = "1.3.0"
|
pretty_assertions = "1.3.0"
|
|
@ -1,23 +1,19 @@
|
||||||
use crate::arguments;
|
use crate::arguments;
|
||||||
use crate::bf_interpreter::error::{InterpreterError, InterpreterErrorKind};
|
use crate::bf_interpreter::error::{InterpreterError, InterpreterErrorKind};
|
||||||
use std::io::{Read, Write};
|
use std::io::{BufRead, Read, Write};
|
||||||
use std::{char, usize, vec};
|
use std::{char, usize, vec};
|
||||||
|
|
||||||
pub struct Interpreter<'a> {
|
pub struct Interpreter {
|
||||||
pub cells: Vec<u8>,
|
pub cells: Vec<u8>,
|
||||||
pub pointer: usize,
|
pub pointer: usize,
|
||||||
pub bf_commands: Vec<BfCommand>,
|
pub bf_commands: Vec<BfCommand>,
|
||||||
brackets: Vec<BfCommand>,
|
brackets: Vec<BfCommand>,
|
||||||
pub input: &'a Box<dyn Read>,
|
|
||||||
pub output: &'a Box<dyn Write>,
|
|
||||||
pub features: Vec<arguments::Feature>,
|
pub features: Vec<arguments::Feature>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Interpreter<'a> {
|
impl Interpreter {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
array_size: usize,
|
array_size: usize,
|
||||||
input: &'a mut Box<dyn Read>,
|
|
||||||
output: &'a mut Box<dyn Write>,
|
|
||||||
features: Vec<arguments::Feature>,
|
features: Vec<arguments::Feature>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
@ -25,8 +21,6 @@ impl<'a> Interpreter<'a> {
|
||||||
pointer: 0,
|
pointer: 0,
|
||||||
bf_commands: vec![],
|
bf_commands: vec![],
|
||||||
brackets: Vec::new(),
|
brackets: Vec::new(),
|
||||||
input,
|
|
||||||
output,
|
|
||||||
features,
|
features,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
13
src/main.rs
13
src/main.rs
|
@ -3,7 +3,7 @@ mod repl;
|
||||||
mod utils;
|
mod utils;
|
||||||
mod bf_interpreter;
|
mod bf_interpreter;
|
||||||
|
|
||||||
use std::io::{Read, Write};
|
use std::io::{BufRead, Write};
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
extern crate pretty_env_logger;
|
extern crate pretty_env_logger;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
|
@ -20,13 +20,9 @@ fn main() {
|
||||||
let args = Args::parse();
|
let args = Args::parse();
|
||||||
info!("Parsed command line arguments: {:?}", args);
|
info!("Parsed command line arguments: {:?}", args);
|
||||||
|
|
||||||
let mut stdin: Box<dyn Read> = Box::new(std::io::stdin());
|
|
||||||
let mut stdout: Box<dyn Write> = Box::new(std::io::stdout());
|
|
||||||
info!("Initializing interpreter");
|
info!("Initializing interpreter");
|
||||||
let mut interpreter = Interpreter::new(
|
let mut interpreter = Interpreter::new(
|
||||||
args.array_size,
|
args.array_size,
|
||||||
&mut stdin,
|
|
||||||
&mut stdout,
|
|
||||||
args.features.unwrap_or_else(|| vec![]),
|
args.features.unwrap_or_else(|| vec![]),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -41,8 +37,9 @@ fn main() {
|
||||||
"Successfully ran brainfuck source code from file: {}",
|
"Successfully ran brainfuck source code from file: {}",
|
||||||
source
|
source
|
||||||
).bold().green());
|
).bold().green());
|
||||||
println!("Exiting with code: {exit_code}");
|
println!("{}{}", "Exiting with code: ".truecolor(33, 97, 61),
|
||||||
std::process::exit(0);
|
exit_code.to_string().bold().green());
|
||||||
|
std::process::exit(exit_code);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
@ -51,6 +48,6 @@ fn main() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => repl::start(&mut interpreter, &mut std::io::stdin(), &mut std::io::stdout()),
|
None => repl::start(interpreter),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
298
src/repl.rs
298
src/repl.rs
|
@ -1,41 +1,45 @@
|
||||||
use crate::bf_interpreter::interpreter::Interpreter;
|
use crate::bf_interpreter::interpreter::Interpreter;
|
||||||
use std::io::{Write, Read};
|
use std::io::{Write, Read, BufRead, BufReader, Stdout, Stdin};
|
||||||
use colored::Colorize;
|
use colored::Colorize;
|
||||||
|
use no_panic::no_panic;
|
||||||
|
|
||||||
struct Repl<'a> {
|
struct Repl {
|
||||||
interpreter: &'a mut Interpreter<'a>,
|
interpreter: Interpreter,
|
||||||
history: Vec<String>,
|
history: Vec<String>,
|
||||||
|
loop_body: String,
|
||||||
|
loop_depth: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
const PROMPT: &str = "bf-interpreter> ";
|
const PROMPT: &str = "bf-interpreter> ";
|
||||||
const HISTORY_FILE: &str = "bf-interpreter-history.bfr";
|
const HISTORY_FILE: &str = "bf-interpreter-history.bfr";
|
||||||
const COMMAND_PREFIX: &str = "!";
|
const COMMAND_PREFIX: &str = "!";
|
||||||
|
|
||||||
impl Repl<'_> {
|
impl Repl {
|
||||||
pub fn new<'a>(interpreter: &'a mut Interpreter<'a>) -> Repl<'a> {
|
pub fn new(interpreter: Interpreter) -> Repl {
|
||||||
Repl {
|
Repl {
|
||||||
interpreter,
|
interpreter,
|
||||||
history: Vec::new(),
|
history: Vec::new(),
|
||||||
|
loop_body: String::new(),
|
||||||
|
loop_depth: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(mut self,
|
// #[no_panic]
|
||||||
input: &mut impl Read,
|
pub fn run(mut self) -> Result<(), std::io::Error> {
|
||||||
output: &mut impl Write) -> Result<(), std::io::Error> {
|
|
||||||
let mut code_bat = String::new();
|
|
||||||
let mut is_loop = false;
|
|
||||||
loop {
|
loop {
|
||||||
output.write_all(
|
print!("{}",
|
||||||
if is_loop {
|
if self.loop_depth != 0 {
|
||||||
format!("{}", "... ".yellow())
|
"........ ".yellow()
|
||||||
} else {
|
} else {
|
||||||
format!("{}", PROMPT)
|
PROMPT.to_string().truecolor(54, 76, 76)
|
||||||
}.as_bytes()
|
}
|
||||||
)?;
|
);
|
||||||
|
|
||||||
|
std::io::stdout().flush()?;
|
||||||
|
|
||||||
let mut user_input = String::new();
|
let mut user_input = String::new();
|
||||||
|
|
||||||
match input.read_to_string(&mut user_input) {
|
match std::io::stdin().read_line(&mut user_input) {
|
||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("Failed to read input: {}", e);
|
error!("Failed to read input: {}", e);
|
||||||
|
@ -44,72 +48,106 @@ impl Repl<'_> {
|
||||||
}
|
}
|
||||||
user_input = user_input.trim().to_string(); // Remove trailing newline
|
user_input = user_input.trim().to_string(); // Remove trailing newline
|
||||||
|
|
||||||
self.history.push(user_input.clone()); // Save input to history
|
if !user_input.is_empty() && user_input.len() > 0 {
|
||||||
|
self.history.push(user_input.clone()); // Save input to history
|
||||||
if user_input.contains('[') && (!user_input.contains(']') && !is_loop) {
|
self.process(user_input); // Process the input
|
||||||
let loop_start_index = user_input.find('[').unwrap();
|
|
||||||
|
|
||||||
code_bat.push_str(&user_input[loop_start_index..]);
|
|
||||||
is_loop = true;
|
|
||||||
user_input = user_input[..loop_start_index].to_string();
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if user_input.starts_with(COMMAND_PREFIX) {
|
pub fn process(&mut self, mut user_input: String) {
|
||||||
self.run_repl_cmd(user_input, output)?;
|
match user_input.find('[') {
|
||||||
} else {
|
Some(index) if self.loop_depth == 0 => {
|
||||||
match self.interpreter.run(user_input) {
|
self.loop_body.push_str(&user_input[index..]);
|
||||||
Ok(_) => {
|
self.loop_depth = 1;
|
||||||
info!("Successfully ran brainfuck source code from REPL");
|
user_input = user_input[..index].to_string();
|
||||||
|
},
|
||||||
|
Some(_) => {
|
||||||
|
self.loop_body.push_str(&user_input);
|
||||||
|
user_input.matches('[').for_each(|_| self.loop_depth += 1);
|
||||||
|
user_input.matches(']').for_each(|_| self.loop_depth -= 1);
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
if user_input.contains(']') {
|
||||||
|
if self.loop_depth == 0 {
|
||||||
|
error!("Found ']' without matching '['");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
Err(e) => {
|
self.loop_depth -= 1;
|
||||||
error!("Failed to run brainfuck source code from REPL: {}", e);
|
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");
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!("Failed to run brainfuck source code from REPL: {}", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_repl_cmd(&mut self, user_input: String, output: &mut impl Write) -> Result<(), std::io::Error> {
|
fn run_repl_cmd(&mut self, user_input: String) {
|
||||||
let mut cmd = user_input.split_whitespace();
|
let mut cmd = user_input.split_whitespace();
|
||||||
match cmd.next() {
|
match cmd.next() {
|
||||||
Some(repl_cmd) => {
|
Some(repl_cmd) => {
|
||||||
match repl_cmd.get(COMMAND_PREFIX.len()..).unwrap_or("") {
|
match repl_cmd.get(COMMAND_PREFIX.len()..).unwrap_or("") {
|
||||||
"fuck" => {
|
"fuck" => {
|
||||||
output.write_all("Bye bye :D\n".green().as_bytes())?;
|
println!("{}", "Bye bye :D".green());
|
||||||
std::process::exit(0);
|
std::process::exit(0);
|
||||||
}
|
}
|
||||||
"array" | "a" => {
|
"array" | "a" => {
|
||||||
output.write_all(format!("Current array: {:?}\n", self.interpreter.cells).as_bytes())?;
|
println!("{}", format!("Current array: {:?}", self.interpreter.cells));
|
||||||
}
|
}
|
||||||
"array_size" | "as" => {
|
"array_size" | "as" => {
|
||||||
output.write_all(format!("Current array size: {}\n",
|
println!("{}", format!("Current array size: {}",
|
||||||
self.interpreter.array_size.to_string().bold().green()).as_bytes())?;
|
self.interpreter.cells.len()
|
||||||
|
.to_string().bold().green()));
|
||||||
}
|
}
|
||||||
"pointer" | "p" => {
|
"pointer" | "p" => {
|
||||||
output.write_all(format!("Current pointer: {}\n",
|
println!("{}", format!("Current pointer: {}",
|
||||||
self.interpreter.pointer.to_string().bold().green()).as_bytes())?;
|
self.interpreter.pointer.to_string().bold().green()));
|
||||||
}
|
}
|
||||||
"pointer_value" | "pv" => {
|
"pointer_value" | "pv" => {
|
||||||
format!(
|
println!(
|
||||||
"Current pointer value: {} = \'{}\' (char)\n",
|
"Current pointer value: {} = \'{}\' (char)",
|
||||||
self.interpreter.cells[self.interpreter.pointer],
|
self.interpreter.cells[self.interpreter.pointer],
|
||||||
self.interpreter.cells[self.interpreter.pointer] as char
|
self.interpreter.cells[self.interpreter.pointer] as char
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
"history" | "h" => {
|
"history" | "h" => {
|
||||||
output.write_all("History:\n".underline().green().as_bytes())?;
|
println!("{}", "History:".underline().green());
|
||||||
for (i, cmd) in self.history.iter().enumerate() {
|
for (i, cmd) in self.history.iter().enumerate() {
|
||||||
output.write_all(format!("{}: {}", i, cmd).as_bytes())?;
|
println!("{}", format!("{}: {}", i, cmd));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"save" | "s" => {
|
"save" | "s" => {
|
||||||
let file_name = cmd.next().unwrap_or(HISTORY_FILE);
|
let file_name = cmd.next().unwrap_or(HISTORY_FILE);
|
||||||
|
|
||||||
output.write_all(format!("Saving history to file: {file_name}").yellow().as_bytes())?;
|
println!("{}", format!("Saving history to file: {file_name}").yellow());
|
||||||
match std::fs::write(file_name, self.history.join("\n")) {
|
match std::fs::write(file_name, self.history.join("\n")) {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
output.write_all(format!("Successfully saved history to file: {file_name}")
|
println!("{}", format!("Successfully saved history to file: {file_name}")
|
||||||
.green().as_bytes())?;
|
.green());
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("Failed to save history to file: {}", e);
|
error!("Failed to save history to file: {}", e);
|
||||||
|
@ -119,11 +157,11 @@ impl Repl<'_> {
|
||||||
"load" | "l" => {
|
"load" | "l" => {
|
||||||
let file_name = cmd.next().unwrap_or(HISTORY_FILE);
|
let file_name = cmd.next().unwrap_or(HISTORY_FILE);
|
||||||
|
|
||||||
output.write_all(format!("Loading history from file: {file_name}\n").yellow().as_bytes())?;
|
println!("{}", format!("Loading history from file: {file_name}").yellow());
|
||||||
match std::fs::read_to_string(file_name) {
|
match std::fs::read_to_string(file_name) {
|
||||||
Ok(history) => {
|
Ok(history) => {
|
||||||
output.write_all(format!("Successfully loaded history from file: {file_name}")
|
println!("{}", format!("Successfully loaded history from file: {file_name}")
|
||||||
.green().as_bytes())?;
|
.green());
|
||||||
self.history = history.split("\n").map(|s| s.to_string()).collect();
|
self.history = history.split("\n").map(|s| s.to_string()).collect();
|
||||||
|
|
||||||
// Run all commands in history
|
// Run all commands in history
|
||||||
|
@ -149,12 +187,12 @@ impl Repl<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"reset" | "r" => {
|
"reset" | "r" => {
|
||||||
output.write_all("Resetting REPL\n".truecolor(56, 33, 102).as_bytes())?;
|
println!("{}", "Resetting REPL".truecolor(56, 33, 102));
|
||||||
self.interpreter.reset();
|
self.interpreter.reset();
|
||||||
self.history = Vec::new();
|
self.history = Vec::new();
|
||||||
}
|
}
|
||||||
"help" => {
|
"help" => {
|
||||||
output.write_all("!array, !a: print the current array\n\
|
println!("!array, !a: print the current array\n\
|
||||||
!array_size, !as: print the current array size\n\
|
!array_size, !as: print the current array size\n\
|
||||||
!pointer, !p: print the current pointer\n\
|
!pointer, !p: print the current pointer\n\
|
||||||
!pointer_value, !pv: print the current pointer value\n\
|
!pointer_value, !pv: print the current pointer value\n\
|
||||||
|
@ -163,43 +201,43 @@ impl Repl<'_> {
|
||||||
!load, !l: load the REPL history from a file\n\
|
!load, !l: load the REPL history from a file\n\
|
||||||
!reset, !r: reset the REPL\n\
|
!reset, !r: reset the REPL\n\
|
||||||
!help: print this help message\n\
|
!help: print this help message\n\
|
||||||
!fuck: exit the REPL\n".as_bytes())?;
|
!fuck: exit the REPL");
|
||||||
}
|
}
|
||||||
_ => output.write_all(format!("Unknown command: {}, type {} to show the help",
|
_ => println!("{}", format!("Unknown command: {}, type {} to show the help",
|
||||||
user_input, (COMMAND_PREFIX.to_string() + "help").green()
|
user_input, (COMMAND_PREFIX.to_string() + "help").green()
|
||||||
).red().as_bytes())?,
|
).red()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => {}
|
None => {}
|
||||||
}
|
}
|
||||||
Ok(())
|
}
|
||||||
|
|
||||||
|
/// Get the interpreter
|
||||||
|
/// for testing purposes only!
|
||||||
|
pub fn interpreter(&self) -> &Interpreter {
|
||||||
|
&self.interpreter
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Run the REPL
|
/// Run the REPL
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
/// * `interpreter` - The interpreter to use
|
/// * `interpreter` - The interpreter to use
|
||||||
pub fn start<'a>(interpreter: &'a mut Interpreter<'a>,
|
pub fn start(interpreter: Interpreter) {
|
||||||
input: &mut impl Read,
|
|
||||||
output: &mut impl Write) {
|
|
||||||
info!("Entering REPL mode");
|
info!("Entering REPL mode");
|
||||||
output.write_all(
|
println!("{}\n\
|
||||||
format!(
|
|
||||||
"{}\n\
|
|
||||||
Brainfuck interpreter v {}\nBy {}\n\
|
Brainfuck interpreter v {}\nBy {}\n\
|
||||||
{}\n\
|
{}\n\
|
||||||
Type {} to exit :D\n\
|
Type {} to exit :D\n\
|
||||||
type {} to get more fu*king help\n",
|
type {} to get more fu*king help",
|
||||||
"Welcome to the brainfuck REPL mode! :)".green(),
|
"Welcome to the brainfuck REPL mode! :)".green(),
|
||||||
clap::crate_version!().to_string().yellow(),
|
clap::crate_version!().to_string().yellow(),
|
||||||
clap::crate_authors!().to_string().green(),
|
clap::crate_authors!().to_string().green(),
|
||||||
"Enter your brainfuck code and press enter to run it.".italic().blue(),
|
"Enter your brainfuck code and press enter to run it.".italic().blue(),
|
||||||
(COMMAND_PREFIX.to_string() + "fuck").bold().red(),
|
(COMMAND_PREFIX.to_string() + "fuck").bold().red(),
|
||||||
(COMMAND_PREFIX.to_string() + "help").bold().green(),
|
(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() {
|
||||||
match Repl::new(interpreter).run(input, output) {
|
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
info!("Successfully ran REPL");
|
info!("Successfully ran REPL");
|
||||||
}
|
}
|
||||||
|
@ -213,19 +251,113 @@ pub fn start<'a>(interpreter: &'a mut Interpreter<'a>,
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use pretty_assertions::assert_eq;
|
||||||
|
|
||||||
/*#[test]
|
#[test]
|
||||||
fn nested_loop_level_1() {
|
fn nested_loop_level_1() {
|
||||||
let mut interpreter = Interpreter::new(
|
let interpreter = Interpreter::new(
|
||||||
30000,
|
4,
|
||||||
vec![],
|
vec![],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let mut repl = Repl::new(interpreter);
|
||||||
|
|
||||||
assert_eq!(interpreter.run(String::from("++")), Ok(0));
|
repl.process("++".to_string());
|
||||||
assert_eq!(interpreter.run(String::from("[>++")), Ok(0));
|
repl.process("[>++".to_string());
|
||||||
assert_eq!(interpreter.run(String::from("[>+<-]")), Ok(0));
|
repl.process("[>+<-]".to_string());
|
||||||
assert_eq!(interpreter.run(String::from("<-]")), Ok(0));
|
repl.process("<-]".to_string());
|
||||||
assert_eq!(interpreter.cells[2], 4);
|
|
||||||
}*/
|
let cells = &repl.interpreter().cells;
|
||||||
|
|
||||||
|
assert_eq!(cells[0], 0);
|
||||||
|
assert_eq!(cells[1], 0);
|
||||||
|
assert_eq!(cells[2], 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn nested_loop_level_2() {
|
||||||
|
let interpreter = Interpreter::new(
|
||||||
|
4,
|
||||||
|
vec![],
|
||||||
|
);
|
||||||
|
|
||||||
|
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], 0);
|
||||||
|
assert_eq!(cells[1], 0);
|
||||||
|
assert_eq!(cells[2], 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn print_my_first_name() {
|
||||||
|
let interpreter = Interpreter::new(
|
||||||
|
10,
|
||||||
|
vec![],
|
||||||
|
);
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Reference in a new issue