File Coverage

blib/lib/Types/Core.pm
Criterion Covered Total %
statement 81 178 45.5
branch 29 140 20.7
condition 7 50 14.0
subroutine 27 43 62.7
pod 2 24 8.3
total 146 435 33.5


line stmt bran cond sub pod time code
1             #!/usr/bin/perl
2 5     5   310308 use warnings; use strict;
  5     5   46  
  5         171  
  5         27  
  5         11  
  5         192  
3             # vim=:SetNumberAndWidth
4             =encoding utf-8
5              
6             =head1 NAME
7              
8             Types::Core - Core types defined as tests and literals (ease of use)
9              
10             =head1 VERSION
11              
12             Version "0.3.2";
13              
14             =cut
15              
16              
17             ################################################################################
18             { package Types::Core;
19 5     5   26 use strict; use warnings;
  5     5   9  
  5         98  
  5         24  
  5         7  
  5         241  
20             our $VERSION='0.3.2';
21 5     5   2426 use mem;
  5         1413  
  5         55  
22 5     5   311 use constant Self => __PACKAGE__;
  5         10  
  5         1179  
23             ;
24             # 0.3.2 - EhV now same code as ErV (uses goto &ErV)
25             # - add TEST method:Exist-method-Value: EmV. Mostly same as
26             # ErV, but first test if blessed (class) ref. If so,
27             # test for field as a class method to be called. If not class
28             # method, try HASH+ARRAY ref. Since call to method may result
29             # in unpredictable execution time as it is calling a
30             # method function (if blessed ptr+method)
31             # only in EXPORT_OK at this point, not default EXPORT
32             # ... Need tests for this
33             # - in Cmp made sure numeric arg tst tested full field with '$' at end
34             # - some refactoring for Cmp around using pkgvars "$a" + "$b"
35             # - some doc changes around Cmp functionality
36             # 0.3.1 - store number constant regex in var before using
37             # 0.3.0 - NOTE: change in false value of blessed + type from
38             # "undef" to "" (0 length string)
39             # This was done to provide compatibility with "ref" when
40             # it is used on a non-reference or on undef. Logic _seems_
41             # to be that a non-ref, untyped or unblessed value is
42             # empty -- not undefined. While this is arbitrary, that
43             # "ref" returns an empty string, establishes precedent.
44             # The same logic would not apply using 'length' on an
45             # "undef" value, as "undef" would likely be considered
46             # dimensionless and therefore would have no length.
47             # classname for 'undef'.
48             # - Finished test 't06' that exercises 'Cmp' and made sure
49             # Cmp is working (as per test). Will fix problems as found.
50             # 0.2.8 - isnum was broken
51             # - ErV+EhV didn't argcheck undefs. Hmph!
52             # 0.2.7 - EhV didn't properly test a blessed HASH (but ErV did)
53             # - Added tests for both in t00.t and fixed code
54             # 0.2.6 - Removed another spurious ref, this time to Carp::Always.
55             # 0.2.5 - Removed spurious reference to unneeded module in t/t00.t.
56             # No other source changes.
57             # 0.2.4 - fixed current tests in 5.{12,10,8}.x; added some tests for
58             # Cmp function to allow comparing objects and sorting them
59             # though still leaving it undocumented, as not sure how
60             # useful it is
61             # 0.2.2 - fixed prototype of isnum, tighted up interface and documented it
62             # - fix some test suit failures under older (<5.12) perls
63             # 0.2.1 - re-add 'type' as OK synonym for 'typ' (both in EXPORT_OK)
64             # 0.2.0 - Allow isnum to take inferred $_ as param.
65             # 0.1.10 - Added Cmp function for nested data structs
66             # 0.1.9 Features:
67             # - add ErV supercedes EhV, but also works for arrays (EXPORT)
68             # - change EhV proto to support multiply nested refs.
69             # - add 'LongFunc' & 'ShortFunc' for name of current function either
70             # with Package(Long) or without(Short); (EXPORT_OK)
71             # - Add 'mk_array/mkARRAY' + 'mk_hash/mkHASH' optional exports
72             # Fixes:
73             # - delete unused sub referencing 'B'
74             # - PerlDoc updates
75             # 0.1.8 - (fix) remove reference created during development, but not needed
76             # - in final version, in t04.t in the testing directory
77             # 0.1.7 - Attempt to fix a parsing problem in 5.8.9
78             # 0.1.6 - Needed to split a statement that parsed in a different order under
79             # 5.8.x
80             # 0.1.5 - Added code and test case to handle type-named classes
81             # - use Scalar::Utils for blessed and 'ref' if available.
82             # 0.1.4 - Add BUILD_REQ for more modern Ext:MM
83             # 0.1.3 - investigate fails on perl 5.12.x:
84             # - changed prototypes on single arg tests to use '$' instead of '*';
85             # - changed test to use parens around unary ops (needed in 5.12 & before)
86             # - tested back to 5.8.9 ( & remove version restriction "use 5.12").
87             # - added tests for CODE & REF
88             # - clarified true/false returned values
89             # 0.1.2 - Write tests to verify solo string equality, returning $var on true,
90             # capturing undef and returning false;
91             # - doc updates
92             # 0.1.1 - move to using Xporter so EXPORT_OK works w/o deleting defaults
93             # - narrow focus of module -- Default to: Basic types, EhV &
94             # possible addon of "blessed", "typ"
95             # 0.1.0 - regularized some naming (Type->type cf. ref; Ref->ref, cf ref) in
96             # function names; modularized/functionized type checks
97             # - Made previous True/False values return the original value for True
98             # 0.0.6 - Add ability to use Scalar::Util 'reftype' to determine which
99             # of the base types something is (sans classes). Fall back
100             # to pattern matching if it isn't available.
101             # - Add IO & GLOB to fill out basic type representation
102             # - remove obsolute function calls prior to publishing;
103             # 0.0.5 - Added type_check function
104             # 0.0.4 - add RefInit
105             # 0.0.3 - add SCALAR test
106             # - code simplification
107             # 0.0.2 - Export EhV by default
108             #}}}
109              
110             # MAINT NOTE: this module must not "use P" (literally _use_)
111             # as "P" needs to use this mod (or dup the functionality)
112              
113             our (@CORETYPES, @EXPORT, @EXPORT_OK, %type_lens);
114              
115             BEGIN {
116 5     5   25 @CORETYPES = qw(ARRAY CODE GLOB HASH IO REF SCALAR);
117 5         13 %type_lens = map { ($_, length $_ ) } @CORETYPES;
  35         95  
118 5         19 @EXPORT = (@CORETYPES, qw(ErV EhV));
119 5         19 @EXPORT_OK = ( qw( typ type
120             blessed EmV
121             LongSub ShortSub
122             isnum Cmp
123             InClass IsClass
124             Obj EhV
125             mk_array mkARRAY
126             mk_hash mkHASH
127             ) );
128 5     5   2444 use Xporter;
  5         10074  
  5         23  
129              
130             sub InClass(*;$) {
131 0     0 0 0 my $class = shift;
132             @_ ? ref $_[0] eq $class
133             : sub (;*) {
134 0 0   0   0 @_ ? $class eq ref $_[0]
135 0 0       0 : $class } }
136             # sub InClass(*;$) {
137             # my $class = shift;
138             # if (!@_) { return sub (;*) { ref $_[0] eq $class } }
139             # else { return ref $_[0] eq $class }
140             # }
141             #
142 0     0 0 0 sub IsClass(*;$) { goto &InClass }
143            
144 5     5   801 use constant shortest_type => 'REF';
  5         12  
  5         404  
145 5     5   32 use constant last_let_offset => length(shortest_type)-1;
  5         10  
  5         303  
146 5     5   41 use constant Empty => "";
  5         10  
  5         521  
147              
148 5         1568 eval '# line ' . __LINE__ .' '. __FILE__ .'
149 6 50   6   14 sub _type ($) {
150 6         15 return Empty unless defined $_[0];
151 6 50       14 my $end = index $_[0], "(";
152 6         11 return Empty unless $end > '. &last_let_offset .';
153 6         76 my $start = 1+rindex($_[0], "=", $end);
154             substr $_[0], $start, $end-$start;
155             }
156            
157 20     20   41 sub _isatype($$) {
158 20 100 66     158 my ($var, $type) = @_;
159             ref $var && (1 + index($var, $type."(" )) ? $var : Empty;
160             }
161 10     10 0 2540 sub blessed ($) {
162 10 50       34 my $arg = $_[0];
163 10         21 return Empty unless defined $arg;
164 10 50       24 my $ref_arg = ref $arg;
165 10 100 66     73 return Empty unless $ref_arg;
166 6         12 ($ref_arg && !$type_lens{$ref_arg}) ? $arg : do {
167 6 100       69 my $len = $type_lens{$ref_arg};
168             $ref_arg."=" eq substr($arg, 0, $len+1) ? $arg : Empty };
169             }
170             '; #end of eval
171 5 50       908 $@ && die "_isatype+blessed eval(2): $@";
172              
173             }
174              
175 20     20 0 69 sub isatype($$) {goto &_isatype}
176 6     6 0 869 sub typ($) {goto &_type}
177 0     0 0 0 sub type($) {goto &_type}
178              
179              
180            
181             =head1 SYNOPSIS
182              
183              
184             my @data_types = (ARRAY CODE GLOB HASH IO REF SCALAR);
185             my $ref = $_[0];
186             P "Error: expected %s", HASH unless HASH $ref;
187              
188             Syntax symplifier for type checking.
189              
190             Allows easy, unquoted use of var types (ARRAY, SCALAR, etc.)
191             as literals, and allows standard type names to be used as boolean
192             checks of the type of a reference as well as passing through the value
193             of the reference. For example: C will return true
194             if the reference points to a HASH or a HASH-based object.
195             For example, "HASH $href"
196             check routines of references.
197              
198              
199             =head1 USAGE
200              
201             =over
202              
203             B>> - Check if I has underlying type, I
204              
205             B> - Literal usage equal to itself
206              
207              
208             =back
209              
210             =head1 EXAMPLE
211              
212             printf "type = %s\n", HASH if HASH $var;
213              
214             Same as:
215              
216             printf "type = %s\n", 'HASH' if ref $var eq 'HASH';)
217              
218             =head1 DESCRIPTION
219              
220             For the most basic functions listed in the Synopsis, they take
221             either 0 or 1 arguments. If 1 parameter, then they test it
222             to see if the C is of the given I (blessed or not).
223             If false, I> is returned, of true, the ref, itself is returned.
224              
225             For no args, they return literals of themselves, allowing the
226             named strings to be used as Literals w/o quotes.
227              
228             =head1 MORE EXAMPLES
229              
230             =head4 Initialization
231              
232             our %field_types = (Paths{type => ARRAY, ...});
233              
234             =head4 Flow Routing
235              
236             ...
237             my $ref_arg = ref $arg;
238             return ARRAY $ref_arg ? StatAR_2_Ino_t($path,$arg) :
239             InClass(Stat_t, $ref_arg) ? Stat_t_2_Ino_t($path, $arg) :
240             _path_2_Ino_t($path); }
241              
242             =head4 Create Class function as constant and to test membership
243              
244             ...
245             sub Stat(;*); # needed for prototype
246             local * Stat = InClass Stat;
247             my $p=bless [], Task;
248             P "p isa %s", Task if Task $p;
249              
250             =head4 Data Verification
251              
252             sub Type_check($;$) { ...
253             if (ARRAY $cfp) {
254             for (@$cfp) {
255             die P "Field %s does not exist", $_ unless exists $v->{$_};
256             my $cls_ftpp = $class."::field_types";
257             if (HASH $cls_ftpp) {
258             if ($cls_ftpp->{type} eq ARRAY) { ...
259              
260             =head4 Param Checking
261              
262             sub popable (+) {
263             my $ar = $_[0];
264             ARRAY $ar or die P "popable only works with arrays, not %s", ref $ar; }
265              
266             =head4 Return Value Checks and Dereference Protection
267              
268             my $Inos = $mp->get_sorted_Ino_t_Array;
269             return undef unless ARRAY $Inos and @$Inos >= 2;
270              
271             =cut
272              
273              
274              
275              
276             BEGIN { # create the type functions...
277 9 100   9 0 346 eval '# line ' . __LINE__ .' '. __FILE__ .'
  4 100   4 0 99  
  1 50   1 0 5  
  14 100   14 0 2428  
  0 0   0 0 0  
  3 100   3 0 13  
  1 50   1   5  
278             sub ' . $_ . ' (;*) { @_ ? isatype($_[0], '.$_.') : '.$_.' } '
279 5     5   6813 for @CORETYPES;
280             }
281              
282              
283             =head2 Non-instantiating existence checks in references: C.
284              
285             S< >
286              
287             ErV $ref, FIELDNAME; # Exist[in]reference? Value : C
288             ErV $hashref, FIELDNAME; # Exist[in]hashref? Value : C
289              
290             =over
291              
292             If fieldname exists in the ref pointed to by the reference, return the value,
293             else return undef.
294              
295             =back
296              
297             =head2 Note: What's EhV? (Deprecated)
298              
299             =over
300              
301             You may see older code using C. M only had this checker
302             for hashes, but given combinations of various references, the more
303             general C replaced it.
304              
305             =back
306              
307              
308             =head1 OPTIONAL FUNCTIONS: C & C
309              
310             S< >
311              
312             typ REF; #return underlying type of REF
313              
314              
315             Once you bless a reference to an object, its type becomes hidden
316             from C. C allows you to peek into a class reference to
317             see the basic perl type that the class is based on.
318              
319             Most users of a class won't have a need for that information,
320             but a 'friend' of the class might in order to offer helper functions.
321              
322              
323             blessed REF; #test if REF is blessed or not
324              
325              
326             Needed for consistency with 'ref' (and typ). 'ref' passes back the
327             actual value of the 'ref' if it is a ref. Following that example,
328             'typ' return the underlying type of a perl-ref if it is a reference.
329             In the same way, 'blessed' returns the name of the object's
330             blessing (its class or package name) if it is 'blessed'.
331              
332             Warning: take care that L's version of C
333             isn't also included, as it throws away the package or blessing
334             name and only returns '1' if its argument is blessed.
335             perl-type of a reference if it is a reference.Included for it's usefulness in type checking. Similar functionality
336             as implemented in L. This version of C
337             will use the C version if it is already present.
338             Otherwise it uses a pure-perl implementation.
339              
340              
341              
342             =head1 EXAMPLE: C
343              
344             S< >
345              
346             To prevent automatic creation of variables when accessed
347             or tested for C, (i.e. autovivification), one must test
348             for existence first, before attempting to read or test the
349             'defined'-ness of the value.
350              
351             This results in a 2 step process to retrive a value:
352              
353             exists $name{$testname} ? $name{testname} : undef;
354              
355             If you have multiple levels of hash tables say retrieving SSN's
356             via {$lastname}{$firstname} in object member 'name2ssns' but
357             don't know if the object member is valid or not, the safe way
358             to write this would be:
359              
360             my $p = $this;
361             if (exists $p->{name2ssns} && defined $p->{name2ssns}) {
362             $p = $p->{name2ssns};
363             if (exists $p->{$lastname} && defined $p->{$lastname}) {
364             $p = $p->{$lastname};
365             if (exists $p->{$firstname}) {
366             return $p->{$firstname};
367             }
368             }
369             }
370             return undef;
371              
372             C saves some steps. Instead of testing for existence, 'definedness',
373             and then use the value to go deeper in the structuer, C does the
374             testing and returns the value (or undef) in one step.
375             Thus, the above could be written:
376              
377             my $p = $this;
378             return $p = ErV $p, name2ssns and
379             $p = ErV $p, $lastname and
380             ErV $p, $firstname;
381              
382             This not only saves coding space & time, but allows faster
383             comprehension of what is going on (presuming familiarity
384             with C).
385              
386             Multiple levels of hashes or arrays may be tested in one usage. Example:
387              
388             my $nested_refs = {};
389             $nested_refs->{a}{b}{c}{d}[2]{f}[1] = 7;
390             P "---\nval=%s", ErV $nested_refs, a, b, c, d, e, f, g;
391             ---
392             val=7
393              
394             The current ErV handles around thirty levels of nested references
395            
396             =cut
397              
398 0         0 BEGIN {
399             sub ErV ($*;******************************) {
400 20     20 0 45 my ($argp, $field) = (shift, shift);
401 20 100 100     101 return undef unless defined($field) && defined($argp);
402 10         15 my $offset;
403 10 50       25 $field = substr $field,$offset+2 if 1 + ($offset = rindex $field,'::');
404 10         12 my $h;
405             # while (defined $field and
406             # (($h=HASH $argp) && exists $h->{$field} or
407             # ARRAY $argp && $field =~ /^[-\d]+$/ && exists $argp->[$field])) {
408 10         13 while (1) {
409 10 50       19 last unless defined $field;
410 10 50       17 if ($h = HASH $argp) {
    0          
411 10 100       29 last unless exists $h->{$field};
412 6         12 $argp = $h->{$field};
413             } elsif ($h = ARRAY $argp) {
414 0 0 0     0 last unless $field =~ /^[-\d]+$/ &&
415             exists $argp->[$field];
416 0         0 $argp = $h->[$field];
417             }
418 6 50       23 $field = shift, next if @_;
419 6         31 return $argp;
420             }
421 4         54 return undef;
422             }
423              
424 5         599 { my %msgs;
  0         0  
425             sub EhV ($*;******************************) {
426 0     0 0   my ($pkg,$fn, $ln) = caller(1);
427 0           my $loc=$fn.':'.$ln;
428 0 0         unless ($msgs{$loc}++) {
429 0           printf STDERR "Warning: Deprecated EhV works now, but please switch ".
430             "to ErV at %s\n", $loc;
431             }
432 0           goto &ErV; }
433             }
434              
435             # doesn't work with CODE ref
436 5     5   22 sub EmV ($*;******************************) {
437 0     0 0   my ($argp, $field) = (shift, shift);
438 0 0         return undef unless defined $argp;
439 0           my $offset;
440 0 0         $field = substr $field,$offset+2 if 1 + ($offset = rindex $field,'::');
441 0           my ($c, $h);
442 0   0       while (defined $field && defined $argp and (
      0        
      0        
443             ($c = blessed $argp) && $argp->can($field) or
444             ($h = HASH $argp) && exists $argp->{$field} or
445             ARRAY $argp && $field =~ /^[-\d]+$/ && exists $argp->[$field])) {
446             $argp = $c ? $argp->$field
447 0 0         : $h ? $argp->{$field}
    0          
448             : $argp->[$field];
449 0 0         $field = shift, next if @_;
450 0           return $argp;
451             }
452 0           return undef;
453             }
454              
455 0 0   0 0   sub LongSub(;$) { ((caller (@_ ? 1+$_[0] : 1))[3]) || __PACKAGE__."::" }
    0          
456             sub ShortSub(;$) {
457 0   0 0 0   my $f = (@_ ? LongSub(1+$_[0]) : LongSub(1) ) || "";
458 0           substr $f, (1+rindex $f,':') }
459              
460 0 0   0 1   sub mk_array($) { $_[0] = [] unless q(ARRAY) eq ref $_[0] ; $_[0] }
  0            
461 0     0 0   sub mkARRAY($) { goto &mk_array }
462 0 0   0 1   sub mk_hash($) { $_[0] = {} unless q(HASH) eq ref $_[0] ; $_[0] }
  0            
463 0     0 0   sub mkHASH($) { goto &mk_hash }
464              
465             # Obj - 1 or 2 parms (on top of "objref" ($p))
466             ##1st param - name to verify against; verify against objptr by default
467             #2nd optional parm = verify against this ref instead of objptr
468             #
469             sub Obj($;$) {
470 0 0 0 0 0   my $objref = ref $_[0] || $_[0] eq Self ? shift : "";
471 0           my $objname = shift; # txt name
472 0 0         $objref = ref $_[0] if @_; # if another parm, chk it as ref
473 0 0         return $objref eq $objname ? $objname : "";
474             }
475              
476             }
477              
478              
479             =head2 MORE OPTIONAL FUNCTIONS C and C
480              
481              
482             $< >
483              
484             mk_array $p->ar;
485              
486             without C, the following generates a runtime error (can't
487             use an undefined value as an ARRAY reference):
488              
489             my $ar;
490             printf "items in ar:%s\n", 0+@{$ar};
491              
492             but using mk_array will ensure there is an ARRAY ref there if there
493             is not one there already:
494            
495             my $ar;
496             mk_array $ar;
497             printf "items in ar:%s\n", 0+@{$ar};
498              
499             While the above would be solved by initalizing $ar when defined,
500             expicit initialization might be useful to protect against the same
501             type of error in dynamically allocated variables.
502              
503              
504             =head1 UTILITY FUNCTIONS: C & C
505              
506             S< >
507              
508             isnum STR #return if it starts at beginning of STR
509              
510             Cmp [$p1,$p2] # C-like function for nested structures
511             # uses C<$a>, C<$b> as default inputs
512             # can be used in sort for well-behaved data
513             # (incompare-able data will return undef)
514             # builtin debug to see where compare fails
515             #
516              
517             C checks for a number (int, float, or with exponent) as the
518             value of the string passed in. With no argument uses C<$_>
519             as the parameter. Returns the number or C if the field
520             does not evaluate to a number. C is an optional import that
521             must be mentioned in the modules arguments.
522             Note: to determine if false, you must use C
523             since numeric '0' can be returned and also evaluates to false.
524              
525             The existence of C is a B needs. To compare
526             validity of released functions, it was necessary to recursively
527             compare nested data structures. To support development, debug
528             output was added that can be toggled on at runtime to see where
529             a compare fails.
530              
531             Normally you only use two parameters C<$a> and C<$b> that are references
532             to the data structures to be compared. If debugging is wanted,
533             a third (or first if C<$a> and C<$b> are used) parameter can be
534             pass with a non-zero value to enable primitive debug output.
535              
536             Additionally, if the compare I and does not return an integer
537             value (returning C instead), a 2nd return value can tell you
538             where in the compare it failed. To grab that return value,
539             use a two element list or an array to catch the status, like
540              
541             C and C<$b>)
542              
543             If the compare was successful, it will return -1, 0 or 1 as 'cmp'
544             does. If it fails, C<$result> will contain C and C<$err> will
545             contain a number indicating what test failed (for debugging purposes).
546              
547             Failures can occur if Cmp is asked to compare different object with
548             different refs ('blessed' refname), or same blessed class and different
549             underlying types. Unblessed values and those of the same class can
550             be compared.
551              
552              
553              
554             =cut
555              
556 5         4095 use constant numRE => qr{^ (
557             [-+]? (?: (?: \d* \.? \d+ ) |
558             (?: \d+ \.? \d* ) )
559 5     5   43 (?: [eE] [-+]? \d+)? ) $ }x;
  5         11  
560              
561             sub isnum(;$) {
562 0 0   0 0   local $_ = @_ ? $_[0] : $_;
563 0 0         return undef unless defined $_;
564 0           my $numRE = numRE;
565 0 0         m{$numRE} ? 1 : 0;
566             }
567              
568              
569             sub Cmp($$;$);
570 0     0 0   sub Cmp ($$;$) { my $r=0;
571 0 0         local ($a, $b) = @_ >= 2 ? (shift, shift) : ($a, $b);
572 0 0         my $dbg = @_ ? $_[0] : undef;
573 0 0         require P if $dbg;
574 0 0 0       return undef unless defined $a && defined $b;
575 0           my ($ra, $rb) = (ref $a, ref $b);
576 0           my ($ta, $tb) = (typ $a, typ $b);
577 0 0 0       return undef unless defined $ra && defined $rb;
578 0           my ($dta, $dtb) = (defined $ta, defined $tb);
579 0 0 0       return undef unless $dta && $dtb;
580 0 0         do { my $out = P::P("a='%s', b='%s'; ", $a, $b);
  0            
581 0           $out .= P::P("ta='%s', tb='%s'; ", $ta, $tb);
582 0           $out .= P::P("ra='%s', rb='%s'; ", $ra, $rb);
583 0           P::Pe("%s", $out) } if $dbg;
584              
585 0 0         return undef if $ta ne $tb;
586              
587 0 0 0       unless ($ta || $tb) { # do val processing if both are vals
588             # handle values, nums if nums, else as strings
589 0 0 0       $r = isnum($a) && isnum($b)
590             ? $a <=> $b
591             : $a cmp $b;
592 0 0         P::Pe("isnum(a)=%s, isnum(b)=%s, r=%s", isnum($a), isnum($b), $r) if $dbg;
593 0           return $r
594             }
595            
596             # then handle unequal type references
597 0 0         if ($dta ^ $dtb) { # one defined
598 0           return (undef, 1);
599             }
600 0 0 0       unless ($dta && $dtb) { return (undef, 2) } #either undef
  0            
601              
602             # now, either do same thing again, or handle differing classes
603             # the no-class on either implies no type-ref on either & is handled above
604 0           my ($dra, $drb) = (defined $ra, defined $rb);
605 0 0 0       if ($dra ^ $drb) { return (undef, 4) }
  0 0 0        
606 0           elsif ($dra && $drb && $ra ne $rb) { return (undef, 5) }
607              
608             # now start comparing references: dereference and call Cmp again
609 0 0         if ($ta eq SCALAR) {
    0          
    0          
610 0           return Cmp($$a, $$b, $dbg) }
611             elsif ($ta eq ARRAY) {
612              
613 0 0         P::Pe("len of array a vs. b: (%s <=> %s)", 0+@$a, 0+@$b) if $dbg;
614 0 0         return $r if $r = @$a <=> @$b;
615              
616             # for each member, compare them using Cmp
617 0           for (my $i=0; $i < 0+@$a; ++$i) {
618 0 0         P::Pe("a->[$i] Cmp b->[$i]...\x83") if $dbg;
619            
620 0           $r = Cmp($a->[$i], $b->[$i], $dbg);
621            
622 0 0         P::Pe("%s Cmp %s, r=%s", $a->[$i], $b->[$i], $r) if $dbg;
623 0 0         return $r if $r;
624             }
625 0           return 0; # arrays are equal
626             } elsif ($ta eq HASH) {
627 0           my @ka = sort keys %$a;
628 0           my @kb = sort keys %$b;
629 0           $r = Cmp(0+@ka, 0+@kb, $dbg);
630 0 0         P::Pe("Cmp #keys a(%s) b(%s), in hashes: r=%s", 0+@ka, 0+@kb, $r) if $dbg;
631 0 0         return $r if $r;
632              
633 0           $r = Cmp(\@ka, \@kb, $dbg);
634 0 0         P::Pe("Cmp keys of hash: r=%s", $r) if $dbg;
635 0 0         return $r if $r;
636              
637 0           my @va = map {$a->{$_}} @ka;
  0            
638 0           my @vb = map {$b->{$_}} @kb;
  0            
639 0           $r = Cmp(\@va, \@vb, $dbg);
640 0 0         P::Pe("Cmp values for each key, r=%s", $r) if $dbg;
641 0           return $r;
642             } else {
643 0 0         P::Pe("no comparison for type %s, ref %s", $ta, $ra) if $dbg;
644 0           return (undef,6); ## unimplemented comparison
645             }
646             }
647 5     5   43 use Xporter;
  5         10  
  5         33  
648              
649            
650             1}
651              
652             =head3 COMPATIBILITY NOTE: with Perl 5.12.5 and earlier
653              
654             =over
655              
656             In order for earlier perls to parse things correctly parentheses are needed
657             for two or more arguments after a B test verb.
658              
659             =cut
660              
661             # vim: ts=2 sw=2 sts=2