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}