File Coverage

blib/lib/Acme/Shukugawa/Atom.pm
Criterion Covered Total %
statement 19 21 90.4
branch n/a
condition n/a
subroutine 7 7 100.0
pod n/a
total 26 28 92.8


line stmt bran cond sub pod time code
1             # $Id: /mirror/coderepos/lang/perl/Acme-Shukugawa-Atom/trunk/lib/Acme/Shukugawa/Atom.pm 47728 2008-03-14T01:07:28.622095Z daisuke $
2              
3             package Acme::Shukugawa::Atom;
4 3     3   19 use strict;
  3         5  
  3         123  
5 3     3   17 use warnings;
  3         5  
  3         153  
6 3     3   18 use base qw(Class::Accessor::Fast);
  3         6  
  3         29  
7 3     3   29 use utf8;
  3         10  
  3         25  
8 3     3   3512 use Encode qw(decode_utf8);
  3         43193  
  3         452  
9 3     3   3189 use File::ShareDir;
  3         27635  
  3         195  
10 3     3   1717 use Text::MeCab;
  0            
  0            
11             use YAML ();
12              
13             our $VERSION = '0.00004';
14              
15             __PACKAGE__->mk_accessors($_) for qw(custom_words);
16              
17             # Special case handling -- this could be optimized further
18             # put it in a sharefile later
19             our ($CONFIG, @DEFAULT_WORDS, $RE_EXCEPTION, $RE_SMALL, $RE_SYLLABLE, $RE_NBAR);
20             BEGIN
21             {
22             my $config = YAML::LoadFile(
23             $CONFIG || File::ShareDir::module_file(__PACKAGE__, 'config.yaml') );
24             $RE_SMALL = decode_utf8("[ャュョッー]");
25             $RE_SYLLABLE = decode_utf8("(?:.$RE_SMALL?)");
26             $RE_NBAR = decode_utf8("^ンー");
27             @DEFAULT_WORDS = map {
28             (decode_utf8($_->[0]), decode_utf8($_->[1]))
29             } @{ $config->{custom_words} || [] };
30             }
31              
32             sub _create_exception_re
33             {
34             my $self = shift;
35             my $custom = $self->custom_words;
36              
37             return decode_utf8(join("|",
38             map { $custom->[$_ * 2 + 1] } (0..(scalar(@$custom) - 1)/2) ));
39             }
40              
41             sub translate
42             {
43             my $self = shift;
44             my $string = decode_utf8(shift);
45              
46             if (! ref $self) {
47             $self = $self->new({ custom_words => \@DEFAULT_WORDS, @_ });
48             }
49              
50             # Create local RE_EXCEPTION
51             local $RE_EXCEPTION = $self->_create_exception_re;
52              
53             $self->preprocess(\$string);
54             $self->runthrough(\$string);
55             $self->postprocess(\$string);
56              
57             return $string;
58             }
59              
60             sub preprocess
61             {
62             my ($self, $strref) = @_;
63             my $custom = $self->custom_words;
64              
65             for(0..(scalar(@$custom) - 1)/2) {
66             my $pattern = $custom->[$_ * 2];
67             my $replace = $custom->[$_ * 2 + 1];
68             $$strref =~ s/$pattern/$replace/g;
69             }
70             }
71              
72             sub runthrough
73             {
74             my ($self, $strref) = @_;
75              
76             my $mecab = Text::MeCab->new;
77              
78             # First, make it all katakana, except for where the surface is already
79             # in hiragana
80             my $ret = '';
81              
82             foreach my $text (split(/($RE_EXCEPTION|\s+)/, $$strref)) {
83             if ($text =~ /$RE_EXCEPTION/) {
84             $ret .= $text;
85             next;
86             }
87              
88             if ($text !~ /\S/) {
89             $ret .= $text;
90             next;
91             }
92              
93             foreach (my $node = $mecab->parse($text); $node; $node = $node->next) {
94             next unless $node->surface;
95              
96             my $surface = decode_utf8($node->surface);
97             my $feature = decode_utf8($node->feature);
98             my ($type, $yomi) = (split(/,/, $feature))[0,8];
99             # warn "$surface -> $type, $yomi";
100              
101             if ($surface eq '上手') {
102             $ret .= 'マイウー';
103             next;
104             }
105              
106             if ($type eq '動詞' && $node->next) {
107             # 助動詞を計算に入れる
108             my $next_feature = decode_utf8($node->next->feature);
109             my ($next_type, $next_yomi) = (split(/,/, $next_feature))[0,8];
110             if ($next_type eq '助動詞') {
111             $yomi .= $next_yomi;
112             $node = $node->next;
113             }
114             }
115              
116             if ($type =~ /副詞|助動詞|形容詞|接続詞|助詞/ && $surface =~ /^\p{InHiragana}+$/) {
117             $ret .= $surface;
118             } elsif ($yomi) {
119             $ret .= $self->atomize($yomi) || $surface;
120             } else {
121             $ret .= $surface;
122             }
123             }
124             }
125             $$strref = $ret;
126             }
127              
128             sub postprocess {}
129              
130             # シースールール
131             # 寿司→シースー
132             # ン、が最後だったらひっくり返さない
133             sub apply_shisu_rule
134             {
135             my ($self, $yomi) = @_;
136             return $yomi if $yomi =~ s{^($RE_SYLLABLE)($RE_SYLLABLE)$}{
137             my ($a, $b) = ($1, $2);
138             $a =~ s/ー$//;
139             $b =~ s/ー$//;
140             "${b}ー${a}ー";
141             }e;
142             return;
143             }
144              
145             # ワイハールール
146             # ハワイ→ワイハー
147             sub apply_waiha_rule
148             {
149             my ($self, $yomi) = @_;
150              
151             # warn "WAIHA $yomi";
152             if ($yomi =~ s/^(${RE_SYLLABLE}[$RE_NBAR]?)([^$RE_NBAR].)$/$2$1/) {
153             $yomi =~ s/(^.[^ー].*[^ー])$/$1ー/;
154             return $yomi;
155             }
156             return;
157             }
158              
159             # クリビツルール
160             # びっくり→クリビツ
161             sub apply_kuribitsu_rule
162             {
163             my ($self, $yomi) = @_;
164              
165             # warn "KURIBITSU $yomi";
166             if ($yomi =~ s/^(${RE_SYLLABLE}.)([^$RE_NBAR]${RE_SYLLABLE}$)/$2$1/) {
167             return $yomi;
168             }
169             return;
170             }
171              
172             sub atomize
173             {
174             my ($self, $yomi) = @_;
175             $yomi =~ s/ー+/ー/g;
176              
177             # Length
178             my $word_length = length($yomi);
179             my $length = $word_length - ($yomi =~ /$RE_SMALL/g);
180             if ($length == 3 && $yomi =~ s/^(${RE_SYLLABLE})ッ/${1}ツ/) {
181             # warn "Special rule!";
182             $length = 4;
183             }
184             my $done = 0;
185              
186             # warn "$yomi LENGTH: $length";
187             if ($length == 2) {
188             my $tmp = $self->apply_shisu_rule($yomi);
189             if ($tmp) {
190             $yomi = $tmp;
191             $done = 1;
192             }
193             }
194              
195             if ($length == 3) {
196             my $tmp = $self->apply_waiha_rule($yomi);
197             if ($tmp) {
198             $yomi = $tmp;
199             $done = 1;
200             }
201             }
202              
203             if ($length == 4) { # 4 character words tend to have special xformation
204             my $tmp = $self->apply_kuribitsu_rule($yomi);
205             if ($tmp) {
206             $yomi = $tmp;
207             $done = 1;
208             }
209             }
210              
211             if (! $done) {
212             $yomi =~ s/(.(?:ー+)?)$//;
213             $yomi = $1 . $yomi;
214             }
215              
216             $yomi =~ s/ッ$/ツ/;
217             return $yomi;
218             }
219              
220              
221             1;
222              
223             __END__