File Coverage

blib/lib/Lingua/EN/Hyphenate.pm
Criterion Covered Total %
statement 12 192 6.2
branch 0 112 0.0
condition 0 158 0.0
subroutine 4 28 14.2
pod 0 21 0.0
total 16 511 3.1


line stmt bran cond sub pod time code
1             package Lingua::EN::Hyphenate;
2              
3 1     1   5 use strict;
  1         1  
  1         36  
4 1     1   4 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK);
  1         1  
  1         424  
5              
6             require Exporter;
7              
8             @ISA = qw(Exporter);
9              
10             @EXPORT_OK = qw( hyphenate syllables def_syl def_hyph );
11              
12             $VERSION = '0.01';
13              
14 0 0   0 0   sub debug { print @_ if $::debug }
15              
16             my @diphthong = qw { ao ia io ii iu oe uo ue };
17             my @diphthong1 = map { substr($_,0,1)."(?=".substr($_,1,1).")" } @diphthong;
18             my $diphthong = "(" . join('|', @diphthong1) . ")(.)";
19              
20             my $vowels = '(?:[aeiou]+y?|y)';
21              
22             my $precons = '( str
23             |sch
24             |sph
25             |squ
26             |thr
27             |b[r]
28             |d[rw]
29             |f[lr]
30             |g[nr]
31             |k[n]
32             |p[nr]
33             |r[h]
34             |s[lmnw]
35             |t[w]
36             |qu
37             )';
38              
39             my $ppcons1 = '( b[l]
40             |c[hlr]
41             |g[hl]
42             |m[n]
43             |p[l]
44             |t[h](?!r)
45             |s[chpt](?!r)
46             |s[k]
47             |tr
48             )';
49              
50             my $ppcons2 = '((?=[a-z])[^aeiouy])';
51              
52             my $postcons = '( ght
53             |nst
54             |rst
55             |tch
56             |rth
57             |bb
58             |c[ckt]
59             |d[dlz]
60             |f[ft]
61             |g[gt]
62             |l[bcdfgklmnptv]
63             |m[mp]
64             |n[cdgknstx]
65             |pp
66             |r[bcdfgklmnprtv]
67             |ss
68             |t[tz]
69             |vv
70             |wn
71             |x[tx]
72             )';
73              
74             my @paircons = qw { ph tl n't };
75             my $paircons = "(" . join('|', @paircons) . ")";
76              
77             my @dblcons = qw { c~tr n~th n~c[th] n~s[th] ns~d l~pr s~tl
78             n~c n~s c~t r~t };
79             my @dblcons1 = map { /(.+)~(.+)/; "$1(?=$2)" } @dblcons;
80             my @dblcons2 = map { /(.+)~(.+)/; "$2" } @dblcons;
81             my $dblcons = "(" . join('|', @dblcons1) . ")(" . join('|', @dblcons2) . ")";
82              
83             my @repcons = map { "$_(?=$_)" } qw { b c g h j k m n p q r t v w x z };
84             my $repcons = "(" . join('|', @repcons) . ")";
85              
86             my $pprecons = "($ppcons1|$precons|$ppcons2)";
87             my $ppostcons = "($ppcons1|$postcons|$ppcons2)";
88              
89             sub abstract
90             {
91 1     1   5 no strict;
  1         1  
  1         2259  
92 0     0 0   sub C_ { debug "C_($_[0])\n"; return { type => 'C_', val => $_[0] } }
  0            
93 0     0     sub _C { debug "_C($_[0])\n"; return { type => '_C', val => $_[0] } }
  0            
94 0     0     sub _S { debug "_S($_[0])\n"; return { type => '_S', val => $_[0] } }
  0            
95 0     0     sub _C_ { debug "_C_($_[0])\n"; return { type => '_C_', val => $_[0] } }
  0            
96 0     0 0   sub V { debug "V($_[0])\n"; return { type => 'V', val => $_[0] } }
  0            
97 0     0 0   sub E { debug "E($_[0])\n"; return { type => 'E', val => $_[0] } }
  0            
98              
99 0     0 0   local $_ = shift;
100 0 0   0 0   local @head = (); sub app { push @head, @_ if defined $_[0]; '' }
  0            
  0            
101 0 0   0 0   local @tail = (); sub prep { unshift @tail, @_ if defined $_[0]; '' }
  0            
  0            
102              
103             #debug "\A${pprecons}${diphthong}${postcons}\Z\n";
104              
105 0           s/\A${pprecons}${diphthong}${ppostcons}\Z/app C_($1),V("$5$6"),_C($7)/eix;
  0            
106              
107 0 0 0       s/\Ay/app C_("y")/ei
  0   0        
      0        
      0        
      0        
      0        
      0        
      0        
108 0           or s/\Aex/app V("e"),_C("x")/ei
109 0           or s/\Ai([nmg])/app V("i"),_C($1)/ei
110 0           or s/\A([eu])([nm])/app V($1),_C($2)/ei
111 0           or s/\Airr/app V("i"),_C("r"),C_("r")/ei
112 0           or s/\Aill/app V("i"),_C("l"),C_("l")/ei
113 0           or s/\Acon/app C_("c"), V("o"), _C("n")/ei
114 0           or s/\Aant([ie])/app V("a"),_C("n"),C_("t"),V($1),_C('')/ei
115 0           or s/\A(w[hr])/app C_("$1")/ei
116 0           or s/\Amay/app C_("m"), V("a"), _C("y")/ei
117             ;
118              
119 0 0 0       s/([bd])le\Z/prep C_($1), V(''), _C("le")/ei
  0   0        
      0        
      0        
      0        
      0        
      0        
      0        
      0        
      0        
      0        
      0        
      0        
      0        
      0        
120 0           or s/sm\Z/prep C_("s"), V(''), _C("m")/ei
121 0           or s/${repcons}\1e\Z/do{prep _C("$1$1e")}/eix
  0            
122 0           or s/(?=..e)${dblcons}e\Z/do{prep _C("$1$2e")}/eix
  0            
123 0           or s/(${vowels})${ppcons2}es\Z/do{prep _C("$2es");$1}/eix
  0            
  0            
124 0           or s/(${vowels})(ples?)\Z/do{prep C_($2);$1}/eix
  0            
  0            
125 0           or s/([td])ed\Z/prep C_($1),V("e"), _C("d")/eix
126 0           or s/([^aeiou])\1ed\Z/prep _C("$1$1ed")/eix
127 0           or s/${pprecons}ed\Z/prep _C("$1ed")/eix
128 0           or s/${ppostcons}ed\Z/prep _C("$1ed")/eix
129 0           or s/([aeou])ic(s?)\Z/prep V($1), V("i"),_C("c$2")/ei
130 0           or s/([sct])ion(s?)\Z/prep _C_($1),V("io"),_C("n$2")/ei
131 0           or s/([cts])ia([nl]s?)\Z/prep _C_($1),V("ia"),_C($2)/ei
132 0           or s/([ts])ia(s?)\Z/prep _C_($1),V("ia$2")/ei
133 0           or s/t(i?ou)s\Z/prep _C_("t"),V($1),_C("s")/ei
134 0           or s/cious\Z/prep _C_("c"),V("iou"),_C("s")/ei
135 0           or s/${ppostcons}(e?s)\Z/prep _C("$1$5")/eix
136             ;
137              
138 0           1 while s/${dblcons}\Z/do{prep _C("$1$2")}/eix;
  0            
  0            
139              
140 0           while (/[a-z]/i)
141             {
142 0           debug "=====[$_]=====\n";
143 0 0         s/\A(s'|'s)\Z/app _S($1)/eix and next;
  0            
144 0 0         s/\A${dblcons}/app _C($1),C_($2)/eix and next;
  0            
145 0 0         s/\A${dblcons}/app _C($1),C_($2)/eix and next;
  0            
146 0 0         s/\A${repcons}/app _C($1)/eix and next;
  0            
147 0 0         s/\A${paircons}/app _C($1)/eix and next;
  0            
148 0 0         s/\A${ppcons1}e(?![aeiouy])/app _C_($1),E("e")/eix
  0            
149             and next;
150 0 0         s/\A${precons}e(?![aeiouy])/app C_($1),E("e")/eix
  0            
151             and next;
152 0 0         s/\A${postcons}e(?![aeiouy])/app _C($1),E("e")/eix
  0            
153             and next;
154 0 0         s/\A${ppcons2}e(?![aeiouy])/app _C_($1),E("e")/eix
  0            
155             and next;
156 0 0 0       s/\A${postcons}?([sct])ion/app C_(($1||'').$2),V("io"),_C("n")/eix
  0            
157             and next;
158 0 0 0       s/\A${postcons}?tial/app C_(($1||'')."t"),V("ia"),_C("l")/eix
  0            
159             and next;
160 0 0 0       s/\A${postcons}?([ct])ia([nl])/app C_(($1||'').$2),V("ia"),_C($3)/eix
  0            
161             and next;
162 0 0 0       s/\A${postcons}?t(i?ou)s/app C_(($1||'')."t"),V($1),_C("s")/eix
  0            
163             and next;
164 0 0         s/\Aience/app V("i"),V("e"),_C("nc"),E('e')/eix
  0            
165             and next;
166 0 0 0       s/\Acious/app C_(($1||'')."c"),V("iou"),_C("s")/eix
  0            
167             and next;
168 0 0         s/\A$diphthong/app V($1),V($2)/ei and next;
  0            
169 0 0         s/\A$ppcons1/app _C_($1)/eix and next;
  0            
170 0 0         s/\A$precons/app C_($1)/eix and next;
  0            
171 0 0         s/\A$postcons/app _C($1)/eix and next;
  0            
172 0 0         s/\A$ppcons2/app _C_($1)/eix and next;
  0            
173 0 0         s/\A($vowels)/app V($1)/ei and next;
  0            
174             }
175 0           return (@head, @tail);
176             }
177              
178             sub partition
179             {
180 1     1   5 no strict;
  1         1  
  1         1436  
181 0     0 0   local @list = @_;
182 0           local @syls = ();
183              
184 0 0   0 0   sub is_S { @list > 1 && $list[$#list]->{val} =~ /'?s'?/ }
185 0 0 0 0 0   sub isR { my $i = $#list+$_[0]; $i >= 0 && $list[$i]->{type}=~'C'
  0            
186             && $list[$i]->{val} eq 'r' }
187 0 0   0 0   sub isC { my $i = $#list+$_[0]; $i >= 0 && $list[$i]->{type}=~'C' }
  0            
188 0 0   0 0   sub is_C { my $i = $#list+$_[0]; $i >= 0 && $list[$i]->{type}=~'_C' }
  0            
189 0 0   0 0   sub isC_ { my $i = $#list+$_[0]; $i >= 0 && $list[$i]->{type}=~'C_' }
  0            
190 0 0   0 0   sub isV { my $i = $#list+$_[0]; $i >= 0 && $list[$i]->{type}=~/V|E/ }
  0            
191 0 0 0 0 0   sub isVnE { my $i = $#list+$_[0]; $i >= 0 && $list[$i]->{type} eq 'V'
  0            
192             && $list[$i]->{val} !~ /\Ae/
193             }
194 0 0   0 0   sub isE { my $i = $#list+$_[0]; $i >= 0 && $list[$i]->{type} eq 'E' }
  0            
195              
196 0     0 0   sub syl { my $syl = "";
197 0           for (1..$_[0]) { $syl = pop(@list)->{val}.$syl }
  0            
198 0           unshift @syls, $syl;
199 0           1}
200              
201 0 0         is_S(0) && do { my $val = pop @list; $list[$#list]->{val} .= $val->{val} };
  0            
  0            
202              
203 0           while (@list)
204             {
205 0 0         print "\t[@syls]\n" if $::debug;
206 0 0 0       isE(-2) && isR(-1) && isVnE(0) && syl(1) && next;
      0        
      0        
207 0 0 0       isC(-1) && is_C(0) && syl(1) && next;
      0        
208 0 0 0       isC_(-3) && isV(-2) && isC(-1) && isE(0) && syl(4) && next;
      0        
      0        
      0        
209 0 0 0       isC_(-2) && isV(-1) && is_C(0) && syl(3) && next;
      0        
      0        
210 0 0 0       isV(-2) && isC(-1) && isE(0) && syl(3) && next;
      0        
      0        
211 0 0 0       isC_(-1) && isV(0) && syl(2) && next;
      0        
212 0 0 0       isV(-1) && is_C(0) && syl(2) && next;
      0        
213 0 0 0       isC(0) && syl(1) && next;
214 0 0 0       isV(0) && syl(1) && next;
215             }
216 0           return @syls;
217             }
218              
219             my %user_def_syl = ();
220             my %user_def_hyph = ();
221              
222             sub def_syl($)
223             {
224 0     0 0   my $word = $_[0];
225 0           $word =~ tr/~//d;
226 0           $user_def_syl{$word} = [split /\~/, $_[0]];
227             }
228              
229             sub def_hyph($)
230             {
231 0     0 0   my $word = $_[0];
232 0           $word =~ tr/~//d;
233 0           $user_def_hyph{$word} = [split /\~/, $_[0]];
234             }
235              
236             sub syllables($) # ($word)
237             {
238 0 0   0 0   return ($_[0]) unless $_[0] =~ /[A-Za-z]/;
239 0           my $word = $_[0];
240 0           $word =~ s/\A([^a-zA-Z]+)//;
241 0   0       my $leader = $1||'';
242 0           $word =~ s/([^a-zA-Z]+)\Z//;
243 0   0       my $trailer = $1||'';
244 0 0         my @syls = @{$user_def_syl{$word}||[]};
  0            
245 0 0         unless (@syls)
246             {
247 0           my @part = split /((?:\s|'(?![ts]\b)|'[^A-Za-z]|[^A-Za-z'])+)/, $word;
248 0           for (my $p = 0; $p < @part; $p++)
249             {
250 0 0         if ($p & 1) { $syls[$#syls] .= $part[$p] }
  0            
251 0           else { push @syls, partition(abstract($part[$p])) }
252             }
253             }
254 0           $syls[0] = $leader . $syls[0];
255 0           $syls[$#syls] .= $trailer;
256 0 0         return @syls if wantarray;
257 0           return join '~', @syls;
258             }
259              
260              
261             sub hyphenate($$;$) # ($word, $width; $hyphen)
262             {
263 0     0 0   my $word = shift;
264 0 0         my @syls = @{$user_def_hyph{$word}||[]};
  0            
265 0 0         @syls = syllables($word) unless @syls;
266 0           my ($width, $hyphen) = (@_,'-');
267 0           my $hlen = length $hyphen;
268 0           my $first = '';
269 0           while (@syls)
270             {
271 0 0         if ($#syls) { last if length($first) + length($syls[0]) + $hlen > $width }
  0 0          
272 0 0         else { last if length($first) + length($syls[0]) > $width }
273 0           $first .= shift @syls;
274             }
275 0 0 0       $first .= $hyphen if $first && @syls && $first !~ /$hyphen\Z/;
      0        
276 0           return ("$first",join '',@syls);
277             }
278              
279             1;
280             __END__