1use crate::alphabet::Alphabet;
5use crate::dfa::{Dfa, MinDfa};
6use crate::limits::{Budget, Limits};
7use crate::lower::Lower;
8use crate::term::{TermId, TermStore};
9use rexact_syntax::error::{ErrorCode, RexactError};
10use rexact_syntax::Pattern;
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub enum Mode {
16 Full,
18 Search,
20}
21
22pub struct Language {
24 store: TermStore,
25 dfa: Dfa,
26 root: TermId,
27 alphabet: Alphabet,
28 limits: Limits,
29}
30
31fn limit(code: ErrorCode) -> RexactError {
32 RexactError::new(code)
33}
34
35impl Language {
36 pub fn compile(
38 p: &Pattern,
39 alphabet: Option<Alphabet>,
40 mode: Mode,
41 limits: &Limits,
42 ) -> Result<Language, RexactError> {
43 let alphabet = alphabet.unwrap_or_else(|| Alphabet::default_for(p.dialect, p.flags));
44 let mut store = TermStore::new(alphabet.universe());
45 let root = lower_into(&mut store, p, alphabet, mode, limits)?;
46 Self::from_parts(store, root, alphabet, limits)
47 }
48
49 pub fn from_parts(
51 mut store: TermStore,
52 root: TermId,
53 alphabet: Alphabet,
54 limits: &Limits,
55 ) -> Result<Language, RexactError> {
56 let mut budget = Budget::new(limits.clone());
57 let dfa = Dfa::new(&mut store, root, alphabet.name(), &mut budget).map_err(limit)?;
58 Ok(Language {
59 store,
60 dfa,
61 root,
62 alphabet,
63 limits: limits.clone(),
64 })
65 }
66
67 pub fn alphabet(&self) -> Alphabet {
69 self.alphabet
70 }
71 pub fn root(&self) -> TermId {
73 self.root
74 }
75 pub fn store(&self) -> &TermStore {
77 &self.store
78 }
79
80 pub fn matches(&mut self, input: &str) -> Result<bool, RexactError> {
82 let units = self.alphabet.encode(input);
83 self.matches_units(&units)
84 }
85
86 pub fn matches_units(&mut self, input: &[u32]) -> Result<bool, RexactError> {
88 let mut budget = Budget::new(self.limits.clone());
89 self.dfa
90 .matches(&mut self.store, input, &mut budget)
91 .map_err(limit)
92 }
93
94 pub fn minimal(&mut self) -> Result<MinDfa, RexactError> {
96 let mut budget = Budget::new(self.limits.clone());
97 self.dfa
98 .minimize(&mut self.store, &mut budget)
99 .map_err(limit)
100 }
101
102 pub fn fingerprint(&mut self) -> Result<String, RexactError> {
104 Ok(self.minimal()?.fingerprint())
105 }
106
107 pub fn stats(&self) -> (usize, usize) {
109 (self.dfa.num_states(), self.dfa.num_classes())
110 }
111}
112
113pub fn fingerprint_term(
117 store: &mut TermStore,
118 root: TermId,
119 alphabet: Alphabet,
120 limits: &Limits,
121) -> Result<String, RexactError> {
122 let mut budget = Budget::new(limits.clone());
123 let mut dfa = Dfa::new(store, root, alphabet.name(), &mut budget).map_err(limit)?;
124 Ok(dfa
125 .minimize(store, &mut budget)
126 .map_err(limit)?
127 .fingerprint())
128}
129
130pub fn lower_into(
134 store: &mut TermStore,
135 p: &Pattern,
136 alphabet: Alphabet,
137 mode: Mode,
138 limits: &Limits,
139) -> Result<TermId, RexactError> {
140 let mut budget = Budget::new(limits.clone());
141 budget
142 .check_length(p.source.chars().count() as u64)
143 .map_err(limit)?;
144 let (term, _) = {
145 let mut l = Lower {
146 store,
147 alphabet,
148 dialect: p.dialect,
149 chars: p.source.chars().collect(),
150 budget: &mut budget,
151 };
152 l.pattern(p)?
153 };
154 Ok(match mode {
155 Mode::Full => term,
156 Mode::Search => {
157 let any = store.any_symbol();
158 let all = store.star(any);
159 store.concat_all(&[all, term, all])
160 }
161 })
162}
163
164pub fn minimal_of_term(
166 store: &mut TermStore,
167 root: TermId,
168 alphabet: Alphabet,
169 limits: &Limits,
170) -> Result<crate::dfa::MinDfa, RexactError> {
171 let mut budget = Budget::new(limits.clone());
172 let mut dfa = Dfa::new(store, root, alphabet.name(), &mut budget).map_err(limit)?;
173 dfa.minimize(store, &mut budget).map_err(limit)
174}
175
176pub struct Resolved {
179 pub root: crate::resolved::RNode,
181 pub store: TermStore,
183 pub alphabet: Alphabet,
185}
186
187pub fn resolve(
189 p: &Pattern,
190 alphabet: Option<Alphabet>,
191 limits: &Limits,
192) -> Result<Resolved, RexactError> {
193 let alphabet = alphabet.unwrap_or_else(|| Alphabet::default_for(p.dialect, p.flags));
194 let mut store = TermStore::new(alphabet.universe());
195 let mut budget = Budget::new(limits.clone());
196 budget
197 .check_length(p.source.chars().count() as u64)
198 .map_err(limit)?;
199 let root = {
200 let mut l = Lower {
201 store: &mut store,
202 alphabet,
203 dialect: p.dialect,
204 chars: p.source.chars().collect(),
205 budget: &mut budget,
206 };
207 l.resolve_pattern(p)?
208 };
209 Ok(Resolved {
210 root,
211 store,
212 alphabet,
213 })
214}