brainfuck/
brainfuck.rs

1use cypress::prelude::*;
2
3// See: https://gist.github.com/roachhd/dce54bec8ba55fb17d3a for an overview of BrainFuck
4// > = increases memory pointer, or moves the pointer to the right 1 block.
5// < = decreases memory pointer, or moves the pointer to the left 1 block.
6// + = increases value stored at the block pointed to by the memory pointer
7// - = decreases value stored at the block pointed to by the memory pointer
8// [ = like c while(cur_block_value != 0) loop.
9// ] = if block currently pointed to's value is not zero, jump back to [
10// , = like c getchar(). input 1 character.
11// . = like c putchar(). print 1 character to the console
12
13#[derive(Debug, Clone, PartialEq)]
14enum Instruction {
15    Left,
16    Right,
17    Increment,
18    Decrement,
19    Read,
20    Write,
21    Loop(Vec<Self>),
22}
23
24fn main() {
25    let parser = recursive(|expr| {
26        // Parser for a single instruction (not the whole program)
27        let instr = choice!(
28            select! {
29                '<' => Instruction::Left,
30                '>' => Instruction::Right,
31                '+' => Instruction::Increment,
32                '-' => Instruction::Decrement,
33                ',' => Instruction::Read,
34                '.' => Instruction::Write,
35            },
36            // Now inside Loop, parse *many instructions* again — not the full `expr`
37            expr.many()
38                .between(just('['), just(']'))
39                .map(Instruction::Loop)
40        );
41
42        Box::new(instr)
43    })
44    .many()
45    .until_end();
46
47    let input = b"+++++[>>+<<-]".into_input();
48
49    let expected_bf = vec![
50        Instruction::Increment,
51        Instruction::Increment,
52        Instruction::Increment,
53        Instruction::Increment,
54        Instruction::Increment,
55        Instruction::Loop(vec![
56            Instruction::Right,
57            Instruction::Right,
58            Instruction::Increment,
59            Instruction::Left,
60            Instruction::Left,
61            Instruction::Decrement,
62        ]),
63    ];
64
65    match parser.parse(input) {
66        Ok(PSuccess {
67            val: actual_bf,
68            rest: _,
69        }) => {
70            println!("{:?}", actual_bf);
71            assert_eq!(actual_bf, expected_bf)
72        }
73        Err(e) => println!("{}", e),
74    }
75}