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

Hello. I am looking for a way to return-to-main, as opposed to return-to-caller. Is there a simple way to do this?

I am writing a small and simple interpreter, which just aborts and prints a simple error message if it finds any errors, then waits for you to try again. Error traps can be in any level of sub-sub-etc-routine, and I'm looking for a simple way to go directly back to the keyboard-input main loop without letting the return stack fill up to infinity...

'die' and 'exit' will quit the Perl script, so they aren't what I want; 'goto' won't clear the return stack, so I don't think this is a good choice either. Can you help?

Thanks!

SEI

pseudocode example:

pseudocode example: # this is the (infinite) main loop, which takes a line of # keyboard input and processes it, until the program is # killed while (<>) { # call subs here if (foo) { &a(); next; } elsif (bar) { &b(); next; } else { &c; } } sub a { # do stuff if (divide by zero error) &myerror(message); &c(); # do more stuff return value; } sub b { # do a lot of stuff if (format is wrong) &myerror(message); # do more stuff return hash; } sub c { # do stuff if (type mismatch) &myerror(message); &b(); return list; } sub myerror { # print error message # and now I want to go straight back to the main # loop, no matter how many levels of subroutines # deep this &myerror call was, as if the program # were just starting (with the single exception # that I want to keep all the program's # global variables intact) }

Replies are listed 'Best First'.
Re: How can I return to main loop, not to caller?
by ambrus (Abbot) on Jul 29, 2004 at 20:54 UTC

    You can safely use goto, as it does clear the call stack (this is perl, not basic). A somewhat more readable way might be to use redo or next, but you should know that redo is essentially the same as goto except that goto allows labels without loops and that only goto allows computed labels. (Update: they're not the same, redo jumps inside the loop, goto jumps before the loop. Redo is the same as a goto to a label that is placed the first thing in the loop body.)

    The drawback of gotos and labels is that there are some language constructs that you can not leave with goto; but as far as I know, you can jump out from anything with die/eval. Goto will also warn you when you exit functions, but this warning is only informational, you can safely turn it off.

    Thus, you can use some code like:

    # -- next MAIN_LOOP jumps here unless there's a continue block too MAIN_LOOP: while (<>) { # -- redo MAIN_LOOP or goto MAIN_LOOP jumps here .... a() .... } sub a { .... myerror (); .... } sub myerror { redo MAIN_LOOP; }
    Don't reuse the label MAIN_LOOP in any function that can be called from the main loop or else myerror will jump there if called.
Re: How can I return to main loop, not to caller?
by BrowserUk (Patriarch) on Jul 29, 2004 at 20:54 UTC

    Actually, I think that goto will clean up the stack. My "evidence" for this is:

    #! perl -slw use strict; sub a{ my( @args ) = @_; goto MAIN unless @args; a( @args[ 100 .. $#args ] ); return 1; } printf 'Check ram'; <STDIN>; a( 1 .. 10000 ); MAIN: printf 'Check ram'; <STDIN>; my @a = ( 1 .. 50000 ); printf 'Check ram'; <STDIN>;

    At the second ram check, there has been substantial growth in the process memory (as expected) through the 100 or so chunks of ram littering the stack.

    But allocating a new large array after the goto shows barely any increase. That's leads me to think that the statement in perlsub on goto's "It can be used to go almost anywhere else within the dynamic scope, including out of subroutines," means that the stack is cleaned up.

    Whether it's the Right Way for your application is a different matter. The last para of the same section says:

    In almost all cases like this, it's usually a far, far better idea to use the structured control flow mechanisms of next, last, or redo instead of resorting to a goto. For certain applications, the catch and throw pair of eval{} and die() for exception processing can also be a prudent approach.

    Examine what is said, not who speaks.
    "Efficiency is intelligent laziness." -David Dunham
    "Think for yourself!" - Abigail
    "Memory, processor, disk in that order on the hardware side. Algorithm, algoritm, algorithm on the code side." - tachyon
Re: How can I return to main loop, not to caller?
by EdwardG (Vicar) on Jul 29, 2004 at 20:44 UTC
    eval
    while (<>) { if (/a/i) { eval {&a}; } else { eval {&b}; } } sub a { print "sub a\n"; &c; print "never get here\n"; } sub b { print "sub b\n"; } sub c { print "sub c\n"; die "never see this death\n"; }