| 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 alloc::borrow::Cow; | 
|---|
| 13 | use alloc::boxed::Box; | 
|---|
| 14 | use alloc::vec::Vec; | 
|---|
| 15 | use core::iter::Peekable; | 
|---|
| 16 | use core::ops::BitOr; | 
|---|
| 17 |  | 
|---|
| 18 | use crate::iterators::Pair; | 
|---|
| 19 | use crate::RuleType; | 
|---|
| 20 |  | 
|---|
| 21 | /// Macro for more convenient const fn definition of `prec_climber::PrecClimber`. | 
|---|
| 22 | /// | 
|---|
| 23 | /// # Examples | 
|---|
| 24 | /// | 
|---|
| 25 | /// ``` | 
|---|
| 26 | /// # use pest::prec_climber::{Assoc, PrecClimber}; | 
|---|
| 27 | /// # use pest::prec_climber; | 
|---|
| 28 | /// # #[allow(non_camel_case_types)] | 
|---|
| 29 | /// # #[allow(dead_code)] | 
|---|
| 30 | /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] | 
|---|
| 31 | /// # enum Rule { | 
|---|
| 32 | /// #     plus, | 
|---|
| 33 | /// #     minus, | 
|---|
| 34 | /// #     times, | 
|---|
| 35 | /// #     divide, | 
|---|
| 36 | /// #     power | 
|---|
| 37 | /// # } | 
|---|
| 38 | /// static CLIMBER: PrecClimber<Rule> = prec_climber![ | 
|---|
| 39 | ///     L   plus | minus, | 
|---|
| 40 | ///     L   times | divide, | 
|---|
| 41 | ///     R   power, | 
|---|
| 42 | /// ]; | 
|---|
| 43 | /// ``` | 
|---|
| 44 | #[ cfg(feature = "const_prec_climber")] | 
|---|
| 45 | #[ macro_export] | 
|---|
| 46 | macro_rules! prec_climber { | 
|---|
| 47 | ( | 
|---|
| 48 | $( $assoc:ident $rule:ident $( | $rules:ident )* ),+ $(,)? | 
|---|
| 49 | ) => {{ | 
|---|
| 50 | prec_climber!( | 
|---|
| 51 | @precedences { 1u32 } | 
|---|
| 52 | $( [ $rule $( $rules )* ] )* | 
|---|
| 53 | ); | 
|---|
| 54 |  | 
|---|
| 55 | $crate::prec_climber::PrecClimber::new_const( | 
|---|
| 56 | prec_climber!( | 
|---|
| 57 | @array | 
|---|
| 58 | $( $assoc $rule $(, $assoc $rules )* ),* | 
|---|
| 59 | ) | 
|---|
| 60 | ) | 
|---|
| 61 | }}; | 
|---|
| 62 |  | 
|---|
| 63 | ( @assoc L ) => { $crate::prec_climber::Assoc::Left }; | 
|---|
| 64 | ( @assoc R ) => { $crate::prec_climber::Assoc::Right }; | 
|---|
| 65 |  | 
|---|
| 66 | ( | 
|---|
| 67 | @array | 
|---|
| 68 | $( | 
|---|
| 69 | $assoc:ident $rule:ident | 
|---|
| 70 | ),* | 
|---|
| 71 | ) => { | 
|---|
| 72 | &[ | 
|---|
| 73 | $( | 
|---|
| 74 | ( | 
|---|
| 75 | Rule::$rule, | 
|---|
| 76 | $rule, | 
|---|
| 77 | prec_climber!( @assoc $assoc ), | 
|---|
| 78 | ) | 
|---|
| 79 | ),* | 
|---|
| 80 | ] | 
|---|
| 81 | }; | 
|---|
| 82 |  | 
|---|
| 83 | ( | 
|---|
| 84 | @precedences { $precedence:expr } | 
|---|
| 85 | ) => {}; | 
|---|
| 86 |  | 
|---|
| 87 | ( | 
|---|
| 88 | @precedences { $precedence:expr } | 
|---|
| 89 | [ $( $rule:ident )* ] | 
|---|
| 90 | $( [ $( $rules:ident )* ] )* | 
|---|
| 91 | ) => { | 
|---|
| 92 | $( | 
|---|
| 93 | #[allow(non_upper_case_globals)] | 
|---|
| 94 | const $rule: u32 = $precedence; | 
|---|
| 95 | )* | 
|---|
| 96 | prec_climber!( | 
|---|
| 97 | @precedences { 1u32 + $precedence } | 
|---|
| 98 | $( [ $( $rules )* ] )* | 
|---|
| 99 | ); | 
|---|
| 100 | }; | 
|---|
| 101 | } | 
|---|
| 102 |  | 
|---|
| 103 | /// Associativity of an [`Operator`]. | 
|---|
| 104 | /// | 
|---|
| 105 | /// [`Operator`]: struct.Operator.html | 
|---|
| 106 | #[ derive(Clone, Copy, Debug, Eq, PartialEq)] | 
|---|
| 107 | pub enum Assoc { | 
|---|
| 108 | /// Left `Operator` associativity | 
|---|
| 109 | Left, | 
|---|
| 110 | /// Right `Operator` associativity | 
|---|
| 111 | Right, | 
|---|
| 112 | } | 
|---|
| 113 |  | 
|---|
| 114 | /// Infix operator used in [`PrecClimber`]. | 
|---|
| 115 | /// | 
|---|
| 116 | /// [`PrecClimber`]: struct.PrecClimber.html | 
|---|
| 117 | #[ derive(Debug)] | 
|---|
| 118 | pub struct Operator<R: RuleType> { | 
|---|
| 119 | rule: R, | 
|---|
| 120 | assoc: Assoc, | 
|---|
| 121 | next: Option<Box<Operator<R>>>, | 
|---|
| 122 | } | 
|---|
| 123 |  | 
|---|
| 124 | impl<R: RuleType> Operator<R> { | 
|---|
| 125 | /// Creates a new `Operator` from a `Rule` and `Assoc`. | 
|---|
| 126 | /// | 
|---|
| 127 | /// # Examples | 
|---|
| 128 | /// | 
|---|
| 129 | /// ``` | 
|---|
| 130 | /// # use pest::prec_climber::{Assoc, Operator}; | 
|---|
| 131 | /// # #[ allow(non_camel_case_types)] | 
|---|
| 132 | /// # #[ allow(dead_code)] | 
|---|
| 133 | /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] | 
|---|
| 134 | /// # enum Rule { | 
|---|
| 135 | /// #     plus, | 
|---|
| 136 | /// #     minus | 
|---|
| 137 | /// # } | 
|---|
| 138 | /// Operator::new(Rule::plus, Assoc::Left) | Operator::new(Rule::minus, Assoc::Right); | 
|---|
| 139 | /// ``` | 
|---|
| 140 | pub fn new(rule: R, assoc: Assoc) -> Operator<R> { | 
|---|
| 141 | Operator { | 
|---|
| 142 | rule, | 
|---|
| 143 | assoc, | 
|---|
| 144 | next: None, | 
|---|
| 145 | } | 
|---|
| 146 | } | 
|---|
| 147 | } | 
|---|
| 148 |  | 
|---|
| 149 | impl<R: RuleType> BitOr for Operator<R> { | 
|---|
| 150 | type Output = Self; | 
|---|
| 151 |  | 
|---|
| 152 | fn bitor(mut self, rhs: Self) -> Self { | 
|---|
| 153 | fn assign_next<R: RuleType>(op: &mut Operator<R>, next: Operator<R>) { | 
|---|
| 154 | if let Some(ref mut child: &mut Box>) = op.next { | 
|---|
| 155 | assign_next(op:child, next); | 
|---|
| 156 | } else { | 
|---|
| 157 | op.next = Some(Box::new(next)); | 
|---|
| 158 | } | 
|---|
| 159 | } | 
|---|
| 160 |  | 
|---|
| 161 | assign_next(&mut self, next:rhs); | 
|---|
| 162 | self | 
|---|
| 163 | } | 
|---|
| 164 | } | 
|---|
| 165 |  | 
|---|
| 166 | /// List of operators and precedences, which can perform [precedence climbing][1] on infix | 
|---|
| 167 | /// expressions contained in a [`Pairs`]. The token pairs contained in the `Pairs` should start | 
|---|
| 168 | /// with a *primary* pair and then alternate between an *operator* and a *primary*. | 
|---|
| 169 | /// | 
|---|
| 170 | /// [1]: https://en.wikipedia.org/wiki/Operator-precedence_parser#Precedence_climbing_method | 
|---|
| 171 | /// [`Pairs`]: ../iterators/struct.Pairs.html | 
|---|
| 172 | #[ derive(Debug)] | 
|---|
| 173 | pub struct PrecClimber<R: Clone + 'static> { | 
|---|
| 174 | ops: Cow<'static, [(R, u32, Assoc)]>, | 
|---|
| 175 | } | 
|---|
| 176 |  | 
|---|
| 177 | #[ cfg(feature = "const_prec_climber")] | 
|---|
| 178 | impl<R: Clone + 'static> PrecClimber<R> { | 
|---|
| 179 | /// Creates a new `PrecClimber` directly from a static slice of | 
|---|
| 180 | /// `(rule: Rule, precedence: u32, associativity: Assoc)` tuples. | 
|---|
| 181 | /// | 
|---|
| 182 | /// Precedence starts from `1`.  Entries don't have to be ordered in any way, but it's easier to read when | 
|---|
| 183 | /// sorted. | 
|---|
| 184 | /// | 
|---|
| 185 | /// # Examples | 
|---|
| 186 | /// | 
|---|
| 187 | /// ``` | 
|---|
| 188 | /// # use pest::prec_climber::{Assoc, PrecClimber}; | 
|---|
| 189 | /// # #[allow(non_camel_case_types)] | 
|---|
| 190 | /// # #[allow(dead_code)] | 
|---|
| 191 | /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] | 
|---|
| 192 | /// # enum Rule { | 
|---|
| 193 | /// #     plus, | 
|---|
| 194 | /// #     minus, | 
|---|
| 195 | /// #     times, | 
|---|
| 196 | /// #     divide, | 
|---|
| 197 | /// #     power | 
|---|
| 198 | /// # } | 
|---|
| 199 | /// static CLIMBER: PrecClimber<Rule> = PrecClimber::new_const(&[ | 
|---|
| 200 | ///     (Rule::plus, 1, Assoc::Left), (Rule::minus, 1, Assoc::Left), | 
|---|
| 201 | ///     (Rule::times, 2, Assoc::Left), (Rule::divide, 2, Assoc::Left), | 
|---|
| 202 | ///     (Rule::power, 3, Assoc::Right) | 
|---|
| 203 | /// ]); | 
|---|
| 204 | /// ``` | 
|---|
| 205 | pub const fn new_const(ops: &'static [(R, u32, Assoc)]) -> PrecClimber<R> { | 
|---|
| 206 | PrecClimber { | 
|---|
| 207 | ops: Cow::Borrowed(ops), | 
|---|
| 208 | } | 
|---|
| 209 | } | 
|---|
| 210 | } | 
|---|
| 211 |  | 
|---|
| 212 | impl<R: RuleType> PrecClimber<R> { | 
|---|
| 213 | // find matching operator by `rule` | 
|---|
| 214 | fn get(&self, rule: &R) -> Option<(u32, Assoc)> { | 
|---|
| 215 | self.ops | 
|---|
| 216 | .iter() | 
|---|
| 217 | .find(|(r, _, _)| r == rule) | 
|---|
| 218 | .map(|(_, precedence, assoc)| (*precedence, *assoc)) | 
|---|
| 219 | } | 
|---|
| 220 |  | 
|---|
| 221 | /// Creates a new `PrecClimber` from the `Operator`s contained in `ops`. Every entry in the | 
|---|
| 222 | /// `Vec` has precedence *index + 1*. In order to have operators with same precedence, they need | 
|---|
| 223 | /// to be chained with `|` between them. | 
|---|
| 224 | /// | 
|---|
| 225 | /// # Examples | 
|---|
| 226 | /// | 
|---|
| 227 | /// ``` | 
|---|
| 228 | /// # use pest::prec_climber::{Assoc, Operator, PrecClimber}; | 
|---|
| 229 | /// # #[ allow(non_camel_case_types)] | 
|---|
| 230 | /// # #[ allow(dead_code)] | 
|---|
| 231 | /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] | 
|---|
| 232 | /// # enum Rule { | 
|---|
| 233 | /// #     plus, | 
|---|
| 234 | /// #     minus, | 
|---|
| 235 | /// #     times, | 
|---|
| 236 | /// #     divide, | 
|---|
| 237 | /// #     power | 
|---|
| 238 | /// # } | 
|---|
| 239 | /// PrecClimber::new(vec![ | 
|---|
| 240 | ///     Operator::new(Rule::plus, Assoc::Left) | Operator::new(Rule::minus, Assoc::Left), | 
|---|
| 241 | ///     Operator::new(Rule::times, Assoc::Left) | Operator::new(Rule::divide, Assoc::Left), | 
|---|
| 242 | ///     Operator::new(Rule::power, Assoc::Right) | 
|---|
| 243 | /// ]); | 
|---|
| 244 | /// ``` | 
|---|
| 245 | pub fn new(ops: Vec<Operator<R>>) -> PrecClimber<R> { | 
|---|
| 246 | let ops = ops | 
|---|
| 247 | .into_iter() | 
|---|
| 248 | .zip(1..) | 
|---|
| 249 | .fold(Vec::new(), |mut vec, (op, prec)| { | 
|---|
| 250 | let mut next = Some(op); | 
|---|
| 251 |  | 
|---|
| 252 | while let Some(op) = next.take() { | 
|---|
| 253 | let Operator { | 
|---|
| 254 | rule, | 
|---|
| 255 | assoc, | 
|---|
| 256 | next: op_next, | 
|---|
| 257 | } = op; | 
|---|
| 258 |  | 
|---|
| 259 | vec.push((rule, prec, assoc)); | 
|---|
| 260 | next = op_next.map(|op| *op); | 
|---|
| 261 | } | 
|---|
| 262 |  | 
|---|
| 263 | vec | 
|---|
| 264 | }); | 
|---|
| 265 |  | 
|---|
| 266 | PrecClimber { | 
|---|
| 267 | ops: Cow::Owned(ops), | 
|---|
| 268 | } | 
|---|
| 269 | } | 
|---|
| 270 |  | 
|---|
| 271 | /// Performs the precedence climbing algorithm on the `pairs` in a similar manner to map-reduce. | 
|---|
| 272 | /// *Primary* pairs are mapped with `primary` and then reduced to one single result with | 
|---|
| 273 | /// `infix`. | 
|---|
| 274 | /// | 
|---|
| 275 | /// # Panics | 
|---|
| 276 | /// | 
|---|
| 277 | /// Panics will occur when `pairs` is empty or when the alternating *primary*, *operator*, | 
|---|
| 278 | /// *primary* order is not respected. | 
|---|
| 279 | /// | 
|---|
| 280 | /// # Examples | 
|---|
| 281 | /// | 
|---|
| 282 | /// ```ignore | 
|---|
| 283 | /// let primary = |pair| { | 
|---|
| 284 | ///     consume(pair, climber) | 
|---|
| 285 | /// }; | 
|---|
| 286 | /// let infix = |lhs: i32, op: Pair<Rule>, rhs: i32| { | 
|---|
| 287 | ///     match op.rule() { | 
|---|
| 288 | ///         Rule::plus => lhs + rhs, | 
|---|
| 289 | ///         Rule::minus => lhs - rhs, | 
|---|
| 290 | ///         Rule::times => lhs * rhs, | 
|---|
| 291 | ///         Rule::divide => lhs / rhs, | 
|---|
| 292 | ///         Rule::power => lhs.pow(rhs as u32), | 
|---|
| 293 | ///         _ => unreachable!() | 
|---|
| 294 | ///     } | 
|---|
| 295 | /// }; | 
|---|
| 296 | /// | 
|---|
| 297 | /// let result = climber.climb(pairs, primary, infix); | 
|---|
| 298 | /// ``` | 
|---|
| 299 | pub fn climb<'i, P, F, G, T>(&self, mut pairs: P, mut primary: F, mut infix: G) -> T | 
|---|
| 300 | where | 
|---|
| 301 | P: Iterator<Item = Pair<'i, R>>, | 
|---|
| 302 | F: FnMut(Pair<'i, R>) -> T, | 
|---|
| 303 | G: FnMut(T, Pair<'i, R>, T) -> T, | 
|---|
| 304 | { | 
|---|
| 305 | let lhs = primary( | 
|---|
| 306 | pairs | 
|---|
| 307 | .next() | 
|---|
| 308 | .expect( "precedence climbing requires a non-empty Pairs"), | 
|---|
| 309 | ); | 
|---|
| 310 | self.climb_rec(lhs, 0, &mut pairs.peekable(), &mut primary, &mut infix) | 
|---|
| 311 | } | 
|---|
| 312 |  | 
|---|
| 313 | fn climb_rec<'i, P, F, G, T>( | 
|---|
| 314 | &self, | 
|---|
| 315 | mut lhs: T, | 
|---|
| 316 | min_prec: u32, | 
|---|
| 317 | pairs: &mut Peekable<P>, | 
|---|
| 318 | primary: &mut F, | 
|---|
| 319 | infix: &mut G, | 
|---|
| 320 | ) -> T | 
|---|
| 321 | where | 
|---|
| 322 | P: Iterator<Item = Pair<'i, R>>, | 
|---|
| 323 | F: FnMut(Pair<'i, R>) -> T, | 
|---|
| 324 | G: FnMut(T, Pair<'i, R>, T) -> T, | 
|---|
| 325 | { | 
|---|
| 326 | while pairs.peek().is_some() { | 
|---|
| 327 | let rule = pairs.peek().unwrap().as_rule(); | 
|---|
| 328 | if let Some((prec, _)) = self.get(&rule) { | 
|---|
| 329 | if prec >= min_prec { | 
|---|
| 330 | let op = pairs.next().unwrap(); | 
|---|
| 331 | let mut rhs = primary(pairs.next().expect( | 
|---|
| 332 | "infix operator must be followed by \ | 
|---|
| 333 |                          a primary expression", | 
|---|
| 334 | )); | 
|---|
| 335 |  | 
|---|
| 336 | while pairs.peek().is_some() { | 
|---|
| 337 | let rule = pairs.peek().unwrap().as_rule(); | 
|---|
| 338 | if let Some((new_prec, assoc)) = self.get(&rule) { | 
|---|
| 339 | if new_prec > prec || assoc == Assoc::Right && new_prec == prec { | 
|---|
| 340 | rhs = self.climb_rec(rhs, new_prec, pairs, primary, infix); | 
|---|
| 341 | } else { | 
|---|
| 342 | break; | 
|---|
| 343 | } | 
|---|
| 344 | } else { | 
|---|
| 345 | break; | 
|---|
| 346 | } | 
|---|
| 347 | } | 
|---|
| 348 |  | 
|---|
| 349 | lhs = infix(lhs, op, rhs); | 
|---|
| 350 | } else { | 
|---|
| 351 | break; | 
|---|
| 352 | } | 
|---|
| 353 | } else { | 
|---|
| 354 | break; | 
|---|
| 355 | } | 
|---|
| 356 | } | 
|---|
| 357 |  | 
|---|
| 358 | lhs | 
|---|
| 359 | } | 
|---|
| 360 | } | 
|---|
| 361 |  | 
|---|