use std::str;
pub enum Instructions {
PointerRight(usize),
PointerLeft(usize),
Add(u8),
Sub(u8),
PutChar,
GetChar,
LoopStart(usize),
LoopEnd(usize),
Clear,
ScanLeft,
ScanRight,
}
fn minify(source: &str) -> String {
let mut minified = Vec::new();
for character in source.chars() {
match character {
'>' | '<' | '+' | '-' | ',' | '.' | '[' | ']' => minified.push(character),
_ => {}
}
}
minified.into_iter().collect()
}
fn optimize(source: &str) -> String {
source
.replace("[-]", "c")
.replace("[+]", "c")
.replace("[<]", "l")
.replace("[>]", "r")
}
pub fn compile(source: &str) -> Vec<Instructions> {
let mut program = Vec::new();
let characters = source.as_bytes();
let mut position = 0;
while position != characters.len() {
let instruction = match characters[position] as char {
'>' | '<' => {
let mut value: i32 = 0;
match characters[position] as char {
'>' => value += 1,
'<' => value -= 1,
_ => panic!(),
}
while position < (characters.len() - 1) {
match characters[position + 1] as char {
'>' => value += 1,
'<' => value -= 1,
_ => break,
}
position += 1;
}
match value {
x if x > 0 => Instructions::PointerRight(value as usize),
x if x < 0 => Instructions::PointerLeft(value.abs() as usize),
_ => break,
}
}
'+' | '-' => {
let mut value: i32 = 0;
match characters[position] as char {
'+' => value += 1,
'-' => value -= 1,
_ => panic!(),
}
while position < (characters.len() - 1) {
match characters[position + 1] as char {
'+' => value += 1,
'-' => value -= 1,
_ => break,
}
position += 1;
}
match value {
num if num > 0 => Instructions::Add(value as u8),
num if num < 0 => Instructions::Sub(value.abs() as u8),
_ => break,
}
}
'.' => Instructions::PutChar, ',' => Instructions::GetChar,
'[' => Instructions::LoopStart(0),
']' => Instructions::LoopEnd(0),
'c' => Instructions::Clear,
'l' => Instructions::ScanLeft,
'r' => Instructions::ScanRight,
_ => panic!(),
};
program.push(instruction);
position += 1;
}
for position in 0..program.len() {
match program[position] {
Instructions::LoopStart(_) => {
let mut seek = position;
let mut unmatched = 1;
while unmatched > 0 {
seek += 1;
match &program[seek] {
x if matches!(x, Instructions::LoopStart(_)) => {
unmatched += 1;
}
x if matches!(x, Instructions::LoopEnd(_)) => {
unmatched -= 1;
}
_ => continue,
}
program[position] = Instructions::LoopStart(seek);
}
}
Instructions::LoopEnd(_) => {
let mut seek = position;
let mut unmatched = 1;
while unmatched > 0 {
seek -= 1;
match &program[seek] {
x if matches!(x, Instructions::LoopStart(_)) => {
unmatched -= 1;
}
x if matches!(x, Instructions::LoopEnd(_)) => {
unmatched += 1;
}
_ => continue,
}
program[position] = Instructions::LoopEnd(seek);
}
}
_ => continue,
}
}
program
}
fn execute(program: &[Instructions], print_live: bool) -> String {
let mut memory: [u8; 30_000] = [0; 30_000];
let mut program_pos: usize = 0;
let mut pointer_pos: usize = 0;
let mut output = Vec::new();
while program_pos != program.len() {
match program[program_pos] {
Instructions::PointerRight(num) => pointer_pos += num,
Instructions::PointerLeft(num) => pointer_pos -= num,
Instructions::Add(num) => {
memory[pointer_pos] = memory[pointer_pos].wrapping_add(num);
}
Instructions::Sub(num) => {
memory[pointer_pos] = memory[pointer_pos].wrapping_sub(num);
}
Instructions::LoopStart(pos) => {
if memory[pointer_pos] == 0 {
program_pos = pos;
}
}
Instructions::LoopEnd(pos) => {
if memory[pointer_pos] != 0 {
program_pos = pos;
}
}
Instructions::PutChar => {
output.push(memory[pointer_pos]);
if print_live {
print!("{}", memory[pointer_pos] as char);
}
}
Instructions::GetChar => {
panic!();
}
Instructions::Clear => {
memory[pointer_pos] = 0;
}
Instructions::ScanRight => {
while memory[pointer_pos] != 0 {
pointer_pos += 1;
}
}
Instructions::ScanLeft => {
while memory[pointer_pos] != 0 {
pointer_pos -= 1;
}
}
}
program_pos += 1;
}
String::from_utf8(output).unwrap()
}
pub fn get_code(source: &str) -> Vec<Instructions> {
compile(&optimize(&minify(source)))
}
pub fn evaluate(source: &str, print_live: bool) -> String {
execute(&get_code(&source), print_live)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn hello_world() {
assert_eq!(evaluate("++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>.", false), "Hello World!\n");
}
}