File Coverage

blib/lib/Template/Perlish.pm
Criterion Covered Total %
statement 300 309 97.0
branch 115 146 78.7
condition 39 46 84.7
subroutine 51 51 100.0
pod 13 13 100.0
total 518 565 91.6


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