File Coverage

blib/lib/Zoidberg/DispatchTable.pm
Criterion Covered Total %
statement 124 158 78.4
branch 45 66 68.1
condition 4 8 50.0
subroutine 15 20 75.0
pod 6 8 75.0
total 194 260 74.6


line stmt bran cond sub pod time code
1             package Zoidberg::DispatchTable;
2              
3             our $VERSION = '0.981';
4              
5 20     20   29699 use strict;
  20         28  
  20         718  
6 20     20   2642 use Zoidberg::Utils qw/debug bug error/;
  20         44  
  20         329  
7 20     20   2856 use Exporter::Tidy all => [qw/stack wipe tag tags/];
  20         41  
  20         215  
8              
9             our $ERROR_CALLER = 1;
10              
11             # reserved keys _AUTOLOAD and _META
12              
13             # $self->[0] hash with arrays of dispatch strings/refs
14             # $self->[1] hash with arrays of tags
15             # $self->[2] object ref
16             # $self->[3] object can parent bit
17             # $self->[4] array with keys to keep them in order
18             # $self->[5] iteration index for keys()
19              
20             # keys are kept in order to avoid inconsistencies
21             # for example when iterating trough {parser}
22              
23             sub new { # create a blessed AND tie'ed hash
24 49     49 0 938 my $class = shift;
25 49         67 my %hash;
26 49         186 tie %hash, $class, @_;
27 49         696 bless \%hash, $class;
28             }
29              
30             sub TIEHASH {
31 85     85   568 my $class = shift;
32 85   33     269 my $ref = shift || error 'need object ref to tie hash';
33             # $ref is either array ref or object ref
34 85 50       812 my $self = (ref($ref) eq 'ARRAY')
35             ? $ref
36             : [{}, {}, $ref, $ref->can('parent'), [], 0];
37 85         344 bless $self, $class;
38 85         330 while (my $hash = shift @_) {
39 67         448 $self->STORE($_, $$hash{$_}) for keys %$hash;
40             }
41 85         314 return $self;
42             }
43              
44             sub STORE {
45 897     897   5741 my ($self, $key, $value) = @_;
46 897         1161 my $tag = 'undef';
47 897 100       4160 ($value, $tag) = @$value if ref($value) eq 'ARRAY';
48              
49 897         1522 my $t = ref $value;
50 897 100       2282 if ($t eq 'HASH') {
    100          
    50          
51 34 50       172 unless (tied $value) { # recurs tie'ing
52 34         3409 tie %$value, __PACKAGE__, $$self[2], $value;
53             # be careful to reuse same ref - else perl bugs :(
54             }
55             # else just store the tied hash
56             }
57             elsif (! $t) {
58 782         14600 $value =~ s/(^\s*|\s*$)//g;
59 782 50 33     4614 error "Can't use ==>$value<== as subroutine."
60             if ! length $value
61             or $value =~ /^\$/; # no vars
62             }
63 0         0 elsif ($t ne 'CODE') { bug "Can't store ref of type $t in DispatchTable" }
64              
65 897         1114 push @{$self->[0]{$key}}, $value;
  897         3354  
66 897         1076 push @{$self->[1]{$key}}, $tag;
  897         2267  
67 897         976 push @{$self->[4]}, $key;
  897         4951  
68             }
69              
70             sub add {
71 0     0 1 0 my $self = tied %{ shift() };
  0         0  
72 0         0 $self->STORE(@_);
73             }
74              
75             sub FETCH {
76 331     331   3039 my ($self, $key) = @_;
77 331 100 100     2157 if ( exists $$self[0]{$key} and scalar @{$$self[0]{$key}} ) {
  207 100       1050  
78 207 100       979 $$self[0]{$key}[-1] = $self->convert($self->[0]{$key}[-1])
79             unless ref $self->[0]{$key}[-1];
80 207         3853 return $self->[0]{$key}[-1];
81             }
82             elsif ($self->EXISTS('_AUTOLOAD')) {
83 32         53 my $sub;
84 32         67 for (@{$self->[0]{_AUTOLOAD}}) {
  32         132  
85 32         302 $sub = $_->($key);
86 32 50       200 next unless $sub;
87 0 0       0 $self->STORE($key, $sub) unless $self->EXISTS($key);
88 0         0 return $self->FETCH($key);
89             }
90             }
91 124         2378 return undef;
92             }
93              
94             sub convert {
95 38     38 0 114 my ($self, $ding) = @_;
96              
97 38 50       244 if ($ding =~ /^\s*sub\s*{.*}\s*$/) { # undocumented hack
98 0         0 debug "going to eval: $ding";
99 0         0 my $closure = eval $ding;
100 0 0       0 die if $@;
101 0     0   0 return sub { $closure->($$self[2], @_) };
  0         0  
102             }
103              
104 38         412 $ding =~ s#^->((\w+)->)?#
105 35 100       485 ( $self->[3] ? q/parent->/ : '' ) .
    100          
106             ( $1 ? qq/{objects}{$2}->/ : '' )
107             #e;
108              
109 38 50       378 if ($ding =~ /\(\s*\)$/s) { $ding =~ s/\s*\)$/\@_\)/ }
  0 100       0  
110 6         28 elsif ($ding =~ /\(.*\)$/s) { $ding =~ s/\)$/, \@_\)/ }
111 32         99 else { $ding .= '(@_)' }
112              
113 38         342 debug "going to eval: sub { \$self->[2]->$ding }";
114 38         5421 my $sub = eval "sub { \$\$self[2]->$ding }";
115 38 50       164 die if $@;
116 38         209 return $sub;
117             }
118              
119 1376014 100   1376014   20267883 sub EXISTS { exists $_[0][0]->{$_[1]} and scalar @{$_[0][0]->{$_[1]}} }
  506         7027  
120              
121             sub DELETE { # doesn't really delete, merely pops
122 1     1   1480 my ($self, $key) = @_;
123 1 50       7 return undef unless exists $self->[0]{$key};
124              
125 1         2 pop @{$self->[1]{$key}};
  1         4  
126 1         3 my $re = pop @{$self->[0]{$key}};
  1         3  
127            
128 1 50       3 unless (scalar @{$self->[0]{$key}}) {
  1         6  
129 0         0 delete $self->[0]{$key};
130 0         0 delete $self->[1]{$key};
131 0         0 @{$self->[4]} = grep {$_ ne $key} @{$self->[4]};
  0         0  
  0         0  
  0         0  
132             }
133            
134 1         4 return $re;
135             }
136              
137             sub pop {
138 0     0 1 0 my $self = tied %{ shift() };
  0         0  
139 0         0 $self->DELETE(@_);
140             }
141              
142             sub CLEAR {
143 1     1   759 %{$_[0][0]} = ();
  1         31  
144 1         2 %{$_[0][1]} = ();
  1         6  
145 1         1 @{$_[0][4]} = ();
  1         5  
146 1         7 $_[0][5] = 0;
147             }
148              
149             sub FIRSTKEY {
150 2     2   12 $_[0][5] = 0;
151 2         10 goto \&NEXTKEY
152             }
153              
154             sub NEXTKEY {
155 8     8   12 my $self = shift;
156 8 100       13 if ($$self[5] > $#{$$self[4]}) {
  8 50       27  
157 2         4 $$self[5] = 0;
158 2 50       13 return wantarray ? () : undef;
159             }
160             elsif (wantarray) { # ($key, $value) = each(%table)
161 0         0 my $key = $$self[4][$$self[5]++];
162 0         0 return $key, $self->FETCH($key);
163             }
164 6         32 else { return $self->[4][$$self[5]++] } # for $key (keys %table)
165             }
166              
167             sub stack {
168 827     827 1 4311 my $self = tied %{ shift() };
  827         2480  
169 827         6543 my ($key, $use_tag) = @_;
170 827 100       5957 return () unless exists $$self[0]{$key};
171 313 100       689 for (@{$self->[0]{$key}}) { $_ = $self->convert($_) unless ref $_ }
  313         1464  
  331         2643  
172 313 100       1113 return map [ $$self[0]{$key}[$_], $$self[1]{$key}[$_] ], (0..$#{$$self[0]{$key}})
  1         13  
173             if $use_tag;
174 312         548 return @{$self->[0]{$key}};
  312         1964  
175             }
176              
177             sub tag {
178 0     0 1 0 my $self = tied %{ shift() };
  0         0  
179 0         0 my $key = shift;
180 0 0       0 return undef unless exists $$self[1]{$key};
181 0         0 return $$self[1]{$key}[-1];
182             }
183              
184             sub tags {
185 0     0 1 0 my $self = tied %{ shift() };
  0         0  
186 0         0 my $key = shift;
187 0 0       0 return undef unless exists $$self[1]{$key};
188 0         0 return @{$self->[1]{$key}};
  0         0  
189             }
190              
191             sub wipe {
192 1     1 1 10 my $self = tied %{ shift() };
  1         4  
193 1         3 my ($tag, @keys) = @_;
194 1 50       4 @keys = keys %{$self->[0]} unless scalar @keys;
  1         7  
195 1         4 my %old;
196 1         3 for my $key (@keys) {
197 5         11 for (my $i = 0; $i < @{$self->[1]{$key}}; $i++) {
  10         34  
198 5 100       20 next unless $self->[1]{$key}[$i] eq $tag;
199 2         10 $old{$key} = [$self->[0]{$key}[$i], $tag];
200 2         5 $self->[0]{$key}[$i] = undef;
201 2         7 $self->[1]{$key}[$i] = undef;
202             }
203 5         8 @{$self->[0]{$key}} = grep {defined $_} @{$self->[0]{$key}};
  5         16  
  5         18  
  5         13  
204 5         8 @{$self->[1]{$key}} = grep {defined $_} @{$self->[1]{$key}};
  5         12  
  5         12  
  5         12  
205 5 100       7 unless (scalar @{$self->[0]{$key}}) {
  5         17  
206 2         6 delete $self->[0]{$key};
207 2         3 delete $self->[1]{$key};
208 2         4 @{$self->[4]} = grep {$_ ne $key} @{$self->[4]};
  2         10  
  9         17  
  2         5  
209             }
210             }
211 1         6 return \%old;
212             }
213              
214             1;
215              
216             __END__