Fix interpreter errors yoo 🥰💙
This commit is contained in:
parent
3ee4f37354
commit
6dd7888aaf
18 changed files with 542 additions and 230 deletions
6
.idea/compilerexplorer.settings.xml
Normal file
6
.idea/compilerexplorer.settings.xml
Normal 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>
|
|
@ -2,6 +2,6 @@
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="DiscordProjectSettings">
|
<component name="DiscordProjectSettings">
|
||||||
<option name="show" value="PROJECT_FILES" />
|
<option name="show" value="PROJECT_FILES" />
|
||||||
<option name="description" value="" />
|
<option name="description" value="Fixing loop bug 🥲" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
|
@ -37,6 +37,7 @@ exclude = [
|
||||||
clap = { version = "4.0.10", features = ["derive", "color", "cargo"] }
|
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"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
pretty_assertions = "1.3.0"
|
pretty_assertions = "1.3.0"
|
|
@ -18,10 +18,11 @@ pub struct Args {
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Copy, Clone, ValueEnum)]
|
#[derive(Debug, PartialEq, Copy, Clone, ValueEnum)]
|
||||||
pub enum Feature {
|
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 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, set the value to 0, otherwise increment 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.
|
||||||
ReverseValue,
|
NoReverseValue,
|
||||||
/// If the pointer at the end of the array, set the pointer to 0, otherwise increment the pointer.
|
/// 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.
|
/// If the pointer at the beginning of the array, set the pointer to the end of the array, otherwise decrement the pointer.
|
||||||
ReversePointer,
|
ReversePointer,
|
||||||
|
AllowUtf8,
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,9 +36,10 @@ impl std::error::Error for InterpreterError {
|
||||||
pub enum InterpreterErrorKind {
|
pub enum InterpreterErrorKind {
|
||||||
PointerOutOfBounds(usize), // takes pointer value
|
PointerOutOfBounds(usize), // takes pointer value
|
||||||
ValueOutOfBounds,
|
ValueOutOfBounds,
|
||||||
ByteReadError(std::io::Error),
|
IoError(std::io::Error),
|
||||||
ReadError,
|
FlushError(std::io::Error),
|
||||||
UnmatchedClosingBracket(usize), // takes position
|
UnmatchedBracket,
|
||||||
|
InvalidUtf8,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InterpreterErrorKind {
|
impl InterpreterErrorKind {
|
||||||
|
@ -50,9 +51,10 @@ impl InterpreterErrorKind {
|
||||||
match self {
|
match self {
|
||||||
InterpreterErrorKind::PointerOutOfBounds(_) => 11,
|
InterpreterErrorKind::PointerOutOfBounds(_) => 11,
|
||||||
InterpreterErrorKind::ValueOutOfBounds => 12,
|
InterpreterErrorKind::ValueOutOfBounds => 12,
|
||||||
InterpreterErrorKind::ByteReadError(_) => 13,
|
InterpreterErrorKind::IoError(_) => 13,
|
||||||
InterpreterErrorKind::ReadError => 14,
|
InterpreterErrorKind::FlushError(_) => 14,
|
||||||
InterpreterErrorKind::UnmatchedClosingBracket(_) => 15,
|
InterpreterErrorKind::UnmatchedBracket => 15,
|
||||||
|
InterpreterErrorKind::InvalidUtf8 => 16,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -62,10 +64,11 @@ impl Display for InterpreterErrorKind {
|
||||||
match self {
|
match self {
|
||||||
InterpreterErrorKind::PointerOutOfBounds(pointer) => write!(f, "Pointer out of bounds {}", pointer),
|
InterpreterErrorKind::PointerOutOfBounds(pointer) => write!(f, "Pointer out of bounds {}", pointer),
|
||||||
InterpreterErrorKind::ValueOutOfBounds => write!(f, "Value out of bounds"),
|
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),
|
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::FlushError(e) => write!(f, "Failed to flush stdout: {}", e),
|
||||||
InterpreterErrorKind::UnmatchedClosingBracket(pos) => write!(f, "Unmatched closing bracket at position {}", pos),
|
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.to_string(), "Value out of bounds");
|
||||||
assert_eq!(error.code, 12);
|
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.to_string(), "Failed to read byte from stdin: no bytes available: test");
|
||||||
assert_eq!(error.code, 13);
|
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.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();
|
let error = InterpreterErrorKind::UnmatchedBracket.to_error();
|
||||||
assert_eq!(error.to_string(), "Unmatched closing bracket at position 10");
|
assert_eq!(error.to_string(), "Unmatched bracket");
|
||||||
assert_eq!(error.code, 15);
|
assert_eq!(error.code, 15);
|
||||||
|
|
||||||
|
let error = InterpreterErrorKind::InvalidUtf8.to_error();
|
||||||
|
assert_eq!(error.to_string(), "Invalid utf8");
|
||||||
|
assert_eq!(error.code, 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::{arguments, mode};
|
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::{Read, Write};
|
||||||
use std::{char, usize, vec};
|
use std::{char, usize, vec};
|
||||||
|
@ -7,89 +7,76 @@ pub struct Interpreter {
|
||||||
pub cells: Vec<u8>,
|
pub cells: Vec<u8>,
|
||||||
pub pointer: usize,
|
pub pointer: usize,
|
||||||
pub array_size: usize,
|
pub array_size: usize,
|
||||||
pub bf_code: Vec<char>,
|
pub bf_commands: Vec<BfCommand>,
|
||||||
brackets: Vec<BfCommand>,
|
brackets: Vec<BfCommand>,
|
||||||
pub features: Vec<arguments::Feature>,
|
pub features: Vec<arguments::Feature>,
|
||||||
mode: mode::RunMode,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Interpreter {
|
impl Interpreter {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
array_size: usize,
|
array_size: usize,
|
||||||
bf_code: Option<String>,
|
|
||||||
features: Vec<arguments::Feature>,
|
features: Vec<arguments::Feature>,
|
||||||
run_mode: mode::RunMode
|
|
||||||
) -> Self {
|
) -> Self {
|
||||||
trace!("Run mode{run_mode:?}");
|
|
||||||
Self {
|
Self {
|
||||||
cells: vec![0; array_size],
|
cells: vec![0; array_size],
|
||||||
pointer: 0,
|
pointer: 0,
|
||||||
array_size,
|
array_size,
|
||||||
bf_code: bf_code.unwrap_or_else(|| String::new()).chars().collect(),
|
bf_commands: vec![],
|
||||||
brackets: Vec::new(),
|
brackets: Vec::new(),
|
||||||
features,
|
features,
|
||||||
mode: run_mode,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(&mut self, bf_code: Option<String>) -> Result<i32, InterpreterError> {
|
pub fn run(&mut self, bf_code: String) -> Result<i32, InterpreterError> {
|
||||||
let bf_code = match bf_code {
|
self.bf_commands = to_bf_commands(bf_code.chars().collect())?;
|
||||||
Some(bf_code) => {
|
|
||||||
bf_code.chars().collect()
|
|
||||||
}
|
|
||||||
None => self.bf_code.clone(),
|
|
||||||
};
|
|
||||||
|
|
||||||
match self.run_brainfuck_code(bf_code, false) {
|
match self.run_brainfuck_code(&self.bf_commands.clone()) {
|
||||||
Ok(_) => Ok(0),
|
Ok(_) => Ok(0),
|
||||||
Err(e) => Err(e),
|
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);
|
trace!("Iterate: {:?}", code);
|
||||||
while self.cells[self.pointer] != 0 {
|
while self.cells[self.pointer] != 0 {
|
||||||
self.run_brainfuck_code(code.clone(), true)?;
|
self.run_brainfuck_code(code)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_brainfuck_code(&mut self, bf_code: Vec<char>, from_loop: bool) -> Result<(), InterpreterError> {
|
fn run_brainfuck_code(&mut self, bf_code: &Vec<BfCommand>) -> Result<(), InterpreterError> {
|
||||||
let mut removed_num = 0_usize;
|
for command in bf_code {
|
||||||
for (i, ch) in bf_code.iter().enumerate() {
|
match command {
|
||||||
match BfCommand::from_char(ch, i - removed_num) {
|
BfCommand::IncPtr => self.increment_pointer()?,
|
||||||
Some(cmd) => {
|
BfCommand::DecPtr => self.decrement_pointer()?,
|
||||||
trace!("Executing command: {:?}", cmd);
|
BfCommand::IncVal => self.increment_value()?,
|
||||||
self.execute(cmd)?;
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute(&mut self, cmd: BfCommand) -> Result<(), InterpreterError> {
|
fn increment_pointer(&mut self) -> Result<(), InterpreterError> {
|
||||||
match cmd {
|
trace!("Increment pointer");
|
||||||
BfCommand::IncPtr => {
|
|
||||||
self.pointer += 1;
|
self.pointer += 1;
|
||||||
if self.pointer >= self.array_size {
|
if self.pointer >= self.array_size {
|
||||||
if self.features.contains(&arguments::Feature::ReversePointer) {
|
if self.features.contains(&arguments::Feature::ReversePointer) {
|
||||||
self.pointer = 0;
|
self.pointer = 0;
|
||||||
} else {
|
} else {
|
||||||
return Err(InterpreterErrorKind::PointerOutOfBounds(self.pointer).to_error())
|
return Err(InterpreterErrorKind::PointerOutOfBounds(self.pointer).to_error());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
BfCommand::DecPtr => {
|
|
||||||
|
fn decrement_pointer(&mut self) -> Result<(), InterpreterError> {
|
||||||
|
trace!("Decrement pointer");
|
||||||
if self.pointer == 0 {
|
if self.pointer == 0 {
|
||||||
if self.features.contains(&arguments::Feature::ReversePointer) {
|
if self.features.contains(&arguments::Feature::ReversePointer) {
|
||||||
self.pointer = self.array_size - 1;
|
self.pointer = self.array_size - 1;
|
||||||
|
@ -99,10 +86,13 @@ impl Interpreter {
|
||||||
} else {
|
} else {
|
||||||
self.pointer -= 1;
|
self.pointer -= 1;
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
BfCommand::IncVal => {
|
|
||||||
|
fn increment_value(&mut self) -> Result<(), InterpreterError> {
|
||||||
|
trace!("Increment value");
|
||||||
if self.cells[self.pointer] == 255 {
|
if self.cells[self.pointer] == 255 {
|
||||||
if self.features.contains(&arguments::Feature::ReverseValue) {
|
if !self.features.contains(&arguments::Feature::NoReverseValue) {
|
||||||
self.cells[self.pointer] = 0;
|
self.cells[self.pointer] = 0;
|
||||||
} else {
|
} else {
|
||||||
return Err(InterpreterErrorKind::ValueOutOfBounds.to_error());
|
return Err(InterpreterErrorKind::ValueOutOfBounds.to_error());
|
||||||
|
@ -110,10 +100,13 @@ impl Interpreter {
|
||||||
} else {
|
} else {
|
||||||
self.cells[self.pointer] += 1;
|
self.cells[self.pointer] += 1;
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
BfCommand::DecVal => {
|
|
||||||
|
fn decrement_value(&mut self) -> Result<(), InterpreterError> {
|
||||||
|
trace!("Decrement value");
|
||||||
if self.cells[self.pointer] == 0 {
|
if self.cells[self.pointer] == 0 {
|
||||||
if self.features.contains(&arguments::Feature::ReverseValue) {
|
if !self.features.contains(&arguments::Feature::NoReverseValue) {
|
||||||
self.cells[self.pointer] = 255;
|
self.cells[self.pointer] = 255;
|
||||||
} else {
|
} else {
|
||||||
return Err(InterpreterErrorKind::ValueOutOfBounds.to_error());
|
return Err(InterpreterErrorKind::ValueOutOfBounds.to_error());
|
||||||
|
@ -121,89 +114,97 @@ impl Interpreter {
|
||||||
} else {
|
} else {
|
||||||
self.cells[self.pointer] -= 1;
|
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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
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) {
|
pub fn reset(&mut self) {
|
||||||
self.cells = vec![0; self.array_size];
|
self.cells = vec![0; self.array_size];
|
||||||
self.pointer = 0;
|
self.pointer = 0;
|
||||||
self.brackets = Vec::new();
|
self.brackets = Vec::new();
|
||||||
self.bf_code = Vec::new();
|
self.bf_commands = Vec::new();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
enum BfCommand {
|
pub enum BfCommand {
|
||||||
IncPtr,
|
IncPtr,
|
||||||
DecPtr,
|
DecPtr,
|
||||||
IncVal,
|
IncVal,
|
||||||
DecVal,
|
DecVal,
|
||||||
Print,
|
Print,
|
||||||
Read,
|
Read,
|
||||||
LoopStart(usize),
|
Loop(Vec<BfCommand>),
|
||||||
LoopEnd(usize),
|
}
|
||||||
|
|
||||||
|
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 {
|
impl BfCommand {
|
||||||
fn from_char(c: &char, index: usize) -> Option<BfCommand> {
|
fn from(c: char) -> Option<Self> {
|
||||||
match c {
|
match c {
|
||||||
'>' => Some(BfCommand::IncPtr),
|
'>' => Some(BfCommand::IncPtr),
|
||||||
'<' => Some(BfCommand::DecPtr),
|
'<' => Some(BfCommand::DecPtr),
|
||||||
|
@ -211,8 +212,6 @@ impl BfCommand {
|
||||||
'-' => Some(BfCommand::DecVal),
|
'-' => Some(BfCommand::DecVal),
|
||||||
'.' => Some(BfCommand::Print),
|
'.' => Some(BfCommand::Print),
|
||||||
',' => Some(BfCommand::Read),
|
',' => Some(BfCommand::Read),
|
||||||
'[' => Some(BfCommand::LoopStart(index)),
|
|
||||||
']' => Some(BfCommand::LoopEnd(index)),
|
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -221,91 +220,162 @@ impl BfCommand {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::mode::RunMode;
|
use pretty_assertions::assert_eq; // for testing only
|
||||||
use pretty_assertions::assert_eq;
|
use crate::utils;
|
||||||
use crate::utils; // for testing only
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn print_h_combine_repl() {
|
fn print_h_combine_repl() {
|
||||||
let mut interpreter = Interpreter::new(
|
let mut interpreter = Interpreter::new(
|
||||||
30000,
|
30000,
|
||||||
None,
|
|
||||||
vec![],
|
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]
|
#[test]
|
||||||
fn print_h_repl() {
|
fn print_h_repl() {
|
||||||
let mut interpreter = Interpreter::new(
|
let mut interpreter = Interpreter::new(
|
||||||
30000,
|
30000,
|
||||||
None,
|
|
||||||
vec![],
|
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));
|
println!();
|
||||||
assert_eq!(interpreter.run(Some(String::from("[<++++ ++++>-]<."))), Ok(0));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[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]
|
#[test]
|
||||||
fn execute_hello_world_from_file() {
|
fn execute_hello_world_from_file() {
|
||||||
let mut interpreter = Interpreter::new(
|
let mut interpreter = Interpreter::new(
|
||||||
30000,
|
30000,
|
||||||
utils::read_brainfuck_code_if_any(&Some(String::from("test_code/hello_world.bf"))),
|
|
||||||
vec![],
|
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]
|
#[test]
|
||||||
fn execute_print_hi_from_file() {
|
fn execute_print_hi_from_file() {
|
||||||
let mut interpreter = Interpreter::new(
|
let mut interpreter = Interpreter::new(
|
||||||
30000,
|
30000,
|
||||||
utils::read_brainfuck_code_if_any(&Some(String::from("test_code/print_hi.bf"))),
|
|
||||||
vec![],
|
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]
|
#[test]
|
||||||
fn execute_print_hi_yooo_from_file() {
|
fn execute_print_hi_yooo_from_file() {
|
||||||
let mut interpreter = Interpreter::new(
|
let mut interpreter = Interpreter::new(
|
||||||
30000,
|
30000,
|
||||||
utils::read_brainfuck_code_if_any(&Some(String::from("test_code/print_hi_yooo.bf"))),
|
|
||||||
vec![],
|
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]
|
#[test]
|
||||||
fn reset() {
|
fn reset() {
|
||||||
let mut interpreter = Interpreter::new(
|
let mut interpreter = Interpreter::new(
|
||||||
30000,
|
30000,
|
||||||
None,
|
|
||||||
vec![],
|
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.pointer, 1);
|
||||||
assert_eq!(interpreter.cells[0], 0);
|
assert_eq!(interpreter.cells[0], 0);
|
||||||
assert_eq!(interpreter.cells[1], 4);
|
assert_eq!(interpreter.cells[1], 4);
|
||||||
assert_eq!(interpreter.bf_code, vec!['>', '+', '+', '+' , '+']);
|
// assert_eq!(interpreter.commands, vec!['>', '+', '+', '+', '+']);
|
||||||
|
|
||||||
// reset
|
// reset
|
||||||
interpreter.reset();
|
interpreter.reset();
|
||||||
|
@ -313,21 +383,6 @@ mod tests {
|
||||||
assert_eq!(interpreter.pointer, 0);
|
assert_eq!(interpreter.pointer, 0);
|
||||||
assert_eq!(interpreter.cells[0], 0);
|
assert_eq!(interpreter.cells[0], 0);
|
||||||
assert_eq!(interpreter.cells[1], 0);
|
assert_eq!(interpreter.cells[1], 0);
|
||||||
assert_eq!(interpreter.bf_code, Vec::<char>::new());
|
assert_eq!(interpreter.bf_commands, Vec::<BfCommand>::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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@ mod arguments;
|
||||||
mod repl;
|
mod repl;
|
||||||
mod utils;
|
mod utils;
|
||||||
mod bf_interpreter;
|
mod bf_interpreter;
|
||||||
mod mode;
|
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
extern crate pretty_env_logger;
|
extern crate pretty_env_logger;
|
||||||
|
@ -22,18 +21,13 @@ fn main() {
|
||||||
info!("Initializing bf_interpreter");
|
info!("Initializing bf_interpreter");
|
||||||
let mut interpreter = Interpreter::new(
|
let mut interpreter = Interpreter::new(
|
||||||
args.array_size,
|
args.array_size,
|
||||||
utils::read_brainfuck_code_if_any(&args.source),
|
|
||||||
args.features.unwrap_or_else(|| vec![]),
|
args.features.unwrap_or_else(|| vec![]),
|
||||||
match args.source {
|
|
||||||
Some(_) => mode::RunMode::Execute,
|
|
||||||
None => mode::RunMode::Repl
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
|
||||||
match args.source {
|
match args.source {
|
||||||
Some(source) => {
|
Some(source) => {
|
||||||
info!("Running brainfuck source code from file: {}", source);
|
info!("Running brainfuck source code from file: {}", source);
|
||||||
match interpreter.run(None) {
|
match interpreter.run(utils::read_brainfuck_code(&source)) {
|
||||||
Ok(exit_code) => {
|
Ok(exit_code) => {
|
||||||
info!("Finished running brainfuck source code from file: {}", source);
|
info!("Finished running brainfuck source code from file: {}", source);
|
||||||
if !args.without_tiles {
|
if !args.without_tiles {
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
#[derive(PartialEq, Debug)]
|
|
||||||
pub enum RunMode {
|
|
||||||
Execute,
|
|
||||||
Repl,
|
|
||||||
}
|
|
43
src/repl.rs
43
src/repl.rs
|
@ -19,8 +19,15 @@ impl Repl {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(mut self) {
|
pub fn run(mut self) {
|
||||||
|
let mut code_bat = String::new();
|
||||||
|
let mut is_loop = false;
|
||||||
loop {
|
loop {
|
||||||
print!("\n {}", PROMPT);
|
if is_loop {
|
||||||
|
print!("... ");
|
||||||
|
} else {
|
||||||
|
print!("{}", PROMPT);
|
||||||
|
}
|
||||||
|
|
||||||
std::io::stdout().flush().unwrap_or_else(|_| {
|
std::io::stdout().flush().unwrap_or_else(|_| {
|
||||||
error!("Failed to flush stdout");
|
error!("Failed to flush stdout");
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
|
@ -38,10 +45,18 @@ impl Repl {
|
||||||
|
|
||||||
self.history.push(input.clone()); // Save input to history
|
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) {
|
if input.starts_with(COMMAND_PREFIX) {
|
||||||
self.run_repl_cmd(input);
|
self.run_repl_cmd(input);
|
||||||
} else {
|
} else {
|
||||||
match self.interpreter.run(Some(input)) {
|
match self.interpreter.run(input) {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
info!("Successfully ran brainfuck source code from REPL");
|
info!("Successfully ran brainfuck source code from REPL");
|
||||||
}
|
}
|
||||||
|
@ -108,7 +123,7 @@ impl Repl {
|
||||||
|
|
||||||
// Run all commands in history
|
// Run all commands in history
|
||||||
for cmd in self.history.iter() {
|
for cmd in self.history.iter() {
|
||||||
match self.interpreter.run(Some(cmd.clone())) {
|
match self.interpreter.run(cmd.clone()) {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
info!(
|
info!(
|
||||||
"Successfully ran brainfuck source code from REPL"
|
"Successfully ran brainfuck source code from REPL"
|
||||||
|
@ -169,3 +184,25 @@ pub fn start(interpreter: Interpreter) {
|
||||||
|
|
||||||
Repl::new(interpreter).run();
|
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!();
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,18 +1,13 @@
|
||||||
pub(crate) fn read_brainfuck_code_if_any(source: &Option<String>) -> Option<String> {
|
pub fn read_brainfuck_code(source: &String) -> String {
|
||||||
match source {
|
|
||||||
Some(source) => {
|
|
||||||
info!("Reading brainfuck source code from file: {}", source);
|
info!("Reading brainfuck source code from file: {}", source);
|
||||||
match std::fs::read_to_string(source) {
|
match std::fs::read_to_string(source) {
|
||||||
Ok(source) => Some(clean(source)),
|
Ok(source) => clean(source),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("Failed to read source code file: {}", e);
|
error!("Failed to read source code file: {}", e);
|
||||||
eprintln!("Failed to read source code file: {}", e);
|
eprintln!("Failed to read source code file: {}", e);
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
None => None,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clean(source: String) -> String {
|
fn clean(source: String) -> String {
|
||||||
|
|
20
test_code/print_7_formatted.bf
Normal file
20
test_code/print_7_formatted.bf
Normal 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"!
|
1
test_code/print_hello_world_2.bf
Normal file
1
test_code/print_hello_world_2.bf
Normal file
|
@ -0,0 +1 @@
|
||||||
|
++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.
|
43
test_code/print_hello_world_2_formated.bf
Normal file
43
test_code/print_hello_world_2_formated.bf
Normal 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
|
3
test_code/print_my_first_name.bf
Normal file
3
test_code/print_my_first_name.bf
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
Print my first name (Anas)
|
||||||
|
Unformated:
|
||||||
|
++++++++[>++++[>++<-]>>>>>>++[<<<->>>-]<<<<<<<-]>>+.<<++++[>+++[>+++<-]>++<<-]>>+.<<+++[>+++[>-<-]>-<<-]>>-.<<++++++[>>+++<<-]>>.
|
3
test_code/print_my_first_name_and_last_name.bf
Normal file
3
test_code/print_my_first_name_and_last_name.bf
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
Print my first name and my last name (Anas Elgarhy)
|
||||||
|
Unformated:
|
||||||
|
++++++++[>++++[>++>+>++>+++<<<<-]>>>>+<<<<<-]>>+.<<++++[>+++[>+++<-]>++<<-]>>+.<<+++[>+++[>-<-]>-<<-]>>-.<<++++++[>>+++<<-]>>.>.>+++++.<<-------.-----.------.<+++++[>+++<-]>++.>>>.<<<+++++++.
|
56
test_code/print_my_first_name_and_last_name_formated.bf
Normal file
56
test_code/print_my_first_name_and_last_name_formated.bf
Normal 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
|
45
test_code/print_my_first_name_and_last_name_formatted.bf
Normal file
45
test_code/print_my_first_name_and_last_name_formatted.bf
Normal 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
|
50
test_code/print_my_first_name_formatted.bf
Normal file
50
test_code/print_my_first_name_formatted.bf
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
My first name (Anas)
|
||||||
|
Formated:
|
||||||
|
++++ ++++ 8
|
||||||
|
[
|
||||||
|
>++++
|
||||||
|
[
|
||||||
|
>++ A
|
||||||
|
>+++ a
|
||||||
|
>++++
|
||||||
|
>+ space
|
||||||
|
<<<<-
|
||||||
|
]
|
||||||
|
|
||||||
|
>>>>>>++
|
||||||
|
[
|
||||||
|
<<<-
|
||||||
|
>>>-
|
||||||
|
]
|
||||||
|
|
||||||
|
<<<<<<<-
|
||||||
|
]
|
||||||
|
>>+. Print cell 2: A
|
||||||
|
<<++++
|
||||||
|
[
|
||||||
|
>+++
|
||||||
|
[
|
||||||
|
>+++
|
||||||
|
<-
|
||||||
|
]
|
||||||
|
>++
|
||||||
|
<<-
|
||||||
|
]
|
||||||
|
>>+. Print n
|
||||||
|
<<+++
|
||||||
|
[
|
||||||
|
>+++
|
||||||
|
[
|
||||||
|
>-
|
||||||
|
<-
|
||||||
|
]
|
||||||
|
>-
|
||||||
|
<<-
|
||||||
|
]
|
||||||
|
>>-. Print n
|
||||||
|
<<++++++
|
||||||
|
[
|
||||||
|
>>+++
|
||||||
|
<<-
|
||||||
|
]
|
||||||
|
>>. Print s
|
Loading…
Reference in a new issue