Compleate the base interpreter yoo 🥰

This commit is contained in:
Anas Elgarhy 2022-10-07 23:40:56 +02:00
parent 67667d9cb9
commit ed155f285b
7 changed files with 124 additions and 64 deletions

View file

@ -6,6 +6,6 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
clap = { version = "4.0.10", features = ["derive", "color"] }
clap = { version = "4.0.10", features = ["derive", "color", "cargo"] }
log = "0.4.17"
pretty_env_logger = "0.4.0"

View file

@ -24,5 +24,4 @@ pub enum Feature {
/// 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,
}

View file

@ -1,21 +1,22 @@
use std::io::{Read, Write};
use std::io::{Write};
use std::usize;
use crate::arguments;
pub struct Interpreter {
pub array: Vec<u8>,
pub cells: Vec<u8>,
pub pointer: usize,
pub array_size: usize,
pub bf_code: String,
pub brackets: Vec<BfCommand>,
brackets: Vec<BfCommand>,
pub features: Vec<arguments::Feature>,
}
impl Interpreter {
pub fn new(array_size: usize,
bf_code: Option<String>,
features: Vec<arguments::Feature>) -> Self {
Self {
array: vec![0; array_size],
cells: vec![0; array_size],
pointer: 0,
array_size,
bf_code: bf_code.unwrap_or_else(|| String::new()),
@ -24,65 +25,107 @@ impl Interpreter {
}
}
pub fn run(&mut self, bf_code: Option<String>) {
let mut cells = vec![0u8; bf_arr_size];
let mut ptr = 0;
let mut brackets = vec![];
pub fn run(&mut self, bf_code: Option<String>) -> Result<u32, (String, u32)> {
let bf_code = match bf_code {
Some(bf_code) => {
self.bf_code.push_str(&*bf_code);
bf_code
}
None => self.bf_code.clone()
};
match self.run_brainfuck_code(&bf_code) {
Ok(_) => Ok(0),
Err(e) => Err((e, 1)),
}
}
// +[>++<-]
fn iterate(&mut self, code: String) -> Result<(), String> {
while self.cells[self.pointer] != 0 {
self.run_brainfuck_code(&code)?;
}
Ok(())
}
fn run_brainfuck_code(&mut self, bf_code: &str) -> Result<(), String> {
for (i, ch) in bf_code.chars().enumerate() {
trace!("Current character: {}", ch);
trace!("Current pointer: {}", ptr);
trace!("Current cell: {}", cells[ptr]);
match BfCommand::from_char(ch, i) {
Some(cmd) => {
trace!("Executing command: {:?}", cmd);
match cmd {
BfCommand::IncPtr => {
if ptr == bf_arr_size - 1 {
eprintln!("Error: pointer out of bounds");
} else {
ptr += 1;
}
},
BfCommand::DecPtr => ptr -= 1,
BfCommand::IncVal => {
cells[ptr] += 1
},
BfCommand::DecVal => cells[ptr] -= 1,
BfCommand::Print => {
trace!("Printing value: {}", cells[ptr]);
println!("{}", cells[ptr]);
std::io::stdout().flush().unwrap();
},
BfCommand::Read => cells[ptr] = std::io::stdin()
.bytes().next().unwrap().unwrap() as u8,
BfCommand::LoopStart(index) => brackets.push(cmd),
BfCommand::LoopEnd => {
if cells[ptr] != 0 {
let i = brackets
.iter()
.map(|cmd| match cmd {
BfCommand::LoopStart(index) => *index,
_ => unreachable!()
})
.last();
brackets.truncate(i);
ptr = index;
} else {
brackets.pop();
}
}
}
},
self.execute(cmd)?
}
None => {
trace!("Ignoring character: {}", ch);
} // Ignore unknown characters
trace!("Skipping character: {}", ch);
}
}
}
Ok(())
}
fn execute(&mut self, cmd: BfCommand) -> Result<(), String> {
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(format!("Pointer out of bounds: {}", self.pointer));
}
}
}
BfCommand::DecPtr => {
if self.pointer == 0 {
if self.features.contains(&arguments::Feature::ReversePointer) {
self.pointer = self.array_size - 1;
} else {
return Err(format!("Pointer out of bounds: {}", self.pointer));
}
} else {
self.pointer -= 1;
}
},
BfCommand::IncVal => {
self.cells[self.pointer] = self.cells[self.pointer].wrapping_add(1);
},
BfCommand::DecVal => {
self.cells[self.pointer] = self.cells[self.pointer].wrapping_sub(1);
},
BfCommand::Print => {
print!("{}", self.cells[self.pointer] as char);
std::io::stdout().flush().unwrap();
},
BfCommand::Read => {
let mut input = String::new();
// TODO: Handle errors, and read only one byte
std::io::stdin().read_line(&mut input).unwrap();
self.cells[self.pointer] = input.chars().next().unwrap() as u8;
},
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 code = self.bf_code[j..i].to_string();
self.iterate(code)?;
}
},
_ => {
return Err(format!("Unmatched closing bracket at position: {}", i));
}
}
}
}
Ok(())
}
}
#[derive(Debug, PartialEq)]
enum BfCommand {
IncPtr,
@ -92,7 +135,7 @@ enum BfCommand {
Print,
Read,
LoopStart(usize),
LoopEnd,
LoopEnd(usize),
}
impl BfCommand {
@ -105,8 +148,8 @@ impl BfCommand {
'.' => Some(BfCommand::Print),
',' => Some(BfCommand::Read),
'[' => Some(BfCommand::LoopStart(index)),
']' => Some(BfCommand::LoopEnd),
']' => Some(BfCommand::LoopEnd(index)),
_ => None,
}
}
}
}

View file

@ -2,12 +2,14 @@ mod arguments;
mod interpreter;
mod utils;
use std::io::Write;
use clap::Parser;
extern crate pretty_env_logger;
#[macro_use] extern crate log;
use arguments::Args;
use utils::;
use crate::utils::read_brainfuck_code_if_any;
fn main() {
pretty_env_logger::init();
@ -23,31 +25,45 @@ fn main() {
info!("Initializing interpreter");
let mut interpreter = interpreter::Interpreter::new(
args.array_size,
urils::read_brainfuck_code_if_any(args.source),
read_brainfuck_code_if_any(&args.source),
args.features.unwrap_or_else(|| vec![]));
match args.source {
Some(source) => {
info!("Running brainfuck source code from file: {}", source);
interpreter.run(None)
interpreter.run(None).unwrap();
},
None => {
info!("Entering REPL mode");
println!("Welcome to the brainfuck REPL mode! :)");
println!("Brainfuck interpreter v {}\nBy {}",
clap::crate_version!(), clap::crate_authors!());
println!("Enter your brainfuck code and press enter to run it.");
println!("Enter !fuck to exit :D");
println!("Enter !help fuck to get more help");
loop {
print!("> ");
std::io::stdout().flush().unwrap();
let mut input = String::new();
std::io::stdin().read_line(&mut input).unwrap();
if input.starts_with("!") {
match input.trim() {
"!fuck" => {
match input.trim().get(1..).unwrap() {
"fuck" => {
println!("Bye bye :D");
break;
},
"!help" => {
"array" | "a" => {
println!("Current array: {:#?}", interpreter.cells);
},
"array_size" | "as" => {
println!("Current array size: {}", interpreter.array_size);
},
"pointer" | "p" => {
println!("Current pointer: {}", interpreter.pointer);
},
"help" => {
println!("!fuck: exit the REPL mode");
println!("!array, !a: print the current array");
println!("!array_size, !as: print the current array size");

View file

@ -1,4 +1,4 @@
fn read_brainfuck_code_if_any(source: Option<String>) -> Option<String> {
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);

View file

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

View file

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