| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | # Error::TryCatch | 
| 2 |  |  |  |  |  |  | # | 
| 3 |  |  |  |  |  |  | # Copyright (c) 2005-2009 Nilson Santos Figueiredo Jr. . | 
| 4 |  |  |  |  |  |  | # All rights reserved.  This program is free software; | 
| 5 |  |  |  |  |  |  | # you can redistribute it and/or modify it under the same | 
| 6 |  |  |  |  |  |  | # terms as perl itself. | 
| 7 |  |  |  |  |  |  | # | 
| 8 |  |  |  |  |  |  | # Some portions based on Error.pm from Graham Barr | 
| 9 |  |  |  |  |  |  |  | 
| 10 |  |  |  |  |  |  | ##################################################################### | 
| 11 |  |  |  |  |  |  | # WARNING!                                                          # | 
| 12 |  |  |  |  |  |  | # This code is old, don't blame me if it's an unreadable mess.      # | 
| 13 |  |  |  |  |  |  | # Some day I might clean it up. Be glad that, apparently, it works. # | 
| 14 |  |  |  |  |  |  | ##################################################################### | 
| 15 |  |  |  |  |  |  |  | 
| 16 |  |  |  |  |  |  | package Error::TryCatch; | 
| 17 | 1 |  |  | 1 |  | 21632 | use warnings; | 
|  | 1 |  |  |  |  | 3 |  | 
|  | 1 |  |  |  |  | 29 |  | 
| 18 | 1 |  |  | 1 |  | 5 | use strict; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 35 |  | 
| 19 | 1 |  |  | 1 |  | 6 | use vars qw($VERSION @EXPORT $DEFAULT_EXCEPTION $DEBUG); | 
|  | 1 |  |  |  |  | 5 |  | 
|  | 1 |  |  |  |  | 74 |  | 
| 20 | 1 |  |  | 1 |  | 5 | use base 'Exporter'; | 
|  | 1 |  |  |  |  | 1 |  | 
|  | 1 |  |  |  |  | 116 |  | 
| 21 | 1 |  |  | 1 |  | 2746 | use Filter::Simple; | 
|  | 1 |  |  |  |  | 35535 |  | 
|  | 1 |  |  |  |  | 9 |  | 
| 22 | 1 |  |  | 1 |  | 2195 | use Parse::RecDescent; | 
|  | 1 |  |  |  |  | 31982 |  | 
|  | 1 |  |  |  |  | 7 |  | 
| 23 | 1 |  |  | 1 |  | 41 | use Carp; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 598 |  | 
| 24 |  |  |  |  |  |  |  | 
| 25 |  |  |  |  |  |  | $VERSION = '0.07'; | 
| 26 |  |  |  |  |  |  | @EXPORT = qw(throw); | 
| 27 |  |  |  |  |  |  |  | 
| 28 |  |  |  |  |  |  | $DEFAULT_EXCEPTION = 'Error::Unhandled' unless defined $DEFAULT_EXCEPTION; | 
| 29 |  |  |  |  |  |  |  | 
| 30 |  |  |  |  |  |  | my $grammar = q! | 
| 31 |  |  |  |  |  |  |  | 
| 32 |  |  |  |  |  |  | program:  statement(s) | 
| 33 |  |  |  |  |  |  | statement: starting_bracket | except_handler(s) | non_relevant | 
| 34 |  |  |  |  |  |  | starting_bracket: /^[\s]*[{}]/ | 
| 35 |  |  |  |  |  |  | non_relevant: | 
| 36 |  |  |  |  |  |  | { bless { __VALUE__ => join "", @{ $item[1] } }, $item[0] } | 
| 37 |  |  |  |  |  |  | | /[^\n]*\n?/ | 
| 38 |  |  |  |  |  |  | exception_type: /[\w_]+(?:::[\w_]+)*/ | 
| 39 |  |  |  |  |  |  | except_handler: "try" /[\s]*/  /[\s]*/ | 
| 40 |  |  |  |  |  |  | | "catch" /[\s]*/ exception_type with(?)  /[\s]*/ | 
| 41 |  |  |  |  |  |  | | "otherwise" /[\s]*/  /[\s]*/ | 
| 42 |  |  |  |  |  |  | | "finally" /[\s]*/  /[\s]*/ | 
| 43 |  |  |  |  |  |  |  | 
| 44 |  |  |  |  |  |  | with: "with" | 
| 45 |  |  |  |  |  |  |  | 
| 46 |  |  |  |  |  |  | !; | 
| 47 |  |  |  |  |  |  |  | 
| 48 |  |  |  |  |  |  | my $parser = new Parse::RecDescent($grammar); | 
| 49 |  |  |  |  |  |  |  | 
| 50 |  |  |  |  |  |  | FILTER { | 
| 51 |  |  |  |  |  |  | return unless defined $_; | 
| 52 |  |  |  |  |  |  | my $tree = $parser->program($_); | 
| 53 |  |  |  |  |  |  | $_ = _traverse($tree); | 
| 54 |  |  |  |  |  |  | }; | 
| 55 |  |  |  |  |  |  |  | 
| 56 |  |  |  |  |  |  | sub _traverse { | 
| 57 | 1 |  |  | 1 |  | 4 | my $tree = shift; | 
| 58 | 1 |  |  |  |  | 2 | my $code; | 
| 59 | 1 |  |  |  |  | 2 | for my $stm (@{$tree->{'statement(s)'}}) { | 
|  | 1 |  |  |  |  | 4 |  | 
| 60 | 3 | 50 |  |  |  | 9 | if (defined $stm->{'non_relevant'}) { | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 61 | 3 | 50 |  |  |  | 16 | $code .= $stm->{'non_relevant'}->{'__VALUE__'} | 
| 62 |  |  |  |  |  |  | if defined $stm->{'non_relevant'}->{'__VALUE__'}; | 
| 63 |  |  |  |  |  |  | } | 
| 64 |  |  |  |  |  |  | elsif (defined $stm->{'starting_bracket'}) { | 
| 65 | 0 |  |  |  |  | 0 | $code .= $stm->{'starting_bracket'}->{'__VALUE__'}; | 
| 66 |  |  |  |  |  |  | } | 
| 67 |  |  |  |  |  |  | elsif (defined $stm->{'except_handler(s)'}) { | 
| 68 | 0 |  |  |  |  | 0 | my %clauses; | 
| 69 | 0 |  |  |  |  | 0 | for my $eh (@{$stm->{'except_handler(s)'}}) { | 
|  | 0 |  |  |  |  | 0 |  | 
| 70 | 0 |  |  |  |  | 0 | my $innertree = $parser->program($eh->{'__DIRECTIVE1__'}); | 
| 71 | 0 |  |  |  |  | 0 | my $innercode = _traverse($innertree); | 
| 72 |  |  |  |  |  |  |  | 
| 73 |  |  |  |  |  |  | # try to keep line count | 
| 74 | 0 |  |  |  |  | 0 | $eh->{'__PATTERN1__'} =~ s/[^\n]//g; | 
| 75 | 0 |  |  |  |  | 0 | $eh->{'__PATTERN2__'} =~ s/[^\n]//g; | 
| 76 | 0 |  |  |  |  | 0 | $innercode = $eh->{'__PATTERN1__'} . $innercode . $eh->{'__PATTERN2__'}; | 
| 77 |  |  |  |  |  |  |  | 
| 78 | 0 |  |  |  |  | 0 | my $clause = $eh->{'__STRING1__'}; | 
| 79 | 0 | 0 |  |  |  | 0 | if ($clause ne 'catch') { | 
|  |  | 0 |  |  |  |  |  | 
| 80 | 0 |  |  |  |  | 0 | $clauses{$clause} = $innercode; | 
| 81 |  |  |  |  |  |  | } | 
| 82 |  |  |  |  |  |  | elsif ($clause eq 'catch') { | 
| 83 | 0 |  |  |  |  | 0 | push(@{$clauses{'catch'}}, { | 
|  | 0 |  |  |  |  | 0 |  | 
| 84 |  |  |  |  |  |  | exception => $eh->{'exception_type'}->{'__VALUE__'}, | 
| 85 |  |  |  |  |  |  | code	  => $innercode | 
| 86 |  |  |  |  |  |  | }); | 
| 87 |  |  |  |  |  |  | } | 
| 88 | 0 |  |  |  |  | 0 | else { die 'unexpected parse error(1)' } | 
| 89 |  |  |  |  |  |  | } | 
| 90 | 0 | 0 |  |  |  | 0 | if (defined $clauses{try}) { | 
| 91 | 0 |  |  |  |  | 0 | my $innercode = "eval $clauses{try};"; | 
| 92 | 0 | 0 | 0 |  |  | 0 | if (defined($clauses{catch}) || defined $clauses{otherwise}) { | 
| 93 | 0 |  |  |  |  | 0 | $innercode .= 'if ($@) {$@ = new '.$DEFAULT_EXCEPTION.'($@) unless ref($@);'; | 
| 94 | 0 |  |  |  |  | 0 | my $catch = defined $clauses{catch}; | 
| 95 | 0 | 0 |  |  |  | 0 | if ($catch) { | 
| 96 | 0 |  |  |  |  | 0 | my $els = ''; | 
| 97 | 0 |  |  |  |  | 0 | for my $clause (@{$clauses{catch}}) { | 
|  | 0 |  |  |  |  | 0 |  | 
| 98 | 0 |  |  |  |  | 0 | $innercode .= "${els}if (\$\@->isa('$clause->{exception}')) $clause->{code}"; | 
| 99 | 0 | 0 |  |  |  | 0 | $els = 'els' if ($els eq ''); | 
| 100 |  |  |  |  |  |  | } | 
| 101 |  |  |  |  |  |  | } | 
| 102 | 0 | 0 |  |  |  | 0 | if (defined $clauses{otherwise}) { | 
|  |  | 0 |  |  |  |  |  | 
| 103 | 0 | 0 |  |  |  | 0 | $innercode .= 'else' if $catch; | 
| 104 | 0 |  |  |  |  | 0 | $innercode .= $clauses{otherwise}; | 
| 105 |  |  |  |  |  |  | } | 
| 106 |  |  |  |  |  |  | elsif ($catch) { | 
| 107 | 0 |  |  |  |  | 0 | $innercode .= 'else{Carp::croak($@)}'; | 
| 108 |  |  |  |  |  |  | } | 
| 109 | 0 |  |  |  |  | 0 | $innercode .= '}'; | 
| 110 |  |  |  |  |  |  | } | 
| 111 | 0 | 0 |  |  |  | 0 | if (defined $clauses{finally}) { | 
| 112 | 0 |  |  |  |  | 0 | $innercode = "eval{$innercode};$clauses{finally};if(\$\@){die \$\@}"; | 
| 113 |  |  |  |  |  |  | } | 
| 114 | 0 |  |  |  |  | 0 | $code .= $innercode; | 
| 115 |  |  |  |  |  |  | } | 
| 116 | 0 |  |  |  |  | 0 | else { die "syntax error: no try clause found\n"	} | 
| 117 |  |  |  |  |  |  | } | 
| 118 | 0 |  |  |  |  | 0 | else { die "unexpected parse error(2)\n" } | 
| 119 |  |  |  |  |  |  | } | 
| 120 | 1 |  |  |  |  | 27 | return $code; | 
| 121 |  |  |  |  |  |  | } | 
| 122 |  |  |  |  |  |  |  | 
| 123 | 0 |  |  | 0 | 0 |  | sub throw { croak @_ } | 
| 124 |  |  |  |  |  |  |  | 
| 125 |  |  |  |  |  |  | 1; | 
| 126 |  |  |  |  |  |  |  | 
| 127 |  |  |  |  |  |  | package Error::Generic; | 
| 128 | 1 |  |  | 1 |  | 6 | use base 'Class::Accessor'; | 
|  | 1 |  |  |  |  | 1 |  | 
|  | 1 |  |  |  |  | 1038 |  | 
| 129 | 1 |  |  | 1 |  | 1898 | use Carp; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 111 |  | 
| 130 |  |  |  |  |  |  |  | 
| 131 |  |  |  |  |  |  | # overloadable | 
| 132 |  |  |  |  |  |  | __PACKAGE__->mk_accessors(qw[package file line text value]); | 
| 133 | 0 |  |  | 0 |  |  | sub stringify { $_[0]->text } | 
| 134 |  |  |  |  |  |  |  | 
| 135 |  |  |  |  |  |  | use overload ( | 
| 136 |  |  |  |  |  |  | '""'	   =>	'stringify', | 
| 137 |  |  |  |  |  |  | '0+'	   =>	'value', | 
| 138 | 0 |  |  | 0 |  | 0 | 'bool'     =>	sub { return 1 }, | 
| 139 | 1 |  |  |  |  | 12 | 'fallback' =>	1 | 
| 140 | 1 |  |  | 1 |  | 5 | ); | 
|  | 1 |  |  |  |  | 1 |  | 
| 141 |  |  |  |  |  |  |  | 
| 142 | 0 |  |  | 0 |  |  | sub get { $_[0]->{"-$_[1]"} } | 
| 143 | 0 |  |  | 0 |  |  | sub set { $_[0]->{"-$_[1]"} = $_[2] } | 
| 144 |  |  |  |  |  |  |  | 
| 145 |  |  |  |  |  |  | sub new { | 
| 146 | 0 |  |  | 0 |  |  | my $class  = shift; | 
| 147 | 0 |  |  |  |  |  | my ($pkg, $file, $line) = caller(1); | 
| 148 | 0 |  |  |  |  |  | my %e = ( | 
| 149 |  |  |  |  |  |  | '-package'	=> $pkg, | 
| 150 |  |  |  |  |  |  | '-file'		=> $file, | 
| 151 |  |  |  |  |  |  | '-line'		=> $line, | 
| 152 |  |  |  |  |  |  | '-value'	=> 0, | 
| 153 |  |  |  |  |  |  | @_ | 
| 154 |  |  |  |  |  |  | ); | 
| 155 | 0 | 0 |  |  |  |  | if ($Error::TryCatch::DEBUG) { | 
| 156 | 0 |  |  |  |  |  | warn "thrown $class\n"; | 
| 157 | 0 | 0 |  |  |  |  | for (keys %e) { warn "\t$_ => ". (defined($e{$_}) ? $e{$_} : "(undef)") ."\n" } | 
|  | 0 |  |  |  |  |  |  | 
| 158 |  |  |  |  |  |  | } | 
| 159 | 0 |  |  |  |  |  | bless { %e }, $class; | 
| 160 |  |  |  |  |  |  | } | 
| 161 |  |  |  |  |  |  |  | 
| 162 |  |  |  |  |  |  | 1; | 
| 163 |  |  |  |  |  |  |  | 
| 164 |  |  |  |  |  |  | package Error::Unhandled; | 
| 165 | 1 |  |  | 1 |  | 261 | use base 'Error::Generic'; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 651 |  | 
| 166 |  |  |  |  |  |  |  | 
| 167 |  |  |  |  |  |  | sub new { | 
| 168 | 0 |  |  | 0 |  |  | my $class = shift; | 
| 169 | 0 |  |  |  |  |  | my $text = shift; | 
| 170 | 0 |  |  |  |  |  | chomp $text; | 
| 171 |  |  |  |  |  |  |  | 
| 172 | 0 |  |  |  |  |  | my @args; | 
| 173 | 0 | 0 |  |  |  |  | @args = ( -file => $1, -line => $2) | 
| 174 |  |  |  |  |  |  | if($text =~ s/ at (\S+) line (\d+)([.\n]+)?$//s); | 
| 175 |  |  |  |  |  |  |  | 
| 176 | 0 |  |  |  |  |  | __PACKAGE__->SUPER::new(-text => $text, -value => $text, @args); | 
| 177 |  |  |  |  |  |  | } | 
| 178 |  |  |  |  |  |  |  | 
| 179 | 0 |  |  | 0 |  |  | sub stringify { $_[0]->text . " at " . $_[0]->file . " line " . $_[0]->line . ".\n" } | 
| 180 |  |  |  |  |  |  |  | 
| 181 |  |  |  |  |  |  | 1; | 
| 182 |  |  |  |  |  |  | __END__ |