NAME
PPIx::Element::Package - Derive the package an element is defined in
VERSION
version 0.001000
SYNOPSIS
use PPI;
use PPIx::Element::Package qw( identify_package identify_package_namespace );
# Do your normal PPI stuff here.
# Get the logical enclosing package or undef if one cannot be discovered
my $package = identify_package( $element );
# Get the name of the logical enclosing package, or main if one cannot be discovered
my $namespace = identify_package_namespace( $element );
DESCRIPTION
This module aims to determine the scope any PPI::Element
( which includes Nodes
and Tokens
) is defined in.
It provides two utility methods as follows:
identify_package
- The LogicalPPI::Statement::Package
that owns theElement
identify_package_namespace
- The name-space of the logicalPPI::Statement::Package
that owns the element.
The latter of these is just a convenience wrapper on top of x_package
that returns main
when either the owning Statement::Package
cannot be found, or when the owning Statement::Package
's name-space is somehow undefined.
FUNCTIONS
identify_package
Upwards-Recursively identifies the logical owner PPI::Statement::Package
for $element
.
my $package = PPIx::Element::Package::identify_package( $element );
identify_package_namespace
Recursively find the Package
as per identify_package
and return the imagined name-space associated.
my $name = identify_package_namespace( $element );
This is mostly a convenience wrapper that returns main
safely when no package can be otherwise determined.
identify_package_in_previous_siblings
Non-Recursively find a Package
statement that is the nearest preceding sibling of $element
.
my $package = identify_package_in_previous_siblings( $element );
Returns the nearest PPI::Statement::Package
, or undef
if none can be found in the siblings.
LOGIC
Here lies the assumptions that this module uses to find the Package
:
- 1. That any node that has children nodes implies a lexical scope.
- 2. That within a given lexical scope, the first
Package
sibling prior to a given Element is theOwner
package. - 3. In the event a given lexical scope has no
Package
declarations between the Element and the first child of that lexical scope, that thePackage
can be derived from the position of the scope itself, by re-applying rules 1 and 2 recursively upwards until theDocument
is reached. - 4. Any nodes that are
PPI::Statement::Package
are contained within themselves, that is: -
package Foo; # This Whole line is inside "Foo" package Bar; # This Whole line is inside "Bar"
- 5. And subsequently, any children of a
PPI::Statement::Package
Node
(which are the tokens themselves that compose the statement) are themselves within that package. ( This is just a logical extension of #4 ).
The biggest scope I presently have for error in these assumptions is in the assumptions about Package scope being determinable from the PPI
document hierarchy, which may lead to an over-eager presumption that a lexical scope exists where one may not exist.
However, under my testing so far this approach has proven more useful and accurate than manually traversing the tokens and only declaring scopes on Block
boundaries.
SEE ALSO
PPIx::LineToSub
I initially tried using PPIx::LineToSub
, however, in testing it proved far too sloppy for my uses, and having no support for lexical contexts at all and is entirely oriented on a "new package overrides previous package" principle.
Its is also limited for my use cases in that it is entirely line oriented, so cases like this are intractably insolvable:
package Quux; { package Foo; baz() } bar();
Answering "what package is on this line" is not even a sensible question to ask there.
It has however a performance advantage due to being a single-pass indexing sweep with all subsequent checks being a simple array look-up.
THANKS
To MITHALDU
for feedback and code review on the initial design. All lurking bugs still present I can take full credit for.
AUTHOR
Kent Fredric <kentnl@cpan.org>
COPYRIGHT AND LICENSE
This software is copyright (c) 2015 by Kent Fredric <kentfredric@gmail.com>.
This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.