File Coverage

blib/lib/rperltypes.pm
Criterion Covered Total %
statement 102 290 35.1
branch 7 136 5.1
condition 2 27 7.4
subroutine 33 37 89.1
pod 0 1 0.0
total 144 491 29.3


line stmt bran cond sub pod time code
1             # [[[ HEADER ]]]
2             ## no critic qw(Capitalization ProhibitMultiplePackages ProhibitReusedNames) # SYSTEM DEFAULT 3: allow multiple & lower case package names
3             package # hide from PAUSE indexing
4             rperltypes;
5 9     9   57 use strict;
  9         22  
  9         265  
6 9     9   50 use warnings;
  9         24  
  9         230  
7 9     9   43 use RPerl::Config;
  9         22  
  9         1549  
8             our $VERSION = 0.008_000;
9              
10             # NEED UPGRADE: create GrammarComponents
11             #use parent qw(RPerl::GrammarComponent)
12              
13             # [[[ CRITICS ]]]
14             ## no critic qw(ProhibitUselessNoCritic ProhibitMagicNumbers RequireCheckedSyscalls) # USER DEFAULT 1: allow numeric values & print operator
15             ## no critic qw(RequireInterpolationOfMetachars) # USER DEFAULT 2: allow single-quoted control characters & sigils
16             ## no critic qw(ProhibitExcessComplexity) # SYSTEM SPECIAL 5: allow complex code inside subroutines, must be after line 1
17             ## no critic qw(ProhibitPostfixControls) # SYSTEM SPECIAL 6: PERL CRITIC FILED ISSUE #639, not postfix foreach or if
18             ## no critic qw(ProhibitDeepNests) # SYSTEM SPECIAL 7: allow deeply-nested code
19             ## no critic qw(RequireBriefOpen) # SYSTEM SPECIAL 10: allow complex processing with open filehandle
20             ## no critic qw(ProhibitCascadingIfElse) # SYSTEM SPECIAL 12: allow complex conditional logic
21              
22             # [[[ NON-RPERL MODULES ]]]
23 9     9   4420 use File::Copy qw(copy);
  9         16906  
  9         503  
24 9     9   65 use Scalar::Util qw(blessed);
  9         19  
  9         547  
25 9     9   55 use Config;
  9         21  
  9         321  
26              
27             # DEV NOTE, CORRELATION #rp008: can't use Exporter here
28             # [[[ EXPORTS ]]]
29             #use Exporter 'import';
30             #our @EXPORT = qw(type types ETC);
31              
32             # all following type lists lowest-to-highest level
33              
34 9     9   64 use rperltypessizes;
  9         22  
  9         183  
35              
36             # DEV NOTE, CORRELATION #rp012: type system includes, hard-copies in rperltypes.pm & rperltypesconv.pm & Class.pm
37              
38             # [[[ DATA TYPES ]]]
39 9     9   52 use RPerl::DataType::Void;
  9         18  
  9         248  
40 9     9   50 use RPerl::DataType::Boolean;
  9         27  
  9         586  
41 9     9   60 use RPerl::DataType::UnsignedInteger;
  9         27  
  9         537  
42 9     9   54 use RPerl::DataType::Integer;
  9         16  
  9         510  
43 9     9   55 use RPerl::DataType::Number;
  9         17  
  9         469  
44 9     9   49 use RPerl::DataType::Character;
  9         19  
  9         470  
45 9     9   50 use RPerl::DataType::String;
  9         21  
  9         475  
46 9     9   51 use RPerl::DataType::Scalar;
  9         18  
  9         224  
47 9     9   43 use RPerl::DataType::Unknown;
  9         18  
  9         190  
48 9     9   47 use RPerl::DataType::FileHandle;
  9         18  
  9         222  
49              
50             # [[[ DATA STRUCTURES ]]]
51 9     9   40 use RPerl::DataStructure::Array;
  9         18  
  9         1734  
52 9     9   64 use RPerl::DataStructure::Array::SubTypes;
  9         20  
  9         612  
53 9     9   53 use RPerl::DataStructure::Array::Reference;
  9         37  
  9         181  
54 9     9   47 use RPerl::DataStructure::Hash;
  9         18  
  9         984  
55 9     9   60 use RPerl::DataStructure::Hash::SubTypes;
  9         23  
  9         540  
56 9     9   51 use RPerl::DataStructure::Hash::Reference;
  9         19  
  9         189  
57              
58             #use RPerl::DataStructure::LinkedList;
59             #use RPerl::DataStructure::LinkedList::Node;
60             #use RPerl::DataStructure::Graph;
61             #use RPerl::DataStructure::Graph::Tree;
62             #use RPerl::DataStructure::Graph::Tree::Binary;
63             #use RPerl::DataStructure::Graph::Tree::Binary::Node;
64              
65             # [[[ OBJECT-ORIENTED ]]]
66 9     9   3233 use RPerl::Object;
  9         23  
  9         226  
67 9     9   3471 use RPerl::CodeBlock::Subroutine::Method; # Method is the only item that is both a Data Type & a Grammar Rule???
  9         25  
  9         504  
68              
69             # these types are currently implemented for the 2 primary RPerl modes: PERLOPS_PERLTYPES, CPPOPS_CPPTYPES
70             # NEED REMOVE: hard-coded list
71             # MISSING: boolean, unsigned_integer, char, *_arrayref, *_hashref
72             our string_arrayref $SUPPORTED = [
73             qw(
74             void
75             integer
76             number
77             string
78             integer_arrayref
79             number_arrayref
80             string_arrayref
81             integer_hashref
82             number_hashref
83             string_hashref
84             )
85             ];
86              
87             # DEV NOTE, CORRELATION #rp008: export to_string(), class(), type() and types() to main:: namespace;
88             # can't achieve via Exporter due to circular dependency issue caused by Exporter in Config.pm and solved by 'require rperltypes;' in RPerl.pm
89             package main;
90 9     9   54 use RPerl::Config;
  9         17  
  9         1106  
91 9     9   64 use Scalar::Util qw(blessed);
  9         16  
  9         493  
92              
93             # for type-checking via SvIOKp(), SvNOKp(), and SvPOKp(); inside INIT to delay until after 'use MyConfig'
94             #INIT { RPerl::diag("in rperltypes.pm, loading C++ helper functions for type-checking...\n"); }
95             INIT {
96 9     9   53 use RPerl::HelperFunctions_cpp;
  9         16  
  9         14410  
97 9     9   98 RPerl::HelperFunctions_cpp::cpp_load();
98             }
99              
100             # [[[ GENERIC OVERLOADED TYPE CONVERSION ]]]
101             # [[[ GENERIC OVERLOADED TYPE CONVERSION ]]]
102             # [[[ GENERIC OVERLOADED TYPE CONVERSION ]]]
103              
104             #my number $to_number = sub {
105             sub to_number {
106 0     0   0 ( my unknown $variable) = @_;
107 0 0       0 if ( not defined $variable ) { return 0; }
  0         0  
108 0         0 my string $type = type($variable);
109 0 0       0 if ( $type eq 'unknown' ) { return ($variable + 0); }
  0 0       0  
    0          
    0          
    0          
110 0         0 elsif ( $type eq 'boolean' ) { return boolean_to_number($variable); }
111             # elsif ( $type eq 'unsigned_integer' ) { return unsigned_integer_to_number($variable); } # DEV NOTE: causes auto-vivification of empty unsigned_integer_to_number() if not already properly bound
112             # elsif ( $type eq 'gmp_integer' ) { return gmp_integer_to_number($variable); } # NEED IMPLEMENT
113 0         0 elsif ( $type eq 'integer' ) { return integer_to_number($variable); }
114             # elsif ( $type eq 'number' ) { return number_to_number($variable); } # NEED ANSWER: is this totally unneeded, and should it be deleted?
115 0         0 elsif ( $type eq 'character' ) { return character_to_number($variable); }
116 0         0 elsif ( $type eq 'string' ) { return string_to_number($variable); }
117             else {
118 0         0 croak q{ERROR ERPTY01: Invalid data type '} . $type . q{' specified, croaking};
119             }
120             }
121              
122             # NEED UPGRADE: don't fall back to Perl qq{} string interpolation or Dumper() for stringification;
123             # Dumper will fail to call *_to_string() until stringification overloading is implemented
124             #my string $to_string = sub {
125             sub to_string {
126 0     0   0 ( my unknown $variable) = @_;
127             # RPerl::diag('in rperltypes::to_string(), received $variable = ' . $variable . "\n");
128 0 0       0 if ( not defined $variable ) { return 'undef'; }
  0         0  
129 0         0 my string $type = type($variable);
130             # RPerl::diag('in rperltypes::to_string(), have $type = ' . $type . "\n");
131              
132 0 0       0 if ( $type eq 'unknown' ) { return qq{$variable}; }
  0 0       0  
    0          
    0          
    0          
    0          
133 0         0 elsif ( $type eq 'boolean' ) { return boolean_to_string($variable); }
134             # elsif ( $type eq 'unsigned_integer' ) { return unsigned_integer_to_string($variable); } # DEV NOTE: causes auto-vivification of empty unsigned_integer_to_string() if not already properly bound
135             # elsif ( $type eq 'gmp_integer' ) { return gmp_integer_to_string($variable); } # NEED IMPLEMENT
136 0         0 elsif ( $type eq 'integer' ) { return integer_to_string($variable); }
137 0         0 elsif ( $type eq 'number' ) { return number_to_string($variable); }
138 0         0 elsif ( $type eq 'character' ) { return character_to_string($variable); }
139 0         0 elsif ( $type eq 'string' ) { return string_to_string($variable); }
140             else {
141 0         0 my $retval = Dumper($variable);
142 0         0 $retval =~ s/\$VAR1\ =\ //gxms;
143 0         0 chomp $retval;
144 0         0 chop $retval;
145 0         0 return $retval;
146             }
147             }
148              
149             # DEV NOTE: class() is a wrapper around blessed() from Scalar::Util, class() is preferred for readability,
150             # blessed() and class() both generate as classname() in C++ to avoid conflict with 'class' C++ reserved word
151             #my string $class = sub {
152             sub class {
153 126     126   231 ( my unknown $object ) = @_;
154 126         733 return blessed($object);
155             }
156              
157             # DEV NOTE: type() and types() are more powerful replacements for ref(), and ref() is not supported in RPerl
158             #my string $type = sub {
159             sub type {
160 1864     1864   5366 ( my unknown $variable, my integer $recurse_level ) = @_;
161 1864 50       6533 if ( not defined $variable ) { return 'unknown'; }
  0         0  
162 1864 50       5969 if ( not defined $recurse_level ) { $recurse_level = 10; } # default to limited recursion
  1864         3308  
163 1864         5256 my integer_hashref $is_type = build_is_type($variable);
164             # RPerl::diag('in rperltypes::type(), have $is_type = ' . Dumper($is_type) . "\n");
165             # DEV NOTE, CORRELATION #rp025: only report core types integer, number, string, arrayref, hashref, object;
166             # do NOT report non-core types boolean, unsigned_integer, char, etc.
167             # DEV NOTE: Perl's implicit casting can cause 1 constant or variable to report multiple types,
168             # always report number before integer to avoid incorrect to_string() formatting
169 1864 50       9643 if ( $is_type->{number} ) { return 'number'; }
  0 50       0  
    50          
170 0         0 elsif ( $is_type->{integer} ) { return 'integer'; }
171 1864         8277 elsif ( $is_type->{string} ) { return 'string'; }
172             else { # arrayref, hashref, or blessed object
173 0         0 my arrayref $types = types_recurse( $variable, $recurse_level, $is_type );
174 0         0 return $types->[0]; # only return flat type string, discard nested type hashref
175             }
176             }
177              
178             #my string_hashref $types = sub {
179             sub types {
180 0     0   0 ( my unknown $variable, my integer $recurse_level ) = @_;
181 0 0       0 if ( not defined $variable ) { return 'unknown'; }
  0         0  
182 0 0       0 if ( not defined $recurse_level ) { $recurse_level = 10; } # default to limited recursion
  0         0  
183 0         0 my integer_hashref $is_type = build_is_type($variable);
184             # DEV NOTE, CORRELATION #rp025: only report core types integer, number, string, arrayref, hashref, object;
185             # do NOT report non-core types boolean, unsigned_integer, char, etc.
186 0 0       0 if ( $is_type->{integer} ) { return { 'integer' => undef }; }
  0 0       0  
    0          
187 0         0 elsif ( $is_type->{number} ) { return { 'number' => undef }; }
188 0         0 elsif ( $is_type->{string} ) { return { 'string' => undef }; }
189             else { # arrayref, hash, or blessed object
190 0         0 my arrayref $types = types_recurse( $variable, $recurse_level, $is_type );
191 0         0 return $types->[1]; # only return nested type hashref, discard flat type string
192             }
193             }
194              
195             #my integer_hashref build_is_type = sub {
196             sub build_is_type {
197 1864     1864   4045 ( my unknown $variable ) = @_;
198              
199 1864         36370 my integer_hashref $is_type = {
200             boolean => main::RPerl_SvBOKp($variable),
201             unsigned_integer => main::RPerl_SvUIOKp($variable),
202              
203             # START HERE: figure out why SvIOKp() below is returning true for floating-point number Pi()
204             # START HERE: figure out why SvIOKp() below is returning true for floating-point number Pi()
205             # START HERE: figure out why SvIOKp() below is returning true for floating-point number Pi()
206              
207             integer => main::RPerl_SvIOKp($variable),
208             number => main::RPerl_SvNOKp($variable),
209             character => main::RPerl_SvCOKp($variable),
210             string => main::RPerl_SvPOKp($variable),
211             arrayref => main::RPerl_SvAROKp($variable),
212             hashref => main::RPerl_SvHROKp($variable),
213             blessed => 0,
214             class => blessed $variable
215             };
216 1864 50       6670 if ( defined $is_type->{class} ) { $is_type->{blessed} = 1; }
  0         0  
217              
218             # RPerl::diag('in rperltypes::build_is_type(), have $is_type =' . "\n" . Dumper($is_type) . "\n");
219              
220 1864         4096 return $is_type;
221             }
222              
223             #my string_hashref $types_recurse = sub {
224             sub types_recurse {
225 0     0   0 ( my unknown $variable, my integer $recurse_level, my integer_hashref $is_type ) = @_;
226              
227             # RPerl::diag('in rperltypes::types_recurse(), received $variable =' . "\n" . Dumper($variable) . "\n");
228              
229 0 0       0 if ( not defined $recurse_level ) { $recurse_level = 999; } # default to full recursion
  0         0  
230 0 0       0 if ( not defined $is_type ) { $is_type = build_is_type($variable); }
  0         0  
231              
232             # RPerl::diag('in rperltypes::types_recurse(), have $recurse_level = ' . $recurse_level . "\n");
233              
234             # RPerl::diag('in rperltypes::types_recurse(), have $is_type =' . "\n" . Dumper($is_type) . "\n");
235              
236 0         0 my string $type = undef;
237 0         0 my string_hashref $types = undef;
238              
239             # DEV NOTE, CORRELATION #rp025: only report core types integer, number, string, arrayref, hashref, object;
240             # do NOT report non-core types boolean, unsigned_integer, character, etc.
241 0 0       0 if ( not defined $variable ) { $type = 'unknown'; }
  0 0       0  
    0          
    0          
242 0         0 elsif ( $is_type->{integer} ) { $type = 'integer'; }
243 0         0 elsif ( $is_type->{number} ) { $type = 'number'; }
244 0         0 elsif ( $is_type->{string} ) { $type = 'string'; }
245              
246 0 0       0 if ( defined $type ) {
    0          
247              
248             # RPerl::diag('in rperltypes::types_recurse(), about to return undef or scalar $type = ' . $type . "\n");
249 0         0 return [ $type, $types ];
250             }
251             elsif ( $recurse_level <= 0 ) {
252              
253             # blessed class must be tested first, because it also matches on hashref
254 0 0       0 if ( $is_type->{blessed} ) {
    0          
    0          
255 0         0 $type = 'object';
256 0         0 $types = { $type => { '__CLASS' => $is_type->{class} } };
257             }
258 0         0 elsif ( $is_type->{arrayref} ) { $type = 'arrayref'; }
259 0         0 elsif ( $is_type->{hashref} ) { $type = 'hashref'; }
260 0         0 else { $type = '__UNRECOGNIZED_TYPE'; }
261              
262             # RPerl::diag('in rperltypes::types_recurse(), max recurse reached, about to return unrecognized or non-scalar $type = ' . $type . "\n");
263 0         0 return [ $type, $types ];
264             }
265             else {
266 0         0 $recurse_level--;
267              
268             # blessed class must be tested first, because it also matches on hashref
269             # DEV NOTE: objects don't inherit subtypes of their $properties hash entries, even if homogeneous;
270             # no such thing as integer_object even if all $properties are integers, etc.
271 0 0       0 if ( $is_type->{blessed} ) {
    0          
    0          
272 0         0 $type = 'object';
273 0         0 $types = {};
274 0         0 $types->{$type} = { '__CLASS' => $is_type->{class} };
275              
276             # RPerl::diag('in rperltypes::types_recurse(), top of blessed class...' . "\n");
277              
278 0         0 foreach my $hash_key ( sort keys %{$variable} ) {
  0         0  
279 0         0 my hashref $subtypes = types_recurse( $variable->{$hash_key}, $recurse_level );
280 0 0       0 if ( not defined $subtypes->[1] ) {
281              
282             # for scalar subtypes or non-scalar subtypes w/ max recurse reached, discard undef nested type hashref
283 0         0 $types->{$type}->{$hash_key} = $subtypes->[0];
284             }
285             else {
286             # for non-scalar subtypes w/out max recurse reached, append nested subtype hashref to list of types for this arrayref
287 0         0 $types->{$type}->{$hash_key} = $subtypes->[1];
288             }
289 0         0 RPerl::diag('in rperltypes::types_recurse(), inside blessed class, have $types = ' . "\n" . Dumper($types) . "\n");
290 0         0 RPerl::diag('in rperltypes::types_recurse(), inside blessed class, have $subtypes = ' . "\n" . Dumper($subtypes) . "\n");
291              
292             }
293              
294             # RPerl::diag('in rperltypes::types_recurse(), bottom of blessed class, have $type = ' . $type . "\n");
295             }
296             elsif ( $is_type->{arrayref} ) {
297 0         0 $type = 'arrayref';
298 0         0 $types = {};
299 0         0 $types->{$type} = [];
300 0         0 my string $subtype = undef;
301 0         0 my string $subtype_class = undef;
302 0         0 my integer $object_mismatch = 0;
303 0         0 my integer $is_homogeneous = 1;
304              
305             # RPerl::diag('in rperltypes::types_recurse(), top of arrayref...' . "\n");
306              
307 0         0 foreach my $array_element ( @{$variable} ) {
  0         0  
308 0         0 my hashref $subtypes = types_recurse( $array_element, $recurse_level );
309 0 0       0 if ( not defined $subtypes->[1] ) {
310              
311             # for scalar subtypes or non-scalar subtypes w/ max recurse reached, discard undef nested type hashref
312 0         0 push @{ $types->{$type} }, $subtypes->[0];
  0         0  
313             }
314             else {
315             # for non-scalar subtypes w/out max recurse reached, append nested subtype hashref to list of types for this arrayref
316 0         0 push @{ $types->{$type} }, $subtypes->[1];
  0         0  
317             }
318              
319             # RPerl::diag('in rperltypes::types_recurse(), inside arrayref, have $types = ' . "\n" . Dumper($types) . "\n");
320             # RPerl::diag('in rperltypes::types_recurse(), inside arrayref, have $subtypes = ' . "\n" . Dumper($subtypes) . "\n");
321              
322             # use first element's type as test for remaining element types
323 0 0       0 if ( not defined $subtype ) {
    0          
324 0         0 $subtype = $subtypes->[0];
325 0 0       0 if ( $subtype eq 'object' ) {
326 0         0 $subtype_class = $subtypes->[1]->{object}->{__CLASS};
327             }
328             }
329             elsif ($is_homogeneous) {
330              
331             # RPerl::diag('in rperltypes::types_recurse(), inside arrayref, have $subtype = ' . $subtype . "\n");
332             # RPerl::diag('in rperltypes::types_recurse(), inside arrayref, have $subtypes->[0] = ' . $subtypes->[0] . "\n");
333              
334             # RPerl::diag('in rperltypes::types_recurse(), inside arrayref, have $subtype_class = ' . $subtype_class . "\n");
335             # RPerl::diag('in rperltypes::types_recurse(), inside arrayref, have $subtypes->[1]->{object}->{__CLASS} = ' . $subtypes->[1]->{object}->{__CLASS} . "\n");
336             # object classes must match for homogeneity
337 0 0 0     0 if ( ( $subtype eq 'object' ) and ( $subtypes->[0] eq 'object' ) and ( $subtype_class ne $subtypes->[1]->{object}->{__CLASS} ) ) {
      0        
338              
339             # RPerl::diag('in rperltypes::types_recurse(), inside arrayref, MISMATCH OF OBJECT CLASSES' . "\n");
340             # RPerl::diag('in rperltypes::types_recurse(), inside arrayref, have $types = ' . "\n" . Dumper($types) . "\n");
341             # RPerl::diag('in rperltypes::types_recurse(), inside arrayref, have $subtypes = ' . "\n" . Dumper($subtypes) . "\n");
342 0         0 $object_mismatch = 1;
343             }
344 0         0 else { $object_mismatch = 0; }
345              
346 0 0 0     0 if ( $object_mismatch or ( $subtype ne $subtypes->[0] ) ) {
347 0         0 my string_arrayref $reverse_split_subtype = [ reverse split /_/xms, $subtype ];
348 0         0 my string_arrayref $reverse_split_subtypes_0 = [ reverse split /_/xms, $subtypes->[0] ];
349              
350             # RPerl::diag('in rperltypes::types_recurse(), inside arrayref, have $reverse_split_subtype = ' . "\n" . Dumper($reverse_split_subtype) . "\n");
351             # RPerl::diag('in rperltypes::types_recurse(), inside arrayref, have $reverse_split_subtypes_0 = ' . "\n" . Dumper($reverse_split_subtypes_0) . "\n");
352             # discard non-matching 'object' subtype
353 0 0       0 if ($object_mismatch) {
354 0         0 pop @{$reverse_split_subtype};
  0         0  
355 0         0 pop @{$reverse_split_subtypes_0};
  0         0  
356 0         0 $object_mismatch = 0;
357             }
358 0         0 my string $new_subtype = q{};
359 0         0 my integer $shorter_split_length = scalar @{$reverse_split_subtype};
  0         0  
360 0 0       0 if ( scalar @{$reverse_split_subtypes_0} < $shorter_split_length ) {
  0         0  
361 0         0 $shorter_split_length = scalar @{$reverse_split_subtypes_0};
  0         0  
362             }
363 0         0 for my integer $i ( 0 .. ( $shorter_split_length - 1 ) ) {
364              
365             # RPerl::diag('in rperltypes::types_recurse(), inside arrayref, have $reverse_split_subtype->[' . $i . '] = ' . $reverse_split_subtype->[$i] . "\n");
366             # RPerl::diag('in rperltypes::types_recurse(), inside arrayref, have $reverse_split_subtypes_0->[' . $i . '] = ' . $reverse_split_subtypes_0->[$i] . "\n");
367 0 0       0 if ( $reverse_split_subtype->[$i] eq $reverse_split_subtypes_0->[$i] ) {
368 0 0       0 if ( $new_subtype eq q{} ) {
369 0         0 $new_subtype = $reverse_split_subtype->[$i];
370             }
371             else {
372 0         0 $new_subtype = $reverse_split_subtype->[$i] . '_' . $new_subtype;
373             }
374             }
375             }
376 0 0       0 if ( $new_subtype ne q{} ) {
377 0         0 $subtype = $new_subtype;
378             }
379             else {
380 0         0 $is_homogeneous = 0;
381             }
382             }
383             }
384              
385             # RPerl::diag('in rperltypes::types_recurse(), inside arrayref, have $subtype = ' . $subtype . "\n");
386             }
387 0 0       0 if ($is_homogeneous) {
388              
389             # DEV NOTE: flatten unknown_hashref to hashref
390 0 0 0     0 if ( ( not defined $subtype ) or ( $subtype eq 'unknown' ) ) { $subtype = q{} }
  0         0  
391 0 0       0 if ( $subtype ne q{} ) {
392 0         0 my string $type_old = $type;
393 0         0 $type = $subtype . '_' . $type;
394 0         0 $types->{$type} = $types->{$type_old};
395 0         0 delete $types->{$type_old};
396             }
397             }
398              
399             # RPerl::diag('in rperltypes::types_recurse(), bottom of arrayref, have $type = ' . $type . "\n");
400             }
401             elsif ( $is_type->{hashref} ) {
402 0         0 $type = 'hashref';
403 0         0 $types = {};
404 0         0 $types->{$type} = {};
405 0         0 my string $subtype = undef;
406 0         0 my string $subtype_class = undef;
407 0         0 my integer $object_mismatch = 0;
408 0         0 my integer $is_homogeneous = 1;
409              
410             # RPerl::diag('in rperltypes::types_recurse(), top of hashref...' . "\n");
411              
412 0         0 foreach my $hash_key ( sort keys %{$variable} ) {
  0         0  
413 0         0 my hashref $subtypes = types_recurse( $variable->{$hash_key}, $recurse_level );
414 0 0       0 if ( not defined $subtypes->[1] ) {
415              
416             # for scalar subtypes or non-scalar subtypes w/ max recurse reached, discard undef nested type hashref
417 0         0 $types->{$type}->{$hash_key} = $subtypes->[0];
418             }
419             else {
420             # for non-scalar subtypes w/out max recurse reached, append nested subtype hashref to list of types for this hashref
421 0         0 $types->{$type}->{$hash_key} = $subtypes->[1];
422             }
423              
424             # RPerl::diag('in rperltypes::types_recurse(), inside hashref, have $types = ' . "\n" . Dumper($types) . "\n");
425             # RPerl::diag('in rperltypes::types_recurse(), inside hashref, have $subtypes = ' . "\n" . Dumper($subtypes) . "\n");
426              
427             # use first element's type as test for remaining element types
428 0 0       0 if ( not defined $subtype ) {
    0          
429 0         0 $subtype = $subtypes->[0];
430 0 0       0 if ( $subtype eq 'object' ) {
431 0         0 $subtype_class = $subtypes->[1]->{object}->{__CLASS};
432             }
433             }
434             elsif ($is_homogeneous) {
435              
436             # RPerl::diag('in rperltypes::types_recurse(), inside hashref, have $subtype = ' . $subtype . "\n");
437             # RPerl::diag('in rperltypes::types_recurse(), inside hashref, have $subtypes->[0] = ' . $subtypes->[0] . "\n");
438              
439             # RPerl::diag('in rperltypes::types_recurse(), inside hashref, have $subtype_class = ' . $subtype_class . "\n");
440             # RPerl::diag('in rperltypes::types_recurse(), inside hashref, have $subtypes->[1]->{object}->{__CLASS} = ' . $subtypes->[1]->{object}->{__CLASS} . "\n");
441             # object classes must match for homogeneity
442 0 0 0     0 if ( ( $subtype eq 'object' ) and ( $subtypes->[0] eq 'object' ) and ( $subtype_class ne $subtypes->[1]->{object}->{__CLASS} ) ) {
      0        
443              
444             # RPerl::diag('in rperltypes::types_recurse(), inside hashref, MISMATCH OF OBJECT CLASSES' . "\n");
445             # RPerl::diag('in rperltypes::types_recurse(), inside hashref, have $types = ' . "\n" . Dumper($types) . "\n");
446             # RPerl::diag('in rperltypes::types_recurse(), inside hashref, have $subtypes = ' . "\n" . Dumper($subtypes) . "\n");
447 0         0 $object_mismatch = 1;
448             }
449 0         0 else { $object_mismatch = 0; }
450              
451 0 0 0     0 if ( $object_mismatch or ( $subtype ne $subtypes->[0] ) ) {
452 0         0 my string_arrayref $reverse_split_subtype = [ reverse split /_/xms, $subtype ];
453 0         0 my string_arrayref $reverse_split_subtypes_0 = [ reverse split /_/xms, $subtypes->[0] ];
454              
455             # RPerl::diag('in rperltypes::types_recurse(), inside hashref, have $reverse_split_subtype = ' . "\n" . Dumper($reverse_split_subtype) . "\n");
456             # RPerl::diag('in rperltypes::types_recurse(), inside hashref, have $reverse_split_subtypes_0 = ' . "\n" . Dumper($reverse_split_subtypes_0) . "\n");
457             # discard non-matching 'object' subtype
458 0 0       0 if ($object_mismatch) {
459 0         0 pop @{$reverse_split_subtype};
  0         0  
460 0         0 pop @{$reverse_split_subtypes_0};
  0         0  
461 0         0 $object_mismatch = 0;
462             }
463 0         0 my string $new_subtype = q{};
464 0         0 my integer $shorter_split_length = scalar @{$reverse_split_subtype};
  0         0  
465 0 0       0 if ( scalar @{$reverse_split_subtypes_0} < $shorter_split_length ) {
  0         0  
466 0         0 $shorter_split_length = scalar @{$reverse_split_subtypes_0};
  0         0  
467             }
468 0         0 for my integer $i ( 0 .. ( $shorter_split_length - 1 ) ) {
469              
470             # RPerl::diag('in rperltypes::types_recurse(), inside hashref, have $reverse_split_subtype->[' . $i . '] = ' . $reverse_split_subtype->[$i] . "\n");
471             # RPerl::diag('in rperltypes::types_recurse(), inside hashref, have $reverse_split_subtypes_0->[' . $i . '] = ' . $reverse_split_subtypes_0->[$i] . "\n");
472 0 0       0 if ( $reverse_split_subtype->[$i] eq $reverse_split_subtypes_0->[$i] ) {
473 0 0       0 if ( $new_subtype eq q{} ) {
474 0         0 $new_subtype = $reverse_split_subtype->[$i];
475             }
476             else {
477 0         0 $new_subtype = $reverse_split_subtype->[$i] . '_' . $new_subtype;
478             }
479             }
480             }
481 0 0       0 if ( $new_subtype ne q{} ) {
482 0         0 $subtype = $new_subtype;
483             }
484             else {
485 0         0 $is_homogeneous = 0;
486             }
487             }
488             }
489              
490             # RPerl::diag('in rperltypes::types_recurse(), inside hashref, have $subtype = ' . $subtype . "\n");
491             }
492 0 0       0 if ($is_homogeneous) {
493              
494             # DEV NOTE: flatten unknown_arrayref to arrayref
495 0 0 0     0 if ( ( not defined $subtype ) or ( $subtype eq 'unknown' ) ) { $subtype = q{} }
  0         0  
496 0 0       0 if ( $subtype ne q{} ) {
497 0         0 my string $type_old = $type;
498 0         0 $type = $subtype . '_' . $type;
499 0         0 $types->{$type} = $types->{$type_old};
500 0         0 delete $types->{$type_old};
501             }
502             }
503              
504             # RPerl::diag('in rperltypes::types_recurse(), bottom of hashref, have $type = ' . $type . "\n");
505             }
506             else {
507 0         0 $type = '__UNRECOGNIZED_TYPE';
508             }
509 0         0 return [ $type, $types ];
510             }
511             }
512             1;
513              
514              
515             # [[[ C++ TYPE CONTROL ]]]
516             package RPerl;
517             if ( not defined $RPerl::INCLUDE_PATH ) {
518             our $INCLUDE_PATH = '/FAILURE/BECAUSE/RPERL/INCLUDE/PATH/NOT/YET/SET';
519             }
520             1; # suppress warnings about typo in types_enable() below
521              
522             package # hide from PAUSE indexing
523             rperltypes;
524              
525             #our void $types_input_enable = sub { (my $types_input) = @_; # NEED FIX: RPerl typed functions not working in types.pm, must call as normal Perl function
526             sub types_enable {
527 19     19 0 169317 ( my $types_input ) = @_;
528              
529             # RPerl::diag('in rperltypes::types_enable(), received $types_input = ' . $types_input . "\n");
530              
531 19 50 66     173 if (($types_input ne 'PERL') and ($types_input ne 'CPP')) {
532 0         0 croak q{ERROR ERPTY00: Invalid RPerl types '} . $types_input . q{' specified where PERL or CPP expected, croaking};
533             }
534              
535 19         94 $RPerl::TYPES_CCFLAG = ' -D__' . $types_input . '__TYPES';
536              
537             # RPerl::diag('in rperltypes::types_enable(), set $RPerl::TYPES_CCFLAG = ' . $RPerl::TYPES_CCFLAG . "\n");
538              
539 19         93 return;
540             }
541              
542             1; # end of package