File Coverage

blib/lib/LooksLike.pm
Criterion Covered Total %
statement 110 116 94.8
branch 86 134 64.1
condition 19 24 79.1
subroutine 20 21 95.2
pod 17 17 100.0
total 252 312 80.7


line stmt bran cond sub pod time code
1              
2 1     1   876 use v5.12.0;
  1         4  
3              
4 1     1   6 use strict;
  1         2  
  1         22  
5 1     1   4 use warnings;
  1         2  
  1         41  
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   7 use B ();
  1         1  
  1         2573  
12              
13              
14             our $VERSION = 'v0.20.050'; # 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 511 local $_ = shift if @_;
35 2 50       6 return undef unless defined;
36 2 50       4 return undef if ref;
37              
38 2         60 return /\A\s*$Binary\s*\z/;
39             }
40              
41              
42              
43             our $Octal = $octal;
44              
45              
46             sub octal {
47 8 50   8 1 3181 local $_ = shift if @_;
48 8 50       19 return undef unless defined;
49 8 50       17 return undef if ref;
50              
51 8         96 return /\A\s*$Octal\s*\z/;
52             }
53              
54              
55              
56             our $Hex = $hex;
57              
58              
59             sub hex {
60 3 50   3 1 1119 local $_ = shift if @_;
61 3 50       7 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 1539 local $_ = shift if @_;
74 8 50       18 return undef unless defined;
75 8 50       17 return undef if ref;
76              
77 8         140 return /\A\s*$Decimal\s*\z/;
78             }
79              
80              
81              
82             my $infinity = 9e9999;
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 76306 local $_ = shift if @_;
128 47 50       106 return unless defined;
129 47 50       86 return if ref;
130              
131 47         77 my ( $sign, $number, $frac, $exp_sign, $exp_number, $excess );
132              
133 47         797 ( $sign, $number ) = m/\A\s*([+-]?)($inf|$nan|$int?)/cg;
134 47 100       382 if ( $number =~ m/\A(?:$inf|$nan)\z/ ) {
135 21 100 66     439 $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         222 ( $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         5 $sign = $number = $frac = $exp_sign = $exp_number = undef;
149 2         7 pos() = 0;
150             }
151 47 100       144 m/\G\s*/cg if pos();
152 47         113 $excess = substr( $_, pos() );
153              
154 47         331 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 85 50   85 1 16149 local $_ = shift if @_;
166 85 100       171 return undef unless defined;
167 84 50       155 return undef if ref;
168              
169 84 100       417 if ( B::svref_2object( \$_ )->FLAGS & B::SVp_NOK ) {
170 16   66     143 return $_ == $infinity || $_ == -$infinity;
171             }
172 68         765 return /\A\s*$Infinity\s*\z/;
173             }
174              
175              
176              
177             our $NaN = qr/[+-]?$nan/;
178              
179              
180             sub nan {
181 99 50   99 1 14792 local $_ = shift if @_;
182 99 100       224 return undef unless defined;
183 98 50       166 return undef if ref;
184              
185 98 100       510 if ( B::svref_2object( \$_ )->FLAGS & B::SVp_NOK ) {
186 29         184 return not defined( $_ <=> 0 );
187             }
188 69         1028 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 14579 local $_ = shift if @_;
198 73 100       155 return undef unless defined;
199 72 50       124 return undef if ref;
200              
201 72         293 my $flags = B::svref_2object( \$_ )->FLAGS;
202 72 100 100     225 if ( $flags & B::SVp_IOK && !( $flags & B::SVp_NOK ) ) {
203 16         69 return 1;
204             }
205 56         606 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 9220 local $_ = shift if @_;
216 89 100       169 return undef unless defined;
217 88 50       166 return undef if ref;
218              
219 88 100       405 if ( B::svref_2object( \$_ )->FLAGS & ( B::SVp_NOK | B::SVp_IOK ) ) {
220 36   33     346 return defined( $_ <=> 0 ) && $_ != $infinity && $_ != -$infinity;
221             }
222 52         741 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 17972 local $_ = shift if @_;
243 165 100       356 return undef unless defined;
244 164 50       282 return undef if ref;
245              
246 164 100       802 if ( B::svref_2object( \$_ )->FLAGS & ( B::SVp_NOK | B::SVp_IOK ) ) {
247 62         284 return 1;
248             }
249 102         1701 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 5531 local $_ = shift if @_;
261 40 100       96 return undef unless defined;
262 36 50       91 return undef if ref;
263              
264 36 100       199 if ( B::svref_2object( \$_ )->FLAGS & ( B::SVp_NOK | B::SVp_IOK ) ) {
265 14         72 return $_ == 0;
266             }
267 22         336 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 11636 local $_ = shift if @_;
284 98 50       187 return undef unless defined;
285 98 50       182 return undef if ref;
286              
287 98 100       464 if ( B::svref_2object( \$_ )->FLAGS & ( B::SVp_NOK | B::SVp_IOK ) ) {
288 48         246 return $_ != 0;
289             }
290 50         761 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 98 50   98 1 11448 local $_ = shift if @_;
301 98 50       202 return undef unless defined;
302 98 50       177 return undef if ref;
303              
304 98 100       462 if ( B::svref_2object( \$_ )->FLAGS & ( B::SVp_NOK | B::SVp_IOK ) ) {
305 48         248 return $_ > 0;
306             }
307 50         803 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 11616 local $_ = shift if @_;
317 98 50       223 return undef unless defined;
318 98 50       176 return undef if ref;
319              
320 98 100       474 if ( B::svref_2object( \$_ )->FLAGS & ( B::SVp_NOK | B::SVp_IOK ) ) {
321 48         250 return $_ < 0;
322             }
323 50         821 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 13647 local $_ = shift if @_;
335 75 100       158 return undef unless defined;
336 74 50       140 return undef if ref;
337              
338 74         310 my $flags = B::svref_2object( \$_ )->FLAGS;
339 74 100 100     254 if ( $flags & B::SVp_IOK && !( $flags & B::SVp_NOK ) ) {
340 15         89 return 0 == ( $_ % 2 );
341             }
342 59         670 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 13225 local $_ = shift if @_;
354 75 100       151 return undef unless defined;
355 74 50       140 return undef if ref;
356              
357 74         343 my $flags = B::svref_2object( \$_ )->FLAGS;
358 74 100 100     226 if ( $flags & B::SVp_IOK && !( $flags & B::SVp_NOK ) ) {
359 15         82 return 0 != ( $_ % 2 );
360             }
361 59         640 return /\A\s*$Odd\s*\z/;
362             }
363              
364             1;
365              
366             __END__