#[derive(Debug, Copy, Clone)]
pub enum Op {
MovePointer(isize),
Increment(i32),
JumpIfZero(usize),
JumpBack(usize),
Read,
Write,
SetToZero,
}
fn compact(mut program: Vec<Op>) -> Vec<Op> {
let mut compacted;
while !program.is_empty() {
compacted = Vec::new();
let mut previous_op = program[0];
for op in program.iter().skip(1) {
match (previous_op, op) {
(Op::MovePointer(n), Op::MovePointer(m)) => {
previous_op = Op::MovePointer(n + m);
}
(Op::Increment(n), Op::Increment(m)) => {
previous_op = Op::Increment(n + m);
}
_ => {
match previous_op {
Op::MovePointer(0) | Op::Increment(0) => (),
_ => compacted.push(previous_op),
}
previous_op = *op;
}
}
}
match previous_op {
Op::MovePointer(0) | Op::Increment(0) => (),
_ => compacted.push(previous_op),
}
if compacted.len() == program.len() {
break;
}
program = compacted;
}
program
}
fn optimize_set_to_zero(mut program: Vec<Op>) -> Vec<Op> {
let mut i = 0;
while i + 2 < program.len() {
match program[i..i + 3] {
[Op::JumpIfZero(_), Op::Increment(-1), Op::JumpBack(_)] => {
program[i] = Op::SetToZero;
program.remove(i + 1);
program.remove(i + 1);
}
_ => i += 1,
}
}
i = 0;
while i + 1 < program.len() {
match program[i..i + 2] {
[Op::Increment(_), Op::SetToZero] => {
program[i] = Op::SetToZero;
program.remove(i + 1);
}
_ => i += 1,
}
}
program
}
fn match_jumps(mut program: Vec<Op>) -> Result<Vec<Op>, &'static str> {
let mut jumps = Vec::new();
for i in 0..program.len() {
match program[i] {
Op::JumpIfZero(_) => jumps.push(i),
Op::JumpBack(_) => {
if jumps.is_empty() {
return Err("Unmatched ']'");
}
let jz = jumps.pop().unwrap();
program[jz] = Op::JumpIfZero(i + 1);
program[i] = Op::JumpBack(jz);
}
_ => (),
}
}
if jumps.is_empty() {
Ok(program)
} else {
Err("Unmatched '['")
}
}
pub fn finalize(program: Vec<Op>) -> Result<Vec<Op>, &'static str> {
let program = compact(program);
let program = optimize_set_to_zero(program);
match_jumps(program)
}