use std::collections::vec_deque::VecDeque;
use std::io::{Read, Write};
pub fn run_with_fixed_block(
code: &str,
stdin: &mut dyn Read,
stdout: &mut dyn Write,
block_size: usize,
) {
let instructions: Vec<char> = code.chars().collect();
let mut cursor: usize = 0;
let mut cells: Vec<u8> = vec![0; block_size];
let mut position: usize = 0;
let mut loop_positions: VecDeque<usize> = VecDeque::new();
while cursor < instructions.len() {
match instructions[cursor] {
'<' => {
if position > 0 {
position -= 1;
}
}
'>' => {
if position < block_size - 1 {
position += 1;
}
}
'-' => {
if cells[position] > 0 {
cells[position] -= 1;
}
}
'+' => {
if cells[position] < 255 {
cells[position] += 1;
}
}
',' => {
cells[position] = get_char(stdin) as u8;
}
'.' => {
put_char(cells[position] as char, stdout);
}
'[' => {
if cells[position] != 0 {
loop_positions.push_back(cursor);
} else {
cursor += 1;
let mut ignore_brace: usize = 1;
while cursor < instructions.len() - 1 && ignore_brace != 0 {
if instructions[cursor] == ']' {
ignore_brace -= 1;
} else if instructions[cursor] == '[' {
ignore_brace += 1;
}
cursor += 1;
}
cursor -= 1;
}
}
']' => {
if cells[position] != 0 {
cursor = *loop_positions.back().unwrap();
} else {
loop_positions.pop_back();
}
}
_ => {}
}
cursor += 1;
}
}
pub fn run_with_dynamic_block(code: &str, stdin: &mut dyn Read, stdout: &mut dyn Write) {
let instructions: Vec<char> = code.chars().collect();
let mut cursor: usize = 0;
let mut cells: Vec<u8> = vec![0];
let mut position: usize = 0;
let mut loop_positions: VecDeque<usize> = VecDeque::new();
while cursor < instructions.len() {
match instructions[cursor] {
'<' => {
if position > 0 {
position -= 1;
}
}
'>' => {
if position + 1 == cells.len() {
cells.push(0);
}
position += 1;
}
'-' => {
if cells[position] > 0 {
cells[position] -= 1;
}
}
'+' => {
if cells[position] < 255 {
cells[position] += 1;
}
}
',' => {
cells[position] = get_char(stdin) as u8;
}
'.' => {
put_char(cells[position] as char, stdout);
}
'[' => {
if cells[position] != 0 {
loop_positions.push_back(cursor);
} else {
cursor += 1;
let mut ignore_brace: usize = 1;
while cursor < instructions.len() - 1 && ignore_brace != 0 {
if instructions[cursor] == ']' {
ignore_brace -= 1;
} else if instructions[cursor] == '[' {
ignore_brace += 1;
}
cursor += 1;
}
cursor -= 1;
}
}
']' => {
if cells[position] != 0 {
cursor = *loop_positions.back().unwrap();
} else {
loop_positions.pop_back();
}
}
_ => {}
}
cursor += 1;
}
}
fn put_char(c: char, stdout: &mut dyn Write) {
let buf: [u8; 1] = [c as u8];
stdout.write_all(&buf).unwrap();
}
fn get_char(stdin: &mut dyn Read) -> char {
let mut buf: [u8; 1] = [0];
stdin.read_exact(&mut buf).unwrap();
buf[0] as char
}
#[cfg(test)]
mod tests {
use super::*;
use std::io::BufReader;
use std::{fs, io};
const HELLO_SOURCE_FILE: &str = "test_assets/hello_world.bf";
const BLOCK_SIZE: usize = 30000;
const BUF_SIZE: usize = 1024 * 8;
#[test]
fn hello_world_fixed_block() {
let test_source = fs::read_to_string(HELLO_SOURCE_FILE).unwrap();
let mut stdout: Vec<u8> = Vec::new();
let mut stdin = BufReader::with_capacity(BUF_SIZE, io::stdin());
run_with_fixed_block(&test_source, &mut stdin, &mut stdout, BLOCK_SIZE);
let stdout = String::from_utf8_lossy(stdout.as_slice()).to_string();
assert_eq!(stdout.trim(), "Hello World!");
}
#[test]
fn hello_world_dynamic_block() {
let test_source = fs::read_to_string(HELLO_SOURCE_FILE).unwrap();
let mut stdout: Vec<u8> = Vec::new();
let mut stdin = BufReader::with_capacity(BUF_SIZE, io::stdin());
run_with_dynamic_block(&test_source, &mut stdin, &mut stdout);
let stdout = String::from_utf8_lossy(stdout.as_slice()).to_string();
assert_eq!(stdout.trim(), "Hello World!");
}
}