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}