1 /*
2 * 文節に対して候補のリストを生成する。
3 * make_candidates()がcontext管理部から呼ばれる。
4 *
5 * 候補の生成は次の方法で行う
6 * (1)splitterが割り当てた品詞に対してproc_splitter_info()
7 * から候補を生成する
8 * (2)ひらがなのみとカタカナのみの候補を生成する
9 * (3)最後の文字を助詞と解釈して無理矢理候補を生成する
10 */
11 /*
12 * Funded by IPA未踏ソフトウェア創造事業 2001 9/30
13 * Copyright (C) 2000-2005 TABATA Yusuke
14 * Copyright (C) 2004-2005 YOSHIDA Yuichi
15 * Copyright (C) 2002 UGAWA Tomoharu
16 *
17 */
18 /*
19 This library is free software; you can redistribute it and/or
20 modify it under the terms of the GNU Lesser General Public
21 License as published by the Free Software Foundation; either
22 version 2 of the License, or (at your option) any later version.
23
24 This library is distributed in the hope that it will be useful,
25 but WITHOUT ANY WARRANTY; without even the implied warranty of
26 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
27 Lesser General Public License for more details.
28
29 You should have received a copy of the GNU Lesser General Public
30 License along with this library; if not, write to the Free Software
31 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
32 */
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36
37 #include <anthy/dic.h>
38 #include <anthy/splitter.h>
39 #include <anthy/segment.h>
40 #include "wordborder.h"
41
42
43 static struct cand_ent *
alloc_cand_ent(void)44 alloc_cand_ent(void)
45 {
46 struct cand_ent *ce;
47 ce = (struct cand_ent *)malloc(sizeof(struct cand_ent));
48 ce->nr_words = 0;
49 ce->elm = NULL;
50 ce->mw = NULL;
51 ce->core_elm_index = -1;
52 ce->dep_word_hash = 0;
53 return ce;
54 }
55
56 /*
57 * 候補を複製する
58 */
59 static struct cand_ent *
dup_candidate(struct cand_ent * ce)60 dup_candidate(struct cand_ent *ce)
61 {
62 struct cand_ent *ce_new;
63 int i;
64 ce_new = alloc_cand_ent();
65 ce_new->nr_words = ce->nr_words;
66 ce_new->str.len = ce->str.len;
67 ce_new->str.str = anthy_xstr_dup_str(&ce->str);
68 ce_new->elm = malloc(sizeof(struct cand_elm)*ce->nr_words);
69 ce_new->flag = ce->flag;
70 ce_new->core_elm_index = ce->core_elm_index;
71 ce_new->mw = ce->mw;
72 ce_new->score = ce->score;
73 ce_new->dep_word_hash = ce->dep_word_hash;
74
75 for (i = 0 ; i < ce->nr_words ; i++) {
76 ce_new->elm[i] = ce->elm[i];
77 }
78 return ce_new;
79 }
80
81 /** 文節に候補を追加する */
82 static void
push_back_candidate(struct seg_ent * seg,struct cand_ent * ce)83 push_back_candidate(struct seg_ent *seg, struct cand_ent *ce)
84 {
85 /* seg_entに候補ceを追加 */
86 seg->nr_cands++;
87 seg->cands = (struct cand_ent **)
88 realloc(seg->cands, sizeof(struct cand_ent *) * seg->nr_cands);
89 seg->cands[seg->nr_cands - 1] = ce;
90 /**/
91 if (anthy_splitter_debug_flags() & SPLITTER_DEBUG_CAND) {
92 anthy_print_candidate(ce);
93 printf("\n");
94 }
95 }
96
97 static void
push_back_guessed_candidate(struct seg_ent * seg)98 push_back_guessed_candidate(struct seg_ent *seg)
99 {
100 xchar xc;
101 xstr *xs;
102 struct cand_ent *ce;
103 if (seg->str.len < 2) {
104 return ;
105 }
106 /* 最後の文字は助詞か? */
107 xc = seg->str.str[seg->str.len - 1];
108 if (!(anthy_get_xchar_type(xc) & XCT_DEP)) {
109 return ;
110 }
111 /* 最後の文字以外をカタカナにしてみる */
112 ce = alloc_cand_ent();
113 xs = anthy_xstr_hira_to_kata(&seg->str);
114 xs->str[xs->len-1] = xc;
115 ce->str.str = anthy_xstr_dup_str(xs);
116 ce->str.len = xs->len;
117 ce->flag = CEF_GUESS;
118 anthy_free_xstr(xs);
119 push_back_candidate(seg, ce);
120 }
121
122 /** 再帰で1単語ずつ候補を割当てていく */
123 static int
enum_candidates(struct seg_ent * seg,struct cand_ent * ce,int from,int n)124 enum_candidates(struct seg_ent *seg,
125 struct cand_ent *ce,
126 int from, int n)
127 {
128 int i, p;
129 struct cand_ent *cand;
130 int nr_cands = 0;
131 int pos;
132
133 if (n == ce->mw->nr_parts) {
134 /* 完成形 */
135 /* 文節後部の解析しなかった部分を候補文字列に追加 */
136 xstr tail;
137 tail.len = seg->len - from;
138 tail.str = &seg->str.str[from];
139 anthy_xstrcat(&ce->str, &tail);
140 if (ce->str.str && (0 < ce->str.len)) { /* 辞書もしくは学習データが壊れていた時の対策 */
141 push_back_candidate(seg, dup_candidate(ce));
142 }
143 return 1;
144 }
145
146 p = anthy_get_nr_dic_ents(ce->elm[n].se, &ce->elm[n].str);
147
148 /* 品詞が割当てられているので、その品詞にマッチするものを割当てる */
149 for (i = 0; i < p; i++) {
150 wtype_t wt;
151 if (anthy_get_nth_dic_ent_is_compound(ce->elm[n].se, i)) {
152 continue;
153 }
154 anthy_get_nth_dic_ent_wtype(ce->elm[n].se, &ce->elm[n].str, i, &wt);
155
156 if (anthy_wtype_equal (ce->elm[n].wt, wt)) {
157 xstr word, yomi;
158
159 yomi.len = ce->elm[n].str.len;
160 yomi.str = &seg->str.str[from];
161 cand = dup_candidate(ce);
162 anthy_get_nth_dic_ent_str(cand->elm[n].se,
163 &yomi, i, &word);
164 cand->elm[n].nth = i;
165 cand->elm[n].id = anthy_xstr_hash(&word);
166
167 /* 単語の本体 */
168 anthy_xstrcat(&cand->str, &word);
169 free(word.str);
170 /* 自分を再帰呼び出しして続きを割り当てる */
171 nr_cands += enum_candidates(seg, cand,
172 from + yomi.len,
173 n+1);
174 anthy_release_cand_ent(cand);
175 }
176 }
177
178 /* 品詞不定の場合には未変換で次の単語へ行く */
179 pos = anthy_wtype_get_pos(ce->elm[n].wt);
180 if (nr_cands == 0 || pos == POS_INVAL || pos == POS_NONE) {
181 xstr xs;
182 xs.len = ce->elm[n].str.len;
183 xs.str = &seg->str.str[from];
184 cand = dup_candidate(ce);
185 cand->elm[n].nth = -1;
186 cand->elm[n].id = -1;
187 anthy_xstrcat(&cand->str, &xs);
188 nr_cands = enum_candidates(seg,cand,
189 from + xs.len,
190 n + 1);
191 anthy_release_cand_ent(cand);
192 return nr_cands;
193 }
194
195 return nr_cands;
196 }
197
198 /**
199 * 文節全体を含む一単語(単漢字を含む)の候補を生成する
200 */
201 static void
push_back_singleword_candidate(struct seg_ent * seg,int is_reverse)202 push_back_singleword_candidate(struct seg_ent *seg,
203 int is_reverse)
204 {
205 seq_ent_t se;
206 struct cand_ent *ce;
207 wtype_t wt;
208 int i, n;
209 xstr xs;
210
211 se = anthy_get_seq_ent_from_xstr(&seg->str, is_reverse);
212 n = anthy_get_nr_dic_ents(se, &seg->str);
213 /* 辞書の各エントリに対して */
214 for (i = 0; i < n; i++) {
215 int ct;
216 if (anthy_get_nth_dic_ent_is_compound(se, i)) {
217 continue;
218 }
219 /* 品詞を取り出して */
220 anthy_get_nth_dic_ent_wtype(se, &seg->str, i, &wt);
221 ct = anthy_wtype_get_ct(wt);
222 /* 終止形か活用しないものの原形なら */
223 if (ct == CT_SYUSI || ct == CT_NONE) {
224 ce = alloc_cand_ent();
225 anthy_get_nth_dic_ent_str(se,&seg->str, i, &xs);
226 ce->str.str = xs.str;
227 ce->str.len = xs.len;
228 ce->flag = CEF_SINGLEWORD;
229 push_back_candidate(seg, ce);
230 }
231 }
232 }
233
234 static void
push_back_noconv_candidate(struct seg_ent * seg)235 push_back_noconv_candidate(struct seg_ent *seg)
236 {
237 /* 無変換で片仮名になる候補と平仮名のみになる候補を追加 */
238 struct cand_ent *ce;
239 xstr *xs;
240
241 /* ひらがなのみ */
242 ce = alloc_cand_ent();
243 ce->str.str = anthy_xstr_dup_str(&seg->str);
244 ce->str.len = seg->str.len;
245 ce->flag = CEF_HIRAGANA;
246 push_back_candidate(seg, ce);
247
248 /* 次にカタカナ */
249 ce = alloc_cand_ent();
250 xs = anthy_xstr_hira_to_kata(&seg->str);
251 ce->str.str = anthy_xstr_dup_str(xs);
252 ce->str.len = xs->len;
253 ce->flag = CEF_KATAKANA;
254 anthy_free_xstr(xs);
255 push_back_candidate(seg, ce);
256
257 /* 記号のみの文節 */
258 xs = anthy_conv_half_wide(&seg->str);
259 if (xs) {
260 ce = alloc_cand_ent();
261 ce->str.str = anthy_xstr_dup_str(xs);
262 ce->str.len = xs->len;
263 ce->flag = CEF_NONE;
264 anthy_free_xstr(xs);
265 push_back_candidate(seg, ce);
266 }
267 }
268
269 /* word_listの要素part_infoの配列からcand_elmの配列を作る */
270 static void
make_cand_elem_from_word_list(struct seg_ent * se,struct cand_ent * ce,struct word_list * wl,int index,int is_reverse)271 make_cand_elem_from_word_list(struct seg_ent *se,
272 struct cand_ent *ce,
273 struct word_list *wl,
274 int index,
275 int is_reverse)
276 {
277 int i;
278 int from = wl->from - se->from;
279
280 for (i = 0; i < NR_PARTS; ++i) {
281 struct part_info *part = &wl->part[i];
282 xstr core_xs;
283 if (part->len == 0) {
284 /* 長さの無いpartは無視する */
285 continue;
286 }
287 if (i == PART_CORE) {
288 ce->core_elm_index = i + index;
289 }
290 core_xs.str = &se->str.str[from];
291 core_xs.len = part->len;
292 if (i == PART_DEPWORD) {
293 ce->dep_word_hash = anthy_dep_word_hash(&core_xs);
294 }
295 ce->elm[i + index].se = anthy_get_seq_ent_from_xstr(&core_xs, is_reverse);
296 ce->elm[i + index].str.str = core_xs.str;
297 ce->elm[i + index].str.len = core_xs.len;
298 ce->elm[i + index].wt = part->wt;
299 ce->elm[i + index].ratio = RATIO_BASE * wl->len;
300 from += part->len;
301 }
302 }
303
304
305 /** まずwordlistを持つmetawordからmeta_wordを取り出す */
306 static void
make_candidate_from_simple_metaword(struct seg_ent * se,struct meta_word * mw,struct meta_word * top_mw,int is_reverse)307 make_candidate_from_simple_metaword(struct seg_ent *se,
308 struct meta_word *mw,
309 struct meta_word *top_mw,
310 int is_reverse)
311 {
312 /*
313 * 各単語の品詞が決定された状態でコミットされる。
314 */
315 struct cand_ent *ce;
316
317 /* 複数(1も含む)の単語で構成される文節に単語を割当てていく */
318 ce = alloc_cand_ent();
319 ce->nr_words = mw->nr_parts;
320 ce->str.str = NULL;
321 ce->str.len = 0;
322 ce->elm = calloc(sizeof(struct cand_elm),ce->nr_words);
323 ce->mw = mw;
324 ce->score = 0;
325
326 /* 接頭辞, 自立語部, 接尾辞, 付属語 */
327 make_cand_elem_from_word_list(se, ce, mw->wl, 0, is_reverse);
328
329 /* WRAPされていたらGUESSと同じ扱いにして点数を下げる */
330 if (anthy_metaword_type_tab[top_mw->type].status != MW_STATUS_WRAPPED) {
331 ce->flag = (se->best_mw == mw) ? CEF_BEST : CEF_NONE;
332 } else {
333 ce->flag = CEF_GUESS;
334 }
335
336 enum_candidates(se, ce, 0, 0);
337 anthy_release_cand_ent(ce);
338 }
339
340 /** combinedなmetawordは二つの語を合体して一つの語として出す */
341 static void
make_candidate_from_combined_metaword(struct seg_ent * se,struct meta_word * mw,struct meta_word * top_mw,int is_reverse)342 make_candidate_from_combined_metaword(struct seg_ent *se,
343 struct meta_word *mw,
344 struct meta_word *top_mw,
345 int is_reverse)
346 {
347 /*
348 * 各単語の品詞が決定された状態でコミットされる。
349 */
350 struct cand_ent *ce;
351
352 /* 複数(1も含む)の単語で構成される文節に単語を割当てていく */
353 ce = alloc_cand_ent();
354 ce->nr_words = mw->nr_parts;
355 ce->score = 0;
356 ce->str.str = NULL;
357 ce->str.len = 0;
358 ce->elm = calloc(sizeof(struct cand_elm),ce->nr_words);
359 ce->mw = top_mw;
360
361 /* 接頭辞, 自立語部, 接尾辞, 付属語 */
362 make_cand_elem_from_word_list(se, ce, mw->mw1->wl, 0, is_reverse);
363 if (mw->mw2) {
364 make_cand_elem_from_word_list(se, ce, mw->mw2->mw1->wl, NR_PARTS, is_reverse);
365 }
366
367 /* WRAPされていたらGUESSと同じ扱いにして点数を下げる */
368 if (anthy_metaword_type_tab[top_mw->type].status != MW_STATUS_WRAPPED) {
369 ce->flag = (se->best_mw == mw) ? CEF_BEST : CEF_NONE;
370 } else {
371 ce->flag = CEF_GUESS;
372 }
373
374 enum_candidates(se, ce, 0, 0);
375 anthy_release_cand_ent(ce);
376 }
377
378
379 /** splitterの情報を利用して候補を生成する
380 */
381 static void
proc_splitter_info(struct seg_ent * se,struct meta_word * mw,struct meta_word * top_mw,int is_reverse)382 proc_splitter_info(struct seg_ent *se,
383 struct meta_word *mw,
384 /* topとはtreeのトップ */
385 struct meta_word *top_mw,
386 int is_reverse)
387 {
388 enum mw_status st;
389 if (!mw) return;
390
391 /* まずwordlistを持つmetawordの場合 */
392 if (mw->wl && mw->wl->len) {
393 make_candidate_from_simple_metaword(se, mw, top_mw, is_reverse);
394 return;
395 }
396
397 st = anthy_metaword_type_tab[mw->type].status;
398 switch (st) {
399 case MW_STATUS_WRAPPED:
400 /* wrapされたものの情報を取り出す */
401 proc_splitter_info(se, mw->mw1, top_mw, is_reverse);
402 break;
403 case MW_STATUS_COMBINED:
404 make_candidate_from_combined_metaword(se, mw, top_mw, is_reverse);
405 break;
406 case MW_STATUS_COMPOUND:
407 /* 連文節の葉 */
408 {
409 struct cand_ent *ce;
410 ce = alloc_cand_ent();
411 ce->str.str = anthy_xstr_dup_str(&mw->cand_hint);
412 ce->str.len = mw->cand_hint.len;
413 ce->flag = CEF_COMPOUND;
414 ce->mw = top_mw;
415 push_back_candidate(se, ce);
416 }
417 break;
418 case MW_STATUS_COMPOUND_PART:
419 /* 連文節の個々の文節を結合して一つの文節としてみたもの */
420 /* BREAK THROUGH */
421 case MW_STATUS_OCHAIRE:
422 {
423 /* metawordを持たない候補文字列が
424 直接に指定された */
425 struct cand_ent *ce;
426 ce = alloc_cand_ent();
427 ce->str.str = anthy_xstr_dup_str(&mw->cand_hint);
428 ce->str.len = mw->cand_hint.len;
429 ce->mw = top_mw;
430 ce->flag = (st == MW_STATUS_OCHAIRE) ? CEF_OCHAIRE : CEF_COMPOUND_PART;
431
432 if (mw->len < se->len) {
433 /* metawordでカバーされてない領域の文字列を付ける */
434 xstr xs;
435 xs.str = &se->str.str[mw->len];
436 xs.len = se->len - mw->len;
437 anthy_xstrcat(&ce->str ,&xs);
438 }
439 push_back_candidate(se, ce);
440 }
441 break;
442 case MW_STATUS_NONE:
443 break;
444 default:
445 break;
446 }
447 }
448
449 /** context.cから呼出されるもっとも大物
450 * 一つ以上の候補を必ず生成する
451 */
452 void
anthy_do_make_candidates(struct splitter_context * sc,struct seg_ent * se,int is_reverse)453 anthy_do_make_candidates(struct splitter_context *sc,
454 struct seg_ent *se, int is_reverse)
455 {
456 int i;
457
458 /* metawordから候補を生成する */
459 for (i = 0; i < se->nr_metaword; i++) {
460 struct meta_word *mw = se->mw_array[i];
461 if (anthy_splitter_debug_flags() & SPLITTER_DEBUG_CAND) {
462 anthy_print_metaword(sc, mw);
463 }
464 proc_splitter_info(se, mw, mw, is_reverse);
465 }
466 if (anthy_splitter_debug_flags() & SPLITTER_DEBUG_CAND) {
467 printf("#done\n");
468 }
469 /* 単漢字などの候補 */
470 push_back_singleword_candidate(se, is_reverse);
471
472 /* ひらがな、カタカナの無変換エントリを作る */
473 push_back_noconv_candidate(se);
474
475 /* 候補が二つしか無いときは最後が助詞で残りが平仮名の候補を作れるか試す */
476 push_back_guessed_candidate(se);
477 }
478