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
|
# 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"
|
||||||
|
|
|
@ -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,
|
||||||
|
|
||||||
}
|
}
|
|
@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
28
src/main.rs
28
src/main.rs
|
@ -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");
|
||||||
|
|
|
@ -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);
|
||||||
|
|
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