File Coverage

blib/lib/Parse/Range.pm
Criterion Covered Total %
statement 44 44 100.0
branch 24 24 100.0
condition 10 12 83.3
subroutine 7 7 100.0
pod 1 1 100.0
total 86 88 97.7


line stmt bran cond sub pod time code
1             package Parse::Range;
2            
3             our $VERSION = 0.96;
4            
5 1     1   22697 use strict;
  1         2  
  1         36  
6 1     1   5 use warnings;
  1         3  
  1         32  
7 1     1   926 use List::MoreUtils qw(uniq first_index);
  1         1273  
  1         91  
8            
9 1     1   8 use base qw(Exporter);
  1         2  
  1         709  
10            
11             our @EXPORT_OK = qw(parse_range);
12            
13             sub parse_range {
14 63     63 1 16454 my $in = join(',', @_);
15 63         83 my $level = 0;
16 63         116 my @range = ('');
17 63         226 foreach my $char (split('', $in)) {
18 543 100       994 $level++ if($char eq '(');
19 543 100 66     1972 $range[-1] .= $char if($char !~ /,/ || $char =~ /[\(\),]/ && $level != 0);
      66        
20 543 100       966 $level-- if($char eq ')');
21 543 100 100     1773 push(@range, '') if($level == 0 && $char eq ',');
22             }
23 63 100       189 if($level != 0) {
24 8 100       36 return $level < 0 ? parse_range(('(' x -$level) . $in) : parse_range($in . ')' x $level);
25             }
26 55         75 my @out = ();
27 55         86 foreach my $range (@range) {
28 128         209 $range =~ s/\s//gsm;
29 128         193 my @except = ();
30 128 100       259 if($range =~ /^\^(.*)$/) {
31 10         20 push(@except, _parse_range($1));
32             } else {
33 118         214 push(@out, _parse_range($range));
34             }
35 128         918 @out = uniq(@out);
36 128         437 foreach my $e (@except) {
37 28     83   119 my $idx = first_index { $_ eq $e } @out;
  83         113  
38 28 100       127 splice(@out, $idx, 1) if($idx != -1);
39             }
40             }
41 55         297 return @out;
42             }
43            
44             sub _parse_range {
45 128     128   207 my $range = shift;
46 128 100 100     880 if($range =~ /^(-?\d+)\-(-?\d+)$/ || $range =~ /^(-?\d+)\)?\-\((-\d+)\)?$/) {
    100          
    100          
47 26         65 my($from, $to) = ($1, $2);
48 26 100       65 ($to, $from) = ($from, $to) if($from > $to);
49 26         1322 return eval $from.'..'.$to;
50             } elsif($range =~ /^(-?\d+)$/) {
51 71         214 return $1;
52             } elsif($range =~ /^\((.*)\)$/) {
53 27         64 return parse_range($1);
54 4         53 } else { warn 'non-numeric range: \'' . $range. '\''; return () }
  4         11  
55             }
56            
57             1;
58            
59             __END__