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 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 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, ',' => 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 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}