File Coverage

blib/lib/Lingua/IND/Words2Nums.pm
Criterion Covered Total %
statement 89 126 70.6
branch 24 44 54.5
condition 12 26 46.1
subroutine 13 15 86.6
pod 9 9 100.0
total 147 220 66.8


line stmt bran cond sub pod time code
1             # For Emacs: -*- mode:cperl; mode:folding -*-
2              
3             package Lingua::IND::Words2Nums;
4             # ABSTRACT: Word 2 number conversion in IND.
5              
6             # {{{ use block
7              
8 1     1   49650 use 5.10.1;
  1         5  
  1         79  
9              
10 1     1   7 use strict;
  1         2  
  1         47  
11 1     1   5 use warnings;
  1         7  
  1         175  
12              
13 1     1   1734 use Perl6::Export::Attrs;
  1         36958  
  1         7  
14              
15             # }}}
16             # {{{ variables declaration
17              
18             our $VERSION = 0.0682;
19              
20             our %Digits = (
21             nol => 0, kosong => 0,
22             se => 1, satu => 1,
23             dua => 2,
24             tiga => 3,
25             empat => 4,
26             lima => 5,
27             enam => 6,
28             tujuh => 7,
29             delapan => 8,
30             sembilan => 9
31             );
32              
33             our %Mults = (
34             puluh => 1e1,
35             ratus => 1e2,
36             ribu => 1e3,
37             juta => 1e6,
38             milyar => 1e9, milyard => 1e9, miliar => 1e9, miliard => 1e9,
39             triliun => 1e12, trilyun => 1e12
40             );
41              
42             our %Words = (
43             %Digits,
44             %Mults,
45             belas => 0
46             );
47              
48             our $Neg_pat = '(?:negatif|min|minus)';
49             our $Exp_pat = '(?:(?:di)?kali(?:kan)? sepuluh pangkat)';
50             our $Dec_pat = '(?:koma|titik)';
51              
52             # }}}
53              
54             ### public subs
55             # {{{ words2nums
56              
57             sub words2nums :Export {
58 6     6 1 5922 my @a = @_;
59 6         19 return w2n1(@a);
60 1     1   1434 }
  1         2  
  1         10  
61              
62             # }}}
63             # {{{ words2nums
64              
65             sub words2nums_simple :Export {
66 0     0 1 0 my @a = @_;
67 0         0 return w2n5(@a);
68 1     1   479 }
  1         3  
  1         5  
69              
70             # }}}
71              
72             ### private subs
73              
74              
75             # for debugging
76             our $DEBUG = 0;
77 66 50   66 1 307 sub hmm___ {my @a = @_; print "(", (caller 1)[3], ") Hmm, ", @a if $DEBUG; return; }
  66         133  
  66         170  
78              
79              
80             # {{{ handle exponential
81             sub w2n1 {
82 6   100 6 1 33 my $words = shift // '';
83 6         16 $words = lc $words;
84 6         7 my ($num1, $num2);
85              
86 6 50       63 if( $words =~ /(.+)\b$Exp_pat\b(.+)/ ) {
87 0         0 hmm___ "it's an exponent.\n";
88 0         0 $num1 = w2n2($1);
89 0         0 $num2 = w2n2($2);
90 0         0 hmm___ "\$num1 is $num1, \$num2 is $num2\n";
91 0 0 0     0 not defined $num1 or not defined $num2 and return;
92 0         0 return $num1 * 10 ** $num2
93             } else {
94 6         15 hmm___ "not an exponent.\n";
95 6         14 $num1 = w2n2($words);
96 6 100       23 not defined $num1 and return;
97 4         15 hmm___ "\$num1 = $num1\n";
98 4         17 return $num1
99             }
100             }
101              
102             # }}}
103             # {{{ handle negative
104              
105             sub w2n2 {
106 6     6 1 10 my $words = lc shift;
107 6         8 my $num1;
108              
109 6 50       133 if( $words =~ /^[\s\t]*$Neg_pat\b(.+)/ ) {
110 0         0 hmm___ "it's negative.\n";
111 0         0 $num1 = -w2n3($1);
112 0 0       0 not defined $num1 and return;
113 0         0 hmm___ "\$num1 = $num1\n";
114 0         0 return $num1
115             } else {
116 6         14 hmm___ "it's not negative.\n";
117 6         18 $num1 = w2n3($words);
118 6 100       15 not defined $num1 and return;
119 4         12 hmm___ "\$num1 = $num1\n";
120 4         11 return $num1
121             }
122             }
123              
124             # }}}
125             # {{{ handle decimal
126              
127             sub w2n3 {
128 6     6 1 12 my $words = lc shift;
129 6         7 my ($num1, $num2);
130              
131 6 50       109 if( $words =~ /(.+)\b$Dec_pat\b(.+)/ ) {
132 0         0 hmm___ "it has decimals.\n";
133 0         0 $num1 = w2n4($1);
134 0         0 $num2 = w2n5($2);
135 0 0 0     0 not defined $num1 or not defined $num2 and return;
136 0         0 hmm___ "\$num1 is $num1, \$num2 is $num2\n";
137 0         0 return $num1 + "0.".$num2
138             } else {
139 6         12 hmm___ "it's an integer.\n";
140 6         12 $num1 = w2n4($words);
141 6 100       17 not defined $num1 and return;
142 4         12 hmm___ "\$num1 is $num1\n";
143 4         12 return $num1
144             }
145             }
146              
147             # }}}
148             # {{{ handle words before decimal (e.g, 'seratus dua puluh tiga', ...)
149              
150             sub w2n4 {
151 6     6 1 18 my @words = &split_it( lc shift );
152 6         17 my ($num, $mult);
153 6         9 my $seen_digits = 0;
154 6         145 my ($aa, $subtot, $tot);
155 6         8 my @nums = ();
156              
157 6 100 100     36 (defined $words[0] and $words[0] eq 'ERR') and return;
158 4         21 hmm___ "the words are @words.\n";
159              
160 4         14 for my $w (@words) {
161 14 100       48 if( defined $Digits{$w} ) { # digits (satuan)
    50          
162 8         27 hmm___ "saw a digit: $w.\n";
163 0         0 $seen_digits and do { push @nums, ((10 * (pop @nums)) + $Digits{$w}) }
164 8 50 33     24 or do { push @nums, $Digits{$w} ; $seen_digits = 1 }
  8         16  
  8         18  
165             }
166              
167             elsif( $w eq 'belas' ) { # special case, teens (belasan)
168 0         0 hmm___ "saw a teen: $w.\n";
169 0 0       0 return unless $seen_digits ; # (salah penulisan belasan)
170 0         0 push @nums, 10 + pop @nums;
171 0         0 $seen_digits = 0;
172             }
173              
174             else{ # must be a multiplier
175 6         43 hmm___ "saw a multiplier: $w.\n";
176 6 50       15 return unless @nums ; # (salah penulisan puluhan/pengali)
177              
178 6         7 $a = 0 ; $subtot = 0;
  6         8  
179 6   100     9 do { $aa = pop @nums ; $subtot += $aa }
  10         12  
  10         288  
180             until ( $aa > $Mults{$w} || !@nums );
181              
182 6 100       16 if( $aa > $Mults{$w} ) { push @nums, $aa; $subtot -= $aa }
  4         7  
  4         6  
183 6         12 push @nums, $Mults{$w}*$subtot;
184 6         10 $seen_digits = 0;
185             }
186             }
187              
188             # calculate total
189 4         7 $tot = 0;
190 4         13 while( @nums ){ $tot += shift @nums }
  8         18  
191              
192 4         12 return $tot;
193             }
194              
195              
196             # {{{ handle words after decimal (simple with no 'belas', 'puluh', 'ratus', ...)
197             sub w2n5 {
198 0   0 0 1 0 my $words = shift // '';
199 0         0 my @words = &split_it( lc $words );
200 0         0 my ($num, $mult);
201              
202 0 0 0     0 (defined $words[0] and $words[0] eq 'ERR') and return;
203              
204 0         0 $num = 0;
205 0         0 $mult = 1;
206 0         0 for my $w (reverse @words) {
207 0 0       0 not defined $Digits{$w} and return;
208 0         0 $num += $Digits{$w}*$mult;
209 0         0 $mult *= 10;
210             }
211              
212 0         0 return $num;
213             }
214              
215             # }}}
216             # {{{ split string into array of words. also splits 'sepuluh' -> (se, puluh), 'tigabelas' -> (tiga, belas), etc.
217              
218             sub split_it {
219 6     6 1 9 my $words = lc shift;
220 6         8 my @words = ();
221              
222 6         41 for my $w ($words =~ /\b(\w+)\b/g) {
223 14         40 hmm___ "saw $w.\n";
224 14 100 66     786 if( $w =~ /^se(.+)$/ and defined $Words{$1} ) {
    50 33        
    100          
225 2         8 hmm___ "i should split $w.\n";
226 2         6 push @words, 'se', $1 }
227             elsif( $w =~ /^(.+)(belas|puluh|ratus|ribu|juta|mil[iy]ard?|tril[iy]un)$/ and defined $Words{$1} ) {
228 0         0 hmm___ "i should split $w.\n";
229 0         0 push @words, $1, $2 }
230             elsif( defined $Words{$w} ) {
231 10         712 push @words, $w }
232             else {
233 2         177 hmm___ "i don't recognize $w.\n";
234 2         6 unshift @words, 'ERR';
235 2         4 last }
236             }
237              
238 6         28 return @words;
239             }
240              
241             # }}}
242              
243             1;
244             __END__