brainiac_22ccb748d608358e/
lib.rs

1//! This crate includes an implementation of the Brainfuck esoteric language as a macro rule.
2//!
3//! A couple of limitations apply:
4//! Anytime a series of characters encountered would be interpreted by the Rust compiler as a
5//! symbol, they need to be separated by a space.
6//!
7//! i.e:
8//! ">>" must be "> >"
9//! "->" must be "- >"
10//! "<<" must be "< <"
11//! "<-" must be "< -"
12//! ".." must be ". ."
13//!
14#[macro_export]
15macro_rules! brainfuck {
16    (@start $($sym:tt)*) => {{
17        type Cell = u8;
18        const MAX_SIZE: usize = 30_000;
19
20        fn brainfuck() -> ::std::io::Result<Vec<Cell>> {
21            use ::std::io;
22            use ::std::io::prelude::*;
23
24            fn _err() -> io::Error {
25                io::Error::new(
26                    io::ErrorKind::Other,
27                    String::from("No more input available"),
28                )
29            }
30
31            fn _inc(a: &mut [Cell], i: usize) {
32                a[i] = a[i].wrapping_add(1);
33            }
34
35            fn _dec(a: &mut [Cell], i: usize) {
36                a[i] = a[i].wrapping_sub(1);
37            }
38
39            let _r = &mut io::stdin();
40            let _w = &mut io::stdout();
41
42            let mut _tape: Vec<Cell> = ::std::iter::repeat(0).take(MAX_SIZE).collect();
43            let mut _idx = 0;
44            {
45                let _tape = &mut _tape;
46                brainfuck!(@fuckery (_tape, _idx, _r, _w, _inc, _dec, _err); ($($sym)*));
47            }
48
49            Ok(_tape)
50        }
51        let _ = brainfuck();
52    }};
53
54    (@fuckery $sym:tt; ()) => {};
55
56    // Rules
57    (@fuckery ($tape:expr, $idx:expr, $r:expr, $w:expr, $inc:expr, $dec:expr, $err:expr); (> $($sym:tt)*)) => {
58        $idx = ($idx + 1) % MAX_SIZE;
59        brainfuck!(@fuckery ($tape, $idx, $r, $w, $inc, $dec, $err); ($($sym)*));
60    };
61
62    (@fuckery ($tape:expr, $idx:expr, $r:expr, $w:expr, $inc:expr, $dec:expr, $err:expr); (< $($sym:tt)*)) => {
63        $idx = ($idx + MAX_SIZE - 1) % MAX_SIZE;
64        brainfuck!(@fuckery ($tape, $idx, $r, $w, $inc, $dec, $err); ($($sym)*));
65    };
66
67    (@fuckery ($tape:expr, $idx:expr, $r:expr, $w:expr, $inc:expr, $dec:expr, $err:expr); (+ $($sym:tt)*)) => {
68        $inc($tape, $idx);
69        brainfuck!(@fuckery ($tape, $idx, $r, $w, $inc, $dec, $err); ($($sym)*));
70    };
71
72    (@fuckery ($tape:expr, $idx:expr, $r:expr, $w:expr, $inc:expr, $dec:expr, $err:expr); (- $($sym:tt)*)) => {
73        $dec($tape, $idx);
74        brainfuck!(@fuckery ($tape, $idx, $r, $w, $inc, $dec, $err); ($($sym)*));
75    };
76
77    (@fuckery ($tape:expr, $idx:expr, $r:expr, $w:expr, $inc:expr, $dec:expr, $err:expr); (. $($sym:tt)*)) => {
78        $w.write_all(&$tape[$idx .. $idx + 1])?;
79        brainfuck!(@fuckery ($tape, $idx, $r, $w, $inc, $dec, $err); ($($sym)*));
80    };
81
82    (@fuckery ($tape:expr, $idx:expr, $r:expr, $w:expr, $inc:expr, $dec:expr, $err:expr); (, $($sym:tt)*)) => {
83        match $r.read(&$tape[$idx .. $idx + 1])? {
84            // Unable to read a byte. We're fucked. Return an error
85            Ok(0) => Err($err()),
86
87            // Otherwise, return whether or not we were able to read a single byte
88            v => v
89        }?;
90        brainfuck!(@fuckery ($tape, $idx, $r, $w, $inc, $dec, $err); ($($sym)*));
91    };
92
93    (@fuckery ($tape:expr, $idx:expr, $r:expr, $w:expr, $inc:expr, $dec:expr, $err:expr); ([ $($inner:tt)* ] $($tail:tt)*)) => {
94        while $tape[$idx] != 0 {
95            brainfuck!(@fuckery ($tape, $idx, $r, $w, $inc, $dec, $err); ($($inner)*));
96        }
97        brainfuck!(@fuckery ($tape, $idx, $r, $w, $inc, $dec, $err); ($($tail)*));
98    };
99
100    // Invalid characters are ignored
101    (@fuckery ($tape:expr, $idx:expr, $r:expr, $w:expr, $inc:expr, $dec:expr, $err:expr); ($head:tt $($tail:tt)*)) => {
102        brainfuck!(@fuckery ($tape, $idx, $r, $w, $inc, $dec, $err); ($($tail)*));
103    };
104
105    // Ignore any "initial comment loops"
106    ([$($ignore:tt)*] $($sym:tt)*) => {
107        brainfuck!($($sym)*)
108    };
109
110    ($($sym:tt)*) => {
111        brainfuck!(@start $($sym)*)
112    }
113
114}