metrale_grammar/grammar/functor/
optimizer_pipeline.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2//
3// Optimization pipeline — `RepetitionNormalizer` and `GrammarOptimizer`.
4// Split out of `optimizer.rs` to keep each file under the 250-line cap.
5// Port of the corresponding functors in `cpp/grammar_functor.cc`.
6
7use crate::grammar::data::GrammarData;
8use crate::grammar::expr::GrammarExprType;
9use crate::grammar::functor::analyzer::AllowEmptyRuleAnalyzer;
10use crate::grammar::functor::fsm_builder::GrammarFsmBuilder;
11use crate::grammar::functor::lookahead::LookaheadAssertionAnalyzer;
12use crate::grammar::functor::optimizer::{ByteStringFuser, DeadCodeEliminator, RuleInliner};
13
14/// Normalize repetition ranges: if the repeated rule is nullable, the
15/// minimum repeat count is forced to 0. Operates in place.
16pub struct RepetitionNormalizer;
17
18impl RepetitionNormalizer {
19    /// Run the pass on `grammar` in place.
20    pub fn apply(grammar: &mut GrammarData) {
21        for i in 0..grammar.num_exprs() {
22            let expr = grammar.expr(i);
23            if expr.kind != GrammarExprType::Repeat {
24                continue;
25            }
26            let repeat_rule_id = expr.data[0];
27            grammar.rule_mut(repeat_rule_id).is_exact_lookahead = true;
28            if grammar
29                .allow_empty_rule_ids
30                .binary_search(&repeat_rule_id)
31                .is_ok()
32            {
33                grammar.set_expr_data(i, 1, 0);
34            }
35        }
36    }
37}
38
39/// Full optimization pipeline. Port of `GrammarOptimizer`.
40pub struct GrammarOptimizer;
41
42impl GrammarOptimizer {
43    /// Apply byte fusion, inlining, dead-code elimination, lookahead
44    /// analysis, allow-empty analysis, repetition normalization and FSM
45    /// construction. Always returns a new (optimized) grammar.
46    pub fn apply(grammar: GrammarData) -> GrammarData {
47        let mut result = ByteStringFuser::apply(grammar);
48        result = RuleInliner::apply(result);
49        result = DeadCodeEliminator::apply(result);
50        result = LookaheadAssertionAnalyzer::apply(result);
51        result.allow_empty_rule_ids = AllowEmptyRuleAnalyzer::apply(&result);
52        RepetitionNormalizer::apply(&mut result);
53        GrammarFsmBuilder::apply(&mut result);
54        result.optimized = true;
55        result
56    }
57}