File Coverage

blib/lib/Sort/Key/Types.pm
Criterion Covered Total %
statement 77 81 95.0
branch 31 50 62.0
condition 1 3 33.3
subroutine 11 11 100.0
pod 1 3 33.3
total 121 148 81.7


line stmt bran cond sub pod time code
1             package Sort::Key::Types;
2              
3             our $VERSION = '1.30';
4              
5 5     5   29 use strict;
  5         11  
  5         165  
6 5     5   26 use warnings;
  5         11  
  5         119  
7 5     5   24 use Carp;
  5         16  
  5         4448  
8              
9             require Exporter;
10             our @ISA = qw(Exporter);
11             our @EXPORT_OK = qw(register_type);
12              
13             our $DEBUG;
14             $DEBUG ||= 0;
15              
16             # this hash is also used from Sort::Key::Multi to find out which
17             # letters can be used as types:
18              
19             our %mktypes = ( s => 0,
20             l => 1,
21             n => 2,
22             i => 3,
23             u => 4 );
24              
25             sub _mks2n {
26 33 50   33   150 if (my ($rev, $key)=$_[0]=~/^([-+]?)(.)$/) {
27 33 50       72 exists $mktypes{$key}
28             or croak "invalid multi-key type '$_[0]'";
29 33         45 my $n = $mktypes{$key};
30 33 100       63 $n+=128 if $rev eq '-';
31 33         160 return $n
32             }
33 0         0 die "internal error, bad key '$_[0]'";
34             }
35              
36             our %mkmap = qw(str s
37             string s
38             locale l
39             loc l
40             lstr l
41             int i
42             integer i
43             uint u
44             unsigned_integer u
45             number n
46             num n);
47              
48             $_ = [$_] for (values %mkmap);
49             our %mksub = map { $_ => undef } keys %mkmap;
50              
51             sub _get_map {
52 50     50   214 my ($rev, $name) = $_[0]=~/^([+-]?)(.*)$/;
53 50 50       133 exists $mkmap{$name}
54             or croak "unknown key type '$name'\n";
55 50 100       112 if ($rev eq '-') {
56 15 50       26 return map { /^-(.*)$/ ? $1 : "-$_" } @{$mkmap{$name}}
  15         82  
  15         44  
57             }
58 35         36 @{$mkmap{$name}}
  35         145  
59             }
60              
61             sub _get_sub {
62 36     36   87 $_[0]=~/^[+-]?(.*)$/;
63 36 50       108 exists $mksub{$1}
64             or croak "unknown key type '$1'\n";
65 36         111 return $mksub{$1}
66             }
67              
68 44     44   65 sub _combine_map { map { _get_map $_ } @_ }
  50         88  
69              
70 5     5   33 use constant _nl => "\n";
  5         10  
  5         8017  
71              
72 24     24 0 56 sub combine_types { pack('C*', (map { _mks2n $_ } _combine_map(@_))) }
  33         54  
73              
74             sub combine_sub {
75 31     31 0 41 my $sub = shift;
76 31         64 my $for = shift;
77 31 100       68 $for = defined $for ? " for $for" : "";
78              
79 31         41 my @subs = map { _get_sub $_ } @_;
  36         61  
80              
81 31 50       59 if ($sub) {
82 31         44 my $code = 'sub { '._nl;
83 31 100       61 if (ref $sub eq 'CODE') {
84 19 100       27 unless (grep { defined $_ } @subs) {
  23         74  
85 16         55 return $sub
86             }
87 3         6 $code.= 'my @keys = &{$sub};'._nl;
88             }
89             else {
90 12 50       28 if ($sub eq '@_') {
91 12 100       16 return undef unless grep {defined $_} @subs;
  13         48  
92             }
93 10         19 $code.= 'my @keys = '.$sub.';'._nl;
94             }
95 13 50       29 $code.= 'print "in: |@keys|\n";'._nl if $DEBUG;
96              
97 13         37 $code.= '@keys == '.scalar(@_)
98             . ' or croak "wrong number of keys generated$for '
99             . '(expected '.scalar(@_).', returned ".scalar(@keys).")";'._nl;
100              
101             { # new scope so @map doesn't get captured
102 13         13 my @map = _combine_map @_;
  13         23  
103 13 100       32 if (@map==@_) {
104 10         82 for my $i (0..$#_) {
105 10 50       22 if (defined $subs[$i]) {
106 10         42 $code.= '{ local $_ = $keys['.$i.']; ($keys['.$i.']) = &{$subs['.$i.']}() }'._nl;
107             }
108             }
109 10 50       26 $code.='print "out: |@keys|\n";'._nl if $DEBUG;
110 10         22 $code.='return @keys'._nl;
111             }
112             else {
113 3         4 $code.='my @keys1;'._nl;
114 3         8 for my $i (0..$#_) {
115 4 50       9 if (defined $subs[$i]) {
116 4         10 $code.= '{ local $_ = shift @keys; push @keys1, &{$subs['.$i.']}() }'._nl;
117             }
118             else {
119 0         0 $code.= 'push @keys1, shift @keys;'._nl;
120             }
121             }
122 3 50       10 $code.='print "out: |@keys1|\n";'._nl if $DEBUG;
123 3         5 $code.='return @keys1'._nl;
124             }
125             }
126 13         16 $code.='}'._nl;
127 13 50       28 print "CODE$for:\n$code----\n" if $DEBUG >= 2;
128 13         1752 my $map = eval $code;
129 13 50       35 $@ and die "internal error: code generation failed ($@)";
130 13         41 return $map;
131             }
132             else {
133 0 0       0 @_==1 or croak "too many keys or keygen subroutine undefined$for";
134 0         0 return @subs;
135             }
136             }
137              
138             sub register_type {
139 7     7 1 12 my $name = shift;
140 7         9 my $sub = shift;
141 7 50       59 $name=~/^\w+(?:::\w+)*$/
142             or croak "invalid type name '$name'";
143 7 50       20 @_ or
144             croak "too few keys";
145 7 50 33     52 (exists $mkmap{$name} or exists $mktypes{$name})
146             and croak "type '$name' already registered or reserved in ".__PACKAGE__;
147 7         21 $mkmap{$name} = [ _combine_map @_ ];
148 7         27 $mksub{$name} = combine_sub $sub, $name, @_;
149             ()
150 7         215 }
151              
152              
153             1;
154              
155             __END__