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