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