File Coverage

blib/lib/Error/TryCatch.pm
Criterion Covered Total %
statement 40 96 41.6
branch 2 34 5.8
condition 0 3 0.0
subroutine 12 20 60.0
pod 0 1 0.0
total 54 154 35.0


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__