| File: | blib/lib/B/RecDeparse.pm |
| Coverage: | 92.3% |
| line | stmt | bran | cond | sub | pod | time | code |
|---|---|---|---|---|---|---|---|
| 1 | package B::RecDeparse; | ||||||
| 2 | |||||||
| 3 | 11 11 11 | 313 31 16 | use 5.008; | ||||
| 4 | |||||||
| 5 | 11 11 11 | 40 15 53 | use strict; | ||||
| 6 | 11 11 11 | 45 16 33 | use warnings; | ||||
| 7 | |||||||
| 8 | 11 11 11 | 43 19 68 | use Carp qw/croak/; | ||||
| 9 | 11 11 11 | 43 17 47 | use Config; | ||||
| 10 | |||||||
| 11 | 11 11 11 | 45 13 58 | use base qw/B::Deparse/; | ||||
| 12 | |||||||
| 13 - 21 | =head1 NAME B::RecDeparse - Deparse recursively into subroutines. =head1 VERSION Version 0.04 =cut | ||||||
| 22 | |||||||
| 23 | our $VERSION = '0.04'; | ||||||
| 24 | |||||||
| 25 - 47 | =head1 SYNOPSIS
perl -MO=RecDeparse,deparse,[@B__Deparse_opts],level,-1 [ -e '...' | bleh.pl ]
# Or as a module :
use B::RecDeparse;
my $brd = B::RecDeparse->new(deparse => [ @b__deparse_opts ], level => $level);
my $code = $brd->coderef2text(sub { ... });
=head1 DESCRIPTION
This module extends L<B::Deparse> by making it recursively replace subroutine calls encountered when deparsing.
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.
=head1 METHODS
=head2 C<< new < deparse => [ @B__Deparse_opts ], level => $level > >>
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.
=cut | ||||||
| 48 | |||||||
| 49 | use constant { | ||||||
| 50 | # p31268 made pp_entersub call single_delim | ||||||
| 51 | 11 | 186 | FOOL_SINGLE_DELIM => | ||||
| 52 | ($^V ge v5.9.5) | ||||||
| 53 | || ($^V lt v5.9.0 and $^V ge v5.8.9) | ||||||
| 54 | || ($Config{perl_patchlevel} && $Config{perl_patchlevel} >= 31268) | ||||||
| 55 | 11 11 | 47 20 | }; | ||||
| 56 | |||||||
| 57 | sub _parse_args { | ||||||
| 58 | 30 | 88 | croak 'Optional arguments must be passed as key/value pairs' if @_ % 2; | ||||
| 59 | 29 | 86 | my %args = @_; | ||||
| 60 | 29 | 52 | my $deparse = $args{deparse}; | ||||
| 61 | 29 | 62 | if (defined $deparse) { | ||||
| 62 | 24 | 84 | if (!ref $deparse) { | ||||
| 63 | 6 | 14 | $deparse = [ $deparse ]; | ||||
| 64 | } elsif (ref $deparse ne 'ARRAY') { | ||||||
| 65 | 1 | 2 | $deparse = [ ]; | ||||
| 66 | } | ||||||
| 67 | } else { | ||||||
| 68 | 5 | 8 | $deparse = [ ]; | ||||
| 69 | } | ||||||
| 70 | 29 | 46 | my $level = $args{level}; | ||||
| 71 | 29 | 65 | $level = -1 unless defined $level; | ||||
| 72 | 29 | 40 | $level = int $level; | ||||
| 73 | 29 | 73 | return $deparse, $level; | ||||
| 74 | } | ||||||
| 75 | |||||||
| 76 | sub new { | ||||||
| 77 | 29 | 1 | 184 | my $class = shift; | |||
| 78 | 29 | 146 | $class = ref($class) || $class || __PACKAGE__; | ||||
| 79 | 29 | 70 | my ($deparse, $level) = _parse_args(@_); | ||||
| 80 | 28 | 265 | my $self = bless $class->SUPER::new(@$deparse), $class; | ||||
| 81 | 28 | 49 | $self->{brd_level} = $level; | ||||
| 82 | 28 | 55 | return $self; | ||||
| 83 | } | ||||||
| 84 | |||||||
| 85 | sub _recurse { | ||||||
| 86 | 107 | 645 | return $_[0]->{brd_level} < 0 || $_[0]->{brd_cur} < $_[0]->{brd_level} | ||||
| 87 | } | ||||||
| 88 | |||||||
| 89 | sub compile { | ||||||
| 90 | 1 | 1 | 5 | my $bd = B::Deparse->new(); | |||
| 91 | 1 | 69 | my @args = @_; | ||||
| 92 | 1 | 3 | my ($deparse, $level) = _parse_args(@args); | ||||
| 93 | 1 | 3 | my $compiler = $bd->coderef2text(B::Deparse::compile(@$deparse)); | ||||
| 94 | 1 | 36 | $compiler =~ s/ | ||||
| 95 | ['"]? B::Deparse ['"]? \s* -> \s* (new) \s* \( ([^\)]*) \) | ||||||
| 96 | /B::RecDeparse->$1(deparse => [ $2 ], level => $level)/gx; | ||||||
| 97 | 1 1 1 1 1 1 1 | 2 6 2 4 5 2 3 | $compiler = eval 'sub ' . $compiler; | ||||
| 98 | 1 | 4 | die if $@; | ||||
| 99 | 1 | 2 | return $compiler; | ||||
| 100 | } | ||||||
| 101 | |||||||
| 102 | sub init { | ||||||
| 103 | 58 | 1 | 1813 | my $self = shift; | |||
| 104 | 58 | 125 | $self->{brd_cur} = 0; | ||||
| 105 | 58 | 81 | $self->{brd_sub} = 0; | ||||
| 106 | 58 | 255 | $self->SUPER::init(@_); | ||||
| 107 | } | ||||||
| 108 | |||||||
| 109 | my $key = $; . __PACKAGE__ . $;; | ||||||
| 110 | |||||||
| 111 | if (FOOL_SINGLE_DELIM) { | ||||||
| 112 | my $oldsd = *B::Deparse::single_delim{CODE}; | ||||||
| 113 | 11 11 11 | 56 18 37 | no warnings 'redefine'; | ||||
| 114 | *B::Deparse::single_delim = sub { | ||||||
| 115 | 69 | 2527 | my $body = $_[2]; | ||||
| 116 | 69 | 540 | if ((caller 1)[0] eq __PACKAGE__ and $body =~ s/^$key//) { | ||||
| 117 | 34 | 112 | return $body; | ||||
| 118 | } else { | ||||||
| 119 | 35 | 84 | $oldsd->(@_); | ||||
| 120 | } | ||||||
| 121 | } | ||||||
| 122 | } | ||||||
| 123 | |||||||
| 124 | sub pp_entersub { | ||||||
| 125 | 57 | 1 | 1435 | my $self = shift; | |||
| 126 | 57 | 75 | my $body = do { | ||||
| 127 | 57 | 111 | local $self->{brd_sub} = 1; | ||||
| 128 | 57 | 301 | $self->SUPER::pp_entersub(@_); | ||||
| 129 | }; | ||||||
| 130 | 57 | 14740 | $body =~ s/^&\s*(\w)/$1/ if $self->_recurse; | ||||
| 131 | 57 | 182 | return $body; | ||||
| 132 | } | ||||||
| 133 | |||||||
| 134 | sub pp_refgen { | ||||||
| 135 | 15 | 1 | 340 | my $self = shift; | |||
| 136 | 15 | 19 | return do { | ||||
| 137 | 15 | 27 | local $self->{brd_sub} = 0; | ||||
| 138 | 15 | 73 | $self->SUPER::pp_refgen(@_); | ||||
| 139 | } | ||||||
| 140 | } | ||||||
| 141 | |||||||
| 142 | sub pp_gv { | ||||||
| 143 | 66 | 1 | 18710 | my $self = shift; | |||
| 144 | 66 | 84 | my $body; | ||||
| 145 | 66 | 210 | if ($self->{brd_sub} <= 0 || !$self->_recurse) { | ||||
| 146 | 32 | 154 | $body = $self->SUPER::pp_gv(@_); | ||||
| 147 | } else { | ||||||
| 148 | 34 | 89 | my $gv = $self->gv_or_padgv($_[0]); | ||||
| 149 | 34 | 612 | $body = do { | ||||
| 150 | 34 34 | 68 95 | local @{$self}{qw/brd_sub brd_cur/} = (0, $self->{brd_cur} + 1); | ||||
| 151 | 34 | 157 | 'sub ' . $self->indent($self->deparse_sub($gv->CV)); | ||||
| 152 | }; | ||||||
| 153 | 34 | 53 | if (FOOL_SINGLE_DELIM) { | ||||
| 154 | 34 | 63 | $body = $key . $body; | ||||
| 155 | } else { | ||||||
| 156 | $body .= '->'; | ||||||
| 157 | } | ||||||
| 158 | } | ||||||
| 159 | 66 | 2142 | return $body; | ||||
| 160 | } | ||||||
| 161 | |||||||
| 162 - 208 | =head2 C<compile>
=head2 C<init>
=head2 C<pp_entersub>
=head2 C<pp_refgen>
=head2 C<pp_gv>
Functions and methods from L<B::Deparse> reimplemented by this module. Never call them directly.
Otherwise, L<B::RecDeparse> inherits all methods from L<B::Deparse>.
=head1 EXPORT
An object-oriented module shouldn't export any function, and so does this one.
=head1 DEPENDENCIES
L<Carp> (standard since perl 5), L<Config> (since perl 5.00307) and L<B::Deparse> (since perl 5.005).
=head1 AUTHOR
Vincent Pit, C<< <perl at profvince.com> >>, L<http://www.profvince.com>.
You can contact me by mail or on C<irc.perl.org> (vincent).
=head1 BUGS
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.
=head1 SUPPORT
You can find documentation for this module with the perldoc command.
perldoc B::RecDeparse
Tests code coverage report is available at L<http://www.profvince.com/perl/cover/B-RecDeparse>.
=head1 COPYRIGHT & LICENSE
Copyright 2008 Vincent Pit, all rights reserved.
This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
=cut | ||||||
| 209 | |||||||
| 210 | 1; # End of B::RecDeparse | ||||||