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            Expr::RepOnce(expr) => Expr::Seq(expr.clone(), Box::new(Expr::Rep(expr))),
19            Expr::RepExact(expr, num) => (1..num + 1)
20                .map(|_| *expr.clone())
21                .rev()
22                .fold(None, |rep, expr| match rep {
23                    None => Some(expr),
24                    Some(rep) => Some(Expr::Seq(Box::new(expr), Box::new(rep))),
25                })
26                .unwrap(),
27            Expr::RepMin(expr, min) => (1..min + 2)
28                .map(|i| {
29                    if i <= min {
30                        *expr.clone()
31                    } else {
32                        Expr::Rep(expr.clone())
33                    }
34                })
35                .rev()
36                .fold(None, |rep, expr| match rep {
37                    None => Some(expr),
38                    Some(rep) => Some(Expr::Seq(Box::new(expr), Box::new(rep))),
39                })
40                .unwrap(),
41            Expr::RepMax(expr, max) => (1..max + 1)
42                .map(|_| Expr::Opt(expr.clone()))
43                .rev()
44                .fold(None, |rep, expr| match rep {
45                    None => Some(expr),
46                    Some(rep) => Some(Expr::Seq(Box::new(expr), Box::new(rep))),
47                })
48                .unwrap(),
49            Expr::RepMinMax(expr, min, max) => (1..max + 1)
50                .map(|i| {
51                    if i <= min {
52                        *expr.clone()
53                    } else {
54                        Expr::Opt(expr.clone())
55                    }
56                })
57                .rev()
58                .fold(None, |rep, expr| match rep {
59                    None => Some(expr),
60                    Some(rep) => Some(Expr::Seq(Box::new(expr), Box::new(rep))),
61                })
62                .unwrap(),
63            expr => expr,
64        }),
65    }
66}