File Coverage

blib/lib/Types/Core.pm
Criterion Covered Total %
statement 64 151 42.3
branch 19 108 17.5
condition 15 50 30.0
subroutine 26 46 56.5
pod 2 24 8.3
total 126 379 33.2


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