#!/usr/bin/env perl
=head1 Abstract
p5find-regex is a program to find regular expressions in perl5 code. This is
less trivial with a grep/ack, since it's also non-trivial to write a regular
expression matching all regular expressions.
=head1 Examples
p5find-regex -h
p5find-regex ~/src/mycode
=head1 Description
This program p5find-regexp locates regular expressions in perl source code
under the given path.
The output is formatted like the output of C<grep -Hnr>. Each line consists of
colon-separated fields of filename, line number, and the content of that line.
Here's a partial result of regexes found in perlbrew source code.
> p5find-regex ./lib | head
lib/App/Perlbrew/Util.pm:24: my @a = split //, shift;
lib/App/Perlbrew/Util.pm:25: my @b = split //, shift;
lib/App/Perlbrew/Path.pm:99: $path =~ s!\Q$home/\E!~/! if $home;
lib/App/perlbrew.pm:77: my ($name) = $_->{opt} =~ /([^|]+)/;
lib/App/perlbrew.pm:168: $cmd =~ s!{$_}!$params->{$_}!g;
lib/App/perlbrew.pm:224: my @v = split(/[\.\-_]/, $version);
lib/App/perlbrew.pm:232: $v[3] =~ s/[^0-9]//g;
lib/App/perlbrew.pm:282: $value =~ s/^=//;
lib/App/perlbrew.pm:419: $shell_name =~ s/\d+$//;
lib/App/perlbrew.pm:465: $self->{cpan_mirror} =~ s{/+$}{};
Alternatively, a C<-o> flag will make it print only the regex instead of
entire line. In this display mode, an extra tab character ("\t") is added
right in front of the regex so is easier to separate them visually and
programatically by piping to C<cut -f 2->:
> {./script/p5find-regex -o ./lib | cut -f 2- | head
//
//
s!\Q$home/\E!~/!
/([^|]+)/
s!{$_}!$params->{$_}!g
/[\.\-_]/
s/[^0-9]//g
s/^=//
s/\d+$//
s{/+$}{}
=cut
use v5.18;
use Getopt::Long;
use App::p5find qw(p5_doc_iterator print_file_linenum_line);
sub print_usage {
print <<USAGE;
Usage: p5find-regex [switches] [--] [dir...]
-h show this help message.
-o Print only the matching regex
See also: `perldoc p5find-regex` for full documentation.
USAGE
}
my %opts;
GetOptions(
\%opts,
"h", # help!
"o", # Print only the Regexp, not the entire line.
);
if ($opts{h}) {
print_usage();
exit(0);
}
my @args = @ARGV;
@args = (".") unless @args;
for my $dir (@args) {
my $iter = p5_doc_iterator($dir);
while ( defined ( my $doc = $iter->() ) ) {
my $regexps = $doc->find("PPI::Token::Regexp") or next;
my $file = $doc->filename;
if ($opts{o}) {
for my $it (@$regexps) {
my $ln = $it->line_number;
print "${file}:${ln}:\t$it\n";
}
} else {
my %matched;
for my $it (@$regexps) {
my $ln = $it->line_number;
$matched{$ln} = 1;
}
print_file_linenum_line( $file, \%matched );
}
}
}