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