File Coverage

blib/lib/Lingua/ITA/Word2Num.pm
Criterion Covered Total %
statement 25 46 54.3
branch 0 34 0.0
condition 2 4 50.0
subroutine 9 10 90.0
pod 3 3 100.0
total 39 97 40.2


line stmt bran cond sub pod time code
1             # For Emacs: -*- mode:cperl; eval: (folding-mode 1); coding:utf-8; -*-
2              
3             package Lingua::ITA::Word2Num;
4             # ABSTRACT: Word to number conversion in Italian
5              
6 1     1   87255 use 5.16.0;
  1         3  
7 1     1   3 use utf8;
  1         1  
  1         11  
8 1     1   18 use warnings;
  1         1  
  1         49  
9              
10             # {{{ use block
11              
12 1     1   455 use Export::Attrs;
  1         7785  
  1         6  
13 1     1   1079 use Parse::RecDescent;
  1         29884  
  1         6  
14              
15             # }}}
16             # {{{ var block
17             our $VERSION = '0.2603300';
18             our @EXPORT_OK = qw(cardinal2num w2n);
19             my $parser = ita_numerals();
20              
21             # }}}
22              
23             # {{{ w2n convert text to number
24              
25             sub w2n :Export {
26 3   100 3 1 231122 my $input = shift // return;
27              
28 2         10 $input =~ s/,//g;
29 2         15 $input =~ s/ //g;
30              
31 2         30 return $parser->numeral($input);
32 1     1   164 }
  1         1  
  1         8  
33             # }}}
34             # {{{ ita_numerals create parser for numerals
35              
36             sub ita_numerals {
37 1     1 1 4 return Parse::RecDescent->new(q{
38            
39              
40             numeral: mega
41             | kOhOd
42             | 'zero' { 0 }
43             | { }
44              
45             number:
46             'undici' { 11 }
47             | 'tredici' { 13 }
48             | 'un' { 1 }
49             | 'due' { 2 }
50             | 'tre' { 3 }
51             | 'quattro' { 4 }
52             | 'cinque' { 5 }
53             | 'sei' { 6 }
54             | 'sette' { 7 }
55             | 'otto' { 8 }
56             | 'nove' { 9 }
57             | 'dieci' { 10 }
58             | 'dodici' { 12 }
59             | 'quattordici' { 14 }
60             | 'quindici' { 15 }
61             | 'sedici' { 16 }
62             | 'diciassette' { 17 }
63             | 'diciotto' { 18 }
64             | 'diciannove' { 19 }
65              
66             tens: 'venti' { 20 }
67             | /ventuno?/ { 21 }
68             | 'ventotto' { 28 }
69             | 'trenta' { 30 }
70             | 'trent' { 30 }
71             | 'quaranta' { 40 }
72             | 'quarant' { 40 }
73             | 'cinquanta' { 50 }
74             | 'cinquant' { 50 }
75             | 'sessanta' { 60 }
76             | 'sessant' { 60 }
77             | 'settanta' { 70 }
78             | 'settant' { 70 }
79             | 'ottanta' { 80 }
80             | 'ottant' { 80 }
81             | 'novanta' { 90 }
82             | 'novant' { 90 }
83              
84             deca: tens number { $item[1] + $item[2] }
85             | tens
86             | number
87              
88             hecto: number /cento/ deca { $item[1] * 100 + $item[3] }
89             | number /cento/ { $item[1] * 100 }
90             | /cento/ deca { 100 + $item[2] }
91             | 'cento' { 100 }
92              
93             hOd: hecto
94             | deca
95              
96             kilo: hOd /mill?[ae]/ hOd { $item[1] * 1000 + $item[3] }
97             | hOd /mill?[ae]/ { $item[1] * 1000 }
98             | /mill?[ae]/ hOd { 1000 + $item[2] }
99             | /mill?[ae]/ { 1000 }
100              
101             kOhOd: kilo
102             | hOd
103              
104             mega: kOhOd /mill?ion[ei]/ kOhOd { $item[1] * 1_000_000 + $item[3] }
105              
106             });
107             }
108             # }}}
109             # {{{ ordinal2cardinal convert ordinal text to cardinal text
110              
111             sub ordinal2cardinal :Export {
112 0   0 0 1   my $input = shift // return;
113              
114             # Italian ordinals 0-10 are fully irregular
115 0           state $irregular = {
116             'primo' => 'un', 'prima' => 'un',
117             'secondo' => 'due', 'seconda' => 'due',
118             'terzo' => 'tre', 'terza' => 'tre',
119             'quarto' => 'quattro', 'quarta' => 'quattro',
120             'quinto' => 'cinque', 'quinta' => 'cinque',
121             'sesto' => 'sei', 'sesta' => 'sei',
122             'settimo' => 'sette', 'settima' => 'sette',
123             'ottavo' => 'otto', 'ottava' => 'otto',
124             'nono' => 'nove', 'nona' => 'nove',
125             'decimo' => 'dieci', 'decima' => 'dieci',
126             };
127              
128 0 0         return $irregular->{$input} if exists $irregular->{$input};
129              
130             # Regular: cardinal (drop final vowel) + "esimo/esima"
131             # Strip suffix, restore dropped vowel where needed
132 0 0         $input =~ s{esim[oa]\z}{}xms or return;
133              
134             # Italian drops the final vowel before adding -esimo. The dropped vowel
135             # varies by word, so we restore it based on the stem ending.
136             #
137             # Stems ending in a vowel may also need restoration when two vowels
138             # collapsed (ventidue→ventiduesimo: stem "ventidu" needs +e,
139             # ventisei→ventiseiesimo: stem "ventise" needs +i).
140              
141             # dieci (10): centodiec→centodieci, diec→dieci
142 0 0         if ($input =~ m{diec\z}xms) { $input .= 'i' }
  0 0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
143             # -dici family: undic→undici, dodic→dodici, sedic→sedici, etc.
144 0           elsif ($input =~ m{ic\z}xms) { $input .= 'i' }
145             # sette family: diciassett→diciassette, ventisett→ventisette
146 0           elsif ($input =~ m{ett\z}xms) { $input .= 'e' }
147             # otto family: diciott→diciotto, ventott→ventotto, trentott→trentotto
148 0           elsif ($input =~ m{ott\z}xms) { $input .= 'o' }
149             # nove family: diciannov→diciannove, ventinov→ventinove
150 0           elsif ($input =~ m{ov\z}xms) { $input .= 'e' }
151             # quattro: ventiquattr→ventiquattro
152 0           elsif ($input =~ m{ttr\z}xms) { $input .= 'o' }
153             # cinque: venticinqu→venticinque
154 0           elsif ($input =~ m{qu\z}xms) { $input .= 'e' }
155             # uno: ventun→ventuno (parser also accepts "ventun" but full form is safe)
156 0           elsif ($input =~ m{un\z}xms) { $input .= 'o' }
157             # due: ventidu→ventidue
158 0           elsif ($input =~ m{du\z}xms) { $input .= 'e' }
159             # sei: ventise→ventisei
160 0           elsif ($input =~ m{se\z}xms) { $input .= 'i' }
161             # venti (20): vent→venti
162 0           elsif ($input =~ m{vent\z}xms) { $input .= 'i' }
163             # cento: cent→cento
164 0           elsif ($input =~ m{cent\z}xms) { $input .= 'o' }
165             # mille (1000): mill→mille
166 0           elsif ($input =~ m{mill\z}xms) { $input .= 'e' }
167             # mila (thousands): duemil→duemila, cinquemil→cinquemila, centomil→centomila
168 0           elsif ($input =~ m{mil\z}xms) { $input .= 'a' }
169             # decades (trenta, quaranta, etc.): trent→trenta, quarant→quaranta
170             # The parser accepts contracted "trent", "quarant", etc. but
171             # the full form is also fine.
172 0           elsif ($input =~ m{ant\z}xms) { $input .= 'a' }
173              
174 0           return $input;
175 1     1   583 }
  1         2  
  1         3  
176              
177             # }}}
178              
179             1;
180              
181             __END__