Ref-Util-Rewriter
view release on metacpan or search on metacpan
lib/Ref/Util/Rewriter.pm view on Meta::CPAN
package Ref::Util::Rewriter;
# ABSTRACT: Rewrite your code to use Ref::Util
use strict;
use warnings;
use PPI;
use Safe::Isa;
use Exporter qw< import >;
use List::Util qw< first >;
our @EXPORT_OK = qw< rewrite_string rewrite_file >;
my %reftype_to_reffunc = (
SCALAR => 'is_scalarref',
ARRAY => 'is_arrayref',
HASH => 'is_hashref',
CODE => 'is_coderef',
Regexp => 'is_regexpref',
GLOB => 'is_globref',
IO => 'is_ioref',
REF => 'is_refref',
);
sub rewrite_string {
my $string = shift;
my $res = rewrite_doc( PPI::Document->new(\$string) );
return $res;
}
sub rewrite_file {
my $file = shift;
my $content = rewrite_doc( PPI::Document->new($file) );
open my $fh, '>', $file
or die "Failed to open file $file: $!";
print {$fh} $content;
close $fh;
return $content;
}
sub rewrite_doc {
my $doc = shift or die;
my $all_statements = $doc->find('PPI::Statement');
$all_statements = [] unless defined $all_statements;
my @cond_ops = qw<or || and &&>;
my @new_statements;
ALL_STATEMENTS:
foreach my $statement ( @{$all_statements} ) {
# if there's an "if()" statement, it appears as a Compound statement
# and then each internal statement appears again,
# causing duplication in results
$statement->$_isa('PPI::Statement::Compound')
and next;
_handle_eval($statement);
# find the 'ref' functions
my $ref_subs = $statement->find( sub {
lib/Ref/Util/Rewriter.pm view on Meta::CPAN
$after = '}';
if ( $sib->content =~ qr{^q(.).*(.)$} ) {
$before = 'q' . $1;
$after = $2;
}
$content = $sib->literal;
} elsif ( $sib->isa('PPI::Token::Quote::Interpolate') ) {
$before = 'qq{';
$after = '}';
if ( $sib->string =~ qr{^q(.).*(.)$} ) {
$before = $1;
$after = $2;
}
$content = $sib->string;
}
$content
or next;
# FIXME: this is very ugly....
# FIXME need to escape for literals but the idea is there
$sib->{'content'}
= $before . rewrite_string($content) . $after;
# Idea:
# my $p = PPI::Token::Quote::Double->new( rewrite_string($content) );
# $sib->insert_before( $p );
# $sib->delete;
last;
}
}
}
}
sub _create_statement {
my ( $func, $args, $rest ) = @_;
my $args_str = join '', @{$args};
$args_str =~ s/^\s+//;
$args_str =~ s/\s+$//;
$args_str =~ s/^\(+//;
$args_str =~ s/\)+$//;
defined $rest or $rest = '';
return PPI::Document::Fragment->new(\"$func($args_str)$rest");
}
1;
__END__
=pod
=encoding UTF-8
=head1 NAME
Ref::Util::Rewriter - Rewrite your code to use Ref::Util
=head1 VERSION
version 0.100
=head1 SYNOPSIS
use Ref::Util::Rewriter qw< rewrite_string >;
my $new_string = rewrite_string(
q! if ( ref($foo) eq 'HASH' ) { ... } !
);
# $new_string = q! if ( is_hashref($foo) ) { ... } !;
use Ref::Util::Rewriter qw< rewrite_file >;
rewrite_file("script.pl"); # file was now rewritten
=head1 DESCRIPTION
B<Warning:> You should take into account that the meaning of
L<Ref::Util>'s functions are subject to change with regards to
blessed objects. This might change the rewriter code in the future
to be smarter. This might also mean this won't necessarily achieve
what you're expecting.
Run it, check the diff, check your code, run your code, then
(maybe) - knowing the risk you take and absolutely no liability on
me, my family, nor my pets - merge it.
This module rewrites Perl code to use L<Ref::Util> instead of your
regular calls to C<ref>. It is much substantially faster and avoids
several mistakes that haunt beginning and advanced Perl developers.
Please review L<Ref::Util> to fully understand the possible implications
of using it in your case instead of the built-in C<ref> function.
The following constructs of code are supported:
=over 4
=item * Simple statement conditions
ref($foo) eq 'CODE'; # -> is_coderef($foo)
ref $foo eq 'CODE'; # -> is_coderef($foo)
ref $foo; # -> is_ref($foo)
=item * Compound statement conditions
if ( ref($foo) eq 'HASH' ) {...} # -> if( is_hashref($foo) ) {...}
if ( ref $foo eq 'HASH' ) {...} # -> if( is_hashref($foo) ) {...}
if ( ref $foo ) {...} # -> if( is_ref($foo) ) {...}
=item * Postfix logical conditions
ref($foo) eq 'ARRAY' and ... # -> is_arrayref($foo) and ...
ref($foo) eq 'ARRAY' or ... # -> is_arrayref($foo) or ...
ref($foo) or ... # -> is_ref($foo) or ...
=back
The following types of references comparisons are recognized:
=over 4
=item * C<SCALAR> = C<is_scalarref>
=item * C<ARRAY> = C<is_arrayref>
=item * C<HASH> = C<is_hashref>
=item * C<CODE> = C<is_coderef>
=item * C<Regexp> = C<is_coderef>
=item * C<GLOB> = C<is_globref>
=item * C<IO> = C<is_ioref>
=item * C<REF> = C<is_refref>
=back
=head1 SUBROUTINES
=head2 rewrite_string($perl_code_string)
Receive a string representing Perl code and return a new string in which
all C<ref> calls are replaced with the appropriate calls to L<Ref::Util>.
=head2 rewrite_file($filename)
Receive a filename as a string and rewrite the file in place (thus the
file is altered) in which all C<ref> calls are replaced with the
appropriate calls to L<Ref::Util>.
Careful, this function changes your file in place. It is advised to put
your file in some revision control so you could see what changes it has
done and commit them if you accept them.
This does B<not> add a new statement to use L<Ref::Util>, you will still
need to do that yourself.
=head2 rewrite_doc
The guts of the module which uses a direct L<PPI::Document> object and
works on that. It is used internally, but you may call it yourself if
you so wish.
=head1 AUTHOR
Sawyer X
=head1 COPYRIGHT AND LICENSE
This software is Copyright (c) 2019 by Sawyer X.
This is free software, licensed under:
The MIT (X11) License
=cut
( run in 1.106 second using v1.01-cache-2.11-cpan-39bf76dae61 )