File Coverage

blib/lib/Number/Range/Regex.pm
Criterion Covered Total %
statement 64 64 100.0
branch 22 24 91.6
condition 2 3 66.6
subroutine 11 11 100.0
pod 3 4 75.0
total 102 106 96.2


line stmt bran cond sub pod time code
1             # Number::Range::Regex
2              
3             #
4             # Copyright 2012 Brian Szymanski. All rights reserved. This module is
5             # free software; you can redistribute it and/or modify it under the same
6             # terms as Perl itself.
7              
8             package Number::Range::Regex;
9              
10 14     14   556266 use strict;
  14         38  
  14         1582  
11 14     14   10276 use Number::Range::Regex::Range;
  14         57  
  14         1112  
12 14     14   9661 use Number::Range::Regex::Iterator;
  14         43  
  14         790  
13 14     14   90 use Number::Range::Regex::Util;
  14         34  
  14         1851  
14 14     14   88 use Number::Range::Regex::Util::inf qw( neg_inf pos_inf );
  14         23  
  14         855  
15 14     14   150 use vars qw ( @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $VERSION );
  14         27  
  14         1428  
16             eval { require warnings; }; #it's ok if we can't load warnings
17              
18             require Exporter;
19 14     14   75 use base 'Exporter';
  14         25  
  14         17785  
20             @ISA = qw( Exporter );
21             @EXPORT = qw( range rangespec );
22             @EXPORT_OK = qw ( init regex_range ) ;
23             %EXPORT_TAGS = ( all => [ @EXPORT, @EXPORT_OK ] );
24              
25             $VERSION = '0.32';
26              
27             my $init_opts = $Number::Range::Regex::Range::default_opts;
28              
29             sub init {
30 4     4 0 3414 my ($self, @opts) = @_;
31              
32             # vestigial limb: init( foo => "bar" ) == init( { foo => "bar" } );
33 4 50       24 my %opts = (@opts == 1) ? %{$opts[0]} :
  1 100       4  
34             (@opts % 2 == 0) ? @opts :
35             die 'usage: init( $options_ref )';
36              
37 4         6 $init_opts = $Number::Range::Regex::Range::default_opts;
38             # override any values of init_opts that were passed to init
39 4         19 while (my ($key, $value) = each %opts) {
40 3         19 $init_opts->{$key} = $value;
41             }
42             }
43              
44             # regex_range( $min, $max ); #undef = no limit, so. e.g.
45             # regex_range(3, undef) yields the equivalent of qr/[+]?[3-9]|\d+/;
46             sub regex_range {
47 271     271 1 444273 my ($min, $max, $passed_opts) = @_;
48 271         961 my $opts = option_mangler( $init_opts, $passed_opts );
49 270         844 return range($min, $max, $opts)->regex();
50             }
51              
52             sub range {
53 345 100   345 1 26102 my $opts = option_mangler( ref $_[-1] eq 'HASH' ? pop : undef );
54 345         1109 my ($min, $max) = @_;
55 345 100 66     887 if(!defined $min && !defined $max) {
56 3 100       36 die "for the set of all integers, you must specify min as '-inf' and max as '+inf', or use the allow_wildcard argument" if !$opts->{allow_wildcard};
57             }
58 344 100       828 $min = neg_inf if !defined $min;
59 344 100       764 $max = pos_inf if !defined $max;
60 344         1328 return rangespec( "$min..$max", $opts );
61             }
62              
63             sub rangespec {
64 491 100   491 1 30161 my $opts = option_mangler( ref $_[-1] eq 'HASH' ? pop : undef );
65             # we allow (but do not like) e.g. rangespec(5,7,10..18);
66             # we don't like it because it can make us run out of memory for
67             # large ranges. preferred: rangespec('5,7,10..18');
68 491         720 my $spec;
69 491 100       1202 if(@_ > 1) {
70 1         5 warn "passed literal range to rangespec!\n";
71 1         10 $spec = join $opts->{range_separator}, @_;
72             } else {
73 490         993 $spec = $_[0];
74             }
75              
76 491         954 my $base = $opts->{base};
77 491         1569 my $base_digits = base_digits($base);
78 491         2104 my $base_max = substr($base_digits, -1);
79              
80 491         1163 my $digits_validate = "[$base_digits]+";
81 491         1615 my $range_operator = '\s*'.quotemeta( $opts->{range_operator} ).'\s*';
82 491         1263 my $range_separator = '\s*'.quotemeta( $opts->{range_separator} ).'\s*';
83 491         5490 my $section_validate = qr/(?:-?$digits_validate|(?:-?$digits_validate|-inf)$range_operator(?:\+?inf|-?$digits_validate))/;
84 491         5050 my $range_validate = qr/(?:|$section_validate(?:$range_separator$section_validate)*)/;
85 491 100       7932 die "invalid rangespec '$spec' !~ /$range_validate/" unless $spec =~ /^$range_validate$/;
86              
87 487         3039 my @sections = split /$range_separator/, $spec;
88 487         771 my @ranges;
89 487         822 foreach my $section (@sections) {
90 617 100       5094 if($section =~ /^(-?$digits_validate)$/) {
91 64         288 push @ranges, Number::Range::Regex::SimpleRange->new( $1, $1, $opts );
92             } else {
93 553         3727 my ($min, $max) = map { s/^\s+//; s/\s+$//; $_ } split /$range_operator/, $section, 2;
  1106         2290  
  1106         2366  
  1106         17043  
94 553         3205 push @ranges, Number::Range::Regex::SimpleRange->new( $min, $max, $opts );
95             }
96             }
97 485 50       1975 my $warn_overlap = defined $opts->{warn_overlap} ?
98             $opts->{warn_overlap} : 'rangespec';
99             # note: multi_union() will have the side effect of sorting
100             # and de-overlap-ify-ing the input ranges
101 485         2534 return multi_union( @ranges, { warn_overlap => $warn_overlap } );
102             }
103              
104             1;
105              
106             __END__