Skip to content

Instantly share code, notes, and snippets.

@briandfoy
Last active June 6, 2016 14:43
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save briandfoy/b1409e5ee06bd38200d8 to your computer and use it in GitHub Desktop.
Save briandfoy/b1409e5ee06bd38200d8 to your computer and use it in GitHub Desktop.
Find all the environment variables you might use in Perl. There are are false positives here, but so what.
use File::Find;
use List::MoreUtils qw(uniq);
use v5.22;
use feature qw(postderef signatures);
no warnings qw(experimental::postderef experimental::signatures);
use Data::Dumper;
use PPI::Document;
use PPI::Dumper;
my %Results;
my $wanted = sub {
return unless /\.(?:pod|pm|pl)\z/;
my $data = do { local( @ARGV, $/ ) = $File::Find::name; <> };
my @vars = extract_env( $data );
push @{ $Results{$_} }, $File::Find::name for @vars;
};
find( { wanted => $wanted, no_chdir => 1 }, @ARGV );
say "===Key Count Report===";
foreach my $key ( sort { @{ $Results{$a} } <=> @{ $Results{$b} } } keys %Results ) {
say "$key: " . @{ $Results{$key} };
}
say "===File Report===";
foreach my $key ( keys %Results ) {
next if $key =~ / \A [A-Z0-9_]+ \z /x;
say "$key: " . @{ $Results{$key} };
}
sub extract_env ( $data ) {
my @keys =
map { # some PPI trees may contain many types, so try all of them
my @words =
handle_words( $_->[1] ),
handle_double_quoted( $_->[1] ),
handle_double_qq( $_->[1] );
}
map { [ $_, PPI::Document->new( \$_ ) ] }
$data =~ /
[ \$%\@ ] # sigil
\s*
ENV # var name
\s*
\{ # starting key list
(
.*?
)
} # ending key list
/xg;
@keys;
}
sub handle_words ( $ppi ) {
my $nodes = $ppi->find( sub {
(
$_[1]->isa('PPI::Token::Word') ||
$_[1]->isa('PPI::Token::Quote') ||
$_[1]->isa('PPI::Token::QuoteLike::Words')
)
&&
not_next_to_operator( $_[1] )
&&
$_[1]->can( 'literal' )
&&
$_[1]->literal
} );
return unless $nodes;
my @words = map { $_->literal } @$nodes;
@words;
}
sub handle_double_quoted ( $ppi ) {
my $nodes = $ppi->find( sub {
(
$_[1]->isa('PPI::Token::Quote::Double')
)
&&
! $_[1]->interpolations
&&
$_[1]->can( 'string' )
&&
$_[1]->string
} );
return unless $nodes;
my @words = map { $_->string } @$nodes;
@words;
}
sub handle_double_qq ( $ppi ) {
my $nodes = $ppi->find( sub {
(
$_[1]->isa('PPI::Token::Quote::Interpolate')
)
&&
$_[1]->can( 'string' )
&&
$_[1]->string
} );
return unless $nodes;
my @words = map { $_->string } @$nodes;
@words;
}
sub not_next_to_operator ( $ppi ) {
my $next_to_operator =
eval { $ppi->snext_sibling->isa( 'PPI::Token::Operator' ) }
||
eval { $ppi->sprevious_sibling->isa( 'PPI::Token::Operator' ) };
# if we aren't next to an operator, who cares?
return 1 unless $next_to_operator;
return 0 if(
eval {
$ppi->snext_sibling
&&
$ppi->snext_sibling->content ne ','
}
);
return 0 if( eval {
$ppi->sprevious_sibling
&&
$ppi->sprevious_sibling->content ne ','
}
);
return 1;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment