1 #================================================================= -*-Perl-*-
6 # Factory module for constructing templates from Perl code.
9 # Andy Wardley <abw@wardley.org>
12 # Much of this module is hairy, even furry in places. It needs
13 # a lot of tidying up and may even be moved into a different place
14 # altogether. The generator code is often inefficient, particulary in
15 # being very anal about pretty-printing the Perl code all neatly, but
16 # at the moment, that's still high priority for the sake of easier
20 # Copyright (C) 1996-2007 Andy Wardley. All Rights Reserved.
22 # This module is free software; you can redistribute it and/or
23 # modify it under the same terms as Perl itself.
25 #============================================================================
27 package Template::Directive;
31 use base 'Template::Base';
32 use Template::Constants;
33 use Template::Exception;
36 our $DEBUG = 0 unless defined $DEBUG;
37 our $WHILE_MAX = 1000 unless defined $WHILE_MAX;
38 our $PRETTY = 0 unless defined $PRETTY;
39 our $OUTPUT = '$output .= ';
43 my ($self, $config) = @_;
44 $self->{ NAMESPACE } = $config->{ NAMESPACE };
50 my ($text, $pad) = @_;
51 $pad = ' ' x ($pad * 4);
52 $text =~ s/^(?!#line)/$pad/gm;
56 #========================================================================
59 # These methods are called by the parser to construct directive instances.
60 #========================================================================
62 #------------------------------------------------------------------------
64 #------------------------------------------------------------------------
67 my ($class, $block) = @_;
68 $block = pad($block, 2) if $PRETTY;
70 return "sub { return '' }" unless $block =~ /\S/;
74 my \$context = shift || die "template sub called without context\\n";
75 my \$stash = \$context->stash;
83 \$_tt_error = \$context->catch(\$@, \\\$output);
84 die \$_tt_error unless \$_tt_error->type eq 'return';
93 #------------------------------------------------------------------------
94 # anon_block($block) [% BLOCK %] ... [% END %]
95 #------------------------------------------------------------------------
98 my ($class, $block) = @_;
99 $block = pad($block, 2) if $PRETTY;
112 \$_tt_error = \$context->catch(\$@, \\\$output);
113 die \$_tt_error unless \$_tt_error->type eq 'return';
122 #------------------------------------------------------------------------
124 #------------------------------------------------------------------------
127 my ($class, $block) = @_;
128 return join("\n", @{ $block || [] });
132 #------------------------------------------------------------------------
134 #------------------------------------------------------------------------
137 my ($class, $text) = @_;
138 return "$OUTPUT " . &text($class, $text) . ';';
142 #------------------------------------------------------------------------
144 #------------------------------------------------------------------------
147 my ($class, $text) = @_;
149 s/(["\$\@\\])/\\$1/g;
152 return '"' . $text . '"';
156 #------------------------------------------------------------------------
157 # quoted(\@items) "foo$bar"
158 #------------------------------------------------------------------------
161 my ($class, $items) = @_;
162 return '' unless @$items;
163 return ("('' . " . $items->[0] . ')') if scalar @$items == 1;
164 return '(' . join(' . ', @$items) . ')';
165 # my $r = '(' . join(' . ', @$items) . ' . "")';
166 # print STDERR "[$r]\n";
171 #------------------------------------------------------------------------
172 # ident(\@ident) foo.bar(baz)
173 #------------------------------------------------------------------------
176 my ($class, $ident) = @_;
177 return "''" unless @$ident;
180 # does the first element of the identifier have a NAMESPACE
182 if (ref $class && @$ident > 2 && ($ns = $class->{ NAMESPACE })) {
183 my $key = $ident->[0];
184 $key =~ s/^'(.+)'$/$1/s;
185 if ($ns = $ns->{ $key }) {
186 return $ns->ident($ident);
190 if (scalar @$ident <= 2 && ! $ident->[1]) {
191 $ident = $ident->[0];
194 $ident = '[' . join(', ', @$ident) . ']';
196 return "\$stash->get($ident)";
199 #------------------------------------------------------------------------
200 # identref(\@ident) \foo.bar(baz)
201 #------------------------------------------------------------------------
204 my ($class, $ident) = @_;
205 return "''" unless @$ident;
206 if (scalar @$ident <= 2 && ! $ident->[1]) {
207 $ident = $ident->[0];
210 $ident = '[' . join(', ', @$ident) . ']';
212 return "\$stash->getref($ident)";
216 #------------------------------------------------------------------------
217 # assign(\@ident, $value, $default) foo = bar
218 #------------------------------------------------------------------------
221 my ($class, $var, $val, $default) = @_;
224 if (scalar @$var == 2 && ! $var->[1]) {
228 $var = '[' . join(', ', @$var) . ']';
231 $val .= ', 1' if $default;
232 return "\$stash->set($var, $val)";
236 #------------------------------------------------------------------------
237 # args(\@args) foo, bar, baz = qux
238 #------------------------------------------------------------------------
241 my ($class, $args) = @_;
242 my $hash = shift @$args;
243 push(@$args, '{ ' . join(', ', @$hash) . ' }')
246 return '0' unless @$args;
247 return '[ ' . join(', ', @$args) . ' ]';
250 #------------------------------------------------------------------------
252 #------------------------------------------------------------------------
255 my ($class, $names) = @_;
257 $names = '[ ' . join(', ', @$names) . ' ]';
260 $names = shift @$names;
266 #------------------------------------------------------------------------
267 # get($expr) [% foo %]
268 #------------------------------------------------------------------------
271 my ($class, $expr) = @_;
272 return "$OUTPUT $expr;";
276 #------------------------------------------------------------------------
277 # call($expr) [% CALL bar %]
278 #------------------------------------------------------------------------
281 my ($class, $expr) = @_;
287 #------------------------------------------------------------------------
288 # set(\@setlist) [% foo = bar, baz = qux %]
289 #------------------------------------------------------------------------
292 my ($class, $setlist) = @_;
294 while (my ($var, $val) = splice(@$setlist, 0, 2)) {
295 $output .= &assign($class, $var, $val) . ";\n";
302 #------------------------------------------------------------------------
303 # default(\@setlist) [% DEFAULT foo = bar, baz = qux %]
304 #------------------------------------------------------------------------
307 my ($class, $setlist) = @_;
309 while (my ($var, $val) = splice(@$setlist, 0, 2)) {
310 $output .= &assign($class, $var, $val, 1) . ";\n";
317 #------------------------------------------------------------------------
318 # insert(\@nameargs) [% INSERT file %]
319 # # => [ [ $file, ... ], \@args ]
320 #------------------------------------------------------------------------
323 my ($class, $nameargs) = @_;
324 my ($file, $args) = @$nameargs;
325 $file = $class->filenames($file);
326 return "$OUTPUT \$context->insert($file);";
330 #------------------------------------------------------------------------
331 # include(\@nameargs) [% INCLUDE template foo = bar %]
332 # # => [ [ $file, ... ], \@args ]
333 #------------------------------------------------------------------------
336 my ($class, $nameargs) = @_;
337 my ($file, $args) = @$nameargs;
338 my $hash = shift @$args;
339 $file = $class->filenames($file);
340 $file .= @$hash ? ', { ' . join(', ', @$hash) . ' }' : '';
341 return "$OUTPUT \$context->include($file);";
345 #------------------------------------------------------------------------
346 # process(\@nameargs) [% PROCESS template foo = bar %]
347 # # => [ [ $file, ... ], \@args ]
348 #------------------------------------------------------------------------
351 my ($class, $nameargs) = @_;
352 my ($file, $args) = @$nameargs;
353 my $hash = shift @$args;
354 $file = $class->filenames($file);
355 $file .= @$hash ? ', { ' . join(', ', @$hash) . ' }' : '';
356 return "$OUTPUT \$context->process($file);";
360 #------------------------------------------------------------------------
361 # if($expr, $block, $else) [% IF foo < bar %]
366 #------------------------------------------------------------------------
369 my ($class, $expr, $block, $else) = @_;
370 my @else = $else ? @$else : ();
372 $block = pad($block, 1) if $PRETTY;
374 my $output = "if ($expr) {\n$block\n}\n";
376 foreach my $elsif (@else) {
377 ($expr, $block) = @$elsif;
378 $block = pad($block, 1) if $PRETTY;
379 $output .= "elsif ($expr) {\n$block\n}\n";
382 $else = pad($else, 1) if $PRETTY;
383 $output .= "else {\n$else\n}\n";
390 #------------------------------------------------------------------------
391 # foreach($target, $list, $args, $block) [% FOREACH x = [ foo bar ] %]
394 #------------------------------------------------------------------------
397 my ($class, $target, $list, $args, $block, $label) = @_;
398 $args = shift @$args;
399 $args = @$args ? ', { ' . join(', ', @$args) . ' }' : '';
402 my ($loop_save, $loop_set, $loop_restore, $setiter);
404 $loop_save = 'eval { $_tt_oldloop = ' . &ident($class, ["'loop'"]) . ' }';
405 $loop_set = "\$stash->{'$target'} = \$_tt_value";
406 $loop_restore = "\$stash->set('loop', \$_tt_oldloop)";
409 $loop_save = '$stash = $context->localise()';
410 # $loop_set = "\$stash->set('import', \$_tt_value) "
411 # . "if ref \$value eq 'HASH'";
412 $loop_set = "\$stash->get(['import', [\$_tt_value]]) "
413 . "if ref \$_tt_value eq 'HASH'";
414 $loop_restore = '$stash = $context->delocalise()';
416 $block = pad($block, 3) if $PRETTY;
422 my (\$_tt_value, \$_tt_error, \$_tt_oldloop);
423 my \$_tt_list = $list;
425 unless (UNIVERSAL::isa(\$_tt_list, 'Template::Iterator')) {
426 \$_tt_list = Template::Config->iterator(\$_tt_list)
427 || die \$Template::Config::ERROR, "\\n";
430 (\$_tt_value, \$_tt_error) = \$_tt_list->get_first();
432 \$stash->set('loop', \$_tt_list);
434 $label: while (! \$_tt_error) {
437 (\$_tt_value, \$_tt_error) = \$_tt_list->get_next();
442 \$_tt_error = 0 if \$_tt_error && \$_tt_error eq Template::Constants::STATUS_DONE;
443 die \$_tt_error if \$_tt_error;
448 #------------------------------------------------------------------------
451 # Next iteration of a FOREACH loop (experimental)
452 #------------------------------------------------------------------------
455 my ($class, $label) = @_;
458 (\$_tt_value, \$_tt_error) = \$_tt_list->get_next();
464 #------------------------------------------------------------------------
465 # wrapper(\@nameargs, $block) [% WRAPPER template foo = bar %]
466 # # => [ [$file,...], \@args ]
467 #------------------------------------------------------------------------
470 my ($class, $nameargs, $block) = @_;
471 my ($file, $args) = @$nameargs;
472 my $hash = shift @$args;
475 # print STDERR "wrapper([@$file], { @$hash })\n";
477 return $class->multi_wrapper($file, $hash, $block)
479 $file = shift @$file;
481 $block = pad($block, 1) if $PRETTY;
482 push(@$hash, "'content'", '$output');
483 $file .= @$hash ? ', { ' . join(', ', @$hash) . ' }' : '';
491 \$context->include($file);
498 my ($class, $file, $hash, $block) = @_;
499 $block = pad($block, 1) if $PRETTY;
501 push(@$hash, "'content'", '$output');
502 $hash = @$hash ? ', { ' . join(', ', @$hash) . ' }' : '';
504 $file = join(', ', reverse @$file);
505 # print STDERR "multi wrapper: $file\n";
514 \$output = \$context->include(\$_$hash);
522 #------------------------------------------------------------------------
523 # while($expr, $block) [% WHILE x < 10 %]
526 #------------------------------------------------------------------------
529 my ($class, $expr, $block, $label) = @_;
530 $block = pad($block, 2) if $PRETTY;
537 my \$_tt_failsafe = $WHILE_MAX;
539 while (--\$_tt_failsafe && ($expr)) {
542 die "WHILE loop terminated (> $WHILE_MAX iterations)\\n"
543 unless \$_tt_failsafe;
549 #------------------------------------------------------------------------
550 # switch($expr, \@case) [% SWITCH %]
554 #------------------------------------------------------------------------
557 my ($class, $expr, $case) = @_;
559 my ($match, $block, $default);
562 $default = pop @case;
564 foreach $case (@case) {
567 $block = pad($block, 1) if $PRETTY;
569 \$_tt_match = $match;
570 \$_tt_match = [ \$_tt_match ] unless ref \$_tt_match eq 'ARRAY';
571 if (grep(/^\\Q\$_tt_result\\E\$/, \@\$_tt_match)) {
578 $caseblock .= $default
580 $caseblock = pad($caseblock, 2) if $PRETTY;
586 my \$_tt_result = $expr;
596 #------------------------------------------------------------------------
597 # try($block, \@catch) [% TRY %]
602 #------------------------------------------------------------------------
605 my ($class, $block, $catch) = @_;
607 my ($match, $mblock, $default, $final, $n);
611 $block = pad($block, 2) if $PRETTY;
613 $final = "# FINAL\n" . ($final ? "$final\n" : '')
614 . 'die $_tt_error if $_tt_error;' . "\n" . '$output;';
615 $final = pad($final, 1) if $PRETTY;
618 foreach $catch (@catch) {
619 $match = $catch->[0] || do {
620 $default ||= $catch->[1];
623 $mblock = $catch->[1];
624 $mblock = pad($mblock, 1) if $PRETTY;
625 push(@$handlers, "'$match'");
627 ? "elsif (\$_tt_handler eq '$match') {\n$mblock\n}\n"
628 : "if (\$_tt_handler eq '$match') {\n$mblock\n}\n";
630 $catchblock .= "\$_tt_error = 0;";
631 $catchblock = pad($catchblock, 3) if $PRETTY;
633 $default = pad($default, 1) if $PRETTY;
634 $default = "else {\n # DEFAULT\n$default\n \$_tt_error = '';\n}";
637 $default = '# NO DEFAULT';
639 $default = pad($default, 2) if $PRETTY;
641 $handlers = join(', ', @$handlers);
647 my (\$_tt_error, \$_tt_handler);
652 \$_tt_error = \$context->catch(\$@, \\\$output);
653 die \$_tt_error if \$_tt_error->type =~ /^return|stop\$/;
654 \$stash->set('error', \$_tt_error);
655 \$stash->set('e', \$_tt_error);
656 if (defined (\$_tt_handler = \$_tt_error->select_handler($handlers))) {
667 #------------------------------------------------------------------------
668 # throw(\@nameargs) [% THROW foo "bar error" %]
669 # # => [ [$type], \@args ]
670 #------------------------------------------------------------------------
673 my ($class, $nameargs) = @_;
674 my ($type, $args) = @$nameargs;
675 my $hash = shift(@$args);
676 my $info = shift(@$args);
677 $type = shift @$type; # uses same parser production as INCLUDE
678 # etc., which allow multiple names
679 # e.g. INCLUDE foo+bar+baz
682 $args = "$type, undef";
684 elsif (@$hash || @$args) {
687 $args = "$type, { args => [ "
688 . join(', ', $info, @$args)
691 (map { "'" . $i++ . "' => $_" } ($info, @$args)),
696 $args = "$type, $info";
699 return "\$context->throw($args, \\\$output);";
703 #------------------------------------------------------------------------
704 # clear() [% CLEAR %]
706 # NOTE: this is redundant, being hard-coded (for now) into Parser.yp
707 #------------------------------------------------------------------------
710 return "\$output = '';";
713 #------------------------------------------------------------------------
714 # break() [% BREAK %]
716 # NOTE: this is redundant, being hard-coded (for now) into Parser.yp
717 #------------------------------------------------------------------------
723 #------------------------------------------------------------------------
724 # return() [% RETURN %]
725 #------------------------------------------------------------------------
728 return "\$context->throw('return', '', \\\$output);";
731 #------------------------------------------------------------------------
733 #------------------------------------------------------------------------
736 return "\$context->throw('stop', '', \\\$output);";
740 #------------------------------------------------------------------------
741 # use(\@lnameargs) [% USE alias = plugin(args) %]
742 # # => [ [$file, ...], \@args, $alias ]
743 #------------------------------------------------------------------------
746 my ($class, $lnameargs) = @_;
747 my ($file, $args, $alias) = @$lnameargs;
748 $file = shift @$file; # same production rule as INCLUDE
750 $args = &args($class, $args);
751 $file .= ", $args" if $args;
752 # my $set = &assign($class, $alias, '$plugin');
754 . "\$stash->set($alias,\n"
755 . " \$context->plugin($file));";
758 #------------------------------------------------------------------------
759 # view(\@nameargs, $block) [% VIEW name args %]
760 # # => [ [$file, ... ], \@args ]
761 #------------------------------------------------------------------------
764 my ($class, $nameargs, $block, $defblocks) = @_;
765 my ($name, $args) = @$nameargs;
766 my $hash = shift @$args;
767 $name = shift @$name; # same production rule as INCLUDE
768 $block = pad($block, 1) if $PRETTY;
771 $defblocks = join(",\n", map { "'$_' => $defblocks->{ $_ }" }
773 $defblocks = pad($defblocks, 1) if $PRETTY;
774 $defblocks = "{\n$defblocks\n}";
775 push(@$hash, "'blocks'", $defblocks);
777 $hash = @$hash ? '{ ' . join(', ', @$hash) . ' }' : '';
783 my \$_tt_oldv = \$stash->get('view');
784 my \$_tt_view = \$context->view($hash);
785 \$stash->set($name, \$_tt_view);
786 \$stash->set('view', \$_tt_view);
790 \$stash->set('view', \$_tt_oldv);
792 # \$output; # not used - commented out to avoid warning
798 #------------------------------------------------------------------------
800 #------------------------------------------------------------------------
803 my ($class, $block) = @_;
804 $block = pad($block, 1) if $PRETTY;
809 \$context->throw('perl', 'EVAL_PERL not set')
810 unless \$context->eval_perl();
813 my \$output = "package Template::Perl;\\n";
817 local(\$Template::Perl::context) = \$context;
818 local(\$Template::Perl::stash) = \$stash;
820 my \$_tt_result = '';
821 tie *Template::Perl::PERLOUT, 'Template::TieString', \\\$_tt_result;
822 my \$_tt_save_stdout = select *Template::Perl::PERLOUT;
825 select \$_tt_save_stdout;
826 \$context->throw(\$@) if \$@;
833 #------------------------------------------------------------------------
835 #------------------------------------------------------------------------
839 return "\$context->throw('perl', 'EVAL_PERL not set');";
843 #------------------------------------------------------------------------
846 # NOTE: perhaps test context EVAL_PERL switch at compile time rather than
848 #------------------------------------------------------------------------
851 my ($class, $block, $line) = @_;
856 $block = pad($block, 1) if $PRETTY;
857 $line = $line ? " (starting line $line)" : '';
861 #line 1 "RAWPERL block$line"
868 #------------------------------------------------------------------------
870 #------------------------------------------------------------------------
873 my ($class, $lnameargs, $block) = @_;
874 my ($name, $args, $alias) = @$lnameargs;
875 $name = shift @$name;
876 $args = &args($class, $args);
877 $args = $args ? "$args, $alias" : ", undef, $alias"
879 $name .= ", $args" if $args;
880 $block = pad($block, 1) if $PRETTY;
887 my \$_tt_filter = \$context->filter($name)
888 || \$context->throw(\$context->error);
892 &\$_tt_filter(\$output);
898 #------------------------------------------------------------------------
899 # capture($name, $block)
900 #------------------------------------------------------------------------
903 my ($class, $name, $block) = @_;
906 if (scalar @$name == 2 && ! $name->[1]) {
910 $name = '[' . join(', ', @$name) . ']';
913 $block = pad($block, 1) if $PRETTY;
918 \$stash->set($name, do {
928 #------------------------------------------------------------------------
929 # macro($name, $block, \@args)
930 #------------------------------------------------------------------------
933 my ($class, $ident, $block, $args) = @_;
934 $block = pad($block, 2) if $PRETTY;
937 my $nargs = scalar @$args;
938 $args = join(', ', map { "'$_'" } @$args);
940 ? "\@_tt_args{ $args } = splice(\@_, 0, $nargs)"
941 : "\$_tt_args{ $args } = shift";
946 \$stash->set('$ident', sub {
948 my (%_tt_args, \$_tt_params);
950 \$_tt_params = shift;
951 \$_tt_params = { } unless ref(\$_tt_params) eq 'HASH';
952 \$_tt_params = { \%_tt_args, %\$_tt_params };
954 my \$stash = \$context->localise(\$_tt_params);
958 \$stash = \$context->delocalise();
969 \$stash->set('$ident', sub {
970 my \$_tt_params = \$_[0] if ref(\$_[0]) eq 'HASH';
973 my \$stash = \$context->localise(\$_tt_params);
977 \$stash = \$context->delocalise();
987 my ($class, $nameargs) = @_;
988 my ($file, $args) = @$nameargs;
989 my $hash = shift @$args;
990 $args = join(', ', @$file, @$args);
991 $args .= @$hash ? ', { ' . join(', ', @$hash) . ' }' : '';
992 return "$OUTPUT \$context->debugging($args); ## DEBUG ##";
1002 Template::Directive - Perl code generator for template directives
1006 # no user serviceable parts inside
1010 The C<Template::Directive> module defines a number of methods that
1011 generate Perl code for the runtime representation of the various
1012 Template Toolkit directives.
1014 It is used internally by the L<Template::Parser> module.
1018 Andy Wardley E<lt>abw@wardley.orgE<gt> L<http://wardley.org/>
1022 Copyright (C) 1996-2007 Andy Wardley. All Rights Reserved.
1024 This module is free software; you can redistribute it and/or
1025 modify it under the same terms as Perl itself.
1035 # perl-indent-level: 4
1036 # indent-tabs-mode: nil
1039 # vim: expandtab shiftwidth=4: