| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package Struct::Path::PerlStyle; |
|
2
|
|
|
|
|
|
|
|
|
3
|
8
|
|
|
8
|
|
272654
|
use 5.010; |
|
|
8
|
|
|
|
|
79
|
|
|
4
|
8
|
|
|
8
|
|
44
|
use strict; |
|
|
8
|
|
|
|
|
17
|
|
|
|
8
|
|
|
|
|
218
|
|
|
5
|
8
|
|
|
8
|
|
50
|
use warnings FATAL => 'all'; |
|
|
8
|
|
|
|
|
17
|
|
|
|
8
|
|
|
|
|
370
|
|
|
6
|
8
|
|
|
8
|
|
3772
|
use parent 'Exporter'; |
|
|
8
|
|
|
|
|
2248
|
|
|
|
8
|
|
|
|
|
40
|
|
|
7
|
8
|
|
|
8
|
|
4470
|
use utf8; |
|
|
8
|
|
|
|
|
134
|
|
|
|
8
|
|
|
|
|
43
|
|
|
8
|
|
|
|
|
|
|
|
|
9
|
8
|
|
|
8
|
|
252
|
use Carp 'croak'; |
|
|
8
|
|
|
|
|
20
|
|
|
|
8
|
|
|
|
|
364
|
|
|
10
|
8
|
|
|
8
|
|
4145
|
use Safe; |
|
|
8
|
|
|
|
|
292833
|
|
|
|
8
|
|
|
|
|
521
|
|
|
11
|
8
|
|
|
8
|
|
5485
|
use Text::Balanced qw(extract_bracketed extract_quotelike); |
|
|
8
|
|
|
|
|
136926
|
|
|
|
8
|
|
|
|
|
751
|
|
|
12
|
8
|
|
|
8
|
|
66
|
use re qw(is_regexp regexp_pattern); |
|
|
8
|
|
|
|
|
17
|
|
|
|
8
|
|
|
|
|
3279
|
|
|
13
|
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
require Struct::Path::PerlStyle::Functions; |
|
15
|
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
our @EXPORT_OK = qw( |
|
17
|
|
|
|
|
|
|
path2str |
|
18
|
|
|
|
|
|
|
str2path |
|
19
|
|
|
|
|
|
|
); |
|
20
|
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
=encoding utf8 |
|
22
|
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
=head1 NAME |
|
24
|
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
Struct::Path::PerlStyle - Perl-style syntax frontend for L. |
|
26
|
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
=begin html |
|
28
|
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
=end html |
|
34
|
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
=head1 VERSION |
|
36
|
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
Version 0.93 |
|
38
|
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
=cut |
|
40
|
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
our $VERSION = '0.93'; |
|
42
|
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
=head1 SYNOPSIS |
|
44
|
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
use Struct::Path qw(path); |
|
46
|
|
|
|
|
|
|
use Struct::Path::PerlStyle qw(path2str str2path); |
|
47
|
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
my $nested = { |
|
49
|
|
|
|
|
|
|
a => { |
|
50
|
|
|
|
|
|
|
b => ["B0", "B1", "B2"], |
|
51
|
|
|
|
|
|
|
c => ["C0", "C1"], |
|
52
|
|
|
|
|
|
|
d => {}, |
|
53
|
|
|
|
|
|
|
}, |
|
54
|
|
|
|
|
|
|
}; |
|
55
|
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
my @found = path($nested, str2path('{a}{}[0,2]'), deref => 1, paths => 1); |
|
57
|
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
while (@found) { |
|
59
|
|
|
|
|
|
|
my $path = shift @found; |
|
60
|
|
|
|
|
|
|
my $data = shift @found; |
|
61
|
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
print "path '" . path2str($path) . "' refer to '$data'\n"; |
|
63
|
|
|
|
|
|
|
} |
|
64
|
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
# path '{a}{b}[0]' refer to 'B0' |
|
66
|
|
|
|
|
|
|
# path '{a}{b}[2]' refer to 'B2' |
|
67
|
|
|
|
|
|
|
# path '{a}{c}[0]' refer to 'C0' |
|
68
|
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
=head1 EXPORT |
|
70
|
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
Nothing is exported by default. |
|
72
|
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
=head1 PATH SYNTAX |
|
74
|
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
Path is a sequence of 'steps', each represents nested level in the structure. |
|
76
|
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
=head2 Hashes |
|
78
|
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
Like in perl hash keys should be specified using curly brackets |
|
80
|
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
{} # all values from a's subhash |
|
82
|
|
|
|
|
|
|
{foo} # value for 'foo' key |
|
83
|
|
|
|
|
|
|
{foo,bar} # slicing: 'foo' and 'bar' values |
|
84
|
|
|
|
|
|
|
{"space inside"} # key must be quoted unless it is a simple word |
|
85
|
|
|
|
|
|
|
{"multi\nline"} # special characters interpolated when double quoted |
|
86
|
|
|
|
|
|
|
{/pattern/mods} # keys regexp match |
|
87
|
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
=head2 Arrays |
|
89
|
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
Square brackets used for array indexes specification |
|
91
|
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
[] # all array items |
|
93
|
|
|
|
|
|
|
[9] # 9-th element |
|
94
|
|
|
|
|
|
|
[0,1,2,5] # slicing: 0, 1, 2 and 5 array items |
|
95
|
|
|
|
|
|
|
[0..2,5] # same, but using ranges |
|
96
|
|
|
|
|
|
|
[9..0] # descending ranges allowed |
|
97
|
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
=head2 Hooks |
|
99
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
Expressions enclosed in parenthesis treated as hooks and evaluated using |
|
101
|
|
|
|
|
|
|
L compartment. Almost all perl operators and core functions available, |
|
102
|
|
|
|
|
|
|
see L for more info. Some path related functions provided by |
|
103
|
|
|
|
|
|
|
L. |
|
104
|
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
[](/pattern/mods) # match array values by regular expression |
|
106
|
|
|
|
|
|
|
[]{foo}(eq "bar" && BACK) # select hashes which have pair 'foo' => 'bar' |
|
107
|
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
There are two global variables available whithin safe compartment: C<$_> which |
|
109
|
|
|
|
|
|
|
refers to value and C<%_> which provides current path via key C (in |
|
110
|
|
|
|
|
|
|
L notation) and structure levels refs stack via key C. |
|
111
|
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
=head2 Aliases |
|
113
|
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
String in angle brackets is an alias - shortcut mapped into sequence of |
|
115
|
|
|
|
|
|
|
steps. Aliases resolved iteratively, so alias may also refer into path with |
|
116
|
|
|
|
|
|
|
another aliases. |
|
117
|
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
Aliases may be defined via global variable |
|
119
|
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
$Struct::Path::PerlStyle::ALIASES = { |
|
121
|
|
|
|
|
|
|
foo => '{some}{long}{path}', |
|
122
|
|
|
|
|
|
|
bar => '{and}{few}{steps}{more}' |
|
123
|
|
|
|
|
|
|
}; |
|
124
|
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
and then |
|
126
|
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
# expands to '{some}{long}{path}{and}{few}{steps}{more}' |
|
128
|
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
or as option for C: |
|
130
|
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
str2path('', {aliases => {foo => '{long}{path}'}}); |
|
132
|
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
=head1 SUBROUTINES |
|
134
|
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
=cut |
|
136
|
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
our $ALIASES; |
|
138
|
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
my %ESCP = ( |
|
140
|
|
|
|
|
|
|
'"' => '\"', |
|
141
|
|
|
|
|
|
|
"\a" => '\a', |
|
142
|
|
|
|
|
|
|
"\b" => '\b', |
|
143
|
|
|
|
|
|
|
"\t" => '\t', |
|
144
|
|
|
|
|
|
|
"\n" => '\n', |
|
145
|
|
|
|
|
|
|
"\f" => '\f', |
|
146
|
|
|
|
|
|
|
"\r" => '\r', |
|
147
|
|
|
|
|
|
|
"\e" => '\e', |
|
148
|
|
|
|
|
|
|
); |
|
149
|
|
|
|
|
|
|
my $ESCP = join('', sort keys %ESCP); |
|
150
|
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
my %INTP = map { $ESCP{$_} => $_ } keys %ESCP; # swap keys <-> values |
|
152
|
|
|
|
|
|
|
my $INTP = join('|', map { "\Q$_\E" } sort keys %INTP); |
|
153
|
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
# $_ will be substituted (if omitted) as first arg if placed on start of |
|
155
|
|
|
|
|
|
|
# hook expression |
|
156
|
|
|
|
|
|
|
my $COMPL_OPS = join('|', ( |
|
157
|
|
|
|
|
|
|
'==', |
|
158
|
|
|
|
|
|
|
'!=', |
|
159
|
|
|
|
|
|
|
'=~', |
|
160
|
|
|
|
|
|
|
'!~', |
|
161
|
|
|
|
|
|
|
'eq', |
|
162
|
|
|
|
|
|
|
'ne', |
|
163
|
|
|
|
|
|
|
'<', |
|
164
|
|
|
|
|
|
|
'>', |
|
165
|
|
|
|
|
|
|
'<=', |
|
166
|
|
|
|
|
|
|
'>=', |
|
167
|
|
|
|
|
|
|
'lt', |
|
168
|
|
|
|
|
|
|
'gt', |
|
169
|
|
|
|
|
|
|
'le', |
|
170
|
|
|
|
|
|
|
'ge', |
|
171
|
|
|
|
|
|
|
'~~', |
|
172
|
|
|
|
|
|
|
)); |
|
173
|
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
my $HASH_KEY_CHARS = qr/[\p{Alnum}_\.\-\+]/; |
|
175
|
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
our $HOOK_STRICT = 1; |
|
177
|
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
my $SAFE = Safe->new; |
|
179
|
|
|
|
|
|
|
$SAFE->share_from( |
|
180
|
|
|
|
|
|
|
'Struct::Path::PerlStyle::Functions', |
|
181
|
|
|
|
|
|
|
\@Struct::Path::PerlStyle::Functions::EXPORT_OK |
|
182
|
|
|
|
|
|
|
); |
|
183
|
|
|
|
|
|
|
$SAFE->deny('warn'); |
|
184
|
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
my $QR_MAP = { |
|
186
|
|
|
|
|
|
|
'' => sub { qr/$_[0]/ }, |
|
187
|
|
|
|
|
|
|
i => sub { qr/$_[0]/i }, |
|
188
|
|
|
|
|
|
|
m => sub { qr/$_[0]/m }, |
|
189
|
|
|
|
|
|
|
s => sub { qr/$_[0]/s }, |
|
190
|
|
|
|
|
|
|
x => sub { qr/$_[0]/x }, |
|
191
|
|
|
|
|
|
|
im => sub { qr/$_[0]/im }, |
|
192
|
|
|
|
|
|
|
is => sub { qr/$_[0]/is }, |
|
193
|
|
|
|
|
|
|
ix => sub { qr/$_[0]/ix }, |
|
194
|
|
|
|
|
|
|
ms => sub { qr/$_[0]/ms }, |
|
195
|
|
|
|
|
|
|
mx => sub { qr/$_[0]/mx }, |
|
196
|
|
|
|
|
|
|
sx => sub { qr/$_[0]/sx }, |
|
197
|
|
|
|
|
|
|
ims => sub { qr/$_[0]/ims }, |
|
198
|
|
|
|
|
|
|
imx => sub { qr/$_[0]/imx }, |
|
199
|
|
|
|
|
|
|
isx => sub { qr/$_[0]/isx }, |
|
200
|
|
|
|
|
|
|
msx => sub { qr/$_[0]/msx }, |
|
201
|
|
|
|
|
|
|
imsx => sub { qr/$_[0]/imsx }, |
|
202
|
|
|
|
|
|
|
}; |
|
203
|
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
=head2 str2path |
|
205
|
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
Convert perl-style string to L path structure |
|
207
|
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
$struct = str2path($string); |
|
209
|
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
=cut |
|
211
|
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
sub _push_hash { |
|
213
|
75
|
|
|
75
|
|
163
|
my ($steps, $text) = @_; |
|
214
|
75
|
|
|
|
|
126
|
my ($body, $delim, $mods, %step, $token, $type); |
|
215
|
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
# extract_quotelike fails to parse bare zero as a string |
|
217
|
75
|
100
|
|
|
|
163
|
push @{$step{K}}, $text if $text eq '0'; |
|
|
1
|
|
|
|
|
3
|
|
|
218
|
|
|
|
|
|
|
|
|
219
|
75
|
|
|
|
|
154
|
while ($text) { |
|
220
|
142
|
|
|
|
|
379
|
($token, $text, $type, $delim, $body, $mods) = |
|
221
|
|
|
|
|
|
|
(extract_quotelike($text))[0,1,3,4,5,10]; |
|
222
|
|
|
|
|
|
|
|
|
223
|
142
|
100
|
100
|
|
|
10836
|
if (not defined $delim) { # bareword |
|
|
|
100
|
100
|
|
|
|
|
|
|
|
100
|
100
|
|
|
|
|
|
|
|
100
|
100
|
|
|
|
|
|
224
|
57
|
100
|
|
|
|
406
|
push @{$step{K}}, $token = $1 |
|
|
55
|
|
|
|
|
698
|
|
|
225
|
|
|
|
|
|
|
if ($text =~ s/^\s*($HASH_KEY_CHARS+)//); |
|
226
|
|
|
|
|
|
|
} elsif (!$type and $delim eq '"') { |
|
227
|
39
|
|
|
|
|
197
|
$body =~ s/($INTP)/$INTP{$1}/gs; # interpolate |
|
228
|
39
|
|
|
|
|
65
|
push @{$step{K}}, $body; |
|
|
39
|
|
|
|
|
105
|
|
|
229
|
|
|
|
|
|
|
} elsif (!$type and $delim eq "'") { |
|
230
|
11
|
|
|
|
|
15
|
push @{$step{K}}, $body; |
|
|
11
|
|
|
|
|
29
|
|
|
231
|
|
|
|
|
|
|
} elsif ($delim eq '/' and !$type or $type eq 'm') { |
|
232
|
33
|
|
|
|
|
119
|
$mods = join('', sort(split('', $mods))); |
|
233
|
33
|
|
|
|
|
59
|
eval { push @{$step{K}}, $QR_MAP->{$mods}->($body) }; |
|
|
33
|
|
|
|
|
49
|
|
|
|
33
|
|
|
|
|
130
|
|
|
234
|
33
|
100
|
|
|
|
93
|
if ($@) { |
|
235
|
3
|
|
|
|
|
18
|
(my $err = $@) =~ s/ at .+//s; |
|
236
|
3
|
|
|
|
|
8
|
croak "Step #" . scalar @{$steps} . " $err"; |
|
|
3
|
|
|
|
|
335
|
|
|
237
|
|
|
|
|
|
|
} |
|
238
|
|
|
|
|
|
|
} else { # things like qr, qw and so on |
|
239
|
2
|
|
|
|
|
6
|
substr($text, 0, 0, $token); |
|
240
|
2
|
|
|
|
|
5
|
undef $token; |
|
241
|
|
|
|
|
|
|
} |
|
242
|
|
|
|
|
|
|
|
|
243
|
139
|
100
|
|
|
|
708
|
croak "Unsupported key '$text', step #" . @{$steps} |
|
|
4
|
|
|
|
|
545
|
|
|
244
|
|
|
|
|
|
|
if (!defined $token); |
|
245
|
|
|
|
|
|
|
|
|
246
|
135
|
|
|
|
|
271
|
$text =~ s/^\s+//; # discard trailing spaces |
|
247
|
|
|
|
|
|
|
|
|
248
|
135
|
100
|
|
|
|
308
|
if ($text ne '') { |
|
249
|
78
|
100
|
|
|
|
266
|
if ($text =~ s/^,//) { |
|
250
|
71
|
100
|
|
|
|
197
|
croak "Trailing delimiter at step #" . @{$steps} |
|
|
2
|
|
|
|
|
217
|
|
|
251
|
|
|
|
|
|
|
if ($text eq ''); |
|
252
|
|
|
|
|
|
|
} else { |
|
253
|
7
|
|
|
|
|
17
|
croak "Delimiter expected before '$text', step #" . @{$steps}; |
|
|
7
|
|
|
|
|
775
|
|
|
254
|
|
|
|
|
|
|
} |
|
255
|
|
|
|
|
|
|
} |
|
256
|
|
|
|
|
|
|
} |
|
257
|
|
|
|
|
|
|
|
|
258
|
59
|
|
|
|
|
99
|
push @{$steps}, \%step; |
|
|
59
|
|
|
|
|
213
|
|
|
259
|
|
|
|
|
|
|
} |
|
260
|
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
sub _push_hook { |
|
262
|
31
|
|
|
31
|
|
71
|
my ($steps, $text) = @_; |
|
263
|
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
# substitute default value if omitted |
|
265
|
31
|
|
|
|
|
287
|
$text =~ s/^\s*($COMPL_OPS)/\$_ $1/; |
|
266
|
|
|
|
|
|
|
|
|
267
|
31
|
|
|
|
|
105
|
my $hook = 'sub {' . |
|
268
|
|
|
|
|
|
|
'$^W = 0; ' . |
|
269
|
|
|
|
|
|
|
'local %_ = ("path", $_[0], "refs", $_[1]); ' . |
|
270
|
|
|
|
|
|
|
'local $_ = (ref $_[1] eq "ARRAY" and @{$_[1]}) ? ${$_[1]->[-1]} : undef; ' . |
|
271
|
|
|
|
|
|
|
$text . |
|
272
|
|
|
|
|
|
|
'}'; |
|
273
|
|
|
|
|
|
|
|
|
274
|
31
|
|
|
2
|
|
370
|
open (local *STDERR,'>', \(my $stderr)); # catch compilation errors |
|
|
2
|
|
|
|
|
14
|
|
|
|
2
|
|
|
|
|
4
|
|
|
|
2
|
|
|
|
|
17
|
|
|
275
|
|
|
|
|
|
|
|
|
276
|
31
|
100
|
|
|
|
1637
|
unless ($hook = $SAFE->reval($hook, $HOOK_STRICT)) { |
|
277
|
10
|
100
|
|
|
|
6365
|
if ($stderr) { |
|
278
|
1
|
|
|
|
|
7
|
$stderr =~ s/ at \(eval \d+\) .+//s; |
|
279
|
1
|
|
|
|
|
4
|
$stderr = " ($stderr)"; |
|
280
|
|
|
|
|
|
|
} else { |
|
281
|
9
|
|
|
|
|
19
|
$stderr = ""; |
|
282
|
|
|
|
|
|
|
} |
|
283
|
|
|
|
|
|
|
|
|
284
|
10
|
|
|
|
|
31
|
(my $err = $@) =~ s/ at \(eval \d+\) .+//s; |
|
285
|
10
|
|
|
|
|
26
|
croak "Failed to eval hook '$text': $err, step #" . @{$steps} . $stderr; |
|
|
10
|
|
|
|
|
1359
|
|
|
286
|
|
|
|
|
|
|
} |
|
287
|
|
|
|
|
|
|
|
|
288
|
21
|
|
|
|
|
14207
|
push @{$steps}, $hook; |
|
|
21
|
|
|
|
|
195
|
|
|
289
|
|
|
|
|
|
|
} |
|
290
|
|
|
|
|
|
|
|
|
291
|
|
|
|
|
|
|
sub _push_list { |
|
292
|
57
|
|
|
57
|
|
124
|
my ($steps, $text) = @_; |
|
293
|
57
|
|
|
|
|
91
|
my (@range, @step); |
|
294
|
|
|
|
|
|
|
|
|
295
|
57
|
|
|
|
|
215
|
for my $i (split /\s*,\s*/, $text, -1) { |
|
296
|
|
|
|
|
|
|
@range = grep { |
|
297
|
69
|
100
|
|
|
|
211
|
croak "Incorrect array index '$i', step #" . @{$steps} |
|
|
7
|
|
|
|
|
837
|
|
|
298
|
80
|
100
|
|
|
|
145
|
unless (eval { $_ == int($_) }); |
|
|
80
|
|
|
|
|
411
|
|
|
299
|
|
|
|
|
|
|
} ($i =~ /^\s*(-?\d+)\s*\.\.\s*(-?\d+)\s*$/) ? ($1, $2) : $i; |
|
300
|
|
|
|
|
|
|
|
|
301
|
62
|
100
|
|
|
|
231
|
push @step, $range[0] < $range[-1] |
|
302
|
|
|
|
|
|
|
? $range[0] .. $range[-1] |
|
303
|
|
|
|
|
|
|
: reverse $range[-1] .. $range[0]; |
|
304
|
|
|
|
|
|
|
} |
|
305
|
|
|
|
|
|
|
|
|
306
|
50
|
|
|
|
|
90
|
push @{$steps}, \@step; |
|
|
50
|
|
|
|
|
171
|
|
|
307
|
|
|
|
|
|
|
} |
|
308
|
|
|
|
|
|
|
|
|
309
|
|
|
|
|
|
|
sub str2path($;$) { |
|
310
|
116
|
|
|
116
|
1
|
69976
|
my ($path, $opts) = @_; |
|
311
|
|
|
|
|
|
|
|
|
312
|
116
|
100
|
|
|
|
485
|
croak "Undefined path passed" unless (defined $path); |
|
313
|
|
|
|
|
|
|
|
|
314
|
115
|
100
|
|
|
|
312
|
local $ALIASES = $opts->{aliases} if (exists $opts->{aliases}); |
|
315
|
|
|
|
|
|
|
|
|
316
|
115
|
|
|
|
|
201
|
my (@steps, $step, $type); |
|
317
|
|
|
|
|
|
|
|
|
318
|
115
|
|
|
|
|
246
|
while ($path) { |
|
319
|
|
|
|
|
|
|
# separated match: to be able to have another brackets inside; |
|
320
|
|
|
|
|
|
|
# currently mostly for hooks, for example: '( $x > $y )' |
|
321
|
182
|
|
|
|
|
370
|
for ('{"}', '["]', '(")', '<">') { |
|
322
|
358
|
|
|
|
|
950
|
($step, $path) = extract_bracketed($path, $_, ''); |
|
323
|
358
|
100
|
|
|
|
39361
|
last if ($step); |
|
324
|
|
|
|
|
|
|
} |
|
325
|
|
|
|
|
|
|
|
|
326
|
182
|
100
|
|
|
|
1381
|
croak "Unsupported thing in the path, step #" . @steps . ": '$path'" |
|
327
|
|
|
|
|
|
|
unless ($step); |
|
328
|
|
|
|
|
|
|
|
|
329
|
173
|
|
|
|
|
398
|
$type = substr $step, 0, 1, ''; # remove leading bracket |
|
330
|
173
|
|
|
|
|
317
|
substr $step, -1, 1, ''; # remove trailing bracket |
|
331
|
|
|
|
|
|
|
|
|
332
|
173
|
100
|
|
|
|
472
|
if ($type eq '{') { |
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
333
|
75
|
|
|
|
|
196
|
_push_hash(\@steps, $step); |
|
334
|
|
|
|
|
|
|
} elsif ($type eq '[') { |
|
335
|
57
|
|
|
|
|
145
|
_push_list(\@steps, $step); |
|
336
|
|
|
|
|
|
|
} elsif ($type eq '(') { |
|
337
|
31
|
|
|
|
|
80
|
_push_hook(\@steps, $step); |
|
338
|
|
|
|
|
|
|
} else { # <> |
|
339
|
10
|
100
|
|
|
|
315
|
croak "Unknown alias '$step'" unless (exists $ALIASES->{$step}); |
|
340
|
|
|
|
|
|
|
|
|
341
|
8
|
|
|
|
|
18
|
substr $path, 0, 0, $ALIASES->{$step}; |
|
342
|
8
|
|
|
|
|
11
|
redo; |
|
343
|
|
|
|
|
|
|
} |
|
344
|
|
|
|
|
|
|
} |
|
345
|
|
|
|
|
|
|
|
|
346
|
71
|
|
|
|
|
399
|
return \@steps; |
|
347
|
|
|
|
|
|
|
} |
|
348
|
|
|
|
|
|
|
|
|
349
|
|
|
|
|
|
|
=head2 path2str |
|
350
|
|
|
|
|
|
|
|
|
351
|
|
|
|
|
|
|
Convert L path structure to perl-style string |
|
352
|
|
|
|
|
|
|
|
|
353
|
|
|
|
|
|
|
$string = path2str($struct); |
|
354
|
|
|
|
|
|
|
|
|
355
|
|
|
|
|
|
|
=cut |
|
356
|
|
|
|
|
|
|
|
|
357
|
|
|
|
|
|
|
sub path2str($) { |
|
358
|
47
|
|
|
47
|
1
|
35393
|
my $path = shift; |
|
359
|
|
|
|
|
|
|
|
|
360
|
47
|
100
|
|
|
|
266
|
croak "Arrayref expected for path" unless (ref $path eq 'ARRAY'); |
|
361
|
46
|
|
|
|
|
79
|
my $out = ''; |
|
362
|
46
|
|
|
|
|
85
|
my $sc = 0; # step counter |
|
363
|
|
|
|
|
|
|
|
|
364
|
46
|
|
|
|
|
69
|
for my $step (@{$path}) { |
|
|
46
|
|
|
|
|
115
|
|
|
365
|
67
|
|
|
|
|
103
|
my @items; |
|
366
|
|
|
|
|
|
|
|
|
367
|
67
|
100
|
|
|
|
183
|
if (ref $step eq 'ARRAY') { |
|
|
|
100
|
|
|
|
|
|
|
368
|
27
|
|
|
|
|
41
|
for my $i (@{$step}) { |
|
|
27
|
|
|
|
|
74
|
|
|
369
|
|
|
|
|
|
|
croak "Incorrect array index '" . ($i // 'undef') . "', step #$sc" |
|
370
|
65
|
100
|
100
|
|
|
95
|
unless (eval { int($i) == $i }); |
|
|
65
|
|
|
|
|
480
|
|
|
371
|
62
|
100
|
100
|
|
|
279
|
if (@items and ( |
|
|
|
|
100
|
|
|
|
|
|
372
|
|
|
|
|
|
|
$items[-1][0] < $i and $items[-1][-1] == $i - 1 or # ascending |
|
373
|
|
|
|
|
|
|
$items[-1][0] > $i and $items[-1][-1] == $i + 1 # descending |
|
374
|
|
|
|
|
|
|
)) { |
|
375
|
30
|
|
|
|
|
59
|
$items[-1][1] = $i; # update range |
|
376
|
|
|
|
|
|
|
} else { |
|
377
|
32
|
|
|
|
|
66
|
push @items, [$i]; # new range |
|
378
|
|
|
|
|
|
|
} |
|
379
|
|
|
|
|
|
|
} |
|
380
|
|
|
|
|
|
|
|
|
381
|
24
|
|
|
|
|
44
|
for (@{items}) { |
|
382
|
|
|
|
|
|
|
$_ = abs($_->[0] - $_->[-1]) < 2 |
|
383
|
32
|
100
|
|
|
|
85
|
? join(',', @{$_}) |
|
|
21
|
|
|
|
|
60
|
|
|
384
|
|
|
|
|
|
|
: "$_->[0]..$_->[-1]" |
|
385
|
|
|
|
|
|
|
} |
|
386
|
|
|
|
|
|
|
|
|
387
|
24
|
|
|
|
|
58
|
$out .= "[" . join(",", @{items}) . "]"; |
|
388
|
|
|
|
|
|
|
} elsif (ref $step eq 'HASH') { |
|
389
|
39
|
|
|
|
|
53
|
my $keys; |
|
390
|
|
|
|
|
|
|
|
|
391
|
39
|
100
|
|
|
|
82
|
if (exists $step->{K}) { |
|
|
|
100
|
|
|
|
|
|
|
392
|
|
|
|
|
|
|
croak "Unsupported hash keys definition, step #$sc" |
|
393
|
36
|
100
|
|
|
|
193
|
unless (ref $step->{K} eq 'ARRAY'); |
|
394
|
|
|
|
|
|
|
croak "Unsupported hash definition (extra keys), step #$sc" |
|
395
|
35
|
100
|
|
|
|
52
|
if (keys %{$step} > 1); |
|
|
35
|
|
|
|
|
189
|
|
|
396
|
34
|
|
|
|
|
59
|
$keys = $step->{K}; |
|
397
|
3
|
|
|
|
|
11
|
} elsif (keys %{$step}) { |
|
398
|
1
|
|
|
|
|
189
|
croak "Unsupported hash definition (unknown keys), step #$sc"; |
|
399
|
|
|
|
|
|
|
} else { |
|
400
|
2
|
|
|
|
|
4
|
$keys = []; |
|
401
|
|
|
|
|
|
|
} |
|
402
|
|
|
|
|
|
|
|
|
403
|
36
|
|
|
|
|
59
|
for my $k (@{$keys}) { |
|
|
36
|
|
|
|
|
64
|
|
|
404
|
84
|
100
|
100
|
|
|
734
|
if (is_regexp($k)) { |
|
|
|
100
|
|
|
|
|
|
|
405
|
15
|
|
|
|
|
50
|
my ($patt, $mods) = regexp_pattern($k); |
|
406
|
15
|
|
|
|
|
35
|
$mods =~ s/[dlu]//g; # for Perl's internal use (c) perlre |
|
407
|
15
|
|
|
|
|
46
|
push @items, "/$patt/$mods"; |
|
408
|
|
|
|
|
|
|
|
|
409
|
|
|
|
|
|
|
} elsif (defined $k and ref $k eq '') { |
|
410
|
67
|
|
|
|
|
111
|
push @items, $k; |
|
411
|
|
|
|
|
|
|
|
|
412
|
67
|
100
|
|
|
|
400
|
unless ($k =~ /^$HASH_KEY_CHARS+$/) { |
|
413
|
33
|
|
|
|
|
141
|
$items[-1] =~ s/([\Q$ESCP\E])/$ESCP{$1}/gs; # escape |
|
414
|
33
|
|
|
|
|
92
|
$items[-1] = qq("$items[-1]"); # quote |
|
415
|
|
|
|
|
|
|
} |
|
416
|
|
|
|
|
|
|
} else { |
|
417
|
2
|
|
100
|
|
|
210
|
croak "Unsupported hash key type '" . |
|
418
|
|
|
|
|
|
|
(ref($k) || 'undef') . "', step #$sc" |
|
419
|
|
|
|
|
|
|
} |
|
420
|
|
|
|
|
|
|
} |
|
421
|
|
|
|
|
|
|
|
|
422
|
34
|
|
|
|
|
126
|
$out .= "{" . join(",", @items) . "}"; |
|
423
|
|
|
|
|
|
|
} else { |
|
424
|
1
|
|
|
|
|
101
|
croak "Unsupported thing in the path, step #$sc"; |
|
425
|
|
|
|
|
|
|
} |
|
426
|
58
|
|
|
|
|
123
|
$sc++; |
|
427
|
|
|
|
|
|
|
} |
|
428
|
|
|
|
|
|
|
|
|
429
|
37
|
|
|
|
|
101
|
return $out; |
|
430
|
|
|
|
|
|
|
} |
|
431
|
|
|
|
|
|
|
|
|
432
|
|
|
|
|
|
|
=head1 AUTHOR |
|
433
|
|
|
|
|
|
|
|
|
434
|
|
|
|
|
|
|
Michael Samoglyadov, C<< >> |
|
435
|
|
|
|
|
|
|
|
|
436
|
|
|
|
|
|
|
=head1 BUGS |
|
437
|
|
|
|
|
|
|
|
|
438
|
|
|
|
|
|
|
Please report any bugs or feature requests to |
|
439
|
|
|
|
|
|
|
C, or through the web interface at |
|
440
|
|
|
|
|
|
|
L. I |
|
441
|
|
|
|
|
|
|
will be notified, and then you'll automatically be notified of progress on |
|
442
|
|
|
|
|
|
|
your bug as I make changes. |
|
443
|
|
|
|
|
|
|
|
|
444
|
|
|
|
|
|
|
=head1 SUPPORT |
|
445
|
|
|
|
|
|
|
|
|
446
|
|
|
|
|
|
|
You can find documentation for this module with the perldoc command. |
|
447
|
|
|
|
|
|
|
|
|
448
|
|
|
|
|
|
|
perldoc Struct::Path::PerlStyle |
|
449
|
|
|
|
|
|
|
|
|
450
|
|
|
|
|
|
|
You can also look for information at: |
|
451
|
|
|
|
|
|
|
|
|
452
|
|
|
|
|
|
|
=over 4 |
|
453
|
|
|
|
|
|
|
|
|
454
|
|
|
|
|
|
|
=item * RT: CPAN's request tracker (report bugs here) |
|
455
|
|
|
|
|
|
|
|
|
456
|
|
|
|
|
|
|
L |
|
457
|
|
|
|
|
|
|
|
|
458
|
|
|
|
|
|
|
=item * AnnoCPAN: Annotated CPAN documentation |
|
459
|
|
|
|
|
|
|
|
|
460
|
|
|
|
|
|
|
L |
|
461
|
|
|
|
|
|
|
|
|
462
|
|
|
|
|
|
|
=item * CPAN Ratings |
|
463
|
|
|
|
|
|
|
|
|
464
|
|
|
|
|
|
|
L |
|
465
|
|
|
|
|
|
|
|
|
466
|
|
|
|
|
|
|
=item * Search CPAN |
|
467
|
|
|
|
|
|
|
|
|
468
|
|
|
|
|
|
|
L |
|
469
|
|
|
|
|
|
|
|
|
470
|
|
|
|
|
|
|
=back |
|
471
|
|
|
|
|
|
|
|
|
472
|
|
|
|
|
|
|
=head1 SEE ALSO |
|
473
|
|
|
|
|
|
|
|
|
474
|
|
|
|
|
|
|
L, L, L |
|
475
|
|
|
|
|
|
|
L, L, L |
|
476
|
|
|
|
|
|
|
|
|
477
|
|
|
|
|
|
|
=head1 LICENSE AND COPYRIGHT |
|
478
|
|
|
|
|
|
|
|
|
479
|
|
|
|
|
|
|
Copyright 2016-2019 Michael Samoglyadov. |
|
480
|
|
|
|
|
|
|
|
|
481
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify it |
|
482
|
|
|
|
|
|
|
under the terms of either: the GNU General Public License as published |
|
483
|
|
|
|
|
|
|
by the Free Software Foundation; or the Artistic License. |
|
484
|
|
|
|
|
|
|
|
|
485
|
|
|
|
|
|
|
See L for more information. |
|
486
|
|
|
|
|
|
|
|
|
487
|
|
|
|
|
|
|
=cut |
|
488
|
|
|
|
|
|
|
|
|
489
|
|
|
|
|
|
|
1; # End of Struct::Path::PerlStyle |