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