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; |