Compleate the base interpreter yoo 🥰
This commit is contained in:
parent
67667d9cb9
commit
ed155f285b
7 changed files with 124 additions and 64 deletions
|
@ -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"
|
||||
|
|
|
@ -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,
|
||||
|
||||
}
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
28
src/main.rs
28
src/main.rs
|
@ -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");
|
||||
|
|
|
@ -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);
|
||||
|
|
1
test_code/print_hi_yooo.brainfuck
Normal file
1
test_code/print_hi_yooo.brainfuck
Normal file
|
@ -0,0 +1 @@
|
|||
>+++++++++[<++++ ++++>-]<.>++++++++[<++++>-]<+.>+++++++++[<-------->-]<-.>++++++++++[<+++++++++>-]<-.>+++[<--->-]<-..............................................................>+++++++++[<--------->-]<+++.`
|
1
test_code/read_name_and_print_hi.brainfuck
Normal file
1
test_code/read_name_and_print_hi.brainfuck
Normal file
|
@ -0,0 +1 @@
|
|||
>+++++++++[<++++ ++++>-]<.>++++++++[<++++>-]<+.>+++++++++[<-------->-]<-.>>,.[[-],.]
|
Loading…
Reference in a new issue