File Coverage

blib/lib/Filter/Cleanup.pm
Criterion Covered Total %
statement 40 64 62.5
branch 4 10 40.0
condition 3 7 42.8
subroutine 10 10 100.0
pod 1 1 100.0
total 58 92 63.0


line stmt bran cond sub pod time code
1             package Filter::Cleanup;
2              
3             our $VERSION = '0.02';
4              
5 1     1   23400 use Carp;
  1         2  
  1         78  
6 1     1   1236 use Filter::Util::Call;
  1         1255  
  1         77  
7 1     1   998 use PPI;
  1         185397  
  1         44  
8 1     1   12 use PPI::Document;
  1         2  
  1         31  
9 1     1   7 use PPI::Document::Fragment;
  1         3  
  1         33  
10              
11 1     1   6 use constant READ_BYTES => 999_999;
  1         2  
  1         729  
12              
13             sub import {
14 1     1   12 my ($class, %args) = @_;
15 1   50     10 my $debug = $args{debug} || 0;
16 1   50     6 my $pad = $args{pad} || 0;
17            
18 1         4 my $self = bless {}, $class;
19 1         7 $self->{dbg} = $debug;
20 1         3 $self->{pad} = $pad;
21              
22 1         5 filter_add($self);
23 1         31 return $self;
24             }
25              
26             sub filter {
27 2     2 1 422 my $self = shift;
28 2         45 my $status = filter_read(READ_BYTES);
29 2         5 my $source = $_;
30              
31 2         6 $source = _transform($source);
32              
33 2 50 33     281 if ($self->{dbg} && $source ne $_) {
34 0         0 my @lines = split /\n/, $_;
35 0 0       0 if (@lines) {
36 0         0 warn sprintf("\n=(%s) expansion\n", __PACKAGE__);
37              
38 0         0 for (my $i = $self->{pad}; $i <= ($self->{pad} + $#lines); ++$i) {
39 0         0 warn sprintf('%3d. %s', $i + 1, $lines[$i - $self->{pad}]), "\n";
40             }
41              
42 0         0 warn "=cut\n\n";
43             }
44             }
45            
46 2         27 $_ = $source;
47 2         2023 return $status;
48             }
49              
50             sub _transform {
51 2     2   4 my $source = shift;
52 2         14 my $pdom = PPI::Document->new(\$source);
53              
54             my $wanted = sub {
55 12     12   194 my ($node, $element) = @_;
56 12 100       83 $element->isa('PPI::Token::Word') && $element->content eq 'cleanup';
57 2         3233 };
58              
59 2 50       13 if (my $cleanup = $pdom->find_first($wanted)) {
60             # Get entire statement
61 0         0 my $statement = $cleanup->statement;
62              
63             # Get code block
64 0         0 my $block = $cleanup->snext_sibling;
65              
66             # Remove from tree
67 0         0 $cleanup->remove(), $block->remove();
68              
69             # Collect rest of statement's lexical scope
70 0         0 my @sibs;
71 0         0 my $node = $statement;
72 0         0 while (ref $node) {
73 0         0 $node = $node->next_sibling;
74 0 0       0 push @sibs, $node if $node;
75             }
76              
77             # Remove rest of the scope from the tree
78 0         0 foreach my $node (@sibs) {
79 0         0 $node->remove();
80             }
81              
82             # Generate code
83 0         0 my $template = '{use Symbol;my($g,$e)=(gensym)x2;$g=eval{%s};$e=$@;%sif($e){use Carp;croak $e}else{$g}}';
84 0         0 my $code = sprintf($template, join('', map {$_->content} @sibs), $block->content);
  0         0  
85              
86             # Replace $statement with new code
87 0         0 my $fragment = PPI::Document::Fragment->new(\$code);
88              
89 0         0 foreach my $child ($fragment->children) {
90 0         0 $statement->insert_before($child);
91             }
92              
93 0         0 $statement->remove;
94 0         0 return _transform($pdom->content);
95             }
96              
97 2         47 return $pdom->content;
98             }
99              
100             1;
101              
102             __END__