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}