File Coverage

blib/lib/LooksLike.pm
Criterion Covered Total %
statement 115 121 95.0
branch 93 148 62.8
condition 19 24 79.1
subroutine 21 22 95.4
pod 18 18 100.0
total 266 333 79.8


line stmt bran cond sub pod time code
1              
2 1     1   826 use v5.12.0;
  1         4  
3              
4 1     1   15 use strict;
  1         2  
  1         24  
5 1     1   5 use warnings;
  1         2  
  1         44  
6              
7             package LooksLike;
8             # ABSTRACT: See if a number looks like a number, integer, numeric, infinity, not-a-number, zero, non-zero, positive, negative, even, or odd.
9              
10              
11 1     1   8 use B ();
  1         2  
  1         2578  
12              
13              
14             our $VERSION = 'v0.20.060'; # VERSION
15              
16              
17             my $digits = '[0123456789]';
18             my $int = qr/$digits+/;
19             my $bits = '[01]';
20             my $binary = qr/0b$bits+/i;
21             my $octits = '[01234567]';
22             my $octal = qr/0$octits+/;
23             my $xigits = '[[:xdigit:]]';
24             my $hex = qr/0x$xigits+/i;
25              
26              
27             ### The following can only be tested with regular expressions ###
28              
29              
30             our $Binary = $binary;
31              
32              
33             sub binary {
34 2 50   2 1 574 local $_ = shift if @_;
35 2 50       6 return undef unless defined;
36 2 50       4 return undef if ref;
37              
38 2         50 return /\A\s*$Binary\s*\z/;
39             }
40              
41              
42              
43             our $Octal = $octal;
44              
45              
46             sub octal {
47 8 50   8 1 3081 local $_ = shift if @_;
48 8 50       22 return undef unless defined;
49 8 50       15 return undef if ref;
50              
51 8         99 return /\A\s*$Octal\s*\z/;
52             }
53              
54              
55              
56             our $Hex = $hex;
57              
58              
59             sub hex {
60 3 50   3 1 1133 local $_ = shift if @_;
61 3 50       8 return undef unless defined;
62 3 50       7 return undef if ref;
63              
64 3         63 return /\A\s*$Hex\s*\z/;
65             }
66              
67              
68              
69             our $Decimal = qr/[+-]?(?:$int(?:\.$digits*)?|\.$int)/;
70              
71              
72             sub decimal {
73 8 50   8 1 1488 local $_ = shift if @_;
74 8 50       17 return undef unless defined;
75 8 50       15 return undef if ref;
76              
77 8         135 return /\A\s*$Decimal\s*\z/;
78             }
79              
80              
81              
82             my $infinity = 8888e8888;
83             my $inf = do {
84             my $inf = qr/inf(?:inity)?/i;
85             if ( $^O eq 'MSWin32' || $^V ge v5.22.0 ) {
86             # Some versions of Perl accept a broader
87             # range of representations of infinity.
88             # 1.#infinity, 1.#inf*
89             my $dotinf = qr/1\.\#inf(?:inity|0*)/i;
90             qr/$dotinf|$inf/;
91             } elsif ( $infinity !~ $inf ) {
92             $inf = join( '|',
93             sort { length($b) <=> length($a) } $inf, quotemeta($infinity)
94             );
95             qr/$inf/;
96             } else {
97             $inf;
98             }
99             };
100              
101             my $notanumber = $infinity / $infinity;
102             my $nan = do {
103             my $nan = qr/nan/i;
104             if ( $^O eq 'MSWin32' || $^V ge v5.22.0 ) {
105             # Some versions of Perl accept a broader
106             # range of representations of NaN.
107             # https://en.wikipedia.org/wiki/NaN#Display
108             # nan[qs]?, [qs]nan,
109             # nan\($int\), nan\($hex\), nan\(\"$octal\"\), nan\($binary\)
110             # 1\.\#nan[qs]?, 1\.\#[qs]nan, 1\.\#ind0*
111             my $nan = qr/nan[qs]?|[qs]nan/i;
112             my $nandig = qr/$nan\((?:$binary|\"$octal\"|$hex|$int)\)/i;
113             my $ind = qr/ind0*/i;
114             my $dotnan = qr/1\.\#(?:$nandig|$nan|$ind)/;
115             qr/$dotnan|$nandig|$nan/
116             } elsif ( $notanumber !~ $nan ) {
117             $nan = join( '|',
118             sort { length($b) <=> length($a) } $nan, quotemeta($notanumber)
119             );
120             qr/$nan/;
121             } else {
122             $nan;
123             }
124             };
125              
126             sub grok_number {
127 47 50   47 1 75833 local $_ = shift if @_;
128 47 50       118 return unless defined;
129 47 50       95 return if ref;
130              
131 47         66 my ( $sign, $number, $frac, $exp_sign, $exp_number, $excess );
132              
133 47         787 ( $sign, $number ) = m/\A\s*([+-]?)($inf|$nan|$int?)/cg;
134 47 100       377 if ( $number =~ m/\A(?:$inf|$nan)\z/ ) {
135 21 100 66     431 $frac = $1
      66        
136             if ( $^V ge v5.22.0
137             && $number =~ s/\A1\.\#//
138             && $number =~ s/(?:\(($binary|\"$octal\"|$hex|$int)\)|0*)\z// );
139              
140             # There should be no additional fractional
141             # nor exponent portion to parse.
142             } else {
143 26         221 ( $frac, $exp_sign, $exp_number )
144             = /\G(?:\.($int?))?(?:[Ee]([+-]?)($int))?/cg;
145             }
146 47 100 100     170 if ( !length($number) && !length($frac) ) {
147             # Nope, this is not a legitimate number.
148 2         6 $sign = $number = $frac = $exp_sign = $exp_number = undef;
149 2         6 pos() = 0;
150             }
151 47 100       146 m/\G\s*/cg if pos();
152 47         99 $excess = substr( $_, pos() );
153              
154 47         329 return ( $sign, $number, $frac, $exp_sign, $exp_number, $excess );
155             }
156              
157              
158             # The following can be tested with mathematics or regular expressions.
159              
160              
161             our $Infinity = qr/[+-]?$inf/;
162              
163              
164             sub infinity {
165 86 50   86 1 14471 local $_ = shift if @_;
166 86 100       181 return undef unless defined;
167 85 50       145 return undef if ref;
168              
169 85 100       425 if ( B::svref_2object( \$_ )->FLAGS & B::SVp_NOK ) {
170 17   66     170 return $_ == $infinity || $_ == -$infinity;
171             }
172 68         774 return /\A\s*$Infinity\s*\z/;
173             }
174              
175              
176              
177             our $NaN = qr/[+-]?$nan/;
178              
179              
180             sub nan {
181 101 50   101 1 14631 local $_ = shift if @_;
182 101 100       210 return undef unless defined;
183 100 50       176 return undef if ref;
184              
185 100 100       875 if ( B::svref_2object( \$_ )->FLAGS & B::SVp_NOK ) {
186 31         187 return not defined( $_ <=> 0 );
187             }
188 69         947 return /\A\s*$NaN\s*\z/;
189             }
190              
191              
192              
193             our $Integer = qr/[+-]?$int/;
194              
195              
196             sub integer {
197 73 50   73 1 14508 local $_ = shift if @_;
198 73 100       139 return undef unless defined;
199 72 50       143 return undef if ref;
200              
201 72         361 my $flags = B::svref_2object( \$_ )->FLAGS;
202 72 100 100     229 if ( $flags & B::SVp_IOK && !( $flags & B::SVp_NOK ) ) {
203 16         73 return 1;
204             }
205 56         584 return /\A\s*$Integer\s*\z/;
206             }
207              
208              
209              
210             my $exponent = qr/[Ee]$Integer/;
211             our $Numeric = qr/$Decimal$exponent?/;
212              
213              
214             sub numeric {
215 89 50   89 1 9132 local $_ = shift if @_;
216 89 100       167 return undef unless defined;
217 88 50       167 return undef if ref;
218              
219 88 100       422 if ( B::svref_2object( \$_ )->FLAGS & ( B::SVp_NOK | B::SVp_IOK ) ) {
220 36   33     353 return defined( $_ <=> 0 ) && $_ != $infinity && $_ != -$infinity;
221             }
222 52         646 return /\A\s*$Numeric\s*\z/;
223             }
224              
225              
226              
227             # NaN is not comparable.
228             sub comparable {
229 0 0   0 1 0 local $_ = shift if @_;
230 0 0       0 return undef unless defined;
231 0 0       0 return undef if ref;
232              
233 0 0       0 if ( B::svref_2object( \$_ )->FLAGS & ( B::SVp_NOK | B::SVp_IOK ) ) {
234 0         0 return defined( $_ <=> 0 );
235             }
236 0         0 return /\A\s*(?:$Infinity|$Integer|$Numeric)\s*\z/;
237             }
238              
239              
240              
241             sub number {
242 165 50   165 1 17770 local $_ = shift if @_;
243 165 100       323 return undef unless defined;
244 164 50       314 return undef if ref;
245              
246 164 100       765 if ( B::svref_2object( \$_ )->FLAGS & ( B::SVp_NOK | B::SVp_IOK ) ) {
247 62         288 return 1;
248             }
249 102         1574 return /\A\s*(?:$Infinity|$Integer|$NaN|$Numeric)\s*\z/;
250             }
251              
252              
253              
254             # 0, 0.0*, .0+, 0E0, 0.0E0, .0E100, ...
255             my $zero = qr/(?:0+(?:[.]0*)?|[.]0+)$exponent?/;
256             our $Zero = qr/[+-]?$zero/;
257              
258              
259             sub zero {
260 40 50   40 1 5338 local $_ = shift if @_;
261 40 100       99 return undef unless defined;
262 36 50       69 return undef if ref;
263              
264 36 100       180 if ( B::svref_2object( \$_ )->FLAGS & ( B::SVp_NOK | B::SVp_IOK ) ) {
265 14         77 return $_ == 0;
266             }
267 22         338 return /\A\s*$Zero\s*\z/;
268             }
269              
270              
271              
272             my $nonzero = do {
273             my $digits19 = '[123456789]';
274             my $nonzeroint = qq/$digits*$digits19+$digits*/;
275             my $nonzerofloat = qq/[.]$nonzeroint/;
276             my $nonzeronum = qr/$nonzeroint(?:[.]$digits*)?|$digits*$nonzerofloat/;
277             qr/$inf|$nonzeronum$exponent?/;
278             };
279             our $NonZero = qr/[+-]?$nonzero/;
280              
281              
282             sub nonzero {
283 98 50   98 1 11285 local $_ = shift if @_;
284 98 50       195 return undef unless defined;
285 98 50       174 return undef if ref;
286              
287 98 100       458 if ( B::svref_2object( \$_ )->FLAGS & ( B::SVp_NOK | B::SVp_IOK ) ) {
288 48         261 return $_ != 0;
289             }
290 50         746 return /\A\s*$NonZero\s*\z/;
291             }
292              
293              
294              
295             our $Positive = qr/[+]?$nonzero/;
296              
297              
298             # Returns true if number would be greater than 0
299             sub positive {
300 99 50   99 1 11729 local $_ = shift if @_;
301 99 50       192 return undef unless defined;
302 99 50       180 return undef if ref;
303              
304 99 100       466 if ( B::svref_2object( \$_ )->FLAGS & ( B::SVp_NOK | B::SVp_IOK ) ) {
305 49         285 return $_ > 0;
306             }
307 50         767 return /\A\s*$Positive\s*\z/;
308             }
309              
310              
311             our $Negative = qr/[-]$nonzero/;
312              
313              
314             # Returns true if number would be less than 0
315             sub negative {
316 98 50   98 1 11304 local $_ = shift if @_;
317 98 50       207 return undef unless defined;
318 98 50       179 return undef if ref;
319              
320 98 100       491 if ( B::svref_2object( \$_ )->FLAGS & ( B::SVp_NOK | B::SVp_IOK ) ) {
321 48         263 return $_ < 0;
322             }
323 50         726 return /\A\s*$Negative\s*\z/;
324             }
325              
326              
327              
328             my $evens = '[02468]';
329             our $Even = qr/[+-]?$digits*$evens/;
330              
331              
332             # Returns true if integer would be divisible by 2
333             sub even {
334 75 50   75 1 13523 local $_ = shift if @_;
335 75 100       160 return undef unless defined;
336 74 50       139 return undef if ref;
337              
338 74         306 my $flags = B::svref_2object( \$_ )->FLAGS;
339 74 100 100     224 if ( $flags & B::SVp_IOK && !( $flags & B::SVp_NOK ) ) {
340 15         84 return 0 == ( $_ % 2 );
341             }
342 59         640 return /\A\s*$Even\s*\z/;
343             }
344              
345              
346              
347             my $odds = '[13579]';
348             our $Odd = qr/[+-]?$digits*$odds/;
349              
350              
351             # Returns true if integer would not be divisible by 2
352             sub odd {
353 75 50   75 1 13247 local $_ = shift if @_;
354 75 100       155 return undef unless defined;
355 74 50       135 return undef if ref;
356              
357 74         307 my $flags = B::svref_2object( \$_ )->FLAGS;
358 74 100 100     228 if ( $flags & B::SVp_IOK && !( $flags & B::SVp_NOK ) ) {
359 15         79 return 0 != ( $_ % 2 );
360             }
361 59         629 return /\A\s*$Odd\s*\z/;
362             }
363              
364              
365             our %representation = (
366             "infinity" => "inf",
367             "-infinity" => "-inf",
368             "nan" => "nan",
369             );
370              
371              
372             sub representation {
373 2 50   2 1 1909 local $_ = shift if @_ % 2;
374 2 50       7 return undef unless defined;
375 2 50       5 return undef if ref;
376              
377 2         10 my %repr = ( %representation, @_ );
378              
379             return nan($_) ? $repr{"nan"}
380             : infinity($_) ? positive($_) ? $repr{"infinity"}
381             : $repr{"-infinity"}
382 2 50       8 : exists( $repr{$_} ) ? $repr{$_}
    0          
    50          
    100          
383             : $_
384             ;
385             }
386              
387             1;
388              
389             __END__