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   801 use v5.12.0;
  1         4  
3              
4 1     1   5 use strict;
  1         2  
  1         40  
5 1     1   6 use warnings;
  1         2  
  1         32  
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   5 use B ();
  1         2  
  1         2258  
12              
13              
14             our $VERSION = 'v0.20.045'; # 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 549 local $_ = shift if @_;
35 2 50       5 return undef unless defined;
36 2 50       5 return undef if ref;
37              
38 2         49 return /\A\s*$Binary\s*\z/;
39             }
40              
41              
42              
43             our $Octal = $octal;
44              
45              
46             sub octal {
47 8 50   8 1 3163 local $_ = shift if @_;
48 8 50       19 return undef unless defined;
49 8 50       16 return undef if ref;
50              
51 8         153 return /\A\s*$Octal\s*\z/;
52             }
53              
54              
55              
56             our $Hex = $hex;
57              
58              
59             sub hex {
60 3 50   3 1 1129 local $_ = shift if @_;
61 3 50       9 return undef unless defined;
62 3 50       7 return undef if ref;
63              
64 3         51 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 1538 local $_ = shift if @_;
74 8 50       17 return undef unless defined;
75 8 50       18 return undef if ref;
76              
77 8         137 return /\A\s*$Decimal\s*\z/;
78             }
79              
80              
81              
82             my $inf = do {
83             my $inf = qr/inf(?:inity)?/i;
84             if ( $^V ge v5.22.0 ) {
85             # Newer versions of Perl accept a broader
86             # range of representations of infinity.
87             # 1.#infinity, 1.#inf*
88             my $dotinf = qr/1\.\#inf(?:inity|0*)/i;
89             qr/$dotinf|$inf/;
90             } else {
91             $inf;
92             }
93             };
94              
95             my $nan = do {
96             if ( $^V ge v5.22.0 ) {
97             # Newer versions of Perl accept a broader
98             # range of representations of NaN.
99             # https://en.wikipedia.org/wiki/NaN#Display
100             # nan[qs]?, [qs]nan,
101             # nan\($int\), nan\($hex\), nan\(\"$octal\"\), nan\($binary\)
102             # 1\.\#nan[qs]?, 1\.\#[qs]nan, 1\.\#ind0*
103             my $nan = qr/nan[qs]?|[qs]nan/i;
104             my $nandig = qr/$nan\((?:$binary|\"$octal\"|$hex|$int)\)/i;
105             my $ind = qr/ind0*/i;
106             my $dotnan = qr/1\.\#(?:$nandig|$nan|$ind)/;
107             qr/$dotnan|$nandig|$nan/
108             } else {
109             qr/nan/i;
110             }
111             };
112              
113             sub grok_number {
114 47 50   47 1 76334 local $_ = shift if @_;
115 47 50       111 return unless defined;
116 47 50       92 return if ref;
117              
118 47         73 my ( $sign, $number, $frac, $exp_sign, $exp_number, $excess );
119              
120 47         771 ( $sign, $number ) = m/\A\s*([+-]?)($inf|$nan|$int?)/cg;
121 47 100       377 if ( $number =~ m/\A(?:$inf|$nan)\z/ ) {
122 21 100 66     421 $frac = $1
      66        
123             if ( $^V ge v5.22.0
124             && $number =~ s/\A1\.\#//
125             && $number =~ s/(?:\(($binary|\"$octal\"|$hex|$int)\)|0*)\z// );
126              
127             # There should be no additional fractional
128             # nor exponent portion to parse.
129             } else {
130 26         217 ( $frac, $exp_sign, $exp_number )
131             = /\G(?:\.($int?))?(?:[Ee]([+-]?)($int))?/cg;
132             }
133 47 100 100     159 if ( !length($number) && !length($frac) ) {
134             # Nope, this is not a legitimate number.
135 2         6 $sign = $number = $frac = $exp_sign = $exp_number = undef;
136 2         6 pos() = 0;
137             }
138 47 100       157 m/\G\s*/cg if pos();
139 47         102 $excess = substr( $_, pos() );
140              
141 47         379 return ( $sign, $number, $frac, $exp_sign, $exp_number, $excess );
142             }
143              
144              
145             # The following can be tested with mathematics or regular expressions.
146              
147              
148             my $infinity = 9e9999;
149              
150             our $Infinity = qr/[+-]?$inf/;
151              
152              
153             sub infinity {
154 83 50   83 1 14800 local $_ = shift if @_;
155 83 100       165 return undef unless defined;
156 82 50       151 return undef if ref;
157              
158 82 100       472 if ( B::svref_2object( \$_ )->FLAGS & B::SVp_NOK ) {
159 16   66     153 return $_ == $infinity || $_ == -$infinity;
160             }
161 66         798 return /\A\s*$Infinity\s*\z/;
162             }
163              
164              
165              
166             #my $notanumber = $infinity / $infinity;
167              
168             our $NaN = qr/[+-]?$nan/;
169              
170              
171             sub nan {
172 97 50   97 1 9047 local $_ = shift if @_;
173 97 100       215 return undef unless defined;
174 96 50       166 return undef if ref;
175              
176 96 100       463 if ( B::svref_2object( \$_ )->FLAGS & B::SVp_NOK ) {
177 29         169 return not defined( $_ <=> 0 );
178             }
179 67         930 return /\A\s*$NaN\s*\z/;
180             }
181              
182              
183              
184             our $Integer = qr/[+-]?$int/;
185              
186              
187             sub integer {
188 71 50   71 1 14975 local $_ = shift if @_;
189 71 100       190 return undef unless defined;
190 70 50       120 return undef if ref;
191              
192 70         357 my $flags = B::svref_2object( \$_ )->FLAGS;
193 70 100 100     226 if ( $flags & B::SVp_IOK && !( $flags & B::SVp_NOK ) ) {
194 16         66 return 1;
195             }
196 54         576 return /\A\s*$Integer\s*\z/;
197             }
198              
199              
200              
201             my $exponent = qr/[Ee]$Integer/;
202             our $Numeric = qr/$Decimal$exponent?/;
203              
204              
205             sub numeric {
206 87 50   87 1 8707 local $_ = shift if @_;
207 87 100       175 return undef unless defined;
208 86 50       163 return undef if ref;
209              
210 86 100       414 if ( B::svref_2object( \$_ )->FLAGS & ( B::SVp_NOK | B::SVp_IOK ) ) {
211 36   33     335 return defined( $_ <=> 0 ) && $_ != $infinity && $_ != -$infinity;
212             }
213 50         620 return /\A\s*$Numeric\s*\z/;
214             }
215              
216              
217              
218             # NaN is not comparable.
219             sub comparable {
220 0 0   0 1 0 local $_ = shift if @_;
221 0 0       0 return undef unless defined;
222 0 0       0 return undef if ref;
223              
224 0 0       0 if ( B::svref_2object( \$_ )->FLAGS & ( B::SVp_NOK | B::SVp_IOK ) ) {
225 0         0 return defined( $_ <=> 0 );
226             }
227 0         0 return /\A\s*(?:$Infinity|$Integer|$Numeric)\s*\z/;
228             }
229              
230              
231              
232             sub number {
233 163 50   163 1 1558 local $_ = shift if @_;
234 163 100       303 return undef unless defined;
235 162 50       293 return undef if ref;
236              
237 162 100       758 if ( B::svref_2object( \$_ )->FLAGS & ( B::SVp_NOK | B::SVp_IOK ) ) {
238 62         290 return 1;
239             }
240 100         1503 return /\A\s*(?:$Infinity|$Integer|$NaN|$Numeric)\s*\z/;
241             }
242              
243              
244              
245             # 0, 0.0*, .0+, 0E0, 0.0E0, .0E100, ...
246             my $zero = qr/(?:0+(?:[.]0*)?|[.]0+)$exponent?/;
247             our $Zero = qr/[+-]?$zero/;
248              
249              
250             sub zero {
251 38 50   38 1 5032 local $_ = shift if @_;
252 38 100       92 return undef unless defined;
253 34 50       55 return undef if ref;
254              
255 34 100       165 if ( B::svref_2object( \$_ )->FLAGS & ( B::SVp_NOK | B::SVp_IOK ) ) {
256 14         72 return $_ == 0;
257             }
258 20         307 return /\A\s*$Zero\s*\z/;
259             }
260              
261              
262              
263             my $nonzero = do {
264             my $digits19 = '[123456789]';
265             my $nonzeroint = qq/$digits*$digits19+$digits*/;
266             my $nonzerofloat = qq/[.]$nonzeroint/;
267             my $nonzeronum = qr/$nonzeroint(?:[.]$digits*)?|$digits*$nonzerofloat/;
268             qr/$inf|$nonzeronum$exponent?/;
269             };
270             our $NonZero = qr/[+-]?$nonzero/;
271              
272              
273             sub nonzero {
274 96 50   96 1 10907 local $_ = shift if @_;
275 96 50       201 return undef unless defined;
276 96 50       168 return undef if ref;
277              
278 96 100       446 if ( B::svref_2object( \$_ )->FLAGS & ( B::SVp_NOK | B::SVp_IOK ) ) {
279 48         243 return $_ != 0;
280             }
281 48         681 return /\A\s*$NonZero\s*\z/;
282             }
283              
284              
285              
286             our $Positive = qr/[+]?$nonzero/;
287              
288              
289             # Returns true if number would be greater than 0
290             sub positive {
291 96 50   96 1 10918 local $_ = shift if @_;
292 96 50       244 return undef unless defined;
293 96 50       173 return undef if ref;
294              
295 96 100       439 if ( B::svref_2object( \$_ )->FLAGS & ( B::SVp_NOK | B::SVp_IOK ) ) {
296 48         270 return $_ > 0;
297             }
298 48         684 return /\A\s*$Positive\s*\z/;
299             }
300              
301              
302             our $Negative = qr/[-]$nonzero/;
303              
304              
305             # Returns true if number would be less than 0
306             sub negative {
307 96 50   96 1 11032 local $_ = shift if @_;
308 96 50       191 return undef unless defined;
309 96 50       188 return undef if ref;
310              
311 96 100       510 if ( B::svref_2object( \$_ )->FLAGS & ( B::SVp_NOK | B::SVp_IOK ) ) {
312 48         241 return $_ < 0;
313             }
314 48         724 return /\A\s*$Negative\s*\z/;
315             }
316              
317              
318              
319             my $evens = '[02468]';
320             our $Even = qr/[+-]?$digits*$evens/;
321              
322              
323             # Returns true if integer would be divisible by 2
324             sub even {
325 73 50   73 1 13476 local $_ = shift if @_;
326 73 100       150 return undef unless defined;
327 72 50       170 return undef if ref;
328              
329 72         305 my $flags = B::svref_2object( \$_ )->FLAGS;
330 72 100 100     294 if ( $flags & B::SVp_IOK && !( $flags & B::SVp_NOK ) ) {
331 15         79 return 0 == ( $_ % 2 );
332             }
333 57         630 return /\A\s*$Even\s*\z/;
334             }
335              
336              
337              
338             my $odds = '[13579]';
339             our $Odd = qr/[+-]?$digits*$odds/;
340              
341              
342             # Returns true if integer would not be divisible by 2
343             sub odd {
344 73 50   73 1 12839 local $_ = shift if @_;
345 73 100       164 return undef unless defined;
346 72 50       149 return undef if ref;
347              
348 72         304 my $flags = B::svref_2object( \$_ )->FLAGS;
349 72 100 100     216 if ( $flags & B::SVp_IOK && !( $flags & B::SVp_NOK ) ) {
350 15         78 return 0 != ( $_ % 2 );
351             }
352 57         607 return /\A\s*$Odd\s*\z/;
353             }
354              
355             1;
356              
357             __END__