File Coverage

blib/lib/Constant/Generate.pm
Criterion Covered Total %
statement 147 156 94.2
branch 43 54 79.6
condition 22 25 88.0
subroutine 28 29 96.5
pod 0 1 0.0
total 240 265 90.5


line stmt bran cond sub pod time code
1             package Constant::Generate;
2 6     6   78584 use strict;
  6         11  
  6         188  
3 6     6   23 use warnings;
  6         7  
  6         236  
4             our $VERSION = '0.17';
5              
6 6     6   3648 use Data::Dumper;
  6         43996  
  6         412  
7 6     6   47 use Carp qw(confess);
  6         8  
  6         255  
8 6     6   2342 use Constant::Generate::Dualvar;
  6         10  
  6         30  
9 6     6   33 use Scalar::Util qw(looks_like_number);
  6         10  
  6         493  
10              
11             #these two functions produce reverse mapping, one for simple constants, and
12             #one for bitfields
13              
14             use constant {
15 6         503 CONST_BITFLAG => 1,
16             CONST_SIMPLE => 2,
17             CONST_STRING => 3
18 6     6   30 };
  6         7  
19              
20             sub _gen_bitfield_fn {
21 6     6   27 no strict "refs";
  6         9  
  6         676  
22 5     5   5 my ($name,$rhash) = @_;
23 5         20 *{$name} = sub($) {
24 8     8   1668 my $flag = $_[0];
25 8         32 join("|",
26 8         36 @{$rhash}{(
27             grep($flag & $_, keys %$rhash)
28             )}
29             );
30 5         14 };
31             }
32              
33             sub _gen_int_fn {
34 6     6   30 no strict 'refs';
  6         7  
  6         518  
35 3     3   5 my ($name,$rhash) = @_;
36 3 50   6   12 *{$name} = sub ($) { $rhash->{$_[0] + 0} || "" };
  3         12  
  6         1764  
37             }
38              
39             sub _gen_str_fn {
40 6     6   27 no strict 'refs';
  6         8  
  6         1334  
41 1     1   1 my ($name,$rhash) = @_;
42 1 50   1   2 *{$name} = sub ($) { $rhash->{ $_[0] } || "" };
  1         3  
  1         348  
43             }
44              
45              
46             sub _gen_integer_syms {
47 10     10   62 my ($uarr, $symhash, $start) = @_;
48 10         16 foreach my $sym (@$uarr) {
49 20         32 $symhash->{$sym} = $start;
50 20         29 $start++;
51             }
52             }
53              
54             sub _gen_bitflag_syms {
55 4     4   7 my ($uarr,$symhash,$start) = @_;
56 4         6 foreach my $sym (@$uarr) {
57 11         18 $symhash->{$sym} = 1 << $start;
58 11         10 $start++;
59             }
60             }
61              
62             sub _gen_string_syms {
63 1     1   9 my ($uarr,$symhash,$prefix) = @_;
64 1         2 foreach my $sym (@$uarr) {
65 3         5 $symhash->{$sym} = $sym;
66             }
67             }
68              
69             sub _gen_constant {
70 47     47   68 my ($pkg,$name,@values) = @_;
71 6     6   31 no strict 'refs';
  6         6  
  6         2023  
72 47         61 my $fqname = $pkg . "::$name";
73 47 100       66 if(@values == 1) {
74 45         39 my $value = $values[0];
75 45     0   239 *{$fqname} = sub () { $value };
  45         276  
  0         0  
76             } else {
77 2     2   3 *{$fqname} = sub () { @values };
  2         8  
  2         21  
78             }
79             }
80              
81             sub _gen_map_rhash {
82 9     9   11 my ($symhash, $prefix, $display_prefix) = @_;
83 9         9 my (%maphash,%rhash);
84 9 50 66     33 if($prefix && $display_prefix) {
85 0         0 while (my ($sym,$val) = each %$symhash) {
86 0         0 $maphash{$prefix.$sym} = $val;
87             }
88             } else {
89 9         23 %maphash = %$symhash;
90             }
91            
92             #Check for duplicate constants pointing to the same value
93 9         24 while (my ($sym,$val) = each %maphash) {
94 25         20 push @{$rhash{$val}}, $sym;
  25         73  
95             }
96 9         25 while (my ($val,$syms) = each %rhash) {
97 25 50       32 if(@$syms > 1) {
98 0         0 $rhash{$val} = sprintf("(%s)", join(",", @$syms));
99             } else {
100 25         62 $rhash{$val} = $syms->[0];
101             }
102             }
103 9         19 return \%rhash;
104             }
105              
106             sub _mangle_exporter {
107 19     19   28 my ($pkg, $symlist, $tag,
108             $uspec_export, $uspec_export_ok, $uspec_export_tags) = @_;
109            
110 19         90 my @emap = (
111             [$uspec_export, \my $my_export, 'EXPORT', 'ARRAY'],
112             [$uspec_export_ok, \my $my_export_ok, 'EXPORT_OK', 'ARRAY'],
113             [$uspec_export_tags, \my $my_export_tags, 'EXPORT_TAGS', 'HASH', \$tag]
114             );
115            
116 19         28 foreach (@emap) {
117 57         66 my ($uspec,$myspec,$pvar,$vtype,$depvar) = @$_;
118 57 100       85 if(!$uspec) {
119 51         65 next;
120             }
121 6 50 66     27 if (defined $depvar && !$$depvar) {
122 0         0 next;
123             }
124 6 50       11 if(ref $uspec) {
125 0         0 $$myspec = $uspec;
126             } else {
127 6     6   30 no strict 'refs';
  6         8  
  6         5026  
128 6 50       5 if(!defined ($$myspec = *{$pkg."::$pvar"}{$vtype})) {
  6         28  
129 0         0 confess "Requested mangling of $pvar, but $pvar not yet declared!";
130             }
131             }
132             }
133            
134 19 100       35 if($uspec_export_ok) {
135 2         4 push @$my_export_ok, @$symlist;
136             }
137 19 100       32 if($uspec_export) {
138 1         2 push @$my_export, @$symlist;
139             }
140 19 100       52 if($uspec_export_tags) {
141 3         15 $my_export_tags->{$tag} = [ @$symlist ];
142             }
143             #Verify the required variables
144             }
145              
146             my $FN_CONST_TBL = {
147             CONST_BITFLAG, \&_gen_bitflag_syms,
148             CONST_SIMPLE, \&_gen_integer_syms,
149             CONST_STRING, \&_gen_string_syms
150             };
151              
152             my $FN_RMAP_TBL = {
153             CONST_BITFLAG, \&_gen_bitfield_fn,
154             CONST_SIMPLE, \&_gen_int_fn,
155             CONST_STRING, \&_gen_str_fn,
156             };
157              
158             sub utype2const {
159 19     19 0 22 my $utype = shift;
160 19 100 100     109 if(!$utype || $utype =~ /int/i) {
    100          
    50          
161 12         23 return CONST_SIMPLE;
162             } elsif ($utype =~ /bit/i) {
163 6         17 return CONST_BITFLAG;
164             } elsif ($utype =~ /str/i) {
165 1         2 return CONST_STRING;
166             } else {
167 0         0 die "Unrecognized type '$utype'";
168             }
169             }
170              
171             sub _getopt(\%$) {
172 246     246   217 my ($h,$opt) = @_;
173 246 100       274 foreach ($opt,"-$opt") { return delete $h->{$_} if exists $h->{$_} }
  476         1151  
174             }
175              
176             sub import {
177 19     19   16423 my ($cls,$symspecs,%opts) = @_;
178 19 50       54 return 1 unless $symspecs;
179            
180 19         32 my $reqpkg = caller();
181 19         40 my $type = utype2const(_getopt(%opts, "type"));
182            
183             #Determine our tag for %EXPORT_TAGS and reverse mapping
184            
185 19         41 my $mapname = _getopt(%opts, "mapname");
186 19         38 my $export_tag = _getopt(%opts, "tag");
187 19   100     30 my $prefix = _getopt(%opts, "prefix") || "";
188 19         35 my $display_prefix = _getopt(%opts, "show_prefix");
189 19   100     32 my $start = _getopt(%opts, "start_at") || 0;
190 19   100     30 my $stringy = _getopt(%opts, "stringy_vars")
191             || _getopt(%opts, "dualvar");
192            
193 19         30 my $listname = _getopt(%opts, "allvalues");
194 19         27 my $symsname = _getopt(%opts, "allsyms");
195            
196 19 100 100     68 if((!$mapname) && $export_tag) {
197 3         5 $mapname = $export_tag . "_to_str";
198             }
199            
200             #Generate the values.
201 19         21 my %symhash;
202             #Initial value
203            
204 19 100       71 ref $symspecs eq 'HASH' ? %symhash = %$symspecs :
205             $FN_CONST_TBL->{$type}->($symspecs, \%symhash, $start);
206            
207             #tie it all together
208            
209 19         57 while (my ($symname,$symval) = each %symhash) {
210 45 100 66     117 if($stringy && looks_like_number($symval)) {
211            
212 13 50       18 my $dv_name = $display_prefix ? $prefix . $symname : $symname;
213            
214 13         30 $symval = Constant::Generate::Dualvar::CG_dualvar(
215             $symval, $dv_name);
216             }
217 45         82 _gen_constant($reqpkg, $prefix.$symname, $symval);
218             }
219            
220             #After we have determined values for all the symbols, we can establish our
221             #reverse mappings, if so requested
222 19 100       36 if($mapname) {
223 9         18 my $rhash = _gen_map_rhash(\%symhash, $prefix, $display_prefix);
224 9         30 $FN_RMAP_TBL->{$type}->($reqpkg."::$mapname", $rhash);
225             }
226            
227 19 100       43 if($prefix) {
228 5         9 foreach my $usym (keys %symhash) {
229 11         12 my $v = delete $symhash{$usym};
230 11         17 $symhash{$prefix.$usym} = $v;
231             }
232             }
233            
234 19         33 my $auto_export = _getopt(%opts, "export");
235 19         29 my $auto_export_ok = _getopt(%opts, "export_ok");
236 19         30 my $h_etags = _getopt(%opts, "export_tags");
237            
238 19         34 my @symlist = keys %symhash;
239            
240 19 100       39 if($listname) {
241 1         4 my %tmp = reverse %symhash;
242 1         2 _gen_constant($reqpkg, $listname, keys %tmp);
243 1         2 push @symlist, $listname;
244             }
245 19 100       28 if($symsname) {
246 1         3 _gen_constant($reqpkg, $symsname, keys %symhash);
247 1         1 push @symlist, $symsname;
248             }
249            
250 19 100       38 push @symlist, $mapname if $mapname;
251 19   100     76 _mangle_exporter($reqpkg, \@symlist,
252             $export_tag,
253             $auto_export, $auto_export_ok, $h_etags || $export_tag);
254              
255 19 50       5665 if(%opts) {
256 0           die "Unknown keys " . join(",", keys %opts);
257             }
258             }
259              
260             __END__