1

I am familiar with the caller function which can be used by a program to know where it was invoked, from which file, package etc. Is there an easy way for a program to know how deeply nested the caller was? The use case would be to add some debugging lines without having to explicitly count the nesting level.

I am not looking for a Stack Trace. I am trying to find how deeply nested a calling function is within the scope of another function.

For example:

sub my_debug {
    my ($txt) = @_;
    my ($package, $filename, $line) = caller;
    # Is there a way to know how deeply nested the caller is?
    my $level = ... # How to get this?
    print "DEBUG: You are calling from package $package in $filename "
        . "line $line nesting level $level: MSG: $txt\n";
}

sub badly_nested {
    for my $i ( 1..10 ) {
        # 1-level deep
        for my $j ( 1 .. 10 ) {
            # 2-levels deep
            my_debug( "j is $j" );
            for my $k ( 1 .. 10 ) {
                # 3-levels deep
            }
        }
    }
}
xxfelixxx
  • 6,512
  • 3
  • 31
  • 38
  • Possible duplicate of [How can I get a call stack listing in Perl?](https://stackoverflow.com/questions/229009/how-can-i-get-a-call-stack-listing-in-perl) – AbhiNickz Feb 03 '18 at 08:59
  • Thanks @AbhiNickz, but I am looking to know not how many stack frames deep I am (what Stack Trace will tell you), but how many levels of nesting ( `if ... { if ... { if ... { for ... { } } } }`. In python, you could count the leading spaces and divide by the indent amount to figure this out. – xxfelixxx Feb 03 '18 at 09:20
  • I may be lacking imagination, but I can't see how this information could be useful. – Borodin Feb 03 '18 at 10:15
  • 1
    Imagine setting a debug flag with higher values for more detail about inner loops – xxfelixxx Feb 03 '18 at 10:36
  • Since you know the $filename and the $line you could perhaps read the file, get that line and count the number of spaces and print that? (divided by 4 or 2 maybe) – Kjetil S. Feb 03 '18 at 11:47
  • 2
    Indent your code properly and do just like in python – Flying_whale Feb 03 '18 at 13:00
  • Sure I could count spaces...I was thinking to perhaps use perltidy on the calling function and parse that, but I was hoping there was a less heavy-handed way of doing it, i.e. something the perl parser stores somewhere... – xxfelixxx Feb 04 '18 at 01:00
  • @xxfelixxx my comment was kind of "troll" ;) , I just thought about it, but I guess it should work if you make the substraction: number of `{` - number of `}` to the point where the function call is – Flying_whale Feb 05 '18 at 09:14
  • 1
    @Flying_whale That might work on some code, but it would be easily spoiled by quoted strings with `{` or `}` characters in them, so that's going into the I need a Full-Blown Parser route...which was what I was trying to avoid. The Solution of using Scope::Upper works well, without the need to re-parse anything. – xxfelixxx Feb 05 '18 at 09:18
  • I wrapped it all into a package, so I can just add debug lines (with hires timing) wherever and then selectively turn them on and off. https://metacpan.org/pod/Devel::Scope – xxfelixxx Feb 05 '18 at 09:23

1 Answers1

6

The Perl module available on CPAN, Scope::Upper will facilitate what you are asking for:

use strict;                     
use warnings;                   

use Scope::Upper qw(SCOPE);     

print SCOPE(), "\n";            
{                               
    print SCOPE(), "\n";        
    {                           
        print SCOPE(), "\n";    
        {                       
            print SCOPE(), "\n";
        }                       
        print SCOPE(), "\n";    
    }                           
    print SCOPE(), "\n";        
}                               
print SCOPE(), "\n";            

The output from this code is:

0
1
2
3
2
1
0

So it will enable you to see what depth your current scope is, which can be used to deduce how deeply nested loops are. You can pass an offset to the SCOPE() function. So for example, if you called {SCOPE(1)} the return value would be zero despite being one scope deep. This could be useful in a situation where you don't care that you're inside of a subroutine or an if block, so want to start counting deeper in. For example:

if ($foo) {
    foreach my $x (1..10) {
        foreach my $y (1..10) {
            print SCOPE(1), "\n";
        }
    }
}

In this example response will show scope depth to be 2 rather than 3 because passing a 1 to SCOPE() hints it to disregard the scope of the if(){...} block.

DavidO
  • 13,812
  • 3
  • 38
  • 66