File Coverage

blib/lib/Template/Like/Processor.pm
Criterion Covered Total %
statement 291 326 89.2
branch 128 170 75.2
condition 16 28 57.1
subroutine 47 51 92.1
pod 0 32 0.0
total 482 607 79.4


line stmt bran cond sub pod time code
1             package Template::Like::Processor;
2            
3 13     13   8055 use Template::Like::Stash;
  13         46  
  13         414  
4 13     13   9855 use Template::Like::Filters;
  13         34  
  13         499  
5 13     13   7268 use Template::Like::VMethods;
  13         36  
  13         998  
6            
7 13         1541 use constant TAG_STYLE_SET => {
8             template1 => ['[\\[%]%', '%[\\]%]'],
9             template => ['\\[%', '%\\]'],
10             metatext => ['%%', '%%'],
11             star => ['\\[\\*', '\\*\\]'],
12             php => ['<\\?', '\\?>'],
13             asp => ['<%', '%>'],
14             mason => ['<%', '>'],
15             html => ['']
16 13     13   104 };
  13         30  
17            
18             # CHOMP constants for PRE_CHOMP and POST_CHOMP
19 13     13   71 use constant CHOMP_NONE => 0; # do not remove whitespace
  13         26  
  13         563  
20 13     13   146 use constant CHOMP_ALL => 1; # remove whitespace up to newline
  13         22  
  13         589  
21 13     13   75 use constant CHOMP_ONE => 1; # new name for CHOMP_ALL
  13         23  
  13         609  
22 13     13   75 use constant CHOMP_COLLAPSE => 2; # collapse whitespace to a single space
  13         20  
  13         672  
23 13     13   64 use constant CHOMP_GREEDY => 3; # remove all whitespace including newlines
  13         30  
  13         20450  
24            
25             # code set.
26             my $codeSet = {
27             IF => 'if ( %s ) {',
28             IF_POST => '}',
29             UNLESS => 'unless ( %s ) {',
30             UNLESS_POST => '}',
31             ELSIF => 'elsif ( %s ) {',
32             ELSIF_POST => '}',
33             ELSE => 'else {',
34             ELSE_POST => '}',
35             END => '%s',
36             FILTER => '{ my $filterOffset = length $output;',
37             FILTER_POST => 'substr($output, $filterOffset) = $self->filter(%s, substr($output, $filterOffset), %s); };',
38             DUMMY => "\$output.= %s;\n=pod",
39             DUMMY_POST => "\n=cut\n",
40             INSERT => '$output.= $self->insert(%s);',
41             INCLUDE => '$output.= $self->include(%s);',
42             PROCESS => '$output.= $self->process(%s);',
43             GET => '$output.= %s;',
44             SET => '%s;',
45             USE => '$self->plugin_use(\'%s\', %s);',
46             CALL => '%s;',
47             PRE_SPACE => '$output.= "%s" unless $self->PRE_CHOMP;',
48             POST_SPACE => '$output.= "%s" unless $self->POST_CHOMP;',
49             FOREACH => 'for ( to_array( %s ) ) {
50             local $stash->{\'%s\'} = $_;',
51             FOREACH_POST => '}',
52             WHILE => '{
53             my $wc = 0;
54             while ( %s ) {
55             die "while " . $self->WHILE_LIMIT . " over."
56             if $self->WHILE_LIMIT && $self->WHILE_LIMIT < ++$wc;',
57             WHILE_POST => "} }",
58             TEXT => '$output.= \'%s\';'
59             };
60            
61             #=====================================================================
62             # new
63             #---------------------------------------------------------------------
64             # - API
65             # $processor = Template::Like::Processor->new( $init_option, $params, $option );
66             #---------------------------------------------------------------------
67             # - args
68             # $init_option ...
69             # $params ... PARAMS ( HASHREF )
70             # $option ...
71             #---------------------------------------------------------------------
72             # - returns
73             # $processor ... Template::Like::Processor Object.
74             #---------------------------------------------------------------------
75             # - Example
76             # $processor = Template::Like::Processor->new( $init_option, $params, $option );
77             #=====================================================================
78             sub new {
79 103     103 0 193 my $class = shift;
80 103         162 my $init_option = shift;
81 103         132 my $params = shift;
82 103         129 my $option = shift;
83            
84 103         3775 my $self = bless {
85             OPTION => {
86             INCLUDE_PATH => [],
87             OUTPUT_PATH => undef,
88             ABSOLUTE => undef,
89             RELATIVE => undef,
90             TAG_STYLE => 'template',
91             START_TAG => undef,
92             END_TAG => undef,
93             FILTERS => {},
94             LOAD_FILTERS => [],
95             NAMESPACE => {},
96             CONSTANTS => undef,
97             CONSTANT_NAMESPACE => 'constants',
98             STASH => undef,
99             DEBUG => undef,
100             PLUGIN_BASE => [],
101             PRE_CHOMP => undef,
102             POST_CHOMP => undef,
103             WHILE_LIMIT => 1000
104             }
105             }, $class;
106            
107             # ---------- marge option ------------------------------------------
108            
109 103         459 @{ $self->{'OPTION'} }{ keys %{ $init_option } } = values %{ $init_option };
  103         424  
  103         186  
  103         536  
110            
111 103         165 @{ $self->{'OPTION'} }{ keys %{ $option } } = values %{ $option };
  103         172  
  103         161  
  103         848  
112            
113 103         233 for my $key ( ('INCLUDE_PATH', 'LOAD_FILTERS', 'PLUGIN_BASE') ) {
114 309 100       1724 unless ( UNIVERSAL::isa($self->{'OPTION'}->{ $key }, 'ARRAY') ) {
115 16         60 $self->{'OPTION'}->{ $key } = [ $self->{'OPTION'}->{ $key } ];
116             }
117             }
118            
119 103         173 push @{ $self->{'OPTION'}->{'INCLUDE_PATH'} }, File::Spec->curdir();
  103         769  
120            
121 103         149 push @{ $self->{'OPTION'}->{'LOAD_FILTERS'} }, Template::Like::Filters->new;
  103         1270  
122            
123 103         164 push @{ $self->{'OPTION'}->{'PLUGIN_BASE'} }, 'Template::Like::Plugin';
  103         242  
124            
125 103 50       598 if ( not UNIVERSAL::isa($self->{'OPTION'}->{'STASH'}, 'Template::Like::Stash') ) {
126 103         701 $self->{'OPTION'}->{'STASH'} = Template::Like::Stash->new;
127             }
128            
129 103 50       295 if ( not $self->START_TAG ) {
130 103         265 $self->{'OPTION'}->{'START_TAG'} = TAG_STYLE_SET->{ $self->TAG_STYLE }->[0];
131             }
132            
133 103 50       253 if ( not $self->END_TAG ) {
134 103         208 $self->{'OPTION'}->{'END_TAG'} = TAG_STYLE_SET->{ $self->TAG_STYLE }->[1];
135             }
136            
137             # ---------- init stash --------------------------------------------
138            
139 103         258 $self->{'STASH'} = $self->{'OPTION'}->{'STASH'};
140            
141 103         1534 $self->stash->update( $params );
142            
143 103         262 $self->stash->update( $self->NAMESPACE );
144            
145 103         255 $self->stash->set( $self->CONSTANT_NAMESPACE, $self->CONSTANTS );
146            
147 103         285 return $self;
148             }
149            
150            
151            
152             #=====================================================================
153             # clone
154             #---------------------------------------------------------------------
155             # - API
156             # $processor = $processor->clone;
157             #---------------------------------------------------------------------
158             # - args
159             # none
160             #---------------------------------------------------------------------
161             # - returns
162             # $processor ... this clone object.
163             #---------------------------------------------------------------------
164             # - Example
165             # use lexical stash.
166             # $processor->clone->process($input);
167             #=====================================================================
168             sub clone {
169 0     0 0 0 my $self = shift;
170            
171 0         0 my $clone = bless { %{ $self } }, 'Template::Like::Processor';
  0         0  
172            
173 0         0 $clone->{'STASH'} = $self->stash->clone;
174            
175 0         0 return $clone;
176             }
177            
178            
179            
180             #=====================================================================
181             # process
182             #---------------------------------------------------------------------
183             # - API
184             # $buffer = $processor->process( $input );
185             #---------------------------------------------------------------------
186             # - args
187             # $input ...
188             #---------------------------------------------------------------------
189             # - returns
190             # $buffer ... String.
191             #---------------------------------------------------------------------
192             # - Example
193             # $buffer = $processor->process( $input );
194             #=====================================================================
195             sub process {
196 103     103 0 163 my $self = shift;
197            
198 103         486 return $self->execute( $self->compile( $self->load( @_ ) ) );
199             }
200            
201            
202            
203             #=====================================================================
204             # load
205             #---------------------------------------------------------------------
206             # - API
207             # $text_ref = $processor->load( $input );
208             #---------------------------------------------------------------------
209             # - args
210             # $input ...
211             #---------------------------------------------------------------------
212             # - returns
213             # $text_ref ... Template Text.
214             #---------------------------------------------------------------------
215             # - Example
216             # $text_ref = $processor->load( $input );
217             #=====================================================================
218             sub load {
219 103     103 0 119 my $self = shift;
220 103         135 my $data = shift;
221            
222             # data is filename
223 103 100       513 if ( !ref $data ) {
    100          
    50          
    50          
224            
225 10         11 my $filename = $data;
226            
227 10         22 $filename=~s|/{2,}|/|g;
228            
229 10 100       23 if ( not $self->RELATIVE ) {
230 8 100       21 if ( $filename=~/(?:^|\/)\.+\// ) {
231 1         15 die "[$filename]: relative paths are not allowed (set RELATIVE option) ";
232             }
233             }
234            
235 9 100       18 if ( not $self->ABSOLUTE ) {
236 8 100       46 if ( File::Spec->file_name_is_absolute($filename) ) {
237 1         15 die "[$filename]: absolute paths are not allowed (set ABSOLUTE option)";
238             }
239             }
240            
241 8         10 my $filepath;
242            
243 8 100       35 if ( File::Spec->file_name_is_absolute($filename) ) {
244 1 50       28 $filepath = $filename if -f $filename;
245             } else {
246 7         17 for my $dir ( $self->INCLUDE_PATH ) {
247 10 100       271 if (-f File::Spec->catfile( $dir, $filename )) {
248 7         56 $filepath = File::Spec->catfile( $dir, $filename );
249 7         18 last;
250             }
251             }
252             }
253            
254 8 50       26 die "file not found. filename is [$filename] include_path is ["
255             . join(',', $self->INCLUDE_PATH)
256             . "]" if not $filepath;
257            
258 8 50 33     28 die "file open endless loop [$filepath]"
259             if ( exists $self->{'OPEND'}->{ $filepath } && $self->{'OPEND'}->{ $filepath } > 10 );
260            
261 8         20 $self->{'OPEND'}->{ $filepath }++;
262            
263 8 50       53 my $fh = IO::File->new($filepath) or die "file open failure [$filepath]";
264            
265 8         741 my $input = join '', <$fh>;
266 8         38 $fh->close;
267 8         135 return \$input;
268             }
269            
270             elsif ( UNIVERSAL::isa($data, "SCALAR") ) {
271 92         131 return \do{ my $str = $$data };
  92         960  
272             }
273            
274             elsif ( UNIVERSAL::isa($data, "ARRAY") ) {
275 0         0 return \do{ my $str = join '', @{$data} };
  0         0  
  0         0  
276             }
277            
278             elsif ( UNIVERSAL::isa($data, "GLOB") ) {
279 1         2 return \do{ my $str = join '', <$data> };
  1         34  
280             }
281             }
282            
283            
284            
285             #=====================================================================
286             # compile
287             #---------------------------------------------------------------------
288             # - API
289             # $code = $processor->compile( $text_ref );
290             #---------------------------------------------------------------------
291             # - args
292             # $text_ref ... Template Text Reference.
293             #---------------------------------------------------------------------
294             # - returns
295             # $code ... Perl code.
296             #---------------------------------------------------------------------
297             # - Example
298             # $code = $processor->compile( $text_ref );
299             #=====================================================================
300             sub compile {
301 101     101 0 155 my $self = shift;
302 101         115 my $text_ref = shift;
303            
304 101         261 my $start = $self->START_TAG;
305 101         218 my $end = $self->END_TAG;
306            
307 101         127 my @endTask;
308 101         138 my $code = '';
309            
310 13     13   105 no warnings 'uninitialized';
  13         42  
  13         50885  
311            
312             my $appendSet = sub {
313 301     301   458 my $directive = shift;
314 301         465 my $directive_post = $directive . '_POST';
315 301         550 my $format = $codeSet->{ $directive };
316 301         777 $code.= ' ' x scalar( @endTask );
317 301         1376 $code.= sprintf $format, @_;
318 301         423 $code.= "\n";
319            
320 301 100       1974 if ( exists $codeSet->{ $directive_post } ) {
321 59         995 push @endTask, sprintf($codeSet->{ $directive_post }, @_);
322             }
323 101         586 };
324            
325             my $escapeQuote = sub {
326 88     88   131 my $str = shift;
327 88         151 $str=~s/\'/\\\'/g;
328 88         209 return $str;
329 101         470 };
330            
331 101         2646 while ( $$text_ref=~ s/^(.*?)(?:$start([-=~+]?)(.*?)([-=~+]?)$end)//sx ) {
332            
333 181         1027 my ($text, $pre_chomp, $ele, $post_chomp) = ($1, $2, $3, $4);
334            
335 181 50       673 $text = '' unless defined $text;
336 181 50       480 $ele = '' unless defined $ele;
337 181   50     805 $pre_chomp ||= $self->PRE_CHOMP || 0;
      33        
338 181   100     594 $post_chomp ||= $self->POST_CHOMP || 0;
      66        
339 181         335 $pre_chomp =~ tr/-=~+/1230/;
340 181         217 $post_chomp =~ tr/-=~+/1230/;
341            
342 181 50       1144 if ($pre_chomp == CHOMP_ALL) {
    50          
    50          
343 0         0 $text =~ s{ (\n|^) [^\S\n]* \z }{}mx;
344             } elsif ($pre_chomp == CHOMP_COLLAPSE) {
345 0         0 $text =~ s{ (\s+) \z }{ }x;
346             } elsif ($pre_chomp == CHOMP_GREEDY) {
347 0         0 $text =~ s{ (\s+) \z }{}x;
348             }
349            
350 181 100       1766 if ($post_chomp == CHOMP_ALL) {
    50          
    50          
351 13         20 $$text_ref =~ s{ ^ ([^\S\n]* \n) }{}x;
352             } elsif ($post_chomp == CHOMP_COLLAPSE) {
353 0         0 $$text_ref =~ s{ ^ (\s+) }{ }x;
354             } elsif ($post_chomp == CHOMP_GREEDY) {
355 0         0 $$text_ref =~ s{ ^ (\s+) }{}x;
356             }
357            
358 181 100       550 $appendSet->( 'TEXT', $escapeQuote->($text) ) if length $text;
359            
360            
361 181         710 $ele=~s/^\s+//;
362 181         803 $ele=~s/\s+$//;
363            
364 181         673 while ( length $ele ) {
365            
366 193         208 my ( $directive, @args );
367            
368 193         994 ( $ele, $directive, @args ) = $self->expansion( $ele );
369            
370 193 100       1115 if ( $directive eq 'END' ) {
    100          
    100          
371 39         72 $appendSet->( $directive, ( pop @endTask ) );
372             }
373            
374             elsif ( $directive eq 'ELSE' ) {
375 13         30 $appendSet->( 'END', ( pop @endTask ) );
376 13         114 $appendSet->( $directive );
377             }
378            
379             elsif ( $directive eq 'ELSIF' ) {
380 7         17 $appendSet->( 'END', ( pop @endTask ) );
381 7         16 $appendSet->( $directive, @args );
382             }
383            
384             else {
385 134         351 $appendSet->( $directive, @args );
386             }
387             }
388             }
389            
390 101 100       291 $appendSet->( 'TEXT', $escapeQuote->($$text_ref) ) if length $$text_ref;
391            
392 101         1217 return "{\n$code}\n";
393             }
394            
395             # The contents, possibly including any embedded template directives, are inserted intact.
396             sub insert {
397 0     0 0 0 ${ shift->load(@_) };
  0         0  
398             }
399            
400             #
401             sub include {
402 0     0 0 0 shift->clone->process(@_);
403             }
404            
405             sub plugin_use {
406 11     11 0 16 my $self = shift;
407 11         14 my $key = shift;
408 11         13 my $plugin_name = $key;
409            
410 11 100       33 if ($key=~/(.*)=(.*)/){
411 3         6 $key = $1;
412 3         6 $plugin_name = $2;
413             }
414            
415 11         23 for my $base ( $self->PLUGIN_BASE ) {
416            
417 11         19 my $plugin_class = $base.'::'.$plugin_name;
418            
419 11     2   623 eval "use $plugin_class;";
  2     2   678  
  2     2   4  
  2     2   33  
  2     2   12  
  2     1   7  
  2         20  
  2         13  
  2         3  
  2         24  
  2         11  
  2         4  
  2         21  
  2         10  
  2         3  
  2         20  
  1         6  
  1         3  
  1         11  
420            
421 11 50       31 unless ($@) {
422 11         25 $self->stash->set( $key, $plugin_class->new($self, @_) );
423 11         273 return;
424             }
425             }
426            
427 0 0       0 die ($@) if ($@);
428             }
429            
430             #-----------------------------
431             # expansion
432             #-----------------------------
433             sub expansion {
434 193     193 0 234 my $self = shift;
435 193         259 my $expression = shift;
436            
437 193         207 my ( $directive, @pre_opts, @post_opts );
438            
439             # -----------------------------------------------------------------
440            
441 193 100       2434 if ( $expression=~s/^(CALL|GET|SET|IF|UNLESS|ELSIF|DUMMY|PRE_SPACE|POST_SPACE)\s+//x ) {
    100          
    100          
    100          
    100          
    100          
    100          
442 48         222 $directive = $1;
443             }
444            
445             # USE
446             elsif ( $expression=~s/^USE\s+// ) {
447            
448 11         13 $directive = 'USE';
449 11         13 my $key = '';
450 11         12 my $code = '';
451 11         20 my @gets = '';
452 11         14 my $text = '';
453            
454             # SET
455 11 100       32 if ( $expression=~s/^(\w+)\s*=\s*// ) {
456 3         6 $key = $1.'=';
457             }
458            
459             # ARGUMENTS
460 11 50       46 if ( $expression=~s/^([a-zA-Z0-9\.]+)// ) {
461 11         19 $text = $1;
462             }
463            
464 11         32 @pre_opts = ( $key.$text );
465             }
466            
467             elsif ( $expression=~/^(FILTER|INSERT|INCLUDE)\s+(\S.*)$/sx ) {
468            
469 1         116 $directive = $1;
470 1         2 $expression = $2;
471            
472 1 50       7 if ($expression=~s/^\$//) {
473             }
474            
475             else {
476 1 50       8 if ($expression=~s/([^\(\);\s]+)//) {
477 1         6 my $name = $1;
478 1         3 $name=~s/\'/\\\'/g;
479 1         6 @pre_opts = ( "'$name'" );
480             }
481             }
482             }
483            
484             # ELSE
485             elsif ( $expression=~s/ELSE// ) {
486 13         18 $directive = 'ELSE';
487             }
488            
489             # END
490             elsif ( $expression=~s/END// ) {
491 39         57 $directive = 'END';
492             }
493            
494             # FOREACH
495             elsif ( $expression=~s/^FOREACH\s*(\w+)\s*(?:\=|IN)\s*// ) {
496 1         30 $directive = 'FOREACH';
497 1         3 @post_opts = ($1);
498             }
499            
500             # WHILE (?:(\w+)\s*\=\s*)?
501             elsif ( $expression=~s/^WHILE\s*// ) {
502 2         4 $directive = 'WHILE';
503             }
504            
505             # OTHER
506             else {
507 78         181 $directive = 'GET';
508             }
509            
510            
511             # -----------------------------------------------------------------
512            
513            
514 193         279 my $token;
515 193         627 my $code = '';
516 193         250 my $depth = 0;
517 193         630 my $start = { 0 => 0 };
518            
519 193         1929 while ($expression =~
520             s/
521             # strip out any comments
522             (\#[^\n]*)
523             |
524             # a quoted phrase matches in $3
525             (["']) # $2 - opening quote, ' or "
526             ( # $3 - quoted text buffer
527             (?: # repeat group (no backreference)
528             \\\\ # an escaped backslash \\
529             | \\\2 # an escaped quote \" or \' (match $1)
530             | . # any other character
531             | \n # \n
532             )*? # non-greedy repeat
533             ) # end of $3
534             \2 # match opening quote
535             |
536             # an unquoted number matches in $4
537             (-?\d+(?:\.\d+)?) # numbers
538             |
539             # filename matches in $5
540             ((?!))
541             |
542             # an identifier matches in $6
543             \s*\|\s*([\w]+)\( # variable identifier
544             |
545             # an identifier matches in $7
546             \s*\|\s*([\w]+) # variable identifier
547             |
548             # an identifier matches in $8
549             ((?!\_)[\$\.]?\w+)\( # variable identifier
550             |
551             # an identifier matches in $9
552             ((?!\_)[\$\.]?\w+)\s*\=(?![=>]) # variable identifier
553             |
554             # an identifier matches in $10
555             ((?!\_)[\$\.]?\w+) # variable identifier
556             |
557             # an unquoted word or symbol matches in $11
558             ( [(){}\[\]:;,\/\\] # misc parenthesis and symbols
559             | [+\-*] # math operations
560             | \$\{? # dollar with option left brace
561             | != # like 'ne'
562             | == # like 'eq'
563             | => # like '='
564             | [=!<>]?= | [!<>] # equality tests
565             | &&? | \|\|? # boolean ops
566             | \.\.? # n..n sequence
567             | \S+ # something unquoted
568             | \s+ # something unquoted
569             ) # end of $11
570             //mxo) {
571            
572 348 100 100     4601 if (defined ($token = $3)) {
    100          
    50          
    100          
    100          
    100          
    100          
    100          
    100          
    50          
573 21         156 $code.= $2 . $token . $2;
574             }
575            
576             elsif (defined ($token = $4)) {
577 8         918 $code.= $token;
578             }
579            
580             elsif (defined ($token = $5)) {
581 0         0 $token=~s/\'/\\\'/g;
582 0         0 $code.= "'$token'";
583             }
584            
585             elsif (defined ($token = $6)) {
586 7         32 $code = sprintf q{$self->filter('%s', %s, }, $token, $code;
587 7         53 $depth++;
588             }
589            
590             elsif (defined ($token = $7)) {
591 12         155 $code = sprintf q{$self->filter('%s', %s)}, $token, $code;
592             }
593            
594             elsif (defined ($token = $8)) {
595             # method after dot.
596 13 50 33     93 if ( $token=~/^\./ && $code=~/\)$/ ) {
    0          
    0          
597 13         25 $token = substr($token, 1);
598 13         52 substr($code, $start->{ $depth }) =
599             '$stash->next(' . substr($code, $start->{ $depth }) . ", '$token', ";
600             }
601            
602             # first dollar.
603             elsif ( $token=~/^\$(.*)$/ ) {
604 0         0 $start->{ $depth } = length $code;
605 0         0 $code.= "\$stash->get('$1', ";
606             }
607            
608             # first dot.
609             elsif ( $token=~/^\.(.*)$/ ) {
610 0         0 substr($code, $start->{ $depth }) =
611             '$stash->next(' . substr($code, $start->{ $depth }) . ", '$1', ";
612             }
613            
614             # directive which can omit the dollar.
615             else {
616 0         0 $start->{ $depth } = length $code;
617 0         0 $code.= "\$stash->get('$token', ";
618             }
619 13         88 $depth++;
620             }
621            
622             elsif (defined ($token = $9) && $directive eq 'USE') {
623            
624 2         15 $code.= "$token =>";
625             }
626            
627             elsif (defined ($token = $9)) {
628            
629 4 100       15 if ( $directive eq 'GET' ) {
630 1         2 $directive = 'SET';
631             }
632            
633             # method after dot.
634 4 100 66     40 if ( $token=~/^\./ && $code=~/\)$/ ) {
    50          
    50          
635 1         3 $token = substr($token, 1);
636 1         17 $code.= "->{'$token'} =";
637             }
638            
639             # first dollar.
640             elsif ( $token=~/^\$(.*)$/ ) {
641 0         0 $code.= "\$stash->{'$1'} =";
642             }
643            
644             # first dot.
645             elsif ( $token=~/^\.(.*)$/ ) {
646 0         0 $code.= "->{'$1'} =";
647             }
648            
649             else {
650 3         26 $code.= "\$stash->{'$token'} =";
651             }
652            
653             # $start->{ $depth } = length $code;
654             }
655            
656             elsif (defined ($token = $10)) {
657            
658             # method after dot.
659 168 100 66     1925 if ( $token=~/^\./ && $code=~/\)$/ ) {
    50          
    50          
660 27         55 $token = substr($token, 1);
661 27         673 substr($code, $start->{ $depth }) =
662             '$stash->next(' . substr($code, $start->{ $depth }) . ", '$token')";
663             }
664            
665             # first dollar.
666             elsif ( $token=~/^\$(.*)$/ ) {
667 0         0 $start->{ $depth } = length $code;
668 0         0 $code.= "\$stash->get('$1')";
669             }
670            
671             # first dot.
672             elsif ( $token=~/^\.(.*)$/ ) {
673 0         0 substr($code, $start->{ $depth }) =
674             '$stash->next(' . substr($code, $start->{ $depth }) . ", '$1')";
675             }
676            
677             else {
678 141         259 $start->{ $depth } = length $code;
679 141         1197 $code.= "\$stash->get('$token')";
680             }
681             }
682            
683             elsif (defined ($token = $11)) {
684 113 100       979 if ( $token eq '==' ) {
    100          
    50          
    100          
    100          
685 6         44 $code.= ' eq ';
686             } elsif ( $token eq '!=' ) {
687 2         15 $code.= ' ne ';
688             } elsif ( $token eq '_' ) {
689 0         0 $code.= '.';
690             } elsif ( $token eq ')' ) {
691 23         29 $code.= ')';
692 23         146 $depth--;
693             } elsif ( $token eq ';' ) {
694 12         124 return ( $expression, $directive, @pre_opts, $code, @post_opts );
695             } else {
696 70         449 $code.= $token;
697             }
698             }
699            
700             # warn "depth: " . $depth;
701             # warn "start: " . $start->{ $depth };
702             # warn "token: $token";
703             # warn "code: " . $code . "\n";
704             }
705            
706 181         1075 return ( $expression, $directive, @pre_opts, $code, @post_opts );
707             }
708            
709            
710            
711             #=====================================================================
712             # filter
713             #---------------------------------------------------------------------
714             # - API
715             # $buffer = $processor->filter( $name, $buffer, @ARGS... );
716             #---------------------------------------------------------------------
717             # - args
718             # $name ... Filter Name.
719             #---------------------------------------------------------------------
720             # - returns
721             # $buffer ... buffer.
722             #---------------------------------------------------------------------
723             # - Example
724             # $buffer = $processor->filter( $name );
725             #=====================================================================
726             sub filter {
727 22     22 0 36 my $self = shift;
728 22         33 my $name = shift;
729            
730 22 100       61 if ( exists $self->FILTERS->{ $name } ) {
731 4         11 return $self->FILTERS->{ $name }->( @_ );
732             }
733            
734 18         54 for my $filter ( $self->LOAD_FILTERS ) {
735 18 50       161 if ( UNIVERSAL::can($filter, $name) ) {
736 18         236 return $filter->$name( @_ );
737             }
738             }
739            
740 0         0 die "not defined filter [$name].";
741             }
742            
743            
744            
745             #=====================================================================
746             # execute
747             #---------------------------------------------------------------------
748             # - API
749             # $buffer = $processor->execute( $code );
750             #---------------------------------------------------------------------
751             # - args
752             # $code ... Perl code.
753             #---------------------------------------------------------------------
754             # - returns
755             # $buffer ... buffer.
756             #---------------------------------------------------------------------
757             # - Example
758             # $buffer = $processor->execute( $code );
759             #=====================================================================
760             sub execute {
761 101     101 0 222 my $self = shift;
762 101         138 my $code = shift;
763            
764 101         142 my $output = '';
765 101         220 my $stash = $self->stash;
766            
767 101 50       416 warn $code if $self->DEBUG;
768            
769 13     13   119 no warnings 'uninitialized';
  13         27  
  13         11890  
770 101         14356 eval $code;
771 101 50       765 die sprintf("Template::Like Error: %s\ncode: \n%s", $@, $code) if $@;
772            
773 101         571 return $output;
774             }
775            
776             #=====================================================================
777             # filalize
778             #---------------------------------------------------------------------
779             # - API
780             # $processor->finalize( $buffer, $output );
781             #---------------------------------------------------------------------
782             # - args
783             # $buffer ... Perl code.
784             # $output ... Perl code.
785             #---------------------------------------------------------------------
786             # - returns
787             # none.
788             #---------------------------------------------------------------------
789             # - Example
790             # $processor->finalize( $buffer, $output );
791             #=====================================================================
792             sub finalize {
793 101     101 0 248 my $self = shift;
794 101         143 my $buffer = shift;
795 101         115 my $output = shift;
796            
797 101 100       253 if ( ref $output ) {
798            
799 98 100       301 if ( UNIVERSAL::isa($output, 'SCALAR') ) {
    100          
    100          
    50          
    50          
800 95         117 ${ $output }.= $buffer;
  95         1199  
801             }
802            
803             elsif ( UNIVERSAL::isa($output, 'ARRAY') ) {
804 1         2 push @{ $output }, $buffer;
  1         7  
805             }
806            
807             elsif ( UNIVERSAL::isa($output, 'CODE') ) {
808 1         5 $output->($buffer);
809             }
810            
811             # filehandle
812             elsif ( UNIVERSAL::isa($output, 'GLOB') ) {
813 0         0 print $output $buffer;
814             }
815            
816             # Apache::Request, Apache2::Request ...
817             elsif ( UNIVERSAL::can($output, 'print') ) {
818 1         4 $output->print($buffer);
819             }
820            
821             else {
822 0         0 die "no support output [$output]";
823             }
824             }
825            
826             # filename
827             else {
828            
829 3 100       7 my $path = $self->OUTPUT_PATH
830             ? File::Spec->catfile( $self->OUTPUT_PATH, $output )
831             : $output;
832            
833 3 50       75 my $mark = -f $path ? '+<' : '>';
834 3 50       24 my $fh = new IO::File $mark.$path
835             or $self->error("output file open failure [".$path."]");
836            
837 3         353 seek $fh, 0, 0;
838 3         29 print $fh $buffer;
839 3         185 truncate $fh, tell($fh);
840 3         61 close $fh;
841             }
842             }
843            
844             sub to_array {
845 1 50 33 1 0 15 return @{ $_[0] } if @_ == 1 && UNIVERSAL::isa($_[0], 'ARRAY');
  1         32  
846 0         0 return @_;
847             }
848            
849            
850             #-----------------------------
851             # Accessors
852             #-----------------------------
853 421     421 0 1541 sub stash { $_[0]->{'STASH'}; }
854 0     0 0 0 sub error { die @_; };
855            
856 101     101 0 321 sub DEBUG { $_[0]->{'OPTION'}->{'DEBUG'} }
857 5     5 0 43 sub OUTPUT_PATH { $_[0]->{'OPTION'}->{'OUTPUT_PATH'} }
858 9     9 0 24 sub ABSOLUTE { $_[0]->{'OPTION'}->{'ABSOLUTE'} }
859 10     10 0 28 sub RELATIVE { $_[0]->{'OPTION'}->{'RELATIVE'} }
860 206     206 0 16371 sub TAG_STYLE { $_[0]->{'OPTION'}->{'TAG_STYLE'} }
861 204     204 0 647 sub START_TAG { $_[0]->{'OPTION'}->{'START_TAG'} }
862 204     204 0 504 sub END_TAG { $_[0]->{'OPTION'}->{'END_TAG'} }
863 26     26 0 120 sub FILTERS { $_[0]->{'OPTION'}->{'FILTERS'} }
864 103     103 0 381 sub NAMESPACE { $_[0]->{'OPTION'}->{'NAMESPACE'} }
865 103     103 0 407 sub CONSTANTS { $_[0]->{'OPTION'}->{'CONSTANTS'} }
866 103     103 0 675 sub CONSTANT_NAMESPACE { $_[0]->{'OPTION'}->{'CONSTANT_NAMESPACE'} }
867 7     7 0 7 sub INCLUDE_PATH { @{ $_[0]->{'OPTION'}->{'INCLUDE_PATH'} } }
  7         23  
868 18     18 0 23 sub LOAD_FILTERS { @{ $_[0]->{'OPTION'}->{'LOAD_FILTERS'} } }
  18         59  
869 11     11 0 11 sub PLUGIN_BASE { @{ $_[0]->{'OPTION'}->{'PLUGIN_BASE'} } }
  11         36  
870 181     181 0 1008 sub PRE_CHOMP { $_[0]->{'OPTION'}->{'PRE_CHOMP'} }
871 181     181 0 1654 sub POST_CHOMP { $_[0]->{'OPTION'}->{'POST_CHOMP'} }
872 12     12 0 691 sub WHILE_LIMIT { $_[0]->{'OPTION'}->{'WHILE_LIMIT'} }
873            
874             1;