File Coverage

blib/lib/Config/Perl.pm
Criterion Covered Total %
statement 230 237 99.1
branch 156 182 86.2
condition 137 178 76.9
subroutine 23 23 100.0
pod 0 4 0.0
total 546 624 88.4


line stmt bran cond sub pod time code
1             #!perl
2             package Config::Perl;
3 5     5   109932 use warnings;
  5         9  
  5         157  
4 5     5   20 use strict;
  5         6  
  5         367  
5              
6             our $VERSION = '0.02';
7              
8             =head1 Name
9              
10             Config::Perl - Perl extension to parse configuration files written in a subset of Perl
11             and (limited) undumping of data structures (safer than eval thanks to parsing via PPI)
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 - see L.
40              
41             The code is parsed via L, eliminating the need for Perl's C.
42             This should provide a higher level of safety* compared to C
43             (even when making use of a module like L).
44              
45             * B A "higher level of safety" does not mean "perfect safety".
46             This software is distributed B; without even the implied
47             warranty of B or B.
48             See also the licence for this software.
49              
50             This module attempts to provide 100% compatibility with Perl over the subset of Perl it supports.
51             When a Perl feature is not supported by this module, it should complain
52             that the feature is not supported, instead of silently giving a wrong result.
53             If the output of a parse is different from how Perl would evaluate the same string,
54             then that is a bug in this module that should be fixed by correcting the output
55             or adding an error message that the particular feature is unsupported.
56             However, the result of using this module to parse something that is not valid Perl is undefined;
57             it may cause an error, or may fail in some other silent way.
58              
59             This document describes version 0.02 of the module.
60             Although this module is well-tested and working, it still lacks some
61             features to make it I useful (see list below).
62             Contributions are welcome!
63              
64             =head2 Interface
65              
66             This module has a simple OO interface. A new parser is created
67             with C<< Config::Perl->new >>, which currently does not take any arguments,
68             and documents are parsed with either the method C or C.
69              
70             my $parser = Config::Perl->new;
71             my $out1 = $parser->parse_or_undef(\' $foo = "bar"; ');
72             warn "parse failed: ".$parser->errstr unless defined $out1;
73             my $out2 = $parser->parse_or_die('filename.pl');
74              
75             The arguments and return values of these two methods are (almost) the same:
76             They each take exactly one argument, which is either a filename,
77             or a reference to a string containing the code to be parsed
78             (this is the same as L's C method).
79              
80             The methods differ in that, as the names imply, C
81             will C on errors, while C will return C;
82             the error message is then accessible via the C method.
83              
84             For a successful parse, the return value of each function is a hashref
85             representing the "symbol table" of the parsed document.
86             This "symbol table" hash is similar to, but not the same as, Perl's symbol table.
87             The hash includes a key for every variable declared or assigned to in the document,
88             the key is the name of the variable including its sigil.
89             If the document ends with a plain value or list that is not part of an assignment,
90             that value is saved in the "symbol table" hash with the key "C<_>" (a single underscore).
91              
92             For example, the string C<"$foo=123; $bar=456;"> will return the data structure
93             C<< { '$foo'=>123, '$bar'=>456 } >>, and the string C<"('foo','bar')"> will return the data
94             structure C<< { _=>["foo","bar"] } >>.
95              
96             Note that documents are currently always parsed in list context.
97             For example, this means that a document like "C<@foo = ("a","b","c"); @foo>"
98             will return the array's elements (C<"a","b","c">) instead of the item count (C<3>).
99             This also means that the special hash element "C<_>" will currently always be an arrayref.
100              
101             =head2 What is currently supported
102              
103             =over
104              
105             =item *
106              
107             plain scalars, arrays, hashes, lists
108              
109             =item *
110              
111             arrayrefs and hashrefs constructed via C<[]> and C<{}> resp.
112              
113             =item *
114              
115             declarations - only C, also C on the outermost level (document)
116             where it is treated exactly like C;
117             not supported are lexical C inside blocks, C or C
118              
119             =item *
120              
121             assignments (except the return value of assignments is not yet implemented)
122              
123             =item *
124              
125             simple array and hash subscripts (e.g. C<$x[1]>, C<$x[$y]>, C<$x{z}>, C<$x{"$y"}>)
126              
127             =item *
128              
129             very simple variable interpolations in strings (currently only C<"hello$world"> or C<"foo${bar}quz">)
130             and some escape sequences (e.g. C<"\x00">)
131              
132             =item *
133              
134             C blocks (contents limited to the supported features listed here)
135              
136             =back
137              
138             =head2 What is not supported (yet)
139              
140             I hope to achieve a balance where this module is useful, without becoming too much of a re-implementation of Perl.
141             I've labeled these items with "wishlist", "maybe", and "no", depending on whether I currently feel that
142             I'd like to support this feature in a later version, I'd consider supporting this feature if the need arises,
143             or I currently don't think the feature should be implemented.
144              
145             =over
146              
147             =item *
148              
149             lexical variables (C) (wishlist)
150              
151             =item *
152              
153             taking references via C<\> and dereferencing (C<@{...}>, C<%{...}>, etc.) (wishlist)
154              
155             =item *
156              
157             return values of assignments (e.g. C<$foo = do { $bar = "quz" }>) (maybe)
158              
159             =item *
160              
161             operators other than assignment (maybe; supporting a subset, like concatenation, is wishlist)
162              
163             =item *
164              
165             conditionals, like for example a very simple C (maybe)
166              
167             =item *
168              
169             any functions (mostly this is "no"; supporting a very small subset of functions, e.g. C, is "maybe")
170              
171             =item *
172              
173             anything that can't be resolved via a static parse (including Cs, many regexps, etc.) (no)
174              
175             =item *
176              
177             Note this list is not complete.
178              
179             =back
180              
181             =head1 Author, Copyright, and License
182              
183             Copyright (c) 2015 Hauke Daempfling (haukex@zero-g.net).
184              
185             This library is free software; you can redistribute it and/or modify
186             it under the same terms as Perl 5 itself.
187              
188             For more information see the L,
189             which should have been distributed with your copy of Perl.
190             Try the command "C" or see
191             L.
192              
193             =cut
194              
195 5     5   22 use Carp;
  5         15  
  5         272  
196 5     5   22 use warnings::register;
  5         6  
  5         614  
197 5     5   2656 use PPI ();
  5         862132  
  5         136  
198 5     5   2180 use PPI::Dumper ();
  5         4074  
  5         14015  
199              
200             sub new {
201 140     140 0 135549 my $class = shift;
202 140 100       603 croak "new currently takes no arguments" if @_;
203 139         414 my $self = {
204             errstr => undef,
205             out => undef,
206             ctx => undef, # Note: valid values for ctx currently "list", "scalar", "scalar-void"
207             };
208 139         505 return bless $self, $class;
209             }
210 2     2 0 12 sub errstr { return shift->{errstr} }
211              
212             #TODO: make _errmsg a little prettier?
213 15     15   66 sub _dump { return PPI::Dumper->new(shift,whitespace=>0,comments=>0,locations=>0)->string }
214 15     15   87 sub _errmsg { chomp(my $e=_dump(shift)); $e=~s/^/\t/mg; return "<<< $e >>>" }
  15         2798  
  15         1642  
215              
216             sub parse_or_undef { ## no critic (RequireArgUnpacking)
217 87     87 0 1358 my $self = shift;
218 87         104 my $out = eval { $self->parse_or_die(@_) };
  87         169  
219 87   100     536 my $errmsg = $@||"Unknown error";
220 87 100       163 $self->{errstr} = defined $out ? undef : $errmsg;
221 87         193 return $out;
222             }
223              
224             sub parse_or_die {
225 147     147 0 3197 my ($self,$input) = @_;
226             # PPI::Documents are not "complete" if they don't have a final semicolon, so tack on on there if it's missing
227 147 100 100     1143 $input = \"$$input;" if ref $input eq 'SCALAR' && $$input!~/;\s*$/;
228 147         550 $self->{doc} = my $doc = PPI::Document->new($input);
229 147   100     313301 my $errmsg = PPI::Document->errstr||"Unknown error";
230 147 100       1495 $doc or croak "Parse failed: $errmsg";
231 146 100       359 $doc->complete or croak "Document incomplete (missing final semicolon?)";
232 144         37187 $self->{ctx} = 'list';
233 144         219 $self->{out} = {};
234 144         341 my @rv = $self->_handle_block($doc, outer=>1);
235 126 100       280 $self->{out}{_} = \@rv if @rv;
236 126         319 return $self->{out};
237             }
238              
239             sub _handle_block { ## no critic (ProhibitExcessComplexity)
240 151     151   359 my ($self,$block,%param) = @_;
241 151 50 66     1104 confess "invalid \$block class"
242             unless $block->isa('PPI::Structure::Block') || $block->isa('PPI::Document');
243 151 100       344 return unless $block->schildren;
244 149         1847 my @rv;
245 149         309 my $el = $block->schild(0);
246 149         1485 ELEMENT: while ($el) {
247             # uncoverable branch true
248 295 50       3230 $el->isa('PPI::Statement') or croak "Unsupported element\n"._errmsg($el);
249 295         568 my @sc = $el->schildren;
250             # remove semicolons from statements
251 295 100 66     4019 if ( @sc && $sc[-1]->isa('PPI::Token::Structure') && $sc[-1]->content eq ';' )
      66        
252 286         1522 { pop(@sc)->delete }
253 295 50       8753 next ELEMENT unless @sc; # empty statement?
254             # last statement in block gets its context, otherwise void context
255 295 100       1564 local $self->{ctx} = $el->snext_sibling ? 'scalar-void' : $self->{ctx};
256 295         4963 my $is_assign; # remove this once _handle_assign return values implemented
257             # variable declaration
258 295 100 33     614 if ($el->class eq 'PPI::Statement::Variable') {
    100 33        
    50          
259             # note that Perl does not allow array or hash elements in declarations
260             # so we don't have to worry about subscripts here
261 65 100 100     287 croak "Unsupported declaration type \"".$el->type."\""
262             unless $el->type eq 'our' || $el->type eq 'my';
263 64 100 66     1947 croak "Lexical variables (\"my\") not supported" # I'd like to support "my" soon
      66        
264             unless $el->type eq 'our' || ($el->type eq 'my' && $param{outer});
265             # Note: Don't use $el->symbols, as that omits undefs on LHS!
266 63         1406 $self->_handle_assign($el,$sc[1],$sc[3]);
267 60         79 $is_assign=1;
268             }
269             elsif ($el->class eq 'PPI::Statement') {
270             # assignment, possibly with symbol+subscript on the RHS
271 226 100 100     3071 if ( (@sc==3||@sc==4) && $sc[1]->isa('PPI::Token::Operator') && $sc[1]->content eq '=' ) { ## no critic (ProhibitCascadingIfElse)
    100 100        
    100 66        
    100 100        
    100 66        
      100        
      100        
      66        
      66        
      66        
      100        
      66        
      66        
      66        
272 181         809 $self->_handle_assign($el,$sc[0],$sc[2]);
273 181         203 $is_assign=1;
274             }
275             # assignment assumed to have a symbol+subscript on the LHS
276             elsif ( (@sc==4||@sc==5) && $sc[2]->isa('PPI::Token::Operator') && $sc[2]->content eq '=' ) {
277 6         33 $self->_handle_assign($el,$sc[0],$sc[3]);
278 3         4 $is_assign=1;
279             }
280             # do-BLOCK
281             elsif ( @sc==2 && $sc[0]->isa('PPI::Token::Word') && $sc[0]->literal eq 'do'
282             && $sc[1]->isa('PPI::Structure::Block') ) {
283 5         98 my @tmprv = $self->_handle_block($sc[1]);
284 4 100       21 @rv = @tmprv unless $self->{ctx} eq 'scalar-void';
285             }
286             # single statements
287             elsif ( @sc==1 || (@sc==2 && $sc[0]->isa('PPI::Token::Symbol') && $sc[1]->isa('PPI::Structure::Subscript')) ) {
288 31         92 my @tmprv = $self->_handle_value($sc[0]);
289 24 100       124 @rv = @tmprv unless $self->{ctx} eq 'scalar-void';
290 24 100       545 warnings::warnif("value in void context") if $self->{ctx} eq 'scalar-void';
291             }
292             # push
293             elsif ( @sc>2 && $sc[0]->isa('PPI::Token::Word') && $sc[0]->literal eq 'push') {
294 1         42 croak "don't support push\n"._errmsg($el); # I'm considering supporting push
295             }
296 2         7 else { croak "Unsupported element\n"._errmsg($el) }
297             }
298             elsif ( $el->isa('PPI::Statement::Compound') && @sc==1 && $sc[0]->isa('PPI::Token::Label') ) {
299             # ignore labels
300             }
301 0         0 else { croak "Unsupported element ".$el->class." in\n"._errmsg($el) }
302 276 100 100     1325 if ($is_assign && $self->{ctx} ne 'scalar-void') {
303             # special case: the last statement of the outermost block
304             #TODO: Would it make sense to not error out on *any* assignment at the end of a block, not just the outermost one?
305 104 50 33     335 if ($param{outer} && !$el->snext_sibling)
306             {} # currently nothing; could warn here?
307             else
308 0         0 { croak "Assignment return values not implemented (current context is $$self{ctx}) in\n"._errmsg($el) }
309             }
310 276         2071 } continue { $el = $el->snext_sibling }
311 130         1800 return @rv;
312             }
313              
314             # returns nothing (yet)
315             sub _handle_assign {
316 250     250   306 my ($self,$as,$lhs,$rhs) = @_;
317 250 50 66     410 confess "invalid \$as class"
318             unless $as->class eq 'PPI::Statement' || $as->class eq 'PPI::Statement::Variable';
319             # Note we expect our caller to pick the correct $lhs and $rhs children from $as,
320             # and at the moment *some* call sites also already check the number of children.
321             # Possible To-Do for Later: Clean up the _handle_assign calling
322 250 100 66     1249 croak "bad assignment statement length in:\n"._errmsg($as)
323             if $as->schildren<3 || $as->schildren>5;
324            
325 249         5711 my $lhs_scalar;
326             my @lhs;
327 249 100       569 if ($lhs->isa('PPI::Token::Symbol')) {
    50          
328 240         423 @lhs = ($self->_handle_symbol($lhs));
329 237         401 $lhs_scalar = $lhs[0]->{atype} eq '$';
330             }
331             elsif ($lhs->isa('PPI::Structure::List')) {
332 9         102 local $self->{ctx} = 'list';
333 9         24 @lhs = $self->_handle_list($lhs,is_lhs=>1);
334             }
335 0         0 else { confess "invalid assignment LHS "._errmsg($lhs) } # uncoverable statement
336            
337 245 100       518 local $self->{ctx} = $lhs_scalar ? 'scalar' : 'list';
338 245         427 my @rhs = $self->_handle_value($rhs);
339            
340 244         1391 for my $l (@lhs) {
341 250 100       604 if (!defined($l)) ## no critic (ProhibitCascadingIfElse)
    100          
    100          
    50          
342 1         2 { shift @rhs }
343             elsif ($l->{atype} eq '$')
344 215         211 { ${ $l->{ref} } = shift @rhs }
  215         451  
345             elsif ($l->{atype} eq '@') {
346 20 100       21 if (!defined ${$l->{ref}})
  20         53  
347 18         34 { ${ $l->{ref} } = [@rhs] }
  18         24  
348             else
349 2         3 { @{ ${ $l->{ref} } } = @rhs }
  2         2  
  2         7  
350 20         33 last; # slurp
351             }
352             elsif ($l->{atype} eq '%') {
353 14 100       15 if (!defined ${$l->{ref}})
  14         29  
354 12         26 { ${ $l->{ref} } = {@rhs} }
  12         15  
355             else
356 2         3 { %{ ${ $l->{ref} } } = @rhs }
  2         2  
  2         6  
357 14         53 last; # slurp
358             }
359 0         0 else { confess "Possible internal error: can't assign to "._errmsg($l)." in\n"._errmsg($as) } # uncoverable statement
360             }
361 244         561 return;
362             }
363              
364             # returns a list (if param is_lhs is true, list will consist of only _handle_symbol return values)
365             sub _handle_list { ## no critic (ProhibitExcessComplexity)
366 110     110   168 my ($self,$outerlist,%param) = @_;
367             # NOTE this handles both () lists as well as the *contents* of {} and [] constructors
368 110 50       234 confess "outerlist is undef?" unless $outerlist;
369 110 50 66     554 confess "bad list class ".$outerlist->class
370             unless $outerlist->isa('PPI::Structure::List') || $outerlist->isa('PPI::Structure::Constructor');
371             # We should already have been placed in list context
372 110 50       614 confess "Internal error: Context is not list? Is \"$$self{ctx} \"at:\n"._errmsg($outerlist)
373             unless $self->{ctx}=~/^list\b/;
374 110 50 66     243 croak "can only handle a plain list on LHS"
375             if $param{is_lhs} && !$outerlist->isa('PPI::Structure::List');
376 110 50       297 return unless $outerlist->schildren; # empty list
377             # the first & only child of the outer list structure is a statement / expression
378 110         1320 my $act_list = $outerlist->schild(0);
379 110 50 66     1071 croak "Unsupported list\n"._errmsg($outerlist)
      33        
380             unless $outerlist->schildren==1 && ($act_list->isa('PPI::Statement::Expression') || $act_list->class eq 'PPI::Statement');
381 110 50       1837 return unless $act_list->schildren; # empty list?
382 110         1415 my @thelist;
383 110         115 my $expect = 'item';
384 110         219 my $el = $act_list->schild(0);
385 110         897 ELEMENT: while ($el) {
386 482 100       5483 if ($expect eq 'item') {
    50          
387 295         600 my $peek_next = $el->snext_sibling;
388 295   100     4556 my $fat_comma_next = $peek_next && $peek_next->isa('PPI::Token::Operator') && $peek_next->content eq '=>';
389 295 100       896 if ($param{is_lhs}) {
390 15 100 66     50 if ($el->isa('PPI::Token::Symbol'))
    100 66        
391 13         22 { push @thelist, $self->_handle_symbol($el) }
392             elsif (!$fat_comma_next && $el->isa('PPI::Token::Word') && $el->literal eq 'undef')
393 1         10 { push @thelist, undef }
394             else
395 1         3 { croak "Don't support this on LHS: "._errmsg($el) }
396             }
397             else {
398             # handle fat comma autoquoting words
399 280 100 100     1819 if ($fat_comma_next && $el->isa('PPI::Token::Word') && $el->literal=~/^\w+$/ )
    100 100        
400 29         349 { push @thelist, $el->literal }
401             elsif ($el->isa('PPI::Token::QuoteLike::Words')) # qw// in a list
402 2         9 { push @thelist, $el->literal } # here "literal" returns a list of words
403             else {
404 249         400 push @thelist, $self->_handle_value($el);
405             # special case of do followed by BLOCKs
406 249 50 100     2180 if ($el->isa('PPI::Token::Word') && $el->literal eq 'do'
      66        
      66        
407             && $peek_next && $peek_next->isa('PPI::Structure::Block'))
408 1         19 { $el = $el->snext_sibling } # this will have been handled by _handle_value
409             }
410             }
411             # special case of symbols followed by subscripts
412             # Possible To-Do for Later: More generalized handling of multi-element list items?
413             # Right now we have special handling of Symbol-Subscript and do-BLOCK pairs, if more special cases appear,
414             # we should figure out a more generalized way of advancing our list pointer over the handled elements.
415 294 100 100     1326 if ($el->isa('PPI::Token::Symbol') && $peek_next && $peek_next->isa('PPI::Structure::Subscript'))
      100        
416 5         10 { $el = $el->snext_sibling } # this will have been handled by _handle_symbol, called from _handle_value
417 294         424 $expect = 'separator';
418             }
419             elsif ($expect eq 'separator') {
420 187 50 66     691 croak "Expected list separator, got "._errmsg($el)
      33        
421             unless $el->isa('PPI::Token::Operator')
422             && ($el->content eq ',' || $el->content eq '=>');
423 187         1007 $expect = 'item';
424             }
425 0         0 else { confess "really shouldn't happen, bad state $expect" } # uncoverable statement
426 481         818 } continue { $el = $el->snext_sibling }
427 109         1783 return @thelist;
428             }
429              
430             # respects context and returns either a single value, or list if applicable
431             sub _handle_value { ## no critic (ProhibitExcessComplexity)
432 538     538   566 my ($self,$val) = @_;
433 538 50       1169 confess "\$val is false" unless $val;
434 538 100 100     2883 if ($val->isa('PPI::Token::Number')) ## no critic (ProhibitCascadingIfElse)
    100 100        
    100 66        
    100 100        
    100 66        
    100          
    100          
    100          
    100          
435 154         357 { return 0+$val->literal }
436             elsif ($val->isa('PPI::Token::Quote'))
437 233         371 { return $self->_handle_quote($val) }
438             elsif ($val->isa('PPI::Structure::Constructor'))
439 77         151 { return $self->_handle_struct($val) }
440             elsif ($val->isa('PPI::Token::Word') && $val->literal eq 'undef')
441 1         11 { return undef } ## no critic (ProhibitExplicitReturnUndef)
442             elsif ($val->isa('PPI::Token::Word') && $val->literal=~/^-\w+$/)
443 4         80 { return $val->literal }
444             elsif ($val->isa('PPI::Token::Symbol')) {
445 21         80 my $sym = $self->_handle_symbol($val);
446 18 100       58 if ($sym->{atype} eq '$') {
    100          
    100          
447 8         7 return ${ $sym->{ref} };
  8         22  
448             }
449             elsif ($sym->{atype} eq '@') {
450 1         3 return $self->{ctx}=~/^scalar\b/
451 1         2 ? scalar(@{ ${ $sym->{ref} } })
  4         43  
452 5 100       13 : @{ ${ $sym->{ref} } };
  4         6  
453             }
454             elsif ($sym->{atype} eq '%') {
455 1         6 return $self->{ctx}=~/^scalar\b/
456 1         1 ? scalar(%{ ${ $sym->{ref} } })
  3         20  
457 4 100       12 : %{ ${ $sym->{ref} } };
  3         3  
458             }
459 1         144 else { confess "bad symbol $sym" }
460             }
461             elsif ($val->isa('PPI::Token::Word') && $val->literal eq 'do'
462             && $val->snext_sibling && $val->snext_sibling->isa('PPI::Structure::Block'))
463 2         121 { return $self->_handle_block($val->snext_sibling) }
464             elsif ($val->isa('PPI::Structure::List')) {
465 24         1056 my @l = do {
466             # temporarily force list context to make _handle_list happy
467 24         40 local $self->{ctx} = 'list';
468 24         45 $self->_handle_list($val);
469             };
470 24 100       94 return $self->{ctx}=~/^scalar\b/ ? $l[-1] : @l;
471             }
472             elsif ($val->isa('PPI::Token::QuoteLike::Words')) { # qw//
473 20         58 my @l = $val->literal; # returns a list of words
474 20 100       209 return $self->{ctx}=~/^scalar\b/ ? $l[-1] : @l;
475             }
476 2         60 croak "Can't handle value "._errmsg($val);
477             }
478              
479             # returns a hashref representing the symbol (see code below for details)
480             sub _handle_symbol {
481 274     274   338 my ($self,$sym) = @_;
482 274 50       595 confess "bad symbol" unless $sym->isa('PPI::Token::Symbol');
483 274         501 my $peek_next = $sym->snext_sibling;
484 274         3718 my %rsym = ( name => $sym->symbol, atype => $sym->raw_type );
485 274 100 100     10482 if ($peek_next && $peek_next->isa('PPI::Structure::Subscript')) {
486 18         40 my $sub = $self->_handle_subscript($peek_next);
487 14 100 100     153 if ($sym->raw_type eq '$' && $sym->symbol_type eq '@' && $peek_next->braces eq '[]') {
    100 66        
      66        
      66        
488 10         655 $rsym{ref} = \( $self->{out}{$sym->symbol}[$sub] );
489 10         467 $rsym{sub} = "[$sub]";
490             }
491             elsif ($sym->raw_type eq '$' && $sym->symbol_type eq '%' && $peek_next->braces eq '{}') {
492 2         273 $rsym{ref} = \( $self->{out}{$sym->symbol}{$sub} );
493 2         93 $rsym{sub} = "{$sub}";
494             }
495 2         35 else { croak "Can't handle this subscript on this variable: "._errmsg($sym)._errmsg($peek_next) }
496             }
497             else {
498 256         579 $rsym{ref} = \( $self->{out}{$sym->symbol} );
499             }
500 268         6681 return \%rsym;
501             }
502              
503             # returns a single value
504             sub _handle_subscript {
505 18     18   26 my ($self,$sub) = @_;
506 18 50       49 confess "bad subscript" unless $sub->isa('PPI::Structure::Subscript');
507 18         50 my @sub_ch = $sub->schildren;
508 18 50 33     208 croak "Expected subscript to contain a single expression\n"._errmsg($sub)
509             unless @sub_ch==1 && $sub_ch[0]->isa('PPI::Statement::Expression');
510 18         45 my @subs = $sub_ch[0]->schildren;
511 18 100       102 croak "Expected subscript to contain a single value\n"._errmsg($sub)
512             unless @subs==1;
513             # autoquoting in hash braces
514 15 100 100     28 if ($sub->braces eq '{}' && $subs[0]->isa('PPI::Token::Word'))
515 2         27 { return $subs[0]->literal }
516             else {
517 13         104 local $self->{ctx} = 'scalar';
518 13         35 return $self->_handle_value($subs[0]);
519             }
520             }
521              
522             # returns arrayref or hashref
523             sub _handle_struct {
524 77     77   85 my ($self,$constr) = @_;
525 77 50       200 confess "bad struct class ".$constr->class
526             unless $constr->isa('PPI::Structure::Constructor');
527 77         123 local $self->{ctx} = 'list';
528 77 100       192 if ($constr->braces eq '[]')
    50          
529 30         237 { return [$self->_handle_list($constr)] }
530             elsif ($constr->braces eq '{}')
531 47         700 { return {$self->_handle_list($constr)} }
532 0         0 croak "Unsupported constructor\n"._errmsg($constr); # uncoverable statement
533             }
534              
535             # handles the known PPI::Token::Quote subclasses
536             # returns a single value
537             sub _handle_quote {
538 233     233   210 my ($self,$q) = @_;
539 233 100 100     1358 if ( $q->isa('PPI::Token::Quote::Single') || $q->isa('PPI::Token::Quote::Literal') )
    50 66        
540 133         331 { return $q->literal }
541             elsif ( $q->isa('PPI::Token::Quote::Double') || $q->isa('PPI::Token::Quote::Interpolate') )
542 100         185 { return $self->_handle_interpolate($q) }
543 0         0 confess "unknown PPI::Token::Quote subclass ".$q->class; # uncoverable statement
544             }
545             # for use in _handle_quote; does very limited string interpolation
546             # returns a single value
547             sub _handle_interpolate {
548 100     100   109 my ($self,$q) = @_;
549 100         268 my $str = $q->string;
550             # Perl (at least v5.20) doesn't allow trailing $, it does allow trailing @
551 100 100       789 croak "Final \$ should be \\\$ or \$name" if $str=~/\$$/;
552             # Variables
553 99         181 $str=~s{(?_fetch_interp_var($2)}eg;
  9         19  
554 99         110 $str=~s{(?_fetch_interp_var($2.$3)}eg;
  2         16  
555 99 100       263 croak "Don't support string interpolation of '$1' in '$str' at "._errmsg($q)
556             if $str=~/(?
557             # Backslash escape sequences
558 98         127 $str=~s{\\([0-7]{1,3}|x[0-9A-Fa-f]{2}|.)}{$self->_backsl($1)}eg;
  16         37  
559 97         250 return $str;
560             }
561             my %_backsl_tbl = ( '\\'=>'\\', '$'=>'$', '"'=>'"', "'"=>"'", 'n'=>"\n", 'r'=>"\r", 't'=>"\t" );
562             sub _backsl { # for use in _handle_interpolate ONLY
563 16     16   28 my ($self,$what) = @_;
564 16 100       56 return chr(oct($what)) if $what=~/^[0-7]+$/;
565 13 50       22 return chr(hex($1)) if $what=~/^x([0-9A-Fa-f]+)$/; ## no critic (ProhibitCaptureWithoutTest)
566 13 100       47 return $_backsl_tbl{$what} if exists $_backsl_tbl{$what};
567 1         103 croak "Don't support escape sequence \"\\$what\"";
568             }
569             sub _fetch_interp_var { # for use in _handle_interpolate ONLY
570 11     11   19 my ($self,$var) = @_;
571 11 100 100     79 return $self->{out}{$var}
572             if exists $self->{out}{$var} && defined $self->{out}{$var};
573 2         500 warnings::warnif("Use of uninitialized value $var in interpolation");
574 2         141 return "";
575             }
576              
577              
578             1;