-
-
Notifications
You must be signed in to change notification settings - Fork 155
/
brainfuck.rs
72 lines (65 loc) · 1.99 KB
/
brainfuck.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
//! This is a Brainfuck parser and interpreter
//! Run it with the following command:
//! cargo run --example brainfuck -- examples/sample.bf
use chumsky::prelude::*;
use std::{
env, fs,
io::{self, Read},
};
#[derive(Clone)]
enum Instr {
Invalid,
Left,
Right,
Incr,
Decr,
Read,
Write,
Loop(Vec<Self>),
}
fn parser<'a>() -> impl Parser<'a, &'a str, Vec<Instr>, extra::Err<Simple<'a, char>>> {
use Instr::*;
recursive(|bf| {
choice((
just('<').to(Left),
just('>').to(Right),
just('+').to(Incr),
just('-').to(Decr),
just(',').to(Read),
just('.').to(Write),
))
.or(bf.delimited_by(just('['), just(']')).map(Loop))
.recover_with(via_parser(nested_delimiters('[', ']', [], |_| Invalid)))
// .recover_with(skip_then_retry_until([']']))
.repeated()
.collect()
})
}
const TAPE_LEN: usize = 10_000;
fn execute(ast: &[Instr], ptr: &mut usize, tape: &mut [u8; TAPE_LEN]) {
use Instr::*;
for symbol in ast {
match symbol {
Invalid => unreachable!(),
Left => *ptr = (*ptr + TAPE_LEN - 1).rem_euclid(TAPE_LEN),
Right => *ptr = (*ptr + 1).rem_euclid(TAPE_LEN),
Incr => tape[*ptr] = tape[*ptr].wrapping_add(1),
Decr => tape[*ptr] = tape[*ptr].wrapping_sub(1),
Read => tape[*ptr] = io::stdin().bytes().next().unwrap().unwrap(),
Write => print!("{}", tape[*ptr] as char),
Loop(ast) => {
while tape[*ptr] != 0 {
execute(ast, ptr, tape)
}
}
}
}
}
fn main() {
let src = fs::read_to_string(env::args().nth(1).expect("Expected file argument"))
.expect("Failed to read file");
match parser().parse(src.trim()).into_result() {
Ok(ast) => execute(&ast, &mut 0, &mut [0; TAPE_LEN]),
Err(errs) => errs.into_iter().for_each(|e| println!("{:?}", e)),
};
}