File Coverage

blib/lib/Debug/Filter/PrintExpr.pm
Criterion Covered Total %
statement 79 81 97.5
branch 20 22 90.9
condition 6 7 85.7
subroutine 23 23 100.0
pod n/a
total 128 133 96.2


line stmt bran cond sub pod time code
1             package Debug::Filter::PrintExpr;
2              
3 4     4   107283 use strict;
  4         40  
  4         117  
4 4     4   21 use warnings;
  4         10  
  4         141  
5            
6 4     4   94 use 5.010;
  4         15  
7 4     4   1979 use Exporter::Tiny;
  4         13385  
  4         27  
8 4     4   2944 use Filter::Simple;
  4         98263  
  4         26  
9 4     4   250 use Scalar::Util qw(isdual blessed);
  4         9  
  4         286  
10 4     4   27 use List::Util 'pairs';
  4         10  
  4         349  
11 4     4   2555 use Data::Dumper;
  4         27828  
  4         538  
12              
13             our
14             $VERSION = '0.18';
15              
16             our @EXPORT_OK = qw(isnumeric isstring);
17             our @ISA = qw(Exporter::Tiny);
18             our %EXPORT_TAGS = (
19             debug => [],
20             nofilter => [],
21             all => [qw(isnumeric isstring)],
22             );
23              
24             require XSLoader;
25             XSLoader::load('Debug::Filter::PrintExpr', $VERSION);
26              
27             # Make Exporter::Tiny::import ours, so this will be called by Filter::Simple
28 4     4   2364 BEGIN {*import = \&Exporter::Tiny::import;}
29              
30             # variable is exposed and my be overwritten by caller
31             our $handle = *STDERR;
32              
33             # generate a prefix containing line number or custom label
34             # consume first three args, return number of printed chars
35             # if no expression is present
36             sub _genprefix {
37 29     29   85 my ($label, $line, $expr, $pos) = splice @_, 0, 3;
38 29         65 local ($,, $\);
39 29   66     241 printf $handle "%s%n", $label || "L$line:", $pos;
40 29 100       127 print $handle $expr ? " $expr = " : " ";
41 29 100       110 return $expr ? undef : $pos + 1;
42             }
43              
44             # create representation of single value
45             sub _singlevalue {
46 25     25   60 my ($val, $str, $num) = shift;
47 25         59 my $isdual = isdual($val);
48 25         55 my $isnumeric = isnumeric($val);
49 25 100       61 $str = "$val" if defined $val;
50 25 100       58 $num = $val + 0 if $isnumeric;
51 25 100       101 if (!defined $val) {
    50          
    50          
    100          
    100          
52 1         5 return 'undef';
53             } elsif (my $class = blessed($val)) {
54 0         0 return "blessed($class)";
55             } elsif (ref($val)) {
56 0         0 return $val;
57             } elsif ($isdual) {
58 3         14 return "dualvar($num, '$str')";
59             } elsif ($isnumeric) {
60 8         37 return $num;
61             } else {
62 13         56 return "'$str'";
63             }
64             }
65              
66             # print out an expression in scalar context
67             sub _valuescalar {
68 21     21   42 local ($,, $\);
69 21         54 print $handle _singlevalue($_[0]);
70             }
71              
72             # print out an expression in list context
73             sub _valuearray {
74 2     2   6 local ($,, $\);
75             print $handle '(', join(', ',
76 2         9 map({_singlevalue($_)} @_)), ");\n";
  2         6  
77             }
78              
79             # print out an expression as key-value pairs
80             sub _valuehash {
81 2     2   7 local ($,, $\);
82             print $handle '(', join(", ",
83             map(
84 2         21 {"'$_->[0]' => " . _singlevalue($_->[1])}
  2         11  
85             pairs(@_))), ");\n";
86             }
87              
88             # process a scalar debug statement
89             sub _print_scalar {
90 23     23   16470 local ($,, $\);
91 23 100       53 unless (&_genprefix) {
92 21         59 _valuescalar($_[0]);
93 21         58 print $handle ';';
94             }
95 23         82 print $handle "\n";
96             }
97              
98             # process a string scalar debug statement
99             sub _print_str {
100 2     2   859 my $val = $_[3];
101 2         8 splice @_, 3, 1, "$val";
102 2         8 goto &_print_scalar;
103             }
104              
105             # process a numeric scalar debug statement
106             sub _print_num {
107 4     4   36 no warnings qw(numeric);
  4         10  
  4         2101  
108 2     2   789 my $val = $_[3];
109 2         7 splice @_, 3, 1, $val + 0;
110 2         7 goto &_print_scalar;
111             }
112              
113             # process an array debug statement
114             sub _print_array {
115 2     2   833 &_genprefix;
116 2         8 goto &_valuearray;
117             }
118              
119             # process a hash debug statement
120             sub _print_hash {
121 2     2   843 &_genprefix;
122 2         7 goto &_valuehash;
123             }
124              
125             # process a reference debug statement
126             sub _print_ref {
127 2     2   3768 my $expr = splice @_, 2, 1, undef;
128 2         6 my $skip = &_genprefix;
129 2         6 local ($,, $\);
130 2         8 print $handle "dump($expr);\n";
131 2         22 print $handle
132             Data::Dumper->new([@_], [map("_[$_]", (0 .. $#_))])
133             ->Pad(' ' x $skip)->Dump;
134             }
135              
136             # type classifications: print function suffix + is scalar
137             my %type_defs = (
138             '$' => ['scalar', 1],
139             '"' => ['str', 1],
140             '#' => ['num', 1],
141             '@' => ['array', 0],
142             '%' => ['hash', 0],
143             '\\' => ['ref', 0],
144             );
145              
146             # process a debug statement, runs in filter context
147             sub _gen_print {
148 29   100 29   297 my ($type, $label, $expr) = map $_ // '', @_;
149 29   100     74 my $val = $_[2] // '()';
150 29         40 my ($ptype, $scalar) = @{$type_defs{$type}};
  29         58  
151 29         60 my $print = __PACKAGE__ . "::_print_$ptype";
152 29 100       261 return qq[{$print("$label", __LINE__, q{$expr}, ] .
153             ($scalar ? qq[scalar($val)] : qq[$val]) .
154             q[)}];
155             }
156              
157             # source code processing happens here
158             FILTER {
159             my ($self, @args) = @_;
160             my ($nofilter, $debug);
161             if (ref($_[1]) eq 'HASH') {
162             my $global = $_[1];
163             $debug = $global->{debug};
164             $nofilter = $global->{nofilter};
165             }
166             $debug ||= grep /^-debug$/, @args;
167             $nofilter ||= grep /^-nofilter$/, @args;
168             s/
169             ^\h*+\#
170             (?[%@\$\\#"])
171             \{\h*+
172             (?
173             \h*+
174             (?\V*[^\s])?\h*
175             \}\h*+\r?$
176 4     4   2037 / _gen_print($+{type}, $+{label}, $+{expr}) /gmex
  4         1490  
  4         832  
177             unless $nofilter;
178             print STDERR if $debug;
179             };
180              
181             1;
182              
183             __END__