bflib_proc_macro/
lib.rs

1use std::str::FromStr;
2use proc_macro::TokenStream;
3
4fn transpiler(input: String) -> String {
5    let mut res = String::from(r##"
6        ::bflib::BrainfuckBlock::new(&|pc: &mut usize, mem: &mut Vec<u8>| {
7            if mem.len() <= *pc {
8                mem.append(&mut vec![0; *pc - mem.len() + 1]);
9            }
10    "##);
11    let mut cache = (' ', 0);
12    for c in input.chars().filter(|c| "><+-,.[]".contains(*c)) {
13        if c == cache.0 {
14            cache.1 += 1;
15            continue;
16        }
17        {
18            match cache.0 {
19                '>' => {
20                    res.push_str(&format!("*pc += {};", cache.1));
21                    res.push_str(r##"
22                        if mem.len() <= *pc {
23                            mem.append(&mut vec![0; *pc - mem.len() + 1]);
24                        }
25                    "##);
26                }
27                '<' => {
28                    res.push_str(&format!("if *pc < {0} {{ panic!(\"BrainFuck Program Counter reduced to below zero !!!\"); }} else {{ *pc -= {0}; }}", cache.1));
29                }
30                '+' => {
31                    res.push_str(&format!("mem[*pc] = mem[*pc].wrapping_add({});", cache.1));
32                }
33                '-' => {
34                    res.push_str(&format!("mem[*pc] = mem[*pc].wrapping_sub({});", cache.1));
35                }
36                _ => {}
37            }
38            cache = (' ', 0);
39        }
40        match c {
41            '>' | '<' | '+' | '-' => {
42                cache = (c, 1);
43            }
44            '.' => {
45                res.push_str("print!(\"{}\", mem[*pc] as char);");
46            },
47            ',' => {
48                res.push_str(r##"
49                {
50                    let mut buffer = [0; 1];
51                    std::io::Read::read_exact(&mut std::io::stdin(), &mut buffer).unwrap();
52                    mem[*pc] = buffer[0];
53                }
54                "##);
55            },
56            '[' => {
57                res.push_str("while mem[*pc] != 0 {");
58            },
59            ']' => {
60                res.push_str("}");
61            },
62            _ => {}
63        }
64    }
65    {
66        match cache.0 {
67            '>' => {
68                res.push_str(&format!("*pc += {};", cache.1));
69                res.push_str(r##"
70                    if mem.len() <= *pc {
71                        mem.append(&mut vec![0; *pc - mem.len() + 1]);
72                    }
73                "##);
74            }
75            '<' => {
76                res.push_str(&format!("if *pc < {0} {{ panic!(\"BrainFuck Program Counter reduced to below zero !!!\"); }} else {{ *pc -= {0}; }}", cache.1));
77            }
78            '+' => {
79                res.push_str(&format!("mem[*pc] = mem[*pc].wrapping_add({});", cache.1));
80            }
81            '-' => {
82                res.push_str(&format!("mem[*pc] = mem[*pc].wrapping_sub({});", cache.1));
83            }
84            _ => {}
85        }
86    }
87    res.push_str("})");
88    res
89}
90
91
92/// inline Brainfuck code  
93/// 
94/// Examples:
95/// 
96/// 1. Hello World
97///    (run on dropping)
98///    ```rust
99///    brain_fuck!(
100///        ++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.
101///        >---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.
102///    );
103///    ```
104/// 2. using `into` method to obtain `(pc: usize, mem: Vec<u8>)` after running
105///    (run on `into` calling)
106///    ```rust
107///    let (pc, mem) = brain_fuck!(
108///        ++++++++[>+>++++<<-]>++>>+<[-[>>+<<-]+>>]>+[
109///            -<<<[
110///                ->[+[-]+>++>>>-<<]<[<]>>++++++[<<+++++>>-]+<<++.[-]<<
111///            ]>.>+[>>]>+
112///        ]
113///    ).into();
114///    println!("{:?}", (pc, mem));
115///    ```
116/// 3. use `env` method to set _Program Counter_ `pc` and _Memory_ `mem` for brainfuck codeblock
117///    (run on dropping)
118///    ```rust
119///    brain_fuck!(
120///        [.>]
121///    ).env(0, vec![79, 75, 10]);
122///    ```
123/// 4. Altogether
124///    (run on `into` calling)
125///    ```rust
126///    let (pc, mem) = brain_fuck!(
127///        [.>]
128///    ).env(0, vec![72, 101, 108, 108, 79, 119, 104, 97, 116, 65, 115, 10]).into();
129///    println!("{:?}", (pc, mem));
130///    ```
131#[proc_macro]
132pub fn brain_fuck(_item: TokenStream) -> TokenStream {
133    let input = _item.to_string();
134    TokenStream::from_str(&transpiler(input)).unwrap()
135}