File Coverage

blib/lib/Lingua/ISL/Word2Num.pm
Criterion Covered Total %
statement 23 47 48.9
branch 0 20 0.0
condition 2 8 25.0
subroutine 9 10 90.0
pod 3 3 100.0
total 37 88 42.0


line stmt bran cond sub pod time code
1             # For Emacs: -*- mode:cperl; eval: (folding-mode 1); coding:utf-8 -*-
2             package Lingua::ISL::Word2Num;
3             # ABSTRACT: Word to number conversion in Icelandic
4              
5 1     1   102777 use 5.16.0;
  1         6  
6 1     1   7 use utf8;
  1         1  
  1         16  
7 1     1   36 use warnings;
  1         2  
  1         80  
8              
9             # {{{ use block
10              
11 1     1   696 use Export::Attrs;
  1         11309  
  1         5  
12 1     1   1166 use Parse::RecDescent;
  1         31296  
  1         6  
13              
14             # }}}
15             # {{{ var block
16             our $VERSION = '0.2603300';
17             my $parser = isl_numerals();
18              
19             # }}}
20              
21             # {{{ w2n convert text to number
22              
23             sub w2n :Export {
24 2   100 2 1 217234 my $input = shift // return;
25              
26 1         14 return $parser->numeral($input);
27 1     1   177 }
  1         8  
  1         9  
28              
29             # }}}
30             # {{{ isl_numerals create parser for icelandic numerals
31              
32             sub isl_numerals {
33 1     1 1 5 return Parse::RecDescent->new(q{
34            
35              
36             numeral: mega /\Z/ { $item[1] }
37             | kOhOd /\Z/ { $item[1] }
38             | /núll/i /\Z/ { 0 }
39             | { }
40              
41             number: /þrettán/i { 13 }
42             | /fjórtán/i { 14 }
43             | /fimmtán/i { 15 }
44             | /sextán/i { 16 }
45             | /sautján/i { 17 }
46             | /átján/i { 18 }
47             | /nítján/i { 19 }
48             | /ellefu/i { 11 }
49             | /tólf/i { 12 }
50             | /tíu/i { 10 }
51             | /einn?|eitt/i { 1 }
52             | /tveir|tvö/i { 2 }
53             | /þrír|þrjú/i { 3 }
54             | /fjórir|fjögur/i { 4 }
55             | /fimm/i { 5 }
56             | /sex/i { 6 }
57             | /sjö/i { 7 }
58             | /átta/i { 8 }
59             | /níu/i { 9 }
60              
61             tens: /tuttugu/i { 20 }
62             | /þrjátíu/i { 30 }
63             | /fjörutíu/i { 40 }
64             | /fimmtíu/i { 50 }
65             | /sextíu/i { 60 }
66             | /sjötíu/i { 70 }
67             | /áttatíu/i { 80 }
68             | /níutíu/i { 90 }
69              
70             deca: tens 'og' number { $item[1] + $item[3] }
71             | number 'og' tens { $item[1] + $item[3] }
72             | tens
73             | number
74              
75             hecto: number /hundruð/i 'og' deca { $item[1] * 100 + $item[4] }
76             | number /hundruð/i deca { $item[1] * 100 + $item[3] }
77             | /hundrað/i 'og' deca { 100 + $item[3] }
78             | /hundrað/i deca { 100 + $item[2] }
79             | number /hundruð/i { $item[1] * 100 }
80             | /hundrað/i { 100 }
81              
82             hOd: hecto
83             | deca
84              
85             kilo: hOd /þúsund/i 'og' hOd { $item[1] * 1000 + $item[4] }
86             | hOd /þúsund/i hOd { $item[1] * 1000 + $item[3] }
87             | hOd /þúsund/i { $item[1] * 1000 }
88             | /þúsund/i 'og' hOd { 1000 + $item[3] }
89             | /þúsund/i hOd { 1000 + $item[2] }
90             | /þúsund/i { 1000 }
91              
92             kOhOd: kilo
93             | hOd
94              
95             mega: hOd /milljón(ir)?/i 'og' kOhOd { $item[1] * 1_000_000 + $item[4] }
96             | hOd /milljón(ir)?/i kOhOd { $item[1] * 1_000_000 + $item[3] }
97             | hOd /milljón(ir)?/i { $item[1] * 1_000_000 }
98             });
99             }
100              
101             # }}}
102             # {{{ ordinal2cardinal convert ordinal text to cardinal text
103              
104             sub ordinal2cardinal :Export {
105 0   0 0 1   my $input = shift // return;
106              
107             # Icelandic ordinal→cardinal: reverse lookup for irregular forms,
108             # suffix stripping for regular/compound forms.
109              
110             # Fully irregular 1-12
111 0           my %irregular = (
112             'fyrsti' => 'einn',
113             'annar' => 'tveir',
114             'þriðji' => 'þrír',
115             'fjórði' => 'fjórir',
116             'fimmti' => 'fimm',
117             'sjötti' => 'sex',
118             'sjöundi' => 'sjö',
119             'áttundi' => 'átta',
120             'níundi' => 'níu',
121             'tíundi' => 'tíu',
122             'ellefti' => 'ellefu',
123             'tólfti' => 'tólf',
124             );
125              
126             # Teens 13-19 (-di suffix on cardinal stem)
127 0           my %teens = (
128             'þrettándi' => 'þrettán',
129             'fjórtándi' => 'fjórtán',
130             'fimmtándi' => 'fimmtán',
131             'sextándi' => 'sextán',
132             'sautjándi' => 'sautján',
133             'átjándi' => 'átján',
134             'nítjándi' => 'nítján',
135             );
136              
137             # Tens ordinals (-asti suffix)
138 0           my %tens = (
139             'tuttugasti' => 'tuttugu',
140             'þrítugasti' => 'þrjátíu',
141             'fertugasti' => 'fjörutíu',
142             'fimmtugasti' => 'fimmtíu',
143             'sextugasti' => 'sextíu',
144             'sjötugasti' => 'sjötíu',
145             'áttugasti' => 'áttatíu',
146             'nítugasti' => 'níutíu',
147             );
148              
149             # Special large number ordinals
150 0           my %special = (
151             'hundraðasti' => 'hundrað',
152             'þúsundasti' => 'þúsund',
153             );
154              
155             # Exact match: standalone ordinals
156 0 0         return $irregular{$input} if exists $irregular{$input};
157 0 0         return $teens{$input} if exists $teens{$input};
158 0 0         return $tens{$input} if exists $tens{$input};
159 0 0         return $special{$input} if exists $special{$input};
160              
161             # Compound ordinals: split on last " og " boundary, convert the tail recursively.
162             # e.g. "tuttugu og fyrsti" → "tuttugu og einn"
163             # e.g. "níu hundruð og níutíu og níuasti" → split at last "og" → prefix + converted tail
164 0 0         if ($input =~ m{\A(.+)\s+og\s+(.+)\z}xms) {
165 0           my ($prefix, $tail_ord) = ($1, $2);
166 0   0       my $tail_card = ordinal2cardinal($tail_ord) // return;
167 0           return $prefix . ' og ' . $tail_card;
168             }
169              
170             # Compound ordinals without "og" (e.g. "tvö hundruðasti")
171             # Split on space, convert last token, rejoin.
172 0 0         if ($input =~ m{\s}xms) {
173 0           my @words = split /\s+/, $input;
174 0           my $last = pop @words;
175 0   0       my $cardinal = ordinal2cardinal($last) // return;
176 0           push @words, $cardinal;
177 0           return join ' ', @words;
178             }
179              
180             # Fallback: strip common ordinal suffixes
181 0 0         $input =~ s{asti\z}{}xms and return $input;
182 0 0         $input =~ s{undi\z}{ur}xms and return $input;
183 0 0         $input =~ s{di\z}{}xms and return $input;
184 0 0         $input =~ s{ti\z}{}xms and return $input;
185              
186 0           return; # not an ordinal
187 1     1   699 }
  1         2  
  1         4  
188              
189             # }}}
190              
191             1;
192              
193             __END__