Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
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