roll_rs/
parser.rs

1use crate::filtermodifier::FilterModifier;
2use crate::interpreter::Ast;
3use crate::options::Options;
4use std::iter::Peekable;
5use std::str::Chars;
6
7#[derive(Debug)]
8pub struct Parser<'a> {
9    expr: Peekable<Chars<'a>>,
10    pos: u64,
11    source: String,
12
13    pub advanced: bool,
14}
15
16impl<'a> Parser<'a> {
17    pub fn new(expr: &'a str) -> Self {
18        Self {
19            source: expr.to_string(),
20            expr: expr.chars().peekable(),
21            pos: 0,
22            advanced: false,
23        }
24    }
25
26    pub fn advanced(mut self) -> Self {
27        self.advanced = true;
28        self
29    }
30
31    pub fn backup(&self) -> Self {
32        Self {
33            expr: self.expr.clone(),
34            source: self.source.clone(),
35            pos: self.pos,
36            advanced: self.advanced,
37        }
38    }
39
40    pub fn restore(&mut self, other: Self) {
41        self.expr = other.expr;
42        self.pos = other.pos;
43        self.source = other.source;
44        self.advanced = other.advanced;
45    }
46
47    pub fn accept(&mut self, c: char, options: Options) -> Result<(), Options> {
48        self.expect(c, options)?;
49
50        self.pos += 1;
51        self.expr.next();
52        Ok(())
53    }
54
55    pub fn accept_string(&mut self, text: &str, options: Options) -> Result<(), Options> {
56        let backup = self.backup();
57        for c in text.chars() {
58            if let Err(e) = self.accept(c, options.clone()) {
59                self.restore(backup);
60                return Err(e);
61            }
62        }
63
64        Ok(())
65    }
66
67    pub fn expect(&mut self, c: char, options: Options) -> Result<(), Options> {
68        while let Some(i) = self.expr.peek() {
69            if !i.is_whitespace() {
70                break;
71            }
72            self.pos += 1;
73            self.expr.next();
74        }
75
76        let pk = self.expr.peek();
77        if pk == Some(&c) {
78            Ok(())
79        } else {
80            Err(options.add(c).pos(self.pos))
81        }
82    }
83
84    pub fn accept_any(
85        &mut self,
86        c: &[char],
87        mut options: Options,
88        name: Option<Options>,
89    ) -> Result<char, Options> {
90        for i in c {
91            match self.accept(*i, options.clone()) {
92                Ok(_) => return Ok(*i),
93                Err(o) => {
94                    if name.is_none() {
95                        options = options.merge(o)
96                    }
97                }
98            }
99        }
100
101        if let Some(n) = name {
102            options = options.merge(n)
103        }
104
105        Err(options)
106    }
107
108    pub fn parse(&mut self) -> Result<Ast, Options> {
109        let result = self.parse_expr(Options::new(self.source.clone()))?;
110
111        if self.expr.next().is_some() {
112            return Err(Options::new(self.source.clone())
113                .pos(self.pos)
114                .message("unexpected trailing character(s)"));
115        }
116
117        Ok(result)
118    }
119
120    pub fn parse_expr(&mut self, options: Options) -> Result<Ast, Options> {
121        self.parse_sum(options)
122    }
123
124    pub fn parse_sum(&mut self, options: Options) -> Result<Ast, Options> {
125        let mut res = self.parse_term(options.clone())?;
126
127        while let Ok(op) = self.accept_any(&['+', '-'], options.clone(), None) {
128            let right = self.parse_term(options.clone())?;
129
130            res = match op {
131                '+' => Ast::Add(Box::new(res), Box::new(right)),
132                '-' => Ast::Sub(Box::new(res), Box::new(right)),
133                _ => unreachable!(),
134            }
135        }
136
137        Ok(res)
138    }
139
140    pub fn parse_term(&mut self, mut options: Options) -> Result<Ast, Options> {
141        let mut res = self.parse_factor(options.clone())?;
142
143        loop {
144            let opres = self.accept_any(&['*', '/'], options.clone(), None);
145            let mut op = if let Ok(i) = opres {
146                i
147            } else if self.accept_string("mod", options.clone()).is_ok() {
148                '%'
149            } else {
150                options.add_str("mod").add_str("//");
151                break;
152            };
153
154            if op == '/' && self.accept('/', options.clone()).is_ok() {
155                op = 'i'
156            } else {
157                options = options.add('/');
158            }
159
160            let right = self.parse_factor(options.clone())?;
161
162            res = match op {
163                '*' => Ast::Mul(Box::new(res), Box::new(right)),
164                '/' => Ast::Div(Box::new(res), Box::new(right)),
165                'i' => Ast::IDiv(Box::new(res), Box::new(right)),
166                '%' => Ast::Mod(Box::new(res), Box::new(right)),
167                _ => unreachable!(),
168            }
169        }
170
171        Ok(res)
172    }
173
174    pub fn parse_factor(&mut self, options: Options) -> Result<Ast, Options> {
175        let backup = self.backup();
176
177        Ok(match self.accept('-', options.clone()) {
178            Ok(_) => Ast::Minus(Box::new(self.parse_power(options)?)),
179            Err(o) => {
180                self.restore(backup);
181
182                return self.parse_power(o);
183            }
184        })
185    }
186
187    pub fn parse_power(&mut self, options: Options) -> Result<Ast, Options> {
188        let mut res = self.parse_atom(options.clone())?;
189        if self.accept_string("**", options.clone()).is_ok() {
190            let right = self.parse_factor(options)?;
191            res = Ast::Power(Box::new(res), Box::new(right));
192        }
193
194        Ok(res)
195    }
196
197    pub fn parse_atom(&mut self, options: Options) -> Result<Ast, Options> {
198        let backup = self.backup();
199        Ok(match self.parse_dice(options) {
200            Err(mut o) => {
201                self.restore(backup);
202
203                let backup = self.backup();
204                if self.accept('(', o.clone()).is_ok() {
205                    let sm = self.parse_sum(o.clone())?;
206                    self.accept(')', o)
207                        .map_err(|e| e.message("missing closing parenthesis"))?;
208
209                    return Ok(sm);
210                } else {
211                    o = o
212                        .add('(')
213                        .message("tried to parse expression between parenthesis");
214                    self.restore(backup);
215                }
216
217                self.parse_number(o.message("tried to parse dice roll"))?
218            }
219            Ok(i) => i,
220        })
221    }
222
223    pub fn parse_dice(&mut self, mut options: Options) -> Result<Ast, Options> {
224        let backup = self.backup();
225
226        let rolls = if self.advanced && self.accept('(', options.clone()).is_ok() {
227            let sm = self.parse_sum(options.clone())?;
228            self.accept(')', options.clone())
229                .map_err(|e| e.message("missing closing parenthesis"))?;
230
231            Some(Box::new(sm))
232        } else {
233            if self.advanced {
234                options = options
235                    .add('(')
236                    .message("tried to parse expression between parenthesis");
237            }
238            self.restore(backup);
239            self.parse_number(options.clone()).map(Box::new).ok()
240        };
241
242        self.accept('d', options.clone())?;
243        let dpos = self.pos - 1;
244
245        let backup = self.backup();
246        let sides = if self.advanced && self.accept('(', options.clone()).is_ok() {
247            let sm = self.parse_sum(options.clone())?;
248            self.accept(')', options.clone())
249                .map_err(|e| e.message("missing closing parenthesis"))?;
250
251            Some(Box::new(sm))
252        } else {
253            if self.advanced {
254                options = options
255                    .add('(')
256                    .message("tried to parse expression between parenthesis");
257            }
258            self.restore(backup);
259
260            self.parse_number_or_percent(options.clone())
261                .map(Box::new)
262                .ok()
263        };
264
265        let fm = if self.accept_string("kh", options.clone()).is_ok()
266            || self.accept('h', options.clone()).is_ok()
267        {
268            FilterModifier::KeepHighest(Box::new(
269                self.parse_number(options)
270                    .unwrap_or_else(|_| Ast::Const("1".to_string())),
271            ))
272        } else if self.accept_string("dl", options.clone()).is_ok()
273            || self.accept('l', options.clone()).is_ok()
274        {
275            FilterModifier::DropLowest(Box::new(
276                self.parse_number(options)
277                    .unwrap_or_else(|_| Ast::Const("1".to_string())),
278            ))
279        } else if self.accept_string("dh", options.clone()).is_ok() {
280            FilterModifier::DropHighest(Box::new(
281                self.parse_number(options)
282                    .unwrap_or_else(|_| Ast::Const("1".to_string())),
283            ))
284        } else if self.accept_string("kl", options.clone()).is_ok() {
285            FilterModifier::KeepLowest(Box::new(
286                self.parse_number(options)
287                    .unwrap_or_else(|_| Ast::Const("1".to_string())),
288            ))
289        } else {
290            FilterModifier::None
291        };
292
293        Ok(Ast::Dice(rolls, sides, fm, dpos))
294    }
295
296    pub fn parse_number_or_percent(&mut self, options: Options) -> Result<Ast, Options> {
297        if self.accept('%', options.clone()).is_ok() {
298            Ok(Ast::Const("100".to_ascii_lowercase()))
299        } else {
300            self.parse_number(options.add('%'))
301        }
302    }
303
304    pub fn parse_number(&mut self, options: Options) -> Result<Ast, Options> {
305        const DIGITS: &[char] = &['1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '.'];
306        let digits_name = Options::new("".to_string()).add_str("0-9");
307
308        let mut number = vec![self
309            .accept_any(&DIGITS, options.clone(), Some(digits_name.clone()))
310            .map_err(|e| {
311                options
312                    .clone()
313                    .merge(e)
314                    .add('(')
315                    .message("tried to parse a number")
316            })?];
317
318        loop {
319            let backup = self.backup();
320
321            let digit = match self.accept_any(&DIGITS, options.clone(), Some(digits_name.clone())) {
322                Ok(i) => i,
323                Err(_) => {
324                    self.restore(backup);
325                    break;
326                }
327            };
328
329            number.push(digit)
330        }
331
332        let string: String = number.iter().collect();
333
334        Ok(Ast::Const(string))
335    }
336}
337
338#[cfg(test)]
339mod tests {
340    use super::*;
341    use crate::filtermodifier::FilterModifier;
342    use crate::interpreter::{Ast, Value, DEFAULT_SIDES};
343
344    #[test]
345    pub fn add() {
346        let mut p = Parser::new("3 + 5");
347        assert_eq!(
348            p.parse().unwrap().interp(&mut Vec::new()).unwrap(),
349            Value::Int(8)
350        );
351    }
352
353    #[test]
354    pub fn sub() {
355        let mut p = Parser::new("3 - 5");
356        assert_eq!(
357            p.parse().unwrap().interp(&mut Vec::new()).unwrap(),
358            Value::Int(-2)
359        );
360    }
361
362    #[test]
363    pub fn mul() {
364        let mut p = Parser::new("3 * 5");
365        assert_eq!(
366            p.parse().unwrap().interp(&mut Vec::new()).unwrap(),
367            Value::Int(15)
368        );
369    }
370
371    #[test]
372    pub fn div() {
373        let mut p = Parser::new("15/3");
374        let ast = p.parse().unwrap();
375        assert_eq!(ast.interp(&mut Vec::new()).unwrap(), Value::Float(5.0));
376    }
377
378    #[test]
379    pub fn pemdas() {
380        let mut p = Parser::new("3 * 5 + -5");
381        let ast = p.parse().unwrap();
382        assert_eq!(ast.interp(&mut Vec::new()).unwrap(), Value::Int(10));
383    }
384
385    #[test]
386    pub fn parens() {
387        let mut p = Parser::new("3 * (5 + -5)");
388        let ast = p.parse().unwrap();
389        assert_eq!(ast.interp(&mut Vec::new()).unwrap(), Value::Int(0))
390    }
391
392    #[test]
393    pub fn meme() {
394        let mut p = Parser::new("6 / 2 * (1 + 2)");
395        let ast = p.parse().unwrap();
396        assert_eq!(ast.interp(&mut Vec::new()).unwrap(), Value::Float(9.0))
397    }
398
399    #[test]
400    pub fn long_sum() {
401        let mut p = Parser::new("3 + 3 + 3");
402        let ast = p.parse().unwrap();
403        assert_eq!(ast.interp(&mut Vec::new()).unwrap(), Value::Int(9));
404    }
405
406    #[test]
407    pub fn long_mul() {
408        let mut p = Parser::new("3 * 3 * 3");
409        let ast = p.parse().unwrap();
410        assert_eq!(ast.interp(&mut Vec::new()).unwrap(), Value::Int(27));
411    }
412
413    #[test]
414    pub fn idiv() {
415        let mut p = Parser::new("3 // 5");
416        let ast = p.parse().unwrap();
417        assert_eq!(ast.interp(&mut Vec::new()).unwrap(), Value::Int(0));
418    }
419
420    #[test]
421    pub fn dice_none() {
422        let mut p = Parser::new("d");
423        let ast = p.parse().unwrap();
424        assert_eq!(ast, Ast::Dice(None, None, FilterModifier::None, 0));
425
426        let mut rolls = Vec::new();
427        let res = ast.interp(&mut rolls).unwrap();
428
429        assert_eq!(rolls.len(), 1);
430
431        let roll = &rolls[0].1;
432        assert_eq!(DEFAULT_SIDES, roll.sides.to_string());
433
434        assert_eq!(res, Value::Int(roll.total));
435    }
436
437    #[test]
438    pub fn dice_6() {
439        let mut p = Parser::new("d6");
440        let ast = p.parse().unwrap();
441        assert_eq!(
442            ast,
443            Ast::Dice(
444                None,
445                Some(Box::new(Ast::Const("6".to_string()))),
446                FilterModifier::None,
447                0
448            )
449        );
450
451        let mut rolls = Vec::new();
452        let res = ast.interp(&mut rolls).unwrap();
453
454        assert_eq!(rolls.len(), 1);
455
456        let roll = &rolls[0].1;
457
458        assert_eq!(res, Value::Int(roll.total as i64));
459    }
460
461    #[test]
462    pub fn dice_3_6() {
463        let mut p = Parser::new("3d6");
464        let ast = p.parse().unwrap();
465        assert_eq!(
466            ast,
467            Ast::Dice(
468                Some(Box::new(Ast::Const("3".to_string()))),
469                Some(Box::new(Ast::Const("6".to_string()))),
470                FilterModifier::None,
471                1
472            )
473        );
474
475        let mut rolls = Vec::new();
476        let res = ast.interp(&mut rolls).unwrap();
477
478        assert_eq!(rolls.len(), 1);
479
480        let roll0 = &rolls[0].1.vals[0];
481        let roll1 = &rolls[0].1.vals[1];
482        let roll2 = &rolls[0].1.vals[2];
483
484        assert_eq!(res, Value::Int((roll0 + roll1 + roll2) as i64));
485    }
486
487    #[test]
488    pub fn dice_0() {
489        let mut p = Parser::new("0d6");
490        let ast = p.parse().unwrap();
491        assert_eq!(
492            ast,
493            Ast::Dice(
494                Some(Box::new(Ast::Const("0".to_string()))),
495                Some(Box::new(Ast::Const("6".to_string()))),
496                FilterModifier::None,
497                1
498            )
499        );
500
501        let mut rolls = Vec::new();
502        let res = ast.interp(&mut rolls).unwrap();
503
504        assert_eq!(rolls.len(), 1);
505
506        assert_eq!(res, Value::Int(0));
507    }
508
509    #[test]
510    pub fn dice_float() {
511        let mut p = Parser::new("3.5d6");
512        let ast = p.parse().unwrap();
513        assert_eq!(
514            ast,
515            Ast::Dice(
516                Some(Box::new(Ast::Const("3.5".to_string()))),
517                Some(Box::new(Ast::Const("6".to_string()))),
518                FilterModifier::None,
519                3
520            )
521        );
522
523        ast.interp(&mut Vec::new()).expect_err("result was okay");
524    }
525
526    #[test]
527    pub fn dice_dfloat() {
528        let mut p = Parser::new("3d3.5");
529        let ast = p.parse().unwrap();
530        assert_eq!(
531            ast,
532            Ast::Dice(
533                Some(Box::new(Ast::Const("3".to_string()))),
534                Some(Box::new(Ast::Const("3.5".to_string()))),
535                FilterModifier::None,
536                1
537            )
538        );
539
540        ast.interp(&mut Vec::new()).expect_err("result was okay");
541    }
542
543    #[test]
544    pub fn dice_d0() {
545        let mut p = Parser::new("3d0");
546        let ast = p.parse().unwrap();
547        assert_eq!(
548            ast,
549            Ast::Dice(
550                Some(Box::new(Ast::Const("3".to_string()))),
551                Some(Box::new(Ast::Const("0".to_string()))),
552                FilterModifier::None,
553                1
554            )
555        );
556
557        ast.interp(&mut Vec::new()).expect_err("result was okay");
558    }
559
560    #[test]
561    pub fn dice_dpercent() {
562        let mut p = Parser::new("d%");
563        let ast = p.parse().unwrap();
564        assert_eq!(
565            ast,
566            Ast::Dice(
567                None,
568                Some(Box::new(Ast::Const("100".to_string()))),
569                FilterModifier::None,
570                0
571            )
572        );
573
574        let mut rolls = Vec::new();
575        let res = ast.interp(&mut rolls).unwrap();
576
577        assert_eq!(rolls.len(), 1);
578
579        let roll = &rolls[0].1;
580
581        assert_eq!(roll.sides.get(), 100);
582        assert_eq!(res, Value::Int(roll.total));
583    }
584
585    #[test]
586    pub fn dice_kl() {
587        let mut p = Parser::new("5d%kl");
588        let ast = p.parse().unwrap();
589
590        let mut rolls = Vec::new();
591        let res = ast.interp(&mut rolls).unwrap();
592
593        assert_eq!(rolls.len(), 1);
594
595        let roll = &rolls[0].1;
596
597        assert_eq!(roll.vals.len(), 1);
598        assert_eq!(res, Value::Int(roll.total));
599    }
600
601    #[test]
602    pub fn pow() {
603        let mut p = Parser::new("5 ** 2");
604        let ast = p.parse().unwrap();
605        assert_eq!(ast.interp(&mut Vec::new()).unwrap(), Value::Int(25));
606    }
607
608    #[test]
609    pub fn compound() {
610        let mut p = Parser::new("(3d5)d(5d3)");
611        p.advanced = true;
612        let _ = p.parse().unwrap();
613    }
614}