mini_brainfuck/
brainfuck.rs1mod errors;
15
16use std::io::Read;
17use std::str::Chars;
18
19use crate::errors as bf_error;
20
21#[derive(Clone)]
22pub struct BFConfig {
23 pub(crate) debug: i32,
25
26 pub(crate) exit_support: bool,
28}
29
30pub fn default_bf_config() -> BFConfig {
31 BFConfig {
32 debug: 0,
33 exit_support: false,
34 }
35}
36
37fn do_left_bracket(chars: Chars, index: i32) -> i32 {
38 let mut ix: i32 = index;
39 let mut open = 1;
40 while open != 0 {
41 ix += 1;
42
43 match chars.clone().nth(ix.try_into().unwrap()).unwrap() {
44 '[' => open += 1,
45 ']' => open -= 1,
46 _ => (),
47 }
48 }
49
50 return ix;
51}
52
53fn do_right_bracket(chars: Chars, index: i32) -> i32 {
54 let mut ix: i32 = index;
55 let mut close = 1;
56 while close != 0 {
57 ix -= 1;
58
59 if ix >= chars.clone().count().try_into().unwrap() {
60 bf_error::error(
61 "Syntax error".to_string(),
62 "couldn't find next matching ']'".to_string(),
63 );
64 }
65
66 match chars.clone().nth(ix.try_into().unwrap()).unwrap() {
67 '[' => close -= 1,
68 ']' => close += 1,
69 _ => (),
70 }
71 }
72
73 return ix;
74}
75
76pub fn brainfuck(programm: String, config: BFConfig) -> [u8; 3000] {
77 let mut cells: [u8; 3000] = [0; 3000];
78 let mut max: i32 = 1;
79 let possition: &mut usize = &mut 0;
80 let chars: Chars = programm.chars();
81
82 let debug: i32 = config.debug;
84 let exit_support: bool = config.exit_support;
85
86 let mut index: i32 = 0;
87 while index < chars.clone().count().try_into().unwrap() {
88 let cur_char = chars.clone().nth(index.try_into().unwrap()).unwrap();
89
90 match cur_char {
91 '#' => {
96 if debug > 0 {
97 println!("{}", cells[*possition])
98 }
99 }
100
101 '!' => {
106 if exit_support {
107 std::process::exit(2);
108 }
109 }
110
111 '+' => {
115 let mut cell: u8 = cells[*possition];
116
117 if cell == 255 {
118 cell = 0;
119 } else {
120 cell = cell.wrapping_add(1);
121 }
122
123 cells[*possition] = cell;
124 }
125
126 '-' => {
128 let mut cell: u8 = cells[*possition];
129
130 if cell == 0 {
131 cell = 255;
132 } else {
133 cell = cell.wrapping_sub(1);
134 }
135
136 cells[*possition] = cell;
137 }
138
139 '>' => {
141 if *possition as i32 == 2999 {
142 *possition = 0
143 } else {
144 *possition += 1
145 }
146 }
147
148 '<' => {
150 if (*possition as i32) == 0 {
151 *possition = 2999;
152 } else {
153 *possition =
154 *&mut ((*possition as usize).checked_sub(1)).unwrap_or_default() as usize;
155 }
156 }
157
158 '.' => print!("{}", cells[*possition] as char),
160
161 ',' => {
163 let mut buf = [0; 1];
166
167 match std::io::stdin().read_exact(&mut buf) {
169 Ok(_) => cells[*possition] = buf[0], Err(_) => {
171 bf_error::error(
172 "IO error".to_string(),
173 "Error while trying to get an input from stdin".to_string(),
174 );
175 }
176 }
177 }
178
179 '[' => {
181 if cells[*possition] == 0 {
182 index = do_left_bracket(chars.clone(), index)
183 }
184 }
185
186 ']' => {
188 if cells[*possition] != 0 {
189 index = do_right_bracket(chars.clone(), index)
190 }
191 }
192
193 _ => (),
197 }
198
199 index += 1;
200
201 if (*possition as i32) + 1 > max {
202 max = (*possition as i32) + 1;
203 }
204 }
205
206 if debug > 1 {
207 println!("\n\n=== CELLS ===");
208 for i in 0..max {
209 println!("c[{}]: {}", i, cells[i as usize]);
210 }
211 }
212
213 return cells;
214}
215
216#[cfg(test)]
217mod tests {
218 use super::*;
219
220 #[test]
221 fn cell_add() {
222 let code: String = "+".to_string();
223 let config: BFConfig = BFConfig {
224 ..default_bf_config()
225 };
226 let cells = brainfuck(code, config);
227 let mut res: [u8; 3000] = [0; 3000];
228
229 res[0] = 1;
230 assert_eq!(res, cells);
231 }
232
233 #[test]
234 fn cell_sub() {
235 let code: String = "-".to_string();
236 let config: BFConfig = BFConfig {
237 ..default_bf_config()
238 };
239
240 let cells = brainfuck(code, config);
241 let mut res: [u8; 3000] = [0; 3000];
242
243 res[0] = 255;
244 assert_eq!(res, cells);
245 }
246
247 #[test]
248 fn cell_sub_plus() {
249 let code: String = "++-".to_string();
250 let config: BFConfig = BFConfig {
251 ..default_bf_config()
252 };
253
254 let cells = brainfuck(code, config);
255 let mut res: [u8; 3000] = [0; 3000];
256
257 res[0] = 1;
258 assert_eq!(res, cells);
259 }
260
261 #[test]
262 fn cell_left() {
263 let code: String = ">+".to_string();
264 let config: BFConfig = BFConfig {
265 ..default_bf_config()
266 };
267
268 let cells = brainfuck(code, config);
269 let mut res: [u8; 3000] = [0; 3000];
270
271 res[1] = 1;
272 assert_eq!(res, cells);
273 }
274
275 #[test]
276 fn cell_right() {
277 let code: String = "><+".to_string();
278 let config: BFConfig = BFConfig {
279 ..default_bf_config()
280 };
281
282 let cells = brainfuck(code, config);
283 let mut res: [u8; 3000] = [0; 3000];
284
285 res[0] = 1;
286 assert_eq!(res, cells);
287 }
288
289 #[test]
290 fn cell_right_end() {
291 let code: String = "<+".to_string();
292 let config: BFConfig = BFConfig {
293 ..default_bf_config()
294 };
295
296 let cells = brainfuck(code, config);
297 let mut res: [u8; 3000] = [0; 3000];
298
299 res[2999] = 1;
300 assert_eq!(res, cells);
301 }
302
303 #[test]
304 fn cell_left_end() {
305 let code: String = "<>+".to_string();
306 let config: BFConfig = BFConfig {
307 ..default_bf_config()
308 };
309
310 let cells = brainfuck(code, config);
311 let mut res: [u8; 3000] = [0; 3000];
312
313 res[0] = 1;
314 assert_eq!(res, cells);
315 }
316
317 #[test]
318 fn cell_loop() {
319 let code: String = ">+++++[<+>-]".to_string();
323 let config: BFConfig = BFConfig {
324 ..default_bf_config()
325 };
326
327 let cells = brainfuck(code, config);
328 let mut res: [u8; 3000] = [0; 3000];
329
330 res[0] = 5;
331 assert_eq!(res, cells);
332 }
333
334 #[test]
335 fn comments() {
336 let code: String = "Lets add one (+) move to left (>) and add 2 (++)".to_string();
337 let config: BFConfig = BFConfig {
338 ..default_bf_config()
339 };
340
341 let cells = brainfuck(code, config);
342 let mut res: [u8; 3000] = [0; 3000];
343
344 res[0] = 1;
345 res[1] = 2;
346 assert_eq!(res, cells);
347 }
348}