1 // pest. The Elegant Parser
2 // Copyright (c) 2018 Dragoș Tiselice
3 //
4 // Licensed under the Apache License, Version 2.0
5 // <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT
6 // license <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
7 // option. All files in the project carrying such notice may not be copied,
8 // modified, or distributed except according to those terms.
9
10 #[macro_use]
11 extern crate pest;
12
13 use pest::error::Error;
14 use pest::iterators::{Pair, Pairs};
15 use pest::pratt_parser::{Assoc, Op, PrattParser};
16 use pest::{state, ParseResult, Parser, ParserState};
17
18 #[allow(dead_code, non_camel_case_types)]
19 #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
20 enum Rule {
21 expression,
22 primary,
23 number,
24 plus,
25 minus,
26 times,
27 divide,
28 modulus,
29 power,
30 }
31
32 struct CalculatorParser;
33
34 impl Parser<Rule> for CalculatorParser {
35 // false positive: pest uses `..` as a complete range (historically)
36 #[allow(clippy::almost_complete_range)]
parse(rule: Rule, input: &str) -> Result<Pairs<Rule>, Error<Rule>>37 fn parse(rule: Rule, input: &str) -> Result<Pairs<Rule>, Error<Rule>> {
38 fn expression(
39 state: Box<ParserState<'_, Rule>>,
40 ) -> ParseResult<Box<ParserState<'_, Rule>>> {
41 state.rule(Rule::expression, |s| {
42 s.sequence(|s| {
43 primary(s).and_then(|s| {
44 s.repeat(|s| {
45 s.sequence(|s| {
46 plus(s)
47 .or_else(minus)
48 .or_else(times)
49 .or_else(divide)
50 .or_else(modulus)
51 .or_else(power)
52 .and_then(primary)
53 })
54 })
55 })
56 })
57 })
58 }
59
60 fn primary(state: Box<ParserState<'_, Rule>>) -> ParseResult<Box<ParserState<'_, Rule>>> {
61 state
62 .sequence(|s| {
63 s.match_string("(")
64 .and_then(expression)
65 .and_then(|s| s.match_string(")"))
66 })
67 .or_else(number)
68 }
69
70 fn number(state: Box<ParserState<'_, Rule>>) -> ParseResult<Box<ParserState<'_, Rule>>> {
71 state.rule(Rule::number, |s| {
72 s.sequence(|s| {
73 s.optional(|s| s.match_string("-")).and_then(|s| {
74 s.match_string("0").or_else(|s| {
75 s.sequence(|s| {
76 s.match_range('1'..'9')
77 .and_then(|s| s.repeat(|s| s.match_range('0'..'9')))
78 })
79 })
80 })
81 })
82 })
83 }
84
85 fn plus(state: Box<ParserState<'_, Rule>>) -> ParseResult<Box<ParserState<'_, Rule>>> {
86 state.rule(Rule::plus, |s| s.match_string("+"))
87 }
88
89 fn minus(state: Box<ParserState<'_, Rule>>) -> ParseResult<Box<ParserState<'_, Rule>>> {
90 state.rule(Rule::minus, |s| s.match_string("-"))
91 }
92
93 fn times(state: Box<ParserState<'_, Rule>>) -> ParseResult<Box<ParserState<'_, Rule>>> {
94 state.rule(Rule::times, |s| s.match_string("*"))
95 }
96
97 fn divide(state: Box<ParserState<'_, Rule>>) -> ParseResult<Box<ParserState<'_, Rule>>> {
98 state.rule(Rule::divide, |s| s.match_string("/"))
99 }
100
101 fn modulus(state: Box<ParserState<'_, Rule>>) -> ParseResult<Box<ParserState<'_, Rule>>> {
102 state.rule(Rule::modulus, |s| s.match_string("%"))
103 }
104
105 fn power(state: Box<ParserState<'_, Rule>>) -> ParseResult<Box<ParserState<'_, Rule>>> {
106 state.rule(Rule::power, |s| s.match_string("^"))
107 }
108
109 state(input, |state| match rule {
110 Rule::expression => expression(state),
111 _ => unreachable!(),
112 })
113 }
114 }
115
116 #[allow(deprecated)]
117 enum PrattOrPrecClimber<'a> {
118 Pratt(&'a PrattParser<Rule>),
119 PrecClimber(&'a pest::prec_climber::PrecClimber<Rule>),
120 }
121
consume(pair: Pair<Rule>, pratt_or_climber: &PrattOrPrecClimber) -> i32122 fn consume(pair: Pair<Rule>, pratt_or_climber: &PrattOrPrecClimber) -> i32 {
123 let primary = |pair| consume(pair, pratt_or_climber);
124 let infix = |lhs: i32, op: Pair<Rule>, rhs: i32| match op.as_rule() {
125 Rule::plus => lhs + rhs,
126 Rule::minus => lhs - rhs,
127 Rule::times => lhs * rhs,
128 Rule::divide => lhs / rhs,
129 Rule::modulus => lhs % rhs,
130 Rule::power => lhs.pow(rhs as u32),
131 _ => unreachable!(),
132 };
133
134 #[allow(deprecated)]
135 match (pair.as_rule(), pratt_or_climber) {
136 (Rule::expression, PrattOrPrecClimber::Pratt(pratt)) => pratt
137 .map_primary(primary)
138 .map_infix(infix)
139 .parse(pair.into_inner()),
140 (Rule::expression, PrattOrPrecClimber::PrecClimber(climber)) => {
141 climber.climb(pair.into_inner(), primary, infix)
142 }
143 (Rule::number, _) => pair.as_str().parse().unwrap(),
144 _ => unreachable!(),
145 }
146 }
147
148 #[test]
number()149 fn number() {
150 parses_to! {
151 parser: CalculatorParser,
152 input: "-12",
153 rule: Rule::expression,
154 tokens: [
155 expression(0, 3, [
156 number(0, 3)
157 ])
158 ]
159 };
160 }
161
162 #[test]
parens()163 fn parens() {
164 parses_to! {
165 parser: CalculatorParser,
166 input: "((-12))",
167 rule: Rule::expression,
168 tokens: [
169 expression(0, 7, [
170 expression(1, 6, [
171 expression(2, 5, [
172 number(2, 5)
173 ])
174 ])
175 ])
176 ]
177 };
178 }
179
180 #[test]
expression()181 fn expression() {
182 parses_to! {
183 parser: CalculatorParser,
184 input: "-12+3*(4-9)^7^2",
185 rule: Rule::expression,
186 tokens: [
187 expression(0, 15, [
188 number(0, 3),
189 plus(3, 4),
190 number(4, 5),
191 times(5, 6),
192 expression(7, 10, [
193 number(7, 8),
194 minus(8, 9),
195 number(9, 10)
196 ]),
197 power(11, 12),
198 number(12, 13),
199 power(13, 14),
200 number(14, 15)
201 ])
202 ]
203 };
204 }
205
206 #[test]
207 #[allow(deprecated)]
prec_climb()208 fn prec_climb() {
209 use pest::prec_climber::{Assoc, Operator, PrecClimber};
210 let climber = PrecClimber::new(vec![
211 Operator::new(Rule::plus, Assoc::Left) | Operator::new(Rule::minus, Assoc::Left),
212 Operator::new(Rule::times, Assoc::Left)
213 | Operator::new(Rule::divide, Assoc::Left)
214 | Operator::new(Rule::modulus, Assoc::Left),
215 Operator::new(Rule::power, Assoc::Right),
216 ]);
217
218 let pairs = CalculatorParser::parse(Rule::expression, "-12+3*(4-9)^3^2/9%7381");
219 assert_eq!(
220 -1_525,
221 consume(
222 pairs.unwrap().next().unwrap(),
223 &PrattOrPrecClimber::PrecClimber(&climber)
224 )
225 );
226 }
227
228 #[test]
pratt_parse()229 fn pratt_parse() {
230 let pratt = PrattParser::new()
231 .op(Op::infix(Rule::plus, Assoc::Left) | Op::infix(Rule::minus, Assoc::Left))
232 .op(Op::infix(Rule::times, Assoc::Left)
233 | Op::infix(Rule::divide, Assoc::Left)
234 | Op::infix(Rule::modulus, Assoc::Left))
235 .op(Op::infix(Rule::power, Assoc::Right));
236
237 let pairs = CalculatorParser::parse(Rule::expression, "-12+3*(4-9)^3^2/9%7381");
238 assert_eq!(
239 -1_525,
240 consume(
241 pairs.unwrap().next().unwrap(),
242 &PrattOrPrecClimber::Pratt(&pratt)
243 )
244 );
245 }
246