brim/
token.rs

1use std::fmt::Display;
2
3use crate::{err, helper::left_right, Cell};
4
5/// An optimized token; it may represent more than one brain* instruction.
6#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
7pub enum Token {
8    /// Equivalent to `'+' * n`.
9    Inc(Cell),
10    /// Equivalent to `'-' * n`.
11    Dec(Cell),
12    /// Equivalent to `'>' * n` if `n > 0`, else `'<' * n`.
13    Goto(isize),
14    /// A left bracket. The value is the index of the corresponding right
15    /// bracket.
16    ///
17    /// *Note*: [`parse`] leaves this set to 0, since [`optimize`] will change
18    /// the indices anyways.
19    LBrack(usize),
20    /// A right bracket. The value is the index of the corresponding left
21    /// bracket.
22    ///
23    /// *Note*: [`parse`] leaves this set to 0, since [`optimize`] will change
24    /// the indices anyways.
25    RBrack(usize),
26    /// Equivalent to `'.'`.
27    Out,
28    /// Equivalent to `','`.
29    In,
30
31    /// Macro-optimization. Equivalent to `[-]`.
32    Zero,
33    /// Macro-optimization. Equivalent to `[-]+`.
34    Set(Cell),
35    /// Macro-optimization. Equivalent to `[->+<]`.
36    Add(isize, Cell),
37    /// Macro-optimization. Equivalent to `[->-<]`.
38    Sub(isize),
39    /// Macro-optimization. Equivalent to `[->+>+<<]`.
40    Dup(isize, Cell, isize, Cell),
41    /// Macro-optimization. Equivalent to `[>>>]`.
42    Scan(isize),
43
44    /// Macro-optimization. Equivalent to `[]`.
45    End,
46
47    /// Equivalent to `';'`. Only available if in debug mode, or if feature
48    /// flag `debug` is enabled.
49    #[cfg(any(debug_assertions, feature = "debug"))]
50    Dump,
51}
52
53/// Parse brain* input into [`Token`]s. Groups together [`Inc`](Token::Inc) and
54/// [`Dec`](Token::Dec) instructions, as well as converting `<` and `>` to
55/// [`Goto`](Token::Goto).
56///
57/// ***Note:*** the output is *not yet valid!* Each [`LBrack`](Token::LBrack)
58/// and [`RBrack`](Token::RBrack) still needs to be set to its match, i.e. via
59/// [`optimize`].
60pub fn parse(input: &str) -> Vec<Token> {
61    // let input = input.to_string();
62    let mut toks = Vec::new();
63
64    for ch in input.chars() {
65        match ch {
66            '+' => {
67                if let Some(&Token::Inc(i)) = toks.last() {
68                    toks.pop();
69                    toks.push(Token::Inc(i.wrapping_add(1)));
70                } else {
71                    toks.push(Token::Inc(1));
72                }
73            }
74            '-' => {
75                if let Some(&Token::Dec(i)) = toks.last() {
76                    toks.pop();
77                    toks.push(Token::Dec(i.wrapping_add(1)));
78                } else {
79                    toks.push(Token::Dec(1));
80                }
81            }
82            '>' => {
83                if let Some(&Token::Goto(i)) = toks.last() {
84                    toks.pop();
85                    toks.push(Token::Goto(i + 1));
86                } else {
87                    toks.push(Token::Goto(1));
88                }
89            }
90            '<' => {
91                if let Some(&Token::Goto(i)) = toks.last() {
92                    toks.pop();
93                    toks.push(Token::Goto(i - 1));
94                } else {
95                    toks.push(Token::Goto(-1));
96                }
97            }
98            '[' => {
99                toks.push(Token::LBrack(0));
100            }
101            ']' => {
102                toks.push(Token::RBrack(0));
103            }
104            '.' => {
105                toks.push(Token::Out);
106            }
107            ',' => {
108                toks.push(Token::In);
109            }
110
111            #[cfg(any(debug_assertions, feature = "debug"))]
112            ';' => {
113                toks.push(Token::Dump);
114            }
115
116            _ => {}
117        }
118    }
119
120    toks
121}
122
123/// Performs macro-optimizations, and sets the final indices for each bracket.
124pub fn optimize(toks: &[Token]) -> Vec<Token> {
125    let mut out = Vec::new();
126    let mut lbracks = Vec::new();
127
128    let mut si = 0;
129    while si < toks.len() {
130        let mut tok = toks[si];
131
132        if matches!(tok, Token::LBrack(_)) {
133            let next = toks.get(si + 1);
134
135            // [
136            // Zero / Set / Add / Sub / Dup
137            if matches!(next, Some(Token::Dec(1))) {
138                let next = toks.get(si + 2);
139
140                // [-
141                // Zero / Set
142                if matches!(next, Some(Token::RBrack(_))) {
143                    // [-]
144                    if let Some(Token::Inc(i)) = toks.get(si + 3) {
145                        // [-]+++
146                        out.push(Token::Set(*i));
147
148                        si += 4;
149                    } else {
150                        out.push(Token::Zero);
151
152                        si += 3;
153                    }
154
155                    continue;
156
157                // [-
158                // Add / Sub / Dup
159                } else if let Some(Token::Goto(there)) = next {
160                    let next = toks.get(si + 3);
161
162                    // [->
163                    // Add / Dup
164                    if let Some(Token::Inc(mul)) = next {
165                        if let Some(Token::Goto(back)) = toks.get(si + 4) {
166                            let next = toks.get(si + 5);
167
168                            // [->+ ><
169                            // Add
170                            if *there == -back {
171                                // [->+<
172                                if matches!(next, Some(Token::RBrack(_))) {
173                                    // [->+<]
174                                    out.push(Token::Add(*there, *mul));
175
176                                    si += 6;
177                                    continue;
178                                }
179
180                            // [->+ ><
181                            // Dup
182                            } else if let Some(Token::Inc(mul2)) = next {
183                                // [->+ >< +
184                                if let Some(Token::Goto(bi)) = toks.get(si + 6) {
185                                    // [->+ >< + ><
186                                    if bi + there + back == 0
187                                        // [->+>+<<]
188                                        && matches!(toks.get(si + 7), Some(Token::RBrack(_)))
189                                    {
190                                        out.push(Token::Dup(*there, *mul, *back, *mul2));
191
192                                        si += 8;
193                                        continue;
194                                    }
195                                }
196                            }
197                        }
198
199                    // Sub
200                    } else if matches!(next, Some(Token::Dec(1))) {
201                        if let Some(Token::Goto(back)) = toks.get(si + 4) {
202                            if *there == -back && matches!(toks.get(si + 5), Some(Token::RBrack(_)))
203                            {
204                                out.push(Token::Sub(*there));
205
206                                si += 6;
207                                continue;
208                            }
209                        }
210                    }
211                }
212
213            // [
214            // Add / Sub / Dup / Scan
215            } else if let Some(Token::Goto(there)) = next {
216                let next = toks.get(si + 2);
217
218                // [>
219                // Add / Dup
220                if let Some(Token::Inc(mul)) = next {
221                    if let Some(Token::Goto(back)) = toks.get(si + 3) {
222                        let next = toks.get(si + 4);
223
224                        // [>+ ><
225                        // Add
226                        if *there == -back
227                            && matches!(next, Some(Token::Dec(1)))
228                            && matches!(toks.get(si + 5), Some(Token::RBrack(_)))
229                        {
230                            // [>+<-]
231                            out.push(Token::Add(*there, *mul));
232
233                            si += 6;
234                            continue;
235
236                        // [>+ ><
237                        // Dup
238                        } else if let Some(Token::Inc(mul2)) = next {
239                            // [>+ >< +
240                            if let Some(Token::Goto(bi)) = toks.get(si + 5) {
241                                if *bi == -(there + back)
242                                    // [>+>+<<
243                                    && matches!(toks.get(si + 6), Some(Token::Dec(1)))
244                                    // [>+>+<<-
245                                    && matches!(toks.get(si + 7), Some(Token::RBrack(_)))
246                                // [>+>+<<-]
247                                {
248                                    out.push(Token::Dup(*there, *mul, *back, *mul2));
249
250                                    si += 8;
251                                    continue;
252                                }
253                            }
254                        }
255                    }
256
257                // Sub
258                } else if matches!(next, Some(Token::Dec(1))) {
259                    if let Some(Token::Goto(back)) = toks.get(si + 3) {
260                        if *there == -back
261                            && matches!(toks.get(si + 4), Some(Token::Dec(1)))
262                            && matches!(toks.get(si + 5), Some(Token::RBrack(_)))
263                        {
264                            out.push(Token::Sub(*there));
265
266                            si += 6;
267                            continue;
268                        }
269                    }
270
271                // Scan
272                } else if matches!(next, Some(Token::RBrack(_))) {
273                    out.push(Token::Scan(*there));
274
275                    si += 3;
276                    continue;
277                }
278
279            // End
280            } else if let Some(Token::RBrack(_)) = next {
281                out.push(Token::End);
282
283                si += 2;
284                continue;
285            }
286        }
287
288        match tok {
289            Token::LBrack(_) => lbracks.push(out.len()),
290            Token::RBrack(_) => {
291                let lb = lbracks
292                    .pop()
293                    .unwrap_or_else(|| err("invalid input", "unmatched closing bracket"));
294                out[lb] = Token::LBrack(out.len());
295                tok = Token::RBrack(lb);
296            }
297
298            Token::Goto(0) | Token::Inc(0) | Token::Dec(0) => {
299                si += 1;
300                continue;
301            }
302
303            _ => {}
304        }
305
306        out.push(tok);
307        si += 1;
308    }
309
310    if !lbracks.is_empty() {
311        err("invalid input", "unmatched opening bracket");
312    }
313
314    out
315}
316
317impl Display for Token {
318    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
319        match self {
320            Token::Inc(i) => write!(f, "{}", "+".repeat(*i as usize)),
321            Token::Dec(i) => write!(f, "{}", "-".repeat(*i as usize)),
322            Token::Goto(i) => write!(f, "{}", left_right(*i)),
323            Token::Set(i) => write!(f, "[-]{}", "+".repeat(*i as usize)),
324            Token::LBrack(_) => write!(f, "["),
325            Token::RBrack(_) => write!(f, "]"),
326            Token::Out => write!(f, "."),
327
328            Token::In => write!(f, ","),
329            Token::Zero => write!(f, "[-]"),
330            Token::Add(i, mul) => write!(
331                f,
332                "[{}-{}{}]",
333                left_right(*i),
334                left_right(-i),
335                "+".repeat(*mul as usize)
336            ),
337            Token::Sub(i) => write!(f, "[{}-{}-]", left_right(*i), left_right(-i)),
338            Token::Dup(i1, mul1, i2, mul2) => write!(
339                f,
340                "[-{}{}{}{}{}]",
341                left_right(*i1),
342                "+".repeat(*mul1 as usize),
343                left_right(*i2),
344                "+".repeat(*mul2 as usize),
345                left_right(-(i1 + i2)),
346            ),
347            Token::Scan(i) => write!(f, "[{}]", left_right(*i)),
348
349            Token::End => write!(f, "[]"),
350
351            #[cfg(any(debug_assertions, feature = "debug"))]
352            Token::Dump => write!(f, ";"),
353        }
354    }
355}