File Coverage

blib/lib/Code/Explain.pm
Criterion Covered Total %
statement 69 69 100.0
branch 34 34 100.0
condition n/a
subroutine 10 10 100.0
pod 0 6 0.0
total 113 119 94.9


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