metrale_grammar/grammar/functor/
mutator.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2//
3// GrammarMutator — the base visitor/transformer pattern.
4// Port of the `GrammarFunctor<int32_t, Grammar>` (a.k.a. `GrammarMutator`)
5// CRTP template from `cpp/grammar_functor.h`.
6//
7// The C++ uses CRTP so a subclass overrides `VisitXxx`. Rust has no CRTP;
8// instead this is a trait with default methods. A pass implements
9// `GrammarMutator` and overrides only the visit methods it cares about.
10// `MutatorState` holds the `base_grammar` + `builder` the C++ keeps as
11// member fields.
12
13use crate::grammar::builder::GrammarBuilder;
14use crate::grammar::data::{GrammarData, TagDispatch};
15use crate::grammar::expr::GrammarExprType;
16
17/// The mutable state every mutator pass carries: the input grammar and
18/// the builder for the output grammar.
19pub struct MutatorState {
20    /// The grammar being transformed (read-only input).
21    pub base: GrammarData,
22    /// The builder accumulating the output grammar.
23    pub builder: GrammarBuilder,
24    /// Name of the rule currently being visited.
25    pub cur_rule_name: String,
26}
27
28impl MutatorState {
29    /// New state wrapping `grammar`, with a fresh empty builder.
30    pub fn new(grammar: GrammarData) -> Self {
31        Self {
32            base: grammar,
33            builder: GrammarBuilder::new(),
34            cur_rule_name: String::new(),
35        }
36    }
37}
38
39/// Decode a `TagDispatch` and rebuild it into `builder` unchanged.
40/// Shared by the default `visit_tag_dispatch` and several passes.
41pub fn rebuild_tag_dispatch(builder: &mut GrammarBuilder, td: &TagDispatch) -> i32 {
42    use crate::grammar::builder::TagDispatchSpec;
43    let spec = TagDispatchSpec {
44        tag_rule_pairs: td.tag_rule_pairs.clone(),
45        stop_eos: td.stop_eos,
46        stop_str: td.stop_str.clone(),
47        loop_after_dispatch: td.loop_after_dispatch,
48        excluded_str: td.excluded_str.clone(),
49    };
50    builder.add_tag_dispatch(&spec)
51}
52
53/// A grammar-transforming pass. Mirrors C++ `GrammarMutator`: the default
54/// `apply` adds empty rules to keep ids stable, then visits each rule
55/// body and lookahead assertion.
56pub trait GrammarMutator {
57    /// The pass's mutable state. Implementors store a `MutatorState`.
58    fn state(&mut self) -> &mut MutatorState;
59
60    /// Run the pass, returning the transformed grammar.
61    fn apply(&mut self, grammar: GrammarData) -> GrammarData {
62        *self.state() = MutatorState::new(grammar);
63        let num_rules = self.state().base.num_rules();
64        for i in 0..num_rules {
65            let name = self.state().base.rule(i).name.clone();
66            self.state()
67                .builder
68                .add_empty_rule(name)
69                .expect("duplicate rule name");
70        }
71        for i in 0..num_rules {
72            let rule = self.state().base.rule(i).clone();
73            self.state().cur_rule_name = rule.name.clone();
74            let new_body = self.visit_expr_id(rule.body_expr_id);
75            self.state()
76                .builder
77                .update_rule_body(i, new_body)
78                .expect("rule id out of range");
79            let new_la = self.visit_lookahead(rule.lookahead_assertion_id);
80            self.state()
81                .builder
82                .update_lookahead_assertion(i, new_la)
83                .expect("rule id out of range");
84        }
85        let root_name = self.state().base.root_rule().name.clone();
86        let builder = std::mem::take(&mut self.state().builder);
87        builder.get(&root_name).expect("root rule not found")
88    }
89
90    /// Visit a lookahead assertion by id (`-1` => `-1`).
91    fn visit_lookahead(&mut self, id: i32) -> i32 {
92        if id == -1 {
93            return -1;
94        }
95        self.visit_expr_id(id)
96    }
97
98    /// Visit an expression by id.
99    fn visit_expr_id(&mut self, id: i32) -> i32 {
100        let expr = self.state().base.owned_expr(id);
101        self.visit_expr(&expr)
102    }
103
104    /// Visit an expression, dispatching on its type.
105    fn visit_expr(&mut self, e: &OwnedExpr) -> i32 {
106        match e.kind {
107            GrammarExprType::Sequence => self.visit_sequence(e),
108            GrammarExprType::Choices => self.visit_choices(e),
109            GrammarExprType::EmptyStr => self.visit_empty_str(e),
110            GrammarExprType::ByteString => self.visit_byte_string(e),
111            GrammarExprType::CharacterClass => self.visit_character_class(e),
112            GrammarExprType::CharacterClassStar => self.visit_character_class_star(e),
113            GrammarExprType::RuleRef => self.visit_rule_ref(e),
114            GrammarExprType::TagDispatch => self.visit_tag_dispatch(e),
115            GrammarExprType::Repeat => self.visit_repeat(e),
116        }
117    }
118
119    /// Default: visit each child, rebuild a `Choices`.
120    fn visit_choices(&mut self, e: &OwnedExpr) -> i32 {
121        let ids: Vec<i32> = e.data.iter().map(|&c| self.visit_expr_id(c)).collect();
122        self.state().builder.add_choices(&ids)
123    }
124
125    /// Default: visit each child, rebuild a `Sequence`.
126    fn visit_sequence(&mut self, e: &OwnedExpr) -> i32 {
127        let ids: Vec<i32> = e.data.iter().map(|&c| self.visit_expr_id(c)).collect();
128        self.state().builder.add_sequence(&ids)
129    }
130
131    /// Default: decode the tag dispatch and rebuild it unchanged.
132    fn visit_tag_dispatch(&mut self, e: &OwnedExpr) -> i32 {
133        let td = e.tag_dispatch.clone().expect("tag dispatch payload");
134        rebuild_tag_dispatch(&mut self.state().builder, &td)
135    }
136
137    /// Default: copy a leaf element verbatim into the builder.
138    fn visit_element(&mut self, e: &OwnedExpr) -> i32 {
139        self.state().builder.add_grammar_expr(e.kind, &e.data)
140    }
141
142    /// Default leaf visitors all defer to `visit_element`.
143    fn visit_empty_str(&mut self, e: &OwnedExpr) -> i32 {
144        self.visit_element(e)
145    }
146    /// Default: copy verbatim.
147    fn visit_byte_string(&mut self, e: &OwnedExpr) -> i32 {
148        self.visit_element(e)
149    }
150    /// Default: copy verbatim.
151    fn visit_character_class(&mut self, e: &OwnedExpr) -> i32 {
152        self.visit_element(e)
153    }
154    /// Default: copy verbatim.
155    fn visit_character_class_star(&mut self, e: &OwnedExpr) -> i32 {
156        self.visit_element(e)
157    }
158    /// Default: copy verbatim.
159    fn visit_rule_ref(&mut self, e: &OwnedExpr) -> i32 {
160        self.visit_element(e)
161    }
162    /// Default: copy verbatim.
163    fn visit_repeat(&mut self, e: &OwnedExpr) -> i32 {
164        self.visit_element(e)
165    }
166}
167
168/// An owned copy of a `GrammarExpr` — needed because visiting borrows
169/// `state` mutably while the expr lives in `state.base`.
170#[derive(Debug, Clone)]
171pub struct OwnedExpr {
172    /// Expression kind.
173    pub kind: GrammarExprType,
174    /// CSR payload.
175    pub data: Vec<i32>,
176    /// Pre-decoded tag dispatch (only set when `kind == TagDispatch`).
177    pub tag_dispatch: Option<TagDispatch>,
178}
179
180impl GrammarData {
181    /// Decode `expr_id` into an [`OwnedExpr`], pre-decoding tag dispatch.
182    pub(crate) fn owned_expr(&self, expr_id: i32) -> OwnedExpr {
183        let e = self.expr(expr_id);
184        let tag_dispatch = if e.kind == GrammarExprType::TagDispatch {
185            Some(self.tag_dispatch(expr_id))
186        } else {
187            None
188        };
189        OwnedExpr {
190            kind: e.kind,
191            data: e.data.to_vec(),
192            tag_dispatch,
193        }
194    }
195}