File Coverage

blib/lib/Lingua/EST/Word2Num.pm
Criterion Covered Total %
statement 23 55 41.8
branch 0 24 0.0
condition 2 11 18.1
subroutine 9 10 90.0
pod 3 3 100.0
total 37 103 35.9


line stmt bran cond sub pod time code
1             # For Emacs: -*- mode:cperl; eval: (folding-mode 1); coding:utf-8 -*-
2             package Lingua::EST::Word2Num;
3             # ABSTRACT: Word to number conversion in Estonian
4              
5 1     1   127334 use 5.16.0;
  1         4  
6 1     1   7 use utf8;
  1         2  
  1         17  
7 1     1   32 use warnings;
  1         2  
  1         74  
8              
9             # {{{ use block
10              
11 1     1   721 use Export::Attrs;
  1         12649  
  1         8  
12 1     1   1497 use Parse::RecDescent;
  1         51732  
  1         10  
13              
14             # }}}
15             # {{{ var block
16             our $VERSION = '0.2603300';
17             my $parser = est_numerals();
18              
19             # }}}
20              
21             # {{{ w2n convert text to number
22              
23             sub w2n :Export {
24 2   100 2 1 280922 my $input = shift // return;
25              
26 1         17 return $parser->numeral($input);
27 1     1   158 }
  1         2  
  1         10  
28              
29             # }}}
30             # {{{ est_numerals create parser for estonian numerals
31              
32             sub est_numerals {
33 1     1 1 6 return Parse::RecDescent->new(q{
34            
35              
36             numeral: mega
37             | kOhOd
38             | { }
39              
40             number: 'üksteist' { 11 }
41             | 'kaksteist' { 12 }
42             | 'kolmteist' { 13 }
43             | 'neliteist' { 14 }
44             | 'viisteist' { 15 }
45             | 'kuusteist' { 16 }
46             | 'seitseteist' { 17 }
47             | 'kaheksateist' { 18 }
48             | 'üheksateist' { 19 }
49             | 'kümme' { 10 }
50             | 'null' { 0 }
51             | 'üks' { 1 }
52             | 'kaks' { 2 }
53             | 'kolm' { 3 }
54             | 'neli' { 4 }
55             | 'viis' { 5 }
56             | 'kuus' { 6 }
57             | 'seitse' { 7 }
58             | 'kaheksa' { 8 }
59             | 'üheksa' { 9 }
60              
61             tens: 'kakskümmend' { 20 }
62             | 'kolmkümmend' { 30 }
63             | 'nelikümmend' { 40 }
64             | 'viiskümmend' { 50 }
65             | 'kuuskümmend' { 60 }
66             | 'seitsekümmend' { 70 }
67             | 'kaheksakümmend' { 80 }
68             | 'üheksakümmend' { 90 }
69              
70             deca: tens number { $item[1] + $item[2] }
71             | tens
72             | number
73              
74             hecto: number 'sada' deca { $item[1] * 100 + $item[3] }
75             | number 'sada' { $item[1] * 100 }
76             | 'sada' deca { 100 + $item[2] }
77             | 'sada' { 100 }
78              
79             hOd: hecto
80             | deca
81              
82             kilo: hOd 'tuhat' hOd { $item[1] * 1000 + $item[3] }
83             | hOd 'tuhat' { $item[1] * 1000 }
84             | 'tuhat' hOd { 1000 + $item[2] }
85             | 'tuhat' { 1000 }
86              
87             kOhOd: kilo
88             | hOd
89              
90             mega: hOd /miljonit?/ kOhOd { $item[1] * 1_000_000 + $item[3] }
91             | hOd /miljonit?/ { $item[1] * 1_000_000 }
92             | 'miljon' kOhOd { 1_000_000 + $item[2] }
93             | 'miljon' { 1_000_000 }
94             });
95             }
96              
97             # }}}
98              
99             # {{{ ordinal2cardinal convert ordinal text to cardinal text
100              
101             sub ordinal2cardinal :Export {
102 0   0 0 1   my $input = shift // return;
103              
104             # Estonian ordinals:
105             # esimene → üks (1st, suppletive)
106             # teine → kaks (2nd, suppletive)
107             # Regular: stem + -s (kolmas→kolm, neljas→neli, etc.)
108             # -ne ending: kümnes→kümme (10th)
109             # Compounds: only last element is ordinal, parts separated by space.
110              
111 0           my %irregular = (
112             'esimene' => 'üks',
113             'teine' => 'kaks',
114             );
115              
116             # Compound: "kakskümmend kolmas" → split, convert last part
117 0 0         if ($input =~ m{\s}xms) {
118 0           my @words = split /\s+/, $input;
119 0           my $last = pop @words;
120 0   0       my $cardinal = ordinal2cardinal($last) // return;
121 0           push @words, $cardinal;
122 0           return join ' ', @words;
123             }
124              
125 0 0         return $irregular{$input} if exists $irregular{$input};
126              
127             # Teens compound ordinals: e.g. "üheteistkümnes" → "üksteist"
128             # The ordinal marker -kümnes is at the end for teens.
129             # The teen ordinal stems differ from cardinal stems:
130             # ühe→üks, kahe→kaks, kolme→kolm, nelja→neli, viie→viis,
131             # kuue→kuus, seitse→seitse, kaheksa→kaheksa, üheksa→üheksa
132 0 0         if ($input =~ m{\A (?.+) teistkümnes \z}xms) {
133 0           my $stem = $+{stem};
134 0           my %teen_ord_to_card = (
135             'ühe' => 'üks',
136             'kahe' => 'kaks',
137             'kolme' => 'kolm',
138             'nelja' => 'neli',
139             'viie' => 'viis',
140             'kuue' => 'kuus',
141             'seitse' => 'seitse',
142             'kaheksa' => 'kaheksa',
143             'üheksa' => 'üheksa',
144             );
145 0   0       my $card_stem = $teen_ord_to_card{$stem} // $stem;
146 0           return $card_stem . 'teist';
147             }
148              
149             # Round tens ordinals: e.g. "kahekümnes" (20th) → "kakskümmend"
150             # Stems: kahe→kaks, kolme→kolm, nelja→neli, viie→viis,
151             # kuue→kuus, seitse→seitse, kaheksa→kaheksa, üheksa→üheksa
152 0 0         if ($input =~ m{\A (?.+) kümnes \z}xms) {
153 0           my $stem = $+{stem};
154             # Plain "kümnes" (10th) → "kümme"
155 0 0         return 'kümme' if $stem eq '';
156              
157 0           my %tens_ord_to_card = (
158             'kahe' => 'kaks',
159             'kolme' => 'kolm',
160             'nelja' => 'neli',
161             'viie' => 'viis',
162             'kuue' => 'kuus',
163             'seitse' => 'seitse',
164             'kaheksa' => 'kaheksa',
165             'üheksa' => 'üheksa',
166             );
167 0   0       my $card_stem = $tens_ord_to_card{$stem} // return;
168 0           return $card_stem . 'kümmend';
169             }
170              
171             # Standalone "kümnes" (10th)
172 0 0         return 'kümme' if $input eq 'kümnes';
173              
174             # Thousands ordinals: "tuhandes" (1000th) → "tuhat"
175             # In compounds: "viis tuhandes" → space-separated, handled by compound splitter above
176 0 0         return 'tuhat' if $input eq 'tuhandes';
177              
178             # Hundreds ordinals: "sajas" (100th) → "sada", compounds: "kakssajas" → "kakssada"
179             # The parser expects "sada" as the hundred token.
180 0 0         if ($input =~ m{\A (?.+?) sajas \z}xms) {
181 0           my $pfx = $+{pfx};
182 0 0         return 'sada' if $pfx eq '';
183             # Compound: prefix is the cardinal multiplier (kaks, kolm, etc.)
184 0           return $pfx . 'sada';
185             }
186 0 0         return 'sada' if $input eq 'sajas';
187              
188             # Regular: strip -s
189             # Map known stems to cardinals the parser expects
190 0           my %stem_to_cardinal = (
191             'kolma' => 'kolm',
192             'nelja' => 'neli',
193             'viie' => 'viis',
194             'kuue' => 'kuus',
195             'seitsme' => 'seitse',
196             'kaheksa' => 'kaheksa',
197             'üheksa' => 'üheksa',
198             );
199              
200 0 0         if ($input =~ s{s\z}{}xms) {
201 0 0         return $stem_to_cardinal{$input} if exists $stem_to_cardinal{$input};
202 0           return $input;
203             }
204              
205 0           return; # not an ordinal
206 1     1   1061 }
  1         2  
  1         8  
207              
208             # }}}
209              
210             1;
211              
212             __END__