sudachi/plugin/path_rewrite/
join_numeric.rs1use serde::Deserialize;
18use serde_json::Value;
19
20use self::numeric_parser::NumericParser;
21use crate::analysis::lattice::Lattice;
22use crate::analysis::node::{concat_nodes, LatticeNode, ResultNode};
23use crate::config::Config;
24use crate::dic::category_type::CategoryType;
25use crate::dic::grammar::Grammar;
26use crate::dic::word_info::WordInfoResolver;
27use crate::input_text::InputBuffer;
28use crate::input_text::InputTextIndex;
29use crate::plugin::path_rewrite::PathRewritePlugin;
30use crate::plugin::PluginError;
31use crate::prelude::*;
32
33mod numeric_parser;
34#[cfg(test)]
35mod test;
36
37#[derive(Default)]
39pub struct JoinNumericPlugin {
40 numeric_pos_id: u16,
42 enable_normalize: bool,
44}
45
46#[allow(non_snake_case)]
48#[derive(Deserialize)]
49struct PluginSettings {
50 enableNormalize: Option<bool>,
51}
52
53impl JoinNumericPlugin {
54 fn concat(
55 &self,
56 mut path: Vec<ResultNode>,
57 begin: usize,
58 end: usize,
59 parser: &mut NumericParser,
60 resolver: &dyn WordInfoResolver,
61 ) -> SudachiResult<Vec<ResultNode>> {
62 let word_info = path[begin].word_info();
63
64 if word_info.pos_id() != self.numeric_pos_id {
65 return Ok(path);
66 }
67
68 if self.enable_normalize {
69 let normalized_form = parser.get_normalized();
70 if end - begin > 1 || normalized_form != word_info.normalized_form(resolver) {
71 path = concat_nodes(path, begin, end, Some(normalized_form), resolver)?;
72 }
73 return Ok(path);
74 }
75
76 if end - begin > 1 {
77 path = concat_nodes(path, begin, end, None, resolver)?;
78 }
79 Ok(path)
80 }
81
82 fn rewrite_gen<T: InputTextIndex>(
83 &self,
84 text: &T,
85 mut path: Vec<ResultNode>,
86 resolver: &dyn WordInfoResolver,
87 ) -> SudachiResult<Vec<ResultNode>> {
88 let mut begin_idx = -1;
89 let mut comma_as_digit = true;
90 let mut period_as_digit = true;
91 let mut parser = NumericParser::new();
92 let mut i = -1;
93 while i < path.len() as i32 - 1 {
94 i += 1;
95 let node = &path[i as usize];
96 let ctypes = text.cat_of_range(node.char_range());
97 let s = node.word_info().normalized_form(resolver);
98 if ctypes.intersects(CategoryType::NUMERIC | CategoryType::KANJINUMERIC)
99 || (comma_as_digit && s == ",")
100 || (period_as_digit && s == ".")
101 {
102 if begin_idx < 0 {
103 parser.clear();
104 begin_idx = i;
105 }
106 for c in s.chars() {
107 if !parser.append(&c) {
108 if begin_idx >= 0 {
109 if parser.error_state == numeric_parser::Error::Comma {
110 comma_as_digit = false;
111 i = begin_idx - 1;
112 } else if parser.error_state == numeric_parser::Error::Point {
113 period_as_digit = false;
114 i = begin_idx - 1;
115 }
116 begin_idx = -1;
117 }
118 break;
119 }
120 }
121 continue;
122 }
123
124 let c = if s.len() == 1 {
125 s.as_bytes()[0] as char
127 } else {
128 char::MAX
129 };
130
131 if begin_idx >= 0 {
134 if parser.done() {
135 path =
136 self.concat(path, begin_idx as usize, i as usize, &mut parser, resolver)?;
137 i = begin_idx + 1;
138 } else {
139 let ss = path[i as usize - 1].word_info().normalized_form(resolver);
140 if (parser.error_state == numeric_parser::Error::Comma && ss == ",")
141 || (parser.error_state == numeric_parser::Error::Point && ss == ".")
142 {
143 path = self.concat(
144 path,
145 begin_idx as usize,
146 i as usize - 1,
147 &mut parser,
148 resolver,
149 )?;
150 i = begin_idx + 2;
151 }
152 }
153 }
154 begin_idx = -1;
155 if !comma_as_digit && c != ',' {
156 comma_as_digit = true;
157 }
158 if !period_as_digit && c != '.' {
159 period_as_digit = true;
160 }
161 }
162
163 if begin_idx >= 0 {
165 let len = path.len();
166 if parser.done() {
167 path = self.concat(path, begin_idx as usize, len, &mut parser, resolver)?;
168 } else {
169 let ss = path[len - 1].word_info().normalized_form(resolver);
170 if (parser.error_state == numeric_parser::Error::Comma && ss == ",")
171 || (parser.error_state == numeric_parser::Error::Point && ss == ".")
172 {
173 path = self.concat(path, begin_idx as usize, len - 1, &mut parser, resolver)?;
174 }
175 }
176 }
177
178 Ok(path)
179 }
180}
181
182impl PathRewritePlugin for JoinNumericPlugin {
183 fn set_up(
184 &mut self,
185 settings: &Value,
186 _config: &Config,
187 grammar: &Grammar,
188 ) -> SudachiResult<()> {
189 let settings: PluginSettings =
190 serde_json::from_value(settings.clone()).map_err(PluginError::from)?;
191
192 let numeric_pos_string = vec!["名詞", "数詞", "*", "*", "*", "*"];
194 let numeric_pos_id = grammar.get_part_of_speech_id(&numeric_pos_string).ok_or(
195 SudachiError::InvalidPartOfSpeech(format!("{:?}", numeric_pos_string)),
196 )?;
197 let enable_normalize = settings.enableNormalize;
198
199 self.numeric_pos_id = numeric_pos_id;
200 self.enable_normalize = enable_normalize.unwrap_or(true);
201
202 Ok(())
203 }
204
205 fn rewrite(
206 &self,
207 text: &InputBuffer,
208 path: Vec<ResultNode>,
209 _lattice: &Lattice,
210 resolver: &dyn WordInfoResolver,
211 ) -> SudachiResult<Vec<ResultNode>> {
212 self.rewrite_gen(text, path, resolver)
213 }
214}