385 lines
10 KiB
Rust
385 lines
10 KiB
Rust
use crate::arguments;
|
|
use crate::bf_interpreter::error::{InterpreterError, InterpreterErrorKind};
|
|
use std::io::{Read, Write};
|
|
use std::{char, usize, vec};
|
|
|
|
pub struct Interpreter {
|
|
pub cells: Vec<u8>,
|
|
pub pointer: usize,
|
|
pub bf_commands: Vec<BfCommand>,
|
|
brackets: Vec<BfCommand>,
|
|
pub features: Vec<arguments::Feature>,
|
|
}
|
|
|
|
impl Interpreter {
|
|
pub fn new(
|
|
array_size: usize,
|
|
features: Vec<arguments::Feature>,
|
|
) -> Self {
|
|
Self {
|
|
cells: vec![0; array_size],
|
|
pointer: 0,
|
|
bf_commands: vec![],
|
|
brackets: Vec::new(),
|
|
features,
|
|
}
|
|
}
|
|
|
|
pub fn run(&mut self, bf_code: String) -> Result<i32, InterpreterError> {
|
|
self.bf_commands = to_bf_commands(bf_code.chars().collect())?;
|
|
|
|
match self.run_brainfuck_code(&self.bf_commands.clone()) {
|
|
Ok(_) => Ok(0),
|
|
Err(e) => Err(e),
|
|
}
|
|
}
|
|
|
|
|
|
// +[>++<-]
|
|
fn iterate(&mut self, code: &Vec<BfCommand>) -> Result<(), InterpreterError> {
|
|
trace!("Iterate: {:?}", code);
|
|
while self.cells[self.pointer] != 0 {
|
|
self.run_brainfuck_code(code)?;
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn run_brainfuck_code(&mut self, bf_code: &Vec<BfCommand>) -> Result<(), InterpreterError> {
|
|
for command in bf_code {
|
|
match command {
|
|
BfCommand::IncPtr => self.increment_pointer()?,
|
|
BfCommand::DecPtr => self.decrement_pointer()?,
|
|
BfCommand::IncVal => self.increment_value()?,
|
|
BfCommand::DecVal => self.decrement_value()?,
|
|
BfCommand::Print => self.output_value()?,
|
|
BfCommand::Read => self.input_value()?,
|
|
BfCommand::Loop(loop_body) => self.iterate(loop_body)?,
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn increment_pointer(&mut self) -> Result<(), InterpreterError> {
|
|
trace!("Increment pointer");
|
|
self.pointer += 1;
|
|
if self.pointer >= self.cells.len() {
|
|
if self.features.contains(&arguments::Feature::ReversePointer) {
|
|
self.pointer = 0;
|
|
} else {
|
|
return Err(InterpreterErrorKind::PointerOutOfBounds(self.pointer).to_error());
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn decrement_pointer(&mut self) -> Result<(), InterpreterError> {
|
|
trace!("Decrement pointer");
|
|
if self.pointer == 0 {
|
|
if self.features.contains(&arguments::Feature::ReversePointer) {
|
|
self.pointer = self.cells.len() - 1;
|
|
} else {
|
|
return Err(InterpreterErrorKind::PointerOutOfBounds(self.pointer).to_error());
|
|
}
|
|
} else {
|
|
self.pointer -= 1;
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn increment_value(&mut self) -> Result<(), InterpreterError> {
|
|
trace!("Increment value");
|
|
if self.cells[self.pointer] == 255 {
|
|
if !self.features.contains(&arguments::Feature::NoReverseValue) {
|
|
self.cells[self.pointer] = 0;
|
|
} else {
|
|
return Err(InterpreterErrorKind::ValueOutOfBounds.to_error());
|
|
}
|
|
} else {
|
|
self.cells[self.pointer] += 1;
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn decrement_value(&mut self) -> Result<(), InterpreterError> {
|
|
trace!("Decrement value");
|
|
if self.cells[self.pointer] == 0 {
|
|
if !self.features.contains(&arguments::Feature::NoReverseValue) {
|
|
self.cells[self.pointer] = 255;
|
|
} else {
|
|
return Err(InterpreterErrorKind::ValueOutOfBounds.to_error());
|
|
}
|
|
} else {
|
|
self.cells[self.pointer] -= 1;
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn output_value(&mut self) -> Result<(), InterpreterError> {
|
|
trace!("Output value");
|
|
|
|
if self.features.contains(&arguments::Feature::AllowUtf8) {
|
|
let c = char::from_u32(self.cells[self.pointer] as u32);
|
|
match c {
|
|
Some(c) => print!("{}", c),
|
|
None => return Err(InterpreterErrorKind::InvalidUtf8.to_error()),
|
|
}
|
|
} else {
|
|
print!("{}", self.cells[self.pointer] as char);
|
|
}
|
|
match std::io::stdout().flush() {
|
|
Ok(_) => Ok(()),
|
|
Err(e) => Err(InterpreterErrorKind::FlushError(e).to_error()),
|
|
}
|
|
}
|
|
|
|
fn input_value(&mut self) -> Result<(), InterpreterError> {
|
|
trace!("Input value");
|
|
let mut input = [0; 1];
|
|
match std::io::stdin().read_exact(&mut input) {
|
|
Ok(_) => {
|
|
self.cells[self.pointer] = input[0];
|
|
Ok(())
|
|
}
|
|
Err(e) => Err(InterpreterErrorKind::IoError(e).to_error()),
|
|
}
|
|
}
|
|
|
|
pub fn reset(&mut self) {
|
|
self.cells = vec![0; self.cells.len()];
|
|
self.pointer = 0;
|
|
self.brackets = Vec::new();
|
|
self.bf_commands = Vec::new();
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, PartialEq, Clone)]
|
|
pub enum BfCommand {
|
|
IncPtr,
|
|
DecPtr,
|
|
IncVal,
|
|
DecVal,
|
|
Print,
|
|
Read,
|
|
Loop(Vec<BfCommand>),
|
|
}
|
|
|
|
fn to_bf_commands(bf_code: Vec<char>) -> Result<Vec<BfCommand>, InterpreterError> {
|
|
let mut bf_commands = Vec::new();
|
|
let mut i = 0;
|
|
while i < bf_code.len() {
|
|
match bf_code[i] {
|
|
'[' => {
|
|
let mut bracket_count = 1;
|
|
let mut j = i + 1;
|
|
while j < bf_code.len() {
|
|
match bf_code[j] {
|
|
'[' => bracket_count += 1,
|
|
']' => bracket_count -= 1,
|
|
_ => (),
|
|
}
|
|
if bracket_count == 0 {
|
|
break;
|
|
}
|
|
j += 1;
|
|
}
|
|
if bracket_count != 0 {
|
|
return Err(InterpreterErrorKind::UnmatchedBracket.to_error());
|
|
}
|
|
bf_commands.push(BfCommand::Loop(to_bf_commands(bf_code[i + 1..j].to_vec())?));
|
|
i = j;
|
|
}
|
|
_ => {
|
|
match BfCommand::from(bf_code[i]) {
|
|
Some(command) => bf_commands.push(command),
|
|
None => (),
|
|
}
|
|
},
|
|
}
|
|
i += 1;
|
|
}
|
|
Ok(bf_commands)
|
|
}
|
|
|
|
impl BfCommand {
|
|
fn from(c: char) -> Option<Self> {
|
|
match c {
|
|
'>' => Some(BfCommand::IncPtr),
|
|
'<' => Some(BfCommand::DecPtr),
|
|
'+' => Some(BfCommand::IncVal),
|
|
'-' => Some(BfCommand::DecVal),
|
|
'.' => Some(BfCommand::Print),
|
|
',' => Some(BfCommand::Read),
|
|
_ => None,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use pretty_assertions::assert_eq; // for testing only
|
|
use crate::utils;
|
|
|
|
#[test]
|
|
fn print_h_combine_repl() {
|
|
let mut interpreter = Interpreter::new(
|
|
30000,
|
|
vec![],
|
|
);
|
|
|
|
assert_eq!(interpreter.run(String::from(">+++++++++[<++++ ++++>-]<.")), Ok(0));
|
|
|
|
println!();
|
|
}
|
|
|
|
#[test]
|
|
fn print_h_repl() {
|
|
let mut interpreter = Interpreter::new(
|
|
30000,
|
|
vec![],
|
|
);
|
|
|
|
assert_eq!(interpreter.run(String::from(">+++++++++")), Ok(0));
|
|
assert_eq!(interpreter.run(String::from("[<++++ ++++>-]<.")), Ok(0));
|
|
|
|
println!();
|
|
}
|
|
|
|
#[test]
|
|
fn nested_loop_level_1_combine() {
|
|
let mut interpreter = Interpreter::new(
|
|
5,
|
|
vec![],
|
|
);
|
|
|
|
assert_eq!(interpreter.run(String::from("++[>++[>+<-]<-]")), Ok(0));
|
|
assert_eq!(interpreter.cells[2], 4);
|
|
|
|
println!();
|
|
}
|
|
|
|
|
|
#[test]
|
|
fn execute_hello_world_from_file() {
|
|
let mut interpreter = Interpreter::new(
|
|
30000,
|
|
vec![],
|
|
);
|
|
|
|
|
|
println!();
|
|
|
|
assert_eq!(interpreter.run(
|
|
utils::read_brainfuck_code(
|
|
&String::from("test_code/hello_world.bf"))), Ok(0));
|
|
}
|
|
|
|
#[test]
|
|
fn execute_print_hi_from_file() {
|
|
let mut interpreter = Interpreter::new(
|
|
30000,
|
|
vec![],
|
|
);
|
|
|
|
println!();
|
|
|
|
assert_eq!(interpreter.run(
|
|
utils::read_brainfuck_code(&String::from("test_code/print_hi.bf"))), Ok(0));
|
|
}
|
|
|
|
#[test]
|
|
fn execute_print_hi_yooo_from_file() {
|
|
let mut interpreter = Interpreter::new(
|
|
30000,
|
|
vec![],
|
|
);
|
|
|
|
println!();
|
|
|
|
assert_eq!(interpreter.run(
|
|
utils::read_brainfuck_code(&String::from("test_code/print_hi_yooo.bf"))),
|
|
Ok(0));
|
|
}
|
|
|
|
#[test]
|
|
fn execute_print_my_first_name_from_formatted_file() {
|
|
let mut interpreter = Interpreter::new(
|
|
30000,
|
|
vec![],
|
|
);
|
|
|
|
println!();
|
|
|
|
assert_eq!(interpreter.run(
|
|
utils::read_brainfuck_code(&String::from("test_code/print_my_first_name_formatted.bf"))),
|
|
Ok(0));
|
|
}
|
|
|
|
#[test]
|
|
fn execute_print_my_first_name_from_file() {
|
|
let mut interpreter = Interpreter::new(
|
|
30000,
|
|
vec![],
|
|
);
|
|
|
|
println!();
|
|
|
|
assert_eq!(interpreter.run(
|
|
utils::read_brainfuck_code(&String::from("test_code/print_my_first_name.bf"))),
|
|
Ok(0));
|
|
}
|
|
|
|
#[test]
|
|
fn execute_print_my_first_name_and_last_name_from_formatted_file() {
|
|
let mut interpreter = Interpreter::new(
|
|
30000,
|
|
vec![],
|
|
);
|
|
|
|
println!();
|
|
|
|
assert_eq!(interpreter.run(
|
|
utils::read_brainfuck_code(
|
|
&String::from("test_code/print_my_first_name_and_last_name_formatted.bf"))),
|
|
Ok(0));
|
|
}
|
|
|
|
#[test]
|
|
fn execute_print_my_first_name_and_last_name_from_file() {
|
|
let mut interpreter = Interpreter::new(
|
|
30000,
|
|
vec![],
|
|
);
|
|
|
|
println!();
|
|
|
|
assert_eq!(interpreter.run(utils::read_brainfuck_code(
|
|
&String::from("test_code/print_my_first_name_and_last_name.bf"))),
|
|
Ok(0));
|
|
}
|
|
|
|
#[test]
|
|
fn reset() {
|
|
let mut interpreter = Interpreter::new(
|
|
30000,
|
|
vec![],
|
|
);
|
|
|
|
|
|
assert_eq!(interpreter.run(String::from(">++++")), Ok(0));
|
|
|
|
assert_eq!(interpreter.pointer, 1);
|
|
assert_eq!(interpreter.cells[0], 0);
|
|
assert_eq!(interpreter.cells[1], 4);
|
|
// assert_eq!(interpreter.commands, 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_commands, Vec::<BfCommand>::new());
|
|
}
|
|
}
|