Skip to main content

iaith/
lib.rs

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}