peg_macros/
analysis.rs

1use proc_macro2::Span;
2use std::collections::HashMap;
3
4use crate::ast::*;
5
6pub struct GrammarAnalysis<'a> {
7    pub rules: HashMap<String, &'a Rule>,
8    pub left_recursion: Vec<LeftRecursionError>,
9    pub loop_nullability: Vec<LoopNullabilityError>,
10}
11
12pub fn check<'a>(grammar: &'a Grammar) -> GrammarAnalysis<'a> {
13    let mut rules = HashMap::new();
14
15    // Pick only the first for duplicate rules (the duplicate is reported when translating the rule)
16    for rule in grammar.iter_rules() {
17        rules.entry(rule.name.to_string()).or_insert(rule);
18    }
19
20    let (rule_nullability, left_recursion) = LeftRecursionVisitor::check(grammar, &rules);
21    let loop_nullability = LoopNullabilityVisitor::check(grammar, &rule_nullability);
22
23    GrammarAnalysis {
24        rules,
25        left_recursion,
26        loop_nullability,
27    }
28}
29
30/// Check for infinite loops in the form of left recursion.
31///
32/// If a PEG expression recurses without first consuming input, it will
33/// recurse until the stack overflows.
34struct LeftRecursionVisitor<'a> {
35    stack: Vec<String>,
36    rules: &'a HashMap<String, &'a Rule>,
37    errors: Vec<LeftRecursionError>,
38}
39
40pub struct LeftRecursionError {
41    pub span: Span,
42    pub path: Vec<String>,
43}
44
45impl LeftRecursionError {
46    pub fn msg(&self) -> String {
47        format!(
48            "left recursive rules create an infinite loop: {}",
49            self.path.join(" -> ")
50        )
51    }
52}
53
54impl<'a> LeftRecursionVisitor<'a> {
55    fn check(grammar: &'a Grammar, rules: &HashMap<String, &'a Rule>) -> (HashMap<String, bool>, Vec<LeftRecursionError>) {
56        let mut visitor = LeftRecursionVisitor {
57            rules,
58            errors: Vec::new(),
59            stack: Vec::new(),
60        };
61
62        let mut rule_nullability: HashMap<String, bool> = HashMap::new();
63
64        for rule in grammar.iter_rules() {
65            let nullable = visitor.walk_rule(rule);
66            debug_assert!(visitor.stack.is_empty());
67            rule_nullability.entry(rule.name.to_string()).or_insert(nullable);
68        }
69
70        (rule_nullability, visitor.errors)
71    }
72
73    fn walk_rule(&mut self, rule: &'a Rule) -> bool {
74        self.stack.push(rule.name.to_string());
75        let res = self.walk_expr(&rule.expr);
76        self.stack.pop().unwrap();
77        res
78    }
79
80    /// Walk the prefix of an expression that can be reached without consuming
81    /// input.
82    ///
83    /// Returns true if the rule is known to match completely without consuming
84    /// any input. This is a conservative heuristic, if unknown, we return false
85    /// to avoid reporting false-positives for left recursion.
86    fn walk_expr(&mut self, this_expr: &SpannedExpr) -> bool {
87        use self::Expr::*;
88        match this_expr.expr {
89            RuleExpr(ref rule_ident, _, _) => {
90                let name = rule_ident.to_string();
91
92                if let Some(rule) = self.rules.get(&name) {
93                    if let Some(loop_start) = self
94                        .stack
95                        .iter()
96                        .position(|caller_name| caller_name == &name)
97                    {
98                        let mut recursive_loop = self.stack[loop_start..].to_vec();
99                        recursive_loop.push(name.clone());
100                        match rule.cache {
101                            None | Some(Cache::Simple) =>
102                                self.errors.push(LeftRecursionError {
103                                    path: recursive_loop,
104                                    span: rule_ident.span(),
105                                }),
106                            _ => ()
107
108                        }
109                        return false;
110                    }
111                    self.walk_rule(rule)
112                } else {
113                    // Missing rule would have already been reported
114                   false
115                }
116            }
117
118            ActionExpr(ref elems, ..) => {
119                for elem in elems {
120                    if !self.walk_expr(&elem.expr) {
121                        return false;
122                    }
123                }
124
125                true
126            }
127
128            ChoiceExpr(ref choices) => {
129                let mut nullable = false;
130                for expr in choices {
131                    nullable |= self.walk_expr(expr);
132                }
133                nullable
134            }
135
136            OptionalExpr(ref expr) | PosAssertExpr(ref expr) | NegAssertExpr(ref expr) => {
137                self.walk_expr(expr);
138                true
139            }
140
141            Repeat { ref inner, ref bound, .. } => {
142                let inner_nullable = self.walk_expr(inner);
143                inner_nullable | !bound.has_lower_bound()
144            }
145
146            MatchStrExpr(ref expr) | QuietExpr(ref expr) => self.walk_expr(expr),
147
148            PrecedenceExpr { ref levels } => {
149                let mut nullable = false;
150
151                for level in levels {
152                    for operator in &level.operators {
153                        let mut operator_nullable = true;
154                        for element in &operator.elements {
155                            if !self.walk_expr(&element.expr) {
156                                operator_nullable = false;
157                                break;
158                            }
159                        }
160                        nullable |= operator_nullable;
161                    }
162                }
163
164               nullable
165            }
166
167            | LiteralExpr(_)
168            | PatternExpr(_)
169            | MethodExpr(_, _)
170            | CustomExpr(_)
171            | FailExpr(_)
172            | MarkerExpr(_) => false,
173
174            PositionExpr => true,
175        }
176    }
177}
178
179/// Check for loops whose body can succeed without consuming any input, which
180/// will loop infinitely.
181struct LoopNullabilityVisitor<'a> {
182    rule_nullability: &'a HashMap<String, bool>,
183    errors: Vec<LoopNullabilityError>,
184}
185
186pub struct LoopNullabilityError {
187    pub span: Span,
188}
189
190impl LoopNullabilityError {
191    pub fn msg(&self) -> String {
192        format!("loops infinitely because loop body can match without consuming input")
193    }
194}
195
196
197impl<'a> LoopNullabilityVisitor<'a> {
198    fn check(grammar: &'a Grammar, rule_nullability: &HashMap<String, bool>) -> Vec<LoopNullabilityError> {
199        let mut visitor = LoopNullabilityVisitor {
200            rule_nullability,
201            errors: Vec::new(),
202        };
203
204        for rule in grammar.iter_rules() {
205            visitor.walk_expr(&rule.expr);
206        }
207
208        visitor.errors
209    }
210
211
212    /// Walk an expr and its children analyzing the nullability of loop bodies.
213    ///
214    /// Returns true if the rule is known to match completely without consuming
215    /// any input. This is a conservative heuristic; if unknown, we return false
216    /// to avoid reporting false-positives.
217    ///
218    /// This is very similar to LeftRecursionVisitor::walk_expr, but walks the
219    /// entire expression tree rather than just the nullable prefix, and doesn't
220    /// recurse into calls.
221    fn walk_expr(&mut self, this_expr: &SpannedExpr) -> bool {
222        use self::Expr::*;
223        match this_expr.expr {
224            RuleExpr(ref rule_ident, _, _) => {
225                let name = rule_ident.to_string();
226                *self.rule_nullability.get(&name).unwrap_or(&false)
227            }
228
229            ActionExpr(ref elems, ..) => {
230                let mut nullable = true;
231                for elem in elems {
232                    nullable &= self.walk_expr(&elem.expr);
233                }
234                nullable
235            }
236
237            ChoiceExpr(ref choices) => {
238                let mut nullable = false;
239                for expr in choices {
240                    nullable |= self.walk_expr(expr);
241                }
242                nullable
243            }
244
245            OptionalExpr(ref expr) | PosAssertExpr(ref expr) | NegAssertExpr(ref expr) => {
246                self.walk_expr(expr);
247                true
248            }
249
250            Repeat { ref inner, ref bound, ref sep } => {
251                let inner_nullable = self.walk_expr(inner);
252                let sep_nullable = sep.as_ref().map_or(true, |sep| self.walk_expr(sep));
253
254                // The entire purpose of this analysis: report errors if the loop body is nullable
255                if inner_nullable && sep_nullable && !bound.has_upper_bound() {
256                    self.errors.push(LoopNullabilityError { span: this_expr.span });
257                }
258
259                inner_nullable | !bound.has_lower_bound()
260            }
261
262            MatchStrExpr(ref expr) | QuietExpr(ref expr) => self.walk_expr(expr),
263
264            PrecedenceExpr { ref levels } => {
265                let mut nullable = false;
266
267                for level in levels {
268                    for operator in &level.operators {
269                        let mut operator_nullable = true;
270                        for element in &operator.elements {
271                            operator_nullable &= self.walk_expr(&element.expr);
272                        }
273                        nullable |= operator_nullable;
274                    }
275                }
276
277                nullable
278            }
279
280            | LiteralExpr(_)
281            | PatternExpr(_)
282            | MethodExpr(_, _)
283            | CustomExpr(_)
284            | FailExpr(_)
285            | MarkerExpr(_) => false,
286
287            PositionExpr => true,
288        }
289    }
290}