pest_meta/optimizer/
unroller.rs

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
10use crate::ast::*;
11
12pub fn unroll(rule: Rule) -> Rule {
13    let Rule { name, ty, expr } = rule;
14    Rule {
15        name,
16        ty,
17        expr: expr.map_bottom_up(|expr| match expr {
18            #[cfg(not(feature = "grammar-extras"))]
19            Expr::RepOnce(expr) => Expr::Seq(expr.clone(), Box::new(Expr::Rep(expr))),
20            Expr::RepExact(expr, num) => (1..num + 1)
21                .map(|_| *expr.clone())
22                .rev()
23                .fold(None, |rep, expr| match rep {
24                    None => Some(expr),
25                    Some(rep) => Some(Expr::Seq(Box::new(expr), Box::new(rep))),
26                })
27                .unwrap(),
28            Expr::RepMin(expr, min) => (1..min + 2)
29                .map(|i| {
30                    if i <= min {
31                        *expr.clone()
32                    } else {
33                        Expr::Rep(expr.clone())
34                    }
35                })
36                .rev()
37                .fold(None, |rep, expr| match rep {
38                    None => Some(expr),
39                    Some(rep) => Some(Expr::Seq(Box::new(expr), Box::new(rep))),
40                })
41                .unwrap(),
42            Expr::RepMax(expr, max) => (1..max + 1)
43                .map(|_| Expr::Opt(expr.clone()))
44                .rev()
45                .fold(None, |rep, expr| match rep {
46                    None => Some(expr),
47                    Some(rep) => Some(Expr::Seq(Box::new(expr), Box::new(rep))),
48                })
49                .unwrap(),
50            Expr::RepMinMax(expr, min, max) => (1..max + 1)
51                .map(|i| {
52                    if i <= min {
53                        *expr.clone()
54                    } else {
55                        Expr::Opt(expr.clone())
56                    }
57                })
58                .rev()
59                .fold(None, |rep, expr| match rep {
60                    None => Some(expr),
61                    Some(rep) => Some(Expr::Seq(Box::new(expr), Box::new(rep))),
62                })
63                .unwrap(),
64            expr => expr,
65        }),
66    }
67}