File Coverage

blib/lib/Datify.pm
Criterion Covered Total %
statement 456 552 82.6
branch 220 370 59.4
condition 82 178 46.0
subroutine 69 76 90.7
pod 33 36 91.6
total 860 1212 70.9


line stmt bran cond sub pod time code
1 5     5   260878 use v5.14;
  5         19  
2 5     5   30 use warnings;
  5         8  
  5         215  
3              
4             package Datify v0.20.060;
5             # ABSTRACT: Simple stringification of data.
6              
7              
8 5     5   32 use mro (); #qw( get_linear_isa );
  5         18  
  5         120  
9 5     5   26 use overload (); #qw( Method Overloaded );
  5         8  
  5         76  
10              
11 5     5   21 use Carp (); #qw( carp croak );
  5         8  
  5         86  
12 5     5   27 use List::Util (); #qw( reduce sum );
  5         10  
  5         168  
13 5     5   2470 use LooksLike v0.20.060 (); #qw( number numeric representation );
  5         19165  
  5         159  
14 5     5   36 use Scalar::Util (); #qw( blessed refaddr reftype );
  5         9  
  5         106  
15 5     5   2476 use String::Tools v0.19.045 (); #qw( stitch stringify subst );
  5         7554  
  5         147  
16 5     5   2373 use Sub::Util 1.40 (); #qw( subname );
  5         1538  
  5         3907  
17              
18              
19             ### Constructor ###
20              
21              
22             sub new {
23 2835   50 2835 1 15909 my $class = shift || __PACKAGE__;
24              
25 2835         3927 my %self = ();
26 2835 50       5896 if ( defined( my $blessed = Scalar::Util::blessed($class) ) ) {
27 0         0 %self = %$class; # shallow copy
28 0         0 $class = $blessed;
29             }
30 2835 100       7169 return @_ ? bless( \%self, $class )->set(@_) : bless( \%self, $class );
31             }
32              
33              
34              
35             ### Accessor ###
36              
37              
38              
39              
40             sub exists {
41 7305     7305 1 9356 my $self = shift;
42 7305 50       13302 return unless my $count = scalar(@_);
43              
44 7305         11203 my $SETTINGS = $self->_settings;
45 7305 100       17036 if ( Scalar::Util::blessed($self) ) {
46             return $count == 1
47             ? exists $self->{ $_[0] } && $self
48             || exists $SETTINGS->{ $_[0] } && $SETTINGS
49             : map {
50 7261 50 100     39319 exists $self->{ $_ } && $self
51 0 0 0     0 || exists $SETTINGS->{ $_ } && $SETTINGS
      0        
52             } @_;
53             } else {
54             return
55             $count == 1 ? exists $SETTINGS->{ $_[0] } && $SETTINGS
56 44 0 66     520 : map { exists $SETTINGS->{ $_ } && $SETTINGS } @_;
  0 50       0  
57             }
58             }
59              
60              
61              
62             sub _get_setting {
63 7267     7267   13754 my $setting = $_[0]->exists( local $_ = $_[1] );
64 7267 100       23867 return $setting ? $setting->{$_} : do {
65 689 50       1270 Carp::carp( 'Unknown key ', $_ )
66             unless $_[0]->_internal(1);
67             undef
68 689         1763 };
69             }
70             sub get {
71 6399     6399 1 14699 my $self = shift;
72 6399         8392 my $count = scalar(@_);
73              
74 6399 100       16419 if ( defined( my $class = Scalar::Util::blessed($self) ) ) {
75             return
76 0         0 $count == 0 ? ( %{ $self->_settings }, %$self )
77             : $count == 1 ? $self->_get_setting(shift)
78 6392 100       14973 : map { $self->_get_setting($_) } @_;
  1316 50       2231  
79             } else {
80             return
81 1         3 $count == 0 ? %{ $self->_settings }
82             : $count == 1 ? $self->_get_setting(shift)
83 7 50       25 : map { $self->_get_setting($_) } @_;
  0 100       0  
84             }
85             }
86              
87              
88             ### Setter ###
89              
90              
91             sub set {
92 1238     1238 1 10119 my $self = shift;
93 1238 50       2367 return $self unless @_;
94 1238         3354 my %set = @_;
95              
96 1238         1828 my $return;
97             my $class;
98 1238 100       3348 if ( defined( $class = Scalar::Util::blessed($self) ) ) {
99             # Make a shallow copy
100 1111         4777 $self = bless { %$self }, $class;
101 1111         1997 $return = 0;
102             } else {
103 127         220 $class = $self;
104 127         235 $self = $class->_settings;
105 127         194 $return = 1;
106             }
107              
108 1238 100       2776 delete $self->{keyword_set} if ( $set{keywords} );
109 1238         2191 delete $self->{"_tr$_"} for grep { exists $set{"quote$_"} } ( 1, 2, 3 );
  3714         8368  
110              
111 1238         2645 my $internal = $class->_internal;
112 1238         4256 while ( my ( $k, $v ) = each %set ) {
113 1435 100 100     3039 Carp::carp( 'Unknown key ', $k )
114             unless $internal || $class->exists($k);
115 1435 100 100     5109 study($v) if defined($v) && !ref($v);
116 1435         5262 $self->{$k} = $v;
117             }
118              
119 1238         3548 return ( $self, $class )[$return];
120             }
121              
122              
123              
124              
125             sub add_handler {
126 0     0 1 0 my $self = &self;
127 0         0 my $code = pop;
128 0 0       0 my $pkg = length( $_[0] ) ? shift : caller;
129              
130 0 0       0 if ( my $name = _nameify($pkg) ) {
131 5     5   42 no strict 'refs';
  5         10  
  5         6350  
132 0         0 *{$name} = $code;
  0         0  
133             }
134             }
135              
136              
137              
138              
139             __PACKAGE__->set(
140             # Var options
141             name => '$self',
142             assign => '$var = $value;',
143             list => '($_)',
144             list_sep => ', ',
145             beautify => undef,
146             );
147              
148             # Name can be any of the following:
149             # * package name (optional) followed by:
150             # * normal word
151             # * ::
152             # * Perl special variable:
153             # * numbers
154             # * punctuation
155             # * control character
156             # * control word
157             my $sigils = '[\\x24\\x25\\x40]'; # $%@
158             my $package = '[[:alpha:]]\w*(?:\::\w+)*';
159             my $word = '[[:alpha:]_]\w*';
160             my $digits = '\d+';
161             my $punct = '[[:punct:]]';
162             my $cntrl = '(?:[[:cntrl:]]|\^[[:upper:]])';
163             my $cntrl_word = "$cntrl$word";
164             my $varname
165             = '(?:' . join( '|', $word, $digits, $punct, $cntrl, $cntrl_word ) . ')';
166             $varname .= "|\\{\\s*$varname\\s*\\}";
167             $varname = "(?:$varname)";
168              
169              
170             sub varify {
171 41     41 1 17368 my $self = &self;
172 41         76 my ($sigil, $name);
173 41 50 33     195 if ( defined $_[0] && !ref $_[0] ) {
174 41         812 ( $sigil, $name )
175             = $_[0] =~ /^($sigils)?((?:$package\::)?$varname|$package\::)$/;
176 41 50       140 shift if length $name;
177             }
178 41 50       98 my $value = 1 == @_ ? shift : \@_;
179              
180 41 50       73 if ( length $name ) {
181 41 50       116 if ( $name =~ /[[:cntrl:]]/ ) {
182 0         0 $name =~ s/([[:cntrl:]])/'^' . chr(64 + ord($1) % 64)/e;
  0         0  
183 0         0 $name =~ s/($cntrl_word)(?!\s*\})/\{$1\}/;
184             }
185             } else {
186 0 0       0 if ( defined( my $ref = Scalar::Util::blessed($value) ) ) {
187 0         0 $name = _nameify($ref);
188             } else {
189 0         0 $name = $self->get('name');
190             }
191             }
192 41 50       83 Carp::croak "Missing name" unless ( length $name );
193              
194 41 100       80 unless ($sigil) {
195 22         40 my $ref = ref $value;
196 22 50       61 $sigil
    50          
197             = $ref eq 'ARRAY' ? '@'
198             : $ref eq 'HASH' ? '%'
199             : '$';
200             }
201 41         77 $name = $sigil . $name;
202 41         104 $self = $self->set( name => $name );
203              
204 41 0       135 $value
    0          
    50          
205             = $sigil eq '$' ? $self->scalarify($value)
206             : $sigil eq '@' ? _subst( $self->get('list'), $self->listify($value) )
207             : $sigil eq '%' ? _subst( $self->get('list'), $self->pairify($value) )
208             : $self->scalarify($value)
209             ;
210              
211 41         84 $value = _subst( $self->get('assign'), var => $name, value => $value );
212 41 50       3857 if ( my $beautify = $self->get('beautify') ) {
213 0         0 return $beautify->($value);
214             } else {
215 41         212 return $value;
216             }
217             }
218              
219              
220              
221             ### Scalar: undef ###
222              
223              
224             __PACKAGE__->set(
225             # Undef options
226             null => 'undef',
227             );
228              
229              
230             sub undefify {
231 17     17 1 43 my $self = &self;
232 17 50 33     56 return $self->scalarify(shift) if @_ and defined($_[0]);
233 17         44 return $self->get('null');
234             }
235              
236              
237              
238             ### Scalar: boolean ###
239              
240              
241             __PACKAGE__->set(
242             # Boolean options
243             true => 1,
244             false => "''",
245             );
246              
247              
248             sub booleanify {
249 0     0 1 0 my $self = &self;
250 0 0       0 local $_ = shift if @_;
251 0 0       0 return $self->undefify unless defined;
252 0 0       0 return $_ ? $self->get('true') : $self->get('false');
253             }
254              
255              
256              
257             ### Scalar: single-quoted string ###
258              
259              
260             sub stringify1 {
261 400     400 1 4533 my $self = &self;
262 400 50       1110 local $_ = shift if @_;
263 400 50       817 return $self->undefify unless defined;
264 400 50       721 $_ = String::Tools::stringify($_) if ref;
265 400         781 my $quote1 = $self->get('quote1');
266 400   66     1624 my ( $open, $close ) = $self->_get_delim( shift // $quote1 );
267              
268 400         868 $self = $self->set( encode => $self->get('encode1') );
269 400         842 my $to_encode = $self->_to_encode( $open, $close );
270 400         1687 s/([$to_encode])/$self->_encode_char($1)/eg;
  116         313  
271              
272 400 100       969 if ( $quote1 ne $open ) {
273 1 50       5 if ( $open =~ /\w/ ) {
274 0         0 $open = ' ' . $open;
275 0         0 $close = ' ' . $close;
276             }
277 1         3 $open = $self->get('q1') . $open;
278             }
279              
280 400         3273 return sprintf '%s%s%s', $open, $_, $close;
281             }
282              
283              
284              
285             ### Scalar: double-quoted string ###
286              
287              
288             sub stringify2 {
289 60     60 1 4424 my $self = &self;
290 60 50       173 local $_ = shift if @_;
291 60 50       130 return $self->undefify unless defined;
292 60 50       123 $_ = String::Tools::stringify($_) if ref;
293 60         132 my $quote2 = $self->get('quote2');
294 60   33     234 my ( $open, $close ) = $self->_get_delim( shift // $quote2 );
295              
296 60         94 my @sigils;
297 60 50       115 if ( my $sigils = $self->get('sigils') ) {
298 60         213 push @sigils, split //, $sigils;
299             }
300              
301             # quote char(s), sigils, and backslash.
302 60         143 $self = $self->set( encode => $self->get('encode2') );
303 60         172 my $to_encode = $self->_to_encode( $open, $close, @sigils );
304 60         535 s/([$to_encode])/$self->_encode_char($1)/eg;
  179         376  
305              
306 60 50       187 if ( $quote2 ne $open ) {
307 0 0       0 if ( $open =~ /\w/ ) {
308 0         0 $open = ' ' . $open;
309 0         0 $close = ' ' . $close;
310             }
311 0         0 $open = $self->get('q2') . $open;
312             }
313              
314 60         467 return sprintf '%s%s%s', $open, $_, $close;
315             }
316              
317              
318              
319             ### Scalar: string ###
320              
321              
322             __PACKAGE__->set(
323             # String options
324             quote => undef, # Auto
325             quote1 => "'",
326             #_tr1 => q!tr\\'\\'\\!,
327             quote2 => '"',
328             #_tr2 => q!tr\\"\\"\\!,
329             q1 => 'q',
330             q2 => 'qq',
331             sigils => '$@',
332             longstr => 1_000,
333             encode1 => {
334             0x5c => '\\\\',
335              
336             byte => '\\%c',
337             },
338             encode2 => {
339             map( { ord( eval qq!"$_"! ) => $_ } qw( \0 \a \b \t \n \f \r \e ) ),
340             #0x00 => '\\0',
341             #0x07 => '\\a',
342             #0x08 => '\\b',
343             #0x09 => '\\t',
344             #0x0a => '\\n',
345             #0x0c => '\\f',
346             #0x0d => '\\r',
347             #0x1b => '\\e',
348             0x5c => '\\\\',
349              
350             also => '[:cntrl:]',
351             byte => '\\x%02x',
352             #utf => 8,
353             wide => '\\x{%04x}',
354             #vwide => '\\x{%06x}',
355             },
356              
357             do {
358 5     5   43 no warnings 'qw';
  5         9  
  5         11197  
359             # To silence the warnings:
360             # Possible attempt to put comments in qw() list
361             # Possible attempt to separate words with commas
362              
363             qpairs => [ qw\ () <> [] {} \ ],
364             qquotes => [
365             # Punctuation, excluding ", ', \, and _
366             qw\ ! # % & * + , - . / : ; = ? ^ | ~ $ @ ` \
367             ],
368             },
369             );
370              
371              
372             sub stringify {
373 422     422 1 11361 my $self = &self;
374 422 50       1064 local $_ = shift if @_;
375 422 50       786 return $self->undefify unless defined;
376 422 50       734 $_ = String::Tools::stringify($_) if ref;
377 422         607 local $@ = undef;
378              
379 422         855 my ( $quote, $quote1, $quote2 ) = $self->get(qw( quote quote1 quote2 ));
380 422 50       977 if ($quote) {
381 0 0 0     0 return $self->stringify1($_) if $quote1 && $quote1 eq $quote;
382 0 0 0     0 return $self->stringify2($_) if $quote2 && $quote2 eq $quote;
383 0         0 Carp::croak("Bad setting for quote: $quote");
384             }
385              
386             # Long strings or strings with special characters
387 422         814 my $longstr = $self->get('longstr');
388 422         825 my $encode2 = $self->get('encode2');
389 422   33     1469 my $also = $encode2 && $encode2->{also};
390 422 100 33     3461 return $self->stringify2($_)
      66        
      66        
391             if ( ( $longstr && $longstr < length() ) || ( $also && /[$also]/ ) );
392              
393 410         940 my $tr1 = $self->get('_tr1');
394 410 50       1445 $self = $self->set( _tr1 => $tr1 = "tr\\$quote1\\$quote1\\" )
395             if ( not $tr1 );
396 410   50     23341 my $single_quotes = eval $tr1 // die $@;
397 410 100       1873 return $self->stringify1($_) unless $single_quotes;
398              
399 21         114 my ( $sigils, $tr2 ) = $self->get(qw( sigils _tr2 ));
400 21 50       122 $self = $self->set( _tr2 => $tr2 = "tr\\$quote2$sigils\\$quote2$sigils\\" )
401             if ( not $tr2 );
402 21   50     1134 my $double_quotes = eval $tr2 // die $@;
403 21 100       129 return $self->stringify2($_) unless $double_quotes;
404              
405 1         3 return $self->stringify1( $_, $self->_find_q($_) );
406             }
407              
408              
409              
410             ### Scalar: number ###
411             # Adapted from Perl FAQ "How can I output my numbers with commas added?"
412              
413              
414             __PACKAGE__->set(
415             # Number options
416             infinite => "'inf'",
417             -infinite => "'-inf'",
418             nonnumber => "'nan'",
419             num_sep => '_',
420             );
421              
422              
423             sub is_numeric {
424 3340     3340 1 5011 my $self = &self;
425 3340 50       7067 local $_ = shift if @_;
426              
427 3340 50       5775 return undef unless defined;
428              
429 3340 50       5468 if (ref) {
430 0 0       0 if ( my $method = $self->overloaded($_) ) {
431 0         0 $_ = $_->$method();
432             }
433             else {
434 0         0 return '';
435             }
436             }
437              
438 3340         6232 return LooksLike::numeric($_);
439             }
440              
441              
442             sub numify {
443 384     384 1 35693 my $self = &self;
444 384 50       860 local $_ = shift if @_;
445              
446 384 100       709 return $self->undefify unless defined;
447              
448 383 100       633 if ( $self->is_numeric($_) ) {
    100          
449 369 50       4839 return $_ unless my $sep = $self->get('num_sep');
450              
451             # Fractional portion
452 369         1025 s{^(\s*[-+]?\d*\.\d\d)(\d+)} [${1}$sep${2}];
453 369         2090 1 while s{^(\s*[-+]?\d*\.(?:\d+$sep)+\d\d\d)(\d+)}[${1}$sep${2}];
454              
455             # Whole portion
456 369         1414 1 while s{^(\s*[-+]?\d+)(\d{3})} [${1}$sep${2}];
457              
458 369         1346 return $_;
459             }
460             elsif ( LooksLike::number($_) ) {
461 12         265 return LooksLike::representation(
462             $_,
463             "infinity" => $self->get('infinite'),
464             "-infinity" => $self->get('-infinite'),
465             "nan" => $self->get('nonnumber')
466             );
467             }
468              
469 2         328 return $self->get('nonnumber');
470             }
471              
472              
473              
474             ### Scalar ###
475              
476              
477             __PACKAGE__->set(
478             # Scalar options
479             scalar_ref => '\do{1;$_}',
480             );
481              
482              
483             sub scalarify {
484 548     548 1 18453 my $self = &self;
485 548 50       1345 local $_ = shift if @_;
486              
487 548   100     1086 my $value = $self->_cache_get($_) // $self->_scalarify($_);
488 548 100       13040 $self->isa( scalar caller )
489             ? $self->_cache_add( $_ => $value )
490             : $self->_cache_reset($_);
491 548         1340 return $value;
492             }
493              
494             sub _scalarify {
495 542     542   956 my $self = &self;
496 542 50       1340 local $_ = shift if @_;
497              
498 542 100       1183 return $self->undefify unless defined $_;
499              
500 526 100       1211 if ( defined( my $blessed = Scalar::Util::blessed($_) ) ) {
501             return
502 36 100       104 $blessed eq 'Regexp' ? $self->regexpify($_)
503             : $self->objectify($_);
504             }
505              
506 490         865 my $ref = Scalar::Util::reftype $_;
507 490 100       963 if ( not $ref ) {
508             # Handle GLOB, LVALUE, and VSTRING
509 384         721 my $ref2 = ref \$_;
510             return
511 384 100       1963 $ref2 eq 'GLOB' ? $self->globify($_)
    50          
    100          
    50          
    100          
512             : $ref2 eq 'LVALUE' ? $self->lvalueify($_)
513             : $ref2 eq 'VSTRING' ? $self->vstringify($_)
514             : $ref2 eq 'SCALAR' ? (
515             LooksLike::number($_)
516             ? $self->numify($_)
517             : $self->stringify($_)
518             )
519             : $self->stringify($_);
520             }
521              
522             return
523             $ref eq 'ARRAY' ? $self->arrayify(@$_)
524             : $ref eq 'CODE' ? $self->codeify($_)
525             : $ref eq 'FORMAT' ? $self->formatify($_)
526             : $ref eq 'HASH' ? $self->hashify($_)
527             : $ref eq 'IO' ? $self->ioify($_)
528             : $ref eq 'REF' ? $self->refify($$_)
529             : $ref eq 'REGEXP' ? $self->regexpify($_) # ???
530 106 50       475 : do {
    100          
    50          
    100          
    100          
    100          
    100          
531 18   33     54 my $reference = $self->get( lc($ref) . '_reference' )
532             || $self->get('reference');
533              
534 18 50       91 $ref eq 'GLOB' ? _subst( $reference, $self->globify($$_) )
    100          
    100          
    100          
535             : $ref eq 'LVALUE' ? _subst( $reference, $self->lvalueify($$_) )
536             : $ref eq 'SCALAR' ? _subst( $reference, $self->scalarify($$_) )
537             : $ref eq 'VSTRING' ? _subst( $reference, $self->vstringify($$_) )
538             : $self->objectify($_)
539             ;
540             };
541             }
542              
543              
544              
545             ### Scalar: LValue ###
546              
547              
548             __PACKAGE__->set(
549             # LValue options
550             lvalue => 'substr($lvalue, 0)',
551             );
552              
553              
554             sub lvalueify {
555 4     4 1 8 my $self = &self;
556 4         11 return _subst( $self->get('lvalue'), lvalue => $self->stringify(shift) );
557             }
558              
559              
560              
561             ### Scalar: VString ###
562              
563              
564             __PACKAGE__->set(
565             # VString options
566             vformat => 'v%vd',
567             #vformat => 'v%*vd',
568             #vsep => '.',
569             );
570              
571              
572             sub vstringify {
573 4     4 1 6 my $self = &self;
574 4 50       10 if ( defined( my $vsep = $self->get('vsep') ) ) {
575 0         0 return sprintf $self->get('vformat'), $vsep, shift;
576             } else {
577 4         9 return sprintf $self->get('vformat'), shift;
578             }
579             }
580              
581              
582              
583             ### Regexp ###
584              
585              
586             __PACKAGE__->set(
587             # Regexp options
588             quote3 => '/',
589             #_tr3 => q!tr\\/\\/\\!,
590             q3 => 'qr',
591              
592             encode3 => {
593             map( { ord( eval qq!"$_"! ) => $_ } qw( \0 \a \t \n \f \r \e ) ),
594             #0x00 => '\\0',
595             #0x07 => '\\a',
596             #0x09 => '\\t',
597             #0x0a => '\\n',
598             #0x0c => '\\f',
599             #0x0d => '\\r',
600             #0x1b => '\\e',
601              
602             also => '[:cntrl:]',
603             byte => '\\x%02x',
604             wide => '\\x{%04x}',
605             #vwide => '\\x{%06x}',
606             },
607             );
608              
609              
610             sub regexpify {
611 4     4 1 9 my $self = &self;
612 4 50       12 local $_ = shift if @_;
613 4         7 local $@ = undef;
614              
615 4         8 my ( $quote3, $tr3 ) = $self->get(qw( quote3 _tr3 ));
616 4 50       19 $self = $self->set( _tr3 => $tr3 = "tr\\$quote3\\$quote3\\" )
617             if ( not $tr3 );
618 4   50     269 my $quoter = eval $tr3 // die $@;
619 4 50 33     34 my ( $open, $close )
620             = $self->_get_delim(
621             shift // $quoter ? $self->_find_q($_) : $self->get('quote3') );
622              
623             # Everything but the quotes should be escaped already.
624 4         12 $self = $self->set( encode => $self->get('encode3') );
625 4         13 my $to_encode = $self->_to_encode( $open, $close );
626 4         36 s/([$to_encode])/$self->_encode_char($1)/eg;
  0         0  
627              
628 4 50       13 if ( $open =~ /\w/ ) {
629 0         0 $open = ' ' . $open;
630 0         0 $close = ' ' . $close;
631             }
632              
633 4         10 $open = $self->get('q3') . $open;
634              
635 4         31 return sprintf '%s%s%s', $open, $_, $close;
636             }
637              
638              
639              
640             ### List/Array ###
641              
642              
643             sub listify {
644 24     24 1 44 my $self = &self;
645 24         37 my @values;
646 24         70 for ( my $i = 0; $i < @_; $i++ ) {
647 116         222 my $value = $_[$i];
648 116         321 $self = $self->_push_position("[$i]");
649 116         257 push @values, $self->scalarify($value);
650 116         254 $self->_pop_position;
651             }
652 24         56 return join( $self->get('list_sep'), @values );
653             }
654              
655              
656              
657              
658             __PACKAGE__->set(
659             # Array options
660             array_ref => '[$_]',
661             );
662              
663              
664             sub arrayify {
665 24     24 1 45 my $self = &self;
666 24         54 return _subst( $self->get('array_ref'), $self->listify(@_) );
667             }
668              
669              
670              
671             ### Hash ###
672              
673              
674             sub is_keyword {
675 322     322 1 554 my $self = &self;
676              
677 322         582 my $keyword_set = $self->get('keyword_set');
678 322 100       651 if ( not $keyword_set ) {
679 63   50     119 my $keywords = $self->get('keywords') // [];
680 63 50       125 return unless @$keywords;
681 63         113 $keyword_set = { map { $_ => 1 } @$keywords };
  63         227  
682 63         145 $self->{keyword_set} = $keyword_set;
683             }
684 322         2157 return exists $keyword_set->{ +shift };
685             }
686              
687              
688             sub keyify {
689 489     489 1 791 my $self = &self;
690 489 50       1166 local $_ = shift if @_;
691              
692 489 50       933 return $self->undefify unless defined;
693 489 50       894 return $_ if ref;
694              
695 489 100 33     947 if ( $self->is_numeric($_) ) {
    100 66        
696 167         2737 return $self->numify($_);
697             } elsif ( length() < $self->get('longstr')
698             && !$self->is_keyword($_)
699             && /\A-?[[:alpha:]_]\w*\z/ )
700             {
701             # If the key would be autoquoted by the fat-comma (=>),
702             # then there is no need to quote it.
703              
704 290         968 return "$_"; # Make sure it's stringified.
705             }
706 32         81 return $self->stringify($_);
707             }
708              
709              
710              
711              
712             sub keysort($$);
713             BEGIN {
714 5     5   43 no warnings 'qw';
  5         33  
  5         627  
715 5     5   46 my $keysort = String::Tools::stitch(qw(
716             sub keysort($$) {
717             my ( $a, $b ) = @_;
718             my $numa = Datify->is_numeric($a);
719             my $numb = Datify->is_numeric($b);
720             return(
721             ( $numa && $numb ? $a <=> $b
722             : $numa ? -1
723             : $numb ? +1
724             : $a_cmp__b )
725             || $a cmp $b
726             );
727             }
728             ));
729 5 50       4190 my $a_cmp__b
730             = $^V >= v5.16.0
731             ? 'CORE::fc($a) cmp CORE::fc($b)'
732             : 'lc($a) cmp lc($b)';
733 5         29 $keysort = String::Tools::subst( $keysort, a_cmp__b => $a_cmp__b );
734 5 50 50 1234 1 6931 eval($keysort) or $@ and die $@;
  1234   100     3085  
  1234         2729  
  1234         36349  
  1234         38251  
735             }
736              
737              
738              
739             sub hashkeys {
740 58     58 1 97 my $self = shift;
741 58         76 my $hash = shift;
742              
743 58         197 my @keys = keys %$hash;
744 58 50       114 if ( my $ref = ref( my $keyfilter = $self->get('keyfilter') ) ) {
745 0         0 my $keyfilternot = !$self->get('keyfilterdefault');
746 0         0 my $keyfilterdefault = !$keyfilternot;
747 0 0 0     0 if ( $ref eq 'ARRAY' || $ref eq 'HASH' ) {
    0          
    0          
    0          
748             my %keyfilterhash
749             = $ref eq 'ARRAY'
750 0 0       0 ? ( map { $_ => $keyfilternot } @$keyfilter )
  0         0  
751             : %$keyfilter;
752             $self->{keyfilter} = $keyfilter = sub {
753             exists $keyfilterhash{$_}
754 0 0   0   0 ? $keyfilterhash{$_}
755             : $keyfilterdefault;
756 0         0 };
757             } elsif ( $ref eq 'CODE' ) {
758             # No-op, just use the code provided
759             } elsif ( $ref eq 'Regexp' ) {
760 0         0 my $keyfilterregexp = $keyfilter;
761             $self->{keyfilter} = $keyfilter = sub {
762 0 0   0   0 m/$keyfilterregexp/ ? $keyfilternot : $keyfilterdefault;
763 0         0 };
764             } elsif ( $ref eq 'SCALAR' ) {
765 0         0 my $keyfiltervalue = $$keyfilter;
766 0     0   0 $self->{keyfilter} = $keyfilter = sub {$keyfiltervalue};
  0         0  
767             }
768 0         0 @keys = grep { $keyfilter->() } @keys;
  0         0  
769             }
770 58 50       130 if ( my $keysort = $self->get('keysort') ) {
771 58         864 @keys = sort $keysort @keys;
772             }
773 58         244 return @keys;
774             }
775              
776             sub hashkeyvals {
777 28     28 0 47 my $self = shift;
778 28         38 my $hash = shift;
779              
780 28         67 return map { $_ => $hash->{$_} } $self->hashkeys($hash);
  288         563  
781             }
782              
783              
784             sub pairify {
785 28     28 1 64 my $self = &self;
786 28 50       73 if (1 == @_) {
787 28         67 my $ref = Scalar::Util::reftype $_[0];
788 28 50       87 if ( $ref eq 'ARRAY' ) { @_ = @{ +shift } }
  0 50       0  
  0         0  
789 28         64 elsif ( $ref eq 'HASH' ) { @_ = $self->hashkeyvals(shift) }
790             }
791             # Use for loop in order to preserve the order of @_,
792             # rather than each %{ { @_ } }, which would mix-up the order.
793 28         59 my @list;
794 28         70 my $pair = $self->get('pair');
795 28         92 for ( my $i = 0; $i < @_ - 1; $i += 2 ) {
796 288         19810 my ( $k, $v ) = @_[ $i, $i + 1 ];
797 288         710 my $key = $self->keyify($k);
798 288         840 $self = $self->_push_position("{$key}");
799 288         640 my $val = $self->scalarify($v);
800 288         766 $self->_pop_position;
801 288         639 push @list, _subst( $pair, key => $key, value => $val );
802             }
803 28         1508 return join( $self->get('list_sep'), @list );
804             }
805              
806              
807              
808              
809             __PACKAGE__->set(
810             # Hash options
811             hash_ref => '{$_}',
812             pair => '$key => $value',
813             keysort => \&Datify::keysort,
814             keyfilter => undef,
815             keyfilterdefault => 1,
816             keywords => [qw(undef)],
817             #keyword_set => { 'undef' => 1 },
818             );
819              
820              
821             sub hashify {
822 28     28 1 54 my $self = &self;
823 28         406 return _subst( $self->get('hash_ref'), $self->pairify(@_) );
824             }
825              
826              
827              
828             ### Objects ###
829              
830              
831             sub overloaded {
832 32     32 1 61 my $self = &self;
833 32 50       71 my $object = @_ ? shift : $_;
834              
835 32 50 33     162 return unless defined( Scalar::Util::blessed($object) )
836             && overload::Overloaded($object);
837              
838 0   0     0 my $overloads = $self->get('overloads') || [];
839 0         0 foreach my $overload (@$overloads) {
840 0 0       0 if ( my $method = overload::Method( $object => $overload ) ) {
841 0         0 return $method;
842             }
843             }
844 0         0 return;
845             }
846              
847              
848              
849              
850             __PACKAGE__->set(
851             # Object options
852             overloads => [ '""', '0+' ],
853             object => 'bless($data, $class_str)',
854             #object => '$class->new($data)',
855             #object => '$class=$data',
856             );
857              
858              
859             sub objectify {
860 32     32 1 55 my $self = &self;
861 32 50       74 my $object = @_ ? shift : $_;
862              
863 32 50       91 return $self->scalarify($object)
864             unless defined( my $class = Scalar::Util::blessed($object) );
865              
866 32         52 my $data;
867 32 50       71 if (0) {
    50          
    50          
868 0         0 } elsif ( my $code = $self->_find_handler($class) ) {
869 0         0 return $self->$code($object);
870             } elsif ( my $method = $self->overloaded($object) ) {
871 0         0 $data = $self->scalarify( $object->$method() );
872             } elsif ( my $attrkeyvals = $object->can('_attrkeyvals') ) {
873             # TODO: Look this up via meta-objects
874 0         0 $data = $self->hashify( $object->$attrkeyvals() );
875             } else {
876 32         1778 $data = Scalar::Util::reftype $object;
877              
878 32 50       184 $data
    50          
    50          
    100          
    100          
    50          
    50          
    50          
    100          
879             = $data eq 'ARRAY' ? $self->arrayify( @$object )
880             : $data eq 'CODE' ? $self->codeify( $object )
881             : $data eq 'FORMAT' ? $self->formatify( $object )
882             : $data eq 'GLOB' ? $self->globify( $object )
883             : $data eq 'HASH' ? $self->hashify( $object )
884             : $data eq 'IO' ? $self->ioify( $object )
885             : $data eq 'REF' ? $self->refify( $$object )
886             : $data eq 'REGEXP' ? $self->regexpify( $object )
887             : $data eq 'SCALAR' ? $self->refify( $$object )
888             : "*UNKNOWN{$data}";
889             }
890              
891 32         2178 return _subst(
892             $self->get('object'),
893             class_str => $self->stringify($class),
894             class => $class,
895             data => $data
896             );
897             }
898              
899              
900              
901             ### Objects: IO ###
902              
903              
904             __PACKAGE__->set(
905             # IO options
906             io => '*$name{IO}',
907             );
908              
909              
910              
911             sub ioify {
912 4     4 1 8 my $self = &self;
913 4 50       11 my $io = @_ ? shift : $_;
914              
915 4         7 my $ioname = 'UNKNOWN';
916 4         7 foreach my $ioe (qw( IN OUT ERR )) {
917 5     5   44 no strict 'refs';
  5         11  
  5         15155  
918 8 100       12 if ( *{"main::STD$ioe"}{IO} == $io ) {
  8         47  
919 4         6 $ioname = "STD$ioe";
920 4         7 last;
921             }
922             }
923             # TODO
924             #while ( my ( $name, $glob ) = each %main:: ) {
925             # no strict 'refs';
926             # if ( defined( *{$glob}{IO} ) && *{$glob}{IO} == $io ) {
927             # keys %main::; # We're done, so reset each()
928             # $ioname = $name;
929             # last;
930             # }
931             #}
932 4         11 return _subst( $self->get('io'), name => $ioname );
933             }
934              
935              
936              
937             ### Other ###
938              
939              
940             __PACKAGE__->set(
941             # Code options
942             code => 'sub {$body}',
943             codename => '\&$codename',
944             body => '...',
945             );
946              
947              
948             sub codeify {
949 8     8 1 20 my $self = &self;
950              
951 8         21 my $template = $self->get('code');
952 8         21 my %data = ( body => $self->get('body') );
953 8 50 33     41 if ( @_ && defined( $_[0] ) ) {
954 8         17 local $_ = shift;
955 8 50       29 if ( my $ref = Scalar::Util::reftype($_) ) {
956 8 50       23 if ( $ref eq 'CODE' ) {
957 8 100       78 if ( ( my $subname = Sub::Util::subname($_) )
958             !~ /\A(?:\w+\::)*__ANON__\z/ )
959             {
960 4   33     14 $template = $self->get('codename') // $template;
961 4         17 %data = ( codename => $subname );
962             }
963             } else {
964 0         0 %data = ( body => $self->scalarify($_) );
965             }
966             } else {
967 0         0 %data = ( body => $_ );
968             }
969             }
970 8         29 return _subst( $template, %data );
971             }
972              
973              
974              
975              
976             __PACKAGE__->set(
977             # Reference options
978             reference => '\\$_',
979             dereference => '$referent->$place',
980             nested => '$referent$place',
981             );
982              
983              
984             sub refify {
985 52     52 1 88 my $self = &self;
986 52 50       131 local $_ = shift if @_;
987 52         99 return _subst( $self->get('reference'), $self->scalarify($_) );
988             }
989              
990              
991              
992              
993             __PACKAGE__->set(
994             # Format options
995             format => "format UNKNOWN =\n.\n",
996             );
997              
998              
999             sub formatify {
1000 4     4 1 7 my $self = &self;
1001             #Carp::croak "Unhandled type: ", ref shift;
1002 4         10 return $self->get('format');
1003             }
1004              
1005              
1006              
1007              
1008             sub globify {
1009 4     4 1 6 my $self = &self;
1010 4         16 my $name = '' . shift;
1011 4 50       125 if ( $name =~ /^\*$package\::(?:$word|$digits)?$/ ) {
1012 4         31 $name =~ s/^\*main::/*::/;
1013             } else {
1014 0         0 $name =~ s/^\*($package\::.+)/'*{' . $self->stringify($1) . '}'/e;
  0         0  
1015             }
1016 4         18 return $name;
1017             }
1018              
1019              
1020              
1021             sub beautify {
1022 0     0 1 0 my $self = &self;
1023 0         0 my ( $method, @params ) = @_;
1024              
1025 0   0     0 $method = $self->can($method) || die "Cannot $method";
1026              
1027 0 0       0 if ( my $beauty = $self->get('beautify') ) {
1028 0         0 return $beauty->( $self->$method(@params) );
1029             } else {
1030 0         0 return $self->$method(@params);
1031             }
1032             }
1033              
1034             ### Private Methods & Settings ###
1035             ### Do not use these methods & settings outside of this package,
1036             ### they are subject to change or disappear at any time.
1037             sub class {
1038 11 100   11 0 9020 return scalar caller unless @_;
1039 10         36 my $caller = caller;
1040 10         13 my $class;
1041 10 50 33     55 if ( defined( $class = Scalar::Util::blessed( $_[0] ) )
      66        
1042             || ( !ref( $_[0] ) && length( $class = $_[0] ) ) )
1043             {
1044 10 100       47 if ( $class->isa($caller) ) {
1045 8         13 shift;
1046 8         22 return $class;
1047             }
1048             }
1049 2         6 return $caller;
1050             }
1051             sub self {
1052 7757     7757 0 11818 my $self = shift;
1053 7757 100       22436 return defined( Scalar::Util::blessed($self) ) ? $self : $self->new();
1054             }
1055 9551   100 9551   42595 sub _internal { return $_[0]->isa( scalar caller( 1 + ( $_[1] // 0 ) ) ) }
1056             sub _private {
1057 6558 100   6558   10484 Carp::croak('Illegal use of private method') unless $_[0]->_internal(1);
1058             }
1059             sub _settings() {
1060 6558     6558   11721 &_private;
1061 6557         11624 \state %SETTINGS;
1062             }
1063              
1064             sub _nameify {
1065 44 50   44   93 local $_ = shift if @_;
1066 44         165 s/::/_/g;
1067 44         294 return lc() . 'ify';
1068             }
1069             sub _find_handler {
1070 32     32   49 my $self = shift;
1071 32         46 my $class = shift;
1072              
1073 32         129 my $isa = mro::get_linear_isa($class);
1074 32         74 foreach my $c (@$isa) {
1075 44 50       88 next unless my $code = $self->can( _nameify($c) );
1076 0         0 return $code;
1077             }
1078 32         106 return;
1079             }
1080              
1081             sub _subst {
1082 531 50   531   1474 die "Cannot subst on an undefined value"
1083             unless defined $_[0];
1084 531         1806 goto &String::Tools::subst;
1085             }
1086              
1087             sub _get_delim {
1088 464     464   758 my $self = shift;
1089 464         670 my $open = shift;
1090              
1091 464         550 my $close;
1092 464 50       941 if ( 1 < length $open ) {
1093 0   0     0 my $qpairs = $self->get('qpairs') || [];
1094 0         0 my %qpairs = map { $_ => 1 } @$qpairs;
  0         0  
1095 0 0       0 if ( $qpairs{$open} ) {
1096 0         0 ( $open, $close ) = split //, $open, 2;
1097             } else {
1098 0         0 ( $open ) = split //, $open, 1
1099             }
1100             }
1101 464 50       903 $close = $open unless $close;
1102              
1103 464         1277 return $open, $close;
1104             }
1105              
1106             sub _to_encode {
1107 464     464   781 my $self = shift;
1108              
1109 464         801 my $encode = $self->get('encode');
1110              
1111             # Ignore the settings for byte, byte2, byte3, byte4, vwide, wide,
1112             # and utf
1113             my @encode
1114 464         1200 = grep { !(/\A(?:also|byte[234]?|v?wide|utf)\z/) } keys(%$encode);
  1665         5058  
1115              
1116 464   100     1745 my @ranges = ( $encode->{also} // () );
1117 464         1252 foreach my $element (@_) {
1118 1048 50       2201 if ( LooksLike::number($element) ) {
    50          
1119 0         0 push @encode, $element;
1120             } elsif ( length($element) == 1 ) {
1121             # An actual character, lets get the ordinal value and use that
1122 1048         19285 push @encode, ord($element);
1123             } else {
1124             # Something longer, it must be a range of chars,
1125             # like [:cntrl:], \x00-\x7f, or similar
1126 0         0 push @ranges, $element;
1127             }
1128             }
1129             @encode = map {
1130             # Encode characters in their \xXX or \x{XXXX} notation,
1131             # to get the literal values
1132 2079 100       6147 sprintf( $_ <= 255 ? '\\x%02x' : '\\x{%04x}', $_ )
1133             } sort {
1134 464         1682 $a <=> $b
  3677         5302  
1135             } @encode;
1136              
1137 464         1639 return join( '', @encode, @ranges );
1138             }
1139              
1140             sub _encode_ord2utf16 {
1141 6     6   7 my $self = shift;
1142 6         9 my $ord = shift;
1143              
1144 6         11 my $encode = $self->get('encode');
1145 6         14 my $format = $encode->{wide};
1146 6         9 my @wides = ();
1147 6 100 33     25 if (0) {
    50          
1148 0 50       0 } elsif ( 0x0000 <= $ord && $ord <= 0xffff ) {
1149 5 50 33     14 if ( 0xd800 <= $ord && $ord <= 0xdfff ) {
1150 0         0 die "Illegal character $ord";
1151             }
1152              
1153 5         11 @wides = ( $ord );
1154             } elsif ( 0x01_0000 <= $ord && $ord <= 0x10_ffff ) {
1155 1   33     4 $format = $encode->{vwide} || $format x 2;
1156              
1157 1         2 $ord -= 0x01_0000;
1158 1         3 my $ord2 = 0xdc00 + ( 0x3ff & $ord );
1159 1         2 $ord >>= 10;
1160 1         3 my $ord1 = 0xd800 + ( 0x3ff & $ord );
1161 1         3 @wides = ( $ord1, $ord2 );
1162             } else {
1163 0         0 die "Illegal character $ord";
1164             }
1165 6         32 return sprintf( $format, @wides );
1166             }
1167             sub _encode_ord2utf8 {
1168 6     6   11 my $self = shift;
1169 6         9 my $ord = shift;
1170              
1171 6         8 my @bytes = ();
1172 6         11 my $format = undef;
1173              
1174 6         9 my $encode = $self->get('encode');
1175 6 50 66     44 if (0) {
    100 66        
    100 33        
    50          
1176 0 50       0 } elsif ( 0x00 <= $ord && $ord <= 0x7f ) {
1177             # 1 byte represenstation
1178 0         0 $format = $encode->{byte};
1179 0         0 @bytes = ( $ord );
1180             } elsif ( 0x0080 <= $ord && $ord <= 0x07ff ) {
1181             # 2 byte represenstation
1182 4   33     22 $format = $encode->{byte2} || $format x 2;
1183              
1184 4         9 my $ord2 = 0x80 + ( 0x3f & $ord );
1185 4         6 $ord >>= 6;
1186 4         7 my $ord1 = 0xc0 + ( 0x1f & $ord );
1187 4         9 @bytes = ( $ord1, $ord2 );
1188             } elsif ( 0x0800 <= $ord && $ord <= 0xffff ) {
1189 1 50 33     5 if ( 0xd800 <= $ord && $ord <= 0xdfff ) {
1190 0         0 die "Illegal character $ord";
1191             }
1192              
1193             # 3 byte represenstation
1194 1   33     4 $format = $encode->{byte3} || $format x 3;
1195              
1196 1         4 my $ord3 = 0x80 + ( 0x3f & $ord );
1197 1         2 $ord >>= 6;
1198 1         2 my $ord2 = 0x80 + ( 0x3f & $ord );
1199 1         2 $ord >>= 6;
1200 1         2 my $ord1 = 0xe0 + ( 0x0f & $ord );
1201 1         2 @bytes = ( $ord1, $ord2, $ord3 );
1202             } elsif ( 0x01_0000 <= $ord && $ord <= 0x10_ffff ) {
1203             # 4 byte represenstation
1204 1   33     5 $format = $encode->{byte4} || $format x 4;
1205              
1206 1         3 my $ord4 = 0x80 + ( 0x3f & $ord );
1207 1         2 $ord >>= 6;
1208 1         3 my $ord3 = 0x80 + ( 0x3f & $ord );
1209 1         13 $ord >>= 6;
1210 1         4 my $ord2 = 0x80 + ( 0x3f & $ord );
1211 1         2 $ord >>= 6;
1212 1         2 my $ord1 = 0xf0 + ( 0x07 & $ord );
1213 1         3 @bytes = ( $ord1, $ord2, $ord3, $ord4 );
1214             } else {
1215 0         0 die "Illegal character $ord";
1216             }
1217 6         36 return sprintf( $format, @bytes );
1218             }
1219             sub _encode_char {
1220 295     295   398 my $self = shift;
1221 295         549 my $ord = ord shift;
1222              
1223 295         490 my $encode = $self->get('encode');
1224 295   100     847 my $utf = $encode->{utf} // 0;
1225 295 100       857 if ( defined $encode->{$ord} ) {
    100          
    100          
    100          
    100          
1226 153         591 return $encode->{$ord};
1227             } elsif ( $utf == 8 ) {
1228 6         15 return $self->_encode_ord2utf8( $ord );
1229             } elsif ( $utf == 16 ) {
1230 6         16 return $self->_encode_ord2utf16( $ord );
1231             } elsif ( $ord <= 255 ) {
1232 127         586 return sprintf $encode->{byte}, $ord;
1233             } elsif ( $ord <= 65_535 ) {
1234 2   33     7 my $encoding = $encode->{wide} // $encode->{byte};
1235 2         15 return sprintf $encoding, $ord;
1236             } else {
1237 1   33     6 my $encoding = $encode->{vwide} // $encode->{wide} // $encode->{byte};
      0        
1238 1         6 return sprintf $encoding, $ord;
1239             }
1240             }
1241              
1242             # Find a good character to use for delimiting q or qq.
1243             sub _find_q {
1244 1     1   2 my $self = shift;
1245 1 50       4 local $_ = shift if @_;
1246              
1247 1         2 my %counts;
1248 1         167 $counts{$_}++ foreach /([[:punct:]])/g;
1249             #$counts{$_}++ foreach grep /[[:punct:]]/, split //;
1250 1   50     15 my $qpairs = $self->get('qpairs') || [];
1251 1         5 foreach my $pair (@$qpairs) {
1252             $counts{$pair}
1253             = List::Util::sum 0,
1254             grep defined,
1255 4         10 map { $counts{$_} }
  8         23  
1256             split //, $pair;
1257             }
1258              
1259             return List::Util::reduce {
1260 23 100 50 23   58 ( ( $counts{$a} //= 0 ) <= ( $counts{$b} //= 0 ) ) ? $a : $b
      50        
1261 1         5 } @{ $self->get('qpairs') }, @{ $self->get('qquotes') };
  1         2  
  1         3  
1262             }
1263             sub _push_position {
1264 592     592   876 my $self = shift;
1265 592         857 my $position = shift;
1266 592   50     747 push @{ $self->{_position} //= [] }, $position;
  592         1615  
1267 592         1051 return $self;
1268             }
1269             sub _pop_position {
1270 590     590   809 my $self = shift;
1271 590         705 return pop @{ $self->{_position} };
  590         1685  
1272             }
1273             sub _cache_position {
1274 142     142   235 my $self = shift;
1275              
1276 142   33     266 my $nest = $self->get('nested') // $self->get('dereference');
1277             my $pos = List::Util::reduce(
1278 0     0   0 sub { _subst( $nest, referent => $a, place => $b ) },
1279 142   100     553 @{ $self->{_position} //= [] }
  142         719  
1280             );
1281              
1282 142         767 my $var = $self->get('name');
1283 142 50       431 my $sigil = length $var ? substr $var, 0, 1 : '';
1284 142 50 33     611 if ( $sigil eq '@' || $sigil eq '%' ) {
    100          
1285 0 0       0 if ($pos) {
1286 0         0 $var = sprintf '$%s%s', substr($var, 1), $pos;
1287             } else {
1288 0         0 $var = _subst( $self->get('reference'), $var );
1289             }
1290             } elsif ($pos) {
1291 32   33     80 $var = _subst(
1292             $self->get('dereference') // $self->get('nested'),
1293             referent => $var,
1294             place => $pos
1295             );
1296             }
1297 142         3700 return $var;
1298             }
1299              
1300             __PACKAGE__->set(
1301             # _caching options
1302             _cache_hit => 0,
1303             );
1304             sub _cache_add {
1305 507     507   792 my $self = shift;
1306 507         663 my $ref = shift;
1307 507         758 my $value = shift;
1308              
1309 507 100       1443 return $self unless my $refaddr = Scalar::Util::refaddr $ref;
1310 110   50     283 my $_cache = $self->{_cache} //= {};
1311 110   50     264 my $entry = $_cache->{$refaddr} //= [ $self->_cache_position ];
1312 110 50       239 push @$entry, $value if @$entry == $self->get('_cache_hit');
1313              
1314 110         203 return $self;
1315             }
1316             sub _cache_get {
1317 548     548   818 my $self = shift;
1318 548         815 my $item = shift;
1319              
1320 548 100       1975 return unless my $refaddr = Scalar::Util::refaddr $item;
1321              
1322 148   100     503 my $_cache = $self->{_cache} //= {};
1323 148 100       355 if ( my $entry = $_cache->{$refaddr} ) {
1324 6         13 my $repr = $self->get('_cache_hit');
1325 6   33     38 return $entry->[$repr]
1326             // Carp::croak 'Recursive structures not allowed at ',
1327             $self->_cache_position;
1328             } else {
1329             # Pre-populate the cache, so that we can check for loops
1330 142         276 $_cache->{$refaddr} = [ $self->_cache_position ];
1331 142         530 return;
1332             }
1333             }
1334             sub _cache_reset {
1335 41     41   66 my $self = shift;
1336 41   100     58 %{ $self->{_cache} //= {} } = ();
  41         174  
1337 41         71 return $self;
1338             }
1339              
1340             1;
1341              
1342             __END__