Article 8165 of comp.lang.perl: Xref: feenix.metronet.com comp.lang.perl:8165 Newsgroups: comp.lang.perl Path: feenix.metronet.com!news.ecn.bgu.edu!usenet.ins.cwru.edu!eff!news.kei.com!ub!csn!iem!husni From: husni@iem.com (Husni Sayed) Subject: TRY/RECOVER in Perl Message-ID: Date: Sat, 20 Nov 1993 03:24:58 GMT Organization: IEM, Inc. X-Newsreader: Tin 1.1 PL4 Lines: 206 Perl has great constructs, but it is weak when it comes to handling errors, run-time, and compile time. In my past life (at HP), I worked with another engineer on implementing a structured error handling into Pascal. The language was used extensively in the late 70's and early 80's. It was called Modcal or (HP Pascal). A huge amount of code has been written using this language. The contruct we used was: TRY try block RECOVER recover block The idea is that if an error took place in the try block, then the code in the recover block was executed. I would like to propose the same contruct for Perl. Larry will have to implement it in his code to make it efficient. I have implemented a perl pre-processor called 'perlpp' that will, using Perl push, pop, and goto, implement the same. Howver, I can only catch errors that I detect and force and ESCAPE to the recover block. It is better than nothing. SIGNALS do not work very well here. 'perlpp' is fully deecribed in the following man page. Notice how I had to use ENDRECOVER to signify the end of the RECOVER block. You will also find my implementation at the end of tha man page. The program and the concept is very simple, but it makes life real easy. ================= perlpp.1 ==================================== perlpp(1) Perl pre-processor. Processes special syntax such as TRY RECOVER. SYNTAX perlpp [infile | - [outfile | -]] DESCRIPTION: This filter will take any perl program and look for special syntax, such as TRY RECOVER and translate it into actual perl statement. Perl programs can now handle error recovery gracefully and in a structured manner. All you have to do is inert the new TRY/RECOVER syntax and process your perl program using this filter 'perlpp' to translate the new syntax into regular perl statements. The following is how to use this construct: Insert the following lines in your perl code: TRY perl lines of code. Somewhere in here or in any sub called from here, you should include ESCAPE or ESCAPE(escapecode). RECOVER error handling for ESCAPES from the previous TRY block ENDRECOVER You can nest as many TRY RECOVER ENDRECOVER blocks as you wish. Just make sure that they are all nest properly. In other words, each TRY must be matched with a corresponding RECOVER, and each RECOVER must be matched with a corresponding ENDRECOVER. The following global 'main' variables will also be defined, so to dot tamper with them: @GERROR_BLK: TRY RECOVER stack $ESCAPECODE: Value of escape code, if set by ESCAPE(code) $TRYLEVEL: Nest level of the TRY blocks, starting with 0. EXAMPLES: #!/usr/contrib/bin/perl sub one { print "In one\n"; ESCAPE(10); } sub two { print "In TWO\n"; &one; print "In TWO Back from one. This should not print\n"; } TRY print "in Main\n"; &two; print "After two in main. This should not print\n"; RECOVER print "MAIN: First recover level\n"; TRY &one; RECOVER print "MAIN: Second recover level\n"; print "In error block of main ESCAPECODE = $ESCAPECODE\n"; print "About to get done TRYLEVEL $TRYLEVEL\n"; ENDRECOVER print "MAIN: First recover level, bye\n"; ENDRECOVER IMPLEMENTATION DETAILS: The perlpp uses a label stack called @GERROR_BLK to keep track of the current TRY block. Here is how it translates the various new statements: TRY ==> $TRYLEVEL=current_try_level; push(@GERROR_BLK, RECOVER_LABEL); RECOVER ==> pop(@GERROR_BLK); goto AFTER_ENDRECOVER; recover_label: { ENDRECOVER ==> } AFTER_ENDRECOVER: ESCAPE(code) ==> $ESCAPECODE=code; $GERRLBL=pop(@GERROR_BLK); eval "goto $GERRLBL"; AUTHOR: Husni S. Sayed (husni@iem.com) MANPAGE REVISION: @(#)$Header: perlpp.1,v 1.1 93/11/19 20:03:53 husni Exp $ ================= perlpp program source ============================ #!/usr/contrib/bin/perl # ********************************************************************* # $Header: perlpp.pp,v 1.1 93/11/19 20:03:58 husni Exp $ # ********************************************************************* sub revision{ '$Revision: 1.1 $' =~ /Revision:\s*(\d+\.\d+)/o; return $1; } if ($ARGV[0] eq '-?'){ } if ($ARGV[0]){$inf=$ARGV[0];}else{$inf="<-";} if ($ARGV[1]){$onf=$ARGV[1];}else{$onf=">-";} open(IN,$inf); open(OUT,$onf); $gerrlbl=0; while (){ /^\s*TRY/o && do { print OUT " # TRY $gerrlbl\n"; print OUT " \$TRYLEVEL=$gerrlbl;\n"; print OUT " push(\@GERROR_BLK,'lbl_$gerrlbl');\n"; $gerrlbl++; next; }; /^\s*RECOVER/o && do { print OUT " # END TRY $gerrlbl\n"; print OUT " pop(\@GERROR_BLK); goto lbl_$gerrlbl;\n"; print OUT " # RECOVER $GERRLBL\n"; $lbl=$gerrlbl-1; print OUT "lbl_$lbl: {\n"; next; }; /^\s*ENDRECOVER/o && do { print OUT " }\n"; print OUT " # ENDRECOVER $gerrlbl\n"; print OUT "lbl_$gerrlbl: "; $gerrlbl--; next; }; /^\s*ESCAPE(\((.*)\))?/o && do { if ($2) {$err=$2} else {$err=0} print OUT " # ESCPE($err)\n"; print OUT " \$ESCAPECODE=$err;\n"; print OUT " \$GERRLBL=pop(\@GERROR_BLK);\n"; print OUT " eval \"goto \$GERRLBL\";\n"; next; }; print OUT $_; } if ($gerrlbl){ print STDERR "perlpp: Problem with TRY RECOVER nesting level $gerrlbl\n"; }