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