File Coverage

blib/lib/Config/Perl.pm
Criterion Covered Total %
statement 336 344 98.8
branch 183 222 82.4
condition 111 151 73.5
subroutine 39 39 100.0
pod 0 4 0.0
total 669 760 88.5


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;