| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
#!perl |
|
2
|
|
|
|
|
|
|
package Config::Perl; |
|
3
|
6
|
|
|
6
|
|
108927
|
use warnings; |
|
|
6
|
|
|
|
|
8
|
|
|
|
6
|
|
|
|
|
171
|
|
|
4
|
6
|
|
|
6
|
|
20
|
use strict; |
|
|
6
|
|
|
|
|
8
|
|
|
|
6
|
|
|
|
|
372
|
|
|
5
|
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
our $VERSION = '0.06'; |
|
7
|
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
=head1 Name |
|
9
|
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
Config::Perl - Perl extension for parsing configuration files written in a |
|
11
|
|
|
|
|
|
|
subset of Perl and (limited) undumping of data structures (via PPI, not eval) |
|
12
|
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
=head1 Synopsis |
|
14
|
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
=for comment |
|
16
|
|
|
|
|
|
|
Remember to test this by copy/pasting to/from 91_author_pod.t |
|
17
|
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
=for comment |
|
19
|
|
|
|
|
|
|
TODO Later: metacpan strips the extra space from the front of the code sample, |
|
20
|
|
|
|
|
|
|
so the extra space we added in ' END_CONFIG_FILE' breaks the script... |
|
21
|
|
|
|
|
|
|
search.cpan.org keeps the space there. What's the best solution? |
|
22
|
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
use Config::Perl; |
|
24
|
|
|
|
|
|
|
my $parser = Config::Perl->new; |
|
25
|
|
|
|
|
|
|
my $data = $parser->parse_or_die( \<<' END_CONFIG_FILE' ); |
|
26
|
|
|
|
|
|
|
# This is the example configuration file |
|
27
|
|
|
|
|
|
|
$foo = "bar"; |
|
28
|
|
|
|
|
|
|
%text = ( test => ["Hello", "World!"] ); |
|
29
|
|
|
|
|
|
|
@vals = qw/ x y a /; |
|
30
|
|
|
|
|
|
|
END_CONFIG_FILE |
|
31
|
|
|
|
|
|
|
print $data->{'$foo'}, "\n"; # prints "bar\n" |
|
32
|
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
# Resulting $data: { |
|
34
|
|
|
|
|
|
|
# '$foo' => "bar", |
|
35
|
|
|
|
|
|
|
# '%text' => { test => ["Hello", "World!"] }, |
|
36
|
|
|
|
|
|
|
# '@vals' => ["x", "y", "a"], |
|
37
|
|
|
|
|
|
|
# }; |
|
38
|
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
=head1 Description |
|
40
|
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
The goal of this module is to support the parsing of a small subset of Perl, |
|
42
|
|
|
|
|
|
|
primarily in order to parse configuration files written in that subset of Perl. |
|
43
|
|
|
|
|
|
|
As a side effect, this module can "undump" some data structures written by |
|
44
|
|
|
|
|
|
|
L, but |
|
45
|
|
|
|
|
|
|
please make sure to read L for details! |
|
46
|
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
The code is parsed via L, eliminating the need for Perl's C. |
|
48
|
|
|
|
|
|
|
This should provide a higher level of safety* compared to C |
|
49
|
|
|
|
|
|
|
(even when making use of a module like L). |
|
50
|
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
* B A "higher level of safety" does not mean "perfect safety". |
|
52
|
|
|
|
|
|
|
This software is distributed B; without even the implied |
|
53
|
|
|
|
|
|
|
warranty of B or B. |
|
54
|
|
|
|
|
|
|
See also the license for this software. |
|
55
|
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
This module attempts to provide 100% compatibility with Perl over the subset of Perl it supports. |
|
57
|
|
|
|
|
|
|
When a Perl feature is not supported by this module, it should complain |
|
58
|
|
|
|
|
|
|
that the feature is not supported, instead of silently giving a wrong result. |
|
59
|
|
|
|
|
|
|
If the output of a parse is different from how Perl would evaluate the same string, |
|
60
|
|
|
|
|
|
|
then that is a bug in this module that should be fixed by correcting the output |
|
61
|
|
|
|
|
|
|
or adding an error message that the particular feature is unsupported. |
|
62
|
|
|
|
|
|
|
However, the result of using this module to parse something that is not valid Perl is undefined; |
|
63
|
|
|
|
|
|
|
it may cause an error, or may fail in some other silent way. |
|
64
|
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
This document describes version 0.06 of the module. |
|
66
|
|
|
|
|
|
|
Although this module has a fair number of tests, it still lacks some |
|
67
|
|
|
|
|
|
|
features (see list below) and there may be bugs lurking. |
|
68
|
|
|
|
|
|
|
Contributions are welcome! |
|
69
|
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
=head2 Interface |
|
71
|
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
This module has a simple OO interface. A new parser is created |
|
73
|
|
|
|
|
|
|
with C<< Config::Perl->new >> |
|
74
|
|
|
|
|
|
|
and documents are parsed with either the method C or C. |
|
75
|
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
my $parser = Config::Perl->new; |
|
77
|
|
|
|
|
|
|
my $out1 = $parser->parse_or_undef(\' $foo = "bar"; '); |
|
78
|
|
|
|
|
|
|
warn "parse failed: ".$parser->errstr unless defined $out1; |
|
79
|
|
|
|
|
|
|
my $out2 = $parser->parse_or_die('filename.pl'); |
|
80
|
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
The arguments and return values of these two methods are (almost) the same: |
|
82
|
|
|
|
|
|
|
They each take exactly one argument, which is either a filename, |
|
83
|
|
|
|
|
|
|
or a reference to a string containing the code to be parsed |
|
84
|
|
|
|
|
|
|
(this is the same as L's C method). |
|
85
|
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
The methods differ in that, as the names imply, C |
|
87
|
|
|
|
|
|
|
will C on errors, while C will return C; |
|
88
|
|
|
|
|
|
|
the error message is then accessible via the C method. |
|
89
|
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
For a successful parse, the return value of each function is a hashref |
|
91
|
|
|
|
|
|
|
representing the "symbol table" of the parsed document. |
|
92
|
|
|
|
|
|
|
This "symbol table" hash is similar to, but not the same as, Perl's symbol table. |
|
93
|
|
|
|
|
|
|
The hash includes a key for every variable declared or assigned to in the document, |
|
94
|
|
|
|
|
|
|
the key is the name of the variable including its sigil. |
|
95
|
|
|
|
|
|
|
If the document ends with a plain value or list that is not part of an assignment, |
|
96
|
|
|
|
|
|
|
that value is saved in the "symbol table" hash with the key "C<_>" (a single underscore). |
|
97
|
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
For example, the string C<"$foo=123; $bar=456;"> will return the data structure |
|
99
|
|
|
|
|
|
|
C<< { '$foo'=>123, '$bar'=>456 } >>, and the string C<"('foo','bar')"> will return the data |
|
100
|
|
|
|
|
|
|
structure C<< { _=>["foo","bar"] } >>. |
|
101
|
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
Note that documents are currently always parsed in list context. |
|
103
|
|
|
|
|
|
|
For example, this means that a document like "C<@foo = ("a","b","c"); @foo>" |
|
104
|
|
|
|
|
|
|
will return the array's elements (C<"a","b","c">) instead of the item count (C<3>). |
|
105
|
|
|
|
|
|
|
This also means that the special hash element "C<_>" will currently always be an arrayref. |
|
106
|
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
C<< Config::Perl->new(debug=>1) >> turns on debugging. |
|
108
|
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
=head2 What is currently supported |
|
110
|
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
=over |
|
112
|
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
=item * |
|
114
|
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
plain scalars, arrays, hashes, lists |
|
116
|
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
=item * |
|
118
|
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
arrayrefs and hashrefs constructed via C<[]> and C<{}> resp. |
|
120
|
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
=item * |
|
122
|
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
declarations - only C, also C on the outermost level (document) |
|
124
|
|
|
|
|
|
|
where it is currently treated exactly like C; |
|
125
|
|
|
|
|
|
|
not supported are lexical C inside blocks, C or C |
|
126
|
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
=item * |
|
128
|
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
assignments (except the return value of assignments is not yet implemented) |
|
130
|
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
=item * |
|
132
|
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
simple array and hash subscripts (e.g. C<$x[1]>, C<$x[$y]>, C<$x{z}>, C<$x{"$y"}>) |
|
134
|
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
=item * |
|
136
|
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
very simple variable interpolations in strings (currently only C<"hello$world"> or C<"foo${bar}quz">) |
|
138
|
|
|
|
|
|
|
and some escape sequences (e.g. C<"\x00">) |
|
139
|
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
=item * |
|
141
|
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
C blocks (contents limited to the supported features listed here) |
|
143
|
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
=item * |
|
145
|
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
dereferencing via the arrow operator (also implicit arrow operator between subscripts) |
|
147
|
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
=back |
|
149
|
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
=head2 What is not supported (yet) |
|
151
|
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
I hope to achieve a balance where this module is useful, without becoming too much of a re-implementation of Perl. |
|
153
|
|
|
|
|
|
|
I've labeled these items with "wishlist", "maybe", and "no", depending on whether I currently feel that |
|
154
|
|
|
|
|
|
|
I'd like to support this feature in a later version, I'd consider supporting this feature if the need arises, |
|
155
|
|
|
|
|
|
|
or I currently don't think the feature should be implemented. |
|
156
|
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
=over |
|
158
|
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
=item * |
|
160
|
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
lexical variables (C) (wishlist) |
|
162
|
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
=item * |
|
164
|
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
taking references via C<\> and dereferencing via C<@{...}>, C<%{...}>, etc. (wishlist) |
|
166
|
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
=item * |
|
168
|
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
return values of assignments (e.g. C<$foo = do { $bar = "quz" }>) (maybe) |
|
170
|
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
=item * |
|
172
|
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
operators other than assignment (maybe; supporting a subset, like concatenation, is wishlist) |
|
174
|
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
=item * |
|
176
|
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
conditionals, like for example a very simple C (maybe) |
|
178
|
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
=item * |
|
180
|
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
any functions, including C |
|
182
|
|
|
|
|
|
|
(mostly this is "no"; supporting a very small subset of functions, e.g. C, is "maybe") |
|
183
|
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
=item * |
|
185
|
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
anything that can't be resolved via a static parse (including Cs, many regexps, etc.) (no) |
|
187
|
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
=item * |
|
189
|
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
Note this list is not complete. |
|
191
|
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
=back |
|
193
|
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
=head1 Author, Copyright, and License |
|
195
|
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
Copyright (c) 2015 Hauke Daempfling (haukex@zero-g.net). |
|
197
|
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
This library is free software; you can redistribute it and/or modify |
|
199
|
|
|
|
|
|
|
it under the same terms as Perl 5 itself. |
|
200
|
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
For more information see the L, |
|
202
|
|
|
|
|
|
|
which should have been distributed with your copy of Perl. |
|
203
|
|
|
|
|
|
|
Try the command "C" or see |
|
204
|
|
|
|
|
|
|
L. |
|
205
|
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
=cut |
|
207
|
|
|
|
|
|
|
|
|
208
|
6
|
|
|
6
|
|
21
|
use Carp; |
|
|
6
|
|
|
|
|
10
|
|
|
|
6
|
|
|
|
|
287
|
|
|
209
|
6
|
|
|
6
|
|
28
|
use warnings::register; |
|
|
6
|
|
|
|
|
7
|
|
|
|
6
|
|
|
|
|
665
|
|
|
210
|
6
|
|
|
6
|
|
3163
|
use PPI (); |
|
|
6
|
|
|
|
|
572995
|
|
|
|
6
|
|
|
|
|
163
|
|
|
211
|
6
|
|
|
6
|
|
2374
|
use PPI::Dumper (); |
|
|
6
|
|
|
|
|
4426
|
|
|
|
6
|
|
|
|
|
21558
|
|
|
212
|
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
our $DEBUG = 0; # global debug setting |
|
214
|
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
my %KNOWN_ARGS_NEW = map {$_=>1} qw/ debug /; |
|
216
|
|
|
|
|
|
|
sub new { |
|
217
|
168
|
|
|
168
|
0
|
148171
|
my ($class,%args) = @_; |
|
218
|
168
|
|
66
|
|
|
734
|
$KNOWN_ARGS_NEW{$_} or croak "unknown argument $_" for keys %args; |
|
219
|
|
|
|
|
|
|
my $self = { |
|
220
|
167
|
|
33
|
|
|
1013
|
debug => $args{debug} || $DEBUG, |
|
221
|
|
|
|
|
|
|
errstr => undef, |
|
222
|
|
|
|
|
|
|
ctx => undef, # Note: valid values for ctx currently "list", "scalar", "scalar-void" |
|
223
|
|
|
|
|
|
|
out => undef, |
|
224
|
|
|
|
|
|
|
ptr => undef, |
|
225
|
|
|
|
|
|
|
}; |
|
226
|
167
|
|
|
|
|
486
|
return bless $self, $class; |
|
227
|
|
|
|
|
|
|
} |
|
228
|
2
|
|
|
2
|
0
|
12
|
sub errstr { return shift->{errstr} } |
|
229
|
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
#TODO: make error messages look better and be more useful |
|
231
|
100
|
|
|
100
|
|
347
|
sub _dump { return PPI::Dumper->new(shift,whitespace=>0,comments=>0,locations=>1)->string } |
|
232
|
100
|
|
|
100
|
|
160
|
sub _errmsg { chomp(my $e=_dump(shift)); $e=~s/^/\t/mg; return "<<< $e >>>" } |
|
|
100
|
|
|
|
|
49086
|
|
|
|
100
|
|
|
|
|
633
|
|
|
233
|
|
|
|
|
|
|
sub _errormsg { |
|
234
|
90
|
|
|
90
|
|
842
|
my ($self,$msg) = @_; |
|
235
|
90
|
50
|
|
|
|
372
|
return "$msg ".($self->{ptr}?_errmsg($self->{ptr}):"UNDEF"); |
|
236
|
|
|
|
|
|
|
} |
|
237
|
|
|
|
|
|
|
sub _debug { |
|
238
|
4298
|
|
|
4298
|
|
22160
|
my ($self,$msg) = @_; |
|
239
|
4298
|
50
|
|
|
|
7658
|
return unless $self->{debug}; |
|
240
|
0
|
0
|
|
|
|
0
|
my $line = $self->{ptr} ? $self->{ptr}->line_number : '?'; |
|
241
|
0
|
0
|
|
|
|
0
|
my $col = $self->{ptr} ? $self->{ptr}->column_number : '?'; |
|
242
|
0
|
|
|
|
|
0
|
return print STDERR "[L$line C$col] $msg\n"; |
|
243
|
|
|
|
|
|
|
} |
|
244
|
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
sub parse_or_undef { ## no critic (RequireArgUnpacking) |
|
246
|
96
|
|
|
96
|
0
|
911
|
my $self = shift; |
|
247
|
96
|
|
|
|
|
122
|
my $out = eval { $self->parse_or_die(@_) }; |
|
|
96
|
|
|
|
|
175
|
|
|
248
|
96
|
|
100
|
|
|
449
|
my $errmsg = $@||"Unknown error"; |
|
249
|
96
|
100
|
|
|
|
161
|
$self->{errstr} = defined $out ? undef : $errmsg; |
|
250
|
96
|
|
|
|
|
218
|
return $out; |
|
251
|
|
|
|
|
|
|
} |
|
252
|
|
|
|
|
|
|
|
|
253
|
|
|
|
|
|
|
sub parse_or_die { |
|
254
|
175
|
|
|
175
|
0
|
3554
|
my ($self,$input) = @_; |
|
255
|
|
|
|
|
|
|
# PPI::Documents are not "complete" if they don't have a final semicolon, so tack one on there if it's missing |
|
256
|
175
|
100
|
100
|
|
|
1441
|
$input = \"$$input;" if ref $input eq 'SCALAR' && $$input!~/;\s*$/; |
|
257
|
175
|
|
|
|
|
666
|
$self->{doc} = my $doc = PPI::Document->new($input); |
|
258
|
175
|
|
100
|
|
|
441445
|
my $errmsg = PPI::Document->errstr||"Unknown error"; |
|
259
|
175
|
100
|
|
|
|
1566
|
$doc or croak "Parse failed: $errmsg"; |
|
260
|
174
|
100
|
|
|
|
421
|
$doc->complete or croak "Document incomplete (missing final semicolon?)"; |
|
261
|
172
|
|
|
|
|
55130
|
$self->{ctx} = 'list'; # we're documented to currently always parse in list context |
|
262
|
172
|
|
|
|
|
226
|
$self->{out} = {}; |
|
263
|
172
|
|
|
|
|
218
|
$self->{ptr} = $doc; |
|
264
|
172
|
|
|
|
|
368
|
my $rv = $self->_handle_block(outer=>1); |
|
265
|
170
|
100
|
|
|
|
1848
|
croak $rv unless ref $rv; |
|
266
|
154
|
|
|
|
|
207
|
my @rv = $rv->(); |
|
267
|
154
|
100
|
|
|
|
305
|
$self->{out}{_} = \@rv if @rv; |
|
268
|
154
|
|
|
|
|
455
|
return $self->{out}; |
|
269
|
|
|
|
|
|
|
} |
|
270
|
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
# Handles Documents, Blocks, and do-Blocks |
|
272
|
|
|
|
|
|
|
# Returns the last return value from the block |
|
273
|
|
|
|
|
|
|
# On Error returns a string, pointer not advanced |
|
274
|
|
|
|
|
|
|
# On Success advances pointer over the block |
|
275
|
|
|
|
|
|
|
sub _handle_block { ## no critic (ProhibitExcessComplexity) |
|
276
|
179
|
|
|
179
|
|
502
|
my ($self,%param) = @_; # params: outer |
|
277
|
179
|
|
|
|
|
224
|
my $block = $self->{ptr}; |
|
278
|
179
|
100
|
|
|
|
301
|
if ($param{outer}) |
|
279
|
172
|
50
|
|
|
|
481
|
{ return $self->_errormsg("expected Document") unless $block->isa('PPI::Document') } |
|
280
|
|
|
|
|
|
|
else { |
|
281
|
7
|
50
|
33
|
|
|
33
|
if ($block->isa('PPI::Token::Word') && $block->literal eq 'do') |
|
282
|
7
|
|
|
|
|
55
|
{ $block = $block->snext_sibling } |
|
283
|
7
|
50
|
|
|
|
98
|
return $self->_errormsg("expected Block") unless $block->isa('PPI::Structure::Block'); |
|
284
|
|
|
|
|
|
|
} |
|
285
|
179
|
|
|
|
|
395
|
$self->_debug("beginning to parse a block with ".$block->schildren." schildren"); |
|
286
|
179
|
|
|
3
|
|
540
|
my $block_rv = sub {}; |
|
287
|
179
|
|
|
|
|
347
|
STATEMENT: for my $stmt ($block->schildren) { |
|
288
|
|
|
|
|
|
|
# last statement in block gets its context, otherwise void context |
|
289
|
506
|
100
|
|
|
|
2392
|
local $self->{ctx} = $stmt->snext_sibling ? 'scalar-void' : $self->{ctx}; |
|
290
|
|
|
|
|
|
|
# ignore labels |
|
291
|
506
|
50
|
66
|
|
|
10621
|
if ($stmt->isa('PPI::Statement::Compound') && $stmt->schildren==1 |
|
|
|
|
66
|
|
|
|
|
|
292
|
|
|
|
|
|
|
&& $stmt->schild(0)->isa('PPI::Token::Label') ) { |
|
293
|
4
|
|
|
|
|
74
|
next STATEMENT; |
|
294
|
|
|
|
|
|
|
} |
|
295
|
502
|
|
|
|
|
663
|
local $self->{ptr} = $stmt; |
|
296
|
502
|
100
|
|
|
|
1207
|
if (ref( my $rv1 = $self->_handle_assignment( $param{outer}?(outer=>1):() ) )) { |
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
297
|
436
|
|
|
|
|
608
|
$self->_debug("parsed an assignment in a block"); |
|
298
|
436
|
50
|
66
|
|
|
2463
|
if ($self->{ptr} && (!$self->{ptr}->isa('PPI::Token::Structure') || !$self->{ptr}->content eq ';' || $self->{ptr}->snext_sibling)) |
|
|
|
|
33
|
|
|
|
|
|
299
|
1
|
|
|
|
|
3
|
{ return $self->_errormsg("expected Semicolon after assignment") } |
|
300
|
435
|
100
|
|
|
|
9996
|
$block_rv = $rv1 unless $self->{ctx} eq 'scalar-void'; |
|
301
|
|
|
|
|
|
|
} |
|
302
|
|
|
|
|
|
|
elsif ($stmt->class eq 'PPI::Statement') { |
|
303
|
61
|
|
|
|
|
292
|
local $self->{ptr} = $stmt->schild(0); |
|
304
|
61
|
|
|
|
|
564
|
my $rv2 = $self->_handle_value(); |
|
305
|
|
|
|
|
|
|
$rv2 = $self->_errormsg("expected Semicolon after value") |
|
306
|
60
|
50
|
66
|
|
|
486
|
if ref($rv2) && $self->{ptr} && (!$self->{ptr}->isa('PPI::Token::Structure') || !$self->{ptr}->content eq ';' || $self->{ptr}->snext_sibling); |
|
|
|
|
66
|
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
307
|
60
|
100
|
|
|
|
902
|
if (ref $rv2) { |
|
308
|
48
|
|
|
|
|
83
|
$self->_debug("parsed a plain value in a block"); |
|
309
|
48
|
100
|
|
|
|
123
|
if ($self->{ctx} eq 'scalar-void') |
|
310
|
3
|
100
|
|
|
|
6
|
{ warnings::warnif("value in void context") if $rv2->() } |
|
311
|
|
|
|
|
|
|
else |
|
312
|
45
|
|
|
|
|
184
|
{ $block_rv = $rv2 } |
|
313
|
|
|
|
|
|
|
} |
|
314
|
|
|
|
|
|
|
else |
|
315
|
12
|
50
|
|
|
|
71
|
{ return $self->_errormsg("couldn't parse ".($param{outer}?"Document":"Block")." Statement: ".join(", and ",$rv1,$rv2)) } |
|
316
|
|
|
|
|
|
|
} |
|
317
|
|
|
|
|
|
|
else |
|
318
|
4
|
|
|
|
|
27
|
{ return $self->_errormsg("unsupported element (not an assignment because: $rv1)") } |
|
319
|
|
|
|
|
|
|
} |
|
320
|
160
|
|
|
|
|
421
|
$self->{ptr} = $block->snext_sibling; |
|
321
|
160
|
|
|
|
|
756
|
return $block_rv |
|
322
|
|
|
|
|
|
|
} |
|
323
|
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
# Handles Variable Declarations and Assignment Statements |
|
325
|
|
|
|
|
|
|
# Returns TODO Later: implement return value of assignments |
|
326
|
|
|
|
|
|
|
# On Error returns a string, pointer not advanced |
|
327
|
|
|
|
|
|
|
# On Success advances pointer over the assignment |
|
328
|
|
|
|
|
|
|
sub _handle_assignment { ## no critic (ProhibitExcessComplexity) |
|
329
|
502
|
|
|
502
|
|
717
|
my ($self,%param) = @_; # params: outer |
|
330
|
502
|
|
|
|
|
479
|
my $as = $self->{ptr}; |
|
331
|
|
|
|
|
|
|
# The handling of ptr is a little tricky here: when we're done, |
|
332
|
|
|
|
|
|
|
# we need to advance the pointer so that it points to just after the assignment, |
|
333
|
|
|
|
|
|
|
# but we also need to be able to roll it back in case of error. |
|
334
|
502
|
|
|
|
|
366
|
my $last_ptr; |
|
335
|
|
|
|
|
|
|
{ # block for local ptr |
|
336
|
502
|
|
|
|
|
358
|
local $self->{ptr}=$self->{ptr}; |
|
|
502
|
|
|
|
|
666
|
|
|
337
|
502
|
100
|
66
|
|
|
1755
|
if ($as && $as->class eq 'PPI::Statement::Variable') { # declaration |
|
338
|
|
|
|
|
|
|
# note that Perl does not allow array or hash elements in declarations (no subscripts here) |
|
339
|
69
|
100
|
100
|
|
|
405
|
return $self->_errormsg("unsupported declaration type \"".$as->type."\"") |
|
340
|
|
|
|
|
|
|
unless $as->type eq 'our' || $as->type eq 'my'; |
|
341
|
|
|
|
|
|
|
return $self->_errormsg("Lexical variables (\"my\") not supported") # I'd like to support "my" soon |
|
342
|
68
|
100
|
33
|
|
|
1970
|
unless $as->type eq 'our' || ($as->type eq 'my' && $param{outer}); |
|
|
|
|
66
|
|
|
|
|
|
343
|
67
|
|
|
|
|
1319
|
$self->_debug("parsing a variable declaration"); |
|
344
|
67
|
|
|
|
|
111
|
$self->{ptr} = $as->schild(1); |
|
345
|
|
|
|
|
|
|
} |
|
346
|
|
|
|
|
|
|
else { |
|
347
|
433
|
100
|
33
|
|
|
2611
|
return $self->_errormsg("expected Assignment") |
|
|
|
|
66
|
|
|
|
|
|
348
|
|
|
|
|
|
|
if !$as || $as->class ne 'PPI::Statement' |
|
349
|
|
|
|
|
|
|
|| $as->schildren<3; # with subscripts, there's no upper limit on schildren |
|
350
|
387
|
|
|
|
|
7669
|
$self->_debug("parsing an assignment (schildren: ".$as->schildren.")"); |
|
351
|
387
|
|
|
|
|
793
|
$self->{ptr} = $as->schild(0); |
|
352
|
|
|
|
|
|
|
} |
|
353
|
|
|
|
|
|
|
|
|
354
|
454
|
|
|
|
|
3391
|
my ($lhs_scalar,@lhs); |
|
355
|
454
|
100
|
|
|
|
1094
|
if ($self->{ptr}->isa('PPI::Token::Symbol')) { |
|
|
|
100
|
|
|
|
|
|
|
356
|
439
|
|
|
|
|
750
|
my $sym = $self->_handle_symbol(); |
|
357
|
438
|
100
|
|
|
|
774
|
return $sym unless ref $sym; |
|
358
|
433
|
|
|
|
|
535
|
$lhs_scalar = $sym->{atype} eq '$'; |
|
359
|
433
|
|
|
|
|
1041
|
$self->_debug("assign single LHS \"$$sym{name}\"/$$sym{atype}"); |
|
360
|
433
|
|
|
|
|
611
|
@lhs = ($sym); |
|
361
|
|
|
|
|
|
|
} |
|
362
|
|
|
|
|
|
|
elsif ($self->{ptr}->isa('PPI::Structure::List')) { |
|
363
|
9
|
|
|
|
|
101
|
local $self->{ctx} = 'list'; |
|
364
|
9
|
|
|
|
|
27
|
my $l = $self->_handle_list(is_lhs=>1); |
|
365
|
9
|
100
|
|
|
|
23
|
return $l unless ref $l; |
|
366
|
8
|
|
|
|
|
20
|
@lhs = @$l; |
|
367
|
|
|
|
|
|
|
} |
|
368
|
|
|
|
|
|
|
else |
|
369
|
6
|
|
|
|
|
16
|
{ return $self->_errormsg("expected Assign LHS") } |
|
370
|
|
|
|
|
|
|
|
|
371
|
|
|
|
|
|
|
return $self->_errormsg("expected Assign Op") |
|
372
|
441
|
100
|
66
|
|
|
1667
|
unless $self->{ptr}->isa('PPI::Token::Operator') && $self->{ptr}->content eq '='; |
|
373
|
437
|
|
|
|
|
2168
|
$self->{ptr} = $self->{ptr}->snext_sibling; |
|
374
|
|
|
|
|
|
|
|
|
375
|
437
|
|
|
|
|
5216
|
my @rhs = do { |
|
376
|
437
|
100
|
|
|
|
865
|
local $self->{ctx} = $lhs_scalar ? 'scalar' : 'list'; |
|
377
|
437
|
|
|
|
|
753
|
my $rv = $self->_handle_value(); |
|
378
|
437
|
100
|
|
|
|
853
|
return $rv unless ref $rv; |
|
379
|
436
|
|
|
|
|
538
|
$rv->() }; |
|
380
|
436
|
|
|
|
|
1119
|
$self->_debug("assignment: LHS ".scalar(@lhs)." values, RHS ".scalar(@rhs)." values"); |
|
381
|
436
|
|
|
|
|
499
|
$last_ptr = $self->{ptr}; |
|
382
|
|
|
|
|
|
|
|
|
383
|
436
|
|
|
|
|
559
|
for my $l (@lhs) { |
|
384
|
442
|
100
|
|
|
|
942
|
if (!defined($l)) ## no critic (ProhibitCascadingIfElse) |
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
385
|
1
|
|
|
|
|
2
|
{ shift @rhs } |
|
386
|
|
|
|
|
|
|
elsif ($l->{atype} eq '$') |
|
387
|
399
|
|
|
|
|
349
|
{ ${ $l->{ref} } = shift @rhs } |
|
|
399
|
|
|
|
|
931
|
|
|
388
|
|
|
|
|
|
|
elsif ($l->{atype} eq '@') { |
|
389
|
27
|
100
|
|
|
|
24
|
if (!defined ${$l->{ref}}) |
|
|
27
|
|
|
|
|
47
|
|
|
390
|
25
|
|
|
|
|
37
|
{ ${ $l->{ref} } = [@rhs] } |
|
|
25
|
|
|
|
|
29
|
|
|
391
|
|
|
|
|
|
|
else |
|
392
|
2
|
|
|
|
|
4
|
{ @{ ${ $l->{ref} } } = @rhs } |
|
|
2
|
|
|
|
|
2
|
|
|
|
2
|
|
|
|
|
5
|
|
|
393
|
27
|
|
|
|
|
52
|
last; # slurp |
|
394
|
|
|
|
|
|
|
} |
|
395
|
|
|
|
|
|
|
elsif ($l->{atype} eq '%') { |
|
396
|
15
|
100
|
|
|
|
15
|
if (!defined ${$l->{ref}}) |
|
|
15
|
|
|
|
|
36
|
|
|
397
|
13
|
|
|
|
|
26
|
{ ${ $l->{ref} } = {@rhs} } |
|
|
13
|
|
|
|
|
20
|
|
|
398
|
|
|
|
|
|
|
else |
|
399
|
2
|
|
|
|
|
3
|
{ %{ ${ $l->{ref} } } = @rhs } |
|
|
2
|
|
|
|
|
2
|
|
|
|
2
|
|
|
|
|
6
|
|
|
400
|
15
|
|
|
|
|
33
|
last; # slurp |
|
401
|
|
|
|
|
|
|
} |
|
402
|
0
|
|
|
|
|
0
|
else { confess "Possible internal error: can't assign to "._errmsg($l) } # uncoverable statement |
|
403
|
|
|
|
|
|
|
} |
|
404
|
|
|
|
|
|
|
} # end block for local ptr |
|
405
|
436
|
|
|
|
|
756
|
$self->{ptr} = $last_ptr; |
|
406
|
112
|
|
|
112
|
|
143
|
return sub { return } |
|
407
|
436
|
|
|
|
|
1389
|
} |
|
408
|
|
|
|
|
|
|
|
|
409
|
|
|
|
|
|
|
# If is_lhs false: |
|
410
|
|
|
|
|
|
|
# Handles () lists as well as the *contents* of {} and [] constructors |
|
411
|
|
|
|
|
|
|
# Returns an arrayref of values; in scalar ctx the last value from the list wrapped in an arrayref |
|
412
|
|
|
|
|
|
|
# If is_lhs true: |
|
413
|
|
|
|
|
|
|
# Handles assignment LHS symbol () lists |
|
414
|
|
|
|
|
|
|
# Returns an arrayref of _handle_symbol() return values (hashrefs) (and undefs) |
|
415
|
|
|
|
|
|
|
# On Error returns a string, pointer not advanced |
|
416
|
|
|
|
|
|
|
# On Success advances pointer over the list |
|
417
|
|
|
|
|
|
|
sub _handle_list { ## no critic (ProhibitExcessComplexity) |
|
418
|
176
|
|
|
176
|
|
237
|
my ($self,%param) = @_; # params: is_lhs |
|
419
|
176
|
|
|
|
|
242
|
my $outerlist = $self->{ptr}; |
|
420
|
176
|
50
|
66
|
|
|
779
|
return $self->_errormsg("expected List or Constructor") |
|
421
|
|
|
|
|
|
|
unless $outerlist->isa('PPI::Structure::List') || $outerlist->isa('PPI::Structure::Constructor'); |
|
422
|
|
|
|
|
|
|
# prevent caller from accidentally expecting a list (we return an arrayref) |
|
423
|
176
|
50
|
|
|
|
464
|
confess "Internal error: _handle_list called in list context" if wantarray; |
|
424
|
|
|
|
|
|
|
croak "can only handle a plain list on LHS" |
|
425
|
176
|
50
|
66
|
|
|
364
|
if $param{is_lhs} && !$outerlist->isa('PPI::Structure::List'); |
|
426
|
176
|
100
|
|
|
|
546
|
$self->_debug("parsing a list ".($param{is_lhs}?"(LHS)":"(Not LHS)")); |
|
427
|
176
|
100
|
|
|
|
438
|
if (!$outerlist->schildren) { # empty list |
|
428
|
24
|
|
|
|
|
197
|
$self->{ptr} = $outerlist->snext_sibling; |
|
429
|
24
|
|
|
|
|
401
|
return []; |
|
430
|
|
|
|
|
|
|
} |
|
431
|
|
|
|
|
|
|
# the first & only child of the outer list structure is a statement / expression |
|
432
|
152
|
|
|
|
|
1687
|
my $act_list = $outerlist->schild(0); |
|
433
|
152
|
50
|
66
|
|
|
1304
|
croak "Unsupported list\n"._errmsg($outerlist) |
|
|
|
|
33
|
|
|
|
|
|
434
|
|
|
|
|
|
|
unless $outerlist->schildren==1 && ($act_list->isa('PPI::Statement::Expression') || $act_list->class eq 'PPI::Statement'); |
|
435
|
152
|
|
|
|
|
2156
|
my @thelist; |
|
436
|
|
|
|
|
|
|
my $last_value; # for scalar context and !is_lhs |
|
437
|
|
|
|
|
|
|
{ # block for local ptr |
|
438
|
152
|
|
|
|
|
128
|
my $expect = 'item'; |
|
|
152
|
|
|
|
|
158
|
|
|
439
|
152
|
|
|
|
|
277
|
local $self->{ptr} = $act_list->schild(0); |
|
440
|
152
|
|
|
|
|
1332
|
while ($self->{ptr}) { |
|
441
|
672
|
100
|
|
|
|
1067
|
if ($expect eq 'item') { |
|
|
|
50
|
|
|
|
|
|
|
442
|
411
|
|
|
|
|
723
|
my $peek_next = $self->{ptr}->snext_sibling; |
|
443
|
411
|
|
100
|
|
|
6191
|
my $fat_comma_next = $peek_next && $peek_next->isa('PPI::Token::Operator') && $peek_next->content eq '=>'; |
|
444
|
411
|
100
|
|
|
|
1262
|
if ($param{is_lhs}) { |
|
445
|
15
|
100
|
66
|
|
|
51
|
if ($self->{ptr}->isa('PPI::Token::Symbol')) { |
|
|
|
100
|
66
|
|
|
|
|
|
446
|
13
|
|
|
|
|
23
|
my $sym = $self->_handle_symbol(); |
|
447
|
13
|
50
|
|
|
|
24
|
return $sym unless ref $sym; |
|
448
|
13
|
|
|
|
|
34
|
$self->_debug("LHS List symbol: \"$$sym{name}\"/$$sym{atype}"); |
|
449
|
13
|
|
|
|
|
20
|
push @thelist, $sym; |
|
450
|
|
|
|
|
|
|
} |
|
451
|
|
|
|
|
|
|
elsif (!$fat_comma_next && $self->{ptr}->isa('PPI::Token::Word') && $self->{ptr}->literal eq 'undef') { |
|
452
|
1
|
|
|
|
|
11
|
$self->_debug("LHS List undef"); |
|
453
|
1
|
|
|
|
|
2
|
push @thelist, undef; |
|
454
|
1
|
|
|
|
|
3
|
$self->{ptr} = $self->{ptr}->snext_sibling; |
|
455
|
|
|
|
|
|
|
} |
|
456
|
|
|
|
|
|
|
else |
|
457
|
1
|
|
|
|
|
5
|
{ return "Don't support this on LHS: "._errmsg($self->{ptr}) } |
|
458
|
|
|
|
|
|
|
} |
|
459
|
|
|
|
|
|
|
else { |
|
460
|
|
|
|
|
|
|
# handle fat comma autoquoting words |
|
461
|
396
|
100
|
100
|
|
|
1229
|
if ($fat_comma_next && $self->{ptr}->isa('PPI::Token::Word') && $self->{ptr}->literal=~/^\w+$/ ) { |
|
|
|
|
100
|
|
|
|
|
|
462
|
32
|
|
|
|
|
353
|
my $word = $self->{ptr}->literal; |
|
463
|
32
|
|
|
|
|
194
|
$self->_debug("list fat comma autoquoted \"$word\""); |
|
464
|
32
|
|
|
|
|
44
|
push @thelist, $word; |
|
465
|
32
|
|
|
|
|
35
|
$last_value = $word; |
|
466
|
32
|
|
|
|
|
53
|
$self->{ptr} = $self->{ptr}->snext_sibling; |
|
467
|
|
|
|
|
|
|
} |
|
468
|
|
|
|
|
|
|
else { |
|
469
|
364
|
|
|
|
|
638
|
my $val = $self->_handle_value(); |
|
470
|
364
|
50
|
|
|
|
725
|
return $val unless ref $val; |
|
471
|
364
|
|
|
|
|
511
|
push @thelist, $val->(); |
|
472
|
364
|
100
|
|
|
|
1003
|
$last_value = $val->() if $self->{ctx}=~/^scalar\b/; |
|
473
|
|
|
|
|
|
|
} |
|
474
|
|
|
|
|
|
|
} |
|
475
|
410
|
|
|
|
|
1333
|
$expect = 'separator'; |
|
476
|
|
|
|
|
|
|
} |
|
477
|
|
|
|
|
|
|
elsif ($expect eq 'separator') { |
|
478
|
|
|
|
|
|
|
return $self->_errormsg("expected List Separator") |
|
479
|
|
|
|
|
|
|
unless $self->{ptr}->isa('PPI::Token::Operator') |
|
480
|
261
|
50
|
66
|
|
|
1059
|
&& ($self->{ptr}->content eq ',' || $self->{ptr}->content eq '=>'); |
|
|
|
|
33
|
|
|
|
|
|
481
|
261
|
|
|
|
|
1505
|
$self->{ptr} = $self->{ptr}->snext_sibling; |
|
482
|
261
|
|
|
|
|
3666
|
$expect = 'item'; |
|
483
|
|
|
|
|
|
|
} |
|
484
|
0
|
|
|
|
|
0
|
else { confess "really shouldn't happen, bad state $expect" } # uncoverable statement |
|
485
|
|
|
|
|
|
|
} |
|
486
|
|
|
|
|
|
|
} # end block for local ptr |
|
487
|
151
|
|
|
|
|
300
|
$self->{ptr} = $outerlist->snext_sibling; |
|
488
|
|
|
|
|
|
|
# don't use $thelist[-1] here because that flattens all lists - consider: my $x = (3,()); |
|
489
|
|
|
|
|
|
|
# in scalar ctx the comma op always throws away its LHS, so $x should be undef |
|
490
|
151
|
100
|
100
|
|
|
2221
|
return [$last_value] if !$param{is_lhs} && $self->{ctx}=~/^scalar\b/; |
|
491
|
148
|
|
|
|
|
267
|
return \@thelist; |
|
492
|
|
|
|
|
|
|
} |
|
493
|
|
|
|
|
|
|
|
|
494
|
|
|
|
|
|
|
# Handles Symbols, subscripts and (implicit) arrow operator derefs |
|
495
|
|
|
|
|
|
|
# Returns a hashref representing the symbol: |
|
496
|
|
|
|
|
|
|
# name = the name of the symbol (TODO Later: Currently only used for debugging messages, remove?) |
|
497
|
|
|
|
|
|
|
# atype = the raw_type of the symbol |
|
498
|
|
|
|
|
|
|
# ref = reference to our storage location |
|
499
|
|
|
|
|
|
|
# On Error returns a string, pointer not advanced |
|
500
|
|
|
|
|
|
|
# On Success advances pointer over the symbol and possible subscript |
|
501
|
|
|
|
|
|
|
sub _handle_symbol { ## no critic (ProhibitExcessComplexity) |
|
502
|
500
|
|
|
500
|
|
452
|
my ($self) = @_; |
|
503
|
500
|
|
|
|
|
440
|
my $sym = $self->{ptr}; |
|
504
|
500
|
50
|
33
|
|
|
2251
|
return $self->_errormsg("expected Symbol") |
|
505
|
|
|
|
|
|
|
unless $sym && $sym->isa('PPI::Token::Symbol'); |
|
506
|
500
|
|
|
|
|
1383
|
my %rsym = ( name => $sym->symbol, atype => $sym->raw_type ); |
|
507
|
500
|
|
|
|
|
17132
|
$self->_debug("parsing a symbol \"".$sym->symbol.'"'); |
|
508
|
500
|
|
|
|
|
896
|
my $temp_ptr = $sym->snext_sibling; |
|
509
|
500
|
100
|
100
|
|
|
7456
|
if ($temp_ptr && $temp_ptr->isa('PPI::Structure::Subscript')) { |
|
510
|
25
|
|
|
|
|
62
|
my $ss = $self->_handle_subscript($temp_ptr); |
|
511
|
24
|
100
|
|
|
|
136
|
return $ss unless ref $ss; |
|
512
|
|
|
|
|
|
|
# fetch the variable reference with subscript |
|
513
|
18
|
100
|
100
|
|
|
34
|
if ($sym->raw_type eq '$' && $sym->symbol_type eq '@' && $$ss{braces} eq '[]') { |
|
|
|
100
|
66
|
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
514
|
11
|
|
|
|
|
555
|
$rsym{ref} = \( $self->{out}{$sym->symbol}[$$ss{sub}] ); |
|
515
|
|
|
|
|
|
|
} |
|
516
|
|
|
|
|
|
|
elsif ($sym->raw_type eq '$' && $sym->symbol_type eq '%' && $$ss{braces} eq '{}') { |
|
517
|
3
|
|
|
|
|
303
|
$rsym{ref} = \( $self->{out}{$sym->symbol}{$$ss{sub}} ); |
|
518
|
|
|
|
|
|
|
} |
|
519
|
4
|
|
|
|
|
47
|
else { return $self->_errormsg("can't handle this subscript on this variable: "._errmsg($sym)._errmsg($temp_ptr)) } |
|
520
|
14
|
|
|
|
|
537
|
$self->_debug("handled symbol with subscript"); |
|
521
|
14
|
|
|
|
|
30
|
$temp_ptr = $temp_ptr->snext_sibling; |
|
522
|
|
|
|
|
|
|
} |
|
523
|
|
|
|
|
|
|
else { |
|
524
|
475
|
|
|
|
|
716
|
$self->_debug("handled symbol without subscript"); |
|
525
|
475
|
|
|
|
|
918
|
$rsym{ref} = \( $self->{out}{$sym->symbol} ); |
|
526
|
475
|
|
|
|
|
11321
|
$temp_ptr = $sym->snext_sibling; |
|
527
|
|
|
|
|
|
|
} |
|
528
|
489
|
|
|
|
|
4968
|
while (1) { |
|
529
|
566
|
100
|
100
|
|
|
4149
|
if ($temp_ptr && $temp_ptr->isa('PPI::Token::Operator') && $temp_ptr->content eq '->') { |
|
|
|
100
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
530
|
29
|
|
|
|
|
143
|
$self->_debug("skipping arrow operator between derefs"); |
|
531
|
29
|
|
|
|
|
69
|
$temp_ptr = $temp_ptr->snext_sibling; |
|
532
|
29
|
|
|
|
|
394
|
next; # ignore arrows |
|
533
|
|
|
|
|
|
|
} |
|
534
|
|
|
|
|
|
|
elsif ($temp_ptr && $temp_ptr->isa('PPI::Structure::Subscript')) { |
|
535
|
48
|
|
|
|
|
87
|
my $ss = $self->_handle_subscript($temp_ptr); |
|
536
|
48
|
50
|
|
|
|
351
|
return $ss unless ref $ss; |
|
537
|
48
|
100
|
|
|
|
103
|
if ($$ss{braces} eq '[]') { |
|
|
|
50
|
|
|
|
|
|
|
538
|
27
|
|
|
|
|
64
|
$self->_debug("deref [$$ss{sub}]"); |
|
539
|
27
|
50
|
|
|
|
28
|
return $self->_errormsg("Not an array reference") unless ref(${$rsym{ref}}) eq 'ARRAY'; |
|
|
27
|
|
|
|
|
61
|
|
|
540
|
27
|
|
|
|
|
20
|
$rsym{ref} = \( ${ $rsym{ref} }->[$$ss{sub}] ); |
|
|
27
|
|
|
|
|
50
|
|
|
541
|
|
|
|
|
|
|
} |
|
542
|
|
|
|
|
|
|
elsif ($$ss{braces} eq '{}') { |
|
543
|
21
|
|
|
|
|
49
|
$self->_debug("deref {$$ss{sub}}"); |
|
544
|
21
|
50
|
|
|
|
19
|
return $self->_errormsg("Not a hash reference") unless ref(${$rsym{ref}}) eq 'HASH'; |
|
|
21
|
|
|
|
|
45
|
|
|
545
|
21
|
|
|
|
|
17
|
$rsym{ref} = \( ${ $rsym{ref} }->{$$ss{sub}} ); |
|
|
21
|
|
|
|
|
49
|
|
|
546
|
|
|
|
|
|
|
} |
|
547
|
0
|
|
|
|
|
0
|
else { croak "unknown braces ".$$ss{braces} } |
|
548
|
48
|
|
|
|
|
74
|
$self->_debug("dereferencing a subscript"); |
|
549
|
48
|
|
|
|
|
94
|
$temp_ptr = $temp_ptr->snext_sibling; |
|
550
|
|
|
|
|
|
|
} |
|
551
|
489
|
|
|
|
|
3399
|
else { last } |
|
552
|
|
|
|
|
|
|
} |
|
553
|
489
|
|
|
|
|
610
|
$self->{ptr} = $temp_ptr; |
|
554
|
489
|
|
|
|
|
677
|
return \%rsym; |
|
555
|
|
|
|
|
|
|
} |
|
556
|
|
|
|
|
|
|
|
|
557
|
|
|
|
|
|
|
# Handles a subscript, for use in _handle_symbol |
|
558
|
|
|
|
|
|
|
# Input: $self, subscript element |
|
559
|
|
|
|
|
|
|
# On Success Returns a hashref with the following elements: |
|
560
|
|
|
|
|
|
|
# sub = the subscript's value |
|
561
|
|
|
|
|
|
|
# braces = the brace type, either [] or {} |
|
562
|
|
|
|
|
|
|
# On Error returns a string |
|
563
|
|
|
|
|
|
|
# Does NOT advance the pointer |
|
564
|
|
|
|
|
|
|
sub _handle_subscript { |
|
565
|
73
|
|
|
73
|
|
87
|
my ($self,$subscr) = @_; |
|
566
|
73
|
50
|
|
|
|
197
|
croak "not a subscript" unless $subscr->isa('PPI::Structure::Subscript'); |
|
567
|
|
|
|
|
|
|
# fetch subscript |
|
568
|
73
|
|
|
|
|
159
|
my @sub_ch = $subscr->schildren; |
|
569
|
73
|
50
|
33
|
|
|
684
|
return $self->_errormsg("expected subscript to contain a single expression") |
|
570
|
|
|
|
|
|
|
unless @sub_ch==1 && $sub_ch[0]->isa('PPI::Statement::Expression'); |
|
571
|
73
|
|
|
|
|
141
|
my @subs = $sub_ch[0]->schildren; |
|
572
|
73
|
100
|
|
|
|
408
|
return $self->_errormsg("expected subscript to contain a single value") |
|
573
|
|
|
|
|
|
|
unless @subs==1; |
|
574
|
67
|
|
|
|
|
61
|
my $sub; |
|
575
|
|
|
|
|
|
|
# autoquoting in hash braces |
|
576
|
67
|
100
|
100
|
|
|
154
|
if ($subscr->braces eq '{}' && $subs[0]->isa('PPI::Token::Word')) |
|
577
|
8
|
|
|
|
|
92
|
{ $sub = $subs[0]->literal } |
|
578
|
|
|
|
|
|
|
else { |
|
579
|
59
|
|
|
|
|
528
|
local $self->{ctx} = 'scalar'; |
|
580
|
59
|
|
|
|
|
73
|
local $self->{ptr} = $subs[0]; |
|
581
|
59
|
|
|
|
|
116
|
my $v = $self->_handle_value(); |
|
582
|
58
|
50
|
|
|
|
121
|
return $v unless ref $v; |
|
583
|
58
|
|
|
|
|
72
|
$sub = $v->(); |
|
584
|
|
|
|
|
|
|
} |
|
585
|
66
|
|
|
|
|
256
|
$self->_debug("evaluated subscript to \"$sub\", braces ".$subscr->braces); |
|
586
|
66
|
|
|
|
|
151
|
return { sub=>$sub, braces=>$subscr->braces }; |
|
587
|
|
|
|
|
|
|
} |
|
588
|
|
|
|
|
|
|
|
|
589
|
|
|
|
|
|
|
# Handles lots of different values (including lists) |
|
590
|
|
|
|
|
|
|
# Returns a coderef which, when called, returns the value(s) |
|
591
|
|
|
|
|
|
|
# On Error returns a string, pointer not advanced |
|
592
|
|
|
|
|
|
|
# On Success advances pointer over the value |
|
593
|
|
|
|
|
|
|
sub _handle_value { ## no critic (ProhibitExcessComplexity) |
|
594
|
921
|
|
|
921
|
|
873
|
my ($self) = @_; |
|
595
|
921
|
|
|
|
|
857
|
my $val = $self->{ptr}; |
|
596
|
921
|
50
|
|
|
|
1896
|
return $self->_errormsg("expected Value") unless $val; |
|
597
|
921
|
100
|
100
|
|
|
6078
|
if ($val->isa('PPI::Token::Number')) { ## no critic (ProhibitCascadingIfElse) |
|
|
|
100
|
100
|
|
|
|
|
|
|
|
100
|
100
|
|
|
|
|
|
|
|
100
|
66
|
|
|
|
|
|
|
|
100
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
598
|
332
|
|
|
|
|
713
|
my $num = 0+$val->literal; |
|
599
|
332
|
|
|
|
|
3508
|
$self->_debug("consuming number $num as value"); |
|
600
|
332
|
|
|
|
|
671
|
$self->{ptr} = $val->snext_sibling; |
|
601
|
332
|
|
|
332
|
|
867
|
return sub { return $num } |
|
602
|
332
|
|
|
|
|
4405
|
} |
|
603
|
|
|
|
|
|
|
elsif ($val->isa('PPI::Token::Word') && $val->literal eq 'undef') { |
|
604
|
1
|
|
|
|
|
10
|
$self->_debug("consuming undef as value"); |
|
605
|
1
|
|
|
|
|
3
|
$self->{ptr} = $val->snext_sibling; |
|
606
|
1
|
|
|
1
|
|
3
|
return sub { return undef } ## no critic (ProhibitExplicitReturnUndef) |
|
607
|
1
|
|
|
|
|
13
|
} |
|
608
|
|
|
|
|
|
|
elsif ($val->isa('PPI::Token::Word') && $val->literal=~/^-\w+$/) { |
|
609
|
4
|
|
|
|
|
71
|
my $word = $val->literal; |
|
610
|
4
|
|
|
|
|
27
|
$self->_debug("consuming dashed bareword \"$word\" as value"); |
|
611
|
4
|
|
|
|
|
7
|
$self->{ptr} = $val->snext_sibling; |
|
612
|
4
|
|
|
4
|
|
8
|
return sub { return $word } |
|
613
|
4
|
|
|
|
|
52
|
} |
|
614
|
|
|
|
|
|
|
elsif ($val->isa('PPI::Token::Quote')) { |
|
615
|
|
|
|
|
|
|
# handle the known PPI::Token::Quote subclasses |
|
616
|
334
|
|
|
|
|
272
|
my $str; |
|
617
|
334
|
100
|
100
|
|
|
1496
|
if ( $val->isa('PPI::Token::Quote::Single') || $val->isa('PPI::Token::Quote::Literal') ) |
|
|
|
50
|
66
|
|
|
|
|
|
618
|
204
|
|
|
|
|
476
|
{ $str = $val->literal } |
|
619
|
|
|
|
|
|
|
elsif ( $val->isa('PPI::Token::Quote::Double') || $val->isa('PPI::Token::Quote::Interpolate') ) { |
|
620
|
|
|
|
|
|
|
# do very limited string interpolation |
|
621
|
130
|
|
|
|
|
332
|
$str = $val->string; |
|
622
|
|
|
|
|
|
|
# Perl (at least v5.20) doesn't allow trailing $, it does allow trailing @ |
|
623
|
130
|
100
|
|
|
|
709
|
return "final \$ should be \\\$ or \$name" if $str=~/\$$/; |
|
624
|
|
|
|
|
|
|
# Variables |
|
625
|
129
|
|
|
|
|
189
|
$str=~s{(?_fetch_interp_var($2)}eg; |
|
|
9
|
|
|
|
|
25
|
|
|
626
|
129
|
|
|
|
|
137
|
$str=~s{(?_fetch_interp_var($2.$3)}eg; |
|
|
3
|
|
|
|
|
15
|
|
|
627
|
129
|
100
|
|
|
|
309
|
return "don't support string interpolation of '$1' in '$str' at "._errmsg($val) |
|
628
|
|
|
|
|
|
|
if $str=~/(?
|
|
629
|
|
|
|
|
|
|
# Backslash escape sequences |
|
630
|
128
|
|
|
|
|
166
|
$str=~s{\\([0-7]{1,3}|x[0-9A-Fa-f]{2}|.)}{_unbackslash($1)}eg; |
|
|
19
|
|
|
|
|
32
|
|
|
631
|
|
|
|
|
|
|
} |
|
632
|
|
|
|
|
|
|
else |
|
633
|
0
|
|
|
|
|
0
|
{ confess "unknown PPI::Token::Quote subclass ".$val->class } # uncoverable statement |
|
634
|
331
|
|
|
|
|
1878
|
$self->_debug("consuming quoted string \"$str\" as value"); |
|
635
|
331
|
|
|
|
|
638
|
$self->{ptr} = $val->snext_sibling; |
|
636
|
331
|
|
|
336
|
|
4407
|
return sub { return $str }; |
|
|
336
|
|
|
|
|
1015
|
|
|
637
|
|
|
|
|
|
|
} |
|
638
|
|
|
|
|
|
|
elsif ($val->isa('PPI::Token::Symbol')) { |
|
639
|
48
|
|
|
|
|
96
|
my $sym = $self->_handle_symbol(); |
|
640
|
48
|
100
|
|
|
|
103
|
return $sym unless ref $sym; |
|
641
|
43
|
|
|
|
|
155
|
$self->_debug("consuming and accessing symbol \"$$sym{name}\"/$$sym{atype} as value (ctx: ".$self->{ctx}.")"); |
|
642
|
43
|
100
|
|
|
|
108
|
if ($sym->{atype} eq '$') { |
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
643
|
30
|
|
|
30
|
|
23
|
return sub { return ${ $sym->{ref} } } |
|
|
30
|
|
|
|
|
113
|
|
|
644
|
30
|
|
|
|
|
88
|
} |
|
645
|
|
|
|
|
|
|
elsif ($sym->{atype} eq '@') { |
|
646
|
|
|
|
|
|
|
return $self->{ctx}=~/^scalar\b/ |
|
647
|
3
|
|
|
3
|
|
3
|
? sub { return scalar( @{ ${ $sym->{ref} } } ) } |
|
|
3
|
|
|
|
|
3
|
|
|
|
3
|
|
|
|
|
10
|
|
|
648
|
4
|
50
|
|
4
|
|
10
|
: sub { wantarray or confess "expected to be called in list context"; |
|
649
|
4
|
|
|
|
|
3
|
return @{ ${ $sym->{ref} } } } |
|
|
4
|
|
|
|
|
5
|
|
|
|
4
|
|
|
|
|
14
|
|
|
650
|
6
|
100
|
|
|
|
32
|
} |
|
651
|
|
|
|
|
|
|
elsif ($sym->{atype} eq '%') { |
|
652
|
|
|
|
|
|
|
return $self->{ctx}=~/^scalar\b/ |
|
653
|
1
|
|
|
1
|
|
1
|
? sub { return scalar( %{ ${ $sym->{ref} } } ) } |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
7
|
|
|
654
|
3
|
50
|
|
3
|
|
8
|
: sub { wantarray or confess "expected to be called in list context"; |
|
655
|
3
|
|
|
|
|
3
|
return %{ ${ $sym->{ref} } } } |
|
|
3
|
|
|
|
|
4
|
|
|
|
3
|
|
|
|
|
11
|
|
|
656
|
6
|
100
|
|
|
|
43
|
} |
|
657
|
1
|
|
|
|
|
138
|
else { confess "bad symbol $sym" } |
|
658
|
|
|
|
|
|
|
} |
|
659
|
|
|
|
|
|
|
elsif ($val->isa('PPI::Structure::Constructor')) { |
|
660
|
128
|
|
|
|
|
220
|
local $self->{ctx} = 'list'; |
|
661
|
128
|
|
|
|
|
235
|
my $l = $self->_handle_list(); |
|
662
|
128
|
50
|
|
|
|
262
|
return $l unless ref $l; |
|
663
|
128
|
|
|
|
|
191
|
$self->_debug("consuming arrayref/hashref constructor as value"); |
|
664
|
128
|
100
|
|
|
|
283
|
if ($val->braces eq '[]') |
|
|
|
50
|
|
|
|
|
|
|
665
|
52
|
|
|
52
|
|
487
|
{ return sub { return [ @$l ] } } |
|
|
52
|
|
|
|
|
163
|
|
|
666
|
|
|
|
|
|
|
elsif ($val->braces eq '{}') |
|
667
|
76
|
|
|
76
|
|
1148
|
{ return sub { return { @$l } } } |
|
|
76
|
|
|
|
|
232
|
|
|
668
|
0
|
|
|
|
|
0
|
croak "Unsupported constructor\n"._errmsg($val); # uncoverable statement |
|
669
|
|
|
|
|
|
|
} |
|
670
|
|
|
|
|
|
|
elsif ($val->isa('PPI::Token::Word') && $val->literal eq 'do' |
|
671
|
|
|
|
|
|
|
&& $val->snext_sibling && $val->snext_sibling->isa('PPI::Structure::Block')) { |
|
672
|
7
|
|
|
|
|
437
|
$self->_debug("attempting to consume block as value"); |
|
673
|
7
|
|
|
|
|
20
|
return $self->_handle_block(); |
|
674
|
|
|
|
|
|
|
} |
|
675
|
|
|
|
|
|
|
elsif ($val->isa('PPI::Structure::List')) { |
|
676
|
39
|
|
|
|
|
1496
|
my $l = $self->_handle_list(); |
|
677
|
39
|
50
|
|
|
|
77
|
return $l unless ref $l; |
|
678
|
39
|
|
|
|
|
61
|
$self->_debug("consuming list as value"); |
|
679
|
|
|
|
|
|
|
return $self->{ctx}=~/^scalar\b/ |
|
680
|
6
|
|
|
6
|
|
17
|
? sub { return $l->[-1] } # note in this case we should only be getting one value from _handle_list anyway |
|
681
|
34
|
50
|
|
34
|
|
65
|
: sub { wantarray or confess "expected to be called in list context"; |
|
682
|
34
|
|
|
|
|
118
|
return @$l } |
|
683
|
39
|
100
|
|
|
|
179
|
} |
|
684
|
|
|
|
|
|
|
elsif ($val->isa('PPI::Token::QuoteLike::Words')) { # qw// |
|
685
|
25
|
|
|
|
|
91
|
my @l = $val->literal; # returns a list of words |
|
686
|
25
|
|
|
|
|
243
|
$self->_debug("consuming qw/@l/ as value"); |
|
687
|
25
|
|
|
|
|
51
|
$self->{ptr} = $val->snext_sibling; |
|
688
|
|
|
|
|
|
|
return $self->{ctx}=~/^scalar\b/ |
|
689
|
1
|
|
|
1
|
|
5
|
? sub { return $l[-1] } |
|
690
|
24
|
50
|
|
24
|
|
36
|
: sub { wantarray or confess "expected to be called in list context"; |
|
691
|
24
|
|
|
|
|
96
|
return @l } |
|
692
|
25
|
100
|
|
|
|
349
|
} |
|
693
|
3
|
|
|
|
|
124
|
return $self->_errormsg("can't handle value"); |
|
694
|
|
|
|
|
|
|
} |
|
695
|
|
|
|
|
|
|
|
|
696
|
|
|
|
|
|
|
my %_backsl_tbl = ( '\\'=>'\\', '$'=>'$', '"'=>'"', "'"=>"'", 'n'=>"\n", 'r'=>"\r", 't'=>"\t" ); |
|
697
|
|
|
|
|
|
|
sub _unbackslash { |
|
698
|
19
|
|
|
19
|
|
28
|
my ($what) = @_; |
|
699
|
19
|
100
|
|
|
|
57
|
return chr(oct($what)) if $what=~/^[0-7]{1,3}$/; |
|
700
|
14
|
100
|
|
|
|
24
|
return chr(hex($1)) if $what=~/^x([0-9A-Fa-f]{2})$/; ## no critic (ProhibitCaptureWithoutTest) |
|
701
|
13
|
100
|
|
|
|
47
|
return $_backsl_tbl{$what} if exists $_backsl_tbl{$what}; |
|
702
|
1
|
|
|
|
|
85
|
croak "Don't support escape sequence \"\\$what\""; |
|
703
|
|
|
|
|
|
|
} |
|
704
|
|
|
|
|
|
|
|
|
705
|
|
|
|
|
|
|
sub _fetch_interp_var { |
|
706
|
12
|
|
|
12
|
|
25
|
my ($self,$var) = @_; |
|
707
|
|
|
|
|
|
|
return $self->{out}{$var} |
|
708
|
12
|
100
|
100
|
|
|
84
|
if exists $self->{out}{$var} && defined $self->{out}{$var}; |
|
709
|
2
|
|
|
|
|
355
|
warnings::warnif("Use of uninitialized value $var in interpolation"); |
|
710
|
2
|
|
|
|
|
100
|
return ""; |
|
711
|
|
|
|
|
|
|
} |
|
712
|
|
|
|
|
|
|
|
|
713
|
|
|
|
|
|
|
|
|
714
|
|
|
|
|
|
|
1; |