1use crate::analysis::created::CreatedWords;
18use crate::analysis::inner::{Node, NodeIdx};
19use crate::analysis::lattice::Lattice;
20use crate::analysis::node::{LatticeNode, ResultNode};
21use crate::analysis::stateless_tokenizer::{dump_path, split_path};
22use crate::analysis::Mode;
23use crate::dic::connect::ConnectionMatrix;
24use crate::dic::lexicon::LexiconEntry;
25use crate::dic::lexicon_set::LexiconSet;
26use crate::dic::subset::InfoSubset;
27use crate::dic::word_info::WordInfo;
28use crate::dic::DictionaryAccess;
29use crate::error::{SudachiError, SudachiResult};
30use crate::input_text::InputBuffer;
31use crate::plugin::oov::OovProviderPlugin;
32use crate::prelude::MorphemeList;
33
34pub struct StatefulTokenizer<D> {
35 dictionary: D,
36 input: InputBuffer,
37 debug: bool,
38 mode: Mode,
39 oov: Vec<Node>,
40 lattice: Lattice,
41 top_path_ids: Vec<NodeIdx>,
42 top_path: Option<Vec<ResultNode>>,
43 subset: InfoSubset,
44 match_cache: Vec<Vec<LexiconEntry>>,
46 pipelined_lookup: bool,
48}
49
50impl<D: DictionaryAccess + Clone> StatefulTokenizer<D> {
51 pub fn dict_clone(&self) -> D {
53 self.dictionary.clone()
54 }
55}
56
57impl<D: DictionaryAccess> StatefulTokenizer<D> {
58 pub fn new(dic: D, mode: Mode) -> Self {
60 Self::create(dic, false, mode)
61 }
62
63 pub fn create(dic: D, debug: bool, mode: Mode) -> Self {
65 Self {
66 dictionary: dic,
67 input: InputBuffer::default(),
68 debug,
69 mode,
70 oov: Vec::with_capacity(10),
71 lattice: Lattice::default(),
72 top_path_ids: Vec::new(),
73 top_path: Some(Vec::new()),
74 subset: InfoSubset::all(),
75 match_cache: Vec::new(),
76 pipelined_lookup: true,
77 }
78 }
79
80 pub fn set_pipelined_lookup(&mut self, enabled: bool) -> bool {
84 std::mem::replace(&mut self.pipelined_lookup, enabled)
85 }
86
87 pub fn set_debug(&mut self, debug: bool) -> bool {
89 std::mem::replace(&mut self.debug, debug)
90 }
91
92 pub fn set_mode(&mut self, mode: Mode) -> Mode {
94 self.subset |= match mode {
95 Mode::A => InfoSubset::SPLIT_A,
96 Mode::B => InfoSubset::SPLIT_B,
97 _ => InfoSubset::empty(),
98 };
99 std::mem::replace(&mut self.mode, mode)
100 }
101
102 pub fn mode(&self) -> Mode {
104 self.mode
105 }
106
107 pub fn set_subset(&mut self, subset: InfoSubset) -> InfoSubset {
109 let mode_subset = match self.mode {
110 Mode::A => InfoSubset::SPLIT_A,
111 Mode::B => InfoSubset::SPLIT_B,
112 _ => InfoSubset::empty(),
113 };
114 let new_subset = (subset | mode_subset).normalize();
115 std::mem::replace(&mut self.subset, new_subset | mode_subset)
116 }
117
118 pub fn reset(&mut self) -> &mut String {
121 if let Some(p) = self.top_path.as_mut() {
122 p.clear()
123 }
124 self.oov.clear();
125 self.input.reset()
126 }
127
128 pub fn dict(&self) -> &D {
130 &self.dictionary
131 }
132
133 pub fn do_tokenize(&mut self) -> SudachiResult<()> {
136 self.input.start_build()?;
137 self.rewrite_input()?;
138 self.input.build(self.dictionary.grammar())?;
139
140 if self.input.current().is_empty() {
141 return Ok(());
142 }
143
144 let debug = self.debug;
145
146 if debug {
147 println!("=== Input dump:\n{}", self.input.current());
148 }
149
150 self.build_lattice()?;
151
152 if debug {
153 println!("=== Lattice dump:");
154 let dict = &self.dictionary;
155 let mut writer = std::io::stdout();
156 self.lattice
157 .dump(&self.input, dict.grammar(), dict.lexicon(), &mut writer)?;
158 };
159
160 let mut path = self.resolve_best_path()?;
161
162 if debug {
163 println!("=== Before Rewriting:");
164 dump_path(&path);
165 };
166
167 for plugin in self.dictionary.path_rewrite_plugins() {
168 path = plugin.rewrite(&self.input, path, &self.lattice, self.dictionary.lexicon())?;
169 }
170
171 path = split_path(&self.dictionary, path, self.mode, self.subset, &self.input)?;
172
173 if debug {
174 println!("=== After Rewriting:");
175 dump_path(&path);
176 println!("===");
177 };
178
179 self.top_path = Some(path);
180
181 Ok(())
182 }
183
184 fn resolve_best_path(&mut self) -> SudachiResult<Vec<ResultNode>> {
186 let lexset = self.dictionary.lexicon();
187 let mut path = self.top_path.take().unwrap_or_default();
188 self.lattice.fill_top_path(&mut self.top_path_ids);
189 self.top_path_ids.reverse();
190 for pid in self.top_path_ids.drain(..) {
191 let (inner, cost) = self.lattice.node(pid);
192 let wi = if inner.word_id().is_oov() {
193 let curr_slice = self.input.curr_slice_c(inner.char_range()).to_owned();
194 WordInfo::new_oov(
195 inner.word_id().entry().as_raw() as u16,
196 curr_slice.len() as i16,
197 inner.word_id(),
198 curr_slice,
199 )
200 } else {
201 lexset.get_word_info_subset(inner.word_id(), self.subset)?
202 };
203
204 let byte_begin = self.input.to_curr_byte_idx(inner.begin());
205 let byte_end = self.input.to_curr_byte_idx(inner.end());
206
207 path.push(ResultNode::new(
208 inner.clone(),
209 cost,
210 byte_begin as u16,
211 byte_end as u16,
212 wi,
213 ));
214 }
215 Ok(path)
216 }
217
218 pub fn swap_result(
220 &mut self,
221 input: &mut InputBuffer,
222 result: &mut Vec<ResultNode>,
223 subset: &mut InfoSubset,
224 ) {
225 std::mem::swap(&mut self.input, input);
226 std::mem::swap(self.top_path.as_mut().unwrap(), result);
227 *subset = self.subset;
228 }
229
230 fn rewrite_input(&mut self) -> SudachiResult<()> {
231 for p in self.dictionary.input_text_plugins() {
232 p.rewrite(&mut self.input)?;
233 }
234 Ok(())
235 }
236
237 fn build_lattice(&mut self) -> SudachiResult<()> {
238 let mut builder = LatticeBuilder {
239 node_buffer: &mut self.oov,
240 lattice: &mut self.lattice,
241 matrix: self.dictionary.grammar().conn_matrix(),
242 oov_providers: self.dictionary.oov_provider_plugins(),
243 lexicon: self.dictionary.lexicon(),
244 input: &self.input,
245 match_cache: &mut self.match_cache,
246 pipelined: self.pipelined_lookup,
247 };
248 builder.build_lattice()
249 }
250
251 pub fn into_morpheme_list(self) -> SudachiResult<MorphemeList<D>> {
253 match self.top_path {
254 None => Err(SudachiError::EosBosDisconnect),
255 Some(path) => Ok(MorphemeList::from_components(
256 self.dictionary,
257 self.input,
258 path,
259 self.subset,
260 )),
261 }
262 }
263}
264
265struct LatticeBuilder<'a> {
268 node_buffer: &'a mut Vec<Node>,
269 lattice: &'a mut Lattice,
270 matrix: &'a ConnectionMatrix<'a>,
271 input: &'a InputBuffer,
272 lexicon: &'a LexiconSet<'a>,
273 oov_providers: &'a [Box<dyn OovProviderPlugin + Sync + Send>],
274 match_cache: &'a mut Vec<Vec<LexiconEntry>>,
275 pipelined: bool,
276}
277
278impl<'a> LatticeBuilder<'a> {
279 #[inline]
280 fn build_lattice(&mut self) -> SudachiResult<()> {
281 self.lattice.reset(self.input.current_chars().len());
282 if self.pipelined {
283 self.build_lattice_pipelined()
284 } else {
285 self.build_lattice_scalar()
286 }
287 }
288
289 #[inline]
292 fn build_lattice_scalar(&mut self) -> SudachiResult<()> {
293 let input_bytes = self.input.current().as_bytes();
294
295 for (ch_off, &byte_off) in self.input.curr_byte_offsets().iter().enumerate() {
296 if !self.lattice.has_previous_node(ch_off) {
297 continue;
298 }
299
300 self.node_buffer.clear();
301 let mut created = CreatedWords::default();
302 for e in self.lexicon.lookup(input_bytes, byte_off) {
303 if (e.end < input_bytes.len()) && !self.input.can_bow(e.end) {
305 continue;
306 }
307 let (left_id, right_id, cost) = self.lexicon.get_word_param(e.word_id);
308 let end_c = self.input.ch_idx(e.end);
309 let node = Node::new(
310 ch_off as u16,
311 end_c as u16,
312 left_id as u16,
313 right_id as u16,
314 cost,
315 e.word_id,
316 );
317 created = created.add_word((end_c - ch_off) as i64);
318 self.node_buffer.push(node.clone());
319 self.lattice.insert(node, self.matrix);
320 }
321
322 self.insert_oovs(ch_off, created)?;
323 }
324 self.lattice.connect_eos(self.matrix)?;
325
326 Ok(())
327 }
328
329 #[inline]
334 fn build_lattice_pipelined(&mut self) -> SudachiResult<()> {
335 let input_bytes = self.input.current().as_bytes();
336
337 {
338 let lexicon = self.lexicon;
339 let starts = self.input.curr_byte_offsets();
340 let cache = &mut *self.match_cache;
341 if cache.len() < starts.len() {
342 cache.resize_with(starts.len(), Vec::new);
343 }
344 for bucket in cache.iter_mut() {
345 bucket.clear();
346 }
347 lexicon.lookup_batch(input_bytes, starts, |bucket, entry| {
348 cache[bucket].push(entry);
349 });
350 }
351
352 let boundaries = self.input.curr_byte_offsets().len();
353 for ch_off in 0..boundaries {
354 if !self.lattice.has_previous_node(ch_off) {
355 continue;
356 }
357
358 self.node_buffer.clear();
359 let mut created = CreatedWords::default();
360 for entry in &self.match_cache[ch_off] {
361 let (word_id, end) = (entry.word_id, entry.end);
362 if (end < input_bytes.len()) && !self.input.can_bow(end) {
363 continue;
364 }
365 let (left_id, right_id, cost) = self.lexicon.get_word_param(word_id);
366 let end_c = self.input.ch_idx(end);
367 let node = Node::new(
368 ch_off as u16,
369 end_c as u16,
370 left_id as u16,
371 right_id as u16,
372 cost,
373 word_id,
374 );
375 created = created.add_word((end_c - ch_off) as i64);
376 self.node_buffer.push(node.clone());
377 self.lattice.insert(node, self.matrix);
378 }
379
380 self.insert_oovs(ch_off, created)?;
381 }
382 self.lattice.connect_eos(self.matrix)?;
383
384 Ok(())
385 }
386
387 #[inline]
390 fn insert_oovs(&mut self, ch_off: usize, mut created: CreatedWords) -> SudachiResult<()> {
391 if self.input.can_oov_bow(ch_off) {
392 for provider in self.oov_providers {
393 created = self.provide_oovs(ch_off, created, provider.as_ref())?;
394 }
395 }
396
397 if created.is_empty() {
398 let provider = self.oov_providers.last().unwrap();
399 created = self.provide_oovs(ch_off, created, provider.as_ref())?;
400 }
401
402 if created.is_empty() {
403 return Err(SudachiError::EosBosDisconnect);
404 }
405 Ok(())
406 }
407
408 #[inline]
409 fn provide_oovs<P>(
410 &mut self,
411 char_offset: usize,
412 mut other: CreatedWords,
413 plugin: &P,
414 ) -> SudachiResult<CreatedWords>
415 where
416 P: OovProviderPlugin + 'a + ?Sized,
417 {
418 let start_size = self.node_buffer.len();
419 let num_provided = plugin.provide_oov(self.input, char_offset, other, self.node_buffer)?;
420 for idx in start_size..(start_size + num_provided) {
421 let node = self.node_buffer[idx].clone();
422 other = other.add_word(node.char_range().len() as i64);
423 self.lattice.insert(node, self.matrix);
424 }
425 Ok(other)
426 }
427}