Fix interpreter errors yoo 🥰💙

This commit is contained in:
Anas Elgarhy 2022-10-10 22:55:50 +02:00
parent 3ee4f37354
commit 6dd7888aaf
18 changed files with 542 additions and 230 deletions

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerExplorerSettingsProvider">
<option name="initialNoticeShown" value="true" />
</component>
</project>

View file

@ -2,6 +2,6 @@
<project version="4">
<component name="DiscordProjectSettings">
<option name="show" value="PROJECT_FILES" />
<option name="description" value="" />
<option name="description" value="Fixing loop bug 🥲" />
</component>
</project>

View file

@ -37,6 +37,7 @@ exclude = [
clap = { version = "4.0.10", features = ["derive", "color", "cargo"] }
log = "0.4.17"
pretty_env_logger = "0.4.0"
colored = "2.0.0"
[dev-dependencies]
pretty_assertions = "1.3.0"

View file

@ -18,10 +18,11 @@ pub struct Args {
#[derive(Debug, PartialEq, Copy, Clone, ValueEnum)]
pub enum Feature {
/// If the value is you want decrement the value and the value is 0, set the value to 255, otherwise decrement the value.
/// If the value is you want increment the value and the value is 255, set the value to 0, otherwise increment the value.
ReverseValue,
/// If the value is you want decrement the value and the value is 0, don't set the value to 255, otherwise decrement the value.
/// If the value is you want increment the value and the value is 255, don't set the value to 0, otherwise increment the value.
NoReverseValue,
/// If the pointer at the end of the array, set the pointer to 0, otherwise increment the pointer.
/// If the pointer at the beginning of the array, set the pointer to the end of the array, otherwise decrement the pointer.
ReversePointer,
AllowUtf8,
}

View file

@ -36,9 +36,10 @@ impl std::error::Error for InterpreterError {
pub enum InterpreterErrorKind {
PointerOutOfBounds(usize), // takes pointer value
ValueOutOfBounds,
ByteReadError(std::io::Error),
ReadError,
UnmatchedClosingBracket(usize), // takes position
IoError(std::io::Error),
FlushError(std::io::Error),
UnmatchedBracket,
InvalidUtf8,
}
impl InterpreterErrorKind {
@ -50,9 +51,10 @@ impl InterpreterErrorKind {
match self {
InterpreterErrorKind::PointerOutOfBounds(_) => 11,
InterpreterErrorKind::ValueOutOfBounds => 12,
InterpreterErrorKind::ByteReadError(_) => 13,
InterpreterErrorKind::ReadError => 14,
InterpreterErrorKind::UnmatchedClosingBracket(_) => 15,
InterpreterErrorKind::IoError(_) => 13,
InterpreterErrorKind::FlushError(_) => 14,
InterpreterErrorKind::UnmatchedBracket => 15,
InterpreterErrorKind::InvalidUtf8 => 16,
}
}
}
@ -62,10 +64,11 @@ impl Display for InterpreterErrorKind {
match self {
InterpreterErrorKind::PointerOutOfBounds(pointer) => write!(f, "Pointer out of bounds {}", pointer),
InterpreterErrorKind::ValueOutOfBounds => write!(f, "Value out of bounds"),
InterpreterErrorKind::ByteReadError(error) =>
InterpreterErrorKind::IoError(error) =>
write!(f, "Failed to read byte from stdin: no bytes available: {}", error),
InterpreterErrorKind::ReadError => write!(f, "Failed to read byte from stdin: no bytes available"),
InterpreterErrorKind::UnmatchedClosingBracket(pos) => write!(f, "Unmatched closing bracket at position {}", pos),
InterpreterErrorKind::FlushError(e) => write!(f, "Failed to flush stdout: {}", e),
InterpreterErrorKind::UnmatchedBracket => write!(f, "Unmatched bracket"),
InterpreterErrorKind::InvalidUtf8 => write!(f, "Invalid utf8"),
}
}
}
@ -85,17 +88,21 @@ mod tests {
assert_eq!(error.to_string(), "Value out of bounds");
assert_eq!(error.code, 12);
let error = InterpreterErrorKind::ByteReadError(std::io::Error::new(std::io::ErrorKind::Other, "test")).to_error();
let error = InterpreterErrorKind::IoError(std::io::Error::new(std::io::ErrorKind::Other, "test")).to_error();
assert_eq!(error.to_string(), "Failed to read byte from stdin: no bytes available: test");
assert_eq!(error.code, 13);
let error = InterpreterErrorKind::ReadError.to_error();
/*let error = InterpreterErrorKind::FlushError(e).to_error();
assert_eq!(error.to_string(), "Failed to read byte from stdin: no bytes available");
assert_eq!(error.code, 14);
assert_eq!(error.code, 14);*/
let error = InterpreterErrorKind::UnmatchedClosingBracket(10).to_error();
assert_eq!(error.to_string(), "Unmatched closing bracket at position 10");
let error = InterpreterErrorKind::UnmatchedBracket.to_error();
assert_eq!(error.to_string(), "Unmatched bracket");
assert_eq!(error.code, 15);
let error = InterpreterErrorKind::InvalidUtf8.to_error();
assert_eq!(error.to_string(), "Invalid utf8");
assert_eq!(error.code, 16);
}
#[test]

View file

@ -1,4 +1,4 @@
use crate::{arguments, mode};
use crate::arguments;
use crate::bf_interpreter::error::{InterpreterError, InterpreterErrorKind};
use std::io::{Read, Write};
use std::{char, usize, vec};
@ -7,203 +7,204 @@ pub struct Interpreter {
pub cells: Vec<u8>,
pub pointer: usize,
pub array_size: usize,
pub bf_code: Vec<char>,
pub bf_commands: Vec<BfCommand>,
brackets: Vec<BfCommand>,
pub features: Vec<arguments::Feature>,
mode: mode::RunMode,
}
impl Interpreter {
pub fn new(
array_size: usize,
bf_code: Option<String>,
features: Vec<arguments::Feature>,
run_mode: mode::RunMode
) -> Self {
trace!("Run mode{run_mode:?}");
Self {
cells: vec![0; array_size],
pointer: 0,
array_size,
bf_code: bf_code.unwrap_or_else(|| String::new()).chars().collect(),
bf_commands: vec![],
brackets: Vec::new(),
features,
mode: run_mode,
}
}
pub fn run(&mut self, bf_code: Option<String>) -> Result<i32, InterpreterError> {
let bf_code = match bf_code {
Some(bf_code) => {
bf_code.chars().collect()
}
None => self.bf_code.clone(),
};
pub fn run(&mut self, bf_code: String) -> Result<i32, InterpreterError> {
self.bf_commands = to_bf_commands(bf_code.chars().collect())?;
match self.run_brainfuck_code(bf_code, false) {
match self.run_brainfuck_code(&self.bf_commands.clone()) {
Ok(_) => Ok(0),
Err(e) => Err(e),
}
}
// +[>++<-]
fn iterate(&mut self, code: Vec<char>) -> Result<(), InterpreterError> {
fn iterate(&mut self, code: &Vec<BfCommand>) -> Result<(), InterpreterError> {
trace!("Iterate: {:?}", code);
while self.cells[self.pointer] != 0 {
self.run_brainfuck_code(code.clone(), true)?;
self.run_brainfuck_code(code)?;
}
Ok(())
}
fn run_brainfuck_code(&mut self, bf_code: Vec<char>, from_loop: bool) -> Result<(), InterpreterError> {
let mut removed_num = 0_usize;
for (i, ch) in bf_code.iter().enumerate() {
match BfCommand::from_char(ch, i - removed_num) {
Some(cmd) => {
trace!("Executing command: {:?}", cmd);
self.execute(cmd)?;
fn run_brainfuck_code(&mut self, bf_code: &Vec<BfCommand>) -> Result<(), InterpreterError> {
for command in bf_code {
match command {
BfCommand::IncPtr => self.increment_pointer()?,
BfCommand::DecPtr => self.decrement_pointer()?,
BfCommand::IncVal => self.increment_value()?,
BfCommand::DecVal => self.decrement_value()?,
BfCommand::Print => self.output_value()?,
BfCommand::Read => self.input_value()?,
BfCommand::Loop(loop_body) => self.iterate(loop_body)?,
}
}
// Push the char to the bf_code vector if isn't from loop and we run in REPL mode
if !from_loop && self.mode == mode::RunMode::Repl {
self.bf_code.push(ch.clone());
}
}
None => {
trace!("Skipping character: \'{}\'", ch);
removed_num += 1;
}
Ok(())
}
fn increment_pointer(&mut self) -> Result<(), InterpreterError> {
trace!("Increment pointer");
self.pointer += 1;
if self.pointer >= self.array_size {
if self.features.contains(&arguments::Feature::ReversePointer) {
self.pointer = 0;
} else {
return Err(InterpreterErrorKind::PointerOutOfBounds(self.pointer).to_error());
}
}
Ok(())
}
fn execute(&mut self, cmd: BfCommand) -> Result<(), InterpreterError> {
match cmd {
BfCommand::IncPtr => {
self.pointer += 1;
if self.pointer >= self.array_size {
if self.features.contains(&arguments::Feature::ReversePointer) {
self.pointer = 0;
} else {
return Err(InterpreterErrorKind::PointerOutOfBounds(self.pointer).to_error())
}
}
}
BfCommand::DecPtr => {
if self.pointer == 0 {
if self.features.contains(&arguments::Feature::ReversePointer) {
self.pointer = self.array_size - 1;
} else {
return Err(InterpreterErrorKind::PointerOutOfBounds(self.pointer).to_error());
}
} else {
self.pointer -= 1;
}
}
BfCommand::IncVal => {
if self.cells[self.pointer] == 255 {
if self.features.contains(&arguments::Feature::ReverseValue) {
self.cells[self.pointer] = 0;
} else {
return Err(InterpreterErrorKind::ValueOutOfBounds.to_error());
}
} else {
self.cells[self.pointer] += 1;
}
}
BfCommand::DecVal => {
if self.cells[self.pointer] == 0 {
if self.features.contains(&arguments::Feature::ReverseValue) {
self.cells[self.pointer] = 255;
} else {
return Err(InterpreterErrorKind::ValueOutOfBounds.to_error());
}
} else {
self.cells[self.pointer] -= 1;
}
}
BfCommand::Print => {
print!("{}", self.cells[self.pointer] as char);
std::io::stdout().flush().unwrap();
}
BfCommand::Read => {
self.cells[self.pointer] = match std::io::stdin().bytes().next() {
Some(Ok(byte)) => byte,
Some(Err(e)) => {
return Err(InterpreterErrorKind::ByteReadError(e).to_error());
}
None => {
return Err(InterpreterErrorKind::ReadError.to_error());
}
};
}
BfCommand::LoopStart(i) => {
self.brackets.push(BfCommand::LoopStart(i));
}
BfCommand::LoopEnd(i) => {
let open_bracket = self.brackets.pop();
match open_bracket {
Some(BfCommand::LoopStart(j)) => {
if self.cells[self.pointer] != 0 {
let start = match &self.mode {
mode::RunMode::Repl if self.bf_code.len() - j >= i =>
self.bf_code.len() - j - i + 1,
_ => j + 1
};
debug!("bf_code array len: {}", self.bf_code.len());
debug!("start index {}", start);
debug!("bf_code at start: {}", self.bf_code[start]);
debug!("i: {i}, j: {j}");
// debug!("{}", self.bf_code[i]);
let end = match &self.mode {
mode::RunMode::Repl => {
let mut s = i + start - 2;
if s >= self.bf_code.len() {
s = s - (self.bf_code.len() - start) + 1;
}
s
},
mode::RunMode::Execute => i - 1,
};
let range = start..=end;
debug!("{range:?}");
let code = self.bf_code[range].to_vec();
self.iterate(code)?;
}
}
_ => {
return Err(InterpreterErrorKind::UnmatchedClosingBracket(i).to_error());
}
}
fn decrement_pointer(&mut self) -> Result<(), InterpreterError> {
trace!("Decrement pointer");
if self.pointer == 0 {
if self.features.contains(&arguments::Feature::ReversePointer) {
self.pointer = self.array_size - 1;
} else {
return Err(InterpreterErrorKind::PointerOutOfBounds(self.pointer).to_error());
}
} else {
self.pointer -= 1;
}
Ok(())
}
fn increment_value(&mut self) -> Result<(), InterpreterError> {
trace!("Increment value");
if self.cells[self.pointer] == 255 {
if !self.features.contains(&arguments::Feature::NoReverseValue) {
self.cells[self.pointer] = 0;
} else {
return Err(InterpreterErrorKind::ValueOutOfBounds.to_error());
}
} else {
self.cells[self.pointer] += 1;
}
Ok(())
}
fn decrement_value(&mut self) -> Result<(), InterpreterError> {
trace!("Decrement value");
if self.cells[self.pointer] == 0 {
if !self.features.contains(&arguments::Feature::NoReverseValue) {
self.cells[self.pointer] = 255;
} else {
return Err(InterpreterErrorKind::ValueOutOfBounds.to_error());
}
} else {
self.cells[self.pointer] -= 1;
}
Ok(())
}
fn output_value(&mut self) -> Result<(), InterpreterError> {
trace!("Output value");
if self.features.contains(&arguments::Feature::AllowUtf8) {
let c = char::from_u32(self.cells[self.pointer] as u32);
match c {
Some(c) => print!("{}", c),
None => return Err(InterpreterErrorKind::InvalidUtf8.to_error()),
}
} else {
print!("{}", self.cells[self.pointer] as char);
}
match std::io::stdout().flush() {
Ok(_) => Ok(()),
Err(e) => Err(InterpreterErrorKind::FlushError(e).to_error()),
}
}
fn input_value(&mut self) -> Result<(), InterpreterError> {
trace!("Input value");
let mut input = [0; 1];
match std::io::stdin().read_exact(&mut input) {
Ok(_) => {
self.cells[self.pointer] = input[0];
Ok(())
}
Err(e) => Err(InterpreterErrorKind::IoError(e).to_error()),
}
}
pub fn reset(&mut self) {
self.cells = vec![0; self.array_size];
self.pointer = 0;
self.brackets = Vec::new();
self.bf_code = Vec::new();
self.bf_commands = Vec::new();
}
}
#[derive(Debug, PartialEq)]
enum BfCommand {
#[derive(Debug, PartialEq, Clone)]
pub enum BfCommand {
IncPtr,
DecPtr,
IncVal,
DecVal,
Print,
Read,
LoopStart(usize),
LoopEnd(usize),
Loop(Vec<BfCommand>),
}
fn to_bf_commands(bf_code: Vec<char>) -> Result<Vec<BfCommand>, InterpreterError> {
let mut bf_commands = Vec::new();
let mut i = 0;
while i < bf_code.len() {
match bf_code[i] {
'[' => {
let mut bracket_count = 1;
let mut j = i + 1;
while j < bf_code.len() {
match bf_code[j] {
'[' => bracket_count += 1,
']' => bracket_count -= 1,
_ => (),
}
if bracket_count == 0 {
break;
}
j += 1;
}
if bracket_count != 0 {
return Err(InterpreterErrorKind::UnmatchedBracket.to_error());
}
bf_commands.push(BfCommand::Loop(to_bf_commands(bf_code[i + 1..j].to_vec())?));
i = j;
}
_ => {
match BfCommand::from(bf_code[i]) {
Some(command) => bf_commands.push(command),
None => (),
}
},
}
i += 1;
}
Ok(bf_commands)
}
impl BfCommand {
fn from_char(c: &char, index: usize) -> Option<BfCommand> {
fn from(c: char) -> Option<Self> {
match c {
'>' => Some(BfCommand::IncPtr),
'<' => Some(BfCommand::DecPtr),
@ -211,8 +212,6 @@ impl BfCommand {
'-' => Some(BfCommand::DecVal),
'.' => Some(BfCommand::Print),
',' => Some(BfCommand::Read),
'[' => Some(BfCommand::LoopStart(index)),
']' => Some(BfCommand::LoopEnd(index)),
_ => None,
}
}
@ -221,91 +220,162 @@ impl BfCommand {
#[cfg(test)]
mod tests {
use super::*;
use crate::mode::RunMode;
use pretty_assertions::assert_eq;
use crate::utils; // for testing only
use pretty_assertions::assert_eq; // for testing only
use crate::utils;
#[test]
fn print_h_combine_repl() {
let mut interpreter = Interpreter::new(
30000,
None,
vec![],
RunMode::Repl
);
assert_eq!(interpreter.run(None), Ok(0));
assert_eq!(interpreter.run(String::from(">+++++++++[<++++ ++++>-]<.")), Ok(0));
assert_eq!(interpreter.run(Some(String::from(">+++++++++[<++++ ++++>-]<."))), Ok(0));
println!();
}
#[test]
fn print_h_repl() {
let mut interpreter = Interpreter::new(
30000,
None,
vec![],
RunMode::Repl
);
assert_eq!(interpreter.run(None), Ok(0));
assert_eq!(interpreter.run(String::from(">+++++++++")), Ok(0));
assert_eq!(interpreter.run(String::from("[<++++ ++++>-]<.")), Ok(0));
assert_eq!(interpreter.run(Some(String::from(">+++++++++"))), Ok(0));
assert_eq!(interpreter.run(Some(String::from("[<++++ ++++>-]<."))), Ok(0));
println!();
}
#[test]
fn nested_loop_level_1_combine() {
let mut interpreter = Interpreter::new(
5,
vec![],
);
assert_eq!(interpreter.run(String::from("++[>++[>+<-]<-]")), Ok(0));
assert_eq!(interpreter.cells[2], 4);
println!();
}
#[test]
fn execute_hello_world_from_file() {
let mut interpreter = Interpreter::new(
30000,
utils::read_brainfuck_code_if_any(&Some(String::from("test_code/hello_world.bf"))),
vec![],
RunMode::Execute
);
assert_eq!(interpreter.run(None), Ok(0));
println!();
assert_eq!(interpreter.run(
utils::read_brainfuck_code(
&String::from("test_code/hello_world.bf"))), Ok(0));
}
#[test]
fn execute_print_hi_from_file() {
let mut interpreter = Interpreter::new(
30000,
utils::read_brainfuck_code_if_any(&Some(String::from("test_code/print_hi.bf"))),
vec![],
RunMode::Execute
);
assert_eq!(interpreter.run(None), Ok(0));
println!();
assert_eq!(interpreter.run(
utils::read_brainfuck_code(&String::from("test_code/print_hi.bf"))), Ok(0));
}
#[test]
fn execute_print_hi_yooo_from_file() {
let mut interpreter = Interpreter::new(
30000,
utils::read_brainfuck_code_if_any(&Some(String::from("test_code/print_hi_yooo.bf"))),
vec![],
RunMode::Execute
);
assert_eq!(interpreter.run(None), Ok(0));
println!();
assert_eq!(interpreter.run(
utils::read_brainfuck_code(&String::from("test_code/print_hi_yooo.bf"))),
Ok(0));
}
#[test]
fn execute_print_my_first_name_from_formatted_file() {
let mut interpreter = Interpreter::new(
30000,
vec![],
);
println!();
assert_eq!(interpreter.run(
utils::read_brainfuck_code(&String::from("test_code/print_my_first_name_formatted.bf"))),
Ok(0));
}
#[test]
fn execute_print_my_first_name_from_file() {
let mut interpreter = Interpreter::new(
30000,
vec![],
);
println!();
assert_eq!(interpreter.run(
utils::read_brainfuck_code(&String::from("test_code/print_my_first_name.bf"))),
Ok(0));
}
#[test]
fn execute_print_my_first_name_and_last_name_from_formatted_file() {
let mut interpreter = Interpreter::new(
30000,
vec![],
);
println!();
assert_eq!(interpreter.run(
utils::read_brainfuck_code(
&String::from("test_code/print_my_first_name_and_last_name_formatted.bf"))),
Ok(0));
}
#[test]
fn execute_print_my_first_name_and_last_name_from_file() {
let mut interpreter = Interpreter::new(
30000,
vec![],
);
println!();
assert_eq!(interpreter.run(utils::read_brainfuck_code(
&String::from("test_code/print_my_first_name_and_last_name.bf"))),
Ok(0));
}
#[test]
fn reset() {
let mut interpreter = Interpreter::new(
30000,
None,
vec![],
RunMode::Repl
);
assert_eq!(interpreter.run(None), Ok(0));
assert_eq!(interpreter.run(Some(String::from(">++++"))), Ok(0));
assert_eq!(interpreter.run(String::from(">++++")), Ok(0));
assert_eq!(interpreter.pointer, 1);
assert_eq!(interpreter.cells[0], 0);
assert_eq!(interpreter.cells[1], 4);
assert_eq!(interpreter.bf_code, vec!['>', '+', '+', '+' , '+']);
// assert_eq!(interpreter.commands, vec!['>', '+', '+', '+', '+']);
// reset
interpreter.reset();
@ -313,21 +383,6 @@ mod tests {
assert_eq!(interpreter.pointer, 0);
assert_eq!(interpreter.cells[0], 0);
assert_eq!(interpreter.cells[1], 0);
assert_eq!(interpreter.bf_code, Vec::<char>::new());
assert_eq!(interpreter.run(None), Ok(0));
}
#[test]
fn test_from_char() {
assert_eq!(BfCommand::from_char(&'>', 0), Some(BfCommand::IncPtr));
assert_eq!(BfCommand::from_char(&'<', 0), Some(BfCommand::DecPtr));
assert_eq!(BfCommand::from_char(&'+', 0), Some(BfCommand::IncVal));
assert_eq!(BfCommand::from_char(&'-', 0), Some(BfCommand::DecVal));
assert_eq!(BfCommand::from_char(&'.', 0), Some(BfCommand::Print));
assert_eq!(BfCommand::from_char(&',', 0), Some(BfCommand::Read));
assert_eq!(BfCommand::from_char(&'[', 0), Some(BfCommand::LoopStart(0)));
assert_eq!(BfCommand::from_char(&']', 0), Some(BfCommand::LoopEnd(0)));
assert_eq!(BfCommand::from_char(&' ', 0), None);
assert_eq!(interpreter.bf_commands, Vec::<BfCommand>::new());
}
}

View file

@ -2,7 +2,6 @@ mod arguments;
mod repl;
mod utils;
mod bf_interpreter;
mod mode;
use clap::Parser;
extern crate pretty_env_logger;
@ -22,18 +21,13 @@ fn main() {
info!("Initializing bf_interpreter");
let mut interpreter = Interpreter::new(
args.array_size,
utils::read_brainfuck_code_if_any(&args.source),
args.features.unwrap_or_else(|| vec![]),
match args.source {
Some(_) => mode::RunMode::Execute,
None => mode::RunMode::Repl
},
);
match args.source {
Some(source) => {
info!("Running brainfuck source code from file: {}", source);
match interpreter.run(None) {
match interpreter.run(utils::read_brainfuck_code(&source)) {
Ok(exit_code) => {
info!("Finished running brainfuck source code from file: {}", source);
if !args.without_tiles {

View file

@ -1,5 +0,0 @@
#[derive(PartialEq, Debug)]
pub enum RunMode {
Execute,
Repl,
}

View file

@ -19,8 +19,15 @@ impl Repl {
}
pub fn run(mut self) {
let mut code_bat = String::new();
let mut is_loop = false;
loop {
print!("\n {}", PROMPT);
if is_loop {
print!("... ");
} else {
print!("{}", PROMPT);
}
std::io::stdout().flush().unwrap_or_else(|_| {
error!("Failed to flush stdout");
std::process::exit(1);
@ -38,10 +45,18 @@ impl Repl {
self.history.push(input.clone()); // Save input to history
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();
}
if input.starts_with(COMMAND_PREFIX) {
self.run_repl_cmd(input);
} else {
match self.interpreter.run(Some(input)) {
match self.interpreter.run(input) {
Ok(_) => {
info!("Successfully ran brainfuck source code from REPL");
}
@ -108,7 +123,7 @@ impl Repl {
// Run all commands in history
for cmd in self.history.iter() {
match self.interpreter.run(Some(cmd.clone())) {
match self.interpreter.run(cmd.clone()) {
Ok(_) => {
info!(
"Successfully ran brainfuck source code from REPL"
@ -169,3 +184,25 @@ pub fn start(interpreter: Interpreter) {
Repl::new(interpreter).run();
}
#[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!();
}
}

View file

@ -1,17 +1,12 @@
pub(crate) fn read_brainfuck_code_if_any(source: &Option<String>) -> Option<String> {
match source {
Some(source) => {
info!("Reading brainfuck source code from file: {}", source);
match std::fs::read_to_string(source) {
Ok(source) => Some(clean(source)),
Err(e) => {
error!("Failed to read source code file: {}", e);
eprintln!("Failed to read source code file: {}", e);
std::process::exit(1);
}
}
pub fn read_brainfuck_code(source: &String) -> String {
info!("Reading brainfuck source code from file: {}", source);
match std::fs::read_to_string(source) {
Ok(source) => clean(source),
Err(e) => {
error!("Failed to read source code file: {}", e);
eprintln!("Failed to read source code file: {}", e);
std::process::exit(1);
}
None => None,
}
}

View file

@ -0,0 +1,20 @@
++ Cell c0 = 2
> +++++ Cell c1 = 5
[ Start your loops with your cell pointer on the loop counter (c1 in our case)
< + Add 1 to c0
> - Subtract 1 from c1
] End your loops with the cell pointer on the loop counter
At this point our program has added 5 to 2 leaving 7 in c0 and 0 in c1
but we cannot output this value to the terminal since it is not ASCII encoded.
To display the ASCII character "7" we must add 48 to the value 7.
We use a loop to compute 48 = 6 * 8.
++++ ++++ c1 = 8 and this will be our loop counter again
[
< +++ +++ Add 6 to c0
> - Subtract 1 from c1
]
< . Print out c0 which has the value 55 which translates to "7"!

View file

@ -0,0 +1 @@
++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.

View file

@ -0,0 +1,43 @@
[ This program prints "Hello World!" and a newline to the screen, its
length is 106 active command characters. [It is not the shortest.]
This loop is an "initial comment loop", a simple way of adding a comment
to a BF program such that you don't have to worry about any command
characters. Any ".", ",", "+", "-", "<" and ">" characters are simply
ignored, the "[" and "]" characters just have to be balanced. This
loop and the commands it contains are ignored because the current cell
defaults to a value of 0; the 0 value causes this loop to be skipped.
]
++++++++ 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

View file

@ -0,0 +1,3 @@
Print my first name (Anas)
Unformated:
++++++++[>++++[>++<-]>>>>>>++[<<<->>>-]<<<<<<<-]>>+.<<++++[>+++[>+++<-]>++<<-]>>+.<<+++[>+++[>-<-]>-<<-]>>-.<<++++++[>>+++<<-]>>.

View file

@ -0,0 +1,3 @@
Print my first name and my last name (Anas Elgarhy)
Unformated:
++++++++[>++++[>++>+>++>+++<<<<-]>>>>+<<<<<-]>>+.<<++++[>+++[>+++<-]>++<<-]>>+.<<+++[>+++[>-<-]>-<<-]>>-.<<++++++[>>+++<<-]>>.>.>+++++.<<-------.-----.------.<+++++[>+++<-]>++.>>>.<<<+++++++.

View file

@ -0,0 +1,56 @@
++++ ++++ 8
[
>++++
[
>++ A
>+ space
>++
>+++
<<<<-
]
>>>>+
<<<<<-
]
>>+. Print cell 2: A
<<++++
[
>+++
[
>+++
<-
]
>++
<<-
]
>>+. Print n
<<+++
[
>+++
[
>-
<-
]
>-
<<-
]
>>-. Print a
<<++++++
[
>>+++
<<-
]
>>. Print s
>. Print spce from the cell 3
>+++++. Print E from cell 4
<<-------. Print l
-----. Print g
------. Print a
<+++++
[
>+++
<-
]
>++. Print r
>>>. Print h
<<<+++++++. Print y

View file

@ -0,0 +1,45 @@
My first name and my last name (Anas Elgarhy)
Formated:
++++ ++++ 8
[
>++++
[
>++ A
>+ space
>++
>+++
<<<<-
]
>>>>+
<<<<<-
]
>>+. Print cell 2: A
<<++++
[
>+++
[
>+++
<-
]
>++
<<-
]
>>+. Print n
<<+++
[
>+++
[
>-
<-
]
>-
<<-
]
>>-. Print a
<<++++++
[
>>+++
<<-
]
>>. Print s

View file

@ -0,0 +1,50 @@
My first name (Anas)
Formated:
++++ ++++ 8
[
>++++
[
>++ A
>+++ a
>++++
>+ space
<<<<-
]
>>>>>>++
[
<<<-
>>>-
]
<<<<<<<-
]
>>+. Print cell 2: A
<<++++
[
>+++
[
>+++
<-
]
>++
<<-
]
>>+. Print n
<<+++
[
>+++
[
>-
<-
]
>-
<<-
]
>>-. Print n
<<++++++
[
>>+++
<<-
]
>>. Print s