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