cfg_classify/
useful.rs

1//! Analysis of rule usefulness.
2
3use bit_matrix::BitMatrix;
4use bit_vec::BitVec;
5
6use crate::derivation;
7use cfg_grammar::rhs_closure::RhsClosure;
8use cfg_grammar::rule::RuleRef;
9use cfg_grammar::symbol::set::SymbolBitSet;
10use cfg_grammar::RuleContainer;
11use cfg_symbol::Symbol;
12
13/// Contains the information about usefulness of the grammar's rules.
14/// Useful rules are both reachable and productive.
15pub struct Usefulness<G> {
16    grammar: G,
17    reachability: BitMatrix,
18    reachable_syms: BitVec,
19    productivity: BitVec,
20}
21
22/// An iterator over the grammar's useless rules.
23pub struct UselessRules<'a, G, I>
24where
25    G: 'a,
26{
27    usefulness: &'a Usefulness<&'a mut G>,
28    rules: I,
29}
30
31/// A reference to a useless rule, together with the reason for its uselessness.
32#[derive(Clone, Debug)]
33pub struct UselessRule<R> {
34    /// Reference to a rule.
35    rule: R,
36    usefulness: RuleUsefulness,
37}
38
39#[derive(Copy, Clone, Debug)]
40pub struct RuleUsefulness {
41    /// Indicates whether the rule is unreachable.
42    reachable: bool,
43    /// Indicates whether the rule is unproductive.
44    productive: bool,
45}
46
47impl<R> UselessRule<R> {
48    pub fn rule(&self) -> &R {
49        &self.rule
50    }
51
52    pub fn usefulness(&self) -> &RuleUsefulness {
53        &self.usefulness
54    }
55}
56
57impl RuleUsefulness {
58    fn is_useless(&self) -> bool {
59        !self.reachable || !self.productive
60    }
61}
62
63/// Returns the set of unused symbols.
64fn unused_syms<'a, G>(grammar: &'a G) -> BitVec
65where
66    G: RuleContainer,
67{
68    let mut result = used_syms(grammar);
69    result.negate();
70    result
71}
72
73/// Returns the set of used symbols.
74fn used_syms<'a, G>(grammar: &'a G) -> BitVec
75where
76    G: RuleContainer,
77{
78    let num_syms = grammar.sym_source().num_syms();
79    let mut used_syms = BitVec::from_elem(num_syms, false);
80
81    for rule in grammar.rules() {
82        used_syms.set(rule.lhs.usize(), true);
83        for &sym in rule.rhs {
84            used_syms.set(sym.usize(), true);
85        }
86    }
87    used_syms
88}
89
90/// Returns the set of productive symbols.
91fn productive_syms<G>(grammar: &G) -> BitVec
92where
93    G: RuleContainer,
94{
95    let mut productive_syms = SymbolBitSet::terminal_or_nulling_set(grammar).into_bit_vec();
96    RhsClosure::new(grammar).rhs_closure(&mut productive_syms);
97    productive_syms
98}
99
100impl<'a, G> Usefulness<&'a mut G>
101where
102    G: RuleContainer,
103{
104    /// Analyzes usefulness of the grammar's rules. In particular, it checks for reachable
105    /// and productive symbols.
106    pub fn new(grammar: &'a mut G) -> Usefulness<&'a mut G> {
107        let mut productivity = productive_syms(grammar);
108        let reachability = derivation::reachability_matrix(grammar);
109        let unused_syms = unused_syms(grammar);
110        let mut reachable_syms = BitVec::from_elem(grammar.sym_source().num_syms(), false);
111
112        productivity.or(&unused_syms);
113        reachable_syms.or(&unused_syms);
114
115        debug_assert_eq!(
116            reachability.size(),
117            (productivity.len(), productivity.len())
118        );
119
120        Usefulness {
121            grammar: grammar,
122            productivity: productivity,
123            reachability: reachability,
124            reachable_syms: reachable_syms,
125        }
126    }
127
128    /// Checks whether a symbol is productive. Can be used to determine the precise reason
129    /// of a rule's unproductiveness.
130    pub fn productivity(&self, sym: Symbol) -> bool {
131        self.productivity[sym.usize()]
132    }
133
134    /// Sets symbol reachability. Takes an array of reachable symbols.
135    pub fn reachable<Sr>(mut self, syms: Sr) -> Self
136    where
137        Sr: AsRef<[Symbol]>,
138    {
139        for &sym in syms.as_ref().iter() {
140            let reachability =
141                self.reachability[sym.usize()].iter_bits(self.grammar.sym_source().num_syms());
142            for (i, is_reachable) in reachability.enumerate() {
143                if is_reachable {
144                    self.reachable_syms.set(i, true);
145                }
146            }
147        }
148        self
149    }
150
151    /// Checks whether all rules in the grammar are useful.
152    pub fn all_useful(&self) -> bool {
153        self.productivity.all() && self.reachable_syms.all()
154    }
155
156    /// Checks whether all rules in the grammar are productive.
157    pub fn all_productive(&self) -> bool {
158        self.productivity.all()
159    }
160
161    /// Checks whether all rules in the grammar are reachable.
162    pub fn all_reachable(&self) -> bool {
163        self.reachable_syms.all()
164    }
165
166    pub fn rule_usefulness(&self, rule: RuleRef) -> RuleUsefulness {
167        let productive = rule.rhs.iter().all(|sym| self.productivity[sym.usize()]);
168        let reachable = self.reachable_syms[rule.lhs.usize()];
169        RuleUsefulness {
170            productive,
171            reachable,
172        }
173    }
174
175    /// Returns an iterator over the grammar's useless rules.
176    pub fn useless_rules(&'a self) -> UselessRules<'a, G, impl Iterator<Item = RuleRef<'a>>> {
177        UselessRules {
178            rules: self.grammar.rules(),
179            usefulness: self,
180        }
181    }
182}
183
184// Watch out: Normal type bounds conflict with HRTB.
185impl<'a, G> Usefulness<&'a mut G>
186where
187    G: RuleContainer,
188{
189    /// Removes useless rules. The language represented by the grammar doesn't change.
190    pub fn remove_useless_rules(&mut self) {
191        if !self.all_useful() {
192            let productivity = &self.productivity;
193            let reachable_syms = &self.reachable_syms;
194            let rule_is_useful = |rule: RuleRef| {
195                let productive = rule.rhs.iter().all(|sym| productivity[sym.usize()]);
196                let reachable = reachable_syms[rule.lhs.usize()];
197                productive && reachable
198            };
199            self.grammar.retain(rule_is_useful);
200        }
201    }
202}
203
204impl<'a, G, I> Iterator for UselessRules<'a, G, I>
205where
206    G: RuleContainer,
207    I: Iterator<Item = RuleRef<'a>>,
208{
209    type Item = UselessRule<I::Item>;
210
211    fn next(&mut self) -> Option<Self::Item> {
212        if self.usefulness.all_useful() {
213            return None;
214        }
215
216        for rule in &mut self.rules {
217            let usefulness = self.usefulness.rule_usefulness(rule);
218
219            if usefulness.is_useless() {
220                return Some(UselessRule { rule, usefulness });
221            }
222        }
223
224        None
225    }
226}