File Coverage

blib/lib/Lingua/LAV/Word2Num.pm
Criterion Covered Total %
statement 24 43 55.8
branch 0 20 0.0
condition 2 12 16.6
subroutine 9 10 90.0
pod 3 3 100.0
total 38 88 43.1


line stmt bran cond sub pod time code
1             # For Emacs: -*- mode:cperl; mode:folding; coding:utf-8; -*-
2              
3             package Lingua::LAV::Word2Num;
4             # ABSTRACT: Word to number conversion in Latvian
5              
6 1     1   103096 use 5.16.0;
  1         4  
7 1     1   6 use utf8;
  1         2  
  1         18  
8 1     1   38 use warnings;
  1         2  
  1         81  
9              
10             # {{{ use block
11              
12 1     1   738 use Export::Attrs;
  1         14859  
  1         10  
13 1     1   1365 use Parse::RecDescent;
  1         50027  
  1         8  
14              
15             # }}}
16             # {{{ var block
17             our $VERSION = '0.2603300';
18             my $parser = lav_numerals();
19              
20             # }}}
21              
22             # {{{ w2n convert text to number
23              
24             sub w2n :Export {
25 2   100 2 1 259283 my $input = shift // return;
26              
27 1         7 $input =~ s{\s\z}{}xms;
28              
29 1         18 return $parser->numeral($input);
30 1     1   136 }
  1         5  
  1         7  
31              
32             # }}}
33             # {{{ lav_numerals create parser for Latvian numerals
34              
35             sub lav_numerals {
36 1     1 1 6 return Parse::RecDescent->new(q{
37            
38              
39             numeral: mega
40             | kOhOd
41             | 'nulle' { 0 }
42             | { }
43              
44             number: 'vienpadsmit' { 11 }
45             | 'divpadsmit' { 12 }
46             | 'trīspadsmit' { 13 }
47             | 'četrpadsmit' { 14 }
48             | 'piecpadsmit' { 15 }
49             | 'sešpadsmit' { 16 }
50             | 'septiņpadsmit' { 17 }
51             | 'astoņpadsmit' { 18 }
52             | 'deviņpadsmit' { 19 }
53             | 'viens' { 1 }
54             | 'divi' { 2 }
55             | 'trīs' { 3 }
56             | 'četri' { 4 }
57             | 'pieci' { 5 }
58             | 'seši' { 6 }
59             | 'septiņi' { 7 }
60             | 'astoņi' { 8 }
61             | 'deviņi' { 9 }
62             | 'desmit' { 10 }
63              
64             tens: 'divdesmit' { 20 }
65             | 'trīsdesmit' { 30 }
66             | 'četrdesmit' { 40 }
67             | 'piecdesmit' { 50 }
68             | 'sešdesmit' { 60 }
69             | 'septiņdesmit' { 70 }
70             | 'astoņdesmit' { 80 }
71             | 'deviņdesmit' { 90 }
72              
73             deca: tens number { $item[1] + $item[2] }
74             | tens
75             | number
76              
77             hecto: number /simt(s|i)/ deca { $item[1] * 100 + $item[3] }
78             | number /simt(s|i)/ { $item[1] * 100 }
79             | /simts/ deca { 100 + $item[2] }
80             | /simts/ { 100 }
81              
82             hOd: hecto
83             | deca
84              
85             kilo: hOd /tūkstotis|tūkstoši/ hOd { $item[1] * 1000 + $item[3] }
86             | hOd /tūkstotis|tūkstoši/ { $item[1] * 1000 }
87              
88             kOhOd: kilo
89             | hOd
90              
91             mega: hOd megas kOhOd { $item[1] * 1_000_000 + $item[3] }
92             | hOd megas { $item[1] * 1_000_000 }
93              
94             megas: /miljon(s|i)/
95             });
96             }
97              
98             # }}}
99              
100             # {{{ ordinal2cardinal convert ordinal text to cardinal text
101              
102             sub ordinal2cardinal :Export {
103 0   0 0 1   my $input = shift // return;
104              
105             # Latvian ordinals are adjectival with -ais (masc) / -ā (fem) endings.
106             # Fully suppletive stems for most ordinals.
107             # Compounds: space-separated, only last element is ordinal.
108              
109 0           my %ordinal_to_cardinal = (
110             'pirmais' => 'viens',
111             'pirmā' => 'viens',
112             'otrais' => 'divi',
113             'otrā' => 'divi',
114             'trešais' => 'trīs',
115             'trešā' => 'trīs',
116             'ceturtais' => 'četri',
117             'ceturtā' => 'četri',
118             'piektais' => 'pieci',
119             'piektā' => 'pieci',
120             'sestais' => 'seši',
121             'sestā' => 'seši',
122             'septītais' => 'septiņi',
123             'septītā' => 'septiņi',
124             'astotais' => 'astoņi',
125             'astotā' => 'astoņi',
126             'devītais' => 'deviņi',
127             'devītā' => 'deviņi',
128             'desmitais' => 'desmit',
129             'desmitā' => 'desmit',
130             # Teens
131             'vienpadsmitais' => 'vienpadsmit',
132             'vienpadsmitā' => 'vienpadsmit',
133             'divpadsmitais' => 'divpadsmit',
134             'divpadsmitā' => 'divpadsmit',
135             'trīspadsmitais' => 'trīspadsmit',
136             'trīspadsmitā' => 'trīspadsmit',
137             'četrpadsmitais' => 'četrpadsmit',
138             'četrpadsmitā' => 'četrpadsmit',
139             'piecpadsmitais' => 'piecpadsmit',
140             'piecpadsmitā' => 'piecpadsmit',
141             'sešpadsmitais' => 'sešpadsmit',
142             'sešpadsmitā' => 'sešpadsmit',
143             'septiņpadsmitais' => 'septiņpadsmit',
144             'septiņpadsmitā' => 'septiņpadsmit',
145             'astoņpadsmitais' => 'astoņpadsmit',
146             'astoņpadsmitā' => 'astoņpadsmit',
147             'deviņpadsmitais' => 'deviņpadsmit',
148             'deviņpadsmitā' => 'deviņpadsmit',
149             );
150              
151             # Hundreds/thousands ordinals with stem changes — handle before generic compound splitter.
152             # Standalone: "tūkstošais" → "viens tūkstotis" (parser needs number + tūkstotis)
153             # Compound: "divi tūkstošais" → "divi tūkstoši"
154 0 0 0       if ($input =~ m{tūkstošai[s]?\z}xms || $input =~ m{tūkstošā\z}xms) {
155 0 0         return 'viens tūkstotis' if $input eq 'tūkstošais';
156 0 0         return 'viens tūkstotis' if $input eq 'tūkstošā';
157 0 0         $input =~ s{tūkstošais\z}{tūkstoši}xms and return $input;
158 0 0         $input =~ s{tūkstošā\z}{tūkstoši}xms and return $input;
159             }
160              
161             # Compound: "divdesmit trešais" → convert last part only
162 0 0         if ($input =~ m{\s}xms) {
163 0           my @words = split /\s+/, $input;
164 0           my $last = pop @words;
165 0   0       my $cardinal = ordinal2cardinal($last) // return;
166 0           push @words, $cardinal;
167 0           return join ' ', @words;
168             }
169              
170 0 0         return $ordinal_to_cardinal{$input} if exists $ordinal_to_cardinal{$input};
171              
172             # Hundreds ordinal: simtais → simts (parser expects /simts/)
173 0 0         $input =~ s{simtais\z}{simts}xms and return $input;
174 0 0         $input =~ s{simtā\z}{simts}xms and return $input;
175              
176             # Tens ordinals: strip -ais/-ā from compound tens
177             # divdesmitais → divdesmit, etc.
178 0 0 0       if ($input =~ s{ais\z}{}xms || $input =~ s{ā\z}{}xms) {
179 0           return $input;
180             }
181              
182 0           return; # not an ordinal
183 1     1   602 }
  1         2  
  1         3  
184              
185             # }}}
186              
187             1;
188              
189             __END__