File Coverage

blib/lib/Lingua/POL/Word2Num.pm
Criterion Covered Total %
statement 24 37 64.8
branch 0 6 0.0
condition 2 19 10.5
subroutine 9 10 90.0
pod 3 3 100.0
total 38 75 50.6


line stmt bran cond sub pod time code
1             # For Emacs: -*- mode:cperl; eval: (folding-mode 1) -*-
2              
3             package Lingua::POL::Word2Num;
4             # ABSTRACT: Word to number conversion in Polish
5              
6 1     1   104434 use 5.16.0;
  1         4  
7 1     1   7 use utf8;
  1         2  
  1         21  
8 1     1   32 use warnings;
  1         2  
  1         112  
9              
10             # {{{ use block
11              
12 1     1   735 use Export::Attrs;
  1         12936  
  1         7  
13 1     1   1479 use Parse::RecDescent;
  1         46424  
  1         8  
14              
15             # }}}
16             # {{{ var block
17             our $VERSION = '0.2603300';
18             my $COPY = 'Copyright (c) PetaMem, s.r.o. 2003-present';
19             my $parser = pol_numerals();
20              
21             # }}}
22              
23             # {{{ w2n convert text to number
24              
25             sub w2n :Export {
26 3   100 3 1 295667 my $input = shift // return;
27              
28             # print "INPUT: '$input'\n";
29 2         10 $input =~ s{\s\z}{}xms;
30             # print "INPUT: '$input'\n";
31              
32 2         27 return $parser->numeral($input);
33 1     1   149 }
  1         2  
  1         7  
34              
35             # }}}
36             # {{{ pol_numerals create parser for numerals
37              
38             sub pol_numerals {
39 1     1 1 4 return Parse::RecDescent->new(q{
40            
41              
42             numeral: mega
43             | kOhOd
44             | 'zero' { 0 }
45             | { }
46              
47             number: 'dziewiętnaście' { 19 }
48             | 'osiemnaście' { 18 }
49             | 'siedemnaście' { 17 }
50             | 'szesnaście' { 16 }
51             | 'piętnaście' { 15 }
52             | 'czternaście' { 14 }
53             | 'trzynaście' { 13 }
54             | 'dwanaście' { 12 }
55             | 'jedenaście' { 11 }
56             | 'dziesięć' { 10 }
57             | 'dziewięć' { 9 }
58             | 'osiem' { 8 }
59             | 'siedem' { 7 }
60             | 'sześć' { 6 }
61             | 'pięć' { 5 }
62             | 'cztery' { 4 }
63             | 'trzy' { 3 }
64             | 'dwa' { 2 }
65             | 'jeden' { 1 }
66              
67             tens: 'dwadzieścia' { 20 }
68             | 'trzydzieści' { 30 }
69             | 'czterdzieści' { 40 }
70             | 'pięćdziesiąt' { 50 }
71             | 'sześćdziesiąt' { 60 }
72             | 'siedemdziesiąt' { 70 }
73             | 'osiemdziesiąt' { 80 }
74             | 'dziewięćdziesiąt' { 90 }
75              
76             deca: tens number { $item[1] + $item[2] }
77             | tens
78             | number
79              
80             hecto: number /(sta|set)/ deca { $item[1] * 100 + $item[3] }
81             | number /(sta|set)/ { $item[1] * 100 }
82             | 'dwieście' deca { 2 * 100 + $item[2] }
83             | 'dwieście' { 200 }
84             | 'sto' deca { 100 + $item[2] }
85             | 'sto' { 100 }
86              
87             hOd: hecto
88             | deca
89              
90             kilo: hOd /(tysiąc[ae]?|tysięcy)/ hOd { $item[1] * 1000 + $item[3] }
91             | hOd /(tysiąc[ae]?|tysięcy)/ { $item[1] * 1000 }
92             | number /(tysiąc[ae]?|tysięcy)/ hOd { $item[1] * 1000 + $item[3] }
93             | number /(tysiąc[ae]?|tysięcy)/ { $item[1] * 1000 }
94             | 'tysiąc' hOd { 1000 + $item[2] }
95             | 'tysiąc' { 1000 }
96             | hOd 'jeden' 'tysiąc' hOd { ($item[1] + 1) * 1000 + $item[4] }
97             | hOd 'jeden' 'tysiąc' { ($item[1] + 1) * 1000 }
98              
99             kOhOd: kilo
100             | hOd
101              
102             mega: hOd megas kOhOd { $item[1] * 1_000_000 + $item[3] }
103             | hOd megas { $item[1] * 1_000_000 }
104              
105             megas: /milion(y|ów)?/
106             });
107             }
108              
109             # }}}
110             # {{{ ordinal2cardinal convert ordinal text to cardinal text
111              
112             sub ordinal2cardinal :Export {
113 0   0 0 1   my $input = shift // return;
114              
115             # Polish ordinals: strip gender/case suffixes, then map stems.
116             # Inflection: -y/-a/-e/-ego/-emu/-ym (masc/fem/neut/oblique).
117             # Special: -i forms for 3rd (trzeci).
118              
119 0           my %irregular = (
120             'zerowy' => 'zero',
121             'pierwszy' => 'jeden',
122             'drugi' => 'dwa',
123             'trzeci' => 'trzy',
124             'czwarty' => 'cztery',
125             'piąty' => 'pięć',
126             'szósty' => 'sześć',
127             'siódmy' => 'siedem',
128             'ósmy' => 'osiem',
129             'dziewiąty' => 'dziewięć',
130             'dziesiąty' => 'dziesięć',
131             'jedenasty' => 'jedenaście',
132             'dwunasty' => 'dwanaście',
133             'trzynasty' => 'trzynaście',
134             'czternasty' => 'czternaście',
135             'piętnasty' => 'piętnaście',
136             'szesnasty' => 'szesnaście',
137             'siedemnasty' => 'siedemnaście',
138             'osiemnasty' => 'osiemnaście',
139             'dziewiętnasty' => 'dziewiętnaście',
140             'dwudziesty' => 'dwadzieścia',
141             'trzydziesty' => 'trzydzieści',
142             'czterdziesty' => 'czterdzieści',
143             'pięćdziesiąty' => 'pięćdziesiąt',
144             'sześćdziesiąty' => 'sześćdziesiąt',
145             'siedemdziesiąty' => 'siedemdziesiąt',
146             'osiemdziesiąty' => 'osiemdziesiąt',
147             'dziewięćdziesiąty' => 'dziewięćdziesiąt',
148             'dwusetny' => 'dwieście',
149             'trzechsetny' => 'trzy sta',
150             'czterechsetny' => 'cztery sta',
151             'pięćsetny' => 'pięć set',
152             'sześćsetny' => 'sześć set',
153             'siedemsetny' => 'siedem set',
154             'osiemsetny' => 'osiem set',
155             'dziewięćsetny' => 'dziewięć set',
156             'setny' => 'sto',
157             'tysięczny' => 'tysiąc',
158             'milionowy' => 'milion',
159             );
160              
161             # Compound ordinals: ALL components are ordinal forms.
162             # Normalize each word individually, then look up in the mapping.
163 0           my @words = split /\s+/, $input;
164 0           my @result;
165 0           my $matched = 0;
166              
167 0           for my $word (@words) {
168             # Strip gender/case suffixes to masculine nominative
169 0           my $norm = $word;
170 0 0 0       $norm =~ s{(i)ego\z}{$1}xms # trzeciego → trzeci
      0        
      0        
      0        
      0        
171             or $norm =~ s{ego\z}{y}xms # czwartego → czwarty
172             or $norm =~ s{emu\z}{y}xms # czwartemu → czwarty
173             or $norm =~ s{ym\z}{y}xms # czwartym → czwarty
174             or $norm =~ s{a\z}{y}xms # czwarta → czwarty
175             or $norm =~ s{(i)ej\z}{$1}xms # trzeciej → trzeci
176             or $norm =~ s{ej\z}{y}xms; # czwartej → czwarty
177              
178 0 0         if (exists $irregular{$norm}) {
179 0           push @result, $irregular{$norm};
180 0           $matched = 1;
181             }
182             else {
183 0           push @result, $word; # pass through unchanged (connectors, etc.)
184             }
185             }
186              
187 0 0         return $matched ? join(' ', @result) : undef;
188 1     1   580 }
  1         4  
  1         3  
189              
190             # }}}
191              
192             1;
193              
194             __END__