use std::fmt::Display;
use crate::{err, helper::left_right, Cell};
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum Token {
Inc(Cell),
Dec(Cell),
Goto(isize),
LBrack(usize),
RBrack(usize),
Out,
In,
Zero,
Set(Cell),
Add(isize, Cell),
Sub(isize),
Dup(isize, Cell, isize, Cell),
Scan(isize),
End,
#[cfg(any(debug_assertions, feature = "debug"))]
Dump,
}
pub fn parse(input: &str) -> Vec<Token> {
let mut toks = Vec::new();
for ch in input.chars() {
match ch {
'+' => {
if let Some(&Token::Inc(i)) = toks.last() {
toks.pop();
toks.push(Token::Inc(i.wrapping_add(1)));
} else {
toks.push(Token::Inc(1));
}
}
'-' => {
if let Some(&Token::Dec(i)) = toks.last() {
toks.pop();
toks.push(Token::Dec(i.wrapping_add(1)));
} else {
toks.push(Token::Dec(1));
}
}
'>' => {
if let Some(&Token::Goto(i)) = toks.last() {
toks.pop();
toks.push(Token::Goto(i + 1));
} else {
toks.push(Token::Goto(1));
}
}
'<' => {
if let Some(&Token::Goto(i)) = toks.last() {
toks.pop();
toks.push(Token::Goto(i - 1));
} else {
toks.push(Token::Goto(-1));
}
}
'[' => {
toks.push(Token::LBrack(0));
}
']' => {
toks.push(Token::RBrack(0));
}
'.' => {
toks.push(Token::Out);
}
',' => {
toks.push(Token::In);
}
#[cfg(any(debug_assertions, feature = "debug"))]
';' => {
toks.push(Token::Dump);
}
_ => {}
}
}
toks
}
pub fn optimize(toks: &[Token]) -> Vec<Token> {
let mut out = Vec::new();
let mut lbracks = Vec::new();
let mut si = 0;
while si < toks.len() {
let mut tok = toks[si];
if matches!(tok, Token::LBrack(_)) {
let next = toks.get(si + 1);
if matches!(next, Some(Token::Dec(1))) {
let next = toks.get(si + 2);
if matches!(next, Some(Token::RBrack(_))) {
if let Some(Token::Inc(i)) = toks.get(si + 3) {
out.push(Token::Set(*i));
si += 4;
} else {
out.push(Token::Zero);
si += 3;
}
continue;
} else if let Some(Token::Goto(there)) = next {
let next = toks.get(si + 3);
if let Some(Token::Inc(mul)) = next {
if let Some(Token::Goto(back)) = toks.get(si + 4) {
let next = toks.get(si + 5);
if *there == -back {
if matches!(next, Some(Token::RBrack(_))) {
out.push(Token::Add(*there, *mul));
si += 6;
continue;
}
} else if let Some(Token::Inc(mul2)) = next {
if let Some(Token::Goto(bi)) = toks.get(si + 6) {
if bi + there + back == 0
&& matches!(toks.get(si + 7), Some(Token::RBrack(_)))
{
out.push(Token::Dup(*there, *mul, *back, *mul2));
si += 8;
continue;
}
}
}
}
} else if matches!(next, Some(Token::Dec(1))) {
if let Some(Token::Goto(back)) = toks.get(si + 4) {
if *there == -back && matches!(toks.get(si + 5), Some(Token::RBrack(_)))
{
out.push(Token::Sub(*there));
si += 6;
continue;
}
}
}
}
} else if let Some(Token::Goto(there)) = next {
let next = toks.get(si + 2);
if let Some(Token::Inc(mul)) = next {
if let Some(Token::Goto(back)) = toks.get(si + 3) {
let next = toks.get(si + 4);
if *there == -back
&& matches!(next, Some(Token::Dec(1)))
&& matches!(toks.get(si + 5), Some(Token::RBrack(_)))
{
out.push(Token::Add(*there, *mul));
si += 6;
continue;
} else if let Some(Token::Inc(mul2)) = next {
if let Some(Token::Goto(bi)) = toks.get(si + 5) {
if *bi == -(there + back)
&& matches!(toks.get(si + 6), Some(Token::Dec(1)))
&& matches!(toks.get(si + 7), Some(Token::RBrack(_)))
{
out.push(Token::Dup(*there, *mul, *back, *mul2));
si += 8;
continue;
}
}
}
}
} else if matches!(next, Some(Token::Dec(1))) {
if let Some(Token::Goto(back)) = toks.get(si + 3) {
if *there == -back
&& matches!(toks.get(si + 4), Some(Token::Dec(1)))
&& matches!(toks.get(si + 5), Some(Token::RBrack(_)))
{
out.push(Token::Sub(*there));
si += 6;
continue;
}
}
} else if matches!(next, Some(Token::RBrack(_))) {
out.push(Token::Scan(*there));
si += 3;
continue;
}
} else if let Some(Token::RBrack(_)) = next {
out.push(Token::End);
si += 2;
continue;
}
}
match tok {
Token::LBrack(_) => lbracks.push(out.len()),
Token::RBrack(_) => {
let lb = lbracks
.pop()
.unwrap_or_else(|| err("invalid input", "unmatched closing bracket"));
out[lb] = Token::LBrack(out.len());
tok = Token::RBrack(lb);
}
Token::Goto(0) | Token::Inc(0) | Token::Dec(0) => {
si += 1;
continue;
}
_ => {}
}
out.push(tok);
si += 1;
}
if !lbracks.is_empty() {
err("invalid input", "unmatched opening bracket");
}
out
}
impl Display for Token {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Token::Inc(i) => write!(f, "{}", "+".repeat(*i as usize)),
Token::Dec(i) => write!(f, "{}", "-".repeat(*i as usize)),
Token::Goto(i) => write!(f, "{}", left_right(*i)),
Token::Set(i) => write!(f, "[-]{}", "+".repeat(*i as usize)),
Token::LBrack(_) => write!(f, "["),
Token::RBrack(_) => write!(f, "]"),
Token::Out => write!(f, "."),
Token::In => write!(f, ","),
Token::Zero => write!(f, "[-]"),
Token::Add(i, mul) => write!(
f,
"[{}-{}{}]",
left_right(*i),
left_right(-i),
"+".repeat(*mul as usize)
),
Token::Sub(i) => write!(f, "[{}-{}-]", left_right(*i), left_right(-i)),
Token::Dup(i1, mul1, i2, mul2) => write!(
f,
"[-{}{}{}{}{}]",
left_right(*i1),
"+".repeat(*mul1 as usize),
left_right(*i2),
"+".repeat(*mul2 as usize),
left_right(-(i1 + i2)),
),
Token::Scan(i) => write!(f, "[{}]", left_right(*i)),
Token::End => write!(f, "[]"),
#[cfg(any(debug_assertions, feature = "debug"))]
Token::Dump => write!(f, ";"),
}
}
}