]> git.vpit.fr Git - perl/modules/B-RecDeparse.git/blob - lib/B/RecDeparse.pm
a9ace562920d0a516f32958bcc93b4f41f35e9a6
[perl/modules/B-RecDeparse.git] / lib / B / RecDeparse.pm
1 package B::RecDeparse;
2
3 use 5.008;
4
5 use strict;
6 use warnings;
7
8 use B ();
9
10 use Config;
11
12 use base qw<B::Deparse>;
13
14 =head1 NAME
15
16 B::RecDeparse - Deparse recursively into subroutines.
17
18 =head1 VERSION
19
20 Version 0.04
21
22 =cut
23
24 our $VERSION = '0.04';
25
26 =head1 SYNOPSIS
27
28     perl -MO=RecDeparse,deparse,[@B__Deparse_opts],level,-1 [ -e '...' | bleh.pl ]
29
30     # Or as a module :
31     use B::RecDeparse;
32
33     my $brd = B::RecDeparse->new(deparse => [ @b__deparse_opts ], level => $level);
34     my $code = $brd->coderef2text(sub { ... });
35
36 =head1 DESCRIPTION
37
38 This module extends L<B::Deparse> by making it recursively replace subroutine calls encountered when deparsing.
39
40 Please refer to L<B::Deparse> documentation for what to do and how to do it. Besides the constructor syntax, everything should work the same for the two modules.
41
42 =head1 METHODS
43
44 =head2 C<< new < deparse => [ @B__Deparse_opts ], level => $level > >>
45
46 The L<B::RecDeparse> object constructor. You can specify the underlying L<B::Deparse> constructor arguments by passing a string or an array reference as the value of the C<deparse> key. The C<level> option expects an integer that specifies how many levels of recursions are allowed : C<-1> means infinite while C<0> means none and match L<B::Deparse> behaviour.
47
48 =cut
49
50 use constant {
51  # p31268 made pp_entersub call single_delim
52  FOOL_SINGLE_DELIM =>
53      ($^V ge v5.9.5)
54   || ($^V lt v5.9.0 and $^V ge v5.8.9)
55   || ($Config{perl_patchlevel} && $Config{perl_patchlevel} >= 31268)
56 };
57
58 sub _parse_args {
59  if (@_ % 2) {
60   require Carp;
61   Carp::croak('Optional arguments must be passed as key/value pairs');
62  }
63  my %args = @_;
64
65  my $deparse = $args{deparse};
66  if (defined $deparse) {
67   if (!ref $deparse) {
68    $deparse = [ $deparse ];
69   } elsif (ref $deparse ne 'ARRAY') {
70    $deparse = [ ];
71   }
72  } else {
73   $deparse = [ ];
74  }
75
76  my $level = $args{level};
77  $level    = -1  unless defined $level;
78  $level    = int $level;
79
80  return $deparse, $level;
81 }
82
83 sub new {
84  my $class = shift;
85  $class = ref($class) || $class || __PACKAGE__;
86
87  my ($deparse, $level) = _parse_args(@_);
88
89  my $self = bless $class->SUPER::new(@$deparse), $class;
90
91  $self->{brd_level} = $level;
92
93  return $self;
94 }
95
96 sub _recurse {
97  return $_[0]->{brd_level} < 0 || $_[0]->{brd_cur} < $_[0]->{brd_level}
98 }
99
100 sub compile {
101  my @args = @_;
102
103  my $bd = B::Deparse->new();
104  my ($deparse, $level) = _parse_args(@args);
105
106  my $compiler = $bd->coderef2text(B::Deparse::compile(@$deparse));
107  $compiler =~ s/
108   ['"]? B::Deparse ['"]? \s* -> \s* (new) \s* \( ([^\)]*) \)
109  /B::RecDeparse->$1(deparse => [ $2 ], level => $level)/gx;
110  $compiler = eval 'sub ' . $compiler;
111  die if $@;
112
113  return $compiler;
114 }
115
116 sub init {
117  my $self = shift;
118
119  $self->{brd_cur}  = 0;
120  $self->{brd_sub}  = 0;
121  $self->{brd_seen} = { };
122
123  $self->SUPER::init(@_);
124 }
125
126 my $key = $; . __PACKAGE__ . $;;
127
128 if (FOOL_SINGLE_DELIM) {
129  my $oldsd = *B::Deparse::single_delim{CODE};
130
131  no warnings 'redefine';
132  *B::Deparse::single_delim = sub {
133   my $body = $_[2];
134
135   if ((caller 1)[0] eq __PACKAGE__ and $body =~ s/^$key//) {
136    return $body;
137   } else {
138    $oldsd->(@_);
139   }
140  }
141 }
142
143 sub deparse_sub {
144  my $self = shift;
145  my $cv   = $_[0];
146
147  my $name;
148  unless ($cv->CvFLAGS & B::CVf_ANON()) {
149   $name = $cv->GV->SAFENAME;
150  }
151
152  local $self->{brd_seen}->{$name} = 1 if defined $name;
153  return $self->SUPER::deparse_sub(@_);
154 }
155
156 sub pp_entersub {
157  my $self = shift;
158
159  my $body = do {
160   local $self->{brd_sub} = 1;
161   $self->SUPER::pp_entersub(@_);
162  };
163
164  $body =~ s/^&\s*(\w)/$1/ if $self->_recurse;
165
166  return $body;
167 }
168
169 sub pp_refgen {
170  my $self = shift;
171
172  return do {
173   local $self->{brd_sub} = 0;
174   $self->SUPER::pp_refgen(@_);
175  }
176 }
177
178 sub pp_gv {
179  my $self = shift;
180
181  my $gv   = $self->gv_or_padgv($_[0]);
182  my $name = $gv->NAME;
183  my $seen = $self->{brd_seen};
184
185  my $body;
186  if ($self->{brd_sub} <= 0 || !$self->_recurse || $seen->{$name}) {
187   $body = $self->SUPER::pp_gv(@_);
188  } else {
189   $body = do {
190    local @{$self}{qw<brd_sub brd_cur>} = (0, $self->{brd_cur} + 1);
191    local $seen->{$name} = 1;
192    'sub ' . $self->indent($self->deparse_sub($gv->CV));
193   };
194
195   if (FOOL_SINGLE_DELIM) {
196    $body = $key . $body;
197   } else {
198    $body .= '->';
199   }
200  }
201
202  return $body;
203 }
204
205 =head2 C<compile>
206
207 =head2 C<init>
208
209 =head2 C<deparse_sub>
210
211 =head2 C<pp_entersub>
212
213 =head2 C<pp_refgen>
214
215 =head2 C<pp_gv>
216
217 Functions and methods from L<B::Deparse> reimplemented by this module. Never call them directly.
218
219 Otherwise, L<B::RecDeparse> inherits all methods from L<B::Deparse>.
220
221 =head1 EXPORT
222
223 An object-oriented module shouldn't export any function, and so does this one.
224
225 =head1 DEPENDENCIES
226
227 L<Carp> (standard since perl 5), L<Config> (since perl 5.00307) and L<B::Deparse> (since perl 5.005).
228
229 =head1 AUTHOR
230
231 Vincent Pit, C<< <perl at profvince.com> >>, L<http://www.profvince.com>.
232
233 You can contact me by mail or on C<irc.perl.org> (vincent).
234
235 =head1 BUGS
236
237 Please report any bugs or feature requests to C<bug-b-recdeparse at rt.cpan.org>, or through the web interface at L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=B-RecDeparse>.  I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.
238
239 =head1 SUPPORT
240
241 You can find documentation for this module with the perldoc command.
242
243     perldoc B::RecDeparse
244
245 Tests code coverage report is available at L<http://www.profvince.com/perl/cover/B-RecDeparse>.
246
247 =head1 COPYRIGHT & LICENSE
248
249 Copyright 2008,2009,2010,2011 Vincent Pit, all rights reserved.
250
251 This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
252
253 =cut
254
255 1; # End of B::RecDeparse