File Coverage

blib/lib/Inline/denter.pm
Criterion Covered Total %
statement 143 249 57.4
branch 38 108 35.1
condition 11 46 23.9
subroutine 18 32 56.2
pod 0 18 0.0
total 210 453 46.3


line stmt bran cond sub pod time code
1             package Inline::denter;
2              
3 8     8   44 use strict;
  8         12  
  8         184  
4 8     8   33 use Carp;
  8         11  
  8         1766  
5              
6             sub new {
7 32     32 0 102 my $class = shift;
8 32         885 bless {width => 4,
9             comma => " : ",
10             level => 0,
11             tabwidth => 8,
12             }, $class;
13             }
14              
15             # Prevent a taint exception being thrown by AutoLoader.pm.
16             # Serves no other purpose.
17       0     sub DESTROY {
18             }
19              
20             sub undent {
21 19     19 0 244 local $/ = "\n";
22 19         90 my ($o, $text) = @_;
23 19         149 my ($comma) = $o->{comma};
24 19         159 my $package = caller;
25 19 50       434 $package = caller(1) if $package eq 'Inline::denter';
26 19         37 %{$o->{xref}} = ();
  19         115  
27 19         44 @{$o->{objects}} = ();
  19         90  
28 19         64 @{$o->{context}} = ();
  19         90  
29 19         79 my $glob = '';
30 19         60 chomp $text;
31 19         382 @{$o->{lines}} = split $/, $text;
  19         264  
32 19         57 $o->{level} = 0;
33 19   50     216 $o->{line} ||= 1;
34 19         137 $o->_setup_line;
35 19         82 while (not $o->{done}) {
36 95 50 33     1181 if ($o->{level} == 0 and
37             $o->{content} =~ /^(\w+)\s*$comma\s*(.*)$/) {
38 95         344 $o->{content} = $2;
39 8     8   49 no strict 'refs';
  8         12  
  8         21771  
40 95         114 push @{$o->{objects}}, "$1";
  95         231  
41             }
42 95         126 push @{$o->{objects}}, $o->_undent_data;
  95         249  
43             }
44 19         31 return @{$o->{objects}};
  19         388  
45             }
46              
47             sub _undent_data {
48 190     190   232 my $o = shift;
49 190         341 my ($obj, $class) = ('', '');
50 190         249 my @refs;
51             my %refs;
52 190         427 while ($o->{content} =~ s/^\\(?:\((\w+)\))?((\%|\@|\$|\\).*)/$2/) {
53 0         0 push @refs, $1;
54 0         0 $refs{$1} = scalar @refs;
55             }
56 190 100       700 if ($o->{content} =~ /^([\%\@\$])
    50          
57             (\w(?:\w|::)*)?
58             \s*$/x
59             ) {
60 76         97 my $foo;
61 76 0       332 $obj = ($1 eq '%') ? {} : ($1 eq '@') ? [] : \$foo;
    50          
62 76   50     294 $class = $2 || '';
63 76 50       142 if ($1 eq '%') {
    0          
64 76         173 %$obj = $o->_undent_hash;
65             }
66             elsif ($1 eq '@') {
67 0         0 @$obj = $o->_undent_array;
68             }
69             else {
70 0         0 $$obj = $o->_undent_scalar;
71             }
72 76 50       168 bless $obj, $class if length $class;
73             }
74             elsif ($o->{content} =~ /^\?\s*$/) {
75 0         0 $obj = $o->_undent_undef;
76             }
77             else {
78 114         215 $obj = $o->_undent_value;
79             }
80 190         330 while (@refs) {
81 0         0 my $ref = pop @refs;
82 0         0 my $copy = $obj;
83 0         0 $obj = \ $copy;
84 0 0       0 $o->{xref}{$ref} = $obj if $ref;
85             }
86 190         580 return $obj;
87             }
88              
89             sub _undent_value {
90 114     114   150 my $o = shift;
91 114         184 my $value = '';
92 114 50       271 if ($o->{content} =~ /^\<\<(\w+)(\-?)\s*$/) {
    50          
93 0         0 my ($marker, $chomp) = ($1, $2);
94 0         0 my $line = $o->{line};
95 0         0 $o->_next_line;
96 0   0     0 while (not $o->{done} and
97             $o->{lines}[0] ne $marker) {
98 0         0 $value .= $o->{lines}[0] . "\n";
99 0         0 $o->_next_line;
100             }
101 0 0       0 croak M03_no_value_end_marker($marker, $line) if $o->{done};
102 0 0       0 chomp $value if $chomp;
103             }
104             elsif ($o->{content} =~ /^\"/) {
105 0 0       0 croak $o->M04_mismatched_quotes unless $o->{content} =~ /^\".*\"\s*$/;
106 0         0 ($value = $o->{content}) =~ s/^\"|\"\s*$//g;
107             }
108             else {
109 114         158 $value = $o->{content};
110             }
111 114         248 $o->_next_line;
112 114         217 $o->_setup_line;
113 114         207 return $value;
114             }
115              
116             sub _undent_hash {
117 76     76   109 my @values;
118 76         97 my $o = shift;
119 76         125 my $level = $o->{level} + 1;
120 76         163 $o->_next_line;
121 76         137 $o->_setup_line;
122 76         154 while ($o->{level} == $level) {
123 95         966 my ($key, $value) = split $o->{comma}, $o->{content};
124 95 50 33     418 croak $o->M05_invalid_key_value unless (defined $key and defined $value);
125 95         139 $o->{content} = $value;
126 95         214 push @values, $o->_get_key($key), $o->_undent_data;;
127             }
128 76 50       140 croak $o->M06_invalid_indent_level if $o->{level} > $level;
129 76         251 return @values;
130             }
131              
132             sub _get_key {
133 95     95   151 my ($o, $key) = @_;
134 95 50       420 return $key unless $key =~ /^\<\<(\w+)(\-?)/;
135 0         0 my ($marker, $chomp) = ($1, $2);
136 0         0 $key = '';
137 0         0 my $line = $o->{line};
138 0         0 $o->_next_line;
139 0   0     0 while (not $o->{done} and
140             $o->{lines}[0] ne $marker) {
141 0         0 $key .= $o->{lines}[0] . "\n";
142 0         0 $o->_next_line;
143             }
144 0 0       0 croak M02_no_key_end_marker($marker, $line) if $o->{done};
145 0 0       0 chomp $key if $chomp;
146 0         0 $o->_next_line;
147 0         0 $o->_setup_line;
148 0         0 return $key;
149             }
150              
151             sub _undent_array {
152 0     0   0 my @values;
153 0         0 my $o = shift;
154 0         0 my $level = $o->{level} + 1;
155 0         0 $o->_next_line;
156 0         0 $o->_setup_line;
157 0         0 while ($o->{level} == $level) {
158 0         0 push @values, $o->_undent_data;
159             }
160 0 0       0 croak $o->M06_invalid_indent_level if $o->{level} > $level;
161 0         0 return @values;
162             }
163              
164             sub _undent_scalar {
165 0     0   0 my $values;
166 0         0 my $o = shift;
167 0         0 my $level = $o->{level} + 1;
168 0         0 $o->_next_line;
169 0         0 $o->_setup_line;
170 0 0       0 croak $o->M06_invalid_indent_level if $o->{level} != $level;
171 0 0       0 croak $o->M07_invalid_scalar_value if $o->{content} =~ /^[\%\@\$\\]/;
172 0 0       0 return $o->_undent_undef if $o->{content} =~ /^\?/;
173 0         0 return $o->_undent_value;
174             }
175              
176             sub _undent_undef {
177 0     0   0 my $o = shift;
178 0         0 $o->_next_line;
179 0         0 $o->_setup_line;
180 0         0 return undef;
181             }
182              
183             sub _next_line {
184 190     190   212 my $o = shift;
185 190 50       200 $o->{done}++, $o->{level} = -1, return unless @{$o->{lines}};
  190         324  
186 190         208 local $_ = shift @{$o->{lines}};
  190         322  
187 190         310 $o->{line}++;
188             }
189              
190             sub _setup_line {
191 209     209   249 my $o = shift;
192 209 100       246 $o->{done}++, $o->{level} = -1, return unless @{$o->{lines}};
  209         447  
193 190         221 my ($width, $tabwidth) = @{$o}{qw(width tabwidth)};
  190         317  
194 190         218 while (1) {
195 190         285 local $_ = $o->{lines}[0];
196             # expand tabs in leading whitespace;
197 190 50       665 $o->next_line, next if /^(\s*$|\#)/; # skip comments and blank lines
198 190         483 while (s{^( *)(\t+)}
  0         0  
199             {' ' x (length($1) + length($2) * $tabwidth -
200 190 50       1097 length($1) % $tabwidth)}e){}
201 190         568 croak $o->M01_invalid_indent_width unless /^(( {$width})*)(\S.*)$/;
202 190         388 $o->{level} = length($1) / $width;
203 190         315 $o->{content} = $3;
204             last;
205             }
206             }
207              
208 13     13 0 28 sub indent {
209 13         31 my $o = shift;
210 13 50       97 my $package = caller;
211 13         28 $package = caller(1) if $package eq 'Inline::denter';
212 13         90 my $stream = '';
213 13         46 $o->{key} = '';
214 130         337 while (@_) {
215 130 50       866 local $_ = shift;
216             $stream .= $o->indent_name($_, shift), next
217 0         0 if (/^\*$package\::\w+$/);
218             $stream .= $o->indent_data($_);
219 13         144 }
220             return $stream;
221             }
222              
223 286     286 0 319 sub indent_data {
224 286         317 my $o = shift;
225 286 100       468 local $_ = shift;
226             return $o->indent_undef($_)
227 273 100       476 if not defined;
228             return $o->indent_value($_)
229 26 50 33     183 if (not ref);
      33        
230             return $o->indent_hash($_)
231 0 0 0     0 if (ref eq 'HASH' and not /=/ or /=HASH/);
      0        
232             return $o->indent_array($_)
233 0 0 0     0 if (ref eq 'ARRAY' and not /=/ or /=ARRAY/);
      0        
234             return $o->indent_scalar($_)
235 0 0       0 if (ref eq 'SCALAR' and not /=/ or /=SCALAR/);
236             return $o->indent_ref($_)
237 0         0 if (ref eq 'REF');
238             return "$_\n";
239             }
240              
241 247     247 0 342 sub indent_value {
242 247         247 my ($o, $data) = @_;
243 247 50 33     1888 my $stream;
    100 33        
      66        
244 0         0 if ($data =~ /\n/) {
245 0         0 my $marker = 'EOV';
246 0 0       0 $marker++ while $data =~ /^$marker$/m;
247 0         0 my $chomp = ($data =~ s/\n\Z//) ? '' : '-';
248 0 0       0 $stream = "<<$marker$chomp\n";
249 0         0 $stream .= $o->{key}, $o->{key} = '' if $o->{key};
250             $stream .= "$data\n$marker\n";
251             }
252             elsif ($data =~ /^[\s\%\@\$\\?\"]|\s$/ or
253             $data =~ /\Q$o->{comma}\E/ or
254             $data =~ /[\x00-\x1f]/ or
255 13         29 $data eq '') {
256 13 50       28 $stream = qq{"$data"\n};
257             $stream .= $o->{key}, $o->{key} = '' if $o->{key};
258             }
259 234         357 else {
260 234 50       334 $stream = "$data\n";
261             $stream .= $o->{key}, $o->{key} = '' if $o->{key};
262 247         538 }
263             return $stream;
264             }
265              
266 26     26 0 46 sub indent_hash {
267 26         78 my ($o, $data) = @_;
268 26 50       48 my $stream = $o->_print_ref($data, '%', 'HASH');
269 26         62 return $$stream if ref $stream;
270 26         150 my $indent = ++$o->{level} * $o->{width};
271 156         201 for my $key (sort keys %$data) {
272 156 50 33     963 my $key_out = $key;
    50          
273             if ($key =~ /\n/ or
274 0         0 $key =~ /\Q$o->{comma}\E/) {
275 0         0 my $marker = 'EOK';
276 0 0       0 $marker++ while $key =~ /^$marker$/m;
277 0         0 my $chomp = (($o->{key} = $key) =~ s/\n\Z//m) ? '' : '-';
278 0         0 $o->{key} .= "\n$marker\n";
279             $key_out = "<<$marker$chomp";
280             }
281 0         0 elsif ($data =~ /^[\s\%\@\$\\?\"]|\s$/) {
282             $key_out = qq{"$key"};
283 156         324 }
284 156         296 $stream .= ' ' x $indent . $key_out . $o->{comma};
285             $stream .= $o->indent_data($data->{$key});
286 26         48 }
287 26         66 $o->{level}--;
288             return $stream;
289             }
290              
291 0     0 0 0 sub indent_array {
292 0         0 my ($o, $data) = @_;
293 0 0       0 my $stream = $o->_print_ref($data, '@', 'ARRAY');
294 0         0 return $$stream if ref $stream;
295 0         0 my $indent = ++$o->{level} * $o->{width};
296 0         0 for my $datum (@$data) {
297 0         0 $stream .= ' ' x $indent;
298             $stream .= $o->indent_data($datum);
299 0         0 }
300 0         0 $o->{level}--;
301             return $stream;
302             }
303              
304 0     0 0 0 sub indent_scalar {
305 0         0 my ($o, $data) = @_;
306 0 0       0 my $stream = $o->_print_ref($data, q{$}, 'SCALAR');
307 0         0 return $$stream if ref $stream;
308 0         0 my $indent = ($o->{level} + 1) * $o->{width};
309 0         0 $stream .= ' ' x $indent;
310 0         0 $stream .= $o->indent_data($$data);
311             return $stream;
312             }
313              
314 0     0 0 0 sub indent_ref {
315 0         0 my ($o, $data) = @_;
316 0 0       0 my $stream = $o->_print_ref($data, '\\', 'SCALAR');
317 0         0 return $$stream if ref $stream;
318 0         0 chomp $stream;
319             return $stream . $o->indent_data($$data);
320             }
321              
322 13     13 0 27 sub indent_undef {
323 13         56 my ($o, $data) = @_;
324 13 50       38 my $stream = "?\n";
325 13         36 $stream .= $o->{key}, $o->{key} = '' if $o->{key};
326             return $stream;
327             }
328              
329 130     130 0 316 sub indent_name {
330 130         778 my ($o, $name, $value) = @_;
331 130         302 $name =~ s/^.*:://;
332 130         220 my $stream = $name . $o->{comma};
333 130         417 $stream .= $o->indent_data($value);
334             return $stream;
335             }
336              
337 26     26   67 sub _print_ref {
338 26 50       505 my ($o, $data, $symbol, $type) = @_;
339             $data =~ /^(([\w:]+)=)?$type\(0x([0-9a-f]+)\)$/
340 26         51 or croak "Invalid reference: $data\n";
341 26 50       65 my $stream = $symbol;
342 26         82 $stream .= $2 if defined $2;
343             $o->{xref}{$3}++;
344 26 50       61 croak "Inline::denter does not handle duplicate references"
345 26         35 if $o->{xref}{$3} > 1;
346 26 50       45 $stream .= "\n";
347 26         55 $stream .= $o->{key}, $o->{key} = '' if $o->{key};
348             return $stream;
349             }
350              
351             # Undent error messages
352 0     0 0   sub M01_invalid_indent_width {
353 0           my $o = shift;
354             "Invalid indent width detected at line $o->{line}\n";
355             }
356              
357 0     0 0   sub M02_no_key_end_marker {
358 0           my ($marker, $line) = @_;
359             "No terminating marker '$marker' found for key at line $line\n";
360             }
361              
362 0     0 0   sub M03_no_value_end_marker {
363 0           my ($marker, $line) = @_;
364             "No terminating marker '$marker' found for value at line $line\n";
365             }
366              
367 0     0 0   sub M04_mismatched_quotes {
368 0           my $o = shift;
369             "Mismatched double quotes for value at line $o->{line}\n";
370             }
371              
372 0     0 0   sub M05_invalid_key_value {
373 0           my $o = shift;
374             "Missing or invalid hash key/value pair at $o->{line}\n";
375             }
376              
377 0     0 0   sub M06_invalid_indent_level {
378 0           my $o = shift;
379             "Invalid indentation level at $o->{line}\n";
380             }
381              
382 0     0 0   sub M07_invalid_scalar_value {
383 0           my $o = shift;
384             "Invalid value for scalar ref context at $o->{line}\n";
385             }
386              
387             1;
388             __END__