Subject: | suggest PPI::Statement::Include module_version and module_first_arg |
Date: | Sun, 31 May 2009 10:51:02 +1000 |
To: | bug-PPI [...] rt.cpan.org |
From: | Kevin Ryde <user42 [...] zip.com.au> |
As a suggestion, it'd be good if PPI::Statement::Include had methods
like ->module_version and ->module_first_arg which returned and skipped
(respectively) the module version number which perl checks. Eg.
use Foo 1.0 'an arg';
would give back the 1.0 from $incl->module_version and the 'an arg' from
$incl->module_first_arg. Both would be the respective PPI elements I
think, in particular so you can then snext etc for the rest of the args.
I've been getting some joy from code like below. The way I've got
first_arg not returning ';' suits me, but maybe for maximum generality
it shouldn't check that, just return it and let the caller notice it as
the terminator etc.
# This regexp is what Perl's toke.c S_force_version() demands, as of
# versions 5.004 through 5.8.9. A version number in a "use" must start with
# a digit and then have only digits, dots and underscores. In particular
# other normal numeric forms like hex or exponential are not taken to be
# version numbers, and even omitting the 0 from a decimal like ".25" is not
# a version number.
#
our $use_module_version_number_re = qr/^v?[0-9][0-9._]*$/;
# $inc is a PPI::Statement::Include.
# If it has a version number for a module "use" or "no" then return that
# element. As of PPI 1.203 there's no v-number parsing, so the version
# element is always a PPI::Token::Number.
#
# A "require" is treated the same as "use" and "no", though a module version
# number like "require Foo::Bar 1.5" is actually a syntax error.
#
# A module version is a literal number following the module name, with
# either nothing else after it, or with no comma before the arglist.
#
sub include_module_version {
my ($inc) = @_;
# only a module style "use Foo", not a perl version num like "use 5.010"
defined ($inc->module) || return undef;
my $ver = $inc->schild(2) || return undef;
# ENHANCE-ME: when PPI recognises v-strings may have to extend this
$ver->isa('PPI::Token::Number') || return undef;
$ver->content =~ $use_module_version_number_re or return undef;
# when followed by a comma it's an argument not a version number,
# eg. "use Foo 1.0, 'hello'"
if (my $after = $ver->snext_sibling) {
if ($after->isa('PPI::Token::Operator')
&& ($after eq ',' || $after eq '=>')) {
return undef;
}
}
return $ver;
}
# $inc is a PPI::Statement::Include.
# Return the element which is the start of the first argument to its
# import() or unimport(), for "use" or "no" respectively.
#
# A "require" is treated the same as "use" and "no", but arguments to it
# like "require Foo::Bar '-init';" is in fact a syntax error.
#
sub include_module_first_arg {
my ($inc) = @_;
defined ($inc->module) || return undef;
my $arg;
if (my $ver = include_module_version ($inc)) {
$arg = $ver->snext_sibling;
} else {
# eg. "use Foo 'xxx'"
$arg = $inc->schild(2);
}
# don't return terminating ";"
if ($arg
&& $arg->isa('PPI::Token::Structure')
&& $arg->content eq ';'
&& ! $arg->snext_sibling) {
return undef;
}
return $arg;
}