• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 //! Constructs useful in infix operator parsing with the precedence climbing method.
11 
12 use std::collections::HashMap;
13 use std::iter::Peekable;
14 use std::ops::BitOr;
15 
16 use iterators::Pair;
17 use RuleType;
18 
19 /// Associativity of an [`Operator`].
20 ///
21 /// [`Operator`]: struct.Operator.html
22 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
23 pub enum Assoc {
24     /// Left `Operator` associativity
25     Left,
26     /// Right `Operator` associativity
27     Right,
28 }
29 
30 /// Infix operator used in [`PrecClimber`].
31 ///
32 /// [`PrecClimber`]: struct.PrecClimber.html
33 #[derive(Debug)]
34 pub struct Operator<R: RuleType> {
35     rule: R,
36     assoc: Assoc,
37     next: Option<Box<Operator<R>>>,
38 }
39 
40 impl<R: RuleType> Operator<R> {
41     /// Creates a new `Operator` from a `Rule` and `Assoc`.
42     ///
43     /// # Examples
44     ///
45     /// ```
46     /// # use pest::prec_climber::{Assoc, Operator};
47     /// # #[allow(non_camel_case_types)]
48     /// # #[allow(dead_code)]
49     /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
50     /// # enum Rule {
51     /// #     plus,
52     /// #     minus
53     /// # }
54     /// Operator::new(Rule::plus, Assoc::Left) | Operator::new(Rule::minus, Assoc::Right);
55     /// ```
new(rule: R, assoc: Assoc) -> Operator<R>56     pub fn new(rule: R, assoc: Assoc) -> Operator<R> {
57         Operator {
58             rule,
59             assoc,
60             next: None,
61         }
62     }
63 }
64 
65 impl<R: RuleType> BitOr for Operator<R> {
66     type Output = Self;
67 
bitor(mut self, rhs: Self) -> Self68     fn bitor(mut self, rhs: Self) -> Self {
69         fn assign_next<R: RuleType>(op: &mut Operator<R>, next: Operator<R>) {
70             if let Some(ref mut child) = op.next {
71                 assign_next(child, next);
72             } else {
73                 op.next = Some(Box::new(next));
74             }
75         }
76 
77         assign_next(&mut self, rhs);
78         self
79     }
80 }
81 
82 /// List of operators and precedences, which can perform [precedence climbing][1] on infix
83 /// expressions contained in a [`Pairs`]. The token pairs contained in the `Pairs` should start
84 /// with a *primary* pair and then alternate between an *operator* and a *primary*.
85 ///
86 /// [1]: https://en.wikipedia.org/wiki/Operator-precedence_parser#Precedence_climbing_method
87 /// [`Pairs`]: ../iterators/struct.Pairs.html
88 #[derive(Debug)]
89 pub struct PrecClimber<R: RuleType> {
90     ops: HashMap<R, (u32, Assoc)>,
91 }
92 
93 impl<R: RuleType> PrecClimber<R> {
94     /// Creates a new `PrecClimber` from the `Operator`s contained in `ops`. Every entry in the
95     /// `Vec` has precedence *index + 1*. In order to have operators with same precedence, they need
96     /// to be chained with `|` between them.
97     ///
98     /// # Examples
99     ///
100     /// ```
101     /// # use pest::prec_climber::{Assoc, Operator, PrecClimber};
102     /// # #[allow(non_camel_case_types)]
103     /// # #[allow(dead_code)]
104     /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
105     /// # enum Rule {
106     /// #     plus,
107     /// #     minus,
108     /// #     times,
109     /// #     divide,
110     /// #     power
111     /// # }
112     /// PrecClimber::new(vec![
113     ///     Operator::new(Rule::plus, Assoc::Left) | Operator::new(Rule::minus, Assoc::Left),
114     ///     Operator::new(Rule::times, Assoc::Left) | Operator::new(Rule::divide, Assoc::Left),
115     ///     Operator::new(Rule::power, Assoc::Right)
116     /// ]);
117     /// ```
new(ops: Vec<Operator<R>>) -> PrecClimber<R>118     pub fn new(ops: Vec<Operator<R>>) -> PrecClimber<R> {
119         let ops = ops
120             .into_iter()
121             .zip(1..)
122             .fold(HashMap::new(), |mut map, (op, prec)| {
123                 let mut next = Some(op);
124 
125                 while let Some(op) = next.take() {
126                     match op {
127                         Operator {
128                             rule,
129                             assoc,
130                             next: op_next,
131                         } => {
132                             map.insert(rule, (prec, assoc));
133                             next = op_next.map(|op| *op);
134                         }
135                     }
136                 }
137 
138                 map
139             });
140 
141         PrecClimber { ops }
142     }
143 
144     /// Performs the precedence climbing algorithm on the `pairs` in a similar manner to map-reduce.
145     /// *Primary* pairs are mapped with `primary` and then reduced to one single result with
146     /// `infix`.
147     ///
148     /// # Panics
149     ///
150     /// Panics will occur when `pairs` is empty or when the alternating *primary*, *operator*,
151     /// *primary* order is not respected.
152     ///
153     /// # Examples
154     ///
155     /// ```ignore
156     /// let primary = |pair| {
157     ///     consume(pair, climber)
158     /// };
159     /// let infix = |lhs: i32, op: Pair<Rule>, rhs: i32| {
160     ///     match op.rule() {
161     ///         Rule::plus => lhs + rhs,
162     ///         Rule::minus => lhs - rhs,
163     ///         Rule::times => lhs * rhs,
164     ///         Rule::divide => lhs / rhs,
165     ///         Rule::power => lhs.pow(rhs as u32),
166     ///         _ => unreachable!()
167     ///     }
168     /// };
169     ///
170     /// let result = climber.climb(pairs, primary, infix);
171     /// ```
climb<'i, P, F, G, T>(&self, mut pairs: P, mut primary: F, mut infix: G) -> T where P: Iterator<Item = Pair<'i, R>>, F: FnMut(Pair<'i, R>) -> T, G: FnMut(T, Pair<'i, R>, T) -> T,172     pub fn climb<'i, P, F, G, T>(&self, mut pairs: P, mut primary: F, mut infix: G) -> T
173     where
174         P: Iterator<Item = Pair<'i, R>>,
175         F: FnMut(Pair<'i, R>) -> T,
176         G: FnMut(T, Pair<'i, R>, T) -> T,
177     {
178         let lhs = primary(
179             pairs
180                 .next()
181                 .expect("precedence climbing requires a non-empty Pairs"),
182         );
183         self.climb_rec(lhs, 0, &mut pairs.peekable(), &mut primary, &mut infix)
184     }
185 
climb_rec<'i, P, F, G, T>( &self, mut lhs: T, min_prec: u32, pairs: &mut Peekable<P>, primary: &mut F, infix: &mut G, ) -> T where P: Iterator<Item = Pair<'i, R>>, F: FnMut(Pair<'i, R>) -> T, G: FnMut(T, Pair<'i, R>, T) -> T,186     fn climb_rec<'i, P, F, G, T>(
187         &self,
188         mut lhs: T,
189         min_prec: u32,
190         pairs: &mut Peekable<P>,
191         primary: &mut F,
192         infix: &mut G,
193     ) -> T
194     where
195         P: Iterator<Item = Pair<'i, R>>,
196         F: FnMut(Pair<'i, R>) -> T,
197         G: FnMut(T, Pair<'i, R>, T) -> T,
198     {
199         while pairs.peek().is_some() {
200             let rule = pairs.peek().unwrap().as_rule();
201             if let Some(&(prec, _)) = self.ops.get(&rule) {
202                 if prec >= min_prec {
203                     let op = pairs.next().unwrap();
204                     let mut rhs = primary(pairs.next().expect(
205                         "infix operator must be followed by \
206                          a primary expression",
207                     ));
208 
209                     while pairs.peek().is_some() {
210                         let rule = pairs.peek().unwrap().as_rule();
211                         if let Some(&(new_prec, assoc)) = self.ops.get(&rule) {
212                             if new_prec > prec || assoc == Assoc::Right && new_prec == prec {
213                                 rhs = self.climb_rec(rhs, new_prec, pairs, primary, infix);
214                             } else {
215                                 break;
216                             }
217                         } else {
218                             break;
219                         }
220                     }
221 
222                     lhs = infix(lhs, op, rhs);
223                 } else {
224                     break;
225                 }
226             } else {
227                 break;
228             }
229         }
230 
231         lhs
232     }
233 }
234