File Coverage

blib/lib/Template/Perlish.pm
Criterion Covered Total %
statement 300 309 97.0
branch 116 148 78.3
condition 39 46 84.7
subroutine 51 51 100.0
pod 13 13 100.0
total 519 567 91.5


line stmt bran cond sub pod time code
1             package Template::Perlish;
2              
3             # vim: ts=3 sts=3 sw=3 et ai :
4              
5 16     16   949785 use 5.008_000;
  16         181  
6 16     16   743 use warnings;
  16         40  
  16         364  
7 16     15   151 use strict;
  15         36  
  15         277  
8 15     15   389 use Carp;
  15         43  
  15         957  
9 15     15   7045 use English qw( -no_match_vars );
  15         52907  
  15         83  
10 15     15   5219 use constant ERROR_CONTEXT => 3;
  15         39  
  15         1789  
11             { our $VERSION = '1.62'; }
12 15     15   153 use Scalar::Util qw< blessed reftype >;
  15         34  
  15         1710  
13              
14             # Function-oriented interface
15             sub import {
16 15     15   141 my ($package, @list) = @_;
17              
18 15         369 for my $sub (@list) {
19             croak "subroutine '$sub' not exportable"
20 6 50       21 unless grep { $sub eq $_ } qw< crumble render traverse >;
  16         48  
21              
22 6         65 my $caller = caller();
23              
24 15     15   428 no strict 'refs'; ## no critic (ProhibitNoStrict)
  15         35  
  15         3472  
25 6         37 local $SIG{__WARN__} = \&Carp::carp;
26 6         14 *{$caller . q<::> . $sub} = \&{$package . q<::> . $sub};
  6         376  
  6         28  
27             } ## end for my $sub (@list)
28              
29 15         240373 return;
30             } ## end sub import
31              
32             sub render {
33 19     19 1 11764 my ($template, @rest) = @_;
34 19         43 my ($variables, %params);
35 19 100       57 if (@rest) {
36 14 50       378 $variables = ref($rest[0]) ? shift(@rest) : {splice @rest, 0};
37 14 100       42 %params = %{shift @rest} if @rest;
  2         8  
38             }
39 19         158 return __PACKAGE__->new(%params)->process($template, $variables);
40             } ## end sub render
41              
42             # Object-oriented interface
43             {
44             my (%preset_for, %inhibits_defaults);
45             BEGIN {
46 15     15   194 %preset_for = (
47             'default' => {
48             method_over_key => 0,
49             start => '[%',
50             stdout => 1,
51             stop => '%]',
52             strict_blessed => 0,
53             traverse_methods => 0,
54             utf8 => 1,
55             },
56             '1.52' => {
57             method_over_key => 1,
58             stdout => 0,
59             traverse_methods => 1,
60             },
61             );
62              
63             # some defaults are inhibited by the presence of certain input
64             # parameters. These parameters can still be put externally, though.
65 15         40492 %inhibits_defaults = (
66             binmode => [qw< utf8 >],
67             );
68             }
69             sub new {
70 29     29 1 2534 my $package = shift;
71              
72 29         59 my %external;
73 29 50       488 if (@_ == 1) {
    50          
74 1         8 %external = %{$_[0]};
  1         2  
75             }
76             elsif (scalar(@_) % 2 == 0) {
77 29         256 while (@_) {
78 5         20 my ($key, $value) = splice @_, 0, 2;
79 5 50       17 if ($key eq '-preset') {
80             croak "invalid preset $value in new()"
81 1 0       320 unless exists $preset_for{$value};
82 1         6 %external = (%external, %{$preset_for{$value}});
  1         2  
83             }
84             else {
85 5         56 $external{$key} = $value;
86             }
87             }
88             }
89             else {
90 1         8 croak 'invalid number of input arguments for constructor';
91             }
92              
93             # compute defaults, removing inhibitions
94 29         55 my %defaults =(%{$preset_for{'default'}}, variables => {});
  29         547  
95 29         109 for my $inhibitor (keys %inhibits_defaults) {
96 29 100       191 next unless exists $external{$inhibitor};
97 2         53 delete $defaults{$_} for @{$inhibits_defaults{$inhibitor}};
  2         13  
98             }
99              
100 29         248 return bless {%defaults, %external}, $package;
101             } ## end sub new
102             }
103              
104             sub process {
105 74     74 1 43027 my ($self, $template, $vars) = @_;
106 74         257 return $self->evaluate($self->compile($template), $vars);
107             }
108              
109             sub evaluate {
110 74     74 1 225 my ($self, $compiled, $vars) = @_;
111             $self->_compile_sub($compiled)
112 74 50       259 unless exists $compiled->{sub};
113 74         2092 return $compiled->{sub}->($vars);
114             } ## end sub evaluate
115              
116             sub compile { ## no critic (RequireArgUnpacking)
117 77     77 1 214 my ($self, undef, %args) = @_;
118 77         527 my $outcome = $self->_compile_code_text($_[1]);
119 77 50       212 return $outcome if $args{no_check};
120 77         225 return $self->_compile_sub($outcome);
121             } ## end sub compile
122              
123             sub compile_as_sub { ## no critic (RequireArgUnpacking)
124 4     4 1 2173 my $self = shift;
125 4         23 return $self->compile($_[0])->{'sub'};
126             }
127              
128             sub _compile_code_text {
129 77     77   156 my ($self, $template) = @_;
130              
131 77         735 my $starter = $self->{start};
132 77         156 my $stopper = $self->{stop};
133              
134 77         122 my $compiled = "# line 1 'input'\n";
135 77 100       360 $compiled .= "use utf8;\n\n" if $self->{utf8};
136 77         150 $compiled .= "P('');\n\n";
137 77         130 my $pos = 0;
138 77         443 my $line_no = 1;
139 77         218 while ($pos < length $template) {
140              
141             # Find starter and emit all previous text as simple text
142 144         340 my $start = index $template, $starter, $pos;
143 144 100       410 last if $start < 0;
144 88         210 my $chunk = substr $template, $pos, $start - $pos;
145 88 100       247 $compiled .= _simple_text($chunk)
146             if $start > $pos;
147              
148             # Update scanning variables. The line counter is advanced for
149             # the chunk but not yet for the $starter, so that error reporting
150             # for unmatched $starter will point to the correct line
151 88         517 $pos = $start + length $starter;
152 88         197 $line_no += ($chunk =~ tr/\n//);
153              
154             # Grab code
155 88         181 my $stop = index $template, $stopper, $pos;
156 88 50       289 if ($stop < 0) { # no matching $stopper, bummer!
157 1         8 my $section = _extract_section({template => $template}, $line_no);
158 1         2 croak "unclosed starter '$starter' at line $line_no\n$section";
159             }
160 88         546 my $code = substr $template, $pos, $stop - $pos;
161              
162             # Now I can advance the line count considering the $starter too
163 88         151 $line_no += ($starter =~ tr/\n//);
164              
165 88 100       200 if (length $code) {
166 84 100       275 if (my $path = crumble($code)) {
    50          
    100          
167 33         129 $compiled .= _variable($path);
168             }
169             elsif (my ($scalar) =
170             $code =~ m{\A\s* (\$ [[:alpha:]_]\w*) \s*\z}mxs)
171             {
172 1         2 $compiled .=
173             "\nP($scalar); ### straight scalar\n\n";
174             } ## end elsif (my ($scalar) = $code...)
175             elsif (substr($code, 0, 1) eq q<=>) {
176 25         437 $compiled .= "\n# line $line_no 'template<3,$line_no>'\n"
177             . _expression(substr $code, 1);
178             }
179             else {
180 28         194 $compiled .=
181             "\n# line $line_no 'template<0,$line_no>'\n" . $code;
182             }
183             } ## end if (length $code)
184              
185             # Update scanning variables
186 88         187 $pos = $stop + length $stopper;
187 88         376 $line_no += (($code . $stopper) =~ tr/\n//);
188              
189             } ## end while ($pos < length $template)
190              
191             # put last part of input string as simple text
192 77   100     345 $compiled .= _simple_text(substr($template, $pos || 0));
193              
194             return {
195 77         333 template => $template,
196             code_text => $compiled,
197             };
198             } ## end sub _compile_code_text
199              
200             # The following function is long and complex because it deals with many
201             # different cases. It is kept as-is to avoid too many calls to other
202             # subroutines; for this reason, it's reasonably commented.
203             sub traverse { ## no critic (RequireArgUnpacking,ProhibitExcessComplexity)
204              
205             ## no critic (ProhibitDoubleSigils)
206 88     88 1 20392 my $iref = ref($_[0]);
207 88   100     401 my $ref_wanted = ($iref eq 'SCALAR') || ($iref eq 'REF');
208 88 100       219 my $ref_to_value = $ref_wanted ? shift : \shift;
209              
210             # early detection of options, remove them from args list
211 88 100 100     445 my $opts = (@_ && (ref($_[-1]) eq 'HASH')) ? pop(@_) : {};
212              
213             # if there's not $path provided, just don't bother going on. Actually,
214             # no $path means just return root, undefined path is always "not
215             # present" though.
216 88 100       221 return ($ref_wanted ? $ref_to_value : $$ref_to_value) unless @_;
    100          
217 86         136 my $path_input = shift;
218 86 0       516 return ($ref_wanted ? undef : '') unless defined $path_input;
    50          
219              
220 86         134 my $crumbs;
221 86 100       223 if (ref $path_input) {
222 57         138 $crumbs = $path_input;
223             }
224             else {
225 30 0 33     121 return ($ref_wanted ? $ref_to_value : $$ref_to_value)
    50          
226             if defined($path_input) && !length($path_input);
227 30         69 $crumbs = crumble($path_input);
228             }
229 86 0       774 return ($ref_wanted ? undef : '') unless defined $crumbs;
    50          
230              
231             # go down the rabbit hole
232 86   100     276 my $use_method = $opts->{traverse_methods} || 0;
233 86         212 my ($strict_blessed, $method_pre) = (0, 0);
234 86 100       249 if ($use_method) {
235 11   100     41 $strict_blessed = $opts->{strict_blessed} || 0;
236 11   100     40 $method_pre = (! $strict_blessed && $opts->{method_over_key}) || 0;
237             }
238 86         612 for my $crumb (@$crumbs) {
239              
240             # $key is what we will look into $$ref_to_value. We don't use
241             # $crumb directly as we might change $key in the loop, and we
242             # don't want to spoil $crumbs
243 170         264 my $key = $crumb;
244              
245             # $ref tells me how to look down into $$ref_to_value, i.e. as
246             # an ARRAY or a HASH... or object
247 170         403 my $ref = reftype $$ref_to_value;
248              
249             # if $ref is not true, we hit a wall. How we proceed depends on
250             # whether we were asked to auto-vivify or not.
251 170 100       361 if (!$ref) {
252 8 50       28 return (exists($opts->{missing}) ? $opts->{missing} : '')
    100          
253             unless $ref_wanted; # don't bother going on
254              
255             # auto-vivification requested! $key will tell us how to
256             # proceed further, hopefully
257 7         12 $ref = ref $key;
258             } ## end if (!$ref)
259              
260             # if $key is a reference, it will tell us what's expected now
261 169 100       682 if (my $key_ref = ref $key) {
262              
263             # if $key_ref is not the same as $ref there is a mismatch
264             # between what's available ($ref) and what' expected ($key_ref)
265 10 50       31 return($ref_wanted ? undef : '') if $key_ref ne $ref;
    100          
266              
267             # OK, data and expectations agree. Get the "real" key
268 9 50       22 if ($key_ref eq 'ARRAY') {
    50          
269 1         49 $key = $crumb->[0]; # it's an array, key is (only) element
270             }
271             elsif ($key_ref eq 'HASH') {
272 9         33 ($key) = keys %$crumb; # hash... key is (only) key
273             }
274             } ## end if (my $key_ref = ref ...)
275              
276             # if $ref is still not true at this point, we're doing
277             # auto-vivification and we have a plain key. Some guessing
278             # will be needed! Plain non-negative integers resolve to ARRAY,
279             # otherwise we'll consider $key as a HASH key
280 168 100 66     348 $ref ||= ($key =~ m{\A (?: 0 | [1-9]\d*) \z}mxs) ? 'ARRAY' : 'HASH';
281              
282             # time to actually do the next step
283 168         622 my $is_blessed = blessed $$ref_to_value;
284 168   100     378 my $method = $is_blessed && $$ref_to_value->can($key);
285 168 100 100     916 if ($is_blessed && $strict_blessed) {
    100 100        
    100 66        
    100 66        
    100 66        
    100          
    50          
286 2 50       58 return($ref_wanted ? undef : '') unless $method;
    50          
287 0         0 $ref_to_value = \($$ref_to_value->$method());
288             }
289             elsif ($method && $method_pre) {
290 2         8 $ref_to_value = \($$ref_to_value->$method());
291             }
292             elsif (($ref eq 'HASH') && exists($$ref_to_value->{$key})) {
293 134         330 $ref_to_value = \($$ref_to_value->{$key});
294             }
295             elsif (($ref eq 'ARRAY') && exists($$ref_to_value->[$key])) {
296 17         45 $ref_to_value = \($$ref_to_value->[$key]);
297             }
298             elsif ($method && $use_method) {
299 1         4 $ref_to_value = \($$ref_to_value->$method());
300             }
301             # autovivification goes here eventually
302             elsif ($ref eq 'HASH') {
303 10         34 $ref_to_value = \($$ref_to_value->{$key});
304             }
305             elsif ($ref eq 'ARRAY') {
306 2         6 $ref_to_value = \($$ref_to_value->[$key]);
307             }
308             else { # don't know what to do with other references!
309 0 0       0 return $ref_wanted ? undef : '';
310             }
311             } ## end for my $crumb (@$crumbs)
312              
313             # normalize output, substitute undef with '' unless $ref_wanted
314             return
315 82 100       1514 $ref_wanted ? $ref_to_value
    100          
316             : defined($$ref_to_value) ? $$ref_to_value
317             : '';
318              
319             ## use critic
320             } ## end sub traverse
321              
322 0     1 1 0 sub V { return '' }
323 0     1 1 0 sub A { return }
324 0     1 1 0 sub H { return }
325 0     1 1 0 sub HK { return }
326 0     1 1 0 sub HV { return }
327              
328             sub _compile_sub {
329 76     77   185 my ($self, $outcome) = @_;
330              
331 76         119 my @warnings;
332             {
333 76 100       117 my $utf8 = $self->{utf8} ? 1 : 0;
  76         217  
334 76 100       168 my $stdout = $self->{stdout} ? 1 : 0;
335 76     3   506 local $SIG{__WARN__} = sub { push @warnings, @_ };
  2         302  
336 76         198 my @code;
337 76         154 push @code, <<'END_OF_CODE';
338             sub {
339             my %variables = %{$self->{variables}};
340             my $V = \%variables; # generic kid, as before by default
341              
342             {
343             my $vars = shift || {};
344             if (ref($vars) eq 'HASH') { # old case
345             %variables = (%variables, %$vars);
346             }
347             else {
348             $V = $vars;
349             %variables = (HASH => { %variables }, REF => $V);
350             }
351             }
352              
353             my $buffer = ''; # output variable
354             my $OFH;
355             END_OF_CODE
356              
357 76         123 my $handle = '$OFH';
358 76 100       192 if ($stdout) {
359 67         97 $handle = 'STDOUT';
360 67         113 push @code, <<'END_OF_CODE';
361             local *STDOUT;
362             open STDOUT, '>', \$buffer or croak "open(): $OS_ERROR";
363             $OFH = select(STDOUT);
364             END_OF_CODE
365             }
366             else {
367 9         15 push @code, <<'END_OF_CODE';
368             open $OFH, '>', \$buffer or croak "open(): $OS_ERROR";
369             END_OF_CODE
370             }
371              
372 76 100       241 push @code, "binmode $handle, ':encoding(utf8)';\n"
373             if $utf8;
374             push @code, "binmode $handle, '$self->{binmode}';\n"
375 76 100       210 if defined $self->{binmode};
376              
377             # add functions that can be seen only within the compiled code
378 76         194 push @code, $self->_compile_code_localsubs($handle);
379              
380 76         146 push @code, <<'END_OF_CODE';
381             { # double closure to free "my" variables
382             my ($buffer, $OFH); # hide external ones
383             END_OF_CODE
384              
385             # the real code! one additional scope indentation to ensure we
386             # can "my" variables again
387             push @code,
388             "{\n", # this enclusure allows using "my" again
389             $outcome->{code_text},
390 76         165 "}\n}\n\n";
391              
392 76 100       172 push @code, "select(\$OFH);\n" if $stdout;
393 76         147 push @code, "close $handle;\n\n";
394              
395 76 100       151 if ($utf8) {
396 74         125 push @code, <<'END_OF_CODE';
397             require Encode;
398             $buffer = Encode::decode(utf8 => $buffer);
399              
400             END_OF_CODE
401             }
402              
403 76         115 push @code, "return \$buffer;\n}\n";
404              
405 76         452 my $code = join '', @code;
406             #print {*STDOUT} $code, "\n\n\n\n\n"; exit 0;
407 76     12   16544 $outcome->{sub} = eval $code; ## no critic (ProhibitStringyEval)
  11     12   93  
  11     12   31  
  11     12   4001  
  11     8   110  
  11     8   23  
  11     7   2195  
  11     7   70  
  11     6   23  
  11     6   428  
  11     6   65  
  11     6   86  
  11     6   104  
  7     6   62  
  7     5   17  
  7     4   2334  
  7     4   91  
  7     4   18  
  7     4   420  
  6     4   48  
  6         39  
  6         2052  
  6         44  
  6         15  
  6         379  
  5         38  
  5         12  
  5         1746  
  5         37  
  5         18  
  5         260  
  5         37  
  5         10  
  5         1744  
  5         37  
  5         17  
  5         269  
  5         38  
  5         11  
  5         1769  
  5         43  
  5         11  
  5         249  
  4         29  
  4         8  
  4         1584  
  4         31  
  4         8  
  4         204  
  4         28  
  4         9  
  4         1351  
  4         31  
  4         8  
  4         201  
  4         69  
  4         45  
  4         1381  
  4         28  
  4         7  
  4         204  
408 76 100       26364 return $outcome if $outcome->{sub};
409             }
410              
411 2         6 my $error = $EVAL_ERROR;
412 2         22 my ($offset, $starter, $line_no) =
413             $error =~ m{at[ ]'template<(\d+),(\d+)>'[ ]line[ ](\d+)}mxs;
414 2         6 $line_no -= $offset;
415 3         24 s{at[ ]'template<\d+,\d+>'[ ]line[ ](\d+)}
416 2         17 {'at line ' . ($1 - $offset)}egmxs
417 2 50       6 for @warnings, $error;
418             if ($line_no == $starter) {
419 0         0 s{,[ ]near[ ]"[#][ ]line.*?\n\s+}{, near "}gmxs
420             for @warnings, $error;
421             }
422 2         6  
423 2         36 my $section = _extract_section($outcome, $line_no);
424             $error = join '', @warnings, $error, "\n", $section;
425 2         251  
426             croak $error;
427             } ## end sub _compile_sub
428              
429 76     77   166 sub _compile_code_localsubs {
430 76         103 my ($self, $handle) = @_;
431 76         124 my @code;
432             push @code, <<'END_OF_CODE';
433              
434             no warnings 'redefine';
435              
436             END_OF_CODE
437              
438 76 100       202 # custom functions to be injected
439             if (defined(my $custom = $self->{functions})) {
440 1         4 push @code, map {
  2         8  
441             " local *$_ = \$self->{functions}{$_};\n"
442             } keys %$custom;
443             }
444              
445 76         122 # input data structure traversing facility
446             push @code, <<'END_OF_CODE';
447              
448             local *V = sub {
449             my $path = scalar(@_) ? shift : [];
450             my $input = scalar(@_) ? shift : $V;
451             return traverse($input, $path, $self);
452             };
453             local *A = sub {
454             my $path = scalar(@_) ? shift : [];
455             my $input = scalar(@_) ? shift : $V;
456             return @{traverse($input, $path, $self) || []};
457             };
458             local *H = sub {
459             my $path = scalar(@_) ? shift : [];
460             my $input = scalar(@_) ? shift : $V;
461             return %{traverse($input, $path, $self) || {}};
462             };
463             local *HK = sub {
464             my $path = scalar(@_) ? shift : [];
465             my $input = scalar(@_) ? shift : $V;
466             return keys %{traverse($input, $path, $self) || {}};
467             };
468             local *HV = sub {
469             my $path = scalar(@_) ? shift : [];
470             my $input = scalar(@_) ? shift : $V;
471             return values %{traverse($input, $path, $self) || {}};
472             };
473              
474             END_OF_CODE
475              
476 76         171 # this comes separated because we need $handle
477             push @code, <<"END_OF_CODE";
478             local *P = sub { return print $handle \@_; };
479              
480             use warnings 'redefine';
481              
482             END_OF_CODE
483 76         207  
484             return @code;
485             }
486              
487 2     3   4 sub _extract_section {
488 2         4 my ($hash, $line_no) = @_;
489 2         4 $line_no--; # for proper comparison with 0-based array
490 2         4 my $start = $line_no - ERROR_CONTEXT;
491             my $end = $line_no + ERROR_CONTEXT;
492 2         11  
493 2 100       6 my @lines = split /\n/mxs, $hash->{template};
494 2 100       6 $start = 0 if $start < 0;
495 2         5 $end = $#lines if $end > $#lines;
496             my $n_chars = length($end + 1);
497 2 100       5 return join '', map {
  11         51  
498             sprintf "%s%${n_chars}d| %s\n",
499             (($_ == $line_no) ? '>>' : ' '), ($_ + 1), $lines[$_];
500             } $start .. $end;
501             } ## end sub _extract_section
502              
503 140     141   368 sub _simple_text {
504             my $text = shift;
505 140 100       567  
506             return "P('$text');\n\n" if $text !~ /[\n'\\]/mxs;
507 106         517  
508 106         399 $text =~ s/^/ /gmxs; # indent, trick taken from diff -u
509             return <<"END_OF_CHUNK";
510             ### Verbatim text
511             P(do {
512             my \$text = <<'END_OF_INDENTED_TEXT';
513             $text
514             END_OF_INDENTED_TEXT
515             \$text =~ s/^ //gms; # de-indent
516             substr \$text, -1, 1, ''; # get rid of added newline
517             \$text;
518             });
519              
520             END_OF_CHUNK
521             } ## end sub _simple_text
522              
523 139     140 1 14371 sub crumble {
524 139 50       317 my ($input, $allow_partial) = @_;
525             return unless defined $input;
526 139         885  
527 139 100       328 $input =~ s{\A\s+|\s+\z}{}gmxs;
528             return [] unless length $input;
529 138         502  
530 138         383 my $sq = qr{(?mxs: ' [^']* ' )}mxs;
531 138         303 my $dq = qr{(?mxs: " (?:[^\\"] | \\.)* " )}mxs;
532 138         1675 my $ud = qr{(?mxs: \w+ )}mxs;
533             my $chunk = qr{(?mxs: $sq | $dq | $ud)+}mxs;
534              
535 138         321 # save and reset current pos() on $input
536 138         337 my $prepos = pos($input);
537             pos($input) = undef;
538 138         320  
539             my @path;
540 138         2416 ## no critic (RegularExpressions::ProhibitCaptureWithoutTest)
541             push @path, $1 while $input =~ m{\G [.]? ($chunk) }cgmxs;
542             ## use critic
543              
544 138         285 # save and restore pos() on $input
545 138         267 my $postpos = pos($input);
546             pos($input) = $prepos;
547 138 100       483  
548 111 100 100     586 return unless defined $postpos;
549             return if ($postpos != length($input)) && ! ($allow_partial);
550              
551 85         175 # cleanup @path components
552 151         262 for my $part (@path) {
553 151   100     642 my @subparts;
554 151 100       2147 while ((pos($part) || 0) < length($part)) {
    100          
    50          
555 21         98 if ($part =~ m{\G ($sq) }cgmxs) {
556             push @subparts, substr $1, 1, length($1) - 2;
557             }
558 16         47 elsif ($part =~ m{\G ($dq) }cgmxs) {
559 16         54 my $subpart = substr $1, 1, length($1) - 2;
560 16         59 $subpart =~ s{\\(.)}{$1}gmxs;
561             push @subparts, $subpart;
562             }
563 114         535 elsif ($part =~ m{\G ($ud) }cgmxs) {
564             push @subparts, $1;
565             }
566 0         0 else { # shouldn't happen ever
567             return;
568             }
569 151         470 } ## end while ((pos($part) || 0) ...)
570             $part = join '', @subparts;
571             } ## end for my $part (@path)
572 85 100 66     890  
573 83         393 return (\@path, $postpos) if $allow_partial && wantarray;
574             return \@path;
575             } ## end sub crumble
576              
577 32     33   54 sub _variable {
578 32         56 my $path = shift;
579 32         59 my $DQ = q<">; # double quotes
  48         175  
  32         76  
580             $path = join ', ', map { $DQ . quotemeta($_) . $DQ } @{$path};
581 32         164  
582             return <<"END_OF_CHUNK";
583             ### Variable from the stash (\$V)
584             P(V([$path]));
585              
586             END_OF_CHUNK
587             } ## end sub _variable
588              
589 24     25   67 sub _expression {
590 24         95 my $expression = shift;
591             return <<"END_OF_CHUNK";
592             # Expression to be evaluated and printed out
593             {
594             my \$value = do {{
595             $expression
596             }};
597             P(\$value) if defined \$value;
598             }
599              
600             END_OF_CHUNK
601              
602             } ## end sub _expression
603              
604             1;