| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package Data::Annotation::Traverse; |
|
2
|
3
|
|
|
3
|
|
58
|
use v5.24; |
|
|
3
|
|
|
|
|
12
|
|
|
3
|
3
|
|
|
3
|
|
15
|
use experimental qw< signatures >; |
|
|
3
|
|
|
|
|
4
|
|
|
|
3
|
|
|
|
|
18
|
|
|
4
|
3
|
|
|
3
|
|
454
|
use Scalar::Util qw< blessed refaddr reftype >; |
|
|
3
|
|
|
|
|
5
|
|
|
|
3
|
|
|
|
|
251
|
|
|
5
|
|
|
|
|
|
|
|
|
6
|
3
|
|
|
3
|
|
19
|
use Exporter qw< import >; |
|
|
3
|
|
|
|
|
5
|
|
|
|
3
|
|
|
|
|
319
|
|
|
7
|
|
|
|
|
|
|
our @EXPORT_OK = qw< MISSING crumble kpath means_missing traverse_plain >; |
|
8
|
|
|
|
|
|
|
our %EXPORT_TAGS = (all => \@EXPORT_OK); |
|
9
|
|
|
|
|
|
|
|
|
10
|
3
|
|
|
3
|
|
26
|
use constant MISSING => \"Bubù-non-c'è-più"; |
|
|
3
|
|
|
|
|
5
|
|
|
|
3
|
|
|
|
|
4881
|
|
|
11
|
|
|
|
|
|
|
|
|
12
|
18
|
|
|
18
|
1
|
28
|
sub crumble ($input) { |
|
|
18
|
|
|
|
|
59
|
|
|
|
18
|
|
|
|
|
28
|
|
|
13
|
18
|
50
|
|
|
|
42
|
return unless defined $input; |
|
14
|
18
|
100
|
|
|
|
59
|
return $input if ref($input); |
|
15
|
|
|
|
|
|
|
|
|
16
|
7
|
|
|
|
|
51
|
$input =~ s{\A\s+|\s+\z}{}gmxs; |
|
17
|
7
|
50
|
|
|
|
47
|
return [] unless length $input; |
|
18
|
|
|
|
|
|
|
|
|
19
|
7
|
|
|
|
|
35
|
my $sq = qr{(?mxs: ' [^']* ' )}mxs; |
|
20
|
7
|
|
|
|
|
21
|
my $dq = qr{(?mxs: " (?:[^\\"] | \\.)* " )}mxs; |
|
21
|
7
|
|
|
|
|
23
|
my $ud = qr{(?mxs: \w+ )}mxs; |
|
22
|
7
|
|
|
|
|
369
|
my $chunk = qr{(?mxs: $sq | $dq | $ud)+}mxs; |
|
23
|
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
# save and reset current pos() on $input |
|
25
|
7
|
|
|
|
|
72
|
my $prepos = pos($input); |
|
26
|
7
|
|
|
|
|
26
|
pos($input) = undef; |
|
27
|
|
|
|
|
|
|
|
|
28
|
7
|
|
|
|
|
49
|
my @path; |
|
29
|
7
|
|
|
|
|
369
|
push @path, $1 while $input =~ m{\G [.]? ($chunk) }cgmxs; |
|
30
|
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
# save and restore pos() on $input - FIXME do we really need this?!? |
|
32
|
7
|
|
|
|
|
22
|
my $postpos = pos($input); |
|
33
|
7
|
|
|
|
|
50
|
pos($input) = $prepos; |
|
34
|
|
|
|
|
|
|
|
|
35
|
7
|
50
|
|
|
|
23
|
return unless defined $postpos; |
|
36
|
7
|
50
|
|
|
|
22
|
return if ($postpos != length($input)); |
|
37
|
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
# cleanup @path components |
|
39
|
7
|
|
|
|
|
19
|
for my $part (@path) { |
|
40
|
14
|
|
|
|
|
24
|
my @subparts; |
|
41
|
14
|
|
100
|
|
|
63
|
while ((pos($part) || 0) < length($part)) { |
|
42
|
14
|
50
|
|
|
|
377
|
if ($part =~ m{\G ($sq) }cgmxs) { |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
43
|
0
|
|
|
|
|
0
|
push @subparts, substr $1, 1, length($1) - 2; |
|
44
|
|
|
|
|
|
|
} |
|
45
|
|
|
|
|
|
|
elsif ($part =~ m{\G ($dq) }cgmxs) { |
|
46
|
0
|
|
|
|
|
0
|
my $subpart = substr $1, 1, length($1) - 2; |
|
47
|
0
|
|
|
|
|
0
|
$subpart =~ s{\\(.)}{$1}gmxs; |
|
48
|
0
|
|
|
|
|
0
|
push @subparts, $subpart; |
|
49
|
|
|
|
|
|
|
} |
|
50
|
|
|
|
|
|
|
elsif ($part =~ m{\G ($ud) }cgmxs) { |
|
51
|
14
|
|
|
|
|
71
|
push @subparts, $1; |
|
52
|
|
|
|
|
|
|
} |
|
53
|
|
|
|
|
|
|
else { # shouldn't happen ever |
|
54
|
0
|
|
|
|
|
0
|
return; |
|
55
|
|
|
|
|
|
|
} |
|
56
|
|
|
|
|
|
|
} ## end while ((pos($part) || 0) ...) |
|
57
|
14
|
|
|
|
|
47
|
$part = join '', @subparts; |
|
58
|
|
|
|
|
|
|
} ## end for my $part (@path) |
|
59
|
|
|
|
|
|
|
|
|
60
|
7
|
|
|
|
|
61
|
return \@path; |
|
61
|
|
|
|
|
|
|
} ## end sub crumble |
|
62
|
|
|
|
|
|
|
|
|
63
|
11
|
|
|
11
|
1
|
19
|
sub kpath ($input) { |
|
|
11
|
|
|
|
|
19
|
|
|
|
11
|
|
|
|
|
19
|
|
|
64
|
11
|
50
|
|
|
|
27
|
return unless defined $input; |
|
65
|
11
|
50
|
|
|
|
28
|
$input = crumble($input) unless ref($input); |
|
66
|
|
|
|
|
|
|
return join '.', |
|
67
|
11
|
|
|
|
|
45
|
map { s{([.%])}{sprintf('%%%02x', ord($1))}regmxs } $input->@*; |
|
|
11
|
|
|
|
|
79
|
|
|
|
0
|
|
|
|
|
0
|
|
|
68
|
|
|
|
|
|
|
} |
|
69
|
|
|
|
|
|
|
|
|
70
|
11
|
50
|
|
11
|
1
|
28
|
sub means_missing ($x) { ref($x) && refaddr($x) == refaddr(MISSING) } |
|
|
11
|
|
|
|
|
20
|
|
|
|
11
|
|
|
|
|
15
|
|
|
|
11
|
|
|
|
|
46
|
|
|
71
|
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
# The following function is long and complex because it deals with many |
|
73
|
|
|
|
|
|
|
# different cases. It is kept as-is to avoid too many calls to other |
|
74
|
|
|
|
|
|
|
# subroutines; for this reason, it's reasonably commented. |
|
75
|
11
|
|
|
11
|
1
|
19
|
sub traverse_plain ($node, $crumbs, %opts) { |
|
|
11
|
|
|
|
|
20
|
|
|
|
11
|
|
|
|
|
17
|
|
|
|
11
|
|
|
|
|
43
|
|
|
|
11
|
|
|
|
|
18
|
|
|
76
|
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
# figure out what to do with blessed objects, based on configuration |
|
78
|
11
|
|
50
|
|
|
30
|
my $traverse_methods = $opts{traverse_methods} || 0; |
|
79
|
11
|
|
|
|
|
25
|
my ($strict_blessed, $method_pre) = (0, 0); |
|
80
|
11
|
50
|
|
|
|
27
|
if ($traverse_methods) { |
|
81
|
11
|
|
50
|
|
|
47
|
$strict_blessed = $opts{strict_blessed} || 0; |
|
82
|
11
|
|
50
|
|
|
51
|
$method_pre = ((! $strict_blessed) && $opts{method_over_key}) || 0; |
|
83
|
|
|
|
|
|
|
} |
|
84
|
|
|
|
|
|
|
|
|
85
|
11
|
|
|
|
|
31
|
for my $key ($crumbs->@*) { |
|
86
|
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
# $ref tells me how to look down into $$ref_to_child, i.e. as |
|
88
|
|
|
|
|
|
|
# an ARRAY or a HASH or a CODE or an object. |
|
89
|
11
|
|
|
|
|
25
|
my $ref = reftype($node); |
|
90
|
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
# if $ref is not true, we hit a wall and cannot go past |
|
92
|
11
|
50
|
|
|
|
27
|
return MISSING unless $ref; |
|
93
|
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
# set up for the tests |
|
95
|
11
|
|
|
|
|
16
|
my $is_blessed = blessed($node); |
|
96
|
11
|
|
33
|
|
|
30
|
my $method = $is_blessed && $traverse_methods && $node->can($key); |
|
97
|
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
# DWIM dispatch table |
|
99
|
11
|
50
|
33
|
|
|
90
|
if ($is_blessed && $strict_blessed) { |
|
|
|
50
|
33
|
|
|
|
|
|
|
|
50
|
0
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
100
|
0
|
0
|
|
|
|
0
|
return MISSING unless $method; |
|
101
|
0
|
0
|
|
|
|
0
|
($node) = $node->$method or return MISSING; |
|
102
|
|
|
|
|
|
|
} |
|
103
|
|
|
|
|
|
|
elsif ($method && $method_pre) { |
|
104
|
0
|
0
|
|
|
|
0
|
($node) = $node->$method or return MISSING; |
|
105
|
|
|
|
|
|
|
} |
|
106
|
|
|
|
|
|
|
elsif ($ref eq 'CODE') { |
|
107
|
0
|
0
|
|
|
|
0
|
($node) = $node->($key) or return MISSING; |
|
108
|
|
|
|
|
|
|
} |
|
109
|
|
|
|
|
|
|
elsif ($ref eq 'HASH') { |
|
110
|
11
|
50
|
|
|
|
48
|
return MISSING unless exists($node->{$key}); |
|
111
|
11
|
|
|
|
|
36
|
$node = $node->{$key}; |
|
112
|
|
|
|
|
|
|
} |
|
113
|
|
|
|
|
|
|
elsif ($ref eq 'ARRAY') { |
|
114
|
0
|
0
|
0
|
|
|
0
|
return MISSING |
|
115
|
|
|
|
|
|
|
if $key !~ m{\A (?: 0 | [1-9] \d*) \z}mxs || $key > $node->$#*; |
|
116
|
0
|
|
|
|
|
0
|
$node = $node->[$key]; |
|
117
|
|
|
|
|
|
|
} |
|
118
|
|
|
|
|
|
|
elsif ($method && $traverse_methods) { |
|
119
|
0
|
0
|
|
|
|
0
|
($node) = $node->$method or return MISSING; |
|
120
|
|
|
|
|
|
|
} |
|
121
|
|
|
|
|
|
|
else { |
|
122
|
0
|
|
|
|
|
0
|
return MISSING; |
|
123
|
|
|
|
|
|
|
} |
|
124
|
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
} ## end for my $crumb (@$crumbs) |
|
126
|
|
|
|
|
|
|
|
|
127
|
11
|
|
|
|
|
39
|
return $node; |
|
128
|
|
|
|
|
|
|
} ## end sub traverse |
|
129
|
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
1; |