1use std::collections::HashMap;
2use std::str;
3
4#[derive(Debug, PartialEq)]
5enum Token {
6 LoopStart,
7 LoopEnd,
8 Increment,
9 Decrement,
10 ShiftR,
11 ShiftL,
12 Print,
13}
14
15pub struct Program {
16 pointer: i32,
17 tape: HashMap<i32, u8>,
18 program: Vec<Token>,
19 bracket_map: HashMap<usize, usize>,
20 index: usize,
21}
22
23impl Program {
24 pub fn new(source: &str) -> Program {
25 let (program, bracket_map) = parse(source);
26 Program {
27 pointer: 0,
28 index: 0,
29 tape: HashMap::new(),
30 program,
31 bracket_map,
32 }
33 }
34
35 pub fn execute(&mut self) -> String {
36 let mut output: Vec<u8> = Vec::new();
37
38 while self.index < self.program.len() {
39 let ins = self.program.get(self.index).unwrap();
40
41 let val = match self.tape.get(&self.pointer) {
42 Some(v) => *v,
43 None => 0,
44 };
45
46 match ins {
47 Token::ShiftR => self.pointer += 1,
48 Token::ShiftL => self.pointer -= 1,
49 Token::Increment => {
50 self.tape.insert(self.pointer, val + 1);
51 }
52 Token::Decrement => {
53 self.tape.insert(self.pointer, val - 1);
54 }
55 Token::Print => output.push(val),
56 Token::LoopStart => {
57 if val == 0 {
58 self.index = *self.bracket_map.get(&self.index).unwrap();
59 continue;
60 }
61 }
62 Token::LoopEnd => {
63 if val != 0 {
64 self.index = *self.bracket_map.get(&self.index).unwrap();
65 continue;
66 }
67 }
68 }
69
70 self.index += 1;
71 }
72
73 String::from(str::from_utf8(&output[..]).unwrap())
74 }
75}
76
77fn parse(source: &str) -> (Vec<Token>, HashMap<usize, usize>) {
78 let mut brackets: Vec<usize> = Vec::new();
79 let mut bracket_map = HashMap::new();
80
81 let program = source
82 .chars()
83 .enumerate()
84 .map(|(i, c)| match c {
85 '[' => {
86 brackets.push(i);
87 Ok(Token::LoopStart)
88 }
89 ']' => {
90 match brackets.pop() {
91 Some(idx) => {
92 bracket_map.insert(idx, i);
93 bracket_map.insert(i, idx);
94 }
95 None => panic!("Unmatched brackets!"),
96 };
97
98 Ok(Token::LoopEnd)
99 }
100 '+' => Ok(Token::Increment),
101 '-' => Ok(Token::Decrement),
102 '>' => Ok(Token::ShiftR),
103 '<' => Ok(Token::ShiftL),
104 '.' => Ok(Token::Print),
105 _ => Err(()),
106 })
107 .filter_map(Result::ok)
108 .collect();
109
110 (program, bracket_map)
111}
112
113#[cfg(test)]
114mod tests {
115 use super::*;
116
117 #[test]
118 fn test_print_h() {
119 let source = "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.";
120 let mut prog = Program::new(source);
121
122 assert_eq!("H", prog.execute());
123 }
124
125 #[test]
126 fn test_print_hello_world() {
127 let source = "++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.";
128 let mut prog = Program::new(source);
129
130 assert_eq!("Hello World!\n", prog.execute());
131 }
132}