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 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
clap = { version = "4.0.10", features = ["derive", "color"] } 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"

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 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,
} }

View file

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

View file

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

View file

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

View file

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