File Coverage

blib/lib/Datify.pm
Criterion Covered Total %
statement 456 552 82.6
branch 225 376 59.8
condition 82 178 46.0
subroutine 69 76 90.7
pod 33 36 91.6
total 865 1218 71.0


line stmt bran cond sub pod time code
1 5     5   253625 use v5.14;
  5         20  
2 5     5   28 use warnings;
  5         8  
  5         210  
3              
4             package Datify v0.20.052;
5             # ABSTRACT: Simple stringification of data.
6              
7              
8 5     5   32 use mro (); #qw( get_linear_isa );
  5         18  
  5         79  
9 5     5   24 use overload (); #qw( Method Overloaded );
  5         9  
  5         83  
10              
11 5     5   23 use Carp (); #qw( carp croak );
  5         9  
  5         101  
12 5     5   25 use List::Util (); #qw( reduce sum );
  5         9  
  5         79  
13 5     5   2535 use LooksLike (); #qw( numeric );
  5         18198  
  5         163  
14 5     5   38 use Scalar::Util (); #qw( blessed looks_like_number refaddr reftype );
  5         11  
  5         124  
15 5     5   2577 use String::Tools v0.18.277 (); #qw( stitch stringify subst );
  5         7756  
  5         149  
16 5     5   2380 use Sub::Util 1.40 (); #qw( subname );
  5         1573  
  5         3723  
17              
18              
19             ### Constructor ###
20              
21              
22             sub new {
23 2817   50 2817 1 15380 my $class = shift || __PACKAGE__;
24              
25 2817         3805 my %self = ();
26 2817 50       5541 if ( defined( my $blessed = Scalar::Util::blessed($class) ) ) {
27 0         0 %self = %$class; # shallow copy
28 0         0 $class = $blessed;
29             }
30 2817 100       7094 return @_ ? bless( \%self, $class )->set(@_) : bless( \%self, $class );
31             }
32              
33              
34              
35             ### Accessor ###
36              
37              
38              
39              
40             sub exists {
41 7281     7281 1 9082 my $self = shift;
42 7281 50       12338 return unless my $count = scalar(@_);
43              
44 7281         11015 my $SETTINGS = $self->_settings;
45 7281 100       16576 if ( Scalar::Util::blessed($self) ) {
46             return $count == 1
47             ? exists $self->{ $_[0] } && $self
48             || exists $SETTINGS->{ $_[0] } && $SETTINGS
49             : map {
50 7237 50 100     36197 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     516 : map { exists $SETTINGS->{ $_ } && $SETTINGS } @_;
  0 50       0  
57             }
58             }
59              
60              
61              
62             sub _get_setting {
63 7243     7243   13628 my $setting = $_[0]->exists( local $_ = $_[1] );
64 7243 100       23070 return $setting ? $setting->{$_} : do {
65 689 50       1283 Carp::carp( 'Unknown key ', $_ )
66             unless $_[0]->_internal(1);
67             undef
68 689         1694 };
69             }
70             sub get {
71 6375     6375 1 13984 my $self = shift;
72 6375         8672 my $count = scalar(@_);
73              
74 6375 100       16368 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 6368 100       15089 : map { $self->_get_setting($_) } @_;
  1316 50       2172  
79             } else {
80             return
81 1         2 $count == 0 ? %{ $self->_settings }
82             : $count == 1 ? $self->_get_setting(shift)
83 7 50       26 : map { $self->_get_setting($_) } @_;
  0 100       0  
84             }
85             }
86              
87              
88             ### Setter ###
89              
90              
91             sub set {
92 1238     1238 1 10012 my $self = shift;
93 1238 50       2390 return $self unless @_;
94 1238         3006 my %set = @_;
95              
96 1238         1703 my $return;
97             my $class;
98 1238 100       3339 if ( defined( $class = Scalar::Util::blessed($self) ) ) {
99             # Make a shallow copy
100 1111         4539 $self = bless { %$self }, $class;
101 1111         1936 $return = 0;
102             } else {
103 127         179 $class = $self;
104 127         217 $self = $class->_settings;
105 127         168 $return = 1;
106             }
107              
108 1238 100       2803 delete $self->{keyword_set} if ( $set{keywords} );
109 1238         2108 delete $self->{"_tr$_"} for grep { exists $set{"quote$_"} } ( 1, 2, 3 );
  3714         8146  
110              
111 1238         2535 my $internal = $class->_internal;
112 1238         3946 while ( my ( $k, $v ) = each %set ) {
113 1435 100 100     2971 Carp::carp( 'Unknown key ', $k )
114             unless $internal || $class->exists($k);
115 1435 100 100     4396 study($v) if defined($v) && !ref($v);
116 1435         4572 $self->{$k} = $v;
117             }
118              
119 1238         3240 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   44 no strict 'refs';
  5         9  
  5         6506  
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 17561 my $self = &self;
172 41         80 my ($sigil, $name);
173 41 50 33     173 if ( defined $_[0] && !ref $_[0] ) {
174 41         770 ( $sigil, $name )
175             = $_[0] =~ /^($sigils)?((?:$package\::)?$varname|$package\::)$/;
176 41 50       148 shift if length $name;
177             }
178 41 50       108 my $value = 1 == @_ ? shift : \@_;
179              
180 41 50       76 if ( length $name ) {
181 41 50       118 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       104 Carp::croak "Missing name" unless ( length $name );
193              
194 41 100       84 unless ($sigil) {
195 22         47 my $ref = ref $value;
196 22 50       76 $sigil
    50          
197             = $ref eq 'ARRAY' ? '@'
198             : $ref eq 'HASH' ? '%'
199             : '$';
200             }
201 41         79 $name = $sigil . $name;
202 41         141 $self = $self->set( name => $name );
203              
204 41 0       151 $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         88 $value = _subst( $self->get('assign'), var => $name, value => $value );
212 41 50       3826 if ( my $beautify = $self->get('beautify') ) {
213 0         0 return $beautify->($value);
214             } else {
215 41         230 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 41 my $self = &self;
232 17 50 33     66 return $self->scalarify(shift) if @_ and defined($_[0]);
233 17         41 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 4549 my $self = &self;
262 400 50       1078 local $_ = shift if @_;
263 400 50       771 return $self->undefify unless defined;
264 400 50       681 $_ = String::Tools::stringify($_) if ref;
265 400         824 my $quote1 = $self->get('quote1');
266 400   66     1357 my ( $open, $close ) = $self->_get_delim( shift // $quote1 );
267              
268 400         774 $self = $self->set( encode => $self->get('encode1') );
269 400         826 my $to_encode = $self->_to_encode( $open, $close );
270 400         1788 s/([$to_encode])/$self->_encode_char($1)/eg;
  116         296  
271              
272 400 100       1035 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         3214 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 4298 my $self = &self;
290 60 50       168 local $_ = shift if @_;
291 60 50       125 return $self->undefify unless defined;
292 60 50       127 $_ = String::Tools::stringify($_) if ref;
293 60         131 my $quote2 = $self->get('quote2');
294 60   33     238 my ( $open, $close ) = $self->_get_delim( shift // $quote2 );
295              
296 60         100 my @sigils;
297 60 50       122 if ( my $sigils = $self->get('sigils') ) {
298 60         247 push @sigils, split //, $sigils;
299             }
300              
301             # quote char(s), sigils, and backslash.
302 60         170 $self = $self->set( encode => $self->get('encode2') );
303 60         194 my $to_encode = $self->_to_encode( $open, $close, @sigils );
304 60         609 s/([$to_encode])/$self->_encode_char($1)/eg;
  179         403  
305              
306 60 50       184 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         464 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   42 no warnings 'qw';
  5         10  
  5         11028  
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 4786 my $self = &self;
374 422 50       922 local $_ = shift if @_;
375 422 50       744 return $self->undefify unless defined;
376 422 50       716 $_ = String::Tools::stringify($_) if ref;
377 422         641 local $@ = undef;
378              
379 422         805 my ( $quote, $quote1, $quote2 ) = $self->get(qw( quote quote1 quote2 ));
380 422 50       960 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         870 my $encode2 = $self->get('encode2');
389 422   33     1246 my $also = $encode2 && $encode2->{also};
390 422 100 33     3288 return $self->stringify2($_)
      66        
      66        
391             if ( ( $longstr && $longstr < length() ) || ( $also && /[$also]/ ) );
392              
393 410         933 my $tr1 = $self->get('_tr1');
394 410 50       1443 $self = $self->set( _tr1 => $tr1 = "tr\\$quote1\\$quote1\\" )
395             if ( not $tr1 );
396 410   50     24293 my $single_quotes = eval $tr1 // die $@;
397 410 100       1854 return $self->stringify1($_) unless $single_quotes;
398              
399 21         143 my ( $sigils, $tr2 ) = $self->get(qw( sigils _tr2 ));
400 21 50       130 $self = $self->set( _tr2 => $tr2 = "tr\\$quote2$sigils\\$quote2$sigils\\" )
401             if ( not $tr2 );
402 21   50     1116 my $double_quotes = eval $tr2 // die $@;
403 21 100       115 return $self->stringify2($_) unless $double_quotes;
404              
405 1         4 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 3322     3322 1 4909 my $self = &self;
425 3322 50       6571 local $_ = shift if @_;
426              
427 3322 50       5362 return undef unless defined;
428              
429 3322 50       5083 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 3322         5865 return LooksLike::numeric($_);
439             }
440              
441              
442             sub numify {
443 384     384 1 34422 my $self = &self;
444 384 50       882 local $_ = shift if @_;
445              
446 384 100       716 return $self->undefify unless defined;
447              
448 383 100       686 if ( $self->is_numeric($_) ) {
    100          
449 369 50       5060 return $_ unless my $sep = $self->get('num_sep');
450              
451             # Fractional portion
452 369         1129 s{^(\s*[-+]?\d*\.\d\d)(\d+)} [${1}$sep${2}];
453 369         1715 1 while s{^(\s*[-+]?\d*\.(?:\d+$sep)+\d\d\d)(\d+)}[${1}$sep${2}];
454              
455             # Whole portion
456 369         1426 1 while s{^(\s*[-+]?\d+)(\d{3})} [${1}$sep${2}];
457              
458 369         1332 return $_;
459             }
460             elsif ( Scalar::Util::looks_like_number($_) ) {
461             return
462 12 50       204 $_ == 'inf' ? $self->get('infinite')
    100          
    100          
463             : $_ == '-inf' ? $self->get('-infinite')
464             : defined( $_ <=> 0 ) ? $_
465             : $self->get('nonnumber');
466             }
467              
468 2         106 return $self->get('nonnumber');
469             }
470              
471              
472              
473             ### Scalar ###
474              
475              
476             __PACKAGE__->set(
477             # Scalar options
478             scalar_ref => '\do{1;$_}',
479             );
480              
481              
482             sub scalarify {
483 548     548 1 18690 my $self = &self;
484 548 50       1403 local $_ = shift if @_;
485              
486 548   100     1004 my $value = $self->_cache_get($_) // $self->_scalarify($_);
487 548 100       12839 $self->isa( scalar caller )
488             ? $self->_cache_add( $_ => $value )
489             : $self->_cache_reset($_);
490 548         1321 return $value;
491             }
492              
493             sub _scalarify {
494 542     542   867 my $self = &self;
495 542 50       1241 local $_ = shift if @_;
496              
497 542 100       1043 return $self->undefify unless defined $_;
498              
499 526 100       1284 if ( defined( my $blessed = Scalar::Util::blessed($_) ) ) {
500             return
501 36 100       101 $blessed eq 'Regexp' ? $self->regexpify($_)
502             : $self->objectify($_);
503             }
504              
505 490         870 my $ref = Scalar::Util::reftype $_;
506 490 100       885 if ( not $ref ) {
507             # Handle GLOB, LVALUE, and VSTRING
508 384         730 my $ref2 = ref \$_;
509             return
510 384 100       1961 $ref2 eq 'GLOB' ? $self->globify($_)
    50          
    100          
    50          
    100          
511             : $ref2 eq 'LVALUE' ? $self->lvalueify($_)
512             : $ref2 eq 'VSTRING' ? $self->vstringify($_)
513             : $ref2 eq 'SCALAR' ? (
514             Scalar::Util::looks_like_number($_)
515             ? $self->numify($_)
516             : $self->stringify($_)
517             )
518             : $self->stringify($_);
519             }
520              
521             return
522             $ref eq 'ARRAY' ? $self->arrayify(@$_)
523             : $ref eq 'CODE' ? $self->codeify($_)
524             : $ref eq 'FORMAT' ? $self->formatify($_)
525             : $ref eq 'HASH' ? $self->hashify($_)
526             : $ref eq 'IO' ? $self->ioify($_)
527             : $ref eq 'REF' ? $self->refify($$_)
528             : $ref eq 'REGEXP' ? $self->regexpify($_) # ???
529 106 50       527 : do {
    100          
    50          
    100          
    100          
    100          
    100          
530 18   33     59 my $reference = $self->get( lc($ref) . '_reference' )
531             || $self->get('reference');
532              
533 18 50       79 $ref eq 'GLOB' ? _subst( $reference, $self->globify($$_) )
    100          
    100          
    100          
534             : $ref eq 'LVALUE' ? _subst( $reference, $self->lvalueify($$_) )
535             : $ref eq 'SCALAR' ? _subst( $reference, $self->scalarify($$_) )
536             : $ref eq 'VSTRING' ? _subst( $reference, $self->vstringify($$_) )
537             : $self->objectify($_)
538             ;
539             };
540             }
541              
542              
543              
544             ### Scalar: LValue ###
545              
546              
547             __PACKAGE__->set(
548             # LValue options
549             lvalue => 'substr($lvalue, 0)',
550             );
551              
552              
553             sub lvalueify {
554 4     4 1 10 my $self = &self;
555 4         10 return _subst( $self->get('lvalue'), lvalue => $self->stringify(shift) );
556             }
557              
558              
559              
560             ### Scalar: VString ###
561              
562              
563             __PACKAGE__->set(
564             # VString options
565             vformat => 'v%vd',
566             #vformat => 'v%*vd',
567             #vsep => '.',
568             );
569              
570              
571             sub vstringify {
572 4     4 1 10 my $self = &self;
573 4 50       10 if ( defined( my $vsep = $self->get('vsep') ) ) {
574 0         0 return sprintf $self->get('vformat'), $vsep, shift;
575             } else {
576 4         9 return sprintf $self->get('vformat'), shift;
577             }
578             }
579              
580              
581              
582             ### Regexp ###
583              
584              
585             __PACKAGE__->set(
586             # Regexp options
587             quote3 => '/',
588             #_tr3 => q!tr\\/\\/\\!,
589             q3 => 'qr',
590              
591             encode3 => {
592             map( { ord( eval qq!"$_"! ) => $_ } qw( \0 \a \t \n \f \r \e ) ),
593             #0x00 => '\\0',
594             #0x07 => '\\a',
595             #0x09 => '\\t',
596             #0x0a => '\\n',
597             #0x0c => '\\f',
598             #0x0d => '\\r',
599             #0x1b => '\\e',
600              
601             also => '[:cntrl:]',
602             byte => '\\x%02x',
603             wide => '\\x{%04x}',
604             #vwide => '\\x{%06x}',
605             },
606             );
607              
608              
609             sub regexpify {
610 4     4 1 7 my $self = &self;
611 4 50       9 local $_ = shift if @_;
612 4         7 local $@ = undef;
613              
614 4         9 my ( $quote3, $tr3 ) = $self->get(qw( quote3 _tr3 ));
615 4 50       19 $self = $self->set( _tr3 => $tr3 = "tr\\$quote3\\$quote3\\" )
616             if ( not $tr3 );
617 4   50     325 my $quoter = eval $tr3 // die $@;
618 4 50 33     33 my ( $open, $close )
619             = $self->_get_delim(
620             shift // $quoter ? $self->_find_q($_) : $self->get('quote3') );
621              
622             # Everything but the quotes should be escaped already.
623 4         9 $self = $self->set( encode => $self->get('encode3') );
624 4         14 my $to_encode = $self->_to_encode( $open, $close );
625 4         42 s/([$to_encode])/$self->_encode_char($1)/eg;
  0         0  
626              
627 4 50       13 if ( $open =~ /\w/ ) {
628 0         0 $open = ' ' . $open;
629 0         0 $close = ' ' . $close;
630             }
631              
632 4         10 $open = $self->get('q3') . $open;
633              
634 4         30 return sprintf '%s%s%s', $open, $_, $close;
635             }
636              
637              
638              
639             ### List/Array ###
640              
641              
642             sub listify {
643 24     24 1 49 my $self = &self;
644 24         44 my @values;
645 24         88 for ( my $i = 0; $i < @_; $i++ ) {
646 116         204 my $value = $_[$i];
647 116         307 $self = $self->_push_position("[$i]");
648 116         236 push @values, $self->scalarify($value);
649 116         249 $self->_pop_position;
650             }
651 24         60 return join( $self->get('list_sep'), @values );
652             }
653              
654              
655              
656              
657             __PACKAGE__->set(
658             # Array options
659             array_ref => '[$_]',
660             );
661              
662              
663             sub arrayify {
664 24     24 1 74 my $self = &self;
665 24         62 return _subst( $self->get('array_ref'), $self->listify(@_) );
666             }
667              
668              
669              
670             ### Hash ###
671              
672              
673             sub is_keyword {
674 322     322 1 548 my $self = &self;
675              
676 322         595 my $keyword_set = $self->get('keyword_set');
677 322 100       675 if ( not $keyword_set ) {
678 63   50     116 my $keywords = $self->get('keywords') // [];
679 63 50       141 return unless @$keywords;
680 63         117 $keyword_set = { map { $_ => 1 } @$keywords };
  63         236  
681 63         148 $self->{keyword_set} = $keyword_set;
682             }
683 322         2422 return exists $keyword_set->{ +shift };
684             }
685              
686              
687             sub keyify {
688 489     489 1 811 my $self = &self;
689 489 50       1115 local $_ = shift if @_;
690              
691 489 50       927 return $self->undefify unless defined;
692 489 50       817 return $_ if ref;
693              
694 489 100 33     887 if ( $self->is_numeric($_) ) {
    100 66        
695 167         2642 return $self->numify($_);
696             } elsif ( length() < $self->get('longstr')
697             && !$self->is_keyword($_)
698             && /\A-?[[:alpha:]_]\w*\z/ )
699             {
700             # If the key would be autoquoted by the fat-comma (=>),
701             # then there is no need to quote it.
702              
703 290         950 return "$_"; # Make sure it's stringified.
704             }
705 32         105 return $self->stringify($_);
706             }
707              
708              
709              
710              
711             sub keysort($$);
712             BEGIN {
713 5     5   43 no warnings 'qw';
  5         36  
  5         590  
714 5     5   48 my $keysort = String::Tools::stitch(qw(
715             sub keysort($$) {
716             my ( $a, $b ) = @_;
717             my $numa = Datify->is_numeric($a);
718             my $numb = Datify->is_numeric($b);
719             return(
720             ( $numa && $numb ? $a <=> $b
721             : $numa ? -1
722             : $numb ? +1
723             : $a_cmp__b )
724             || $a cmp $b
725             );
726             }
727             ));
728 5 50       4161 my $a_cmp__b
729             = $^V >= v5.16.0
730             ? 'CORE::fc($a) cmp CORE::fc($b)'
731             : 'lc($a) cmp lc($b)';
732 5         43 $keysort = String::Tools::subst( $keysort, a_cmp__b => $a_cmp__b );
733 5 50 50 1225 1 7076 eval($keysort) or $@ and die $@;
  1225   100     3322  
  1225         2577  
  1225         35153  
  1225         37219  
734             }
735              
736              
737              
738             sub hashkeys {
739 58     58 1 96 my $self = shift;
740 58         82 my $hash = shift;
741              
742 58         212 my @keys = keys %$hash;
743 58 50       122 if ( my $ref = ref( my $keyfilter = $self->get('keyfilter') ) ) {
744 0         0 my $keyfilternot = !$self->get('keyfilterdefault');
745 0         0 my $keyfilterdefault = !$keyfilternot;
746 0 0 0     0 if ( $ref eq 'ARRAY' || $ref eq 'HASH' ) {
    0          
    0          
    0          
747             my %keyfilterhash
748             = $ref eq 'ARRAY'
749 0 0       0 ? ( map { $_ => $keyfilternot } @$keyfilter )
  0         0  
750             : %$keyfilter;
751             $self->{keyfilter} = $keyfilter = sub {
752             exists $keyfilterhash{$_}
753 0 0   0   0 ? $keyfilterhash{$_}
754             : $keyfilterdefault;
755 0         0 };
756             } elsif ( $ref eq 'CODE' ) {
757             # No-op, just use the code provided
758             } elsif ( $ref eq 'Regexp' ) {
759 0         0 my $keyfilterregexp = $keyfilter;
760             $self->{keyfilter} = $keyfilter = sub {
761 0 0   0   0 m/$keyfilterregexp/ ? $keyfilternot : $keyfilterdefault;
762 0         0 };
763             } elsif ( $ref eq 'SCALAR' ) {
764 0         0 my $keyfiltervalue = $$keyfilter;
765 0     0   0 $self->{keyfilter} = $keyfilter = sub {$keyfiltervalue};
  0         0  
766             }
767 0         0 @keys = grep { $keyfilter->() } @keys;
  0         0  
768             }
769 58 50       120 if ( my $keysort = $self->get('keysort') ) {
770 58         934 @keys = sort $keysort @keys;
771             }
772 58         247 return @keys;
773             }
774              
775             sub hashkeyvals {
776 28     28 0 42 my $self = shift;
777 28         43 my $hash = shift;
778              
779 28         75 return map { $_ => $hash->{$_} } $self->hashkeys($hash);
  288         608  
780             }
781              
782              
783             sub pairify {
784 28     28 1 71 my $self = &self;
785 28 50       81 if (1 == @_) {
786 28         73 my $ref = Scalar::Util::reftype $_[0];
787 28 50       96 if ( $ref eq 'ARRAY' ) { @_ = @{ +shift } }
  0 50       0  
  0         0  
788 28         78 elsif ( $ref eq 'HASH' ) { @_ = $self->hashkeyvals(shift) }
789             }
790             # Use for loop in order to preserve the order of @_,
791             # rather than each %{ { @_ } }, which would mix-up the order.
792 28         63 my @list;
793 28         73 my $pair = $self->get('pair');
794 28         96 for ( my $i = 0; $i < @_ - 1; $i += 2 ) {
795 288         19387 my ( $k, $v ) = @_[ $i, $i + 1 ];
796 288         668 my $key = $self->keyify($k);
797 288         761 $self = $self->_push_position("{$key}");
798 288         595 my $val = $self->scalarify($v);
799 288         694 $self->_pop_position;
800 288         651 push @list, _subst( $pair, key => $key, value => $val );
801             }
802 28         1509 return join( $self->get('list_sep'), @list );
803             }
804              
805              
806              
807              
808             __PACKAGE__->set(
809             # Hash options
810             hash_ref => '{$_}',
811             pair => '$key => $value',
812             keysort => \&Datify::keysort,
813             keyfilter => undef,
814             keyfilterdefault => 1,
815             keywords => [qw(undef)],
816             #keyword_set => { 'undef' => 1 },
817             );
818              
819              
820             sub hashify {
821 28     28 1 64 my $self = &self;
822 28         79 return _subst( $self->get('hash_ref'), $self->pairify(@_) );
823             }
824              
825              
826              
827             ### Objects ###
828              
829              
830             sub overloaded {
831 32     32 1 60 my $self = &self;
832 32 50       68 my $object = @_ ? shift : $_;
833              
834 32 50 33     173 return unless defined( Scalar::Util::blessed($object) )
835             && overload::Overloaded($object);
836              
837 0   0     0 my $overloads = $self->get('overloads') || [];
838 0         0 foreach my $overload (@$overloads) {
839 0 0       0 if ( my $method = overload::Method( $object => $overload ) ) {
840 0         0 return $method;
841             }
842             }
843 0         0 return;
844             }
845              
846              
847              
848              
849             __PACKAGE__->set(
850             # Object options
851             overloads => [ '""', '0+' ],
852             object => 'bless($data, $class_str)',
853             #object => '$class->new($data)',
854             #object => '$class=$data',
855             );
856              
857              
858             sub objectify {
859 32     32 1 59 my $self = &self;
860 32 50       71 my $object = @_ ? shift : $_;
861              
862 32 50       91 return $self->scalarify($object)
863             unless defined( my $class = Scalar::Util::blessed($object) );
864              
865 32         58 my $data;
866 32 50       59 if (0) {
    50          
    50          
867 0         0 } elsif ( my $code = $self->_find_handler($class) ) {
868 0         0 return $self->$code($object);
869             } elsif ( my $method = $self->overloaded($object) ) {
870 0         0 $data = $self->scalarify( $object->$method() );
871             } elsif ( my $attrkeyvals = $object->can('_attrkeyvals') ) {
872             # TODO: Look this up via meta-objects
873 0         0 $data = $self->hashify( $object->$attrkeyvals() );
874             } else {
875 32         1837 $data = Scalar::Util::reftype $object;
876              
877 32 50       179 $data
    50          
    50          
    100          
    100          
    50          
    50          
    50          
    100          
878             = $data eq 'ARRAY' ? $self->arrayify( @$object )
879             : $data eq 'CODE' ? $self->codeify( $object )
880             : $data eq 'FORMAT' ? $self->formatify( $object )
881             : $data eq 'GLOB' ? $self->globify( $object )
882             : $data eq 'HASH' ? $self->hashify( $object )
883             : $data eq 'IO' ? $self->ioify( $object )
884             : $data eq 'REF' ? $self->refify( $$object )
885             : $data eq 'REGEXP' ? $self->regexpify( $object )
886             : $data eq 'SCALAR' ? $self->refify( $$object )
887             : "*UNKNOWN{$data}";
888             }
889              
890 32         2209 return _subst(
891             $self->get('object'),
892             class_str => $self->stringify($class),
893             class => $class,
894             data => $data
895             );
896             }
897              
898              
899              
900             ### Objects: IO ###
901              
902              
903             __PACKAGE__->set(
904             # IO options
905             io => '*$name{IO}',
906             );
907              
908              
909              
910             sub ioify {
911 4     4 1 6 my $self = &self;
912 4 50       10 my $io = @_ ? shift : $_;
913              
914 4         7 my $ioname = 'UNKNOWN';
915 4         8 foreach my $ioe (qw( IN OUT ERR )) {
916 5     5   42 no strict 'refs';
  5         11  
  5         14957  
917 8 100       11 if ( *{"main::STD$ioe"}{IO} == $io ) {
  8         31  
918 4         7 $ioname = "STD$ioe";
919 4         7 last;
920             }
921             }
922             # TODO
923             #while ( my ( $name, $glob ) = each %main:: ) {
924             # no strict 'refs';
925             # if ( defined( *{$glob}{IO} ) && *{$glob}{IO} == $io ) {
926             # keys %main::; # We're done, so reset each()
927             # $ioname = $name;
928             # last;
929             # }
930             #}
931 4         11 return _subst( $self->get('io'), name => $ioname );
932             }
933              
934              
935              
936             ### Other ###
937              
938              
939             __PACKAGE__->set(
940             # Code options
941             code => 'sub {$body}',
942             codename => '\&$codename',
943             body => '...',
944             );
945              
946              
947             sub codeify {
948 8     8 1 23 my $self = &self;
949              
950 8         22 my $template = $self->get('code');
951 8         25 my %data = ( body => $self->get('body') );
952 8 50 33     39 if ( @_ && defined( $_[0] ) ) {
953 8         14 local $_ = shift;
954 8 50       29 if ( my $ref = Scalar::Util::reftype($_) ) {
955 8 50       18 if ( $ref eq 'CODE' ) {
956 8 100       78 if ( ( my $subname = Sub::Util::subname($_) )
957             !~ /\A(?:\w+\::)*__ANON__\z/ )
958             {
959 4   33     14 $template = $self->get('codename') // $template;
960 4         19 %data = ( codename => $subname );
961             }
962             } else {
963 0         0 %data = ( body => $self->scalarify($_) );
964             }
965             } else {
966 0         0 %data = ( body => $_ );
967             }
968             }
969 8         29 return _subst( $template, %data );
970             }
971              
972              
973              
974              
975             __PACKAGE__->set(
976             # Reference options
977             reference => '\\$_',
978             dereference => '$referent->$place',
979             nested => '$referent$place',
980             );
981              
982              
983             sub refify {
984 52     52 1 96 my $self = &self;
985 52 50       122 local $_ = shift if @_;
986 52         107 return _subst( $self->get('reference'), $self->scalarify($_) );
987             }
988              
989              
990              
991              
992             __PACKAGE__->set(
993             # Format options
994             format => "format UNKNOWN =\n.\n",
995             );
996              
997              
998             sub formatify {
999 4     4 1 10 my $self = &self;
1000             #Carp::croak "Unhandled type: ", ref shift;
1001 4         8 return $self->get('format');
1002             }
1003              
1004              
1005              
1006              
1007             sub globify {
1008 4     4 1 8 my $self = &self;
1009 4         15 my $name = '' . shift;
1010 4 50       147 if ( $name =~ /^\*$package\::(?:$word|$digits)?$/ ) {
1011 4         19 $name =~ s/^\*main::/*::/;
1012             } else {
1013 0         0 $name =~ s/^\*($package\::.+)/'*{' . $self->stringify($1) . '}'/e;
  0         0  
1014             }
1015 4         16 return $name;
1016             }
1017              
1018              
1019              
1020             sub beautify {
1021 0     0 1 0 my $self = &self;
1022 0         0 my ( $method, @params ) = @_;
1023              
1024 0   0     0 $method = $self->can($method) || die "Cannot $method";
1025              
1026 0 0       0 if ( my $beauty = $self->get('beautify') ) {
1027 0         0 return $beauty->( $self->$method(@params) );
1028             } else {
1029 0         0 return $self->$method(@params);
1030             }
1031             }
1032              
1033             ### Private Methods & Settings ###
1034             ### Do not use these methods & settings outside of this package,
1035             ### they are subject to change or disappear at any time.
1036             sub class {
1037 11 100   11 0 8470 return scalar caller unless @_;
1038 10         18 my $caller = caller;
1039 10         15 my $class;
1040 10 50 33     52 if ( defined( $class = Scalar::Util::blessed( $_[0] ) )
      66        
1041             || ( !ref( $_[0] ) && length( $class = $_[0] ) ) )
1042             {
1043 10 100       44 if ( $class->isa($caller) ) {
1044 8         12 shift;
1045 8         20 return $class;
1046             }
1047             }
1048 2         6 return $caller;
1049             }
1050             sub self {
1051 7739     7739 0 10878 my $self = shift;
1052 7739 100       21219 return defined( Scalar::Util::blessed($self) ) ? $self : $self->new();
1053             }
1054 9527   100 9527   41261 sub _internal { return $_[0]->isa( scalar caller( 1 + ( $_[1] // 0 ) ) ) }
1055             sub _private {
1056 6534 100   6534   10549 Carp::croak('Illegal use of private method') unless $_[0]->_internal(1);
1057             }
1058             sub _settings() {
1059 6534     6534   11766 &_private;
1060 6533         11615 \state %SETTINGS;
1061             }
1062              
1063             sub _nameify {
1064 44 50   44   100 local $_ = shift if @_;
1065 44         158 s/::/_/g;
1066 44         298 return lc() . 'ify';
1067             }
1068             sub _find_handler {
1069 32     32   50 my $self = shift;
1070 32         43 my $class = shift;
1071              
1072 32         141 my $isa = mro::get_linear_isa($class);
1073 32         71 foreach my $c (@$isa) {
1074 44 50       84 next unless my $code = $self->can( _nameify($c) );
1075 0         0 return $code;
1076             }
1077 32         107 return;
1078             }
1079              
1080             sub _subst {
1081 531 50   531   1287 die "Cannot subst on an undefined value"
1082             unless defined $_[0];
1083 531         1834 goto &String::Tools::subst;
1084             }
1085              
1086             sub _get_delim {
1087 464     464   654 my $self = shift;
1088 464         640 my $open = shift;
1089              
1090 464         579 my $close;
1091 464 50       961 if ( 1 < length $open ) {
1092 0   0     0 my $qpairs = $self->get('qpairs') || [];
1093 0         0 my %qpairs = map { $_ => 1 } @$qpairs;
  0         0  
1094 0 0       0 if ( $qpairs{$open} ) {
1095 0         0 ( $open, $close ) = split //, $open, 2;
1096             } else {
1097 0         0 ( $open ) = split //, $open, 1
1098             }
1099             }
1100 464 50       844 $close = $open unless $close;
1101              
1102 464         1035 return $open, $close;
1103             }
1104              
1105             sub _to_encode {
1106 464     464   662 my $self = shift;
1107              
1108 464         811 my $encode = $self->get('encode');
1109              
1110             # Ignore the settings for byte, byte2, byte3, byte4, vwide, wide,
1111             # and utf
1112             my @encode
1113 464         1220 = grep { !(/\A(?:also|byte[234]?|v?wide|utf)\z/) } keys(%$encode);
  1665         4822  
1114              
1115 464   100     1602 my @ranges = ( $encode->{also} // () );
1116 464         915 foreach my $element (@_) {
1117 1048 50       2680 if ( Scalar::Util::looks_like_number($element) ) {
    50          
1118 0         0 push @encode, $element;
1119             } elsif ( length($element) == 1 ) {
1120             # An actual character, lets get the ordinal value and use that
1121 1048         1842 push @encode, ord($element);
1122             } else {
1123             # Something longer, it must be a range of chars,
1124             # like [:cntrl:], \x00-\x7f, or similar
1125 0         0 push @ranges, $element;
1126             }
1127             }
1128             @encode = map {
1129             # Encode characters in their \xXX or \x{XXXX} notation,
1130             # to get the literal values
1131 2079 100       6001 sprintf( $_ <= 255 ? '\\x%02x' : '\\x{%04x}', $_ )
1132             } sort {
1133 464         1454 $a <=> $b
  3573         5151  
1134             } @encode;
1135              
1136 464         1666 return join( '', @encode, @ranges );
1137             }
1138              
1139             sub _encode_ord2utf16 {
1140 6     6   9 my $self = shift;
1141 6         7 my $ord = shift;
1142              
1143 6         11 my $encode = $self->get('encode');
1144 6         11 my $format = $encode->{wide};
1145 6         10 my @wides = ();
1146 6 100 33     28 if (0) {
    50          
1147 0 50       0 } elsif ( 0x0000 <= $ord && $ord <= 0xffff ) {
1148 5 50 33     13 if ( 0xd800 <= $ord && $ord <= 0xdfff ) {
1149 0         0 die "Illegal character $ord";
1150             }
1151              
1152 5         12 @wides = ( $ord );
1153             } elsif ( 0x01_0000 <= $ord && $ord <= 0x10_ffff ) {
1154 1   33     6 $format = $encode->{vwide} || $format x 2;
1155              
1156 1         4 $ord -= 0x01_0000;
1157 1         3 my $ord2 = 0xdc00 + ( 0x3ff & $ord );
1158 1         3 $ord >>= 10;
1159 1         2 my $ord1 = 0xd800 + ( 0x3ff & $ord );
1160 1         3 @wides = ( $ord1, $ord2 );
1161             } else {
1162 0         0 die "Illegal character $ord";
1163             }
1164 6         41 return sprintf( $format, @wides );
1165             }
1166             sub _encode_ord2utf8 {
1167 6     6   12 my $self = shift;
1168 6         8 my $ord = shift;
1169              
1170 6         11 my @bytes = ();
1171 6         9 my $format = undef;
1172              
1173 6         12 my $encode = $self->get('encode');
1174 6 50 66     46 if (0) {
    100 66        
    100 33        
    50          
1175 0 50       0 } elsif ( 0x00 <= $ord && $ord <= 0x7f ) {
1176             # 1 byte represenstation
1177 0         0 $format = $encode->{byte};
1178 0         0 @bytes = ( $ord );
1179             } elsif ( 0x0080 <= $ord && $ord <= 0x07ff ) {
1180             # 2 byte represenstation
1181 4   33     9 $format = $encode->{byte2} || $format x 2;
1182              
1183 4         9 my $ord2 = 0x80 + ( 0x3f & $ord );
1184 4         7 $ord >>= 6;
1185 4         7 my $ord1 = 0xc0 + ( 0x1f & $ord );
1186 4         9 @bytes = ( $ord1, $ord2 );
1187             } elsif ( 0x0800 <= $ord && $ord <= 0xffff ) {
1188 1 50 33     6 if ( 0xd800 <= $ord && $ord <= 0xdfff ) {
1189 0         0 die "Illegal character $ord";
1190             }
1191              
1192             # 3 byte represenstation
1193 1   33     3 $format = $encode->{byte3} || $format x 3;
1194              
1195 1         3 my $ord3 = 0x80 + ( 0x3f & $ord );
1196 1         2 $ord >>= 6;
1197 1         3 my $ord2 = 0x80 + ( 0x3f & $ord );
1198 1         2 $ord >>= 6;
1199 1         3 my $ord1 = 0xe0 + ( 0x0f & $ord );
1200 1         2 @bytes = ( $ord1, $ord2, $ord3 );
1201             } elsif ( 0x01_0000 <= $ord && $ord <= 0x10_ffff ) {
1202             # 4 byte represenstation
1203 1   33     3 $format = $encode->{byte4} || $format x 4;
1204              
1205 1         3 my $ord4 = 0x80 + ( 0x3f & $ord );
1206 1         3 $ord >>= 6;
1207 1         3 my $ord3 = 0x80 + ( 0x3f & $ord );
1208 1         3 $ord >>= 6;
1209 1         2 my $ord2 = 0x80 + ( 0x3f & $ord );
1210 1         2 $ord >>= 6;
1211 1         3 my $ord1 = 0xf0 + ( 0x07 & $ord );
1212 1         3 @bytes = ( $ord1, $ord2, $ord3, $ord4 );
1213             } else {
1214 0         0 die "Illegal character $ord";
1215             }
1216 6         33 return sprintf( $format, @bytes );
1217             }
1218             sub _encode_char {
1219 295     295   444 my $self = shift;
1220 295         534 my $ord = ord shift;
1221              
1222 295         477 my $encode = $self->get('encode');
1223 295   100     804 my $utf = $encode->{utf} // 0;
1224 295 100       772 if ( defined $encode->{$ord} ) {
    100          
    100          
    100          
    100          
1225 153         552 return $encode->{$ord};
1226             } elsif ( $utf == 8 ) {
1227 6         16 return $self->_encode_ord2utf8( $ord );
1228             } elsif ( $utf == 16 ) {
1229 6         14 return $self->_encode_ord2utf16( $ord );
1230             } elsif ( $ord <= 255 ) {
1231 127         532 return sprintf $encode->{byte}, $ord;
1232             } elsif ( $ord <= 65_535 ) {
1233 2   33     6 my $encoding = $encode->{wide} // $encode->{byte};
1234 2         11 return sprintf $encoding, $ord;
1235             } else {
1236 1   33     7 my $encoding = $encode->{vwide} // $encode->{wide} // $encode->{byte};
      0        
1237 1         6 return sprintf $encoding, $ord;
1238             }
1239             }
1240              
1241             # Find a good character to use for delimiting q or qq.
1242             sub _find_q {
1243 1     1   3 my $self = shift;
1244 1 50       3 local $_ = shift if @_;
1245              
1246 1         3 my %counts;
1247 1         166 $counts{$_}++ foreach /([[:punct:]])/g;
1248             #$counts{$_}++ foreach grep /[[:punct:]]/, split //;
1249 1   50     15 my $qpairs = $self->get('qpairs') || [];
1250 1         3 foreach my $pair (@$qpairs) {
1251             $counts{$pair}
1252             = List::Util::sum 0,
1253             grep defined,
1254 4         9 map { $counts{$_} }
  8         23  
1255             split //, $pair;
1256             }
1257              
1258             return List::Util::reduce {
1259 23 100 50 23   58 ( ( $counts{$a} //= 0 ) <= ( $counts{$b} //= 0 ) ) ? $a : $b
      50        
1260 1         4 } @{ $self->get('qpairs') }, @{ $self->get('qquotes') };
  1         3  
  1         3  
1261             }
1262             sub _push_position {
1263 592     592   914 my $self = shift;
1264 592         813 my $position = shift;
1265 592   50     790 push @{ $self->{_position} //= [] }, $position;
  592         1504  
1266 592         1031 return $self;
1267             }
1268             sub _pop_position {
1269 590     590   783 my $self = shift;
1270 590         711 return pop @{ $self->{_position} };
  590         1599  
1271             }
1272             sub _cache_position {
1273 142     142   209 my $self = shift;
1274              
1275 142   33     277 my $nest = $self->get('nested') // $self->get('dereference');
1276             my $pos = List::Util::reduce(
1277 0     0   0 sub { _subst( $nest, referent => $a, place => $b ) },
1278 142   100     579 @{ $self->{_position} //= [] }
  142         743  
1279             );
1280              
1281 142         554 my $var = $self->get('name');
1282 142 50       436 my $sigil = length $var ? substr $var, 0, 1 : '';
1283 142 50 33     587 if ( $sigil eq '@' || $sigil eq '%' ) {
    100          
1284 0 0       0 if ($pos) {
1285 0         0 $var = sprintf '$%s%s', substr($var, 1), $pos;
1286             } else {
1287 0         0 $var = _subst( $self->get('reference'), $var );
1288             }
1289             } elsif ($pos) {
1290 32   33     76 $var = _subst(
1291             $self->get('dereference') // $self->get('nested'),
1292             referent => $var,
1293             place => $pos
1294             );
1295             }
1296 142         3710 return $var;
1297             }
1298              
1299             __PACKAGE__->set(
1300             # _caching options
1301             _cache_hit => 0,
1302             );
1303             sub _cache_add {
1304 507     507   766 my $self = shift;
1305 507         641 my $ref = shift;
1306 507         773 my $value = shift;
1307              
1308 507 100       1383 return $self unless my $refaddr = Scalar::Util::refaddr $ref;
1309 110   50     299 my $_cache = $self->{_cache} //= {};
1310 110   50     262 my $entry = $_cache->{$refaddr} //= [ $self->_cache_position ];
1311 110 50       248 push @$entry, $value if @$entry == $self->get('_cache_hit');
1312              
1313 110         201 return $self;
1314             }
1315             sub _cache_get {
1316 548     548   757 my $self = shift;
1317 548         694 my $item = shift;
1318              
1319 548 100       1890 return unless my $refaddr = Scalar::Util::refaddr $item;
1320              
1321 148   100     485 my $_cache = $self->{_cache} //= {};
1322 148 100       361 if ( my $entry = $_cache->{$refaddr} ) {
1323 6         14 my $repr = $self->get('_cache_hit');
1324 6   33     38 return $entry->[$repr]
1325             // Carp::croak 'Recursive structures not allowed at ',
1326             $self->_cache_position;
1327             } else {
1328             # Pre-populate the cache, so that we can check for loops
1329 142         357 $_cache->{$refaddr} = [ $self->_cache_position ];
1330 142         540 return;
1331             }
1332             }
1333             sub _cache_reset {
1334 41     41   67 my $self = shift;
1335 41   100     64 %{ $self->{_cache} //= {} } = ();
  41         176  
1336 41         76 return $self;
1337             }
1338              
1339             1;
1340              
1341             __END__