2022-10-08 20:11:13 +00:00
|
|
|
use crate::{arguments, mode};
|
2022-10-08 10:01:28 +00:00
|
|
|
use crate::bf_interpreter::error::{InterpreterError, InterpreterErrorKind};
|
2022-10-07 23:06:35 +00:00
|
|
|
use std::io::{Read, Write};
|
2022-10-08 20:11:13 +00:00
|
|
|
use std::{char, usize, vec};
|
2022-10-07 18:17:21 +00:00
|
|
|
|
2022-10-07 19:54:49 +00:00
|
|
|
pub struct Interpreter {
|
2022-10-07 21:40:56 +00:00
|
|
|
pub cells: Vec<u8>,
|
2022-10-07 19:54:49 +00:00
|
|
|
pub pointer: usize,
|
|
|
|
pub array_size: usize,
|
2022-10-08 20:11:13 +00:00
|
|
|
pub bf_code: Vec<char>,
|
2022-10-07 21:40:56 +00:00
|
|
|
brackets: Vec<BfCommand>,
|
2022-10-07 19:54:49 +00:00
|
|
|
pub features: Vec<arguments::Feature>,
|
2022-10-08 20:11:13 +00:00
|
|
|
mode: mode::RunMode,
|
2022-10-07 19:54:49 +00:00
|
|
|
}
|
2022-10-07 21:40:56 +00:00
|
|
|
|
2022-10-07 19:54:49 +00:00
|
|
|
impl Interpreter {
|
2022-10-07 23:06:35 +00:00
|
|
|
pub fn new(
|
|
|
|
array_size: usize,
|
|
|
|
bf_code: Option<String>,
|
|
|
|
features: Vec<arguments::Feature>,
|
2022-10-08 20:11:13 +00:00
|
|
|
run_mode: mode::RunMode
|
2022-10-07 23:06:35 +00:00
|
|
|
) -> Self {
|
2022-10-08 20:11:13 +00:00
|
|
|
trace!("Run mode{run_mode:?}");
|
2022-10-07 19:54:49 +00:00
|
|
|
Self {
|
2022-10-07 21:40:56 +00:00
|
|
|
cells: vec![0; array_size],
|
2022-10-07 19:54:49 +00:00
|
|
|
pointer: 0,
|
|
|
|
array_size,
|
2022-10-08 20:11:13 +00:00
|
|
|
bf_code: bf_code.unwrap_or_else(|| String::new()).chars().collect(),
|
2022-10-07 19:54:49 +00:00
|
|
|
brackets: Vec::new(),
|
|
|
|
features,
|
2022-10-08 20:11:13 +00:00
|
|
|
mode: run_mode,
|
2022-10-07 19:54:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-07 23:05:21 +00:00
|
|
|
pub fn run(&mut self, bf_code: Option<String>) -> Result<i32, InterpreterError> {
|
2022-10-07 21:40:56 +00:00
|
|
|
let bf_code = match bf_code {
|
|
|
|
Some(bf_code) => {
|
2022-10-08 20:11:13 +00:00
|
|
|
bf_code.chars().collect()
|
2022-10-07 21:40:56 +00:00
|
|
|
}
|
2022-10-07 23:06:35 +00:00
|
|
|
None => self.bf_code.clone(),
|
2022-10-07 21:40:56 +00:00
|
|
|
};
|
2022-10-07 19:54:49 +00:00
|
|
|
|
2022-10-08 20:11:13 +00:00
|
|
|
match self.run_brainfuck_code(bf_code, false) {
|
2022-10-07 21:40:56 +00:00
|
|
|
Ok(_) => Ok(0),
|
2022-10-07 23:06:35 +00:00
|
|
|
Err(e) => Err(e),
|
2022-10-07 21:40:56 +00:00
|
|
|
}
|
|
|
|
}
|
2022-10-07 19:54:49 +00:00
|
|
|
|
2022-10-07 21:40:56 +00:00
|
|
|
// +[>++<-]
|
2022-10-08 20:11:13 +00:00
|
|
|
fn iterate(&mut self, code: Vec<char>) -> Result<(), InterpreterError> {
|
|
|
|
trace!("Iterate: {:?}", code);
|
2022-10-07 21:40:56 +00:00
|
|
|
while self.cells[self.pointer] != 0 {
|
2022-10-08 20:11:13 +00:00
|
|
|
self.run_brainfuck_code(code.clone(), true)?;
|
2022-10-07 21:40:56 +00:00
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2022-10-08 20:11:13 +00:00
|
|
|
fn run_brainfuck_code(&mut self, bf_code: Vec<char>, from_loop: bool) -> Result<(), InterpreterError> {
|
|
|
|
let mut removed_num = 0_usize;
|
|
|
|
for (i, ch) in bf_code.iter().enumerate() {
|
|
|
|
match BfCommand::from_char(ch, i - removed_num) {
|
2022-10-07 19:54:49 +00:00
|
|
|
Some(cmd) => {
|
|
|
|
trace!("Executing command: {:?}", cmd);
|
2022-10-08 20:11:13 +00:00
|
|
|
self.execute(cmd)?;
|
|
|
|
|
|
|
|
// 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());
|
|
|
|
}
|
2022-10-07 21:40:56 +00:00
|
|
|
}
|
|
|
|
None => {
|
2022-10-08 20:11:13 +00:00
|
|
|
trace!("Skipping character: \'{}\'", ch);
|
|
|
|
removed_num += 1;
|
2022-10-07 21:40:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2022-10-07 23:11:22 +00:00
|
|
|
fn execute(&mut self, cmd: BfCommand) -> Result<(), InterpreterError> {
|
2022-10-07 21:40:56 +00:00
|
|
|
match cmd {
|
|
|
|
BfCommand::IncPtr => {
|
|
|
|
self.pointer += 1;
|
|
|
|
if self.pointer >= self.array_size {
|
|
|
|
if self.features.contains(&arguments::Feature::ReversePointer) {
|
|
|
|
self.pointer = 0;
|
|
|
|
} else {
|
2022-10-08 10:01:28 +00:00
|
|
|
return Err(InterpreterErrorKind::PointerOutOfBounds(self.pointer).to_error())
|
2022-10-07 21:40:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
BfCommand::DecPtr => {
|
|
|
|
if self.pointer == 0 {
|
|
|
|
if self.features.contains(&arguments::Feature::ReversePointer) {
|
|
|
|
self.pointer = self.array_size - 1;
|
|
|
|
} else {
|
2022-10-08 10:01:28 +00:00
|
|
|
return Err(InterpreterErrorKind::PointerOutOfBounds(self.pointer).to_error());
|
2022-10-07 21:40:56 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
self.pointer -= 1;
|
|
|
|
}
|
2022-10-07 23:06:35 +00:00
|
|
|
}
|
2022-10-07 21:40:56 +00:00
|
|
|
BfCommand::IncVal => {
|
2022-10-08 10:01:28 +00:00
|
|
|
if self.cells[self.pointer] == 255 {
|
|
|
|
if self.features.contains(&arguments::Feature::ReverseValue) {
|
|
|
|
self.cells[self.pointer] = 0;
|
|
|
|
} else {
|
|
|
|
return Err(InterpreterErrorKind::ValueOutOfBounds.to_error());
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
self.cells[self.pointer] += 1;
|
|
|
|
}
|
2022-10-07 23:06:35 +00:00
|
|
|
}
|
2022-10-07 21:40:56 +00:00
|
|
|
BfCommand::DecVal => {
|
2022-10-08 10:01:28 +00:00
|
|
|
if self.cells[self.pointer] == 0 {
|
|
|
|
if self.features.contains(&arguments::Feature::ReverseValue) {
|
|
|
|
self.cells[self.pointer] = 255;
|
|
|
|
} else {
|
|
|
|
return Err(InterpreterErrorKind::ValueOutOfBounds.to_error());
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
self.cells[self.pointer] -= 1;
|
|
|
|
}
|
2022-10-07 23:06:35 +00:00
|
|
|
}
|
2022-10-07 21:40:56 +00:00
|
|
|
BfCommand::Print => {
|
|
|
|
print!("{}", self.cells[self.pointer] as char);
|
|
|
|
std::io::stdout().flush().unwrap();
|
2022-10-07 23:06:35 +00:00
|
|
|
}
|
2022-10-07 21:40:56 +00:00
|
|
|
BfCommand::Read => {
|
2022-10-07 22:28:14 +00:00
|
|
|
self.cells[self.pointer] = match std::io::stdin().bytes().next() {
|
|
|
|
Some(Ok(byte)) => byte,
|
|
|
|
Some(Err(e)) => {
|
2022-10-08 10:01:28 +00:00
|
|
|
return Err(InterpreterErrorKind::ByteReadError(e).to_error());
|
2022-10-07 22:28:14 +00:00
|
|
|
}
|
|
|
|
None => {
|
2022-10-08 10:01:28 +00:00
|
|
|
return Err(InterpreterErrorKind::ReadError.to_error());
|
2022-10-07 22:28:14 +00:00
|
|
|
}
|
|
|
|
};
|
2022-10-07 23:06:35 +00:00
|
|
|
}
|
2022-10-07 21:40:56 +00:00
|
|
|
BfCommand::LoopStart(i) => {
|
|
|
|
self.brackets.push(BfCommand::LoopStart(i));
|
2022-10-07 23:06:35 +00:00
|
|
|
}
|
2022-10-07 21:40:56 +00:00
|
|
|
BfCommand::LoopEnd(i) => {
|
|
|
|
let open_bracket = self.brackets.pop();
|
|
|
|
match open_bracket {
|
|
|
|
Some(BfCommand::LoopStart(j)) => {
|
|
|
|
if self.cells[self.pointer] != 0 {
|
2022-10-08 20:11:13 +00:00
|
|
|
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();
|
2022-10-07 21:40:56 +00:00
|
|
|
self.iterate(code)?;
|
2022-10-07 19:54:49 +00:00
|
|
|
}
|
2022-10-07 23:06:35 +00:00
|
|
|
}
|
2022-10-07 21:40:56 +00:00
|
|
|
_ => {
|
2022-10-08 20:11:13 +00:00
|
|
|
return Err(InterpreterErrorKind::UnmatchedClosingBracket(i).to_error());
|
2022-10-07 19:54:49 +00:00
|
|
|
}
|
2022-10-07 21:40:56 +00:00
|
|
|
}
|
2022-10-07 19:54:49 +00:00
|
|
|
}
|
|
|
|
}
|
2022-10-07 21:40:56 +00:00
|
|
|
Ok(())
|
2022-10-07 19:54:49 +00:00
|
|
|
}
|
2022-10-07 22:33:44 +00:00
|
|
|
|
|
|
|
pub fn reset(&mut self) {
|
|
|
|
self.cells = vec![0; self.array_size];
|
|
|
|
self.pointer = 0;
|
|
|
|
self.brackets = Vec::new();
|
2022-10-08 20:11:13 +00:00
|
|
|
self.bf_code = Vec::new();
|
2022-10-07 22:33:44 +00:00
|
|
|
}
|
2022-10-07 19:54:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, PartialEq)]
|
|
|
|
enum BfCommand {
|
|
|
|
IncPtr,
|
|
|
|
DecPtr,
|
|
|
|
IncVal,
|
|
|
|
DecVal,
|
|
|
|
Print,
|
|
|
|
Read,
|
|
|
|
LoopStart(usize),
|
2022-10-07 21:40:56 +00:00
|
|
|
LoopEnd(usize),
|
2022-10-07 19:54:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl BfCommand {
|
2022-10-08 20:11:13 +00:00
|
|
|
fn from_char(c: &char, index: usize) -> Option<BfCommand> {
|
2022-10-07 19:54:49 +00:00
|
|
|
match c {
|
|
|
|
'>' => Some(BfCommand::IncPtr),
|
|
|
|
'<' => Some(BfCommand::DecPtr),
|
|
|
|
'+' => Some(BfCommand::IncVal),
|
|
|
|
'-' => Some(BfCommand::DecVal),
|
|
|
|
'.' => Some(BfCommand::Print),
|
|
|
|
',' => Some(BfCommand::Read),
|
|
|
|
'[' => Some(BfCommand::LoopStart(index)),
|
2022-10-07 21:40:56 +00:00
|
|
|
']' => Some(BfCommand::LoopEnd(index)),
|
2022-10-07 19:54:49 +00:00
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
2022-10-07 21:40:56 +00:00
|
|
|
}
|
2022-10-07 22:52:43 +00:00
|
|
|
|
2022-10-08 20:11:13 +00:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
use crate::mode::RunMode;
|
|
|
|
use pretty_assertions::assert_eq;
|
|
|
|
use crate::utils; // for testing only
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn print_h_combine_repl() {
|
|
|
|
let mut interpreter = Interpreter::new(
|
|
|
|
30000,
|
|
|
|
None,
|
|
|
|
vec![],
|
|
|
|
RunMode::Repl
|
|
|
|
);
|
|
|
|
|
|
|
|
assert_eq!(interpreter.run(None), Ok(0));
|
|
|
|
|
|
|
|
assert_eq!(interpreter.run(Some(String::from(">+++++++++[<++++ ++++>-]<."))), Ok(0));
|
|
|
|
}
|
|
|
|
#[test]
|
|
|
|
fn print_h_repl() {
|
|
|
|
let mut interpreter = Interpreter::new(
|
|
|
|
30000,
|
|
|
|
None,
|
|
|
|
vec![],
|
|
|
|
RunMode::Repl
|
|
|
|
);
|
|
|
|
|
|
|
|
assert_eq!(interpreter.run(None), Ok(0));
|
|
|
|
|
|
|
|
assert_eq!(interpreter.run(Some(String::from(">+++++++++"))), Ok(0));
|
|
|
|
assert_eq!(interpreter.run(Some(String::from("[<++++ ++++>-]<."))), Ok(0));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn execute_hello_world_from_file() {
|
|
|
|
let mut interpreter = Interpreter::new(
|
|
|
|
30000,
|
|
|
|
utils::read_brainfuck_code_if_any(&Some(String::from("test_code/hello_world.bf"))),
|
|
|
|
vec![],
|
|
|
|
RunMode::Execute
|
|
|
|
);
|
|
|
|
|
|
|
|
assert_eq!(interpreter.run(None), Ok(0));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn execute_print_hi_from_file() {
|
|
|
|
let mut interpreter = Interpreter::new(
|
|
|
|
30000,
|
|
|
|
utils::read_brainfuck_code_if_any(&Some(String::from("test_code/print_hi.bf"))),
|
|
|
|
vec![],
|
|
|
|
RunMode::Execute
|
|
|
|
);
|
|
|
|
|
|
|
|
assert_eq!(interpreter.run(None), Ok(0));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn execute_print_hi_yooo_from_file() {
|
|
|
|
let mut interpreter = Interpreter::new(
|
|
|
|
30000,
|
|
|
|
utils::read_brainfuck_code_if_any(&Some(String::from("test_code/print_hi_yooo.bf"))),
|
|
|
|
vec![],
|
|
|
|
RunMode::Execute
|
|
|
|
);
|
|
|
|
|
|
|
|
assert_eq!(interpreter.run(None), Ok(0));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn reset() {
|
|
|
|
let mut interpreter = Interpreter::new(
|
|
|
|
30000,
|
|
|
|
None,
|
|
|
|
vec![],
|
|
|
|
RunMode::Repl
|
|
|
|
);
|
|
|
|
|
|
|
|
assert_eq!(interpreter.run(None), Ok(0));
|
|
|
|
|
|
|
|
assert_eq!(interpreter.run(Some(String::from(">++++"))), Ok(0));
|
|
|
|
|
|
|
|
assert_eq!(interpreter.pointer, 1);
|
|
|
|
assert_eq!(interpreter.cells[0], 0);
|
|
|
|
assert_eq!(interpreter.cells[1], 4);
|
|
|
|
assert_eq!(interpreter.bf_code, vec!['>', '+', '+', '+' , '+']);
|
|
|
|
|
|
|
|
// reset
|
|
|
|
interpreter.reset();
|
|
|
|
|
|
|
|
assert_eq!(interpreter.pointer, 0);
|
|
|
|
assert_eq!(interpreter.cells[0], 0);
|
|
|
|
assert_eq!(interpreter.cells[1], 0);
|
|
|
|
assert_eq!(interpreter.bf_code, Vec::<char>::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);
|
|
|
|
}
|
|
|
|
}
|