File Coverage

blib/lib/Acme/PPIx/MetaSyntactic.pm
Criterion Covered Total %
statement 86 99 86.8
branch 25 42 59.5
condition 14 33 42.4
subroutine 20 23 86.9
pod 0 1 0.0
total 145 198 73.2


line stmt bran cond sub pod time code
1 2     2   54719 use 5.008;
  2         13  
2 2     2   10 use strict;
  2         3  
  2         34  
3 2     2   8 use warnings;
  2         2  
  2         70  
4              
5             package Acme::PPIx::MetaSyntactic;
6              
7 2     2   865 use Moo 2;
  2         18262  
  2         10  
8 2     2   2284 no warnings qw(uninitialized once numeric);
  2         3  
  2         78  
9              
10             BEGIN {
11 2     2   6 $Acme::PPIx::MetaSyntactic::AUTHORITY = 'cpan:TOBYINK';
12 2         28 $Acme::PPIx::MetaSyntactic::VERSION = '0.004';
13             }
14              
15 2     2   795 use Acme::MetaSyntactic;
  2         10126  
  2         6  
16 2     2   801 use PPIx::Utils qw( is_perl_builtin is_function_call );
  2         197044  
  2         138  
17 2     2   18 use PPI;
  2         3  
  2         58  
18              
19 2     2   1026 use Types::Standard -types;
  2         152604  
  2         28  
20              
21             my $Document = (InstanceOf["PPI::Document"])->plus_coercions(
22             ScalarRef[Str], q { "PPI::Document"->new($_) },
23             Str, q { "PPI::Document"->new($_) },
24             FileHandle, q { do { local $/; my $c = <$_>; "PPI::Document"->new(\$c) } },
25             ArrayRef[Str], q { do { my $c = join "\n", map { chomp(my $l = $_); $l } @$_; "PPI::Document"->new(\$c) } },
26             );
27              
28             my $MetaSyntactic = (InstanceOf["Acme::MetaSyntactic"])->plus_coercions(
29             Str, q { "Acme::MetaSyntactic"->new($_) },
30             );
31              
32             my $TruthTable = (Map[Str, Bool])->plus_coercions(
33             ArrayRef[Str], q { +{ map +($_, 1), @$_ } },
34             );
35              
36             has document => (
37             is => "ro",
38             isa => $Document,
39             coerce => 1,
40             required => 1,
41             );
42              
43             has theme => (
44             is => "lazy",
45             isa => $MetaSyntactic,
46             coerce => 1,
47             );
48              
49             has local_subs => (
50             is => "lazy",
51             isa => $TruthTable,
52             coerce => 1,
53             );
54              
55             has names => (
56             is => "lazy",
57             isa => Map[Str, Str],
58             );
59              
60             has already_used => (
61             is => "lazy",
62             isa => $TruthTable,
63             coerce => 1,
64             init_arg => undef,
65             );
66              
67             sub _get_name
68             {
69 4     4   142 my $self = shift;
70 4         86 my $name = $self->theme->name;
71 4         9529 my $i = undef;
72 4         71 my $used = $self->already_used;
73 4         82 $i++ while $used->{"$name$i"};
74 4         9 $used->{"$name$i"} = 1;
75 4         12 return "$name$i";
76             }
77              
78             sub _build_theme
79             {
80 1     1   21 my $self = shift;
81 1         16 "haddock";
82             }
83              
84             sub _build_local_subs
85             {
86 1     1   21 my $self = shift;
87 1         3 my %r;
88            
89 1 50       2 for my $word (@{ $self->document->find("PPI::Token::Word") || [] })
  1         10  
90             {
91 8 50       2260 $r{$word} = 1 if $word->sprevious_sibling eq "sub";
92 8 100 66     159 $r{$word} = 1 if $word->sprevious_sibling eq "constant" && $word->sprevious_sibling->sprevious_sibling eq "use";
93             }
94            
95 1         32 return \%r;
96             }
97              
98             sub _build_names
99             {
100 1     1   12 my $self = shift;
101 1         13 return +{};
102             }
103              
104             sub _build_already_used
105             {
106 1     1   33 my $self = shift;
107             return +{
108 1         3 map +($_, 1), values %{ $self->names },
  1         17  
109             };
110             }
111              
112             sub BUILD
113             {
114 1     1 0 14946 my $self = shift;
115 1         5 $self->_relabel_subs;
116 1         8 $self->_relabel_variables;
117 1         21 return;
118             }
119              
120             sub _relabel_subs
121             {
122 1     1   2 my $self = shift;
123 1         18 my $ls = $self->local_subs;
124 1         53 my $n = $self->names;
125            
126 1 50       26 for my $word (@{ $self->document->find("PPI::Token::Word")||[] })
  1         7  
127             {
128 8 100       2621 next if is_perl_builtin($word);
129            
130             # Function to preserve original case of variable.
131             my $case =
132 1     1   15 ($word eq uc $word) ? sub { uc $_[0] } :
133 4 50   0   58 ($word eq lc $word) ? sub { lc $_[0] } : sub { $_[0] };
  0 100       0  
  0         0  
134            
135 4 50 33     97 if ($word->sprevious_sibling eq "sub" and $ls->{$word})
    100 66        
    100 66        
      66        
136             {
137 0   0     0 $word->set_content($n->{$word} ||= $case->($self->_get_name));
138             }
139             elsif ($word->sprevious_sibling eq "constant" && $word->sprevious_sibling->sprevious_sibling eq "use" and $ls->{$word})
140             {
141 1   33     106 $word->set_content($n->{$word} ||= $case->($self->_get_name));
142             }
143             elsif (is_function_call($word) and $ls->{$word})
144             {
145 1   33     211 $word->set_content($n->{$word} ||= $case->($self->_get_name));
146             }
147             }
148            
149 1         10 return;
150             }
151              
152             sub _relabel_variables
153             {
154 1     1   2 my $self = shift;
155 1         21 my $ls = $self->local_subs;
156 1         20 my $n = $self->names;
157            
158             my $VariableFinder = sub {
159 90 100   90   987 $_[1]->isa("PPI::Token::Symbol") or $_[1]->isa("PPI::Token::ArrayIndex");
160 1         8 };
161            
162 1 50       3 for my $word (@{ $self->document->find($VariableFinder) || [] })
  1         8  
163             {
164 7 50       423 next if $word->isa("PPI::Token::Magic");
165            
166             # Function to preserve original case of variable.
167             my $case =
168 0     0   0 ($word eq uc $word) ? sub { uc $_[0] } :
169 7 50   0   15 ($word eq lc $word) ? sub { lc $_[0] } : sub { $_[0] };
  3 50       11  
  0         0  
170            
171             # Separate sigil from the rest of the variable name.
172 7         174 (my $sigil = "$word") =~ s/(\w.*)$//g;
173 7         51 my $rest = $1;
174            
175 7 50       18 if ($word->isa("PPI::Token::Symbol"))
    0          
176             {
177 7   66     16 $n->{$word->symbol} ||= $case->($self->_get_name);
178 7         297 $word->set_content($sigil . $n->{$word->symbol});
179             }
180             elsif ($word->isa("PPI::Token::ArrayIndex")) # like $#foo
181             {
182 0   0     0 $n->{"\@$rest"} ||= $case->($self->_get_name);
183 0         0 $word->set_content($sigil . $n->{"\@$rest"});
184             }
185             }
186            
187 1 50       100 for my $qq (@{ $self->document->find("PPI::Token::Quote") || [] })
  1         5  
188             {
189             # A string that "co-incidentally" happens to have the name as a locally
190             # defined sub. This might be a __PACKAGE__->can("foo"), so change it!
191             #
192 1 50 33     1912 if ($ls->{$qq->string})
    50          
193             {
194 0         0 my $txt = "$qq";
195 0         0 $txt =~ s/${\quotemeta($qq->string)}/$n->{$qq->string}/eg;
  0         0  
  0         0  
196 0         0 $qq->set_content($txt);
197             }
198            
199             # An interpolated string. We'll do our best to find any variables
200             # within it and rename them, but PPI doesn't really look inside
201             # interpolated strings (yet?).
202             #
203             elsif ($qq->isa("PPI::Token::Quote::Double") or $qq->isa("PPI::Token::Quote::Interpolate"))
204             {
205 1         14 my $txt = "$qq";
206 1 0       6 $txt =~ s/([\$\@]\w+)/$n->{$1}?substr($1,0,1).$n->{$1}:$1/eg;
  0         0  
207 1         9 $qq->set_content($txt);
208             }
209             }
210             }
211              
212             1;
213              
214             __END__