bf_fast/
lib.rs

1use std::str;
2
3pub enum Instructions {
4    PointerRight(usize),
5    PointerLeft(usize),
6
7    Add(u8),
8    Sub(u8),
9
10    PutChar,
11    GetChar,
12
13    LoopStart(usize),
14    LoopEnd(usize),
15
16    Clear,
17
18    ScanLeft,
19    ScanRight,
20}
21
22fn minify(source: &str) -> String {
23    let mut minified = Vec::new();
24    for character in source.chars() {
25        match character {
26            '>' | '<' | '+' | '-' | ',' | '.' | '[' | ']' => minified.push(character),
27            _ => {}
28        }
29    }
30    minified.into_iter().collect()
31}
32
33fn optimize(source: &str) -> String {
34    source
35        .replace("[-]", "c")
36        .replace("[+]", "c")
37        .replace("[<]", "l")
38        .replace("[>]", "r")
39}
40
41pub fn compile(source: &str) -> Vec<Instructions> {
42    let mut program = Vec::new();
43    let characters = source.as_bytes();
44    let mut position = 0;
45    while position != characters.len() {
46        let instruction = match characters[position] as char {
47            '>' | '<' => {
48                // This code compresses both pointer increase and decrease into one instruction
49                let mut value: i32 = 0;
50                match characters[position] as char {
51                    '>' => value += 1,
52                    '<' => value -= 1,
53                    _ => panic!(),
54                }
55                while position < (characters.len() - 1) {
56                    match characters[position + 1] as char {
57                        '>' => value += 1,
58                        '<' => value -= 1,
59                        _ => break,
60                    }
61                    position += 1;
62                }
63                match value {
64                    x if x > 0 => Instructions::PointerRight(value as usize),
65                    x if x < 0 => Instructions::PointerLeft(value.abs() as usize),
66                    _ => break,
67                }
68            }
69            '+' | '-' => {
70                // Same as before but with memory increase and decrease
71                let mut value: i32 = 0;
72                match characters[position] as char {
73                    '+' => value += 1,
74                    '-' => value -= 1,
75                    _ => panic!(),
76                }
77                while position < (characters.len() - 1) {
78                    match characters[position + 1] as char {
79                        '+' => value += 1,
80                        '-' => value -= 1,
81                        _ => break,
82                    }
83                    position += 1;
84                }
85                match value {
86                    num if num > 0 => Instructions::Add(value as u8),
87                    num if num < 0 => Instructions::Sub(value.abs() as u8),
88                    _ => break,
89                }
90            }
91            '.' => Instructions::PutChar, // These can be converted directly
92            ',' => Instructions::GetChar,
93            '[' => Instructions::LoopStart(0),
94            ']' => Instructions::LoopEnd(0),
95            'c' => Instructions::Clear,
96            'l' => Instructions::ScanLeft,
97            'r' => Instructions::ScanRight,
98            _ => panic!(),
99        };
100        program.push(instruction);
101        position += 1;
102    }
103
104    // Match brackets, doing this now makes runtime faster
105    for position in 0..program.len() {
106        match program[position] {
107            Instructions::LoopStart(_) => {
108                let mut seek = position;
109                let mut unmatched = 1;
110                while unmatched > 0 {
111                    seek += 1;
112                    match &program[seek] {
113                        x if matches!(x, Instructions::LoopStart(_)) => {
114                            unmatched += 1;
115                        }
116                        x if matches!(x, Instructions::LoopEnd(_)) => {
117                            unmatched -= 1;
118                        }
119                        _ => continue,
120                    }
121                    program[position] = Instructions::LoopStart(seek);
122                }
123            }
124            Instructions::LoopEnd(_) => {
125                let mut seek = position;
126                let mut unmatched = 1;
127                while unmatched > 0 {
128                    seek -= 1;
129                    match &program[seek] {
130                        x if matches!(x, Instructions::LoopStart(_)) => {
131                            unmatched -= 1;
132                        }
133                        x if matches!(x, Instructions::LoopEnd(_)) => {
134                            unmatched += 1;
135                        }
136                        _ => continue,
137                    }
138                    program[position] = Instructions::LoopEnd(seek);
139                }
140            }
141            _ => continue,
142        }
143    }
144    program
145}
146
147fn execute(program: &[Instructions], print_live: bool) -> String {
148    let mut memory: [u8; 30_000] = [0; 30_000];
149    let mut program_pos: usize = 0;
150    let mut pointer_pos: usize = 0;
151    let mut output = Vec::new();
152
153    while program_pos != program.len() {
154        match program[program_pos] {
155            Instructions::PointerRight(num) => pointer_pos += num,
156
157            Instructions::PointerLeft(num) => pointer_pos -= num,
158
159            Instructions::Add(num) => {
160                memory[pointer_pos] = memory[pointer_pos].wrapping_add(num);
161            }
162
163            Instructions::Sub(num) => {
164                memory[pointer_pos] = memory[pointer_pos].wrapping_sub(num);
165            }
166
167            Instructions::LoopStart(pos) => {
168                if memory[pointer_pos] == 0 {
169                    program_pos = pos;
170                }
171            }
172
173            Instructions::LoopEnd(pos) => {
174                if memory[pointer_pos] != 0 {
175                    program_pos = pos;
176                }
177            }
178
179            Instructions::PutChar => {
180                output.push(memory[pointer_pos]);
181                if print_live {
182                    print!("{}", memory[pointer_pos] as char);
183                }
184            }
185
186            Instructions::GetChar => {
187                panic!();
188            }
189
190            Instructions::Clear => {
191                memory[pointer_pos] = 0;
192            }
193
194            Instructions::ScanRight => {
195                while memory[pointer_pos] != 0 {
196                    pointer_pos += 1;
197                }
198            }
199
200            Instructions::ScanLeft => {
201                while memory[pointer_pos] != 0 {
202                    pointer_pos -= 1;
203                }
204            }
205        }
206        program_pos += 1;
207    }
208    String::from_utf8(output).unwrap()
209}
210
211pub fn get_code(source: &str) -> Vec<Instructions> {
212    compile(&optimize(&minify(source)))
213}
214
215pub fn evaluate(source: &str, print_live: bool) -> String {
216    execute(&get_code(&source), print_live)
217}
218
219#[cfg(test)]
220mod tests {
221    use super::*;
222    #[test]
223    fn hello_world() {
224        assert_eq!(evaluate("++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>.", false), "Hello World!\n");
225    }
226}