line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Code::Explain; |
2
|
1
|
|
|
1
|
|
1210
|
use 5.008; |
|
1
|
|
|
|
|
4
|
|
|
1
|
|
|
|
|
32
|
|
3
|
1
|
|
|
1
|
|
6
|
use strict; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
30
|
|
4
|
1
|
|
|
1
|
|
20
|
use warnings; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
36
|
|
5
|
|
|
|
|
|
|
|
6
|
1
|
|
|
1
|
|
6
|
use Carp qw(croak); |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
886
|
|
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
our $VERSION = '0.02'; |
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
sub new { |
11
|
28
|
|
|
28
|
0
|
25955
|
my ($class, %args) = @_; |
12
|
28
|
|
|
|
|
72
|
my $self = bless {}, $class; |
13
|
|
|
|
|
|
|
|
14
|
28
|
100
|
|
|
|
286
|
$self->{code} = $args{code} |
15
|
|
|
|
|
|
|
or croak('Method ->new needs a "code" => $some_code pair'); |
16
|
|
|
|
|
|
|
|
17
|
27
|
|
|
|
|
76
|
return $self |
18
|
|
|
|
|
|
|
} |
19
|
|
|
|
|
|
|
|
20
|
27
|
|
|
27
|
0
|
55
|
sub code { return $_[0]->{code} }; |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
sub explain { |
23
|
34
|
|
|
34
|
0
|
167
|
my ($self, $code) = @_; |
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
# TODO we will maintain a database of exact matches |
26
|
34
|
|
|
|
|
149
|
my %exact = ( |
27
|
|
|
|
|
|
|
'$_' => 'Default variable', |
28
|
|
|
|
|
|
|
'@_' => 'Default array', |
29
|
|
|
|
|
|
|
'given' => 'keyword in perl 5.10', |
30
|
|
|
|
|
|
|
'say' => 'keyword in perl 5.10', |
31
|
|
|
|
|
|
|
'!!' => 'Creating boolean context by negating the value on the right hand side twice', |
32
|
|
|
|
|
|
|
); |
33
|
|
|
|
|
|
|
|
34
|
34
|
100
|
|
|
|
94
|
$code = $self->code if not defined $code; |
35
|
34
|
100
|
|
|
|
73
|
if ($exact{$code}) { |
36
|
8
|
|
|
|
|
70
|
return $exact{$code}; |
37
|
|
|
|
|
|
|
} |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
# parentheses after the name of a subroutine |
40
|
26
|
100
|
|
|
|
68
|
if ($code =~ /^(\w+)\(\)$/) { |
41
|
2
|
|
|
|
|
13
|
my $sub = $1; |
42
|
2
|
100
|
|
|
|
7
|
if ($exact{$sub}) { |
43
|
1
|
|
|
|
|
7
|
return $exact{$sub}; |
44
|
|
|
|
|
|
|
} |
45
|
|
|
|
|
|
|
} |
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
# '' . |
48
|
25
|
100
|
|
|
|
51
|
if ($code =~ m{^'' \s* \.$}x) { |
49
|
1
|
|
|
|
|
6
|
return 'Forcing string context'; |
50
|
|
|
|
|
|
|
} |
51
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
# 0 + |
53
|
24
|
100
|
|
|
|
52
|
if ($code =~ m{^0 \s* \+$}x) { |
54
|
1
|
|
|
|
|
7
|
return 'Forcing numeric context'; |
55
|
|
|
|
|
|
|
} |
56
|
|
|
|
|
|
|
|
57
|
23
|
|
|
|
|
71
|
my $NUMBER = qr{\d+(?:\.\d+)?}; |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
# 2 + 3 |
60
|
23
|
100
|
|
|
|
155
|
if ($code =~ m{^$NUMBER \s* [/*+-] \s* $NUMBER$}x) { |
61
|
3
|
|
|
|
|
19
|
return 'Numerical operation'; |
62
|
|
|
|
|
|
|
} |
63
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
# 2 |
65
|
|
|
|
|
|
|
# 2.34 |
66
|
20
|
100
|
|
|
|
205
|
if ($code =~ /^$NUMBER$/) { |
67
|
3
|
|
|
|
|
24
|
return 'A number'; |
68
|
|
|
|
|
|
|
} |
69
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
# 23_145 |
71
|
17
|
100
|
|
|
|
38
|
if ($code =~ /^\d+(_\d\d\d)+$/) { |
72
|
1
|
|
|
|
|
39
|
return 'This is the same as the number ' . eval($code) . ' just in a more readable format'; |
73
|
|
|
|
|
|
|
} |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
# $_[2], $_[$var], $name[42] |
76
|
16
|
100
|
|
|
|
38
|
if ($code =~ /\$(\w+)\[(.*?)\]/) { |
77
|
2
|
100
|
|
|
|
7
|
if ($1 eq '_') { |
78
|
1
|
|
|
|
|
7
|
return "This is element $2 of the default array \@_"; |
79
|
|
|
|
|
|
|
} else { |
80
|
1
|
|
|
|
|
7
|
return "This is element $2 of the array \@$1"; |
81
|
|
|
|
|
|
|
} |
82
|
|
|
|
|
|
|
} |
83
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
# $phone{$name} |
85
|
14
|
100
|
|
|
|
27
|
if ($code =~ m{^\$(\w+) \{ \$(\w+) \} }x) { |
86
|
1
|
|
|
|
|
4
|
my ($hash_name, $key_name) = ($1, $2); |
87
|
1
|
|
|
|
|
7
|
return "The element \$$key_name of the hash \%$hash_name"; |
88
|
|
|
|
|
|
|
} |
89
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
# $$x |
91
|
13
|
100
|
|
|
|
26
|
if ($code =~/^\$\$(\w+)$/) { |
92
|
1
|
|
|
|
|
9
|
return "\$$1 is a reference to a scalar value. This expression dereferences it. See perlref"; |
93
|
|
|
|
|
|
|
} |
94
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
# $x ||= $y |
96
|
12
|
100
|
|
|
|
36
|
if ($code =~ m{^\$(\w+) \s* \|\|= \s* \$(\w+)$}x) { |
97
|
1
|
|
|
|
|
3
|
my $lhs = $1; |
98
|
1
|
|
|
|
|
9
|
return "Assigning default value to \$$lhs. It has the disadvantage of not allowing \$$lhs=0. Startin from 5.10 you can use //= instead of ||="; |
99
|
|
|
|
|
|
|
} |
100
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
# $self->editor |
102
|
11
|
100
|
|
|
|
29
|
if ($code =~ m{^\$(\w+) -> (\w+)}x) { |
103
|
1
|
|
|
|
|
3
|
my ($obj_name, $method) = ($1, $2); |
104
|
1
|
|
|
|
|
8
|
return "Calling method '$method' on an object in the variable called \$$obj_name", |
105
|
|
|
|
|
|
|
} |
106
|
|
|
|
|
|
|
|
107
|
10
|
|
|
|
|
59
|
return "Not found"; |
108
|
|
|
|
|
|
|
} |
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
sub ppi_dump { |
111
|
2
|
|
|
2
|
0
|
8
|
my ($self) = @_; |
112
|
|
|
|
|
|
|
|
113
|
2
|
|
|
|
|
884
|
require PPI::Dumper; |
114
|
2
|
|
|
|
|
5023
|
my $dumper = PPI::Dumper->new( $self->ppi_document ); |
115
|
2
|
|
|
|
|
86
|
return $dumper->list; |
116
|
|
|
|
|
|
|
} |
117
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
sub ppi_explain { |
119
|
2
|
|
|
2
|
0
|
12382
|
my ($self) = @_; |
120
|
|
|
|
|
|
|
|
121
|
2
|
|
|
|
|
8
|
my $document = $self->ppi_document; |
122
|
|
|
|
|
|
|
|
123
|
2
|
|
|
|
|
6
|
my @result; |
124
|
2
|
|
|
|
|
14
|
foreach my $token ( $document->tokens ) { |
125
|
9
|
|
|
|
|
197
|
push @result, { |
126
|
|
|
|
|
|
|
code => $token->content, |
127
|
|
|
|
|
|
|
text => $self->explain($token->content), |
128
|
|
|
|
|
|
|
}; |
129
|
|
|
|
|
|
|
} |
130
|
2
|
|
|
|
|
10
|
return @result; |
131
|
|
|
|
|
|
|
} |
132
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
sub ppi_document { |
134
|
4
|
|
|
4
|
0
|
9
|
my ($self) = @_; |
135
|
|
|
|
|
|
|
|
136
|
4
|
100
|
|
|
|
23
|
if (not $self->{ppi_document}) { |
137
|
2
|
|
|
|
|
911
|
require PPI::Document; |
138
|
2
|
|
|
|
|
191507
|
my $code = $self->code; |
139
|
2
|
|
|
|
|
13
|
$self->{ppi_document} = PPI::Document->new(\$code); |
140
|
|
|
|
|
|
|
# $self->{ppi_document}->index_locations; |
141
|
|
|
|
|
|
|
} |
142
|
|
|
|
|
|
|
|
143
|
4
|
|
|
|
|
3943
|
return $self->{ppi_document}; |
144
|
|
|
|
|
|
|
} |
145
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
=head1 NAME |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
Code::Explain - Try to explain what $ @ % & * and the rest mean |
150
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
=head1 SYNOPSIS |
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
my $ce = Code::Explain->new; |
155
|
|
|
|
|
|
|
$str = '$x ||= $y'; |
156
|
|
|
|
|
|
|
print $ce->explain($str), "\n"; |
157
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
or |
159
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
@ppi_dump = $ce->ppi_dump($str); |
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
=head1 COMMAND LINE |
163
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
The module comes with a command line tool called |
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
explain-code |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
You give a perl expression to it and it will give an explanation |
169
|
|
|
|
|
|
|
what that might be. |
170
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
=head1 COMMAND LINE OPTIONS |
173
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
One of the following: |
175
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
--explain Try to exaplain our way |
177
|
|
|
|
|
|
|
--ppidump Run PPI on the code and print the dump |
178
|
|
|
|
|
|
|
--ppiexplain Run PPI on the code and try to explain the individual tokens |
179
|
|
|
|
|
|
|
--all All of the above |
180
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
--help Prints the list of command line options |
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
=head1 DESCRIPTION |
185
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
This is pre-alpha version (whatever that means) of the code |
187
|
|
|
|
|
|
|
explain tool. It should be able to understand various perl |
188
|
|
|
|
|
|
|
constructs such as. |
189
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
$x ||= $y; |
192
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
@data = map { ... } sort { ... } grep { ... } @data; |
194
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
give a short explanation and reasonable pointers to the documentation. |
196
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
See the t/cases.txt file more cases that are already handled. |
198
|
|
|
|
|
|
|
Add further cases to t/todo.txt, preferably with some explanation. |
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
=head1 AUTHOR |
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
Gabor Szabo L |
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
=head1 COPYRIGHT and LICENSE |
205
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
This software is copyright (c) 2011 by Gabor Szabo. |
207
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
This is free software; you can redistribute it and/or modify it under |
209
|
|
|
|
|
|
|
the same terms as the Perl 5 programming language system itself. |
210
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
=cut |
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
1; |
214
|
|
|
|
|
|
|
|