File Coverage

blib/lib/Lingua/IND/Words2Nums.pm
Criterion Covered Total %
statement 88 125 70.4
branch 24 44 54.5
condition 12 26 46.1
subroutine 13 15 86.6
pod 9 9 100.0
total 146 219 66.6


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