File Coverage

blib/lib/rperltypes.pm
Criterion Covered Total %
statement 113 314 35.9
branch 7 136 5.1
condition 2 27 7.4
subroutine 34 38 89.4
pod 0 1 0.0
total 156 516 30.2


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