File Coverage

blib/lib/Template/Parser/CET.pm
Criterion Covered Total %
statement 294 495 59.3
branch 123 254 48.4
condition 25 92 27.1
subroutine 38 61 62.3
pod 2 49 4.0
total 482 951 50.6


line stmt bran cond sub pod time code
1             package Template::Parser::CET;
2              
3             ###----------------------------------------------------------------###
4             # Copyright 2007 - Paul Seamons #
5             # Distributed under the Perl Artistic License without warranty #
6             ###----------------------------------------------------------------###
7              
8 5     5   132515 use vars qw($VERSION $TEMP_VARNAME $ORIG_CONFIG_CLASS $NO_LOAD_EXTRA_VMETHODS);
  5         13  
  5         443  
9 5     5   34 use strict;
  5         11  
  5         199  
10 5     5   36 use warnings;
  5         9  
  5         178  
11 5     5   27 use base qw(Template::Alloy);
  5         9  
  5         5353  
12              
13 5     5   159590 use Template::Alloy 1.008;
  5         149  
  5         39  
14 5     5   163 use Template::Alloy::Operator qw($OP_ASSIGN $OP_DISPATCH);
  5         9  
  5         712  
15 5     5   5434 use Template::Directive;
  5         26968  
  5         183  
16 5     5   48 use Template::Constants;
  5         9  
  5         273  
17              
18             BEGIN {
19 5     5   13 $VERSION = '0.05';
20              
21 5         44755 $TEMP_VARNAME = 'template_parser_cet_temp_varname';
22             };
23              
24             ###----------------------------------------------------------------###
25              
26             sub new {
27 698     698 1 285187 my $class = shift;
28 698         2936 my $self = $class->SUPER::new(@_);
29              
30 698   50     11508 $self->{'FACTORY'} ||= 'Template::Directive';
31              
32             # This debug section taken nearly verbatim from Template::Parser::new
33             # DEBUG config item can be a bitmask
34 698 50       1899 if (defined (my $debug = $self->{'DEBUG'})) {
35 0         0 $self->{ DEBUG } = $debug & ( Template::Constants::DEBUG_PARSER
36             | Template::Constants::DEBUG_FLAGS );
37 0         0 $self->{ DEBUG_DIRS } = $debug & Template::Constants::DEBUG_DIRS;
38             }
39              
40             # This factory section is taken nearly verbatim from Template::Parser::new
41 698 50       1595 if ($self->{'NAMESPACE'}) {
42 0         0 my $fclass = $self->{'FACTORY'};
43 0   0     0 $self->{'FACTORY'} = $fclass->new(NAMESPACE => $self->{'NAMESPACE'} )
44             || return $class->error($fclass->error());
45             }
46              
47 698         1731 return $self;
48             }
49              
50             ###----------------------------------------------------------------###
51             ### methods for installing
52              
53             sub activate {
54 7     7 0 1574 require Template::Config;
55 7 50 33     40 if (! $ORIG_CONFIG_CLASS || $ORIG_CONFIG_CLASS ne $Template::Config::PARSER) {
56 7         17 $ORIG_CONFIG_CLASS = $Template::Config::PARSER;
57 7         16 $Template::Config::PARSER = __PACKAGE__;
58             }
59 7         46 1;
60             }
61              
62             sub deactivate {
63 3 50   3 0 865 if ($ORIG_CONFIG_CLASS) {
64 3         6 $Template::Config::PARSER = $ORIG_CONFIG_CLASS;
65 3         7 $ORIG_CONFIG_CLASS = undef;
66             }
67 3         12 1;
68             }
69              
70             sub import {
71 6     6   525 my ($class, @args) = @_;
72 6 50       35 push @args, 1 if @args % 2;
73 6         18 my %args = @args;
74 6 100       25 $class->activate if $args{'activate'};
75 6 50       24 $class->deactivate if $args{'deactivate'};
76 6         229 1;
77             }
78              
79             ###----------------------------------------------------------------###
80             ### parse the document and return a valid compiled Template::Document
81              
82             sub parse {
83 735     735 1 89479 my ($self, $text, $info) = @_;
84 735         820 my ($tokens, $block);
85              
86 735         832 eval { require Template::Stash };
  735         3783  
87 735         1107 local $Template::Alloy::QR_PRIVATE = $Template::Stash::PRIVATE;
88 735 50 50     4331 local $self->{'_debug'} = defined($info->{'DEBUG'}) ? $info->{'DEBUG'} : $self->{'DEBUG_DIRS'} || undef;
89 735         1526 local $self->{'DEFBLOCK'} = {};
90 735         1585 local $self->{'METADATA'} = [];
91 735         3356 local $self->{'_component'} = {
92             _content => \$text,
93             name => $info->{'name'},
94             modtime => $info->{'time'},
95             };
96              
97             ### parse to the AST
98 735         1040 my $tree = eval { $self->parse_tree(\$text) }; # errors die
  735         2550  
99 735 100       382014 if (! $tree) {
100 19         29 my $err = $@;
101 19 50 33     141 $err->doc($self->{'_component'}) if UNIVERSAL::can($err, 'doc') && ! $err->doc;
102 19         532 die $err;
103             }
104              
105             ### take the AST to the doc
106 716         2157 my $doc = $self->{'FACTORY'}->template($self->compile_tree($tree));
107             # print $doc;
108              
109             return {
110 714         7621 BLOCK => $doc,
111             DEFBLOCKS => $self->{'DEFBLOCK'},
112 714         11043 METADATA => { @{ $self->{'METADATA'} } },
113             };
114             }
115              
116             ###----------------------------------------------------------------###
117              
118             ### takes a tree of DIRECTIVES
119             ### and returns a TT block
120             sub compile_tree {
121 837     837 0 1395 my ($self, $tree) = @_;
122              
123             # node contains (0: DIRECTIVE,
124             # 1: start_index,
125             # 2: end_index,
126             # 3: parsed tag details,
127             # 4: sub tree for block types
128             # 5: continuation sub trees for sub continuation block types (elsif, else, etc)
129             # 6: flag to capture next directive
130 837         1006 my @doc;
131 837         1454 for my $node (@$tree) {
132              
133             # text nodes are just the bare text
134 1392 100       3517 if (! ref $node) {
135 298         1268 my $result = $self->{'FACTORY'}->textblock($node);
136 298 50       5331 push @doc, $result if defined $result;
137 298         839 next;
138             }
139              
140             # add debug info
141 1094 50       2470 if ($self->{'_debug'}) {
142 0         0 my $info = $self->node_info($node);
143 0         0 my ($file, $line, $text) = @{ $info }{qw(file line text) };
  0         0  
144 0         0 s/([\'\\])/\\$1/g for $file, $text;
145 0         0 my $result = $self->{'FACTORY'}->debug([["'msg'"],[["file => '$file'", "line => $line", "text => '$text'"]]]);
146 0 0       0 push @doc, $result if defined $result;
147             }
148              
149             # get method to call
150 1094         1701 my $directive = $node->[0];
151 1094 50       2232 $directive = 'FILTER' if $directive eq '|';
152 1094 50       2181 next if $directive eq '#';
153 1094         1873 my $method = "compile_$directive";
154 1094         3412 my $result = $self->$method($node->[3], $node);
155 1092 100       15931 push @doc, $result if defined $result;
156             }
157              
158 835         3297 return $self->{'FACTORY'}->block(\@doc);
159             }
160              
161             ###----------------------------------------------------------------###
162              
163             ### take arguments parsed in parse_args({named_at_front => 1})
164             ### and turn them into normal TT2 style args
165             sub compile_named_args {
166 25     25 0 48 my $self = shift;
167 25         48 my $args = shift;
168 25         57 my ($named, @positional) = @$args;
169              
170             # [[undef, '{}', 'key1', 'val1', 'key2', 'val2'], 0]
171 25         31 my @named;
172 25         43 $named = $named->[0];
173 25         57 my (undef, $op, @the_rest) = @$named;
174 25         69 while (@the_rest) {
175 4         10 my $key = shift @the_rest;
176 4 50       19 my $val = @the_rest ? $self->compile_expr(shift @the_rest) : 'undef';
177 4 0 33     19 $key = $key->[0] if ref($key) && @$key == 2 && ! ref $key->[0]; # simple keys can be set in place
      33        
178 4 50       11 if (! ref $key) {
179 4         11 $key = $self->compile_expr($key);
180 4         21 push @named, "$key => $val";
181             } else {
182             ### this really is the way TT does it - pseudo assignment into a hash
183             ### with a key that gets thrown away - but "getting" the value assigns into the stash
184             ### scary and gross
185 0         0 push @named, "'_' => ".$self->compile_expr($key, $val);
186             }
187             }
188              
189 25         61 return [\@named, (map { $self->compile_expr($_) } @positional)];
  25         71  
190             }
191              
192             ### takes variables or expressions and translates them
193             ### into the language that compiled TT templates understand
194             ### it will recurse as deep as the expression is deep
195             ### foo : 'foo'
196             ### ['foo', 0] : $stash->get('foo')
197             ### ['foo', 0] = ['bar', 0] : $stash->set('foo', $stash->get('bar'))
198             ### [[undef, '+', 1, 2], 0] : do { no warnings; 1 + 2 }
199             sub compile_expr {
200 2175     2175 0 4809 my ($self, $var, $val, $default) = @_;
201 2175         2885 my $ARGS = {};
202 2175         2659 my $i = 0;
203 2175         3374 my $return_ref = delete $self->{'_return_ref_ident'}; # set in compile_operator
204              
205             ### return literals
206 2175 100       4551 if (! ref $var) {
207 829 50       1820 if ($val) { # allow for bare literal setting [% 'foo' = 'bar' %]
208 0         0 $var = [$var, 0];
209             } else {
210 829 100       4431 return $var if $var =~ /^-?[1-9]\d{0,13}(?:|\.0|\.\d{0,13}[1-9])$/; # return unquoted numbers if it is simple
211 404         544 $var =~ s/\'/\\\'/g;
212 404         2301 return "'$var'"; # return quoted items - if they are simple
213             }
214             }
215              
216             ### determine the top level of this particular variable access
217 1346         1374 my @ident;
218 1346         2206 my $name = $var->[$i++];
219 1346         1689 my $args = $var->[$i++];
220 1346         1757 my $use_temp_varname;
221 1346 100       2875 if (ref $name) {
    50          
222 429 100       1070 if (! defined $name->[0]) { # operator
223 384         1032 my $op_val = '('. $self->compile_operator($name) .')';
224 384 100       2780 return $op_val if $i >= @$var;
225 51         426 $use_temp_varname = "do {\n ".$self->{'FACTORY'}->assign(["'$TEMP_VARNAME'", 0], $op_val).";\n ";
226 51         728 push @ident, "'$TEMP_VARNAME'";
227             } else { # a named variable access (ie via $name.foo)
228 45         92 push @ident, $self->compile_expr($name);
229             }
230             } elsif (defined $name) {
231 917 50       1627 if ($ARGS->{'is_namespace_during_compile'}) {
232             #$ref = $self->{'NAMESPACE'}->{$name};
233             } else {
234 917         1300 $name =~ s/\'/\\\'/g;
235 917         2317 push @ident, "'$name'";
236             }
237             } else {
238 0         0 return '';
239             }
240              
241             ### add args
242 1013 100       2400 if (! $args) {
243 989         1212 push @ident, 0;
244             } else {
245 24         67 push @ident, ("[" . join(",\n", map { $self->compile_expr($_) } @$args) . "]");
  24         68  
246             }
247              
248             ### now decent through the other levels
249 1013         2456 while ($i < @$var) {
250             ### descend one chained level
251 491 50       1359 my $was_dot_call = $ARGS->{'no_dots'} ? 1 : $var->[$i++] eq '.';
252 491         924 $name = $var->[$i++];
253 491         679 $args = $var->[$i++];
254              
255 491 100       787 if ($was_dot_call) {
256 438 100       978 if (ref $name) {
    50          
257 10 50       22 if (! defined $name->[0]) { # operator
258 0         0 push @ident, '('. $self->compile_operator($name) .')';
259             } else { # a named variable access (ie via $name.foo)
260 10         25 push @ident, $self->compile_expr($name);
261             }
262             } elsif (defined $name) {
263 428 50       696 if ($ARGS->{'is_namespace_during_compile'}) {
264             #$ref = $self->{'NAMESPACE'}->{$name};
265             } else {
266 428         564 $name =~ s/\'/\\\'/g;
267 428         927 push @ident, "'$name'";
268             }
269             } else {
270 0         0 return '';
271             }
272              
273 438 100       864 if (! $args) {
274 328         1032 push @ident, 0;
275             } else {
276 110         213 push @ident, ("[" . join(",\n", map { $self->compile_expr($_) } @$args) . "]");
  156         341  
277             }
278              
279             # chained filter access
280             } else {
281             # resolve and cleanup the name
282 53 100       173 if (ref $name) {
    50          
283 2 50       7 if (! defined $name->[0]) { # operator
284 0         0 $name = '('. $self->compile_operator($name) .')';
285             } else { # a named variable access (ie via $name.foo)
286 2         7 $name = $self->compile_expr($name);
287             }
288             } elsif (defined $name) {
289 51 50       106 if ($ARGS->{'is_namespace_during_compile'}) {
290             #$ref = $self->{'NAMESPACE'}->{$name};
291             } else {
292 51         95 $name =~ s/\'/\\\'/g;
293 51         105 $name = "'$name'";
294             }
295             } else {
296 0         0 return '';
297             }
298              
299             # get the ident to operate on
300 53         120 my $ident;
301 53 100       105 if ($use_temp_varname) {
302 23         316 $ident = $use_temp_varname
303             ."my \$val = ".$self->{'FACTORY'}->ident(\@ident).";\n "
304             .$self->{'FACTORY'}->assign(["'$TEMP_VARNAME'", 0], 'undef').";\n "
305             ."\$val; # return of the do\n }";
306             } else {
307 30         151 $ident = $self->{'FACTORY'}->ident(\@ident);
308             }
309              
310             # get args ready
311 53 100       1112 my $filter_args = $args ? [[], map {$self->compile_expr($_)} @$args] : [[]];
  6         18  
312              
313             # return the value that is able to run the filter
314 53         127 my $block = "\$output = $ident;";
315 53         284 my $filt_val = "do { my \$output = '';\n". $self->{'FACTORY'}->filter([[$name], $filter_args], $block) ." \$output;\n }";
316 53         1222 $use_temp_varname = "do {\n ".$self->{'FACTORY'}->assign(["'$TEMP_VARNAME'", 0], $filt_val).";\n ";
317              
318 53         807 @ident = ("'$TEMP_VARNAME'", 0);
319             }
320             }
321              
322             # handle captures
323 1013 100       3908 if ($self->{'_return_capture_ident'}) {
    50          
    100          
    100          
324 2 50       8 die "Can't capture to a variable with filters (@ident)" if $use_temp_varname;
325 2 50       8 die "Can't capture to a variable with a set value" if $val;
326 2         10 return \@ident;
327              
328             # handle refence getting
329             } elsif ($return_ref) {
330 0 0       0 die "Can't get reference to a variable with filters (@ident)" if $use_temp_varname;
331 0 0       0 die "Can't get reference to a variable with a set value" if $val;
332 0         0 return $self->{'FACTORY'}->identref(\@ident);
333              
334             # handle setting values
335             } elsif ($val) {
336 197         1390 return $self->{'FACTORY'}->assign(\@ident, $val, $default);
337              
338             # handle inline filters
339             } elsif ($use_temp_varname) {
340 81         417 return $use_temp_varname
341             ."my \$val = ".$self->{'FACTORY'}->ident(\@ident).";\n "
342             .$self->{'FACTORY'}->assign(["'$TEMP_VARNAME'", 0], 'undef').";\n "
343             ."\$val; # return of the do\n }";
344              
345             # finally - normal getting
346             } else {
347 733         3103 return $self->{'FACTORY'}->ident(\@ident);
348             }
349             }
350              
351             ### plays operators
352             ### [[undef, '+', 1, 2], 0] : do { no warnings; 1 + 2 }
353             ### unfortunately we had to provide a lot of perl
354             ### here ourselves which means that Jemplate can't
355             ### use this parser directly without overriding this method
356             sub compile_operator {
357 384     384 0 496 my $self = shift;
358 384         448 my $args = shift;
359 384         929 my (undef, $op, @the_rest) = @$args;
360 384         613 $op = lc $op;
361              
362 384 50       1181 $op = ($op eq 'mod') ? '%'
    50          
363             : ($op eq 'pow') ? '**'
364             : $op;
365              
366 384 100 100     4437 if ($op eq '{}') {
    100 66        
    100 66        
    100          
    100          
    100          
    100          
    100          
    50          
    100          
    100          
    100          
367 30 100       81 return '{}' if ! @the_rest;
368 29         52 my $out = "{\n";
369 29         77 while (@the_rest) {
370 34         91 my $key = $self->compile_expr(shift @the_rest);
371 34 50       183 my $val = @the_rest ? $self->compile_expr(shift @the_rest) : 'undef';
372 34         186 $out .= " $key => $val,\n";
373             }
374 29         37 $out .= "}";
375 29         93 return $out;
376             } elsif ($op eq '[]') {
377 43         106 return "[".join(",\n ", (map { $self->compile_expr($_) } @the_rest))."]";
  59         164  
378             } elsif ($op eq '~' || $op eq '_') {
379 42         72 return "(''.". join(".\n ", map { $self->compile_expr($_) } @the_rest).")";
  57         181  
380             } elsif ($op eq '=') {
381 16         38 return $self->compile_expr($the_rest[0], $self->compile_expr($the_rest[1]));
382              
383             } elsif ($op eq '++') {
384 3   100     12 my $is_postfix = $the_rest[1] || 0; # set to 1 during postfix
385 3         11 return "do { no warnings;\nmy \$val = 0 + ".$self->compile_expr($the_rest[0]).";\n"
386             .$self->compile_expr($the_rest[0], "\$val + 1").";\n"
387             ."$is_postfix ? \$val : \$val + 1;\n}";
388              
389             } elsif ($op eq '--') {
390 3   100     14 my $is_postfix = $the_rest[1] || 0; # set to 1 during postfix
391 3         11 return "do { no warnings;\nmy \$val = 0 + ".$self->compile_expr($the_rest[0]).";\n"
392             .$self->compile_expr($the_rest[0], "\$val - 1").";\n"
393             ."$is_postfix ? \$val : \$val - 1;\n}";
394              
395             } elsif ($op eq 'div' || $op eq 'DIV') {
396 1         4 return "do { no warnings;\n int(".$self->compile_expr($the_rest[0])." / ".$self->compile_expr($the_rest[1]).")}";
397              
398             } elsif ($op eq '?') {
399 23         60 return "do { no warnings;\n " .$self->compile_expr($the_rest[0])
400             ." ? ".$self->compile_expr($the_rest[1])
401             ." : ".$self->compile_expr($the_rest[2])." }";
402              
403             } elsif ($op eq '\\') {
404 0         0 return do { local $self->{'_return_ref_ident'} = 1; $self->compile_expr($the_rest[0]) };
  0         0  
  0         0  
405              
406             } elsif ($op eq 'qr') {
407 1 50       7 return $the_rest[1] ? "qr{(?$the_rest[1]:$the_rest[0])}" : "qr{$the_rest[0]}";
408              
409             } elsif (@the_rest == 1) {
410 14         51 return $op.$self->compile_expr($the_rest[0]);
411             } elsif ($op eq '//' || $op eq 'err') {
412 65         161 return "do { my \$var = ".$self->compile_expr($the_rest[0])."; defined(\$var) ? \$var : ".$self->compile_expr($the_rest[1])."}";
413             } else {
414 143         414 return "do { no warnings; ".$self->compile_expr($the_rest[0])." $op ".$self->compile_expr($the_rest[1])."}";
415             }
416             }
417              
418             ### takes an already parsed identity
419             ### and strips it of args and outputs a string
420             ### so that the passing mechanism of Template::Directive
421             ### can hand off to set or get which will reparse again - wow and sigh
422             sub compile_ident_str_from_cet {
423 19     19 0 21 my ($self, $ident) = @_;
424 19 50       35 return '' if ! defined $ident;
425 19 50       37 return $ident if ! ref $ident;
426 19 50 33     84 return '' if ref $ident->[0] || ! defined $ident->[0];
427              
428 19         22 my $i = 0;
429 19         25 my $str = $ident->[$i++];
430 19         18 $i++; # for args;
431              
432 19         43 while ($i < @$ident) {
433 0         0 my $dot = $ident->[$i++];
434 0 0       0 return $str if $dot ne '.';
435 0 0 0     0 return $str if ref $ident->[$i] || ! defined $ident->[$i];
436 0         0 $str .= ".". $ident->[$i++];
437 0         0 $i++; # for args
438             }
439 19         82 return $str;
440             }
441              
442             ###----------------------------------------------------------------###
443             ### everything in this section are the output of DIRECTIVES - as much as possible we
444             ### try to use the facilities provided by Template::Directive
445              
446             sub compile_BLOCK {
447 9     9 0 20 my ($self, $name, $node) = @_;
448 9         40 $self->{'DEFBLOCK'}->{$name} = $self->{'FACTORY'}->template($self->compile_tree($node->[4]));
449 9         176 return '';
450             }
451              
452 0     0 0 0 sub compile_BREAK { shift->{'FACTORY'}->break }
453              
454             sub compile_CALL {
455 1     1 0 1 my ($self, $ident) = @_;
456 1         4 return $self->{'FACTORY'}->call($self->compile_expr($ident));
457             }
458              
459             sub compile_CLEAR {
460 0     0 0 0 my $self = shift;
461 0         0 return $self->{'FACTORY'}->clear;
462             }
463              
464 2     2 0 4 sub compile_COMMENT {}
465              
466             sub compile_CONFIG {
467 0     0 0 0 my ($self, $config) = @_;
468              
469             ### prepare runtime config - not many options get these
470 0         0 my ($named, @the_rest) = @$config;
471 0         0 $named = $self->compile_named_args([$named])->[0];
472 0         0 $named = join ",", @$named;
473              
474             ### show what current values are
475 0         0 my $items = join ",", map { s/\\([\'\$])/$1/g; "'$_'" } @the_rest;
  0         0  
  0         0  
476              
477 0         0 my $get = $self->{'FACTORY'}->get($self->{'FACTORY'}->ident(["'$TEMP_VARNAME'", 0]));
478 0         0 return <
479             do {
480             my \$conf = \$context->{'CONFIG'} ||= {};
481             my \$newconf = {$named};
482             \$conf->{\$_} = \$newconf->{\$_} foreach keys %\$newconf;
483              
484             my \@items = ($items);
485             if (\@items) {
486             my \$str = join("\n", map { /(^[A-Z]+)\$/ ? ("CONFIG \$_ = ".(defined(\$conf->{\$_}) ? \$conf->{\$_} : 'undef')) : \$_ } \@items);
487             \$stash->set(['$TEMP_VARNAME', 0], \$str);
488             $get;
489             \$stash->set(['$TEMP_VARNAME', 0], '');
490             }
491             };
492             EOF
493             }
494              
495             sub compile_DEBUG {
496 0     0 0 0 my ($self, $ref) = @_;
497 0         0 my @options = "'$ref->[0]'";
498 0 0       0 if ($ref->[0] eq 'format') {
    0          
499 0         0 my $format = $ref->[1];
500 0         0 $format =~ s/([\'\\])/\\$1/g;
501 0         0 push @options, "'$format'";
502             } elsif (defined $self->{'_debug'}) { # defined if on at beginning
503 0 0       0 if ($ref->[0] eq 'on') {
    0          
504 0         0 $self->{'_debug'} = 1;
505             } elsif ($ref->[0] eq 'off') {
506 0         0 $self->{'_debug'} = 0;
507             }
508             }
509 0         0 return $self->{'FACTORY'}->debug([\@options, [[]]]);
510             }
511              
512             sub compile_DEFAULT {
513 3     3 0 4 my ($self, $set, $node) = @_;
514 3         8 return $self->compile_SET($set, $node, 1);
515             }
516              
517             sub compile_DUMP {
518 0     0 0 0 my ($self, $dump, $node) = @_;
519 0         0 my $info = $self->node_info($node);
520              
521             ### This would work if the DUMP patch was accepted. It wasn't because of concerns about the size of the Grammar table
522             # return $self->{'FACTORY'}->dump($self->compile_named_args($dump), $info->{'file'}, $info->{'line'}, \$info->{'text'});
523              
524             ### so we'll inline the method here
525              
526 0         0 my $args = $self->compile_named_args($dump);
527 0         0 my $_file = $info->{'file'};
528 0         0 my $_line = $info->{'line'};
529 0         0 my $_text = $info->{'text'};
530              
531             # add on named arguments as a final hashref
532 0         0 my $named = shift @$args;
533 0 0       0 push @$args, "{\n " . join(",\n ", @$named) . ",\n },\n" if @$named;
534              
535             # prepare arguments to pass to Dumper
536 0 0       0 my $_args = (@$args > 1) ? "[\n ". join(",\n ", @$args) .",\n ]" # treat multiple args as a single arrayref to help name align
    0          
537             : (@$args > 0) ? $args->[0] # treat single item as a single item
538             : '$stash'; # treat entire stash as one item
539              
540             # find the name of the variables being dumped
541 0 0       0 my $is_entire = ! @$args ? 1 : 0;
542 0 0       0 my $_name = $is_entire ? 'EntireStash' : $_text;
543 0         0 $_name =~ s/^.*?\bDUMP\s*//;
544 0         0 s/\'/\\\'/g for $_name, $_file;
545              
546 0         0 my $get = $self->{'FACTORY'}->get($self->{'FACTORY'}->ident(["'$TEMP_VARNAME'", 0]));
547              
548 0         0 return <
549             do {
550             # DUMP
551             require Template::Parser::CET;
552             \$stash->set(['$TEMP_VARNAME', 0], Template::Parser::CET->play_dump({
553             context => \$context,
554             file => '$_file',
555             line => $_line,
556             name => '$_name',
557             args => $_args,
558             EntireStash => $is_entire,
559             }));
560             $get;
561             \$stash->set(['$TEMP_VARNAME', 0], '');
562             };
563             EOF
564              
565             }
566              
567 80     80 0 138 sub compile_END { '' }
568              
569             sub compile_EVAL {
570 0     0 0 0 my ($self, $ref, $node) = @_;
571 0         0 my ($named, @strs) = @$ref;
572              
573 0         0 $named = [[]]; # TT doesn't allow args to eval ! $named ? [[]] : [[], map { $self->compile_expr($_) } @$named];
574              
575 0         0 my $block = "
576 0         0 foreach my \$str (".join(",\n", map {$self->compile_expr($_)} @strs).") {
577             next if ! defined \$str;
578             \$output .= \$str; # Alloy does them one at a time
579             }";
580              
581 0         0 $self->{'FACTORY'}->filter([["'eval'"], $named, ''], $block);
582             }
583              
584             sub compile_FILTER {
585 17     17 0 30 my ($self, $ref, $node) = @_;
586 17         24 my ($alias, $filter) = @$ref;
587              
588 17         34 my ($filt_name, $args) = @$filter; # doesn't support Template::Alloy chained filters
589              
590 17 100       49 $args = ! $args ? [[]] : [[], map { $self->compile_expr($_) } @$args];
  10         23  
591              
592 17         58 $self->{'FACTORY'}->filter([[$self->compile_expr($filt_name)],
593             $args,
594             $self->compile_expr($alias)
595             ],
596             $self->compile_tree($node->[4]));
597             }
598              
599 4     4 0 20 sub compile_FOR { shift->compile_FOREACH(@_) }
600              
601             sub compile_FOREACH {
602 16     16 0 38 my ($self, $ref, $node) = @_;
603 16         63 my ($var, $items) = @$ref;
604 16 100       42 if ($var) {
605 11         21 $var = $var->[0];
606             }
607              
608 16         52 $items = $self->compile_expr($items);
609              
610 16         79 local $self->{'loop_type'} = 'FOREACH';
611 16         70 return $self->{'FACTORY'}->foreach($var, $items, [[]], $self->compile_tree($node->[4]));
612             }
613              
614             sub compile_GET {
615 707     707 0 1022 my ($self, $ident) = @_;
616 707         1910 return $self->{'FACTORY'}->get($self->compile_expr($ident));
617             }
618              
619             sub compile_IF {
620 32     32 0 56 my ($self, $ref, $node, $unless) = @_;
621              
622 32         73 my $expr = $self->compile_expr($ref);
623 32 50       429 $expr = "!$expr" if $unless;
624              
625 32         98 my $block = $self->compile_tree($node->[4]);
626              
627 32         229 my @elsif;
628             my $had_else;
629 32         90 while ($node = $node->[5]) { # ELSE, ELSIF's
630 20 100       57 if ($node->[0] eq 'ELSE') {
631 13 50       35 if ($node->[4]) {
632 13         48 push @elsif, $self->compile_tree($node->[4]);
633 13         96 $had_else = 1;
634             }
635 13         18 last;
636             }
637 7         54 my $_expr = $self->compile_expr($node->[3]);
638 7         92 my $_block = $self->compile_tree($node->[4]);
639 7         101 push @elsif, [$_expr, $_block];
640             }
641 32 100       75 push @elsif, undef if ! $had_else;
642              
643 32         140 return $self->{'FACTORY'}->if($expr, $block, \@elsif);
644             }
645              
646             sub compile_INCLUDE {
647 0     0 0 0 my ($self, $ref, $node) = @_;
648              
649 0         0 my ($named, @files) = @{ $self->compile_named_args($ref) };
  0         0  
650              
651 0         0 return $self->{'FACTORY'}->include([\@files, [$named]]);
652             }
653              
654             sub compile_INSERT {
655 3     3 0 7 my ($self, $ref, $node) = @_;
656              
657 3         5 my ($named, @files) = @{ $self->compile_named_args($ref) };
  3         9  
658              
659 3         140 return $self->{'FACTORY'}->insert([\@files, [$named]]);
660             }
661              
662             sub compile_LAST {
663 0     0 0 0 my $self = shift;
664 0   0     0 my $type = $self->{'loop_type'} || '';
665 0 0 0     0 return "last LOOP;\n" if $type eq 'WHILE' || $type eq 'FOREACH';
666 0         0 return "last;\n"; # the grammar nicely hard codes the choices
667 0         0 return "last;\n";
668             }
669              
670             sub compile_LOOP {
671 15     15 0 29 my ($self, $ref, $node) = @_;
672 15 100       38 $ref = [$ref, 0] if ! ref $ref;
673              
674 15         195 my $out = "do {
675             my \$var = ".$self->compile_expr($ref).";
676             if (\$var) {
677             my \$conf = \$context->{'CONFIG'} ||= {};
678             my \$global = ! \$conf->{'SYNTAX'} || \$conf->{'SYNTAX'} ne 'ht' || \$conf->{'GLOBAL_VARS'};
679             my \$items = ref(\$var) eq 'ARRAY' ? \$var : ref(\$var) eq 'HASH' ? [\$var] : [];
680             my \$i = 0;
681             for my \$ref (\@\$items) {
682             \$context->throw('loop', 'Scalar value used in LOOP') if \$ref && ref(\$ref) ne 'HASH';
683             my \$stash = \$global ? \$stash : ref(\$stash)->new;
684             \$stash = \$context->localise() if \$global;
685             if (\$conf->{'LOOP_CONTEXT_VARS'} && ! \$Template::Stash::PRIVATE) {
686             my \%set;
687             \@set{qw(__counter__ __first__ __last__ __inner__ __odd__)}
688             = (++\$i, (\$i == 1 ? 1 : 0), (\$i == \@\$items ? 1 : 0), (\$i == 1 || \$i == \@\$items ? 0 : 1), (\$i % 2) ? 1 : 0);
689             \$stash->set(\$_, \$set{\$_}) foreach keys %set;
690             }
691             if (ref(\$ref) eq 'HASH') {
692             \$stash->set(\$_, \$ref->{\$_}) foreach keys %\$ref;
693             }
694             ".$self->compile_tree($node->[4])."
695             \$stash = \$context->delocalise() if \$global;
696             }
697             }
698             };";
699 15         247 return $out;
700             }
701              
702             sub compile_MACRO {
703 10     10 0 15 my ($self, $ref, $node) = @_;
704 10         16 my ($name, $args) = @$ref;
705              
706 10         30 $name = $self->compile_ident_str_from_cet($name);
707 10 100       27 $args = [map {$self->compile_ident_str_from_cet($_)} @$args] if $args;
  9         21  
708              
709             ### get the sub tree
710 10         15 my $sub_tree = $node->[4];
711 10 50 33     86 if (! $sub_tree || ! $sub_tree->[0]) {
    100 100        
712 0         0 $self->set_variable($name, undef);
713 0         0 return;
714             } elsif (ref($sub_tree->[0]) && $sub_tree->[0]->[0] eq 'BLOCK') {
715 3         7 $sub_tree = $sub_tree->[0]->[4];
716             }
717              
718 10         28 return $self->{'FACTORY'}->macro($name, $self->compile_tree($sub_tree), $args);
719             }
720              
721             sub compile_META {
722 2     2 0 5 my ($self, $hash, $node) = @_;
723 2 50       10 push(@{ $self->{'METADATA'} }, %$hash) if $hash;
  2         46  
724 0         0 return '';
725             }
726              
727             sub compile_NEXT {
728 0     0 0 0 my $self = shift;
729 0   0     0 my $type = $self->{'loop_type'} || '';
730 0 0       0 return $self->{'FACTORY'}->next if $type eq 'FOREACH';
731 0 0       0 return "next LOOP;\n" if $type eq 'WHILE';
732 0         0 return "next;\n";
733             }
734              
735             sub compile_PERL {
736 0     0 0 0 my ($self, $ref, $node) = @_;
737 0   0     0 my $block = $node->[4] || return '';
738 0 0       0 return $self->{'FACTORY'}->no_perl if ! $self->{'EVAL_PERL'};
739              
740 0         0 return $self->{'FACTORY'}->perl($self->compile_tree($block));
741             }
742              
743             sub compile_PROCESS {
744 22     22 0 44 my ($self, $ref, $node) = @_;
745              
746 22         32 my ($named, @files) = @{ $self->compile_named_args($ref) };
  22         59  
747              
748 22         270 return $self->{'FACTORY'}->process([\@files, [$named]]);
749             }
750              
751             sub compile_RAWPERL {
752 0     0 0 0 my ($self, $ref, $node) = @_;
753              
754 0 0       0 return $self->{'FACTORY'}->no_perl if ! $self->{'EVAL_PERL'};
755              
756 0   0     0 my $block = $node->[4] || return '';
757 0         0 my $info = $self->node_info($node);
758 0         0 my $txt = '';
759 0         0 foreach my $chunk (@$block) {
760 0 0       0 next if ! defined $chunk;
761 0 0       0 if (! ref $chunk) {
762 0         0 $txt .= $chunk;
763 0         0 next;
764             }
765 0 0       0 next if $chunk->[0] eq 'END';
766 0         0 die "Handling of $chunk->[0] not yet implemented in RAWPERL";
767             }
768              
769 0         0 return $self->{'FACTORY'}->rawperl($txt, $info->{'line'});
770             }
771              
772             sub compile_RETURN {
773 0     0 0 0 my $self = shift;
774 0         0 return $self->{'FACTORY'}->return;
775             }
776              
777             sub compile_SET {
778 173     173 0 273 my ($self, $set, $node, $default) = @_;
779              
780 173         236 my $out = '';
781 173         357 foreach (@$set) {
782 177         386 my ($op, $set, $val) = @$_;
783              
784 177 100 66     732 if (! defined $val) { # not defined
    100          
785 4         8 $val = "''";
786             } elsif ($node->[4] && $val == $node->[4]) { # a captured directive
787 2         5 my $sub_tree = $node->[4];
788 2 50 33     50 $sub_tree = $sub_tree->[0]->[4] if $sub_tree->[0] && $sub_tree->[0]->[0] eq 'BLOCK';
789 2         5 $set = do { local $self->{'_return_capture_ident'} = 1; $self->compile_expr($set) };
  2         5  
  2         7  
790 2         10 $out .= $self->{'FACTORY'}->capture($set, $self->compile_tree($sub_tree));
791 2         48 next;
792             } else { # normal var
793 171         409 $val = $self->compile_expr($val);
794             }
795              
796 175 50       2941 if ($OP_DISPATCH->{$op}) {
797 0 0       0 $op =~ /^([^\w\s\$]+)=$/ || die "Not sure how to handle that op $op during SET";
798 0 0 0     0 my $short = ($1 eq '_' || $1 eq '~') ? '.' : $1;
799 0         0 $val = "do { no warnings;\n". $self->compile_expr($set) ." $short $val}";
800             }
801              
802 175         370 $out .= $self->compile_expr($set, $val, $default).";\n";
803             }
804              
805 173         3013 return $out;
806             }
807              
808             sub compile_STOP {
809 5     5 0 6 my $self = shift;
810 5         23 return $self->{'FACTORY'}->stop;
811             }
812              
813             sub compile_SWITCH {
814 0     0 0 0 my ($self, $var, $node) = @_;
815              
816 0         0 my $expr = $self->compile_expr($var);
817             ### $node->[4] is thrown away
818              
819 0         0 my @cases;
820             my $default;
821 0         0 while ($node = $node->[5]) { # CASES
822 0         0 my $var = $node->[3];
823 0         0 my $block = $self->compile_tree($node->[4]);
824 0 0       0 if (! defined $var) {
825 0         0 $default = $block;
826 0         0 next;
827             }
828              
829 0         0 $var = $self->compile_expr($var);
830 0         0 push @cases, [$var, $block];
831             }
832 0         0 push @cases, $default;
833              
834 0         0 return $self->{'FACTORY'}->switch($expr, \@cases);
835             }
836              
837 0     0 0 0 sub compile_TAGS { '' } # doesn't really do anything - but needs to be in the parse tree
838              
839             sub compile_THROW {
840 0     0 0 0 my ($self, $ref) = @_;
841 0         0 my ($name, $args) = @$ref;
842              
843 0         0 $name = $self->compile_expr($name);
844              
845 0         0 $self->{'FACTORY'}->throw([[$name], $self->compile_named_args($args)]);
846             }
847              
848             sub compile_TRY {
849 0     0 0 0 my ($self, $foo, $node, $out_ref) = @_;
850 0         0 my $out = '';
851              
852 0         0 my $block = $self->compile_tree($node->[4]);
853              
854 0         0 my @catches;
855             my $had_final;
856 0         0 while ($node = $node->[5]) { # FINAL, CATCHES
857 0 0       0 if ($node->[0] eq 'FINAL') {
858 0 0       0 if ($node->[4]) {
859 0         0 $had_final = $self->compile_tree($node->[4]);
860             }
861 0         0 next;
862             }
863 0 0 0     0 my $_expr = defined($node->[3]) && uc($node->[3]) ne 'DEFAULT' ? $node->[3] : ''; #$self->compile_expr($node->[3]);
864 0         0 my $_block = $self->compile_tree($node->[4]);
865 0         0 push @catches, [$_expr, $_block];
866             }
867 0         0 push @catches, $had_final;
868              
869 0         0 return $self->{'FACTORY'}->try($block, \@catches);
870             }
871              
872             sub compile_UNLESS {
873 2     2 0 6 return shift->compile_IF(@_);
874             }
875              
876             sub compile_USE {
877 0     0 0 0 my ($self, $ref) = @_;
878 0         0 my ($var, $module, $args) = @$ref;
879              
880 0 0       0 $var = $self->compile_expr($var) if defined $var;
881              
882 0         0 return $self->{'FACTORY'}->use([[$self->compile_expr($module)], $self->compile_named_args($args), $var]);
883             }
884              
885             sub compile_VIEW {
886 0     0 0 0 my ($self, $ref, $node) = @_;
887              
888 0         0 my ($blocks, $args, $viewname) = @$ref;
889              
890 0         0 $viewname = $self->compile_ident_str_from_cet($viewname);
891 0         0 $viewname =~ s/\\\'/\'/g;
892 0         0 $viewname = "'$viewname'";
893              
894 0         0 my $named = $self->compile_named_args([$args])->[0];
895              
896             ### prepare the blocks
897             #my $prefix = $hash->{'prefix'} || (ref($name) && @$name == 2 && ! $name->[1] && ! ref($name->[0])) ? "$name->[0]/" : '';
898 0         0 foreach my $key (keys %$blocks) {
899 0         0 $blocks->{$key} = $self->{'FACTORY'}->template($self->compile_tree($blocks->{$key})); #{name => "${prefix}${key}", _tree => $blocks->{$key}};
900             }
901              
902 0         0 my $block = $self->compile_tree($node->[4]);
903 0         0 my $stuff= $self->{'FACTORY'}->view([[$viewname], [$named]], $block, $blocks);
904             # print "---------------------\n". $stuff ."------------------------------\n";
905 0         0 return $stuff;
906             }
907              
908             sub compile_WHILE {
909 0     0 0 0 my ($self, $ref, $node) = @_;
910              
911 0         0 my $expr = $self->compile_expr($ref);
912              
913 0         0 local $self->{'loop_type'} = 'WHILE';
914 0         0 my $block = $self->compile_tree($node->[4]);
915              
916 0         0 return $self->{'FACTORY'}->while($expr, $block);
917             }
918              
919             sub compile_WRAPPER {
920 0     0 0 0 my ($self, $ref, $node) = @_;
921              
922 0         0 my ($named, @files) = @{ $self->compile_named_args($ref) };
  0         0  
923              
924 0         0 return $self->{'FACTORY'}->wrapper([\@files, [$named]], $self->compile_tree($node->[4]));
925             }
926              
927             ###----------------------------------------------------------------###
928             ### Install some CET vmethods that dont' exist in TT2 as of 2.19
929              
930             if (! $NO_LOAD_EXTRA_VMETHODS
931             && eval {require Template::Stash}) {
932              
933             for my $meth (qw(0 abs atan2 cos exp fmt hex int js lc log oct rand sin sprintf sqrt uc)) {
934             next if defined $Template::Stash::SCALAR_OPS{$meth};
935             Template::Stash->define_vmethod('scalar', $meth => $Template::Alloy::SCALAR_OPS->{$meth});
936             }
937              
938             for my $meth (qw(fmt pick)) {
939             next if defined $Template::Stash::LIST_OPS{$meth};
940             Template::Stash->define_vmethod('list', $meth => $Template::Alloy::LIST_OPS->{$meth});
941             }
942              
943             for my $meth (qw(fmt)) {
944             next if defined $Template::Stash::HASH_OPS{$meth};
945             Template::Stash->define_vmethod('hash', $meth => $Template::Alloy::HASH_OPS->{$meth});
946             }
947             }
948              
949             sub add_top_level_functions {
950 571     571 0 852434 my ($class, $hash) = @_;
951 571         784 eval {require Template::Stash};
  571         3249  
952 571         702 foreach (keys %{ $Template::Stash::SCALAR_OPS }) {
  571         4232  
953 23411 50       44826 next if defined $hash->{$_};
954 23411         41143 $hash->{$_} = $Template::Stash::SCALAR_OPS->{$_};
955             }
956 571         2357 foreach (keys %{ $Template::Alloy::VOBJS }) {
  571         1643  
957 1713 50       3674 next if defined $hash->{$_};
958 1713         4204 $hash->{$_} = $Template::Alloy::VOBJS->{$_};
959             }
960             }
961              
962             ###----------------------------------------------------------------###
963             ### handle the playing of the DUMP directive since it the patch wasn't accepted
964              
965             sub play_dump {
966 0     0 0   my ($class, $info) = @_;
967 0   0       my $context = $info->{'context'} || die "Missing context";
968              
969             # find configuration overrides
970 0           my $conf = $context->{'CONFIG'}->{'DUMP'};
971 0 0 0       return '' if ! $conf && defined $conf; # DUMP => 0
972 0 0         $conf = {} if ref $conf ne 'HASH';
973              
974 0           my ($file, $line, $name, $args, $EntireStash) = @{ $info }{qw(file line name args EntireStash)};
  0            
975              
976             # allow for handler override
977 0           my $handler = $conf->{'handler'};
978 0 0         if (! $handler) {
979 0           require Data::Dumper;
980              
981             # new object and configure it with keys that it understands
982 0           my $obj = Data::Dumper->new([]);
983 0           my $meth;
984 0           foreach my $prop (keys %$conf) {
985 0 0 0       $obj->$prop($conf->{$prop}) if $prop =~ /^\w+$/ && ($meth = $obj->can($prop));
986             }
987              
988             # add in custom Sortkeys handler that can trim out private variables
989 0 0         my $sort = defined($conf->{'Sortkeys'}) ? $obj->Sortkeys : 1;
990 0 0   0     $obj->Sortkeys(sub { my $h = shift; [grep {$_ !~ $Template::Stash::PRIVATE} ($sort ? sort keys %$h : keys %$h)] });
  0            
  0            
  0            
991              
992 0     0     $handler = sub { $obj->Values([@_]); $obj->Dump }
  0            
993 0           }
994              
995             # play the handler
996 0           my $out;
997 0 0 0       if (! $EntireStash # always play if not EntireStash
      0        
998             || $conf->{'EntireStash'} # explicitly set
999             || ! defined $conf->{'EntireStash'} # default to on
1000             ) {
1001 0 0         delete $args->{$TEMP_VARNAME} if $EntireStash;
1002 0           $out = $handler->($args);
1003             }
1004 0 0         $out = '' if ! defined $out;
1005              
1006             # show our variable names
1007 0 0         $EntireStash ? $out =~ s/\$VAR1/$name/g : $out =~ s/\$VAR1/$name/;
1008              
1009             # add headers and formatting
1010 0 0 0       if ($conf->{'html'} # explicitly html
      0        
1011             || (! defined($conf->{'html'}) # or not explicitly no html
1012             && $ENV{'REQUEST_METHOD'} # and looks like a web request
1013             )) {
1014 0 0         if (defined $out) {
1015 0           $out = $context->filter('html')->($out);
1016 0           $out = "
$out
";
1017             }
1018 0 0 0       $out = "DUMP: File \"$info->{file}\" line $info->{line}$out" if $conf->{'header'} || ! defined $conf->{'header'};
1019             } else {
1020 0 0 0       $out = "DUMP: File \"$info->{file}\" line $info->{line}\n $out" if $conf->{'header'} || ! defined $conf->{'header'};
1021             }
1022              
1023 0           return $out;
1024             }
1025              
1026             ###----------------------------------------------------------------###
1027              
1028             1;
1029              
1030             __END__