metrale_grammar/fsm/builder/
mod.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2//
3// FSM builders — port of `cpp/fsm_builder.{h,cc}`.
4//
5//   regex_ir  — the regex intermediate representation + NFA construction
6//   regex     — the regex string parser (`RegexFsmBuilder`)
7//   trie      — the trie / Aho-Corasick builder (`TrieFsmBuilder`)
8//
9// `GrammarFSMBuilder` (TagDispatch / ByteString / RuleRef / Choices) is
10// NOT ported here — it lives in `cpp/grammar_functor.h` and belongs to a
11// later port wave (W3 functors), since it depends on the grammar AST.
12
13pub mod regex;
14pub mod regex_ir;
15pub mod regex_leaf;
16pub mod regex_parse;
17pub mod trie;
18
19use crate::fsm::with_start_end::FsmWithStartEnd;
20
21/// Builder converting a regex string to an FSM.
22///
23/// Mirrors the C++ `RegexFSMBuilder` (a stateless namespace-like class).
24pub struct RegexFsmBuilder;
25
26impl RegexFsmBuilder {
27    /// Parse `regex` and build its NFA. See [`regex::build_regex`].
28    pub fn build(regex: &str) -> Result<FsmWithStartEnd, String> {
29        regex::build_regex(regex)
30    }
31}
32
33/// Builder converting a pattern list to a trie / Aho-Corasick FSM.
34///
35/// Mirrors the C++ `TrieFSMBuilder`.
36pub struct TrieFsmBuilder;
37
38impl TrieFsmBuilder {
39    /// Build a trie-based FSM. See [`trie::build_trie`] for the full
40    /// contract (overlap rules, back edges, excluded patterns).
41    pub fn build(
42        patterns: &[&[u8]],
43        excluded_patterns: &[&[u8]],
44        allow_overlap: bool,
45        add_back_edges: bool,
46    ) -> Option<trie::TrieBuildResult> {
47        trie::build_trie(patterns, excluded_patterns, allow_overlap, add_back_edges)
48    }
49}
50
51#[cfg(test)]
52mod tests {
53    use super::*;
54
55    #[test]
56    fn regex_builder_facade() {
57        let f = RegexFsmBuilder::build("ab*").unwrap();
58        assert!(f.accept_string(b"a"));
59        assert!(f.accept_string(b"abbb"));
60        assert!(!f.accept_string(b"b"));
61    }
62
63    #[test]
64    fn trie_builder_facade() {
65        let pats: Vec<&[u8]> = vec![b"hi", b"hello"];
66        let res = TrieFsmBuilder::build(&pats, &[], true, false).unwrap();
67        assert_eq!(res.end_states.len(), 2);
68    }
69}