File Coverage

blib/lib/String/Divert.pm
Criterion Covered Total %
statement 221 307 71.9
branch 66 148 44.5
condition 21 45 46.6
subroutine 31 40 77.5
pod 19 19 100.0
total 358 559 64.0


line stmt bran cond sub pod time code
1             ##
2             ## String::Divert - String Object supporting Folding and Diversion
3             ## Copyright (c) 2003-2005 Ralf S. Engelschall
4             ##
5             ## This file is part of String::Divert, a Perl module providing
6             ## a string object supporting folding and diversion.
7             ##
8             ## This program is free software; you can redistribute it and/or
9             ## modify it under the terms of the GNU General Public License
10             ## as published by the Free Software Foundation; either version
11             ## 2.0 of the License, or (at your option) any later version.
12             ##
13             ## This program is distributed in the hope that it will be useful,
14             ## but WITHOUT ANY WARRANTY; without even the implied warranty of
15             ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16             ## General Public License for more details.
17             ##
18             ## You should have received a copy of the GNU General Public License
19             ## along with this file; if not, write to the Free Software Foundation,
20             ## Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
21             ##
22             ## Divert.pm: Module Implementation
23             ##
24              
25             # _________________________________________________________________________
26             #
27             # STANDARD OBJECT ORIENTED API
28             # _________________________________________________________________________
29             #
30              
31             package String::Divert;
32              
33 1     1   22156 use 5.006;
  1         3  
  1         36  
34 1     1   12 use strict;
  1         3  
  1         36  
35 1     1   5 use warnings;
  1         5  
  1         42  
36              
37 1     1   6 use Carp;
  1         2  
  1         3741  
38             require Exporter;
39              
40             our $VERSION = '0.96';
41              
42             our @ISA = qw(Exporter);
43             our @EXPORT = qw();
44             our @EXPORT_OK = qw();
45              
46             # internal: create an anonymous object name
47             my $_anonymous_count = 1;
48             sub _anonymous_name () {
49 2     2   12 return sprintf("ANONYMOUS:%d", $_anonymous_count++);
50             }
51              
52             # object construction
53             sub new ($;$) {
54 7     7 1 29 my ($proto, $name) = @_;
55              
56 7   66     28 my $class = ref($proto) || $proto;
57 7         17 my $self = {};
58 7         18 bless ($self, $class);
59              
60 7   66     23 $name ||= &String::Divert::_anonymous_name();
61              
62 7         22 $self->{name} = $name; # name of object
63 7         16 $self->{overwrite} = 'none'; # overwrite mode (none|once|always)
64 7         20 $self->{storage} = 'all'; # storage mode (none|fold|all)
65 7         13 $self->{copying} = 'pass'; # copying mode (pass|clone)
66 7         18 $self->{chunks} = []; # string chunks
67 7         13 $self->{diversion} = []; # stack of active diversions
68 7         14 $self->{foldermk} = '{#%s#}'; # folder text representation format
69 7         18 $self->{folderre} = '\{#([a-zA-Z_][a-zA-Z0-9_]*)#\}'; # folder text representation regexp
70 7         14 $self->{folderlst} = undef; # folder object of last folding operation
71              
72 7         17 return $self;
73             }
74              
75             # object destruction (explicit)
76             sub destroy ($) {
77 1     1 1 369 $_[0]->overload(0);
78 1         3 bless $_[0], 'UNIVERSAL';
79 1         2 undef $_[0];
80 1         10 return;
81             }
82              
83             # object destruction (implicit)
84             sub DESTROY ($) {
85 7     7   349 $_[0]->overload(0);
86 7         15 bless $_[0], 'UNIVERSAL';
87 7         347 return;
88             }
89              
90             # clone object
91             sub clone ($) {
92 1     1 1 2 my ($self) = @_;
93 1         3 my $ov = $self->overload();
94 1         11 $self->overload(0);
95 1         2 eval { require Storable; };
  1         1715  
96 1 50       4577 croak "required module \"Storable\" not installed" if ($@);
97 1         149 my $clone = Storable::dclone($self);
98 1         7 $self->overload($ov);
99 1         3 $clone->overload($ov);
100 1         5 return $clone;
101             }
102              
103             # operation: set/get name of object
104             sub name ($;$) {
105 14     14 1 289 my ($self, $name) = @_;
106 14         43 return $self->{diversion}->[-1]->name($name)
107 14 50       18 if (@{$self->{diversion}} > 0);
108 14         27 my $old_name = $self->{name};
109 14 100       29 if (defined($name)) {
110 2         4 $self->{name} = $name;
111             }
112 14         56 return $old_name;
113             }
114              
115             # operation: set/get overwrite mode
116             sub overwrite ($;$) {
117 3     3 1 7 my ($self, $mode) = @_;
118 3         10 return $self->{diversion}->[-1]->overwrite($mode)
119 3 50       4 if (@{$self->{diversion}} > 0);
120 3         8 my $old_mode = $self->{overwrite};
121 3 50       8 if (defined($mode)) {
122 3 50       15 croak "invalid overwrite mode argument"
123             if ($mode !~ m/^(none|once|always)$/);
124 3         9 $self->{overwrite} = $mode;
125             }
126 3         7 return $old_mode;
127             }
128              
129             # operation: set/get storage mode
130             sub storage ($;$) {
131 0     0 1 0 my ($self, $mode) = @_;
132 0         0 return $self->{diversion}->[-1]->storage($mode)
133 0 0       0 if (@{$self->{diversion}} > 0);
134 0         0 my $old_mode = $self->{storage};
135 0 0       0 if (defined($mode)) {
136 0 0       0 croak "invalid storage mode argument"
137             if ($mode !~ m/^(none|fold|all)$/);
138 0         0 $self->{storage} = $mode;
139             }
140 0         0 return $old_mode;
141             }
142              
143             # operation: set/get copy constructor mode
144             sub copying ($;$) {
145 0     0 1 0 my ($self, $mode) = @_;
146 0         0 return $self->{diversion}->[-1]->copying($mode)
147 0 0       0 if (@{$self->{diversion}} > 0);
148 0         0 my $old_mode = $self->{copying};
149 0 0       0 if (defined($mode)) {
150 0 0       0 croak "invalid copying mode argument"
151             if ($mode !~ m/^(clone|pass)$/);
152 0         0 $self->{copying} = $mode;
153             }
154 0         0 return $old_mode;
155             }
156              
157             # internal: split string into chunks
158             sub _chunking ($$) {
159 29     29   44 my ($self, $string) = @_;
160 29         42 my @chunks = ();
161 29         46 my $folderre = $self->{folderre};
162 29         197 while ($string =~ m/${folderre}()/s) {
163 0         0 my ($prolog, $id) = ($`, $1);
164 0 0 0     0 push(@chunks, $prolog) if ($prolog ne '' and $self->{storage} !~ m/^(none|fold)/);
165 0 0       0 croak "empty folding object name"
166             if ($id eq '');
167 0 0       0 if ($self->{storage} ne 'none') {
168 0         0 my $object = $self->folding($id);
169 0 0       0 $object = $self->new($id) if (not defined($object));
170 0 0       0 croak "cannot create new folding sub object \"$id\""
171             if (not defined($object));
172 0         0 push(@chunks, $object);
173             }
174 0         0 $string = $';
175             }
176 29 50 33     168 push(@chunks, $string) if ($string ne '' and $self->{storage} !~ m/^(none|fold)/);
177 29         83 return @chunks;
178             }
179              
180             # operation: assign an object
181             sub assign ($$) {
182 11     11 1 1036 my ($self, $obj) = @_;
183 11         123 return $self->{diversion}->[-1]->assign($obj)
184 11 50       11 if (@{$self->{diversion}} > 0);
185 11 50       26 croak "cannot assign undefined object"
186             if (not defined($obj));
187 11 50       26 if (&String::Divert::_isobj($obj)) {
188 0         0 $self->{chunks} = [ $obj ];
189 0 0       0 $self->{folderlst} = $obj if (ref($obj));
190             }
191             else {
192 11         25 $self->{chunks} = [];
193 11         39 foreach my $chunk ($self->_chunking($obj)) {
194 11         16 push(@{$self->{chunks}}, $chunk);
  11         27  
195 11 50       53 $self->{folderlst} = $chunk if (ref($chunk));
196             }
197             }
198 11         26 return $self;
199             }
200              
201             # operation: append an object
202             sub append ($$) {
203 25     25 1 336 my ($self, $obj) = @_;
204 25         98 return $self->{diversion}->[-1]->append($obj)
205 25 100       27 if (@{$self->{diversion}} > 0);
206 21 50       50 croak "cannot append undefined object"
207             if (not defined($obj));
208 21 100 100     160 if ( $self->{overwrite} eq 'once'
209             or $self->{overwrite} eq 'always') {
210 3         7 $self->assign($obj);
211 3 100       14 $self->{overwrite} = 'none'
212             if ($self->{overwrite} eq 'once');
213             }
214             else {
215 18 50       35 if (&String::Divert::_isobj($obj)) {
216 0         0 push(@{$self->{chunks}}, $obj);
  0         0  
217 0 0       0 $self->{folderlst} = $obj if (ref($obj));
218             }
219             else {
220 18         43 foreach my $chunk ($self->_chunking($obj)) {
221 18 100 100     53 if (ref($chunk) or (@{$self->{chunks}} > 0 and ref($self->{chunks}->[-1]))) {
  18 100 33     92  
  12         58  
222 6         10 push(@{$self->{chunks}}, $chunk);
  6         10  
223 6 50       27 $self->{folderlst} = $chunk if (ref($chunk));
224             }
225             elsif (@{$self->{chunks}} > 0) {
226 6         26 $self->{chunks}->[-1] .= $chunk;
227             }
228             else {
229 6         27 $self->{chunks} = [ $chunk ];
230             }
231             }
232             }
233             }
234 21         50 return $self;
235             }
236              
237             # operation: unfold (and return) string contents temporarily
238             sub string ($) {
239 27     27 1 1134 my ($self) = @_;
240 27         90 return $self->{diversion}->[-1]->string()
241 27 100       59 if (@{$self->{diversion}} > 0);
242 25         75 return $self->_string([]);
243             }
244              
245             # internal: string() operation with loop detection
246             sub _string ($$) {
247 45     45   67 my ($self, $visit) = @_;
248 45         112 my $string = '';
249 45 50       51 if (grep { &String::Divert::_isobjeq($_, $self) } @{$visit}) {
  27         50  
  45         136  
250 0         0 croak "folding loop detected: " .
251 0         0 join(" -> ", map { $_->name() } @{$visit}) .
  0         0  
252             " -> " . $self->name();
253             }
254 45         64 push(@{$visit}, $self);
  45         963  
255 45         59 foreach my $chunk (@{$self->{chunks}}) {
  45         176  
256 80 100       141 if (ref($chunk)) {
257             # folding loop detection
258 20         32 my $prefix = '';
259             # check for existing prefix
260             # (keep in mind that m|([^\n]+)$|s _DOES NOT_
261             # take a possibly existing terminating newline
262             # into account, so we really need an extra match!)
263 20 50 33     200 if ($string =~ m|([^\n]+)$|s and $string !~ m|\n$|s) {
264 20         47 $prefix = $1;
265 20         125 $prefix =~ s|[^ \t]| |sg;
266             }
267 20         69 my $block = $chunk->_string($visit); # recursion!
268 20 50       65 $block =~ s|\n(?=.)|\n$prefix|sg if ($prefix ne '');
269 20         45 $string .= $block;
270             }
271             else {
272 60         142 $string .= $chunk;
273             }
274             }
275 45         68 pop(@{$visit});
  45         64  
276 45         430 return $string;
277             }
278              
279             # operation: unfold string contents temporarily until already true or finally false
280             sub bool ($) {
281 0     0 1 0 my ($self) = @_;
282 0         0 return $self->{diversion}->[-1]->bool()
283 0 0       0 if (@{$self->{diversion}} > 0);
284 0         0 my $string = '';
285 0         0 foreach my $chunk (@{$self->{chunks}}) {
  0         0  
286 0 0       0 if (ref($chunk)) {
287 0         0 $string .= $chunk->string(); # recursion!
288             }
289             else {
290 0         0 $string .= $chunk;
291             }
292 0 0       0 return 1 if ($string);
293             }
294 0         0 return 0;
295             }
296              
297             # operation: append folding sub-object
298             sub fold ($;$) {
299 7     7 1 22 my ($self, $id) = @_;
300 7         29 return $self->{diversion}->[-1]->fold($id)
301 7 100       10 if (@{$self->{diversion}} > 0);
302 6 50       21 return undef if ($self->{storage} eq 'none');
303 6 50       14 $id = &String::Divert::_anonymous_name()
304             if (not defined($id));
305 6 50       13 if (ref($id)) {
306 0 0       0 croak "folding object not of class String::Divert"
307             if (not &String::Divert::_isobj($id));
308 0         0 push(@{$self->{chunks}}, $id);
  0         0  
309 0         0 $self->{folderlst} = $id;
310 0         0 return $id;
311             }
312             else {
313 6         22 my $object = $self->folding($id);
314 6 100       27 $object = $self->new($id) if (not defined($object));
315 6 50       19 croak "unable to create new folding object"
316             if (not defined($object));
317 6         10 push(@{$self->{chunks}}, $object);
  6         13  
318 6         10 $self->{folderlst} = $object;
319 6         19 return $object;
320             }
321             }
322              
323             # operation: unfold string contents permanently
324             sub unfold ($) {
325 0     0 1 0 my ($self) = @_;
326 0         0 return $self->{diversion}->[-1]->unfold()
327 0 0       0 if (@{$self->{diversion}} > 0);
328 0         0 my $string = $self->string();
329 0 0       0 $self->{chunks} = $string ne '' ? [ $string ] : [];
330 0         0 $self->{folderlst} = undef;
331 0         0 return $string;
332             }
333              
334             # internal: check whether object is a String::Divert object
335             sub _isobj ($) {
336 29     29   45 my ($obj) = @_;
337 29   33     116 return ( ref($obj)
338             and ( UNIVERSAL::isa($obj, "String::Divert")
339             or UNIVERSAL::isa($obj, "String::Divert::__OVERLOAD__")));
340             }
341              
342             # internal: compare whether two objects are the same
343             sub _isobjeq ($$) {
344 27     27   66 my ($obj1, $obj2) = @_;
345 27         120 my $ov1 = $obj1->overload();
346 27         52 my $ov2 = $obj2->overload();
347 27         56 $obj1->overload(0);
348 27         51 $obj2->overload(0);
349 27         101 my $rv = ($obj1 == $obj2);
350 27         260 $obj1->overload($ov1);
351 27         52 $obj2->overload($ov2);
352 27         277 return $rv;
353             }
354              
355             # operation: lookup particular or all folding sub-object(s)
356             sub folding ($;$) {
357 20     20 1 38 my ($self, $id) = @_;
358 20 100       40 if (defined($id)) {
359 16         21 my $folding; $folding = undef;
  16         19  
360 16         20 foreach my $chunk (@{$self->{chunks}}) {
  16         37  
361 27 100       68 if (ref($chunk)) {
362 11 100 33     76 if ( (ref($id) and &String::Divert::_isobjeq($chunk, $id))
      66        
      33        
363             or (not ref($id) and $chunk->name() eq $id) ) {
364 8         10 $folding = $chunk;
365 8         15 last;
366             }
367 3         12 $folding = $chunk->folding($id); # recursion!
368 3 50       10 last if (defined($folding));
369             }
370             }
371 16         44 return $folding;
372             }
373             else {
374 4         6 my @foldings = ();
375 4         19 foreach my $chunk (@{$self->{chunks}}) {
  4         11  
376 10 100       28 if (ref($chunk)) {
377 3         139 foreach my $subchunk ($chunk->folding()) {
378 2         5 push(@foldings, $subchunk);
379             }
380 3         7 push(@foldings, $chunk);
381             }
382             }
383 4         16 return @foldings;
384             }
385             }
386              
387             # operation: configure or generate textually represented folding object
388             sub folder ($;$$) {
389 1     1 1 3 my ($self, $a, $b) = @_;
390 1 50 33     8 if (defined($a) and defined($b)) {
391             # configure folder
392 1         8 my $test = sprintf($a, "foo");
393 1         59 my ($id) = ($test =~ m|${b}()|s);
394 1 50 33     95 croak "folder construction format and matching regular expression do not correspond"
      33        
395             if (not defined($id) or (defined($id) and $id ne "foo"));
396 1         3 $self->{foldermk} = $a;
397 1         2 $self->{folderre} = $b;
398 1         5 return;
399             }
400             else {
401             # create folder
402 0 0       0 return "" if ($self->{storage} eq 'none');
403 0 0       0 $a = &String::Divert::_anonymous_name()
404             if (not defined($a));
405 0         0 my $folder = sprintf($self->{foldermk}, $a);
406 0         0 return $folder;
407             }
408             }
409              
410             # operation: push diversion of operations to sub-object
411             sub divert ($;$) {
412 5     5 1 17 my ($self, $id) = @_;
413 5         6 my $object; $object = undef;
  5         7  
414 5 50       13 if (not defined($id)) {
415             # choose last folding object
416 0         0 foreach my $obj (reverse ($self, @{$self->{diversion}})) {
  0         0  
417 0         0 $object = $obj->{folderlst};
418 0 0       0 last if (defined($object));
419             }
420 0 0       0 croak "no last folding sub-object found"
421             if (not defined($object));
422             }
423             else {
424             # choose named folding object
425 5         12 $object = $self->folding($id);
426 5 50       14 croak "folding sub-object \"$id\" not found"
427             if (not defined($object));
428             }
429 5         8 push(@{$self->{diversion}}, $object);
  5         13  
430 5         11 return $self;
431             }
432              
433             # operation: pop diversion of operations to sub-object
434             sub undivert ($;$) {
435 4     4 1 290 my ($self, $num) = @_;
436 4 100       15 $num = 1 if (not defined($num));
437 4 50       23 if ($num !~ m|^\d+$|) {
438             # lookup number by name
439 0         0 my $name = $num;
440 0         0 for (my $num = 1; $num <= @{$self->{diversion}}; $num++) {
  0         0  
441 0 0       0 last if ($self->{diversion}->[-$num]->{name} eq $name);
442             }
443 0         0 croak "no object named \"$name\" found for undiversion"
444 0 0       0 if ($num > @{$self->{diversion}});
445             }
446 4 100       18 $num = @{$self->{diversion}} if ($num == 0);
  2         5  
447 0         0 croak "less number (".scalar(@{$self->{diversion}}).") of " .
  4         15  
448             "diversions active than requested ($num) to undivert"
449 4 50       7 if ($num > @{$self->{diversion}});
450 4         14 while ($num-- > 0) {
451 5         6 pop(@{$self->{diversion}});
  5         16  
452             }
453 4         9 return $self;
454             }
455              
456             # operation: lookup last or all diversion(s)
457             sub diversion ($) {
458 2     2 1 7 my ($self) = @_;
459 2 50       6 if (not wantarray) {
460             # return last diversion only (or undef if none exist)
461 0         0 return $self->{diversion}->[-1];
462             }
463             else {
464             # return all diversions (in reverse order of activation) (or empty array if none exist)
465 2         2 return reverse(@{$self->{diversion}});
  2         9  
466             }
467             }
468              
469             # _________________________________________________________________________
470             #
471             # API SWITCHING
472             # _________________________________________________________________________
473             #
474              
475             # object overloading toogle method
476             sub overload ($;$) {
477             # NOTICE: This function is special in that it exploits the fact
478             # that Perl's @_ contains just ALIASES for the arguments of
479             # the function and hence the function can adjust them. This
480             # allows us to tie() the variable of our object ($_[0]) into the
481             # overloading sub class or back to our main class. Just tie()ing
482             # a copy of $_[0] (usually named $self in the other methods)
483             # would be useless, because the Perl TIE mechanism is attached to
484             # _variables_ and not to the objects itself. Hence this function
485             # does no "my ($self, $mode) = @_;" and instead uses @_ directly
486             # throughout its body.
487 177 100   177 1 934 my $old_mode = (ref($_[0]) eq "String::Divert" ? 0 : 1);
488 177 100       477 if (defined($_[1])) {
489 120 100       276 if ($_[1]) {
490             # bless and tie into overloaded subclass
491 5         7 my $self = $_[0];
492 5         15 bless $_[0], "String::Divert::__OVERLOAD__";
493             #tie $_[0], "String::Divert::__OVERLOAD__", $self;
494             # according to "BUGS" section in "perldoc overload":
495             # "Relation between overloading and tie()ing is broken.
496             # Overloading is triggered or not basing on the previous
497             # class of tie()d value. This happens because the presence
498             # of overloading is checked too early, before any tie()d
499             # access is attempted. If the FETCH()ed class of the
500             # tie()d value does not change, a simple workaround is to
501             # access the value immediately after tie()ing, so that
502             # after this call the previous class coincides with the
503             # current one."... So, do this now!
504             #my $dummy = ref($_[0]);
505             }
506             else {
507             # untie and rebless into master class
508             #untie $_[0];
509 115         605 bless $_[0], "String::Divert";
510             }
511             }
512 177         254 return $old_mode;
513             }
514              
515             # _________________________________________________________________________
516             #
517             # OPERATOR OVERLOADING API
518             # _________________________________________________________________________
519             #
520              
521             package String::Divert::__OVERLOAD__;
522              
523             our @ISA = qw(Exporter String::Divert);
524             our @EXPORT = qw();
525             our @EXPORT_OK = qw();
526              
527             # define operator overloading
528             use overload (
529 1         14 '""' => \&op_string,
530             'bool' => \&op_bool,
531             '0+' => \&op_numeric,
532             '.' => \&op_concat,
533             '.=' => \&op_append,
534             '*=' => \&op_fold,
535             '<>' => \&op_unfold,
536             '>>' => \&op_divert,
537             '<<' => \&op_undivert,
538             '=' => \&op_copyconst,
539             #'${}' => \&op_deref_string,
540             #'%{}' => \&op_deref_hash,
541             #'nomethod' => \&op_unknown,
542             'fallback' => 0
543 1     1   2270 );
  1         1081  
544              
545             #sub TIESCALAR ($$) {
546             # my ($class, $self) = @_;
547             # bless $self, $class;
548             # return $self;
549             #}
550              
551             #sub UNTIE ($) {
552             # my ($self) = @_;
553             # return;
554             #}
555              
556             #sub FETCH ($) {
557             # my ($self) = @_;
558             # return $self;
559             #}
560              
561             #sub STORE ($$) {
562             # my ($self, $other) = @_;
563             # return $self if (ref($other));
564             # $self->assign($other);
565             # my $dummy = ref($self);
566             # return $self;
567             #}
568              
569             #sub op_deref_string ($$$) {
570             # my $self = shift;
571             # return $self;
572             #}
573              
574             #sub op_deref_hash ($$$) {
575             # my $self = shift;
576             # return $self;
577             #}
578              
579             sub op_copyconst {
580 0     0   0 my ($self, $other, $reverse) = @_;
581 0 0       0 if ($self->{copying} eq 'pass') {
582             # object is just passed-through
583 0         0 return $self;
584             }
585             else {
586             # object is recursively cloned
587 0         0 return $self->clone();
588             }
589             }
590              
591             sub op_string ($$$) {
592 5     5   19 my ($self, $other, $rev) = @_;
593 5         20 return $self->string();
594             }
595              
596             sub op_bool ($$$) {
597 0     0   0 my ($self, $other, $reverse) = @_;
598 0         0 return $self->bool();
599             }
600              
601             sub op_numeric ($$$) {
602 0     0   0 my ($self, $other, $reverse) = @_;
603 0         0 return $self->string();
604             }
605              
606             sub op_concat ($$$) {
607 0     0   0 my ($self, $other, $reverse) = @_;
608 0 0       0 return ($reverse ? $other . $self->string() : $self->string() . $other);
609             }
610              
611             sub op_append ($$$) {
612 3     3   14 my ($self, $other, $reverse) = @_;
613 3         18 $self->append($other);
614 3         9 return $self;
615             }
616              
617             sub op_fold ($$$) {
618 1     1   3 my ($self, $other, $reverse) = @_;
619 1         9 $self->fold($other);
620 1         4 return $self;
621             }
622              
623             sub op_unfold ($$$) {
624 0     0   0 my ($self, $other, $reverse) = @_;
625 0         0 $self->unfold;
626 0         0 return $self;
627             }
628              
629             #sub op_folding ($$$) {
630             # my ($self, $other, $reverse) = @_;
631             # $self->folding($other);
632             # return $self;
633             #}
634              
635             sub op_divert ($$$) {
636 1     1   4 my ($self, $other, $reverse) = @_;
637 1         10 $self->divert($other);
638 1         3 return $self;
639             }
640              
641             sub op_undivert ($$$) {
642 1     1   7 my ($self, $other, $reverse) = @_;
643 1         9 $self->undivert($other);
644 1         4 return $self;
645             }
646              
647             #sub op_diversion ($$$) {
648             # my ($self, $other, $reverse) = @_;
649             # $self->diversion();
650             # return $self;
651             #}
652              
653             #sub op_unknown ($$$$) {
654             # my ($self, $other, $rev, $op) = @_;
655             # print ": op=$op\n";
656             # return $self;
657             #}
658              
659             1;
660