1 use std::str::FromStr;
2 
3 use winnow::prelude::*;
4 use winnow::{
5     ascii::{digit1 as digits, multispace0 as multispaces},
6     combinator::alt,
7     combinator::delimited,
8     combinator::repeat,
9     token::one_of,
10 };
11 
12 // Parser definition
13 
expr(i: &mut &str) -> PResult<i64>14 pub fn expr(i: &mut &str) -> PResult<i64> {
15     let init = term.parse_next(i)?;
16 
17     repeat(0.., (one_of(['+', '-']), term))
18         .fold(
19             move || init,
20             |acc, (op, val): (char, i64)| {
21                 if op == '+' {
22                     acc + val
23                 } else {
24                     acc - val
25                 }
26             },
27         )
28         .parse_next(i)
29 }
30 
31 // We read an initial factor and for each time we find
32 // a * or / operator followed by another factor, we do
33 // the math by folding everything
term(i: &mut &str) -> PResult<i64>34 fn term(i: &mut &str) -> PResult<i64> {
35     let init = factor.parse_next(i)?;
36 
37     repeat(0.., (one_of(['*', '/']), factor))
38         .fold(
39             move || init,
40             |acc, (op, val): (char, i64)| {
41                 if op == '*' {
42                     acc * val
43                 } else {
44                     acc / val
45                 }
46             },
47         )
48         .parse_next(i)
49 }
50 
51 // We transform an integer string into a i64, ignoring surrounding whitespace
52 // We look for a digit suite, and try to convert it.
53 // If either str::from_utf8 or FromStr::from_str fail,
54 // we fallback to the parens parser defined above
factor(i: &mut &str) -> PResult<i64>55 fn factor(i: &mut &str) -> PResult<i64> {
56     delimited(
57         multispaces,
58         alt((digits.try_map(FromStr::from_str), parens)),
59         multispaces,
60     )
61     .parse_next(i)
62 }
63 
64 // We parse any expr surrounded by parens, ignoring all whitespace around those
parens(i: &mut &str) -> PResult<i64>65 fn parens(i: &mut &str) -> PResult<i64> {
66     delimited('(', expr, ')').parse_next(i)
67 }
68 
69 #[test]
factor_test()70 fn factor_test() {
71     let input = "3";
72     let expected = Ok(("", 3));
73     assert_eq!(factor.parse_peek(input), expected);
74 
75     let input = " 12";
76     let expected = Ok(("", 12));
77     assert_eq!(factor.parse_peek(input), expected);
78 
79     let input = "537 ";
80     let expected = Ok(("", 537));
81     assert_eq!(factor.parse_peek(input), expected);
82 
83     let input = "  24     ";
84     let expected = Ok(("", 24));
85     assert_eq!(factor.parse_peek(input), expected);
86 }
87 
88 #[test]
term_test()89 fn term_test() {
90     let input = " 12 *2 /  3";
91     let expected = Ok(("", 8));
92     assert_eq!(term.parse_peek(input), expected);
93 
94     let input = " 12 *2 /  3";
95     let expected = Ok(("", 8));
96     assert_eq!(term.parse_peek(input), expected);
97 
98     let input = " 2* 3  *2 *2 /  3";
99     let expected = Ok(("", 8));
100     assert_eq!(term.parse_peek(input), expected);
101 
102     let input = " 48 /  3/2";
103     let expected = Ok(("", 8));
104     assert_eq!(term.parse_peek(input), expected);
105 }
106 
107 #[test]
expr_test()108 fn expr_test() {
109     let input = " 1 +  2 ";
110     let expected = Ok(("", 3));
111     assert_eq!(expr.parse_peek(input), expected);
112 
113     let input = " 12 + 6 - 4+  3";
114     let expected = Ok(("", 17));
115     assert_eq!(expr.parse_peek(input), expected);
116 
117     let input = " 1 + 2*3 + 4";
118     let expected = Ok(("", 11));
119     assert_eq!(expr.parse_peek(input), expected);
120 }
121 
122 #[test]
parens_test()123 fn parens_test() {
124     let input = " (  2 )";
125     let expected = Ok(("", 2));
126     assert_eq!(expr.parse_peek(input), expected);
127 
128     let input = " 2* (  3 + 4 ) ";
129     let expected = Ok(("", 14));
130     assert_eq!(expr.parse_peek(input), expected);
131 
132     let input = "  2*2 / ( 5 - 1) + 3";
133     let expected = Ok(("", 4));
134     assert_eq!(expr.parse_peek(input), expected);
135 }
136