metrale_grammar/grammar/functor/
mod.rs1pub mod analyzer;
32pub mod char_range;
33pub mod constructor;
34pub mod fsm_builder;
35pub mod fsm_hasher;
36pub mod hash_walk;
37pub mod lookahead;
38pub mod mutator;
39pub mod normalizer;
40pub mod optimizer;
41pub mod structure_normalizer;
42pub mod tag_dispatch_fsm;
43
44pub use analyzer::{AllowEmptyRuleAnalyzer, RuleRefGraphFinder, UsedRulesAnalyzer};
45pub use constructor::{GrammarConcat, GrammarUnion, add_sub_grammar};
46pub use fsm_builder::GrammarFsmBuilder;
47pub use fsm_hasher::{GrammarFsmHasher, hash_sequence};
48pub use lookahead::LookaheadAssertionAnalyzer;
49pub use mutator::{GrammarMutator, MutatorState};
50pub use normalizer::{GrammarNormalizer, RootRuleRenamer, SingleElementExprEliminator};
51pub use optimizer::{
52 ByteStringFuser, DeadCodeEliminator, GrammarOptimizer, RepetitionNormalizer, RuleInliner,
53};
54pub use structure_normalizer::StructureNormalizer;
55
56#[cfg(test)]
57mod integration_tests {
58 use crate::grammar::functor::{GrammarNormalizer, GrammarOptimizer};
60 use crate::grammar::parse_ebnf_default;
61 use crate::grammar::printer::print_grammar;
62
63 #[test]
64 fn full_pipeline_byte_string() {
65 let g = parse_ebnf_default("root ::= \"hello world\"\n").unwrap();
66 let norm = GrammarNormalizer::apply(g);
67 let opt = GrammarOptimizer::apply(norm);
68 assert!(opt.optimized);
69 let fsm = opt.per_rule_fsms[opt.root_rule_id() as usize]
70 .as_ref()
71 .unwrap();
72 assert!(fsm.accept_string(b"hello world"));
73 }
74
75 #[test]
76 fn full_pipeline_recursive_grammar() {
77 let ebnf = "root ::= \"(\" root \")\" | \"\"\n";
78 let g = parse_ebnf_default(ebnf).unwrap();
79 let norm = GrammarNormalizer::apply(g);
80 let opt = GrammarOptimizer::apply(norm);
81 assert!(opt.optimized);
82 assert!(!opt.allow_empty_rule_ids.is_empty());
83 }
84
85 #[test]
86 fn full_pipeline_alternation() {
87 let g = parse_ebnf_default("root ::= \"yes\" | \"no\"\n").unwrap();
88 let norm = GrammarNormalizer::apply(g);
89 let opt = GrammarOptimizer::apply(norm);
90 let fsm = opt.per_rule_fsms[opt.root_rule_id() as usize]
91 .as_ref()
92 .unwrap();
93 assert!(fsm.accept_string(b"yes"));
94 assert!(fsm.accept_string(b"no"));
95 assert!(!fsm.accept_string(b"maybe"));
96 }
97
98 #[test]
99 fn normalize_then_print_is_stable() {
100 let g = parse_ebnf_default("root ::= \"a\" sub\nsub ::= \"b\" | \"c\"\n").unwrap();
101 let norm = GrammarNormalizer::apply(g);
102 let printed = print_grammar(&norm);
103 assert!(printed.contains("root ::="));
104 assert!(printed.contains("sub ::="));
105 }
106
107 #[test]
108 fn optimizer_idempotent_on_optimized_grammar() {
109 let g = parse_ebnf_default("root ::= \"x\" \"y\"\n").unwrap();
110 let norm = GrammarNormalizer::apply(g);
111 let opt1 = GrammarOptimizer::apply(norm);
112 let opt2 = GrammarOptimizer::apply(opt1.clone());
113 assert_eq!(print_grammar(&opt1), print_grammar(&opt2));
114 }
115}