File Coverage

blib/lib/Datify.pm
Criterion Covered Total %
statement 456 553 82.4
branch 221 372 59.4
condition 82 178 46.0
subroutine 69 76 90.7
pod 33 36 91.6
total 861 1215 70.8


line stmt bran cond sub pod time code
1 5     5   257702 use v5.14;
  5         18  
2 5     5   28 use warnings;
  5         10  
  5         220  
3              
4             package Datify v0.20.064;
5             # ABSTRACT: Simple stringification of data.
6              
7              
8 5     5   31 use mro (); #qw( get_linear_isa );
  5         9  
  5         73  
9 5     5   22 use overload (); #qw( Method Overloaded );
  5         10  
  5         69  
10              
11 5     5   23 use Carp (); #qw( carp croak );
  5         10  
  5         97  
12 5     5   26 use List::Util (); #qw( reduce sum );
  5         7  
  5         133  
13 5     5   2438 use LooksLike v0.20.060 (); #qw( number numeric representation );
  5         18468  
  5         177  
14 5     5   38 use Scalar::Util (); #qw( blessed refaddr reftype );
  5         10  
  5         103  
15 5     5   2495 use String::Tools v0.19.045 (); #qw( stitch stringify subst );
  5         7273  
  5         145  
16 5     5   2341 use Sub::Util 1.40 (); #qw( subname );
  5         1583  
  5         3601  
17              
18              
19             ### Constructor ###
20              
21              
22             sub new {
23 2905   50 2905 1 15004 my $class = shift || __PACKAGE__;
24              
25 2905         3399 my %self = ();
26 2905 50       4727 if ( defined( my $blessed = Scalar::Util::blessed($class) ) ) {
27 0         0 %self = %$class; # shallow copy
28 0         0 $class = $blessed;
29             }
30 2905 100       5892 return @_ ? bless( \%self, $class )->set(@_) : bless( \%self, $class );
31             }
32              
33              
34              
35             ### Accessor ###
36              
37              
38              
39              
40             sub exists {
41 7375     7375 1 7934 my $self = shift;
42 7375 50       10540 return unless my $count = scalar(@_);
43              
44 7375         9599 my $SETTINGS = $self->_settings;
45 7375 100       14082 if ( Scalar::Util::blessed($self) ) {
46             return $count == 1
47             ? exists $self->{ $_[0] } && $self
48             || exists $SETTINGS->{ $_[0] } && $SETTINGS
49             : map {
50 7331 50 100     30626 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     522 : map { exists $SETTINGS->{ $_ } && $SETTINGS } @_;
  0 50       0  
57             }
58             }
59              
60              
61              
62             sub _get_setting {
63 7337     7337   11493 my $setting = $_[0]->exists( local $_ = $_[1] );
64 7337 100       19303 return $setting ? $setting->{$_} : do {
65 689 50       975 Carp::carp( 'Unknown key ', $_ )
66             unless $_[0]->_internal(1);
67             undef
68 689         1429 };
69             }
70             sub get {
71 6469     6469 1 11625 my $self = shift;
72 6469         6787 my $count = scalar(@_);
73              
74 6469 100       13737 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 6462 100       12863 : map { $self->_get_setting($_) } @_;
  1316 50       1856  
79             } else {
80             return
81 1         4 $count == 0 ? %{ $self->_settings }
82             : $count == 1 ? $self->_get_setting(shift)
83 7 50       27 : map { $self->_get_setting($_) } @_;
  0 100       0  
84             }
85             }
86              
87              
88             ### Setter ###
89              
90              
91             sub set {
92 1238     1238 1 9865 my $self = shift;
93 1238 50       2022 return $self unless @_;
94 1238         2612 my %set = @_;
95              
96 1238         1534 my $return;
97             my $class;
98 1238 100       2812 if ( defined( $class = Scalar::Util::blessed($self) ) ) {
99             # Make a shallow copy
100 1111         3809 $self = bless { %$self }, $class;
101 1111         1595 $return = 0;
102             } else {
103 127         164 $class = $self;
104 127         217 $self = $class->_settings;
105 127         180 $return = 1;
106             }
107              
108 1238 100       2400 delete $self->{keyword_set} if ( $set{keywords} );
109 1238         1698 delete $self->{"_tr$_"} for grep { exists $set{"quote$_"} } ( 1, 2, 3 );
  3714         6843  
110              
111 1238         2023 my $internal = $class->_internal;
112 1238         3212 while ( my ( $k, $v ) = each %set ) {
113 1441 100 100     2540 Carp::carp( 'Unknown key ', $k )
114             unless $internal || $class->exists($k);
115 1441 100 100     3941 study($v) if defined($v) && !ref($v);
116 1441         3977 $self->{$k} = $v;
117             }
118              
119 1238         2824 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   41 no strict 'refs';
  5         28  
  5         6134  
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 21860 my $self = &self;
172 41         71 my ($sigil, $name);
173 41 50 33     157 if ( defined $_[0] && !ref $_[0] ) {
174 41         781 ( $sigil, $name )
175             = $_[0] =~ /^($sigils)?((?:$package\::)?$varname|$package\::)$/;
176 41 50       130 shift if length $name;
177             }
178 41 50       87 my $value = 1 == @_ ? shift : \@_;
179              
180 41 50       71 if ( length $name ) {
181 41 50       117 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       77 Carp::croak "Missing name" unless ( length $name );
193              
194 41 100       69 unless ($sigil) {
195 22         72 my $ref = ref $value;
196 22 50       60 $sigil
    50          
197             = $ref eq 'ARRAY' ? '@'
198             : $ref eq 'HASH' ? '%'
199             : '$';
200             }
201 41         69 $name = $sigil . $name;
202 41         99 $self = $self->set( name => $name );
203              
204 41 0       124 $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         74 $value = _subst( $self->get('assign'), var => $name, value => $value );
212 41 50       3265 if ( my $beautify = $self->get('beautify') ) {
213 0         0 return $beautify->($value);
214             } else {
215 41         193 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 21     21 1 40 my $self = &self;
232 21 50 33     70 return $self->scalarify(shift) if @_ and defined($_[0]);
233 21         47 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 4412 my $self = &self;
262 400 50       920 local $_ = shift if @_;
263 400 50       651 return $self->undefify unless defined;
264 400 50       559 $_ = String::Tools::stringify($_) if ref;
265 400         678 my $quote1 = $self->get('quote1');
266 400   66     1189 my ( $open, $close ) = $self->_get_delim( shift // $quote1 );
267              
268 400         674 $self = $self->set( encode => $self->get('encode1') );
269 400         726 my $to_encode = $self->_to_encode( $open, $close );
270 400         1403 s/([$to_encode])/$self->_encode_char($1)/eg;
  116         255  
271              
272 400 100       784 if ( $quote1 ne $open ) {
273 1 50       7 if ( $open =~ /\w/ ) {
274 0         0 $open = ' ' . $open;
275 0         0 $close = ' ' . $close;
276             }
277 1         4 $open = $self->get('q1') . $open;
278             }
279              
280 400         2794 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 4455 my $self = &self;
290 60 50       167 local $_ = shift if @_;
291 60 50       161 return $self->undefify unless defined;
292 60 50       117 $_ = String::Tools::stringify($_) if ref;
293 60         114 my $quote2 = $self->get('quote2');
294 60   33     216 my ( $open, $close ) = $self->_get_delim( shift // $quote2 );
295              
296 60         92 my @sigils;
297 60 50       115 if ( my $sigils = $self->get('sigils') ) {
298 60         225 push @sigils, split //, $sigils;
299             }
300              
301             # quote char(s), sigils, and backslash.
302 60         134 $self = $self->set( encode => $self->get('encode2') );
303 60         172 my $to_encode = $self->_to_encode( $open, $close, @sigils );
304 60         602 s/([$to_encode])/$self->_encode_char($1)/eg;
  179         382  
305              
306 60 50       170 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         428 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         9  
  5         10603  
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 10075 my $self = &self;
374 422 50       850 local $_ = shift if @_;
375 422 50       672 return $self->undefify unless defined;
376 422 50       598 $_ = String::Tools::stringify($_) if ref;
377 422         465 local $@ = undef;
378              
379 422         742 my ( $quote, $quote1, $quote2 ) = $self->get(qw( quote quote1 quote2 ));
380 422 50       747 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         649 my $longstr = $self->get('longstr');
388 422         652 my $encode2 = $self->get('encode2');
389 422   33     1150 my $also = $encode2 && $encode2->{also};
390 422 100 33     2791 return $self->stringify2($_)
      66        
      66        
391             if ( ( $longstr && $longstr < length() ) || ( $also && /[$also]/ ) );
392              
393 410         672 my $tr1 = $self->get('_tr1');
394 410 50       1198 $self = $self->set( _tr1 => $tr1 = "tr\\$quote1\\$quote1\\" )
395             if ( not $tr1 );
396 410   50     20231 my $single_quotes = eval $tr1 // die $@;
397 410 100       1621 return $self->stringify1($_) unless $single_quotes;
398              
399 21         127 my ( $sigils, $tr2 ) = $self->get(qw( sigils _tr2 ));
400 21 50       114 $self = $self->set( _tr2 => $tr2 = "tr\\$quote2$sigils\\$quote2$sigils\\" )
401             if ( not $tr2 );
402 21   50     905 my $double_quotes = eval $tr2 // die $@;
403 21 100       108 return $self->stringify2($_) unless $double_quotes;
404              
405 1         5 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 3414     3414 1 4282 my $self = &self;
425 3414 50       5832 local $_ = shift if @_;
426              
427 3414 50       4608 return undef unless defined;
428              
429 3414 50       4388 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 3414         5390 return LooksLike::numeric($_);
439             }
440              
441              
442             sub numify {
443 384     384 1 34931 my $self = &self;
444 384 50       738 local $_ = shift if @_;
445              
446 384 100       591 return $self->undefify unless defined;
447              
448 383 100       594 if ( $self->is_numeric($_) ) {
    100          
449 369 50       4092 return $_ unless my $sep = $self->get('num_sep');
450              
451             # Fractional portion
452 369         995 s{^(\s*[-+]?\d*\.\d\d)(\d+)} [${1}$sep${2}];
453 369         1555 1 while s{^(\s*[-+]?\d*\.(?:\d+$sep)+\d\d\d)(\d+)}[${1}$sep${2}];
454              
455             # Whole portion
456 369         1276 1 while s{^(\s*[-+]?\d+)(\d{3})} [${1}$sep${2}];
457              
458 369         1150 return $_;
459             }
460             elsif ( LooksLike::number($_) ) {
461 12         263 return LooksLike::representation(
462             $_,
463             "infinity" => $self->get('infinite'),
464             "-infinity" => $self->get('-infinite'),
465             "nan" => $self->get('nonnumber')
466             );
467             }
468              
469 2         303 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 552     552 1 18925 my $self = &self;
485 552 50       1157 local $_ = shift if @_;
486              
487 552   100     969 my $value = $self->_cache_get($_) // $self->_scalarify($_);
488 552 100       10964 $self->isa( scalar caller )
489             ? $self->_cache_add( $_ => $value )
490             : $self->_cache_reset($_);
491 552         1124 return $value;
492             }
493              
494             sub _scalarify {
495 546     546   703 my $self = &self;
496 546 50       1039 local $_ = shift if @_;
497              
498 546 100       914 return $self->undefify unless defined $_;
499              
500 526 100       1062 if ( defined( my $blessed = Scalar::Util::blessed($_) ) ) {
501             return
502 36 100       93 $blessed eq 'Regexp' ? $self->regexpify($_)
503             : $self->objectify($_);
504             }
505              
506 490         739 my $ref = Scalar::Util::reftype $_;
507 490 100       685 if ( not $ref ) {
508             # Handle GLOB, LVALUE, and VSTRING
509 384         637 my $ref2 = ref \$_;
510             return
511 384 100       1417 $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       463 : do {
    100          
    50          
    100          
    100          
    100          
    100          
531 18   33     58 my $reference = $self->get( lc($ref) . '_reference' )
532             || $self->get('reference');
533              
534 18 50       101 $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 6 my $self = &self;
556 4         7 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 8 my $self = &self;
574 4 50       9 if ( defined( my $vsep = $self->get('vsep') ) ) {
575 0         0 return sprintf $self->get('vformat'), $vsep, shift;
576             } else {
577 4         7 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 5 my $self = &self;
612 4 50       10 local $_ = shift if @_;
613 4         7 local $@ = undef;
614              
615 4         6 my ( $quote3, $tr3 ) = $self->get(qw( quote3 _tr3 ));
616 4 50       16 $self = $self->set( _tr3 => $tr3 = "tr\\$quote3\\$quote3\\" )
617             if ( not $tr3 );
618 4   50     219 my $quoter = eval $tr3 // die $@;
619 4 50 33     24 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         8 $self = $self->set( encode => $self->get('encode3') );
625 4         12 my $to_encode = $self->_to_encode( $open, $close );
626 4         37 s/([$to_encode])/$self->_encode_char($1)/eg;
  0         0  
627              
628 4 50       11 if ( $open =~ /\w/ ) {
629 0         0 $open = ' ' . $open;
630 0         0 $close = ' ' . $close;
631             }
632              
633 4         9 $open = $self->get('q3') . $open;
634              
635 4         25 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         36 my @values;
646 24         73 for ( my $i = 0; $i < @_; $i++ ) {
647 116         184 my $value = $_[$i];
648 116         265 $self = $self->_push_position("[$i]");
649 116         209 push @values, $self->scalarify($value);
650 116         194 $self->_pop_position;
651             }
652 24         72 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 51 my $self = &self;
666 24         51 return _subst( $self->get('array_ref'), $self->listify(@_) );
667             }
668              
669              
670              
671             ### Hash ###
672              
673              
674             sub is_keyword {
675 326     326 1 459 my $self = &self;
676              
677 326         508 my $keyword_set = $self->get('keyword_set');
678 326 100       568 if ( not $keyword_set ) {
679 63   50     111 my $keywords = $self->get('keywords') // [];
680 63 50       134 return unless @$keywords;
681 63         95 $keyword_set = { map { $_ => 1 } @$keywords };
  63         201  
682 63         121 $self->{keyword_set} = $keyword_set;
683             }
684 326         1721 return exists $keyword_set->{ +shift };
685             }
686              
687              
688             sub keyify {
689 493     493 1 657 my $self = &self;
690 493 50       965 local $_ = shift if @_;
691              
692 493 50       772 return $self->undefify unless defined;
693 493 50       700 return $_ if ref;
694              
695 493 100 33     745 if ( $self->is_numeric($_) ) {
    100 66        
696 167         2146 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 294         741 return "$_"; # Make sure it's stringified.
705             }
706 32         104 return $self->stringify($_);
707             }
708              
709              
710              
711              
712             sub keysort($$);
713             BEGIN {
714 5     5   41 no warnings 'qw';
  5         30  
  5         587  
715 5 50   5   102 my $a_cmp__b
716             = $^V >= v5.16.0
717             ? 'CORE::fc($a) cmp CORE::fc($b)'
718             : 'lc($a) cmp lc($b)';
719 5         44 my $keysort = String::Tools::stitch(qw(
720             sub keysort($$) {
721             my ( $a, $b ) = @_;
722             my $numa = Datify->is_numeric($a);
723             my $numb = Datify->is_numeric($b);
724             return(
725             ( $numa && $numb ? $a <=> $b
726             : $numa ? -1
727             : $numb ? +1
728             : $a_cmp__b )
729             || $a cmp $b
730             );
731             }
732             ));
733 5         3987 $keysort = String::Tools::subst( $keysort, a_cmp__b => $a_cmp__b );
734 5 50 50 1269 1 6870 eval($keysort) or $@ and die $@;
  1269   100     2613  
  1269         2263  
  1269         30822  
  1269         32183  
735             }
736              
737              
738              
739             sub hashkeys {
740 58     58 1 77 my $self = shift;
741 58         75 my $hash = shift;
742              
743 58         178 my @keys = keys %$hash;
744 58 50       99 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       109 if ( my $keymap = $self->get('keymap') ) {
771 0         0 @keys = map { $self->$keymap($_) } @keys;
  0         0  
772             }
773 58 50       108 if ( my $keysort = $self->get('keysort') ) {
774 58         767 @keys = sort $keysort @keys;
775             }
776 58         265 return @keys;
777             }
778              
779             sub hashkeyvals {
780 28     28 0 39 my $self = shift;
781 28         43 my $hash = shift;
782              
783 28         64 return map { $_ => $hash->{$_} } $self->hashkeys($hash);
  292         519  
784             }
785              
786              
787             sub pairify {
788 28     28 1 52 my $self = &self;
789 28 50       69 if (1 == @_) {
790 28         69 my $ref = Scalar::Util::reftype $_[0];
791 28 50       107 @_ = $ref eq 'ARRAY' ? @{ +shift }
  0 50       0  
792             : $ref eq 'HASH' ? $self->hashkeyvals(shift)
793             : @_;
794             }
795             # Use for loop in order to preserve the order of @_,
796             # rather than each %{ { @_ } }, which would mix-up the order.
797 28         53 my @list;
798 28         62 my $pair = $self->get('pair');
799 28         97 for ( my $i = 0; $i < @_ - 1; $i += 2 ) {
800 292         16426 my ( $k, $v ) = @_[ $i, $i + 1 ];
801 292         574 my $key = $self->keyify($k);
802 292         673 $self = $self->_push_position("{$key}");
803 292         487 my $val = $self->scalarify($v);
804 292         591 $self->_pop_position;
805 292         555 push @list, _subst( $pair, key => $key, value => $val );
806             }
807 28         1627 return join( $self->get('list_sep'), @list );
808             }
809              
810              
811              
812              
813             __PACKAGE__->set(
814             # Hash options
815             hash_ref => '{$_}',
816             pair => '$key => $value',
817             keymap => undef,
818             keysort => \&Datify::keysort,
819             keyfilter => undef,
820             keyfilterdefault => 1,
821             keywords => [qw(undef)],
822             #keyword_set => { 'undef' => 1 },
823             );
824              
825              
826             sub hashify {
827 28     28 1 52 my $self = &self;
828 28         68 return _subst( $self->get('hash_ref'), $self->pairify(@_) );
829             }
830              
831              
832              
833             ### Objects ###
834              
835              
836             sub overloaded {
837 32     32 1 44 my $self = &self;
838 32 50       61 my $object = @_ ? shift : $_;
839              
840 32 50 33     152 return unless defined( Scalar::Util::blessed($object) )
841             && overload::Overloaded($object);
842              
843 0   0     0 my $overloads = $self->get('overloads') || [];
844 0         0 foreach my $overload (@$overloads) {
845 0 0       0 if ( my $method = overload::Method( $object => $overload ) ) {
846 0         0 return $method;
847             }
848             }
849 0         0 return;
850             }
851              
852              
853              
854              
855             __PACKAGE__->set(
856             # Object options
857             overloads => [ '""', '0+' ],
858             object => 'bless($data, $class_str)',
859             #object => '$class->new($data)',
860             #object => '$class=$data',
861             );
862              
863              
864             sub objectify {
865 32     32 1 44 my $self = &self;
866 32 50       61 my $object = @_ ? shift : $_;
867              
868 32 50       77 return $self->scalarify($object)
869             unless defined( my $class = Scalar::Util::blessed($object) );
870              
871 32         44 my $data;
872 32 50       55 if (0) {
    50          
    50          
873 0         0 } elsif ( my $code = $self->_find_handler($class) ) {
874 0         0 return $self->$code($object);
875             } elsif ( my $method = $self->overloaded($object) ) {
876 0         0 $data = $self->scalarify( $object->$method() );
877             } elsif ( my $attrkeyvals = $object->can('_attrkeyvals') ) {
878             # TODO: Look this up via meta-objects
879 0         0 $data = $self->hashify( $object->$attrkeyvals() );
880             } else {
881 32         1457 $data = Scalar::Util::reftype $object;
882              
883 32 50       169 $data
    50          
    50          
    100          
    100          
    50          
    50          
    50          
    100          
884             = $data eq 'ARRAY' ? $self->arrayify( @$object )
885             : $data eq 'CODE' ? $self->codeify( $object )
886             : $data eq 'FORMAT' ? $self->formatify( $object )
887             : $data eq 'GLOB' ? $self->globify( $object )
888             : $data eq 'HASH' ? $self->hashify( $object )
889             : $data eq 'IO' ? $self->ioify( $object )
890             : $data eq 'REF' ? $self->refify( $$object )
891             : $data eq 'REGEXP' ? $self->regexpify( $object )
892             : $data eq 'SCALAR' ? $self->refify( $$object )
893             : "*UNKNOWN{$data}";
894             }
895              
896 32         1858 return _subst(
897             $self->get('object'),
898             class_str => $self->stringify($class),
899             class => $class,
900             data => $data
901             );
902             }
903              
904              
905              
906             ### Objects: IO ###
907              
908              
909             __PACKAGE__->set(
910             # IO options
911             io => '*$name{IO}',
912             );
913              
914              
915              
916             sub ioify {
917 4     4 1 8 my $self = &self;
918 4 50       7 my $io = @_ ? shift : $_;
919              
920 4         6 my $ioname = 'UNKNOWN';
921 4         8 foreach my $ioe (qw( IN OUT ERR )) {
922 5     5   42 no strict 'refs';
  5         9  
  5         14404  
923 8 100       9 if ( *{"main::STD$ioe"}{IO} == $io ) {
  8         28  
924 4         5 $ioname = "STD$ioe";
925 4         5 last;
926             }
927             }
928             # TODO
929             #while ( my ( $name, $glob ) = each %main:: ) {
930             # no strict 'refs';
931             # if ( defined( *{$glob}{IO} ) && *{$glob}{IO} == $io ) {
932             # keys %main::; # We're done, so reset each()
933             # $ioname = $name;
934             # last;
935             # }
936             #}
937 4         9 return _subst( $self->get('io'), name => $ioname );
938             }
939              
940              
941              
942             ### Other ###
943              
944              
945             __PACKAGE__->set(
946             # Code options
947             code => 'sub {$body}',
948             codename => '\&$codename',
949             body => '...',
950             );
951              
952              
953             sub codeify {
954 8     8 1 14 my $self = &self;
955              
956 8         15 my $template = $self->get('code');
957 8         19 my %data = ( body => $self->get('body') );
958 8 50 33     34 if ( @_ && defined( $_[0] ) ) {
959 8         11 local $_ = shift;
960 8 50       22 if ( my $ref = Scalar::Util::reftype($_) ) {
961 8 50       18 if ( $ref eq 'CODE' ) {
962 8 100       65 if ( ( my $subname = Sub::Util::subname($_) )
963             !~ /\A(?:\w+\::)*__ANON__\z/ )
964             {
965 4   33     13 $template = $self->get('codename') // $template;
966 4         18 %data = ( codename => $subname );
967             }
968             } else {
969 0         0 %data = ( body => $self->scalarify($_) );
970             }
971             } else {
972 0         0 %data = ( body => $_ );
973             }
974             }
975 8         26 return _subst( $template, %data );
976             }
977              
978              
979              
980              
981             __PACKAGE__->set(
982             # Reference options
983             reference => '\\$_',
984             dereference => '$referent->$place',
985             nested => '$referent$place',
986             );
987              
988              
989             sub refify {
990 52     52 1 77 my $self = &self;
991 52 50       131 local $_ = shift if @_;
992 52         86 return _subst( $self->get('reference'), $self->scalarify($_) );
993             }
994              
995              
996              
997              
998             __PACKAGE__->set(
999             # Format options
1000             format => "format UNKNOWN =\n.\n",
1001             );
1002              
1003              
1004             sub formatify {
1005 4     4 1 7 my $self = &self;
1006             #Carp::croak "Unhandled type: ", ref shift;
1007 4         7 return $self->get('format');
1008             }
1009              
1010              
1011              
1012              
1013             sub globify {
1014 4     4 1 8 my $self = &self;
1015 4         12 my $name = '' . shift;
1016 4 50       113 if ( $name =~ /^\*$package\::(?:$word|$digits)?$/ ) {
1017 4         15 $name =~ s/^\*main::/*::/;
1018             } else {
1019 0         0 $name =~ s/^\*($package\::.+)/'*{' . $self->stringify($1) . '}'/e;
  0         0  
1020             }
1021 4         15 return $name;
1022             }
1023              
1024              
1025              
1026             sub beautify {
1027 0     0 1 0 my $self = &self;
1028 0         0 my ( $method, @params ) = @_;
1029              
1030 0   0     0 $method = $self->can($method) || die "Cannot $method";
1031              
1032 0 0       0 if ( my $beauty = $self->get('beautify') ) {
1033 0         0 return $beauty->( $self->$method(@params) );
1034             } else {
1035 0         0 return $self->$method(@params);
1036             }
1037             }
1038              
1039             ### Private Methods & Settings ###
1040             ### Do not use these methods & settings outside of this package,
1041             ### they are subject to change or disappear at any time.
1042             sub class {
1043 11 100   11 0 8970 return scalar caller unless @_;
1044 10         19 my $caller = caller;
1045 10         13 my $class;
1046 10 50 33     55 if ( defined( $class = Scalar::Util::blessed( $_[0] ) )
      66        
1047             || ( !ref( $_[0] ) && length( $class = $_[0] ) ) )
1048             {
1049 10 100       42 if ( $class->isa($caller) ) {
1050 8         13 shift;
1051 8         20 return $class;
1052             }
1053             }
1054 2         6 return $caller;
1055             }
1056             sub self {
1057 7851     7851 0 9828 my $self = shift;
1058 7851 100       18072 return defined( Scalar::Util::blessed($self) ) ? $self : $self->new();
1059             }
1060 9621   100 9621   33865 sub _internal { return $_[0]->isa( scalar caller( 1 + ( $_[1] // 0 ) ) ) }
1061             sub _private {
1062 6628 100   6628   8650 Carp::croak('Illegal use of private method') unless $_[0]->_internal(1);
1063             }
1064             sub _settings() {
1065 6628     6628   9965 &_private;
1066 6627         9449 \state %SETTINGS;
1067             }
1068              
1069             sub _nameify {
1070 44 50   44   79 local $_ = shift if @_;
1071 44         125 s/::/_/g;
1072 44         250 return lc() . 'ify';
1073             }
1074             sub _find_handler {
1075 32     32   42 my $self = shift;
1076 32         40 my $class = shift;
1077              
1078 32         110 my $isa = mro::get_linear_isa($class);
1079 32         64 foreach my $c (@$isa) {
1080 44 50       65 next unless my $code = $self->can( _nameify($c) );
1081 0         0 return $code;
1082             }
1083 32         101 return;
1084             }
1085              
1086             sub _subst {
1087 535 50   535   1045 die "Cannot subst on an undefined value"
1088             unless defined $_[0];
1089 535         1488 goto &String::Tools::subst;
1090             }
1091              
1092             sub _get_delim {
1093 464     464   566 my $self = shift;
1094 464         518 my $open = shift;
1095              
1096 464         461 my $close;
1097 464 50       801 if ( 1 < length $open ) {
1098 0   0     0 my $qpairs = $self->get('qpairs') || [];
1099 0         0 my %qpairs = map { $_ => 1 } @$qpairs;
  0         0  
1100 0 0       0 if ( $qpairs{$open} ) {
1101 0         0 ( $open, $close ) = split //, $open, 2;
1102             } else {
1103 0         0 ( $open ) = split //, $open, 1
1104             }
1105             }
1106 464 50       739 $close = $open unless $close;
1107              
1108 464         947 return $open, $close;
1109             }
1110              
1111             sub _to_encode {
1112 464     464   557 my $self = shift;
1113              
1114 464         659 my $encode = $self->get('encode');
1115              
1116             # Ignore the settings for byte, byte2, byte3, byte4, vwide, wide,
1117             # and utf
1118             my @encode
1119 464         1034 = grep { !(/\A(?:also|byte[234]?|v?wide|utf)\z/) } keys(%$encode);
  1665         4251  
1120              
1121 464   100     1287 my @ranges = ( $encode->{also} // () );
1122 464         809 foreach my $element (@_) {
1123 1048 50       1897 if ( LooksLike::number($element) ) {
    50          
1124 0         0 push @encode, $element;
1125             } elsif ( length($element) == 1 ) {
1126             # An actual character, lets get the ordinal value and use that
1127 1048         16180 push @encode, ord($element);
1128             } else {
1129             # Something longer, it must be a range of chars,
1130             # like [:cntrl:], \x00-\x7f, or similar
1131 0         0 push @ranges, $element;
1132             }
1133             }
1134             @encode = map {
1135             # Encode characters in their \xXX or \x{XXXX} notation,
1136             # to get the literal values
1137 2079 100       5201 sprintf( $_ <= 255 ? '\\x%02x' : '\\x{%04x}', $_ )
1138             } sort {
1139 464         1292 $a <=> $b
  3614         4624  
1140             } @encode;
1141              
1142 464         1312 return join( '', @encode, @ranges );
1143             }
1144              
1145             sub _encode_ord2utf16 {
1146 6     6   11 my $self = shift;
1147 6         9 my $ord = shift;
1148              
1149 6         13 my $encode = $self->get('encode');
1150 6         11 my $format = $encode->{wide};
1151 6         10 my @wides = ();
1152 6 100 33     27 if (0) {
    50          
1153 0 50       0 } elsif ( 0x0000 <= $ord && $ord <= 0xffff ) {
1154 5 50 33     14 if ( 0xd800 <= $ord && $ord <= 0xdfff ) {
1155 0         0 die "Illegal character $ord";
1156             }
1157              
1158 5         13 @wides = ( $ord );
1159             } elsif ( 0x01_0000 <= $ord && $ord <= 0x10_ffff ) {
1160 1   33     4 $format = $encode->{vwide} || $format x 2;
1161              
1162 1         3 $ord -= 0x01_0000;
1163 1         2 my $ord2 = 0xdc00 + ( 0x3ff & $ord );
1164 1         3 $ord >>= 10;
1165 1         4 my $ord1 = 0xd800 + ( 0x3ff & $ord );
1166 1         3 @wides = ( $ord1, $ord2 );
1167             } else {
1168 0         0 die "Illegal character $ord";
1169             }
1170 6         34 return sprintf( $format, @wides );
1171             }
1172             sub _encode_ord2utf8 {
1173 6     6   11 my $self = shift;
1174 6         10 my $ord = shift;
1175              
1176 6         8 my @bytes = ();
1177 6         12 my $format = undef;
1178              
1179 6         8 my $encode = $self->get('encode');
1180 6 50 66     56 if (0) {
    100 66        
    100 33        
    50          
1181 0 50       0 } elsif ( 0x00 <= $ord && $ord <= 0x7f ) {
1182             # 1 byte represenstation
1183 0         0 $format = $encode->{byte};
1184 0         0 @bytes = ( $ord );
1185             } elsif ( 0x0080 <= $ord && $ord <= 0x07ff ) {
1186             # 2 byte represenstation
1187 4   33     13 $format = $encode->{byte2} || $format x 2;
1188              
1189 4         8 my $ord2 = 0x80 + ( 0x3f & $ord );
1190 4         6 $ord >>= 6;
1191 4         7 my $ord1 = 0xc0 + ( 0x1f & $ord );
1192 4         11 @bytes = ( $ord1, $ord2 );
1193             } elsif ( 0x0800 <= $ord && $ord <= 0xffff ) {
1194 1 50 33     6 if ( 0xd800 <= $ord && $ord <= 0xdfff ) {
1195 0         0 die "Illegal character $ord";
1196             }
1197              
1198             # 3 byte represenstation
1199 1   33     5 $format = $encode->{byte3} || $format x 3;
1200              
1201 1         4 my $ord3 = 0x80 + ( 0x3f & $ord );
1202 1         3 $ord >>= 6;
1203 1         2 my $ord2 = 0x80 + ( 0x3f & $ord );
1204 1         3 $ord >>= 6;
1205 1         3 my $ord1 = 0xe0 + ( 0x0f & $ord );
1206 1         3 @bytes = ( $ord1, $ord2, $ord3 );
1207             } elsif ( 0x01_0000 <= $ord && $ord <= 0x10_ffff ) {
1208             # 4 byte represenstation
1209 1   33     4 $format = $encode->{byte4} || $format x 4;
1210              
1211 1         4 my $ord4 = 0x80 + ( 0x3f & $ord );
1212 1         2 $ord >>= 6;
1213 1         5 my $ord3 = 0x80 + ( 0x3f & $ord );
1214 1         3 $ord >>= 6;
1215 1         2 my $ord2 = 0x80 + ( 0x3f & $ord );
1216 1         13 $ord >>= 6;
1217 1         3 my $ord1 = 0xf0 + ( 0x07 & $ord );
1218 1         4 @bytes = ( $ord1, $ord2, $ord3, $ord4 );
1219             } else {
1220 0         0 die "Illegal character $ord";
1221             }
1222 6         36 return sprintf( $format, @bytes );
1223             }
1224             sub _encode_char {
1225 295     295   395 my $self = shift;
1226 295         517 my $ord = ord shift;
1227              
1228 295         456 my $encode = $self->get('encode');
1229 295   100     741 my $utf = $encode->{utf} // 0;
1230 295 100       679 if ( defined $encode->{$ord} ) {
    100          
    100          
    100          
    100          
1231 153         488 return $encode->{$ord};
1232             } elsif ( $utf == 8 ) {
1233 6         15 return $self->_encode_ord2utf8( $ord );
1234             } elsif ( $utf == 16 ) {
1235 6         18 return $self->_encode_ord2utf16( $ord );
1236             } elsif ( $ord <= 255 ) {
1237 127         584 return sprintf $encode->{byte}, $ord;
1238             } elsif ( $ord <= 65_535 ) {
1239 2   33     7 my $encoding = $encode->{wide} // $encode->{byte};
1240 2         12 return sprintf $encoding, $ord;
1241             } else {
1242 1   33     8 my $encoding = $encode->{vwide} // $encode->{wide} // $encode->{byte};
      0        
1243 1         5 return sprintf $encoding, $ord;
1244             }
1245             }
1246              
1247             # Find a good character to use for delimiting q or qq.
1248             sub _find_q {
1249 1     1   3 my $self = shift;
1250 1 50       4 local $_ = shift if @_;
1251              
1252 1         2 my %counts;
1253 1         168 $counts{$_}++ foreach /([[:punct:]])/g;
1254             #$counts{$_}++ foreach grep /[[:punct:]]/, split //;
1255 1   50     14 my $qpairs = $self->get('qpairs') || [];
1256 1         2 foreach my $pair (@$qpairs) {
1257             $counts{$pair}
1258             = List::Util::sum 0,
1259             grep defined,
1260 4         11 map { $counts{$_} }
  8         22  
1261             split //, $pair;
1262             }
1263              
1264             return List::Util::reduce {
1265 23 100 50 23   60 ( ( $counts{$a} //= 0 ) <= ( $counts{$b} //= 0 ) ) ? $a : $b
      50        
1266 1         5 } @{ $self->get('qpairs') }, @{ $self->get('qquotes') };
  1         4  
  1         3  
1267             }
1268             sub _push_position {
1269 596     596   755 my $self = shift;
1270 596         688 my $position = shift;
1271 596   50     627 push @{ $self->{_position} //= [] }, $position;
  596         1295  
1272 596         829 return $self;
1273             }
1274             sub _pop_position {
1275 594     594   670 my $self = shift;
1276 594         580 return pop @{ $self->{_position} };
  594         1325  
1277             }
1278             sub _cache_position {
1279 142     142   171 my $self = shift;
1280              
1281 142   33     229 my $nest = $self->get('nested') // $self->get('dereference');
1282             my $pos = List::Util::reduce(
1283 0     0   0 sub { _subst( $nest, referent => $a, place => $b ) },
1284 142   100     520 @{ $self->{_position} //= [] }
  142         647  
1285             );
1286              
1287 142         464 my $var = $self->get('name');
1288 142 50       383 my $sigil = length $var ? substr $var, 0, 1 : '';
1289 142 50 33     550 if ( $sigil eq '@' || $sigil eq '%' ) {
    100          
1290 0 0       0 if ($pos) {
1291 0         0 $var = sprintf '$%s%s', substr($var, 1), $pos;
1292             } else {
1293 0         0 $var = _subst( $self->get('reference'), $var );
1294             }
1295             } elsif ($pos) {
1296 32   33     79 $var = _subst(
1297             $self->get('dereference') // $self->get('nested'),
1298             referent => $var,
1299             place => $pos
1300             );
1301             }
1302 142         3151 return $var;
1303             }
1304              
1305             __PACKAGE__->set(
1306             # _caching options
1307             _cache_hit => 0,
1308             );
1309             sub _cache_add {
1310 511     511   659 my $self = shift;
1311 511         583 my $ref = shift;
1312 511         552 my $value = shift;
1313              
1314 511 100       1181 return $self unless my $refaddr = Scalar::Util::refaddr $ref;
1315 110   50     238 my $_cache = $self->{_cache} //= {};
1316 110   50     205 my $entry = $_cache->{$refaddr} //= [ $self->_cache_position ];
1317 110 50       210 push @$entry, $value if @$entry == $self->get('_cache_hit');
1318              
1319 110         167 return $self;
1320             }
1321             sub _cache_get {
1322 552     552   650 my $self = shift;
1323 552         659 my $item = shift;
1324              
1325 552 100       1588 return unless my $refaddr = Scalar::Util::refaddr $item;
1326              
1327 148   100     401 my $_cache = $self->{_cache} //= {};
1328 148 100       309 if ( my $entry = $_cache->{$refaddr} ) {
1329 6         22 my $repr = $self->get('_cache_hit');
1330 6   33     32 return $entry->[$repr]
1331             // Carp::croak 'Recursive structures not allowed at ',
1332             $self->_cache_position;
1333             } else {
1334             # Pre-populate the cache, so that we can check for loops
1335 142         283 $_cache->{$refaddr} = [ $self->_cache_position ];
1336 142         453 return;
1337             }
1338             }
1339             sub _cache_reset {
1340 41     41   63 my $self = shift;
1341 41   100     60 %{ $self->{_cache} //= {} } = ();
  41         151  
1342 41         55 return $self;
1343             }
1344              
1345             1;
1346              
1347             __END__