cfg_classify/
recursive.rs

1//! Informs us about recursive rules.
2
3use cfg_grammar::{Cfg, CfgRule};
4use cfg_predict_distance::MinimalDistance;
5use cfg_symbol_bit_matrix::{CfgSymbolBitMatrixExt, ReachabilityMatrix};
6
7/// Calculation of parts of grammar that participate in recursion,
8/// be it left-recursion, right-recursion or middle-recursion.
9pub struct Recursion<'a> {
10    grammar: &'a Cfg,
11    derivation: ReachabilityMatrix,
12}
13
14/// Informs us about the kind of recursion in a rule.
15#[derive(Eq, PartialEq, Debug, Clone, Copy)]
16pub struct RecursionKind {
17    /// Rule is left-recursive.
18    pub left: bool,
19    /// Rule has recursion in its middle.
20    pub middle: bool,
21    /// Rule is right-recursive.
22    pub right: bool,
23}
24
25/// Refers to a grammar rule alongside information about
26/// rule recursion and optionally minimal distance to
27/// the closest recursion symbol.
28///
29/// # Example
30///
31/// Recursion is transitive. Here, both `A` and `B` are recursive:
32///
33/// ```ignore
34/// A ::= B c
35/// B ::= A c | c
36/// ```
37#[derive(Eq, PartialEq, Debug, Clone, Copy)]
38pub struct RecursiveRule<'a> {
39    /// Refers to a grammar rule.
40    pub rule: &'a CfgRule,
41    /// Information about recursion.
42    pub recursion: RecursionKind,
43    /// Optionally, minimal dsitance to the closest
44    /// recursion symbol.
45    pub distances: Option<(usize, usize)>,
46}
47
48/// Iterator over recursive rules with information about the
49/// kind of their recursion.
50pub struct RecursiveRules<'a, 'b, R: Iterator<Item = &'b CfgRule>> {
51    rules: R,
52    recursion: &'b Recursion<'a>,
53}
54
55/// Iterator over recursive rules with information about the
56/// kind of their recursion as well as symmetric distance to the
57/// closest recursion.
58pub struct RecursiveRulesWithDistances<'a, 'b, R: Iterator<Item = (usize, &'b CfgRule)>> {
59    rules: R,
60    recursion: &'b Recursion<'a>,
61    minimal_distance: MinimalDistance<'a>,
62}
63
64impl<'a> Recursion<'a> {
65    /// Returns a new `Recursion` for a grammar.
66    pub fn new(grammar: &'a Cfg) -> Self {
67        let reachability = grammar.reachability_matrix();
68
69        Recursion {
70            grammar: grammar,
71            derivation: reachability,
72        }
73    }
74
75    /// Makes an iterator over rules with information about the
76    /// distance to the closest recursion on the RHS.
77    ///
78    /// The distance is [`Symmetric`], meaning it goes both forwards
79    /// and backwars.
80    ///
81    /// [`Symmetric`]: cfg_predict_distance::DistanceDirection::Symmetric
82    pub fn minimal_distances<'b>(&'b self) -> impl Iterator<Item = RecursiveRule<'b>> {
83        let mut minimal_distance = MinimalDistance::new(&self.grammar);
84        let mut dots = vec![];
85        for (idx, rule) in self.grammar.rules().enumerate() {
86            let recursive_dot_pos = rule
87                .rhs
88                .iter()
89                .enumerate()
90                .filter(|&(_dot_pos, &rhs_sym)| self.derivation[(rhs_sym, rule.lhs)]);
91            for (dot_pos, _rhs_sym) in recursive_dot_pos {
92                dots.push((idx, dot_pos));
93            }
94        }
95        minimal_distance.minimal_distances(
96            &dots[..],
97            cfg_predict_distance::DistanceDirection::Symmetric,
98        );
99        RecursiveRulesWithDistances {
100            rules: self.grammar.rules().enumerate(),
101            recursion: self,
102            minimal_distance,
103        }
104    }
105
106    /// Makes an iterator over recursive rules and information
107    /// about their recursion.
108    pub fn recursive_rules<'b>(&'b self) -> impl Iterator<Item = RecursiveRule<'b>> {
109        RecursiveRules {
110            rules: self.grammar.rules(),
111            recursion: self,
112        }
113    }
114}
115
116impl<'a, 'b, R: Iterator<Item = &'b CfgRule>> Iterator for RecursiveRules<'a, 'b, R> {
117    type Item = RecursiveRule<'b>;
118
119    fn next(&mut self) -> Option<Self::Item> {
120        while let Some(rule) = self.rules.next() {
121            if let Some(recursion) = rule_recursion(rule, &self.recursion.derivation) {
122                return Some(RecursiveRule {
123                    rule,
124                    recursion,
125                    distances: None,
126                });
127            }
128        }
129        None
130    }
131}
132
133impl<'a, 'b, R: Iterator<Item = (usize, &'b CfgRule)>> Iterator
134    for RecursiveRulesWithDistances<'a, 'b, R>
135{
136    type Item = RecursiveRule<'b>;
137
138    fn next(&mut self) -> Option<Self::Item> {
139        while let Some((idx, rule)) = self.rules.next() {
140            if let Some(recursion) = rule_recursion(rule, &self.recursion.derivation) {
141                // if self.recursion.derivation[(rule.lhs, rule.lhs)] {
142                let rule_distances = &self.minimal_distance.distances()[idx][..];
143                return Some(RecursiveRule {
144                    rule,
145                    recursion,
146                    distances: Some((
147                        rule_distances[0].unwrap_or(u32::MAX) as usize,
148                        rule_distances[rule_distances.len() - 1].unwrap_or(u32::MAX) as usize,
149                    )),
150                });
151            }
152        }
153        None
154    }
155}
156
157fn rule_recursion(rule: &CfgRule, derivation: &ReachabilityMatrix) -> Option<RecursionKind> {
158    if rule.rhs.len() == 0 {
159        return None;
160    }
161    let rec_kind = RecursionKind {
162        left: rule
163            .rhs
164            .first()
165            .map(|&rhs_sym| derivation[(rhs_sym, rule.lhs)])
166            == Some(true),
167        right: rule
168            .rhs
169            .last()
170            .map(|&rhs_sym| derivation[(rhs_sym, rule.lhs)])
171            == Some(true),
172        middle: rule
173            .rhs
174            .iter()
175            .skip(1)
176            .take(rule.rhs.len().saturating_sub(2))
177            .any(|&rhs_sym| derivation[(rhs_sym, rule.lhs)]),
178    };
179    if rec_kind.any() { Some(rec_kind) } else { None }
180}
181
182impl RecursionKind {
183    fn any(self) -> bool {
184        self.left || self.middle || self.right
185    }
186}