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: [email protected] (Husni Sayed)
Subject: TRY/RECOVER in Perl
Message-ID: <[email protected]>
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 ([email protected])

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 (<IN>){
 /^\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";
}