1 /*
2 * 文節の最小単位であるwordlistを構成する
3 *
4 * anthy_make_word_list_all()
5 * 文節の形式を満たす部分文字列を列挙する
6 * いくかの経路で列挙されたword_listは
7 * anthy_commit_word_listでsplitter_contextに追加される
8 *
9 * Funded by IPA未踏ソフトウェア創造事業 2002 2/27
10 * Copyright (C) 2000-2006 TABATA Yusuke
11 * Copyright (C) 2004-2006 YOSHIDA Yuichi
12 * Copyright (C) 2000-2003 UGAWA Tomoharu
13 *
14 */
15
16 #include <stdlib.h>
17 #include <stdio.h>
18 #include <string.h>
19 #include <arpa/inet.h>
20
21 #include <anthy/alloc.h>
22 #include <anthy/record.h>
23 #include <anthy/xstr.h>
24 #include <anthy/diclib.h>
25 #include <anthy/wtype.h>
26 #include <anthy/ruleparser.h>
27 #include <anthy/dic.h>
28 #include <anthy/splitter.h>
29 #include <anthy/feature_set.h>
30 #include "wordborder.h"
31
32 #define HF_THRESH 784
33
34 static void *weak_word_array;
35
36 static wtype_t anthy_wtype_name_noun;
37 #if 0
38 static wtype_t anthy_wtype_prefix;
39 #endif
40 static wtype_t anthy_wtype_num_prefix;
41 static wtype_t anthy_wtype_num_postfix;
42 static wtype_t anthy_wtype_name_postfix;
43 static wtype_t anthy_wtype_sv_postfix;
44
45 /* デバッグ用 */
46 void
anthy_print_word_list(struct splitter_context * sc,struct word_list * wl)47 anthy_print_word_list(struct splitter_context *sc,
48 struct word_list *wl)
49 {
50 xstr xs;
51 if (!wl) {
52 printf("--\n");
53 return ;
54 }
55 /* 接頭辞 */
56 xs.len = wl->part[PART_CORE].from - wl->from;
57 xs.str = sc->ce[wl->from].c;
58 anthy_putxstr(&xs);
59 printf(".");
60 /* 自立語 */
61 xs.len = wl->part[PART_CORE].len;
62 xs.str = sc->ce[wl->part[PART_CORE].from].c;
63 anthy_putxstr(&xs);
64 printf(".");
65 /* 接尾辞 */
66 xs.len = wl->part[PART_POSTFIX].len;
67 xs.str = sc->ce[wl->part[PART_CORE].from + wl->part[PART_CORE].len].c;
68 anthy_putxstr(&xs);
69 printf("-");
70 /* 付属語 */
71 xs.len = wl->part[PART_DEPWORD].len;
72 xs.str = sc->ce[wl->part[PART_CORE].from +
73 wl->part[PART_CORE].len +
74 wl->part[PART_POSTFIX].len].c;
75 anthy_putxstr(&xs);
76 anthy_print_wtype(wl->part[PART_CORE].wt);
77 printf(" %s%s\n", anthy_seg_class_sym(wl->seg_class),
78 (wl->is_compound ? ",compound" : ""));
79 }
80
81 int
anthy_dep_word_hash(xstr * xs)82 anthy_dep_word_hash(xstr *xs)
83 {
84 return anthy_xstr_hash(xs) % WORD_HASH_MAX;
85 }
86
87 /** word_listを比較する、枝刈りのためなので、
88 厳密な比較である必要は無い */
89 static int
word_list_same(struct word_list * wl1,struct word_list * wl2)90 word_list_same(struct word_list *wl1, struct word_list *wl2)
91 {
92 if (wl1->node_id != wl2->node_id ||
93 wl1->from != wl2->from ||
94 wl1->len != wl2->len ||
95 wl1->mw_features != wl2->mw_features ||
96 wl1->tail_ct != wl2->tail_ct ||
97 wl1->part[PART_CORE].len != wl2->part[PART_CORE].len ||
98 wl1->is_compound != wl2->is_compound ||
99 !anthy_wtype_equal(wl1->part[PART_CORE].wt, wl2->part[PART_CORE].wt) ||
100 wl1->head_pos != wl2->head_pos) {
101 return 0;
102 }
103 if (wl1->part[PART_DEPWORD].dc != wl2->part[PART_DEPWORD].dc) {
104 return 0;
105 }
106 /* 同じと判断 */
107 return 1;
108 }
109
110 static void
set_features(struct word_list * wl)111 set_features(struct word_list *wl)
112 {
113 if (anthy_wtype_get_pos(wl->part[PART_CORE].wt) == POS_NOUN &&
114 anthy_wtype_get_sv(wl->part[PART_CORE].wt)) {
115 wl->mw_features |= MW_FEATURE_SV;
116 }
117 if (wl->part[PART_POSTFIX].len || wl->part[PART_PREFIX].len) {
118 wl->mw_features |= MW_FEATURE_SUFFIX;
119 }
120 if (anthy_wtype_get_pos(wl->part[PART_CORE].wt) == POS_NUMBER) {
121 wl->mw_features |= MW_FEATURE_NUM;
122 }
123 if (wl->part[PART_CORE].len == 1) {
124 wl->mw_features |= MW_FEATURE_CORE1;
125 }
126 if (wl->part[PART_CORE].len == 0) {
127 wl->mw_features |= MW_FEATURE_DEP_ONLY;
128 }
129 if (wl->part[PART_CORE].freq > HF_THRESH) {
130 wl->mw_features |= MW_FEATURE_HIGH_FREQ;
131 }
132 }
133
134 /** 作ったword_listのスコアを計算してからコミットする */
135 void
anthy_commit_word_list(struct splitter_context * sc,struct word_list * wl)136 anthy_commit_word_list(struct splitter_context *sc,
137 struct word_list *wl)
138 {
139 struct word_list *tmp;
140 xstr xs;
141
142 /* 付属語だけのword_listで、長さ0のもやってくるので */
143 if (wl->len == 0) return;
144 /**/
145 wl->last_part = PART_DEPWORD;
146
147 /**/
148 set_features(wl);
149 /* 文節境界の検索で使用するクラスの設定 */
150 anthy_set_seg_class(wl);
151 /**/
152 xs.len = wl->part[PART_DEPWORD].len;
153 xs.str = sc->ce[wl->part[PART_POSTFIX].from + wl->part[PART_POSTFIX].len].c;
154 wl->dep_word_hash = anthy_dep_word_hash(&xs);
155 if (wl->part[PART_POSTFIX].len) {
156 xs.len = wl->part[PART_POSTFIX].len;
157 xs.str = sc->ce[wl->part[PART_POSTFIX].from].c;
158 }
159
160 /* 同じ内容のword_listがないかを調べる */
161 for (tmp = sc->word_split_info->cnode[wl->from].wl; tmp; tmp = tmp->next) {
162 if (word_list_same(tmp, wl)) {
163 return ;
164 }
165 }
166 /* wordlistのリストに追加 */
167 wl->next = sc->word_split_info->cnode[wl->from].wl;
168 sc->word_split_info->cnode[wl->from].wl = wl;
169
170 /* デバッグプリント */
171 if (anthy_splitter_debug_flags() & SPLITTER_DEBUG_WL) {
172 anthy_print_word_list(sc, wl);
173 }
174 }
175
176 struct word_list *
anthy_alloc_word_list(struct splitter_context * sc)177 anthy_alloc_word_list(struct splitter_context *sc)
178 {
179 return anthy_smalloc(sc->word_split_info->WlAllocator);
180 }
181
182 /* 後続の活用語尾、助詞、助動詞を付ける */
183 static void
make_following_word_list(struct splitter_context * sc,struct word_list * tmpl)184 make_following_word_list(struct splitter_context *sc,
185 struct word_list *tmpl)
186 {
187 /* このxsは自立語部の後続の文字列 */
188 xstr xs;
189 xs.str = sc->ce[tmpl->from+tmpl->len].c;
190 xs.len = sc->char_count - tmpl->from - tmpl->len;
191 tmpl->part[PART_DEPWORD].from =
192 tmpl->part[PART_POSTFIX].from + tmpl->part[PART_POSTFIX].len;
193
194 if (tmpl->node_id >= 0) {
195 /* 普通のword_list */
196 anthy_scan_node(sc, tmpl, &xs, tmpl->node_id);
197 } else {
198 /* 自立語がないword_list */
199 struct wordseq_rule rule;
200 struct word_list new_tmpl;
201 int i;
202 int nr_rule = anthy_get_nr_dep_rule();
203 new_tmpl = *tmpl;
204 /* 名詞35の後に続くルールに対して */
205 for (i = 0; i < nr_rule; ++i) {
206 anthy_get_nth_dep_rule(i, &rule);
207 if (anthy_wtype_get_pos(rule.wt) == POS_NOUN
208 && anthy_wtype_get_scos(rule.wt) == SCOS_T35) {
209 new_tmpl.part[PART_CORE].wt = rule.wt;
210 new_tmpl.node_id = rule.node_id;
211 new_tmpl.head_pos = anthy_wtype_get_pos(new_tmpl.part[PART_CORE].wt);
212 anthy_scan_node(sc, &new_tmpl, &xs, new_tmpl.node_id);
213 }
214 }
215 }
216 }
217
218 static void
push_part_back(struct word_list * tmpl,int len,seq_ent_t se,wtype_t wt)219 push_part_back(struct word_list *tmpl, int len,
220 seq_ent_t se, wtype_t wt)
221 {
222 tmpl->len += len;
223 tmpl->part[PART_POSTFIX].len += len;
224 tmpl->part[PART_POSTFIX].wt = wt;
225 tmpl->part[PART_POSTFIX].seq = se;
226 tmpl->last_part = PART_POSTFIX;
227 }
228
229 /* 接尾辞をくっつける */
230 static void
make_suc_words(struct splitter_context * sc,struct word_list * tmpl)231 make_suc_words(struct splitter_context *sc,
232 struct word_list *tmpl)
233 {
234 int i, right;
235
236 wtype_t core_wt = tmpl->part[PART_CORE].wt;
237 /* 数詞、名前、サ変名詞のいずれかに付属語は付く */
238 int core_is_num = 0;
239 int core_is_name = 0;
240 int core_is_sv_noun = 0;
241
242 /* まず、接尾辞が付く自立語かチェックする */
243 if (anthy_wtype_get_pos (core_wt) == POS_NUMBER) {
244 core_is_num = 1;
245 }
246 if (anthy_wtype_get_pos (core_wt) == POS_NOUN &&
247 anthy_wtype_get_cos (core_wt) == COS_JN) {
248 core_is_name = 1;
249 }
250 if (anthy_wtype_get_sv(core_wt)) {
251 core_is_sv_noun = 1;
252 }
253 if (!core_is_num && !core_is_name && !core_is_sv_noun) {
254 return ;
255 }
256
257 right = tmpl->part[PART_CORE].from + tmpl->part[PART_CORE].len;
258 /* 自立語の右側の文字列に対して */
259 for (i = 1;
260 i <= sc->word_split_info->seq_len[right];
261 i++){
262 xstr xs;
263 seq_ent_t suc;
264 xs.str = sc->ce[right].c;
265 xs.len = i;
266 suc = anthy_get_seq_ent_from_xstr(&xs, sc->is_reverse);
267 if (anthy_get_seq_ent_pos(suc, POS_SUC)) {
268 /* 右側の文字列は付属語なので、自立語の品詞にあわせてチェック */
269 struct word_list new_tmpl;
270 if (core_is_num &&
271 anthy_get_seq_ent_wtype_freq (suc, anthy_wtype_num_postfix)) {
272 new_tmpl = *tmpl;
273 push_part_back(&new_tmpl, i, suc, anthy_wtype_num_postfix);
274 make_following_word_list(sc, &new_tmpl);
275 }
276 if (core_is_name &&
277 anthy_get_seq_ent_wtype_freq (suc, anthy_wtype_name_postfix)) {
278 new_tmpl = *tmpl;
279 push_part_back(&new_tmpl, i, suc, anthy_wtype_name_postfix);
280 make_following_word_list(sc, &new_tmpl);
281 }
282 if (core_is_sv_noun &&
283 anthy_get_seq_ent_wtype_freq (suc, anthy_wtype_sv_postfix)) {
284 new_tmpl = *tmpl;
285 push_part_back(&new_tmpl, i, suc, anthy_wtype_sv_postfix);
286 make_following_word_list(sc, &new_tmpl);
287 }
288 }
289 }
290 }
291
292 static void
push_part_front(struct word_list * tmpl,int len,seq_ent_t se,wtype_t wt)293 push_part_front(struct word_list *tmpl, int len,
294 seq_ent_t se, wtype_t wt)
295 {
296 tmpl->from = tmpl->from - len;
297 tmpl->len = tmpl->len + len;
298 tmpl->part[PART_PREFIX].from = tmpl->from;
299 tmpl->part[PART_PREFIX].len += len;
300 tmpl->part[PART_PREFIX].wt = wt;
301 tmpl->part[PART_PREFIX].seq = se;
302 }
303
304 /* 接頭辞をくっつけてから接尾辞をくっつける */
305 static void
make_pre_words(struct splitter_context * sc,struct word_list * tmpl)306 make_pre_words(struct splitter_context *sc,
307 struct word_list *tmpl)
308 {
309 int i;
310 wtype_t core_wt = tmpl->part[PART_CORE].wt;
311 int core_is_num = 0;
312 /* 自立語は数詞か? */
313 if (anthy_wtype_get_pos (core_wt) == POS_NUMBER) {
314 core_is_num = 1;
315 }
316 /* 接頭辞を列挙する */
317 for (i = 1;
318 i <= sc->word_split_info->rev_seq_len[tmpl->part[PART_CORE].from];
319 i++) {
320 seq_ent_t pre;
321 /* このxsは自立語部の前の文字列 */
322 xstr xs;
323 xs.str = sc->ce[tmpl->part[PART_CORE].from - i].c;
324 xs.len = i;
325 pre = anthy_get_seq_ent_from_xstr(&xs, sc->is_reverse);
326 if (anthy_get_seq_ent_pos(pre, POS_PRE)) {
327 struct word_list new_tmpl;
328 if (core_is_num &&
329 anthy_get_seq_ent_wtype_freq (pre, anthy_wtype_num_prefix)) {
330 new_tmpl = *tmpl;
331 push_part_front(&new_tmpl, i, pre, anthy_wtype_num_prefix);
332 make_following_word_list(sc, &new_tmpl);
333 /* 数の場合は接尾辞もくっつける */
334 make_suc_words(sc, &new_tmpl);
335 }
336 #if 0
337 else if (anthy_get_seq_ent_wtype_freq (pre, anthy_wtype_prefix)) {
338 new_tmpl = *tmpl;
339 push_part_front(&new_tmpl, i, pre, anthy_wtype_prefix);
340 make_following_word_list(sc, &new_tmpl);
341 }
342 #endif
343 }
344 }
345 }
346
347 /* wordlistを初期化する */
348 static void
setup_word_list(struct word_list * wl,int from,int len,int is_compound,int is_weak)349 setup_word_list(struct word_list *wl, int from, int len,
350 int is_compound, int is_weak)
351 {
352 int i;
353 wl->from = from;
354 wl->len = len;
355 wl->is_compound = is_compound;
356 /* partの配列を初期化する */
357 for (i = 0; i < NR_PARTS; i++) {
358 wl->part[i].from = 0;
359 wl->part[i].len = 0;
360 wl->part[i].wt = anthy_wt_none;
361 wl->part[i].seq = 0;
362 wl->part[i].freq = 1;/* 頻度の低い単語としておく */
363 wl->part[i].dc = DEP_NONE;
364 }
365 /* 自立語のパートを設定 */
366 wl->part[PART_CORE].from = from;
367 wl->part[PART_CORE].len = len;
368 /**/
369 wl->mw_features = MW_FEATURE_NONE;
370 wl->node_id = -1;
371 wl->last_part = PART_CORE;
372 wl->head_pos = POS_NONE;
373 wl->tail_ct = CT_NONE;
374 if (is_weak) {
375 wl->mw_features |= MW_FEATURE_WEAK_SEQ;
376 }
377 }
378
379 /*
380 * ある独立語に対して、接頭辞、接尾辞、付属語を付けたものを
381 * 文節の候補(=word_list)としてcacheに追加する
382 */
383 static void
make_word_list(struct splitter_context * sc,seq_ent_t se,int from,int len,int is_compound,int is_weak)384 make_word_list(struct splitter_context *sc,
385 seq_ent_t se,
386 int from, int len,
387 int is_compound,
388 int is_weak)
389 {
390 struct word_list tmpl;
391 struct wordseq_rule rule;
392 int nr_rule = anthy_get_nr_dep_rule();
393 int i;
394
395 /* テンプレートの初期化 */
396 setup_word_list(&tmpl, from, len, is_compound, is_weak);
397 tmpl.part[PART_CORE].seq = se;
398
399 /* 各ルールにマッチするか比較 */
400 for (i = 0; i < nr_rule; ++i) {
401 int freq;
402 anthy_get_nth_dep_rule(i, &rule);
403 if (!is_compound) {
404 freq = anthy_get_seq_ent_wtype_freq (se, rule.wt);
405 } else {
406 freq = anthy_get_seq_ent_wtype_compound_freq (se, rule.wt);
407 }
408
409 if (freq) {
410 /* 自立語の品詞はそのルールにあっている */
411 if (anthy_splitter_debug_flags() & SPLITTER_DEBUG_ID) {
412 /* 品詞表のデバッグ用*/
413 xstr xs;
414 xs.str = sc->ce[tmpl.part[PART_CORE].from].c;
415 xs.len = tmpl.part[PART_CORE].len;
416 anthy_putxstr(&xs);
417 printf(" freq=%d rule_id=%d node_id=%d\n",
418 freq, i, rule.node_id);
419 }
420 /* 遷移したルールの情報を転記する */
421 tmpl.part[PART_CORE].wt = rule.wt;
422 tmpl.part[PART_CORE].freq = freq;
423 tmpl.node_id = rule.node_id;
424 tmpl.head_pos = anthy_wtype_get_pos(tmpl.part[PART_CORE].wt);
425
426 /**/
427 tmpl.part[PART_POSTFIX].from =
428 tmpl.part[PART_CORE].from +
429 tmpl.part[PART_CORE].len;
430 /**/
431 if (anthy_wtype_get_pos(rule.wt) == POS_NOUN ||
432 anthy_wtype_get_pos(rule.wt) == POS_NUMBER) {
433 /* 接頭辞、接尾辞は名詞、数詞にしか付かないことにしている */
434 make_pre_words(sc, &tmpl);
435 make_suc_words(sc, &tmpl);
436 }
437 /* 接頭辞、接尾辞無しで助詞助動詞をつける */
438 make_following_word_list(sc, &tmpl);
439 }
440 }
441 }
442
443 static void
make_dummy_head(struct splitter_context * sc)444 make_dummy_head(struct splitter_context *sc)
445 {
446 struct word_list tmpl;
447 setup_word_list(&tmpl, 0, 0, 0, 0);
448 tmpl.part[PART_CORE].seq = 0;
449 tmpl.part[PART_CORE].wt = anthy_wtype_noun;
450
451 tmpl.head_pos = anthy_wtype_get_pos(tmpl.part[PART_CORE].wt);
452 make_suc_words(sc, &tmpl);
453 }
454
455 static int
compare_hash(const void * kp,const void * cp)456 compare_hash(const void *kp, const void *cp)
457 {
458 const int *h = kp;
459 const int *c = cp;
460 return (*h) - ntohl(*c);
461 }
462
463 static int
check_weak(xstr * xs)464 check_weak(xstr *xs)
465 {
466 const int *array = (int *)weak_word_array;
467 int nr;
468 int h;
469 if (!array) {
470 return 0;
471 }
472 nr = ntohl(array[1]);
473 h = anthy_xstr_hash(xs);
474 if (bsearch(&h, &array[16], nr,
475 sizeof(int), compare_hash)) {
476 return 1;
477 }
478 return 0;
479 }
480
481 /* コンテキストに設定された文字列の部分文字列から全てのword_listを列挙する */
482 void
anthy_make_word_list_all(struct splitter_context * sc)483 anthy_make_word_list_all(struct splitter_context *sc)
484 {
485 int i, j;
486 xstr xs;
487 seq_ent_t se;
488 struct depword_ent {
489 struct depword_ent *next;
490 int from, len;
491 int is_compound;
492 int is_weak;
493 seq_ent_t se;
494 } *head, *de;
495 struct word_split_info_cache *info;
496 allocator de_ator;
497
498 weak_word_array = anthy_file_dic_get_section("weak_words");
499
500 info = sc->word_split_info;
501 head = NULL;
502 de_ator = anthy_create_allocator(sizeof(struct depword_ent), 0);
503
504 xs.str = sc->ce[0].c;
505 xs.len = sc->char_count;
506 anthy_gang_load_dic(&xs, sc->is_reverse);
507
508 /* 全ての自立語を列挙 */
509 /* 開始地点のループ */
510 for (i = 0; i < sc->char_count ; i++) {
511 int search_len = sc->char_count - i;
512 int search_from = 0;
513 if (search_len > 30) {
514 search_len = 30;
515 }
516
517 /* 文字列長のループ(長い方から) */
518 for (j = search_len; j > search_from; j--) {
519 /* seq_entを取得する */
520 xs.len = j;
521 xs.str = sc->ce[i].c;
522 se = anthy_get_seq_ent_from_xstr(&xs, sc->is_reverse);
523
524 /* 単語として認識できない */
525 if (!se) {
526 continue;
527 }
528
529 /* 各、部分文字列が単語ならば接頭辞、接尾辞の
530 最大長を調べてマークする */
531 if (j > info->seq_len[i] &&
532 anthy_get_seq_ent_pos(se, POS_SUC)) {
533 info->seq_len[i] = j;
534 }
535 if (j > info->rev_seq_len[i + j] &&
536 anthy_get_seq_ent_pos(se, POS_PRE)) {
537 info->rev_seq_len[i + j] = j;
538 }
539
540 /* 発見した自立語をリストに追加 */
541 if (anthy_get_seq_ent_indep(se) &&
542 /* 複合語で無い候補があることを確認 */
543 anthy_has_non_compound_ents(se)) {
544 de = (struct depword_ent *)anthy_smalloc(de_ator);
545 de->from = i;
546 de->len = j;
547 de->se = se;
548 de->is_compound = 0;
549 de->is_weak = check_weak(&xs);
550
551 de->next = head;
552 head = de;
553 }
554 /* 発見した複合語をリストに追加 */
555 if (anthy_has_compound_ents(se)) {
556 de = (struct depword_ent *)anthy_smalloc(de_ator);
557 de->from = i;
558 de->len = j;
559 de->se = se;
560 de->is_compound = 1;
561 de->is_weak = 0;
562
563 de->next = head;
564 head = de;
565 }
566 }
567 }
568
569 /* 発見した自立語全てに対して付属語パターンの検索 */
570 for (de = head; de; de = de->next) {
571 make_word_list(sc, de->se, de->from, de->len,
572 de->is_compound, de->is_weak);
573 }
574
575 /* 自立語の無いword_list */
576 for (i = 0; i < sc->char_count; i++) {
577 struct word_list tmpl;
578 setup_word_list(&tmpl, i, 0, 0, 0);
579 if (i == 0) {
580 make_following_word_list(sc, &tmpl);
581 } else {
582 int type = anthy_get_xchar_type(*sc->ce[i - 1].c);
583 if ((type & (XCT_CLOSE | XCT_SYMBOL)) &&
584 !(type & XCT_PUNCTUATION)) {
585 /* 句読点以外の記号 */
586 make_following_word_list(sc, &tmpl);
587 }
588 }
589 }
590
591 /* 先頭に0文字の自立語を付ける */
592 make_dummy_head(sc);
593
594 anthy_free_allocator(de_ator);
595 }
596
597 int
anthy_init_wordlist(void)598 anthy_init_wordlist (void)
599 {
600 /* {"人名",POS_NOUN,COS_JN,SCOS_NONE,CC_NONE,CT_NONE,WF_INDEP} */
601 anthy_type_to_wtype ("#JN", &anthy_wtype_name_noun);
602 #if 0
603 /* {"名詞接頭辞",POS_PRE,COS_NONE,SCOS_NONE,CC_NONE,CT_NONE,WF_INDEP} */
604 anthy_type_to_wtype ("#PRE", &anthy_wtype_prefix);
605 #endif
606 /* {"数接頭辞",POS_PRE,COS_NN,SCOS_NONE,CC_NONE,CT_NONE,WF_NONE} */
607 anthy_type_to_wtype ("#NNPRE", &anthy_wtype_num_prefix);
608
609 /* {"数接尾辞",POS_SUC,COS_NN,SCOS_NONE,CC_NONE,CT_NONE,WF_NONE} */
610 /* {"#JS",POS_SUC,COS_NN,SCOS_NONE,CC_NONE,CT_NONE,WF_INDEP} # "助数詞" */
611 anthy_type_to_wtype ("#JS", &anthy_wtype_num_postfix);
612
613 /* {"人名接尾辞",POS_SUC,COS_JN,SCOS_NONE,CC_NONE,CT_NONE,WF_INDEP} */
614 anthy_type_to_wtype ("#JNSUC", &anthy_wtype_name_postfix);
615
616 /* {"サ変接尾辞",POS_SUC,COS_SVSUFFIX,SCOS_NONE,CC_NONE,CT_NONE,WF_INDEP} */
617 anthy_type_to_wtype ("#SVSUC", &anthy_wtype_sv_postfix);
618
619 return 0;
620 }
621