lgy@phys.washington.edu (Laurence G. Yaffe) (03/07/91)
After reading 'the Book' & scanning the man page, I'm still confused about signal handlers & scoping rules for things like statement labels & subroutine names. As an example, consider a structure like: { loop: foreach $file (...) { $SIG {'INT'} = 'interrupt' ; &compare (...) ; sub interrupt { last loop if (...) ; } } return; } sub compare { if (...) { &interrupt ;} } (a) The 'last loop' statement inside the handler 'interrupt' does seem to work. If I omit the statement label on the 'last' statement inside the handler, then perl occasionally dumps core (this is at PL37). Should a label-less 'last', 'redo', etc. statement inside a nested subroutine work? Even if the subroutine is a signal handler? What determines if a statement label is in scope - lexical (static) scoping or dynamic scoping? (b) Calling the 'interrupt' from within the 'compare' routine works. Should it? Although this is very convenient for my purposes, I would have expected 'interrupt' not to be in scope within the 'compare' routine. -- -------------------------------------------------------------------------- Laurence G. Yaffe Internet: lgy@newton.phys.washington.edu University of Washington Bitnet: yaffe@uwaphast.bitnet
tchrist@convex.COM (Tom Christiansen) (03/07/91)
From the keyboard of lgy@phys.washington.edu (Laurence G. Yaffe): :After reading 'the Book' & scanning the man page, I'm still confused about :signal handlers & scoping rules for things like statement labels & :subroutine names. As an example, consider a structure like: [ i reformatted this for personal legibility --tom] sub foo { loop: foreach $file (...) { $SIG {'INT'} = 'interrupt' ; &compare (...) ; sub interrupt { last loop if (...) ; } } # end foreach return; } sub compare { if (...) { &interrupt ; } } :(a) The 'last loop' statement inside the handler 'interrupt' does seem : to work. If I omit the statement label on the 'last' statement inside : the handler, then perl occasionally dumps core (this is at PL37). : Should a label-less 'last', 'redo', etc. statement inside a : nested subroutine work? It's a shock to me that any of them work. There's another thread around here talking about it. I don't think that a next/last/redo should look up through your callstack to find a loop. If there's not one in the current subr, I think you should be out of luck. : Even if the subroutine is a signal handler? Signal handlers have a slightly different context than normal subroutines -- notice how caller() doesn't work right with them. : What determines if a statement label is in scope - : lexical (static) scoping or dynamic scoping? I always thought that labels were lexically scoped. If Larry lets this current obfuscation of nexting up through subs work, then I guess it would have to be dynamic. :(b) Calling the 'interrupt' from within the 'compare' routine works. : Should it? Although this is very convenient for my purposes, : I would have expected 'interrupt' not to be in scope within the : 'compare' routine. I think you're making a bad assumption here. Subroutine declarations do not nest as they do in Pascal. No matter where it appears, a subroutine declaration will be globally visible for the whole package. It is not like local()s, which provide for dynamic scoping. As for calling interrupt from within your &compare function, this too is relying on the weird lasting out of a subroutine behavior. You don't need to set $SIG{'INT'} at every loop iteration. You're not declaring it to be of local scope or anything that way. That handler will be in effect forever that way. Either reset it, or declare it local: { local($SIG{'INT'}) = 'interrupt'; Signal handlers are global unless localized. It looks very much like you're trying to do some exception handling here. In perl, you want to use an eval and a die for such things. May I suggest using die/eval, which is perl's mechanism for this? Something like this: sub interrupt { die 'Interrupted'; } sub foo { local ( $SIG{'INT'} ) = 'interrupt'; LOOP: foreach $file (...) { &compare (...) ; .... } # end foreach .... } sub compare { die "Bad compare" if (...); .... } Now having set if up like this, you call your foo function in an eval, to trap the dies and see what they were. (die == raise, eval == catch): eval '&foo'; if ($@) { # caught an exception if ($@ =~ /interrupted/i) { warn "interrupted, continuing"; } elsif ( $@ =~ /bad compare/i) { warn "bad compare, continuing"; } else { die $@; } } Notice I don't call exit even if it's something I don't expect. I re-raise the exception so someone higher up can catch me if he wants. --tom -- I get so tired of utilities with arbitrary, undocumented, compiled-in limits. Don't you? Tom Christiansen tchrist@convex.com convex!tchrist
lwall@jpl-devvax.JPL.NASA.GOV (Larry Wall) (03/07/91)
In article <lgy.668295480@newton> lgy@phys.washington.edu (Laurence G. Yaffe) writes:
: (a) The 'last loop' statement inside the handler 'interrupt' does seem
: to work. If I omit the statement label on the 'last' statement inside
: the handler, then perl occasionally dumps core (this is at PL37).
: Should a label-less 'last', 'redo', etc. statement inside a
: nested subroutine work?
Some think it should, and some think it shouldn't. I think that it should,
but that it's bad programming style to make use of it. :-)
As Tom mentioned, eval '&some_routine' is the approved method of trapping
exceptions. Use "die" to raise exceptions.
: Even if the subroutine is a signal handler?
Next, last and redo are done via setjmp/longjmp, so if you can longjmp out
of a signal handler, Perl should be able to also.
: What determines if a statement label is in scope -
: lexical (static) scoping or dynamic scoping?
Purely dynamic. It keeps a label stack and walks up it whenever you refer
to an outer label. You can even use the same label on different (non-nested)
loops--Perl doesn't care, and will terminate the loop you're in rather than
some other loop.
: (b) Calling the 'interrupt' from within the 'compare' routine works.
: Should it? Although this is very convenient for my purposes,
: I would have expected 'interrupt' not to be in scope within the
: 'compare' routine.
You can put a subroutine declaration anywhere, and it will be visible from
anywhere else within the same package. Likewise for formats. The only
way to hide such declarations is with a package declaration, which is Perl's
only static scoping mechanism.
Larry
friedman@chekov.UU.NET (Barry Friedman) (03/07/91)
In article <11708@jpl-devvax.JPL.NASA.GOV> lwall@jpl-devvax.JPL.NASA.GOV (Larry Wall) writes: > >As Tom mentioned, eval '&some_routine' is the approved method of trapping >exceptions. Use "die" to raise exceptions. > An 'exit' issued in an eval'd routine is not trapped. IMNSHO it should be. here's an example: #!/usr/bin/perl sub test1 { die("bailing out"); } sub test2 { exit; } eval "&test1"; print "test1 returned: ",$@,"\n"; eval "&test2"; print "test2 returned: ",$@,"\n"; prints: test1 returned: bailing out at /users/friedman/tstp line 6. -- Barry Friedman INTERNET: friedman%chekov@uunet.uu.net Phone: (613) 782-2389 UUCP: ...!uunet!chekov!friedman Emax Computer Systems Inc. 440 Laurier Ave. W., Ottawa, Ont. Canada K1R 5C4
tchrist@convex.COM (Tom Christiansen) (03/08/91)
From the keyboard of friedman%chekov@uunet.uu.net: :An 'exit' issued in an eval'd routine is not trapped. IMNSHO it should be. It's true. I've trained myself to use die instead of exit because of that. I don't know that I agree that it shouldn't really exit though. I might want a way out no matter what, and killing myself is inelegant. --tom -- I get so tired of utilities with arbitrary, undocumented, compiled-in limits. Don't you? Tom Christiansen tchrist@convex.com convex!tchrist