sudachi/plugin/path_rewrite/
join_katakana_oov.rs1use serde::Deserialize;
18use serde_json::Value;
19
20use crate::analysis::lattice::Lattice;
21use crate::analysis::node::{concat_oov_nodes, LatticeNode, ResultNode};
22use crate::config::Config;
23use crate::dic::category_type::CategoryType;
24use crate::dic::grammar::Grammar;
25use crate::dic::word_info::WordInfoResolver;
26use crate::input_text::InputBuffer;
27use crate::input_text::InputTextIndex;
28use crate::plugin::path_rewrite::PathRewritePlugin;
29use crate::plugin::PluginError;
30use crate::prelude::*;
31
32#[cfg(test)]
33mod tests;
34
35#[derive(Default)]
37pub struct JoinKatakanaOovPlugin {
38 oov_pos_id: u16,
40 min_length: usize,
42}
43
44#[allow(non_snake_case)]
46#[derive(Deserialize)]
47struct PluginSettings {
48 oovPOS: Vec<String>,
49 minLength: usize,
50}
51
52impl JoinKatakanaOovPlugin {
53 fn is_katakana_node<T: InputTextIndex>(&self, text: &T, node: &ResultNode) -> bool {
54 text.cat_of_range(node.begin()..node.end())
55 .contains(CategoryType::KATAKANA)
56 }
57
58 fn can_oov_bow_node<T: InputTextIndex>(&self, text: &T, node: &ResultNode) -> bool {
64 !text
65 .cat_at_char(node.begin())
66 .contains(CategoryType::NOOOVBOW)
67 }
68
69 fn is_shorter(&self, node: &ResultNode) -> bool {
70 node.num_codepts() < self.min_length
71 }
72
73 fn rewrite_gen(
74 &self,
75 text: &InputBuffer,
76 mut path: Vec<ResultNode>,
77 lattice: &Lattice,
78 resolver: &dyn WordInfoResolver,
79 ) -> SudachiResult<Vec<ResultNode>> {
80 let mut i = 0;
81 loop {
82 if i >= path.len() {
83 break;
84 }
85
86 let node = &path[i];
87 if !(node.is_oov() || self.is_shorter(node)) || !self.is_katakana_node(text, node) {
88 i += 1;
89 continue;
90 }
91 let mut begin = i as i32 - 1;
92 loop {
93 if begin < 0 {
94 break;
95 }
96 if !self.is_katakana_node(text, &path[begin as usize]) {
97 begin += 1;
98 break;
99 }
100 begin -= 1;
101 }
102 let mut begin = if begin < 0 { 0 } else { begin as usize };
103 let mut end = i + 1;
104 loop {
105 if end >= path.len() {
106 break;
107 }
108 if !self.is_katakana_node(text, &path[end]) {
109 break;
110 }
111 end += 1;
112 }
113 while begin != end && !self.can_oov_bow_node(text, &path[begin]) {
114 begin += 1;
115 }
116
117 if (end - begin) > 1 {
118 path =
119 concat_oov_nodes(path, begin, end, self.oov_pos_id, text, lattice, resolver)?;
120 i = begin + 1;
122 }
123 i += 1;
124 }
125
126 Ok(path)
127 }
128}
129
130impl PathRewritePlugin for JoinKatakanaOovPlugin {
131 fn set_up(
132 &mut self,
133 settings: &Value,
134 _config: &Config,
135 grammar: &Grammar,
136 ) -> SudachiResult<()> {
137 let settings: PluginSettings =
138 serde_json::from_value(settings.clone()).map_err(PluginError::from)?;
139
140 let oov_pos_string: Vec<&str> = settings.oovPOS.iter().map(|s| s.as_str()).collect();
141 let oov_pos_id = grammar.get_part_of_speech_id(&oov_pos_string).ok_or(
142 SudachiError::InvalidPartOfSpeech(format!("{:?}", oov_pos_string)),
143 )?;
144 let min_length = settings.minLength;
145
146 self.oov_pos_id = oov_pos_id;
147 self.min_length = min_length;
148
149 Ok(())
150 }
151
152 fn rewrite(
153 &self,
154 text: &InputBuffer,
155 path: Vec<ResultNode>,
156 lattice: &Lattice,
157 resolver: &dyn WordInfoResolver,
158 ) -> SudachiResult<Vec<ResultNode>> {
159 self.rewrite_gen(text, path, lattice, resolver)
160 }
161}