v_melnik has asked for the wisdom of the Perl Monks concerning the following question:

Dear colleagues,

I'd be very grateful for your suggestions and ideas on a task.

# :zaloopa use constant MMConst1 => 1; use constant MMConst2 => 2; use constant MMConst3 => 3; my @MM_zaloopa = qw( MMConst1 MMConst2 MMConst3 );

In other words, I need to get a list of constants I've declared after the last occurence of /^# :(\S+)$/ in the code. How can I get lines of the script's code?

Opening the __FILE__, seeking the __LINE__? Sounds okay, but are there any other options?

Thank you!

Replies are listed 'Best First'.
Re: Reading the script code from itself
by hdb (Monsignor) on May 28, 2014 at 12:33 UTC

    If you add a __DATA__ segment at the end, and rewind the DATA handle, you can read your code:

    use strict; use warnings; seek( DATA, 0, 0 ); print while( <DATA> ); __DATA__
Re: Reading the script code from itself
by Anonymous Monk on May 28, 2014 at 11:41 UTC

    You certainly could do it as you suggest, e.g. opening __FILE__ and using a regex to parse out the constants. There are theoretically other ways to parse the source (e.g. PPI) or even dig into the symbol table... however, this really smells like an XY Problem, and if you could explain the broader picture and what you're trying to accomplish, I'm sure we could come up with some better solutions to the original problem.

Re: Reading the script code from itself
by v_melnik (Scribe) on May 28, 2014 at 13:25 UTC

    Thank you!

    What I need is to get the list of defined constants to make their names exportable:

    %EXPORT_TAGS = ( zaloopa => \@MM_zaloopa, herovina => \@MM_herovina, poeben => \@MM_poeben );
    So, I want to make those constants exportable on demand. I just don't want to make lists for @MM_zaloopa, @MM_herovina and @MM_poeben by hands, as I don't want the constant name to be entered twice (once when I'm declaring the constant and twice when I'm adding the name to the list of exportable names). So I want this list to be made automatically, by the script itself. :)

      Your use case is weird, instead of using constants I would choose hash (or array) elements right away from the beginning!

      Anyway your constants live in the STASH of your package and you seem to have a clear naming convention...

      ....so just grep {/^MM/} keys %YourPkg:: to find those symbols.

      Introspection is always better than parsing code!

      HTH! :)

      Cheers Rolf

      ( addicted to the Perl Programming Language)

      update

      example:

      DB<110> $a=3 => 3 DB<111> grep /^a/,keys %main:: => ("a", "attributes::")
Re: Reading the script code from itself
by Anonymous Monk on May 28, 2014 at 11:30 UTC

    Opening the __FILE__, seeking the __LINE__? Sounds okay, but are there any other options?

    The use case doesn't make sense, what is your use case?

    Re^7: writing a pragma

Re: Reading the script code from itself
by locked_user sundialsvc4 (Abbot) on May 28, 2014 at 13:02 UTC

    I agree with Anonymous Monk on this one ... this sounds like an XY Problem.   If what you need is a configuration-file, there are lots of good ways to do that.   If, on the other hand, you need constants to be declared within your source code, that is generally what the interpreter is meant for.   There are ways, like __DATA__, to embed text into the source-code (so that you can always find it).   But if you simply say that you want to arbitrarily read the source-code of the module that you’re executing, without giving any of us any idea why you are doing so, then this very strongly implies that you might be chasing after a red herring or a white rabbit.   Please tell us briefly what you want to do achieve.

Re: Reading the script code from itself
by locked_user sundialsvc4 (Abbot) on May 28, 2014 at 17:55 UTC

    Padewan ... draw thee near.

    What I need is to get the list of defined constants to make their names exportable ...

    No.   That is your now-chosen implementation, and it is spinning way off-course from any accepted or acceptable norm of how to use constants in Perl.   If such a contraption ever made it into production anywhere, you must not wonder why your fellow developers would be lobbing bricks into your cube from high altitudes at such times when they know that you are present therein.   Step back, and tell us ... not what you are trying to write (which is clear enough, and smelly enough ...) but what you are trying to achieve.   Let us say:   “Tell us more about the piece(s) of software that are going to be using this.   Tell us more about what they need to achieve.”

    Let me kindly put it this way:   if I were your project-manager, or your team lead, I would order you to nix this idea, whatever it is, and I would circle-back to see that it had been done.   I do not fully understand it, but I understand it already well-enough to know that it is ... a mistake.   That, in a world of “there’s more than one way to do it,” this cannot be The Way.   Therefore, I would trust my gut and exercise my authority, and invite you to understand and come to agree.

    Knowing that I once-again risk a hailstorm of down-votes, I hope that you instead will not take this frank assessment personally.   Step far-enough back from your problem so that you can see it outside of your present “solution.”   You do not want to be where you are, wherever it is.   I may not understand your total requirement well enough (yet) to know what is right, but my gut is telling me quite clearly what is wrong as in not-right.

      Thank you!

      What I want to acheive is to have a package full of constants needed by a project. I just want constants to be exportable by tags, but I don't want to indicate constant names twice, as I want to do it only once - when I'm defining those constants.

      So, I'd love it to look like that:

      # :tag1 use constant constant1 => 1; use constant constant2 => 2; use constant constant3 => 3; # :tag2 use constant constant4 => 4; use constant constant5 => 5; use constant constant6 => 6; # :tag3 use constant constant4 => 4; use constant constant5 => 5; use constant constant6 => 6; @EXPORT_TAGS = some_cool_internal_subrotine_making_the_hash_of_tags_to +_be_exported();

      P.S. I'm already using a complicated structure of configuration files in this project, but I want to have constants somewhere in the code where they can't be changed by an user (as configuration files acutally can be changed).

        Disclaimer: I find the design questionable... you should consider not using constant (in fact Conway's Perl Best Practices recommends against it, e.g. Perl::Critic::Policy::ValuesAndExpressions::ProhibitConstantPragma) and look at alternatives such as using a locked hash (or even a function) to return your "constant" values.

        As is said, Perl gives you just enough rope to hang yourself with, so here you go:

        package ConstExporter; use warnings; use strict; my %CONSTANTS = ( foo => "abc", bar => "xyz", quz => "123", ); sub import { my ($class, @export) = @_; my ($callpack) = caller; for my $exp (@export) { die "bad constant name '$exp'" unless exists $CONSTANTS{$exp}; no strict 'refs'; *{"${callpack}::$exp"} = sub () { $CONSTANTS{$exp} }; } } 1;

        Use it in the usual way: use ConstExporter qw/foo bar/;