use aott::prelude::*;
#[derive(Clone, Debug)]
enum Instruction {
Left,
Right,
Increment,
Decrement,
Read,
Write,
Loop(Vec<Self>),
}
#[parser]
fn parse(input: &str) -> Vec<Instruction> {
choice((
just('<').to(Instruction::Left),
just('>').to(Instruction::Right),
just('+').to(Instruction::Increment),
just('-').to(Instruction::Decrement),
just(',').to(Instruction::Read),
just('.').to(Instruction::Write),
delimited(just('['), parse, just(']')).map(Instruction::Loop),
))
.repeated()
.parse(input)
}
const TAPE_LENGTH: usize = 10_000;
fn eval(ast: &[Instruction], ptr: &mut usize, tape: &mut [u8; TAPE_LENGTH]) {
use std::io::Read;
use Instruction::*;
for sym in ast {
match sym {
Left => *ptr = (*ptr + TAPE_LENGTH - 1).rem_euclid(TAPE_LENGTH),
Right => *ptr = (*ptr + 1).rem_euclid(TAPE_LENGTH),
Increment => tape[*ptr] = tape[*ptr].wrapping_add(1),
Decrement => tape[*ptr] = tape[*ptr].wrapping_sub(1),
Read => tape[*ptr] = std::io::stdin().bytes().next().unwrap().unwrap(),
Write => print!("{}", tape[*ptr] as char),
Loop(next_ast) => {
while tape[*ptr] != 0 {
eval(next_ast, ptr, tape)
}
}
}
}
}
fn main() {
let input_ = "--[>--->->->++>-<<<<<-------]>--.>---------.>--..+++.>----.>+++++++++.<<.+++.------.<-.>>+.";
let input = input_.as_ref();
println!("Brainfuck input: {input}");
let result = parse.parse_from(&input).into_result();
match result {
Ok(ok) => {
println!("Parsed Brainfuck AST: {ok:#?}");
eval(&ok, &mut 0, &mut [0; TAPE_LENGTH]);
}
Err(err) => println!("Parsing error: {err:?}"),
}
}